Saving bitmap change some colors? - java

Today I was working on a game in Java, a classic old MUD RPG, and think that was a good idea to create the map with Paint and saving it in bitmap.
The idea is that, I read the image pixel by pixel and based on the color, I load the properties of the cell.
I had no problem to reading it, but I find a strage thing. Some colors change their RGB value, by a little, and then the values that I expected for a particular type of cell is not matched anymore.
In the first place I thought it was my mistake, some errors in the code but opening paint again just give me the answer, which is that some colors (not just some pixel) are changed.
By changing the format from BMP to DIB it seems to work fine. But why saving a bitmap in bmp format change the colors?
This is how I read the map:
BufferedImage mapImg = ImageIO.read(new File(path));
for (int y = 0; y < col; y++) {
for (int x = 0; x < row; x++) {
int pixel = mapImg.getRGB(y, x);
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel) & 0xff;
}
}
And works fine.

Related

When do I know a new significant color change is reached between pixels?

I'm working on an Android app and would like to locate the edges of an image taken by the phone camera. I have figured that my best bet at locating these edges is by looking for the pixel in between two pixels that are two significant colors. For instance a shade of green and shade of black.
How would I come across this pixel? Is there a range of numbers that correlate with the various colors and there shades? I.e. 100-200 is red, 200-300 is blue, etc.?
First, you can use android.graphics.Bitmap.
If your image is from the device camera or device media, you can do this:
Bitmap bitmap = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), uri);
You may also want a scaled down version of the image so there are less total pixels in the Bitmap; this will help a lot with performance:
bitmap = createScaledBitmap(bitmap, bitmap.getWidth()/scaleFactor, bitmap.getHeight()/scaleFactor, false);
Second, to get the pixels you can do this:
int [] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
Third, to evaluate red, blue, green, black, white, etc, use the following convention:
black = 0x000000
red = 0xff0000
green = 0x00ff00
blue = 0x0000ff
white = 0xffffff
It is a matter of your exact needs what range you want to use qualify a particular color. I would suggest the a significant change would be calculated by the following:
final static int SIGNIFICANT_CHANGE_AMOUNT = 0xff;
int pixelA = pixels[i];
int pixelB = pixels[i+1];
boolean sigChange = false;
int changeInRed = Math.abs(((pixelA & 0xff0000) >> 16) - ((pixelB & 0xff0000) >> 16));
int changeInGreen = Math.abs(((pixelA & 0x00ff00) >> 8) - ((pixelB & 0x00ff00) >> 8));
int changeInBlue = Math.abs(((pixelA & 0x0000ff) >> 0) - ((pixelB & 0x0000ff) >> 0));
int overallChange = changeInRed + changeInGreen + changeInBlue;
if (overallChange > SIGNIFCANT_CHANGE_AMOUNT) {
sigChange = true;
}
You'll still have to write an algorithm to detect an area but I think if you follow the Flood Fill Algorithm wiki it will help a lot. I have used the queue-based implementation since the recursive one is not really feasible.
Also note that when you getPixels from a bitmap that has been used as an android view you will want to mask out the transparency byte... you can see this post

Using BufferedImage to create an image in Java

