OpenHarmony OpenCV应用样例开发

作者: 迪士尼在逃程序员 | 来源:发表于2024-09-10 13:50 被阅读0次

    背景

    OpenCV 介绍

    OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它由一系列的 C 函数和少量 C++ 类构成,同时提供 Python、Java 和 MATLAB 等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。

    OpenCV 具有极广的应用领域,它包括但不限于:

    • 人脸识别和物体识别:这是 OpenCV 的一项重要功能,应用在许多领域,如安全监控、交互设计等。
    • 图像和视频分析:如图像增强、图像分割、视频跟踪等。
    • 图像合成和 3D 重建:在图像处理和计算机视觉领域,OpenCV 可以用于创建 AR 或 VR 效果,生成 3D 模型等。
    • 机器学习:OpenCV 内置了大量的机器学习算法,可以用于图像分类、聚类等任务。
    • 深度学习:OpenCV 中的 dnn 模块提供了一系列深度学习模型的接口,用户可以加载预训练模型进行图像识别、目标检测等任务。

    本文主要介绍 OpenHarmony 如何用 opencvlib 进行应用样例开发

    应用开发

    创建 HAP
    • 通过 DevEcoStudio 创建项目“File->New->Create Project"创建一个工程
    • 工程创建完毕后,界面入口为 Index.ets
    引用 OpenCV lib 库
    • 引入 opencv 头文件库,放在 include 目录下
    • 引入 lib 库,放在 libs 目录下
    • 修改 CMAKE
    • 增加 common 头文件和 cpp 文件
        //
        // Created on 2024/3/5.
        //
        // Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
        // please include "napi/native_api.h".
    
        #ifndef OpencvSample_common_H
        #define OpencvSample_common_H
    
        #include <string>
        #include <stdio.h>
        #include <js_native_api.h>
        #include <js_native_api_types.h>
        #include <vector>
        #include "opencv2/opencv.hpp"
        #include "opencv2/imgcodecs/legacy/constants_c.h"
        #include "hilog/log.h"
        #include "napi/native_api.h"
        #include "rawfile/raw_file_manager.h"
        #include "rawfile/raw_file.h"
        #include "rawfile/raw_dir.h"
    
        #define GLOBAL_RESMGR (0xFFEE)
        constexpr int32_t RGB_565 = 2;
        constexpr int32_t RGBA_8888 = 3;
    
        constexpr int32_t STR_MAX_SIZE = 200;
        constexpr int32_t LONG_STR_MAX_SIZE = 1024;
        constexpr int32_t ERR_OK = 0;
        constexpr int8_t NO_ERROR = 0;
        constexpr int8_t ERROR = -1;
        constexpr uint8_t PARAM0 = 0;
        constexpr uint8_t PARAM1 = 1;
        constexpr uint8_t PARAM2 = 2;
        constexpr uint8_t PARAM3 = 3;
        constexpr uint8_t PARAM4 = 4;
        constexpr uint8_t PARAM5 = 5;
        constexpr uint8_t PARAM6 = 6;
        constexpr uint8_t PARAM7 = 7;
        constexpr uint8_t PARAM8 = 8;
        constexpr uint8_t PARAM9 = 9;
        constexpr uint8_t PARAM10 = 10;
        constexpr uint8_t PARAM11 = 11;
        constexpr uint8_t PARAM12 = 12;
    
        constexpr int32_t ARGS_ONE = 1;
        constexpr int32_t ARGS_TWO = 2;
        constexpr int32_t ONLY_CALLBACK_MAX_PARA = 1;
        constexpr int32_t ONLY_CALLBACK_MIN_PARA = 0;
    
        struct CallbackPromiseInfo {
            napi_ref callback = nullptr;
            napi_deferred deferred = nullptr;
            bool isCallback = false;
            int32_t errorCode = 0;
        };
    
        template <typename T> void FreeMemory(T *p) {
            if (p == nullptr) {
                return;
            }
            delete p;
            p = nullptr;
        }
    
        template <typename T> void FreeMemoryArray(T *p) {
            if (p == nullptr) {
                return;
            }
            delete[] p;
            p = nullptr;
        }
        #define NAPI_RETVAL_NOTHING
        #define NAPI_CALL_BASE(env, theCall, retVal)                                                                           \
            do {                                                                                                               \
                if ((theCall) != 0) {                                                                                          \
                    return retVal;                                                                                             \
                }                                                                                                              \
            } while (0)
    
        #define NAPI_CALL(env, theCall) NAPI_CALL_BASE(env, theCall, nullptr)
        #define NAPI_CALL_RETURN_VOID(env, theCall) NAPI_CALL_BASE(env, theCall, NAPI_RETVAL_NOTHING)
    
        extern bool GetMatFromRawFile(napi_env env, napi_value jsResMgr, const std::string &rawfileDir,
                                      const std::string &fileName, cv::Mat &srcImage);
        extern bool cvtMat2Pixel(cv::InputArray _src, cv::OutputArray &_dst, int code);
        extern napi_value NapiGetNull(napi_env env);
        extern uint32_t GetMatDataBuffSize(const cv::Mat &mat);
        extern bool CreateArrayBuffer(napi_env env, uint8_t *src, size_t srcLen, napi_value *res);
        extern napi_value NapiGetUndefined(napi_env env);
        extern napi_value GetCallbackErrorValue(napi_env env, int32_t errCode);
        extern napi_value NapiGetBoolean(napi_env env, const bool &isValue);
        extern uint32_t GetMatDataBuffSize(const cv::Mat &mat);
        extern void SetCallback(const napi_env &env, const napi_ref &callbackIn, const int32_t &errorCode,
                                const napi_value &result);
        extern void SetPromise(const napi_env &env, const napi_deferred &deferred, const int32_t &errorCode,
                               const napi_value &result);
        extern void ReturnCallbackPromise(const napi_env &env, const CallbackPromiseInfo &info, const napi_value &result);
        extern napi_value JSParaError(const napi_env &env, const napi_ref &callback);
        extern void PaddingCallbackPromiseInfo(const napi_env &env, const napi_ref &callback, CallbackPromiseInfo &info,
                                               napi_value &promise);
        extern bool WrapJsPixelInfoInfo(napi_env env, cv::Mat &outMat, napi_value &result);
    
        #endif //OpencvSample_common_H
    
    
    • 增加灰度转换方法
        using namespace std;
        using namespace cv;
        static const char *TAG = "[opencv_img2Gray]";
    
        napi_value Img2Gray(napi_env env, napi_callback_info info) {
            OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "Img2Gray Begin");
            napi_value result = NapiGetNull(env);
            size_t argc = 3;
            napi_value argv[3] = {nullptr};
    
            napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    
            size_t strSize;
            char strBuf[256];
            napi_get_value_string_utf8(env, argv[1], strBuf, sizeof(strBuf), &strSize);
            std::string fileDir(strBuf, strSize);
            OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "fileDir:%{public}s", fileDir.c_str());
    
            napi_get_value_string_utf8(env, argv[2], strBuf, sizeof(strBuf), &strSize);
            std::string fileName(strBuf, strSize);
            OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "fileName:%{public}s", fileName.c_str());
    
            Mat srcImage;
            if (!GetMatFromRawFile(env, argv[0], fileDir, fileName, srcImage)) {
                OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, "Get Mat from rawfile failed!.");
                return result;
            }
    
            Mat srcGray;
            cvtColor(srcImage, srcGray, COLOR_RGB2GRAY);
    
            // 將图像转换为pixelMap格式
            Mat outMat;
            cvtMat2Pixel(srcGray, outMat, RGBA_8888);
            OH_LOG_Print(LOG_APP, LOG_INFO, GLOBAL_RESMGR, TAG, "outMat size: %{public}d, cols:%{public}d, rows:%{public}d",
                         outMat.total(), outMat.cols, outMat.rows);
    
            napi_create_object(env, &result);
            bool retVal = WrapJsPixelInfoInfo(env, outMat, result);
            if (!retVal) {
                OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, "WrapJsInfo failed!.");
            }
    
            return result;
        }
    
    
    • 导出 //hello.cpp
        EXTERN_C_START
        static napi_value Init(napi_env env, napi_value exports)
        {
            napi_property_descriptor desc[] = {
                {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
                {"img2Gray", nullptr, Img2Gray, nullptr, nullptr, nullptr, napi_default, nullptr}
            };
            napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
            return exports;
        }
        EXTERN_C_END
    
    
    • 导出接口 //index.d.ts
        import resourceManager from '@ohos.resourceManager';
    
        export interface PixelInfo {
          rows: number;
          cols: number;
          buffSize: number;
          byteBuffer: ArrayBuffer;
        }
    
        export const add: (a: number, b: number) => number;
        export const img2Gray: (resmgr: resourceManager.ResourceManager, path: string, file: string) => PixelInfo;
    
    • 在页面添加交互 // index.ets
        Column() {
          Image(this.isGray ? this.imagePixelMap : $rawfile('lena.jpg'))
            .margin({ left: 24, right: 24 })
            .objectFit(ImageFit.Contain)
            .id('backBtn')
          }
          .width('100%')
          .height('60%')
          .alignItems(HorizontalAlign.Center)
          .justifyContent(FlexAlign.Start)
    
          Row() {
            Button($r('app.string.image_gray'), { type: ButtonType.Capsule })
              .backgroundColor(this.isGray ? Color.Gray : Color.Blue)
              .margin({ left: 24 })
              .width('30%')
              .id('imageGray')
              .enabled(this.isGray ? false : true)
              .onClick(() => {
                let pixelInfo: testNapi.PixelInfo = testNapi.img2Gray(getContext().resourceManager, '', 'lena.jpg');
                Logger.info(TAG, `pixelInfo buffSize: ${pixelInfo.buffSize}`);
    
                let opts: image.InitializationOptions = {
                  editable: true,
                  pixelFormat: this.pixelMapFormat,
                  size: { height: pixelInfo.rows, width: pixelInfo.cols }
                }
                image.createPixelMap(pixelInfo.byteBuffer, opts, (error, pixelmap) => {
                  if (error) {
                    Logger.error(TAG, `Failed to create pixelmap error_code ${error.code}`);
                  } else {
                    Logger.info(TAG, 'Succeeded in creating pixelmap.');
                    this.imagePixelMap = pixelmap;
                  }
                })
                this.isGray = true;
            })
    
            Button($r('app.string.image_recover'), { type: ButtonType.Capsule })
              .backgroundColor(Color.Blue)
              .width('30%')
              .id('imageRecover')
              .onClick(() => {
                this.isGray = false;
              })
            }
            .width('100%')
    
    
    • 展示

    总结

    • 可以用 nativec++ 方式导入 opencv 库直接开发应用,目前实现了一个简单接口,后面会实现场景应用

    • 目前只能做到可用,还有以下问题:

      • 需要 NAPI 接口进行 ArkTS 和 C/C++ 交互
      • 速度比较慢,是否可以通过 GPU 加速
      • Arkts 和 native 交互多,考虑转用 xcomponent 方式

    写在最后

    如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

    • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
    • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
    • 想要获取更多完整鸿蒙最新学习知识点,请移步前往小编:https://gitee.com/MNxiaona/733GH/blob/master/jianshu

    相关文章

      网友评论

        本文标题:OpenHarmony OpenCV应用样例开发

        本文链接:https://www.haomeiwen.com/subject/jnxqxjtx.html