OpenHarmony开发者论坛
标题:
OpenHarmony OpenCV应用样例开发
[打印本页]
作者:
深开鸿_王石
时间:
2024-3-5 13:46
标题:
OpenHarmony OpenCV应用样例开发
[md]### 背景
#### 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](
https://forums-obs.openharmony.c ... g5ovog5g9x1xzgh.png
"image.png")
* **工程创建完毕后,界面入口为Index.ets**
![image.png](
https://forums-obs.openharmony.c ... 44fjjiwvh4ve92h.png
"image.png")
##### 引用OpenCV lib库
* **引入opencv头文件库,放在include目录下**
![image.png](
https://forums-obs.openharmony.c ... 6fzxmswn5ppxwpp.png
"image.png")
* **引入lib库,放在libs目录下**
![image.png](
https://forums-obs.openharmony.c ... 7z0n3e0iqje0q33.png
"image.png")
* **修改CMAKE**
![image.png](
https://forums-obs.openharmony.c ... enz4yad41f6fz6n.png
"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](
https://forums-obs.openharmony.c ... 48fpf57hfm05ctp.png
"image.png")
### 总结
* **可以用nativec++方式导入opencv库直接开发应用,目前实现了一个简单接口,后面会实现场景应用**
* **目前只能做到可用,还有以下问题:**
> * **需要NAPI接口进行ArkTS和C/C++交互**
> * **速度比较慢,是否可以通过GPU加速**
> * **Arkts和native交互多,考虑转用xcomponent方式**
>
[/md]
作者:
mufeng521
时间:
2024-3-7 16:31
这个能分享一下demo看看吗?
作者:
深开鸿_王石
时间:
2024-3-7 17:50
lib库太大了就删了,只上传了代码,对应lib库需要从
https://gitee.com/openharmony-si ... releases/tag/v4.5.5
下载
作者:
mufeng521
时间:
2024-3-11 10:14
回复
深开鸿_王石
: 好的,感谢分享
作者:
九弓子
时间:
2024-3-20 18:45
大佬你好,最近在开发北向应用,想实现视频剪辑。
想仔细了解napi混合开发集成三方库的一些细节,您文章中的opencv是如何编译的呢?
我看到了您评论区回复的gitee链接中,有ffmpeg的三方库
https://gitee.com/openharmony-si ... ter/3rdparty/ffmpeg
这个库可以编译并应用么?
先感谢大佬啦···望回复~~
作者:
ouyangzhi
时间:
2024-3-21 11:17
target_link_libraries(entry PUBLIC
libace_napi.z.so
librawfile.z.so
libhilog_ndk.z.so
${OPENCV_LIB_PATH}/libopencv_core.so
opencv_imgcodecs
opencv_imgproc
opencv_photo
opencv_videoio
opencv_flann
opencv_features2d
opencv_calib3d
opencv_video
opencv_highgui
opencv_ml
opencv_stitching
opencv_dnn
opencv_objdetect
opencv_gapi)
想请问一下 ,libopencv_core.so后面的那些如:opencv_imgcodecs,opencv_imgproc等 是引用的libs目录底下的so吗?,这种是简写吗?
作者:
深开鸿_王石
时间:
2024-3-21 13:53
回复
ouyangzhi
: 是的,偷懒了,简写的,
作者:
深开鸿_王石
时间:
2024-3-21 13:57
回复
九弓子
: 可以是可以,但是包起来了,直接用可以用这个
https://gitee.com/openharmony-si ... r/thirdparty/FFmpeg
作者:
golden-king
时间:
2024-4-1 16:09
很感谢楼主分享这个opencv 应用样例开发 非常感谢
但是发现这个样例在本地无法运行 点击 按钮会报错退出应用
手机:华为畅享 60 Pro
系统:harmonyOS 4.0.0.132
需要修改两处报错才能运行,修改如下
1.common.cpp文件下
// if (!isFileExist) {
// OH_LOG_Print(LOG_APP, LOG_ERROR, GLOBAL_RESMGR, TAG, "Raw file directory not exist file: %{public}s.",
// fileName.c_str());
// OH_ResourceManager_CloseRawDir(rawDir);
// OH_ResourceManager_ReleaseNativeResourceManager(mNativeResMgr);
// return false;
// }
复制代码
这段代码需要注释 实际存在lena.jpg文件但是判断条件中多了rawfile// 前缀导致isFileExist一直为false
看了上下文发现注释这段检验代码问题不大(正常需要修改校验逻辑,但是本人c++不懂只能先注释)
2.index.ets文件下
@State isGray: Boolean = false;其中Boolean 需要修改为boolean (如果为大写Boolean 会导致isGray失去响应式)
@State isGray: boolean = false;
希望我的这些发现对大家跑代码的时候少走一点弯路 也非常感谢楼主分享相关代码
作者:
唯意
时间:
2024-7-16 17:03
大佬,灰度转换和到户接口没有在你给你文件里看到
作者:
Z_Men
时间:
2024-9-4 14:02
请教下,OpenHarmony 4.0 API10运行后,点击Gray后程序挂了。是不是不支持直接调用so
错误信息。
Module name:com.example.opencvsample
Version:1.0.0
Pid:17475
Uid:20010047
Lifetime: 0.000000s
Js-Engine: ark
page: pages/Index.js
Error message: Cannot read property img2Gray of undefined
SourceCode:
let pixelInfo: testNapi.PixelInfo = testNapi.img2Gray(getContext().resourceManager, '', 'lena.jpg');
^
Stacktrace:
at anonymous (entry/src/main/ets/pages/Index.ets:53:51)
作者:
宫保鸡丁
时间:
2024-10-29 10:30
回复
Z_Men
: 大佬解决了吗,我也出现了引用libentry.so的错误
欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/)
Powered by Discuz! X3.5