Facing some error when converting cv mat to ImageData object in JavaScript

I’m trying to convert cv mat to ImageData object, but I get this error exception:

failed to construct 'ImageData': The input data length is not equal to (4 * width * height)

But that error appears only after converting the cv mat to a different color space, I use the code below to adjust image saturation:

 var request = e.data;
                var imgData = request.imgData;
                var value = request.value;
		let src = cv.matFromImageData(imgData);
		let hsv = new cv.Mat();
		let hsvCopy = new cv.Mat();

		cv.cvtColor(src,hsv, cv.COLOR_BGR2HSV);
		//create a clone of the hsv image to work on
		hsvCopy = hsv.clone();
		let channels = new cv.MatVector();
		cv.split(hsvCopy, channels);

		let hue = channels.get(0);
		let saturation = channels.get(1);
		let val = channels.get(2);
		for (var y = 0; y < saturation.rows; y++) 
		{
			for (var x = 0; x < saturation.cols; x++) 
			{
				saturation.ucharPtr(y,x)[0] = value;
			}
		}
		cv.merge(channels, hsvCopy);
		cv.cvtColor(hsvCopy, hsvCopy, cv.COLOR_HSV2BGR);

		imgData = new ImageData( new Uint8ClampedArray( hsvCopy.data ), hsvCopy.cols, hsvCopy.rows );
		
		postMessage({
			imgData: imgData
		});

		src.delete();
		hsv.delete();
		hsvCopy.delete();

The code above is inside a WebWorker

from docs :

Data is stored as a one-dimensional array in the RGBA order,

so, it needs 4 channels, but you only got 3.
your problem already starts here:

i think, the flag should be COLOR_RGBA2HSV , but it does not exist in the js API !!

same for COLOR_HSV2RGBA, which would be needed on the way back, to get a proper 4 channel ImageData object

as a workaround, for now, try to

  • use RGB2XXX and XXX2RGB flags (RGB order in js !!)
  • add a cv.cvtColor(hsvCopy, hsvCopy, cv.COLOR_RGB2RGBA); line,
    before getting the ImageData

p.s. i wish, i found a way to replace the for loops with a plain setTo() ,
but that’s WIP !

there are numpy-like libraries for javascript. I found numjs and tensorflow.js (which has also got a numpy-like interface). that ought to be the basis for OpenCV.js… I wonder if the GSoC person that made OpenCV.js didn’t know, or the libraries didn’t exist yet, or it was too complicated, or not deemed appropriate to depend on external libraries.

update:

as long as you want to set the saturation to a fixed value, you can replace the split / merge and the for loops with a plain setTo()

let src = cv.matFromImageData(imgData);
let hsv = new cv.Mat();
cv.cvtColor(src,hsv, cv.COLOR_RGB2HSV);
// empty mask
let mask = new cv.Mat(hsv.size(), hsv.type());
// set 2nd channel to "on"
mask.setTo([0,255,0,0]);
// set (constant) saturation channel, using the mask
hsv.setTo([0,value,0,0], mask)

let rgb = new cv.Mat();
cv.cvtColor(hsv, rgb, cv.COLOR_HSV2RGB);
cv.cvtColor(rgb, rgb, cv.COLOR_RGB2RGBA);

imgData = new ImageData( new Uint8ClampedArray( rgb.data ), rgb.cols, rgb.rows );

I thought about this after I looked into the opencv.js file precisely in “imshow” function,
I found that input mat parameter is being converted into a 4 channel using cv.cvtColor(hsvCopy, hsvCopy, cv.COLOR_RGB2RGBA);, which is very obvious.

Thank you so much for your answer. :slight_smile:

I will look into that, thank you