This commit is contained in:
Ubuntu
2026-07-03 16:58:28 +08:00
commit 32b43f85bd
6240 changed files with 1987678 additions and 0 deletions
+462
View File
@@ -0,0 +1,462 @@
#include "hilog/log.h"
#include "napi/native_api.h"
#include <multimedia/image_framework/image_mdk.h>
#include <multimedia/image_framework/image_mdk_common.h>
#include <multimedia/image_framework/image_pixel_map_mdk.h>
#include <multimedia/image_framework/image_pixel_map_napi.h>
#include "PaperRecognizer.h"
#include "ColorReader.h"
#include "Types.h"
#include <bits/alltypes.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <vector>
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<int>(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<ColorResult> 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<uint32_t>(recResult.locatorColor), &locatorColorValue);
napi_set_named_property(env, object, "locatorColor", locatorColorValue);
// parameter 8: paperType (试纸空间类型)
napi_value paperTypeValue = nullptr;
napi_create_uint32(env, static_cast<uint32_t>(recResult.paperType), &paperTypeValue);
napi_set_named_property(env, object, "paperType", paperTypeValue);
// parameter 9: blurLevel (清晰度等级)
napi_value blurLevelValue = nullptr;
napi_create_uint32(env, static_cast<uint32_t>(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); }