I’m writing a module that is supposed to create an rgb histogram for a given image. I’m analyzing an image onload. But for some reason I got the canvas
is undefined, when I define the canvas a line above. I completely cannot figure out why it doesn’t work. The error in the console point to line 22: const context = canvas.getContext('2d');
. Can you help?
import { useEffect, useRef, useState } from 'react';
import { histogram, max, scaleLinear, format } from 'd3';
import kitty from './kitty.jpg';
import { LinearAxisLeft } from '../../shared/LinearAxisLeft';
import { AxisBottomIntegers } from '../../shared/AxisBottomIntegers';
const histHeight = 200;
export const ImageAnalyzer = () => {
const canvasRef = useRef();
const [loaded, setLoaded] = useState(false);
let img = null;
let data = null;
useEffect(() => {
img = new Image();
img.src = kitty;
img.onload = () => {
// Create a canvas element to get pixel data
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0, img.width, img.height);
// Get pixel data
const imageData = context.getImageData(0, 0, img.width, img.height).data;
// Collect RGB values
const rgbValues = [];
for (let i = 0; i < imageData.length; i += 4) {
const red = imageData[i];
const green = imageData[i + 1];
const blue = imageData[i + 2];
rgbValues.push({ red, green, blue });
}
data = rgbValues;
setLoaded(true);
};
}, []); // Empty dependency array ensures useEffect runs only once
if (!loaded && !img) {
return <p>Loading Image...</p>;
}
const redBins = histogram().value((d) => d.red).domain([0, 255]).thresholds(255);
const greenBins = histogram().value((d) => d.green).domain([0, 255]).thresholds(20);
const blueBins = histogram().value((d) => d.blue).domain([0, 255]).thresholds(20);
const histRed = redBins(data);
const histGreen = greenBins(data);
const histBlue = blueBins(data);
// Assuming histRed, histGreen, and histBlue are arrays of bins
const allBins = [...histRed, ...histGreen, ...histBlue];
// Extract the counts from each bin
const counts = allBins.map((bin) => bin.length);
const maxCount = max(counts);
const yScale = scaleLinear().domain([0, maxCount]).range([0, histHeight]).nice();
const xScale = scaleLinear().domain([0, 255]).range([0, img.width]).nice();
return (
<>
<svg width={img.height} height={histHeight}>
<LinearAxisLeft innerWidth={img.width} tickFormat={format('')} yScale={yScale} />
<AxisBottomIntegers innerHeight={img.height} tickFormat={format('')} xScale={xScale} />
</svg>
<canvas ref={canvasRef} style={{ border: '1px solid red' }} />
</>
);
};