I am trying re-create an image using given 2D pixel arrays (rows, and columns) using the setRGB() method in BufferedImage.
Below is the follwing code:
BufferedImage bufferedImage = new BufferedImage(reconstructedJPEG[0].length, reconstructedJPEG.length, BufferedImage.TYPE_INT_RGB);
//loop through redPixels[][] array
for(int row=0; row<redPixels.length; row++){
for(int col=0; col<redPixels[0].length; col++){
//call setRGB() on redPixels
bufferedImage.setRGB(col, row, (redPixels[row][col]));
}
}
The code above works, but I am not sure how I can also set the green and blue pixel arrays?
Right now, its a very dull, dark red/purple image, that does no look like the original image.
Also, is there a another away I can form these arrays into a 1D image (which would be its raw pixels, red+green+blue components into one integer?
Thanks any help would be great.
Combine the individual color values for the 3 channels (red, green and blue) in one pixel using bitwise operators:
int rgb = (redValue & 0xff) << 16 | (greenValue & 0xff) << 8 | (blueValue & 0xff);
Then call setRGB with the composed value as parameter:
bufferedImage.setRGB(col, row, rgb);
The bitwise operation sentence can be cumbersome at first sight but it does the following:
Take every channel value and make it 8-bit range based value (0, 255) using the & 0xff mask (the format BufferedImage.TYPE_INT_RGB expects channels to be 8-bit values)
redValue & 0xff,
greenValue & 0xff,
blueValue & 0xff
Accommodates the channel values packing then into one 32 bit integer using the following layout:

What's the appropriate way to colorize a grayscale image with transparency in Java?

I'm making an avatar generator where the avatar components are from PNG files with transparency. The files are things like body_1.png or legs_5.png. The transparency is around the parts but not within them and the images are all grayscale. The parts are layering fine and I can get a grayscale avatar.
I would like to be able to colorize these parts dynamically, but so far I'm not having good luck. I've tried converting the pixel data from RGB to HSL and using the original pixel's L value, while supplying the new color's H value, but the results are not great.
I've looked at Colorize grayscale image but I can't seem to make what he's saying work in Java. I end up with an image that has fairly bright, neon colors everywhere.
What I would like is for transparency to remain, while colorizing the grayscale part. The black outlines should still be black and the white highlight areas should still be white (I think).
Does anyone have a good way to do this?
EDIT:
Here's an image I might be trying to color:
Again, I want to maintain the brightness levels of the grayscale image (so the outlines stay dark, the gradients are visible, and white patches are white).
I've been able to get a LookupOp working somewhat based on Colorizing images in Java but the colors always look drab and dark.
Here's an example of my output:
The color that was being used is this one (note the brightness difference): http://www.color-hex.com/color/b124e7
This is my lookupOp
protected LookupOp createColorizeOp(short R1, short G1, short B1) {
short[] alpha = new short[256];
short[] red = new short[256];
short[] green = new short[256];
short[] blue = new short[256];
//int Y = 0.3*R + 0.59*G + 0.11*B
for (short i = 0; i < 30; i++) {
alpha[i] = i;
red[i] = i;
green[i] = i;
blue[i] = i;
}
for (short i = 30; i < 256; i++) {
alpha[i] = i;
red[i] = (short)Math.round((R1 + i*.3)/2);
green[i] = (short)Math.round((G1 + i*.59)/2);
blue[i] = (short)Math.round((B1 + i*.11)/2);
}
short[][] data = new short[][] {
red, green, blue, alpha
};
LookupTable lookupTable = new ShortLookupTable(0, data);
return new LookupOp(lookupTable, null);
}
EDIT 2: I changed my LookupOp to use the following and got much nicer looking colors:
red[i] = (short)((R1)*(float)i/255.0);
green[i] = (short)((G1)*(float)i/255.0);
blue[i] = (short)((B1)*(float)i/255.0);
It seems what will work for you is something like this:
for each pixel
if pixel is white, black or transparent then leave it alone
else
apply desired H and S and make grayscale value the L
convert new HSL back to RGB
Edit: after seeing your images I have a couple of comments:
It seems you want to special treat darker tones, since you are not colorizing anything below 30. Following the same logic, shouldn't you also exempt from colorizing the higher values? That will prevent the whites and near-whites from getting tinted with color.
You should not be setting the alpha values along with RGB. The alpha value from the original image should always be preserved. Your lookup table algorithm should only affect RGB.
While you say that you tried HSL, that is not in the code that you posted. You should do your colorizing in HSL, then convert the resulting colors to RGB for your lookup table as that will preserve the original brightness of the grayscale. Your lookup table creation could be something like this:
short H = ??; // your favorite hue
short S = ??; // your favorite saturation
for (short i = 0; i < 256; i++) {
if (i < 30 || i > 226) {
red[i] = green[i] = blue[i] = i; // don't do alpha here
}
else {
HSL_to_RGB(H, S, i, red[i], green[i], blue[i])
}
}
Note: you have to provide the HSL to RGB conversion function. See my answer on Colorize grayscale image for links to source code.

Java: extract Alpha Channel from BufferedImage

I would like to extract the Alpha Channel from a bufferedimage and paint it in a separate image using greyscale. (like it is displayed in photoshop)
Not tested but contains the main points.
public Image alpha2gray(BufferedImage src) {
if (src.getType() != BufferedImage.TYPE_INT_ARGB)
throw new RuntimeException("Wrong image type.");
int w = src.getWidth();
int h = src.getHeight();
int[] srcBuffer = src.getData().getPixels(0, 0, w, h, (int[]) null);
int[] dstBuffer = new int[w * h];
for (int i=0; i<w*h; i++) {
int a = (srcBuffer[i] >> 24) & 0xff;
dstBuffer[i] = a | a << 8 | a << 16;
}
return Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(w, h, pix, 0, w));
}
I don't believe there's a single method call to do this; you would have to get all the image data and mask off the alpha byte for each pixel. So for example, use getRGB() to get the ARGB pixels for the image. The most significant byte is the alpha value. So for each pixel in the array you get from getRGB(),
int alpha = (pixel >> 24) & 0xFF;
You could grab the Raster from the BufferedImage, and then create a child Raster of this one which contains only the band you're interested in (the bandList parameter). From this child raster you can create a new BufferedImage with a suitable ColorModel which would only contain the grayscale aplpha mask.
The benefit of doing it this way instead of manually iterating over the pixels is that the runtime has chance to get an idea of what you are doing, and thus this might get accelerated by exploiting the hardware capabilities. Honestly I doubt it will be accelerated with current JVMs, but who knows what the future brings?

