I am trying to make a react component to compare two images using the pixel match library and I am stuck on the output image. The outputCanvasRef is always null so in the compareImages
function it errors on the line with outputCanvas.width = image1.width;
import { useRef, useState } from "react";
import pixelmatch from "pixelmatch";
const Tab1 = () => {
const [image1, setImage1] = useState(null);
const [image2, setImage2] = useState(null);
const [difference, setDifference] = useState(null);
const canvas1Ref = useRef(null);
const canvas2Ref = useRef(null);
const outputCanvasRef = useRef(null);
const handleFileChange = (setImage) => (event) => {
const file = event.target.files[0];
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
const image = new Image();
image.src = fileReader.result;
image.onload = () => setImage(image);
};
};
const compareImages = async () => {
if (image1 && image2) {
const canvas1 = canvas1Ref.current;
const canvas2 = canvas2Ref.current;
const outputCanvas = outputCanvasRef.current;
canvas1.width = image1.width;
canvas1.height = image1.height;
canvas2.width = image2.width;
canvas2.height = image2.height;
outputCanvas.width = image1.width;
outputCanvas.height = image1.height;
const ctx1 = canvas1.getContext("2d");
const ctx2 = canvas2.getContext("2d");
const outputCtx = outputCanvas.getContext("2d");
outputCtx.scale(0.5, 0.5);
ctx1.drawImage(image1, 0, 0);
ctx2.drawImage(image2, 0, 0);
const diff = pixelmatch(
ctx1.getImageData(0, 0, image1.width, image1.height),
ctx2.getImageData(0, 0, image2.width, image2.height),
null,
image1.width,
image1.height
);
outputCtx.putImageData(diff, 0, 0);
setDifference(outputCanvas);
}
};
return (
<div>
<input type="file" onChange={handleFileChange(setImage1)} />
<input type="file" onChange={handleFileChange(setImage2)} />
<button onClick={() => { compareImages() }}>Compare Images</button>
<canvas ref={canvas1Ref} />
<canvas ref={canvas2Ref} />
{difference && <canvas ref={outputCanvasRef} />}
</div>
);
}
export default Tab1;
CodePudding user response:
Can use the spread operator like this {...props}
to pass a conditional ref
const itemProps = difference ? { ref: outputCanvasRef } : {};
<canvas {...itemProps} />
CodePudding user response:
There are several things that went wrong in your implementation:
- You need to render the
output canvas
without any condition so that it is always available for use in the comparison function. - the
pixelmatch
package requires that the images you want to compare always have the same width & height. To do this, you can use the width & height of the first image for both image 1 & image 2 canvas. - You need to pass the array buffer that is returned from
getImageData
call to thepixelmatch
. ThegetImageData
returnsImageData
object. You can access the array buffer like thisImageData.data
- You should pass an
ImageData
object'sdata
field to thepixelmatch
library's third parameter as argument so that the difference in pixels can be written in theImageData
object.
Solution
import { useRef, useState } from "react";
import pixelmatch from "pixelmatch";
const Tab1 = () => {
const [image1, setImage1] = useState(null);
const [image2, setImage2] = useState(null);
const [difference, setDifference] = useState(null);
const canvas1Ref = useRef(null);
const canvas2Ref = useRef(null);
const outputCanvasRef = useRef(null);
const handleFileChange = (setImage) => (event) => {
const file = event.target.files[0];
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
const image = new Image();
image.src = fileReader.result;
image.onload = () => setImage(image);
};
};
const compareImages = async () => {
if (image1 && image2) {
const canvas1 = canvas1Ref.current;
const canvas2 = canvas2Ref.current;
const outputCanvas = outputCanvasRef.current;
canvas1.width = image1.width;
canvas1.height = image1.height;
canvas2.width = image1.width;
canvas2.height = image1.height;
outputCanvas.width = image1.width;
outputCanvas.height = image1.height;
const ctx1 = canvas1.getContext("2d");
const ctx2 = canvas2.getContext("2d");
const outputCtx = outputCanvas.getContext("2d");
const diff = outputCtx.createImageData(image1.width, image1.height);
ctx1.drawImage(image1, 0, 0);
ctx2.drawImage(image2, 0, 0);
pixelmatch(
ctx1.getImageData(0, 0, image1.width, image1.height).data,
ctx2.getImageData(0, 0, image1.width, image1.height).data,
diff.data,
image1.width,
image1.height
);
outputCtx.putImageData(diff, 0, 0);
setDifference(diff);
}
};
return (
<div>
<input type="file" onChange={handleFileChange(setImage1)} />
<input type="file" onChange={handleFileChange(setImage2)} />
<button onClick={() => { compareImages() }}>Compare Images</button>
<canvas ref={canvas1Ref} />
<canvas ref={canvas2Ref} />
{<canvas ref={outputCanvasRef} />}
</div>
);
}
export default Tab1;