Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

463 lines
16 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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); }