#include <jni.h>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>
#include <opencv2/stitching.hpp>
// 辅助函数:Java Bitmap 转 cv::Mat (假设 ARGB_8888)
#include <android/bitmap.h>
#include <android/log.h>
#define LOG_TAG "wringstisching"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
cv::Mat bitmapToMat(JNIEnv *env, jobject bitmap) {
AndroidBitmapInfo info;
void *pixels;
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) == ANDROID_BITMAP_RESULT_SUCCESS);
CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) == ANDROID_BITMAP_RESULT_SUCCESS);
cv::Mat mat(info.height, info.width, CV_8UC4, pixels);
cv::Mat mat_rgb;
cv::cvtColor(mat, mat_rgb, cv::COLOR_RGBA2BGR); // 转 BGR 方便 OpenCV 处理
AndroidBitmap_unlockPixels(env, bitmap);
return mat_rgb;
}
jobject matToBitmap(JNIEnv *env, const cv::Mat &mat, jobject bitmapTemplate) {
AndroidBitmapInfo info;
void *pixels;
CV_Assert(AndroidBitmap_getInfo(env, bitmapTemplate, &info) == ANDROID_BITMAP_RESULT_SUCCESS);
CV_Assert(AndroidBitmap_lockPixels(env, bitmapTemplate, &pixels) ==
ANDROID_BITMAP_RESULT_SUCCESS);
cv::Mat mat_rgba;
if (mat.channels() == 3) {
cv::cvtColor(mat, mat_rgba, cv::COLOR_BGR2RGBA);
} else if (mat.channels() == 1) {
cv::cvtColor(mat, mat_rgba, cv::COLOR_GRAY2RGBA);
} else {
mat_rgba = mat;
}
memcpy(pixels, mat_rgba.data, info.height * info.stride);
AndroidBitmap_unlockPixels(env, bitmapTemplate);
return bitmapTemplate;
}
extern "C"
JNIEXPORT jobject JNICALL
Java_com_android_wring_jni_NativeLib_stitchImages(JNIEnv *env, jobject thiz, jobjectArray bitmaps) {
int numImages = env->GetArrayLength(bitmaps);
LOGI("stitchImages: %d", numImages);
if (numImages < 2) return nullptr;
std::vector<cv::Mat> images;
for (int i = 0; i < numImages; i++) {
jobject bitmap = env->GetObjectArrayElement(bitmaps, i);
cv::Mat img = bitmapToMat(env, bitmap);
if (img.empty()) {
LOGE("stitchImages: bitmap is empty");
return nullptr;
}
images.push_back(img);
env->DeleteLocalRef(bitmap);
}
cv::Mat pano;
cv::Ptr<cv::Stitcher> stitcher = cv::Stitcher::create(cv::Stitcher::PANORAMA);
cv::Stitcher::Status status = stitcher->stitch(images, pano);
if (status != cv::Stitcher::OK) {
LOGE("stitchImages: stitch failed");
return nullptr;
}
// 创建一个空 Bitmap 作为输出模板(ARGB_8888)
jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
jmethodID createBitmapMID = env->GetStaticMethodID(bitmapCls, "createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
jclass bitmapConfigCls = env->FindClass("android/graphics/Bitmap$Config");
jfieldID argb8888FID = env->GetStaticFieldID(bitmapConfigCls, "ARGB_8888",
"Landroid/graphics/Bitmap$Config;");
jobject argb8888Obj = env->GetStaticObjectField(bitmapConfigCls, argb8888FID);
jobject resultBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapMID,
pano.cols, pano.rows, argb8888Obj);
jobject ret = matToBitmap(env, pano, resultBitmap);
env->DeleteLocalRef(bitmapCls);
env->DeleteLocalRef(bitmapConfigCls);
env->DeleteLocalRef(argb8888Obj);
env->DeleteLocalRef(resultBitmap);
LOGI("stitchImages: success");
return ret;
}