【鸿蒙南向开发】OpenHarmony 如何用 opencvlib 进行应用样例开发

199 篇文章 8 订阅
198 篇文章 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"创建一个工程

image.png

  • 工程创建完毕后,界面入口为 Index.ets

image.png

引用 OpenCV lib 库
  • 引入 opencv 头文件库,放在 include 目录下

    image.png

  • 引入 lib 库,放在 libs 目录下

image.png

  • 修改 CMAKE

image.png

  • 增加 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%')
    
    
  • 展示

image.png

总结

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

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

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

写在最后

●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
●更多鸿蒙最新技术知识点,请移步前往小编:https://gitee.com/

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值