CoXier CoXier - 3 months ago 12
Java Question

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?

Answer

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.