So I have been trying to implement a Sobel filter in JavaScript using pixel manipulation.
When using it on this image:
The result I got looks like this:
As you can see it does not seem to detect horizontal edges. Where is the mistake in the code?
Here is my code:
function sobel() {
let filter1 = [1, 0, -1, 2, 0, -2, 1, 0, -1];
let filter2 = [1, 2, 1, 0, 0, 0, -1, -2, -1];
let w = canvas.width;
let h = canvas.height;
toGreyScale();
let pixel = context.getImageData(0, 0, w, h);
let filteredImg = context.createImageData(w, h);
for (let y = 0; y < h; y ) {
for (let x = 0; x < w; x ) {
//
let Gx = filter1[0] * pixel.data[(w * y -1 x - 1) * 4]
filter1[1] * pixel.data[(w * y -1 x) * 4]
filter1[2] * pixel.data[(w * y -1 x 1) * 4]
filter1[3] * pixel.data[(w * y x - 1) * 4]
filter1[4] * pixel.data[(w * y x) * 4]
filter1[5] * pixel.data[(w * y x 1) * 4]
filter1[6] * pixel.data[(w * y 1 x - 1) * 4]
filter1[7] * pixel.data[(w * y 1 x) * 4]
filter1[8] * pixel.data[(w * y 1 x 1) * 4];
let Gy = filter2[0] * pixel.data[(w * y -1 x - 1) * 4]
filter2[1] * pixel.data[(w * y -1 x) * 4]
filter2[2] * pixel.data[(w * y -1 x 1) * 4]
filter2[3] * pixel.data[(w * y x - 1) * 4]
filter2[4] * pixel.data[(w * y x) * 4]
filter2[5] * pixel.data[(w * y x 1) * 4]
filter2[6] * pixel.data[(w * y 1 x - 1) * 4]
filter2[7] * pixel.data[(w * y 1 x) * 4]
filter2[8] * pixel.data[(w * y 1 x 1) * 4];
let G = Math.sqrt(Gx * Gx Gy * Gy); // compute total magnitude
G = G/4; // normalize value;
filteredImg.data[(w * y x) * 4] = G;
filteredImg.data[(w * y x) * 4 1] = G;
filteredImg.data[(w * y x) * 4 2] = G;
filteredImg.data[(w * y x) * 4 3] = 255;
}
}
context.putImageData(filteredImg, 0, 0, 0, 0, w, h);
}
CodePudding user response:
When the code says, for example: (w * y -1 x - 1) * 4
, you're not getting the pixel to the upper left, you're getting the current row at x-2. The expression should be:
// expression for pixel that's at (x-1, y-1) over-parenthesized for clarity
((w * (y-1)) (x-1)) * 4
For clarity, put the index computation in one place, like:
const pixelDataAt = (x,y) => pixel.data[4 * (w*y x)];
Then call this in the loop with the simple convolution neighborhood:
let Gx = filter1[0] * pixelDataAt(x-1, y-1)
filter1[0] * pixelDataAt(x , y-1) //...
/* where the convolution neighborhood for each filter is
(x-1, y-1), (x, y-1), (x 1, y-1)
(x-1, y ), (x 1, y )
(x-1, y 1), (x, y 1), (x 1, y 1)
*/
This will make the code simpler to read and debug, and, if needed, simpler to optimize by caching redundant calculations.