鸿蒙NEXT版实战开发:如何将C++侧接收的PixelMap转换成cv::mat格式?

往期鸿蒙全套实战文章必看:(附带鸿蒙全栈学习资料)


如何将C++侧接收的PixelMap转换成cv::mat格式

解决措施:

将ArkTS侧传到Native侧的PixelMap转换成cv::mat有两种方法:

  • 将PixelMap的arraybuffer转换成cv::mat。
  • 使用OH_PixelMap_AccessPixels获取PixelMap的内存地址,将这个内存地址中的数据转换为cv::mat。

上述两种方法都必须保证PixelMap的格式与opencv中mat的格式一致,否则会出现色彩的偏差。

示例代码如下:

ArkTS侧传递参数:

import cPixelMapToMat from 'libcpixelmaptomat.so';
import { BusinessError } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';

@Entry
@Component
struct Index {
  @State pixelMap: image.PixelMap | undefined = undefined

  async arrayBufferToMat() {
    if (this.pixelMap == undefined || this.pixelMap){
      let resourceManager = getContext(this).resourceManager
      let imageArray = await resourceManager.getMediaContent($r('app.media.sample10'));
      let pixelBuffer = new Uint8Array(imageArray).buffer as Object as ArrayBuffer
      console.info("pixelBuffer length: " + pixelBuffer.byteLength);
      let imageResource = image.createImageSource(pixelBuffer);
      let opts: image.DecodingOptions = {
        editable: true,
        desiredPixelFormat: image.PixelMapFormat.RGBA_8888
      }
      this.pixelMap = await imageResource.createPixelMap(opts);
    }

    const readBuffer: ArrayBuffer = new ArrayBuffer(this.pixelMap.getPixelBytesNumber()); // 获取pixelmap的arraybuffer
    console.info("readBuffer length: " + readBuffer.byteLength);
    this.pixelMap.readPixelsToBuffer(readBuffer).then(() => {
      console.info("No Error!")
    }).catch((err: BusinessError) => {
      console.error("Error! " + err.message)
    })
    const dir = getContext(this).filesDir;
    console.info('save path: ' + dir);
    cPixelMapToMat.arrayBufferToMat(this.pixelMap, readBuffer, dir);
  }

  async accessToMat(){
    if (this.pixelMap == undefined || this.pixelMap) {
      let resourceManager = getContext(this).resourceManager
      let imageArray = await resourceManager.getMediaContent($r('app.media.sample14'));
      let pixelBuffer = new Uint8Array(imageArray).buffer as Object as ArrayBuffer
      console.info("pixelBuffer length: " + pixelBuffer.byteLength);
      let imageResource = image.createImageSource(pixelBuffer);
      let opts: image.DecodingOptions = {
        editable: true,
        desiredPixelFormat: image.PixelMapFormat.RGBA_8888
      }
      this.pixelMap = await imageResource.createPixelMap(opts);
    }

    const dir = getContext(this).filesDir;
    console.info('save path: ' + dir);
    cPixelMapToMat.accessToMat(this.pixelMap, dir);
  }

  build() {
    Row() {
      Column() {
        Image(this.pixelMap)
          .width(200)
          .height(200)
        Button('ArrayBufferToMat')
          .onClick(() => {
            this.arrayBufferToMat();
          })

        Button('AccessToMat')
          .onClick(() => {
            this.accessToMat();
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

方案一:将arraybuffer转换成cv::mat代码如下:

#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 "hilog/log.h" 
#include <opencv2/opencv.hpp> 
#include <bits/alltypes.h> 
 
static napi_value ArrayBufferToMat(napi_env env, napi_callback_info info) { 
    size_t argc = 3; 
    napi_value args[3] = {nullptr}; 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    napi_value error; 
    napi_create_int32(env, -1, &error); 
    // 初始化PixelMap对象数据 
    NativePixelMap *native = OH_PixelMap_InitNativePixelMap(env, args[0]); 
    if (native == nullptr) { 
        return error; 
    } 
    // 获取图片信息 
    struct OhosPixelMapInfos pixelMapInfos; 
    if (OH_PixelMap_GetImageInfo(native, &pixelMapInfos) != IMAGE_RESULT_SUCCESS) { 
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Test", "Pure : -1"); 
        return error; 
    } 
    // 获取buffer 
    napi_value buffer = args[1]; 
    napi_valuetype valueType; 
    napi_typeof(env, buffer, &valueType); 
    if (valueType == napi_object) { 
        bool isArrayBuffer = false; 
        napi_is_arraybuffer(env, buffer, &isArrayBuffer); 
        if (!isArrayBuffer) { 
            napi_throw_error(env, "EINVAL", "Error"); 
        } 
    } 
    void *data = nullptr; 
    size_t byteLength = 0; 
    napi_get_arraybuffer_info(env, buffer, &data, &byteLength); 
    int32_t *saveBuffer = (int32_t *)(data); 
    // 转换成Mat 
    cv::Mat originMat(pixelMapInfos.height, pixelMapInfos.width, CV_8UC4, saveBuffer); 
    if (!originMat.data) { 
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Read Image", "Pure : -1"); 
        return error; 
    } 
    // 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[2], pathArray, 1024, &length); 
    std::string path(pathArray); 
    path += "/buffer.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; 
}

 方案二:使用OH_PixelMap_AccessPixels获取PixelMap的内存地址,将这个内存地址中的数据转换为cv::mat的代码如下:

static napi_value AccessToMat(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_value error; 
    napi_create_int32(env, -1, &error); 
    NativePixelMap *native = OH_PixelMap_InitNativePixelMap(env, args[0]); 
    if (native == nullptr) { 
        return error; 
    } 
    struct OhosPixelMapInfos pixelMapInfos; 
    if (OH_PixelMap_GetImageInfo(native, &pixelMapInfos) != IMAGE_RESULT_SUCCESS) { 
        OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Test", "Pure : -1"); 
        return error; 
    } 
    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 error; 
    } 
    // 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; 
}

在HarmonyOS开发中,针对图库支持硬解码的操作, 需要指定图像的内存空间大小,原本OH_PixelMap_AccessPixels()获取到图片的内存地址并锁定该内存,但是实际图像的大小需要lineStride对齐。所以在构造成mat时,要指定lineStride对齐,HarmonyOS项目开发中lineStride即rowSize。可以用OH_GetImageInfo获取到imageInfo,其中包含width,height,rowSize等信息。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值