// // Created by dizi on 2024/7/12. // #include "include/image_tools.h" /** * 生成一个空的ARGB8888的Bitmap * @param env * @param width * @param height * @return */ jobject generate_empty_argb8888_bitmap(JNIEnv *env, int width, int height){ jclass bitmapClass = env->FindClass("android/graphics/Bitmap"); jmethodID constructorID = env->GetStaticMethodID(bitmapClass, "createBitmap","(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config"); jfieldID argb8888FieldID = env->GetStaticFieldID(bitmapConfigClass, "ARGB_8888", "Landroid/graphics/Bitmap$Config;"); jobject argb8888Value = env->GetStaticObjectField(bitmapConfigClass, argb8888FieldID); // 创建Bitmap对象 return env->CallStaticObjectMethod(bitmapClass, constructorID, width, height, argb8888Value); } /** * 将Bitmap转Mat * @param env * @param bitmap * @return */ cv::Mat convert_bitmap_to_mat(JNIEnv *env, jobject bitmap){ cv::Mat mat; // bitmap转mat // 获取Bitmap的信息 AndroidBitmapInfo info; AndroidBitmap_getInfo(env, bitmap, &info); // 根据Bitmap的图片格式决定type int type; if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888){ // LOGD("CV_8UC4"); type = CV_8UC4; } else if (info.format == ANDROID_BITMAP_FORMAT_RGB_565){ // LOGD("CV_8UC2"); type = CV_8UC2; } else{ LOGD("channel error"); return mat; } // 获取Bitmap的像素 void * pixels; AndroidBitmap_lockPixels(env, bitmap, &pixels); if (pixels == nullptr){ return mat; } // Bitmap转Mat mat = cv::Mat(info.height, info.width, type, pixels); // 解锁 AndroidBitmap_unlockPixels(env, bitmap); return mat; } /** * 将Mat转化为Bitmap * @param env * @param mat * @return */ jobject convert_mat_to_bitmap(JNIEnv *env, cv::Mat mat){ AndroidBitmapInfo info; void *pixels = 0; // 生成Bitmap jobject bitmap = generate_empty_argb8888_bitmap(env, mat.cols, mat.rows); try { CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0); CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 || info.format == ANDROID_BITMAP_FORMAT_RGB_565); CV_Assert(mat.dims == 2 && info.height == (uint32_t) mat.rows && info.width == (uint32_t) mat.cols); CV_Assert(mat.type() == CV_8UC1 || mat.type() == CV_8UC3 || mat.type() == CV_8UC4); CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0); CV_Assert(pixels); if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { cv::Mat tmp(info.height, info.width, CV_8UC4, pixels); if (mat.type() == CV_8UC1) { cvtColor(mat, tmp, cv::COLOR_GRAY2RGBA); } else if (mat.type() == CV_8UC3) { cvtColor(mat, tmp, cv::COLOR_RGB2RGBA); } else if (mat.type() == CV_8UC4) { mat.copyTo(tmp); } } else { cv::Mat tmp(info.height, info.width, CV_8UC2, pixels); if (mat.type() == CV_8UC1) { cvtColor(mat, tmp, cv::COLOR_GRAY2BGR565); } else if (mat.type() == CV_8UC3) { cvtColor(mat, tmp, cv::COLOR_RGB2BGR565); } else if (mat.type() == CV_8UC4) { cvtColor(mat, tmp, cv::COLOR_RGBA2BGR565); } } AndroidBitmap_unlockPixels(env, bitmap); } catch (const cv::Exception &e) { AndroidBitmap_unlockPixels(env, bitmap); LOGE("nMatToBitmap catched cv::Exception: %s", e.what()); jclass je = env->FindClass("org/opencv/core/CvException"); if (!je) je = env->FindClass("java/lang/Exception"); env->ThrowNew(je, e.what()); } catch (...) { AndroidBitmap_unlockPixels(env, bitmap); LOGE("nMatToBitmap catched unknown exception (...)"); jclass je = env->FindClass("java/lang/Exception"); env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}"); } return bitmap; } /** * 在MAT上绘制框 * @param mat 三通道 * @param left_top_x * @param left_top_y * @param right_bottom_x * @param right_bottom_y * @return */ cv::Mat draw_rect_on_mat(cv::Mat mat, int left_top_x, int left_top_y, int right_bottom_x, int right_bottom_y){ // 声明左上角和右下角坐标 cv::Point point_left_top(left_top_x, left_top_y); cv::Point point_right_bottom(right_bottom_x, right_bottom_y); // 定义颜色和线宽度 bgr cv::Scalar color(0, 0, 255); int thickness = 2; // 画框 cv::rectangle(mat, point_left_top, point_right_bottom, color, thickness); return mat; } cv::Mat draw_rect_text_on_mat(cv::Mat mat, int left_top_x, int left_top_y, int right_bottom_x, int right_bottom_y, char* text){ // 声明左上角和右下角坐标 cv::Point point_left_top(left_top_x, left_top_y); cv::Point point_right_bottom(right_bottom_x, right_bottom_y); // 定义颜色和线宽度 bgr cv::Scalar red (0, 0, 255); cv::Scalar green(0, 255, 0); int thickness = 2; if (strcmp(text, "未知商品") == 0){ // 画框 cv::rectangle(mat, point_left_top, point_right_bottom, red, thickness); // 绘制文字 cv::putText(mat, text, point_left_top, cv::HersheyFonts::FONT_HERSHEY_SIMPLEX, 1, red, thickness); } else{ // 画框 cv::rectangle(mat, point_left_top, point_right_bottom, green, thickness); // 绘制文字 cv::putText(mat, text, point_left_top, cv::HersheyFonts::FONT_HERSHEY_SIMPLEX, 1, green, thickness); } return mat; } /** * 在图片上绘制框和文字 */ extern "C" JNIEXPORT jobject JNICALL Java_com_wmdigit_core_opencv_OpencvRepository_drawRectAndTextOnBitmap(JNIEnv *env, jobject thiz, jobject bitmap, jobjectArray points, jobjectArray product_names) { // bitmap转mat cv::Mat mat = convert_bitmap_to_mat(env, bitmap); cv::Mat mat_bgr; cv::cvtColor(mat, mat_bgr, CV_RGBA2BGR); // jobjectarray转int** int ** points_array = convert_jobjectArray_to_intArrayArray(env, points); // jobjectArray转char** JStringInfo * jStringInfo = convert_jobjectArray_to_charArrayArray(env, product_names); // 框数量 int count = env->GetArrayLength(points); LOGD("COUNT:%d", count); // 遍历数组,绘制框和文字 for (int i = 0; i < count; i++) { LOGD("%d, %d, %d, %d, %s", points_array[i][0], points_array[i][1], points_array[i][2], points_array[i][3], jStringInfo[i].str); mat_bgr = draw_rect_text_on_mat(mat_bgr, points_array[i][0], points_array[i][1], points_array[i][2], points_array[i][3], jStringInfo[i].str); } // mat转Bitmap cv::Mat mat_rgb; cv::cvtColor(mat_bgr, mat_rgb, CV_BGR2RGB); jobject result = convert_mat_to_bitmap(env, mat_rgb); // 释放资源 freeStringArray(jStringInfo, count); mat.release(); mat_bgr.release(); mat_rgb.release(); freeIntArrayArray(points_array, count); return result; }