What's the rule of binarysation for java.awt.image.BufferedImage - java

I can use this code
BufferedImage image = ImageIO.read(new File("toolbar.png"));
BufferedImage grayImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
Graphics graphics = grayImage.getGraphics();
graphics.drawImage(image, 0, 0, null);
graphics.dispose();
to convert a BufferedImage from TYPE_3BYTE_BGR to TYPE_3BYTE_BGR.
For example , the input:
the output:
I know that the colored pixel is turned into a white pixel or a black pixel based on a computation rule. What's it and what can I do to adjust it? Thanks for your help in advance.

The conversion as in your code above, is an implicit conversion happening, because the input image (RGB) has more colors that the destination (binary indexed color).
The "rule" for this conversion to binary color can be described as simply "pick the color in the destination that is closest to the one from the input ". As your only colors are black and white, all colors that have the average of R, G and B less than 50% will become black, the rest white. There is no way to "adjust" this, directly.
However, you can control the threshold, either directly or compute it from the input, for better images. See for example this blog for an example.

Related

Prevent image from changing color when written to a new TYPE_BYTE_INDEXED BufferedImage

I've been working on optimizing color PNG images for storage size by using the TYPE_BYTE_INDEXED BufferedImage to limit the number of unique colors each image can contain. I noticed that when I save an existing image to this format, the white (255,255,255) background color becomes an off-white color (252,252,252).
Is there any way to prevent this color shift from happening? I found this question which has a solution but it feels very hacky and is much slower than the Graphics2D implementation of drawImage.
Here's my code currently that's producing this issue. The original image is being loaded as a TYPE_3BYTE_BGR BufferedImage.
BufferedImage image = ImageIO.read(imageOutputPath.toFile());
BufferedImage indexedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
Graphics2D g = indexedImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
I know that this type of BufferedImage supports white as I've tried adding white margins around the image and it displays correctly but with the off-white image centered on the page. It just seems like the white in my image isn't converting correctly.
Any ideas and help would be greatly appreciated. Thanks!

How to create BufferedImage with BITMASK Transparency?

The BufferedImage class implements Transparency, which has three values:
OPAQUE means no transparency.
TRANSLUCENT means every pixel has an Alpha value between 0 and 1.
BITMASK means every pixel is either opaque or completely transparent.
I can check this value with the getTransparency() method. In my case, I have a PNG file with transparency:
pic = ImageIO.read(new File(filename));
int transparency = pic.getTransparency(); // returns Transparency.TRANSLUCENT
Now I read that images with Transparency.BITMASK can be drawn much faster than those with Transparency.TRANSLUCENT and in my case BITMASK would be enough. I would just color all transparent pixels in one specific color and then save the png without transparency.
Question: How to create a BufferedImage object, which has Transparency.BITMASK from an existing BufferedImage by just defining one color as transparent?
You mean something like...
// Create the buffered image
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage bimage = gc.createCompatibleImage(width, height, Transparency.BITMASK);
Things to note:
If you're PNG contains alpha values > 0 and < 255, they are likely to be rounded to either 0 or 1, possibly making the PNG appear jagged...
If you use Transparency.TRANSLUCENT instead, the color mode of the BufferedImage will be compatible with the GraphicsDevice, making it faster to render
I did an animated sequence a few years ago which was made up of 5 separate images, layered on top of each other and played back at separate speeds all on top of a transparent window...When I first tried running it the, the playback was terrible and jumped about the place.
After some playing around, I found that using Transparency.TRANSLUCENT to convert the images to a compatible color model for the GraphicsDevice worked like a charm...
Nothing wrong with the accepted answer, just providing an alternative for completeness (and I think it will work in headless mode). :-)
The transparency of a BufferedImage is controlled by its ColorModel.
So to create a BufferedImage with a given Transparency constant, you can use code like this:
// Use default RGB color space, no discrete alpha channel,
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorModel colorModel = new ComponentColorModel(cs, true, false, Transparency.BITMASK, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, w, h, 4, null);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);

