0

I'm trying to tackle a problem where an image is read. I need to exclude the Blue channel in the pixel so the it will result in an RG image.

This is what I have so far and not working D;

Would I need to create a new BufferedImage and apply each averaged pixel (Without the blue channel) to it? I'm not sure how to do it though.

4
  • So you want to take an image which is 2mX2n pixels and convert it to an image that is mXn and each pixel (in the final) is the average pixels of the original minus the blue component (i.e. the blue component will always be zero)? Commented Apr 13, 2014 at 4:43
  • Yes that is basically it! But I cannot seem to figure out how to do it. :S Commented Apr 13, 2014 at 5:02
  • A comment: averaging RGB values worsens image quality slightly. RGB values do not have a linear relation with brightness (a gamma curve has been applied). The result is darker than it should be. But doing it right is involved and much slower. Have a look if you're interested: 4p8.com/eric.brasseur/gamma.html Commented Apr 13, 2014 at 5:22
  • @ErwinBolwidt Averaging the RGB values is good enough to recreate an image with images (thumbnails) as each pixel fairly faithfully so I question the merits of doing it "correctly". Commented Apr 13, 2014 at 16:23

2 Answers 2

2

For starters, change your second-to-last line of code to this and see if it helps:

image.setRGB(i, j, (redData[i][j] << 16) | (greenData[i][j] << 8));

But with this, you will end up with an image that is the same size as the original, except that you drop the blue channel and you average over the other channels (sort-of like a blur), but only in the top left corner of each 2x2-block.

If you want to actually create an image of a quarter size, you do indeed need to create a new BufferedImage with half the width and height and then change the above mentioned line to:

newImage.setRGB(i/2, j/2, (redData[i][j] << 16) | (greenData[i][j] << 8));

Here's an updated version of your code that should do it (untested):

public static BufferedImage isolateBlueChannelAndResize(BufferedImage image) {
    int imageWidth  = image.getWidth();
    int imageHeight = image.getHeight();
    // Round down for odd-sized images to prevent indexing outside image
    if ((imageWidth  & 1) > 0) imageWidth --;
    if ((imageHeight & 1) > 0) imageHeight--;

    BufferedImage newImage = new BufferedImage(imageWidth/2, imageHeight/2, image.getType());

    for (int i = 0; i < imageWidth; i += 2) {
        for (int j = 0; j < imageHeight; j += 2) {
            int r1 = (image.getRGB(i  , j  ) >> 16) & 0xff;
            int r2 = (image.getRGB(i+1, j  ) >> 16) & 0xff;
            int r3 = (image.getRGB(i  , j+1) >> 16) & 0xff;
            int r4 = (image.getRGB(i+1, j+1) >> 16) & 0xff;
            int red = (r1 + r2 + r3 + r4 + 3) / 4;   // +3 rounds up (equivalent to ceil())

            int g1 = (image.getRGB(i  , j  ) >>  8) & 0xff;
            int g2 = (image.getRGB(i+1, j  ) >>  8) & 0xff;
            int g3 = (image.getRGB(i  , j+1) >>  8) & 0xff;
            int g4 = (image.getRGB(i+1, j+1) >>  8) & 0xff;
            int green = (g1 + g2 + g3 + g4 + 3) / 4;   // +3 rounds up (equivalent to ceil()) 

            newImage.setRGB(i/2, j/2, (red << 16) | (green << 8));
        }
    }

    return newImage;
}
Sign up to request clarification or add additional context in comments.

15 Comments

Thanks let me try implement it ;D
Did you change the line that says return image; to return newImage;? ;)
@Jared Agreed... I started programming in the 80's when the luxury of writing more readable code vs. higher performing code did not exist. So, I still feel a bit uneasy about throwing around a bunch of unnecessary objects and function calls. And especially if you try to process a bunch of huge images, speed does start to matter. Also, I just stuck with the approach that the OP used in the question...
@Jared No clue why they don't provide the methods... But I'm sure some Apache Commons library somewhere does. ;) My guess is: If you are using these functions to begin with (especially the array versions), it's because you need to do a lot of processing quickly. And then the last thing you want to do is replace a simple shift operation with a method call... In fact, I shouldn't even be calling getRGB(...) twice for the same pixel in my code above...
@Jared I wish Java had an @ Inline-annotation to force the compiler to do just that. But unfortunately it doesn't and you can't rely on the compiler doing anything there: stackoverflow.com/questions/2096361/… . So, depending on the JVM you are going to be running on, anything may happen. No idea, for example, how Android's Dalvik would handle it. And either way it will increase start-up time. But in general, you should easily be able to write yourself these static methods and stick them somewhere in your own library collection. ;)
|
0

I really think the easier way is to use the Color class:

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class AverageOutBlue {
    public static final float AVG_FACTOR = 1f / (4f * 255f);

    public static BufferedImage processImage(final BufferedImage src) {
        final int w = src.getWidth();
        final int h = src.getHeight();

        final BufferedImage out = new BufferedImage(w / 2, h / 2, src.getType());

        for (int i = 0; i < w; i += 2)
            for (int j = 0; j < h; j += 2) {
                final Color color = avgColor(src, i, j);
                out.setRGB(i / 2, j / 2, color.getRGB());
            }

        return out;
    }

    private static Color avgColor(final BufferedImage src, final int i,
        final int j) {

        final Color c1 = new Color(src.getRGB(i, j));
        final Color c2 = new Color(src.getRGB(i + 1, j));
        final Color c3 = new Color(src.getRGB(i + 1, j));
        final Color c4 = new Color(src.getRGB(i + 1, j + 1));

        final float r = (c1.getRed() + c2.getRed() + c3.getRed() + c4.getRed())
                            * AVG_FACTOR;
        final float g = (c1.getGreen() + c2.getGreen() + c3.getGreen() + 
                            c4.getGreen()) * AVG_FACTOR;

        return new Color(r, g, 0f);
    }
}

1 Comment

If you are dead set against using the Color class, then you can average the pixels given the int RGB value as you attempted to do above (with shifts and masks) and then return an integer instead of a Color object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.