Sobel Filter not functioning correctly

1.9k views Asked by At

I wrote a class for Sobel operator for edge detection, but when I use an example image, my edges are off. Greatly appreciate it if someone can help me with this.

import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.Raster;
import java.util.Arrays;

class SobelFilter {

private static final float[] sobel1 = { 1.0f, 0.0f, -1.0f};
private static final float[] sobel2 = { 1.0f, 2.0f,  1.0f};
private static final boolean[] sobelBoolean = {true, false};

private SobelFilter() {}
 
private static ConvolveOp getSobelX(boolean fs) {
    Kernel kernel = null;
    
    if (fs) {
        kernel = new Kernel(1, 3, sobel1);
    }
    else {
        kernel = new Kernel(3, 1, sobel2);
    }
    
    return new ConvolveOp(kernel, ConvolveOp.EDGE_ZERO_FILL, null);
}

private static ConvolveOp getSobelY(boolean fs) {
    Kernel kernel = null;
    
    if (fs) {
        kernel = new Kernel(1, 3, sobel2);
    }
    else {
        kernel = new Kernel(3, 1, sobel1);
    }
    
    return new ConvolveOp(kernel, ConvolveOp.EDGE_ZERO_FILL, null);
}

public static BufferedImage getSobelFilter(BufferedImage img) {
    int width = img.getWidth();
    int height = img.getHeight();
    int size = width * height;
    int[] x = new int[size];
    int[] y = new int[size];
    int[] pixelM = new int[size];
    //double[] pixelD = new double[size];

    BufferedImage sobelX = null;
    BufferedImage sobelY = null;
    
    for(boolean i : sobelBoolean) {
        sobelX = getSobelX(i).filter(img, null);
        sobelY = getSobelY(i).filter(img, null);
    }
    
    sobelX.getRaster().getPixels(0, 0, width, height, x);
    sobelY.getRaster().getPixels(0, 0, width, height, y);
    
    for(int i = 0; i < size; i++) {
        pixelM[i] = (int) Math.hypot(x[i], y[i]);
        //pixelD[i] = Math.atan2((double) y[i], (double) x[i]);
    }
    
    BufferedImage result = 
        new BufferedImage(width, height,
                          BufferedImage.TYPE_BYTE_GRAY);
    result.getRaster().setPixels(0, 0, width, height, pixelM);
    
    return result;
}
}

I used the valve picture from Wikipedia as an example.

Original test image

original

Expected result

expected

Actual result

Actual Result

1

There are 1 answers

7
Stefano Sanfilippo On BEST ANSWER

What you plotted is the Y component of the gradient. Consider this:

g2.drawImage(sobelX, null, 0, 0);
g2.drawImage(sobelY, null, 0, 0);

sobelX is hidden behind sobelY, so you only see the latter.

What you want is the norm of the gradient. You'll have to scan both images and calculate z = sqrt(x*x + y*y) for each pixel x of sobelX and its corresponding y of sobelY.

Pseudocode:

norm = image of the same size of sobelX and sobelY;

for (int x = 0; x < horizontal dimension of the image; ++x) {
    for (int y = 0; y < vertical dimension of the image; ++y) {
        xPixel = sobelX.pixelAt(x,y);
        yPixel = sobelY.pixelAt(x,y);
        norm.pixelAt(x,y) = Math.hypot(xPixel, yPixel);
    }
}

return norm;