How to set a pixel in a BufferedImage To Transparent

I'm Currently making a Game, and I need to set a couple Pixels of a BufferedImage (loaded using ImageIO.read) to be transparent in the fastest, best way.
I can't really find any other topic with this question, and If I do the answer Doesn't really help/fit What I need.
Thanks :)
Use Color(red, green, blue, alpha) with values 0-255. Where alpha is the opacity.
Buffed image being of type with an Alpha channel (RGBA, BGRA)
Color halfTransparant = new Color(0x76, 0x54, 0x32, 128);
With setRGB on arrays this still is not fast, you might access the raster data.
But why using dynamically generated images in time critical situations.
The smart way is to create your image with correct alpha from the start (using an image format with transparency, e.g. PNG and your favorite imaging application e.g. GIMP).
Otherwise you can directly alter pixels in the BufferedImage returned by ImageIO using the BufferedImage API: setRGB(int ARGB) and its bulk manipulation cousins in the BufferedImage's Raster.
One way that should work with all image types (as long as they support alpha), is very fast, and will not disable hardware acceleration of the image is:
BufferedImage image = ...; // From somewhere
Graphics2D g = destination.createGraphics();
try {
g.setComposite(AlphaComposite.Clear);
g.fillRect(x, y, w, h); // Area to make transparent
}
finally {
g.dispose();
}

Image operation in java

I'm trying to determine if an image is a black and white image, or if it has color (using Java). If it has color, I need to display a warning message to the user that the color will be lost.
Any suggestion for me?
It is possible. You can load images with ImageIO.
BufferedImage img = ImageIO.read(imageFile);
int rgb = img.getRGB(x,y);
Color color = new Color(rgb);
But still you need to create an algorithm that finds x,y bounds and check color of each location.
ImageIO will allow you to load the image, as someone else mentioned.
An optimization is possible which will help in a lot of cases: If the image is grayscale to begin with - as in, the file format says "this is a grayscale image", you can detect that using img.getColorModel().getColorSpace() to determine if you have an image whose color space only includes shades of gray.
For indexed color palettes (GIF files, for instance), you can iterate all of the colors in the color model and quickly determine if any have color (if the red/green/blue values are identical, there is no color).
For the rest, you'll have to sample pixels from the image (use some pattern to walk the image that skips around between positions so you don't spend a lot of time on black/white/gray borders before encountering color) and do something like
Color c = new Color(img.getRGB(x,y));
boolean isGray = c.getRed() == c.getGreen() && c.getRed() == c.getBlue();
...
At some size of image, it may become too expensive to iterate every pixel of the image to detect if even a single colored pixel exists, and you may want to assume there is some color and warn the user always.

Round corners on images using Java and JAI

We're using JAI (https://jai-imageio.dev.java.net/) to scale and crop images in Java. We would like to create round corners on our images. How do we do that?
The images are JPG and PNG. I would think it's easier to do this with JPGs?
The image is a PlanarImage from JAI
PlanarImage src = JAI.create(...,...);
which can be transformed to a java.awt.Graphics object
Has anyone done this before?
PNG supports a transparent alpha channel, but JPG does not. So, for JPG you would have to also pick a color to paint the "invisible" part of the rectangle for the rounded corners.
There is a class java.awt.geom.RoundRectangle2D available to do this:
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
RoundRectangle2D rr = new RoundRectangle2D.Float(50, 50, 200, 100, 10, 10);
g2d.draw(rr);
}
The Float() method of the class RoundRectangle2D takes six arguments:
The first two represent the location of the upper left corner.
Arguments 3 and 4 represent the width and height of the rounded rectangle.
The last two arguments represent the width and height of the arc drawn in the
corners.
So, draw a rounded rectangle that will just contain the image you want to have rounded corners and then either overlay or use a mask to get the desired effect.
What prevents you from drawing whatever corners you like onto the Graphics object obtained from the Image? I'm not really sure what your "round corners" are supposed to look like, but you can perform all reasonable paint operations on the Graphics object.

Categories