Faster way to set a (PNG) bitmap color instead of pixel by pixel

I have some png files that I am applying a color to. The color changes depending on a user selection. I change the color via 3 RGB values set from another method. The png files are a random shape with full transparency outside the shape. I don't want to modify the transparency, only the RGB value. Currently, I'm setting the RGB values pixel by pixel (see code below).
I've come to realize this is incredibly slow and possibly just not efficient enough do in an application. Is there a better way I could do this?
Here is what I am currently doing. You can see that the pixel array is enormous for an image that takes up a decent part of the screen:
public void foo(Component component, ComponentColor compColor, int userColor) {
int h = component.getImages().getHeight();
int w = component.getImages().getWidth();
mBitmap = component.getImages().createScaledBitmap(component.getImages(), w, h, true);
int[] pixels = new int[h * w];
//Get all the pixels from the image
mBitmap[index].getPixels(pixels, 0, w, 0, 0, w, h);
//Modify the pixel array to the color the user selected
pixels = changeColor(compColor, pixels);
//Set the image to use the new pixel array
mBitmap[index].setPixels(pixels, 0, w, 0, 0, w, h);
}
public int[] changeColor(ComponentColor compColor, int[] pixels) {
int red = compColor.getRed();
int green = compColor.getGreen();
int blue = compColor.getBlue();
int alpha;
for (int i=0; i < pixels.length; i++) {
alpha = Color.alpha(pixels[i]);
if (alpha != 0) {
pixels[i] = Color.argb(alpha, red, green, blue);
}
}
return pixels;
}
Have you looked at the functions available in Bitmap? Something like extractAlpha sounds like it might be useful. You an also look at the way functions like that are implemented in Android to see how you could adapt it to your particular case, if it doesn't exactly meet your needs.
The answer that worked for me was a write up Square did here Transparent jpegs
They provide a faster code snippet for doing this exact thing. I tried extractAlpha and it didn't work but Square's solution did. Just modify their solution to instead modify the color bit and not the alpha bit.
i.e.
pixels[x] = (pixels[x] & 0xFF000000) | (color & 0x00FFFFFF);

Categories

Resources