问题
What I'm trying to do:
- the user loads an image in html
- opencv receives that image in C++ and do some work on it.
What I've done is more or less the same as described here :
https://answers.opencv.org/question/222994/how-to-pass-image-data-from-javascript-to-opencv-c-webassembly/
Here is the javascript part (verbatim from the given link):
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);
}
And here is the C++ part (almost verbatim):
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;
}
When I test with some jpg images, the raw_data
on the C++ part and the uint8ArrData
on the JS part contain the same information: that is a list of 15253 numbers like
[255, 216, 255, 224, ..., 184, 227, 255, 217]
Now, the line
img_object = cv::imdecode(raw_data, cv::IMREAD_GRAYSCALE);
returns an empty matrix []
.
What do I do wrong ? Do I have to make some pre-processing on the JS side ?
回答1:
As said in the provided link, the solution is to use a canvas and send to C++ the list of pixels (4x480x640 numbers as an example).
On the JS side you have
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);
You have to adapt the function passToWasm
to take two more arguments (width and height) and pass them to the C++ call.
Then on the C++ side, you create the image as a cv::Mat
directly with these informations:
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);
}
The matrix cv_image
is ready for manipulations.
来源:https://stackoverflow.com/questions/61477860/cvimdecode-an-image-from-js-to-c-opencv-emscripten