#include "hilog/log.h" #include "napi/native_api.h" #include #include #include #include #include "PaperRecognizer.h" #include "ColorReader.h" #include "Types.h" #include #include #include #include #include using namespace cv; using namespace std; // ========== 错误码定义 ========== // 0 - 成功 // 1 - 未检测到定位块 // 2 - 定位块颜色识别失败 // 3 - 定位块配对失败(一大一小) // 4 - 试纸规格匹配失败 // 5 - 色块位置计算失败 // 6 - 检测到多条试纸 // 7 - 图像处理错误 // 100 - 未知错误 // =============================== // https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-image-18 // https://segmentfault.com/q/1010000045355419 // https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/image-pixelmap-operation-native // https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/image-pixelmap-operation-native-V5 //// 假设你有一个cv::Mat对象 // cv::Mat cvMat = ...; // 假设这是你的OpenCV图像 // //// 转换为PixelMap(伪代码) //// 注意:以下代码需要根据你的实际环境进行调整 // PixelMap* pixelMap = new PixelMap(); // //// 设置PixelMap的大小和格式 //// 例如,如果cvMat是RGBA格式,你可能需要设置PixelMap的pixelFormat为PixelFormat::RGBA_8888 // pixelMap->SetSize(cvMat.cols, cvMat.rows); //// 设置PixelFormat等(根据cvMat的类型调整) // //// 分配PixelMap的像素缓冲区(具体API根据HarmonyOS版本而定) //// 假设有一个SetPixelBytes方法来设置像素数据 // uint8_t* buffer = new uint8_t[cvMat.total() * cvMat.elemSize()]; // memcpy(buffer, cvMat.data, cvMat.total() * cvMat.elemSize()); // pixelMap->SetPixelBytes(buffer, cvMat.total() * cvMat.elemSize()); // //// 注意:上面的SetPixelBytes方法是伪造的,你需要根据HarmonyOS的API来实现 // //// 现在,pixelMap包含了cvMat的图像数据,可以在ArkTS中使用 //// ... 将pixelMap传递给ArkTS的UI组件 ... // //// 清理资源 // delete[] buffer; static napi_value AnalysisImage(napi_env env, napi_callback_info info) { napi_value udfVar = nullptr; napi_value pixelMap = nullptr; size_t argc = 2; napi_value args[2] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); // napi_value error; // napi_create_int32(env, -1, &error); NativePixelMap *native = OH_PixelMap_InitNativePixelMap(env, args[0]); if (native == nullptr) { return udfVar; } struct OhosPixelMapInfos pixelMapInfos; if (OH_PixelMap_GetImageInfo(native, &pixelMapInfos) != IMAGE_RESULT_SUCCESS) { OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Test", "Pure : -1"); return udfVar; } void *pixel; // 获取NativePixelMap对象的内存地址并锁定内存 OH_PixelMap_AccessPixels(native, &pixel); // 转换成Mat,注意对齐,所以要传入rowSize cv::Mat originMat(pixelMapInfos.height, pixelMapInfos.width, CV_8UC4, pixel, pixelMapInfos.rowSize); if (!originMat.data) { OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Read Image", "Pure : -1"); return udfVar; } OH_PixelMap_UnAccessPixels(native); cv::Mat saveMat; cv::cvtColor(originMat, saveMat, cv::COLOR_BGRA2RGBA); cv::Mat grayMat; cv::cvtColor(saveMat, grayMat, cv::COLOR_RGB2GRAY); struct OhosPixelMapCreateOps createOps; createOps.width = grayMat.cols; createOps.height = grayMat.rows; createOps.pixelFormat = 1; // 4 createOps.alphaType = 0; size_t bufferSize = createOps.width * createOps.height * 1; // 4 void *buff = malloc(bufferSize); if (buff == nullptr) { return udfVar; } memcpy(buff, grayMat.data, grayMat.total() * grayMat.elemSize()); int32_t res = OH_PixelMap_CreatePixelMap(env, createOps, (uint8_t *)buff, bufferSize, &pixelMap); free(buff); if (res != IMAGE_RESULT_SUCCESS || pixelMap == nullptr) { return udfVar; } return pixelMap; // opencv默认bgra或bgr,若pixelmap创建时未指定为这两种格式,需要进行格式转换 // cv::Mat saveMat; // cv::cvtColor(originMat, saveMat, cv::COLOR_BGRA2RGBA); // char pathArray[1024]; // size_t length; // napi_get_value_string_utf8(env, args[1], pathArray, 1024, &length); // std::string path(pathArray); // path += "/access.jpg"; // if (!cv::imwrite(path, saveMat)) { // OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Write Image", "Pure : -1"); // return error; // } // // napi_value res; // napi_create_int32(env, 1, &res); // return res; } // ImageLocation imgLoc; static napi_value Add(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value args[2] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); napi_valuetype valuetype0; napi_typeof(env, args[0], &valuetype0); napi_valuetype valuetype1; napi_typeof(env, args[1], &valuetype1); double value0; napi_get_value_double(env, args[0], &value0); double value1; napi_get_value_double(env, args[1], &value1); cv::Scalar scalar(10); scalar[0] = 1; scalar[1] = 10; scalar[2] = 100; scalar[3] = 1000; napi_value sum; napi_create_double(env, value0 + value1 + scalar[2], &sum); return sum; } // 全局 PaperRecognizer 实例 PaperRecognizer g_paperRecognizer; static napi_value GetPaperData(napi_env env, napi_callback_info info) { // Create a result object. napi_value object = nullptr; napi_create_object(env, &object); size_t length; Mat paperMat = g_paperRecognizer.getPaperImg(); length = paperMat.total() * paperMat.elemSize(); // parameter 1: byteLength napi_value byteLengthValue = nullptr; napi_create_uint32(env, length, &byteLengthValue); napi_set_named_property(env, object, "byteLength", byteLengthValue); // parameter 2: buffer napi_value bufferData; void *output_ptr = nullptr; napi_create_arraybuffer(env, length, &output_ptr, &bufferData); if (output_ptr != nullptr) { memcpy(output_ptr, paperMat.data, length); } else { OH_LOG_ERROR(LOG_APP, "Failed to allocate memory for ArrayBuffer"); return nullptr; } napi_set_named_property(env, object, "buffer", bufferData); // parameter 3: width napi_value widthValue = nullptr; napi_create_uint32(env, paperMat.cols, &widthValue); napi_set_named_property(env, object, "width", widthValue); // parameter 4: height napi_value heightValue = nullptr; napi_create_uint32(env, paperMat.rows, &heightValue); napi_set_named_property(env, object, "height", heightValue); return object; } // 计算自适应ROI大小(基于定位块高度) static int calculateAdaptiveROI(double locatorHeight) { // 使用定位块高度的0.6倍作为ROI半边长 int halfSize = static_cast(locatorHeight * 0.6); // 限制在5-15像素范围内(即ROI大小25-225像素) halfSize = std::max(5, std::min(15, halfSize)); return halfSize * 2 + 1; // 返回奇数边长(如11, 21, 31) } static napi_value SayHello(napi_env env, napi_callback_info info) { size_t argc = 4; napi_value args[4] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); napi_valuetype valuetype0; napi_typeof(env, args[1], &valuetype0); napi_valuetype valuetype1; napi_typeof(env, args[2], &valuetype1); napi_valuetype valuetype2; napi_typeof(env, args[3], &valuetype2); int width; napi_get_value_int32(env, args[1], &width); // width int height; napi_get_value_int32(env, args[2], &height); // height int format; napi_get_value_int32(env, args[3], &format); // 8 bool result = false; napi_status status = napi_is_arraybuffer(env, args[0], &result); if (status != napi_ok) { napi_throw_error(env, nullptr, "Node-API napi_is_arraybuffer fail"); return nullptr; } void *inputBuffer; size_t length; napi_get_arraybuffer_info(env, args[0], &inputBuffer, &length); // 转换 NV21 到 RGBA cv::Mat yuv_mat(height + height / 2, width, CV_8UC1, inputBuffer); cv::Mat rgb_full_mat;//rgb_mat cv::Mat t; cv::cvtColor(yuv_mat, t, cv::COLOR_YUV2RGBA_NV21); cv::rotate(t, rgb_full_mat, cv::ROTATE_90_CLOCKWISE);//rgb_mat //只使用原始图片的一半高度,进行识别,这样会提高识别效率和显示流畅性 cv::Rect rc_half; rc_half.width = rgb_full_mat.cols; rc_half.height = rgb_full_mat.rows / 2; Mat rgb_mat(rgb_full_mat, rc_half); // 转换为 BGRA 供 PaperRecognizer 使用 cv::Mat bgra_mat; cvtColor(rgb_mat, bgra_mat, COLOR_RGBA2BGRA); // 使用新代码进行识别 RecognitionResult recResult = g_paperRecognizer.recognize(bgra_mat); // 准备输出图像(标注图) cv::Mat outputImage; if (!recResult.annotatedImage.empty()) { cvtColor(recResult.annotatedImage, outputImage, COLOR_BGRA2RGBA); } else { outputImage = rgb_mat.clone(); } // 准备颜色结果字符串 string strResult = ""; uint32_t statusCode = 100; // 默认未知错误 if (recResult.success) { statusCode = 0; // 成功 // 使用 ColorReader 读取颜色(白平衡已应用) ColorReader colorReader; std::vector colors = colorReader.readColors( bgra_mat, recResult.colorBlockCenters, recResult.smallLocatorOnLeft, true); // 计算自适应ROI大小 double locatorHeight = 0; if (!recResult.colorBlockCenters.empty()) { // 使用定位块高度估算(从色块中心点间距推算) if (recResult.colorBlockCenters.size() >= 2) { float dy = std::abs(recResult.colorBlockCenters[1].y - recResult.colorBlockCenters[0].y); locatorHeight = dy * 0.8; // 估算定位块高度 } } int roiSize = calculateAdaptiveROI(locatorHeight); int halfRoi = roiSize / 2; // 重新读取颜色(使用自适应ROI) // 注意:PaperRecognizer::calculateColorBlockCenters 已保证 // centers_[0] 始终靠近小定位块,无论试纸如何放置 colors.clear(); for (size_t i = 0; i < recResult.colorBlockCenters.size(); i++) { const cv::Point2f& center = recResult.colorBlockCenters[i]; cv::Rect roi(center.x - halfRoi, center.y - halfRoi, roiSize, roiSize); // 边界检查 roi.x = std::max(0, roi.x); roi.y = std::max(0, roi.y); roi.width = std::min(roi.width, bgra_mat.cols - roi.x); roi.height = std::min(roi.height, bgra_mat.rows - roi.y); if (roi.width > 0 && roi.height > 0) { Mat roiMat(bgra_mat, roi); Mat rgbRoi; cvtColor(roiMat, rgbRoi, COLOR_BGRA2RGB); Mat hsvMat, labMat; cvtColor(rgbRoi, hsvMat, COLOR_RGB2HSV); cvtColor(rgbRoi, labMat, COLOR_RGB2Lab); Scalar avgHsv = mean(hsvMat); Scalar avgLab = mean(labMat); char buf[100]; sprintf(buf, "{\"id\":%d,\"value\":\"%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\"}", (int)i, avgHsv.val[0], avgHsv.val[1], avgHsv.val[2], avgLab.val[0], avgLab.val[1], avgLab.val[2]); if (i > 0) strResult += ","; strResult += buf; } } strResult = "[" + strResult + "]"; } else { // 映射错误码 statusCode = recResult.errorCode; if (statusCode == 0 && !recResult.success) { statusCode = 100; // 未知错误 } } length = outputImage.total() * outputImage.elemSize(); // Create a result object. napi_value object = nullptr; napi_create_object(env, &object); // parameter 1: byteLength napi_value byteLengthValue = nullptr; napi_create_uint32(env, length, &byteLengthValue); napi_set_named_property(env, object, "byteLength", byteLengthValue); // parameter 2: buffer napi_value bufferData; void *output_ptr = nullptr; napi_create_arraybuffer(env, length, &output_ptr, &bufferData); if (output_ptr != nullptr) { memcpy(output_ptr, outputImage.data, length); } else { OH_LOG_ERROR(LOG_APP, "Failed to allocate memory for ArrayBuffer"); return nullptr; } napi_set_named_property(env, object, "buffer", bufferData); // parameter 3: width napi_value widthValue = nullptr; napi_create_uint32(env, outputImage.cols, &widthValue); napi_set_named_property(env, object, "width", widthValue); // parameter 4: height napi_value heightValue = nullptr; napi_create_uint32(env, outputImage.rows, &heightValue); napi_set_named_property(env, object, "height", heightValue); // parameter 5: strLength napi_value strLengthValue = nullptr; napi_create_uint32(env, strResult.length(), &strLengthValue); napi_set_named_property(env, object, "strLength", strLengthValue); // parameter 6: strBuffer if (strResult.length() > 10) { napi_value bufferStrData; void *output_str_ptr = nullptr; napi_create_arraybuffer(env, strResult.length(), &output_str_ptr, &bufferStrData); if (output_str_ptr != nullptr) { memcpy(output_str_ptr, strResult.data(), strResult.length()); } else { OH_LOG_ERROR(LOG_APP, "Failed to allocate memory for ArrayBuffer 2"); return nullptr; } napi_set_named_property(env, object, "strBuffer", bufferStrData); } // parameter 7: locatorColor (定位块颜色) napi_value locatorColorValue = nullptr; napi_create_uint32(env, static_cast(recResult.locatorColor), &locatorColorValue); napi_set_named_property(env, object, "locatorColor", locatorColorValue); // parameter 8: paperType (试纸空间类型) napi_value paperTypeValue = nullptr; napi_create_uint32(env, static_cast(recResult.paperType), &paperTypeValue); napi_set_named_property(env, object, "paperType", paperTypeValue); // parameter 9: blurLevel (清晰度等级) napi_value blurLevelValue = nullptr; napi_create_uint32(env, static_cast(recResult.blurLevel), &blurLevelValue); napi_set_named_property(env, object, "blurLevel", blurLevelValue); // parameter 10: blurScore (清晰度分数) napi_value blurScoreValue = nullptr; napi_create_double(env, recResult.blurScore, &blurScoreValue); napi_set_named_property(env, object, "blurScore", blurScoreValue); // parameter 11: status code napi_value statusValue = nullptr; napi_create_uint32(env, statusCode, &statusValue); napi_set_named_property(env, object, "statusCode", statusValue); return object; } EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { {"analysisImage", nullptr, AnalysisImage, nullptr, nullptr, nullptr, napi_default, nullptr}, {"sayHello", nullptr, SayHello, nullptr, nullptr, nullptr, napi_default, nullptr}, {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}, {"getPaperData", nullptr, GetPaperData, nullptr, nullptr, nullptr, napi_default, nullptr}}; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports; } EXTERN_C_END static napi_module demoModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = "entry", .nm_priv = ((void *)0), .reserved = {0}, }; extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }