cv::imdecode 图像从 JS 到 C++ (opencv, emscripten)

Posted

技术标签:

【中文标题】cv::imdecode 图像从 JS 到 C++ (opencv, emscripten)【英文标题】:cv::imdecode an image from JS to C++ (opencv, emscripten) 【发布时间】:2020-04-28 10:19:23 【问题描述】:

我正在尝试做的事情:

用户在 html 中加载图像 opencv 在 C++ 中接收该图像并对其进行一些处理。

我所做的和这里描述的差不多:

https://answers.opencv.org/question/222994/how-to-pass-image-data-from-javascript-to-opencv-c-webassembly/

这是 javascript 部分(来自给定链接的逐字记录):

var openFile = function (e) 
    const fileReader = new FileReader();
    fileReader.onload = (event) => 
        const uint8Arr = new Uint8Array(event.target.result);
        passToWasm(uint8Arr);
    ;
    fileReader.readAsArrayBuffer(e.target.files[0]);
;

function passToWasm(uint8ArrData) 
    // copying the uint8ArrData to the heap
    const numBytes = uint8ArrData.length * uint8ArrData.BYTES_PER_ELEMENT;
    const dataPtr = Module._malloc(numBytes);
    const dataOnHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, numBytes);
    dataOnHeap.set(uint8ArrData);
    // calling the Wasm function
    console.log(dataOnHeap.byteOffset);
    const res = Module._image_input(dataOnHeap.byteOffset, uint8ArrData.length);

    Module._free(dataPtr);

这里是 C++ 部分(几乎是逐字逐句):

int image_input(int offset, size_t size) //query image input
    
      uint8_t* pos;
       pos = reinterpret_castw<uint8_t*>(offset);
        cv::Mat raw_data = cv::Mat(1, size, CV_8UC1, pos);
         cout << raw_data << endl;
        img_object = cv::imdecode(raw_data, cv::IMREAD_GRAYSCALE);
        cout << img_object << endl;
    

当我使用一些 jpg 图像进行测试时,C++ 部分的 raw_data 和 JS 部分的 uint8ArrData 包含相同的信息:即 15253 个数字的列表,如

[255, 216, 255, 224, ..., 184, 227, 255, 217]

现在,一行

img_object = cv::imdecode(raw_data, cv::IMREAD_GRAYSCALE);

返回一个空矩阵[]

我做错了什么? 是不是要在JS端做一些预处理?

【问题讨论】:

【参考方案1】:

如提供的链接中所述,解决方案是使用画布并将像素列表(例如 4x480x640 数字)发送到 C++。

在 JS 方面你有

function sendCanvas(canvas, baseImage) 
    const context = canvas.getContext('2d');
    context.drawImage(baseImage, 0, 0, baseImage.width, baseImage.height);
    const  width  = canvas;
    const  height  = canvas;
    const imgData = context.getImageData(0, 0, canvas.width, canvas.height);
    const uint8ArrData = new Uint8Array(imgData.data);

     // pass the width and height too !!
    passToWasm(uint8ArrData, width, height);


 const baseImage = new Image();
    baseImage.src = 'test.jpg';
    baseImage.onload = () => sendCanvas(canvas, baseImage);

您必须调整函数 passToWasm 以获取另外两个参数(宽度和高度)并将它们传递给 C++ 调用。

然后在 C++ 端,使用以下信息直接将图像创建为 cv::Mat

void image_input(int offset, size_t size, int width, int height) 
  // People in the doc do not need this cast trick. Why ???
  uint8_t *pos;
  pos = reinterpret_cast<uint8_t *>(offset);
  auto cv_image = cv::Mat(width, height, CV_8UC4, pos);

矩阵cv_image 已准备好进行操作。

【讨论】:

请参阅我的答案以获得更好的“image_input”版本:***.com/a/63149827/5371582(函数是“get_image”)

以上是关于cv::imdecode 图像从 JS 到 C++ (opencv, emscripten)的主要内容,如果未能解决你的问题,请参考以下文章

cv2.imread与cv2.imdecode用法

PIL Image.open 和 cv2.imdecode 的区别

cv.imdecode和cv.imencode

opencv python图片编码解码

如何在Android模拟器上显示Mat图像?使用NDK

cv2.imread()返回none怎么解决?