Search code examples
javabufferedimagesobel

Sobel filter is strange


Recently I am preparing for Low Poly like this:

My first thing is to pick up some points of the input image.I want to try Sobel for detection and I have read soble article.

Now I meet problem where I don't know how to implement sobel filter.Folowing is my try:

First -> Get gray data from input image

 try {
        mImage = ImageIO.read(new File("D:\\Documents\\Pictures\\engine.png"));
    } catch (IOException e) {
        e.printStackTrace();
    }
    mWidth = mImage.getWidth();
    mHeight = mImage.getHeight();
    mNewImage = new BufferedImage(mWidth, mHeight, mImage.getType());
    mGrayData = new int[mWidth * mHeight];
    // get pixel data from image
    for (int i = 0; i < mHeight; i++) {
        for (int j = 0; j < mWidth; j++) {
            int rgb = mImage.getRGB(j, i);
            int r = (rgb >> 16) & 0xff;
            int g = (rgb >> 8) & 0xff;
            int b = rgb & 0xff;
            int grayLevel = (r + g + b) / 3;
            mGrayData[i * mWidth + j] = grayLevel;
        }
    }

Then -> Calculate gradient of every point

  private int[] getGradient() {
    int[] gradient = new int[mWidth * mHeight];
    for (int x=1;x<mWidth-1;x++){
        for (int y=1;y<mHeight-1;y++){
            int grayX = getGrayPoint(x+1,y-1)+2*getGrayPoint(x+1,y)+getGrayPoint(x+1,y+1)-
                    (getGrayPoint(x-1,y-1)+2*getGrayPoint(x-1,y)+getGrayPoint(x-1,y+1));
            int grayY = (getGrayPoint(x-1, y+1) + 2*getGrayPoint(x,y+1)+getGrayPoint(x+1,y+1))-
                    (getGrayPoint(x-1,y-1) + 2*getGrayPoint(x,y-1) + getGrayPoint(x+1,y-1));
            gradient[x+y*mWidth] = (int) Math.sqrt(grayX*grayX+grayY*grayY);
        }
    }
    return gradient;
}

Last -> Create new image with above gradient

 private void createImage() {
    int[] gradient = getGradient();

    for (int y = 1; y < mHeight - 1; ++y)
        for (int x = 1; x < mWidth - 1; ++x)
            mNewImage.setRGB(x,y,gradient[y * mWidth + x]);

    File file = new File("D:\\Documents\\Pictures\\engine3.png");
    if (!file.exists()) {
        file.getParentFile().mkdir();
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    try {
        ImageIO.write(mNewImage, "png", file);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Original image

What it should look like?

What I got

It's so strange that the color is a little blue

Edit 1:

Now I get:

why is it so black?


Solution

  • You set the color here:

    mNewImage.setRGB(x,y,gradient[y * mWidth + x]);
    

    You're not doing anything to ensure that the values in the red, green and blue channels are the same.

    The 32 bits of the integer are used to pack 4 8-bit channels, representing alpha, red, green and blue.

    0x01234567 = overall color
    
      01       = alpha
        23     = red
          45   = green
            67 = blue
    

    You are only setting a single value; this looks like it doesn't fall outside the range 0 to 255, so you are effectively only setting bits in the blue channel. (Presumably you're also using an RGB color model, rather than ARGB, which is why the pixels aren't fully transparent).

    Set the three channels to the same value (and clamp the value to be between 0-255, so you don't accidentally set bits in the other channels):

    int gray = Math.max(0, Math.min(gradient[...], 255);
    int color = (0xff << 24) | (gray << 16) | (gray << 8) | (gray);
              // Alpha         Red            Green         Blue
    

    Note that you may also want to scale your gradient values so that the point with maximum gradient is assigned the gray value 255:

    int maxGradient = 0;
    for (int g : gradient) { 
      maxGradient = Math.max(maxGradient, g);
    }
    

    then:

    int gray = Math.max(0, gradient) * 255 / maxGradient;
    

    (You no longer need to clamp it to be at most 255).

    Also, you might want to make your gradient array float[] or double[] (and use the same element type for maxGradient), so that you get less quantized output.