Read BMP with fake alpha with twelvemonkeys - java

I have a BMP file with fake alpha (similar to rgb32fakealpha.bmp from the bmp suite). That is, it has 32 bits per pixel, but doesn't have bitfields set, but uses the extra bits in each pixel as alpha anyways. And I'm using the twelvemonkeys library with the imageio-bmp plugin to read the BMP.
If I just do ImageIO.read(inputFile), then it doesn't preserve the transparency. Is there some way I can configure imageio, to use the alpha channel for this image?

Related

Should I convert BufferedImage.TYPE_4BYTE_ABGR to BufferedImage.TYPE_3BYTE_BGR?

I am working on image interpolation for which I am using bi-cubic interpolation to double the resolution of image in java using AffinedTransformOp.I used BufferedImage of TYPE_4BYTE_ABGR while doing up-scaling. When I tried to save back my upscale image using ImageIO.write then I found that openjdk does not support jpeg encoding for TYPE_4BYTE_ABGR so I converted this up-scaled image from TYPE_4BYTE_ABGR to TYPE_3BYTE_BGR. When I saved it in folder then found that the memory taken by this upscale image is way less(about half time) than the memory taken by original image.
So I assume that the original(input) image is represented by four channels ARGB while upscale(output) image is taking 3 channels RGB and that's why getting less memory.
Now my question is that should I use this conversion?
Is there some information that is getting lost?
Does quality of image remains same?
P.S: I've read from the documentation of ImageIO that when we convert ARGB to RGB than the alpha value gets premultiplied to RGB values and I think it should not affect the quality of the image.
I solved my problem and hope to share my answer. Actually the type of my original image was Grayscale and the color space of my original image was grey (meaning only one channel with 8 bits) with quality of 90.Problem arised when I used TYPE_4BYTE_ABGR for the upscaling instead of using TYPE_BYTE_GRAY. Secondly when you try to save this image in a file in jpeg format ImageIO.write uses compression of 75 by default so the image size will get small. You should use the compression factor which suits you or you should save it in PNG format. You can view information about your image by using identify -verbos image.jpg in linux and can see the color space, image type and quality etcYou can check this post to see how to set your compression quality manually in ImageIO.

How to decide which BufferedImage image type to use?

Java BufferedImage class has a long list of class variables known as the image type which can be used as an argument for the BufferedImage constructor.
However, Java docs did a minimal explanation what these image types are used for and how would it affect the BufferedImage to be created.
My question is:
How would an image type affect the BufferedImage to be created? Does it control the number of bits used to store various colors (Red,Green,Blue) and its transparency?
Which image type should we use if we just want to create
an opaque image
a transparent image
a translucent image
I read the description in the Java Doc many times, but just couldn't figure out how should we use it. For example, this one:
TYPE_INT_BGR
Represents an image with 8-bit RGB color components, corresponding to a Windows- or Solaris- style BGR color model, with the colors Blue, Green, and Red packed into integer pixels. There is no alpha. The image has a DirectColorModel. When data with non-opaque alpha is stored in an image of this type, the color data must be adjusted to a non-premultiplied form and the alpha discarded, as described in the AlphaComposite documentation.
Unless you have specific requirements (for example saving memory or saving computations or a specific native pixel format) just go with the default TYPE_INT_ARGB which has 8 bits per channel, 3 channels + alpha.
Skipping the alpha channel when working with 8 bits per channel won't affect the total memory occupied by the image since every pixel will be packed in an int in any case so 8 bits will be discarded.
Basically you have:
TYPE_INT_ARGB, 4 bytes per pixel with alpha channel
TYPE_INT_ARGB_PRE, 4 bytes per pixel, same as before but colors are already multiplied by the alpha of the pixel to save computations
TYPE_INT_RGB, 4 bytes per pixel without alpha channel
TYPE_USHORT_555_RGB and TYPE_USHORT_565_RGB, 2 bytes per pixel, much less colors, don't need to use it unless you have memory constraints
Then there are all the same kind of formats with swapped channels (eg. BGR instead that RGB). You should choose the one native of your platform so that less conversion should be done.

Java ImageIO Grayscale PNG Issue

I have a grayscale image ("lena" actually) which I want to experiment with. I got it as a 512x512 PNG file with 216 shades of gray.
What happens is, when I read it with Java ImageIO, like that:
String name = args[0];
File fi = new File(name);
BufferedImage img = ImageIO.read(fi);
I get a BufferedImage with only 154 colours! I only realized this, cause my processed images which looked sallow, lacking deep black.
Even more irritating, when I use XnView convert the PNG to a GIF, which is a lossless procedure in this case, read the GIF with above code, I get all 216 colours in my BufferedImage.
Is there some kind of documentation or description, what happens to my PNG, when ImageIO reads it? Are there settings to fix that? I did these experiments on a fairly recent JDK1.8. It is just that my trust in Java PNG support is lost now and I will use coloured PNG later.
Welcome to Java's "great" world of implicit color management!
For Java (at least ImageIO) everything internally is sRGB and it implicitely does color management, which often is quite counter-productive for what one actually wants to do.
For gray scale images, at least using ImageIO with most readers and at least for gray scale images without an embedded ICC profile (I haven't tested others yet), Java automatically "assigns" an ICC profile with WhitePoint=D50, Gamma=1.0. I stumbled across this as well.
And then, when you access pixels (I assume you use img.getRGB() or something similar?), you actually access sRGB values (Java's default color space on Windows).
The result is, when converting to sRGB, which has a gamma of ~2.2 (sRGB's gamma is actually a bit more complicated, but close to 2.2 overall), this affectively applies a gamma correction with (1/Gamma)=2.2 to the image, (a) making your image appear "light", and (b) due to the gamma correction from 256 to 256 discrete values, you also effectively loose some of your shades of gray.
You also can see the effect if you access your BufferedImage's data in different ways:
a) access the profile:
ColorSpace colorSpace = img.getColorModel().getColorSpace();
if ( colorSpace instanceof ICC_ColorSpace ) {
ICC_Profile profile = ((ICC_ColorSpace)colorSpace).getProfile();
if ( profile instanceof ICC_ProfileGray ) {
float gamma = ((ICC_ProfileGray)profile).getGamma();
system.out.println("Gray Profile Gamma: "+gamma); // 1.0 !
}
}
b) access some pixel values in different ways ...
//access sRGB values (Colors translated from img's ICC profile to sRGB)
System.out.println( "pixel 0,0 value (sRGB): " + Integer.toHexString(img.getRGB(0,0)) ); // getRGB() actually means "getSRGB()"
//access raw raster data, this will give you the uncorrected gray value
//as it is in the image file
Raster raster = image.getRaster();
System.out.println( "pixel 0,0 value (RAW gray value): " + Integer.toHexString(raster.getSample(0,0,0)) );
If your pixel (0,0) is not by chance 100% black or 100% white, you will see that the sRGB value is "higher" than the gray value, for example gray = d1 -> sRGB = ffeaeaea (alpha, Red, Green, Blue).
From my point of view, it does not only reduce your gray levels, but also makes your image lighter (about the same as applying gamma correction with 1/gamma value of 2.2). It would be more logical if Java for gray images without embedded ICC Profile either translates gray to sRGB with R=G=B=grayValue or would assign an ICC Gray Profile WhitePoint=D50, Gamma=2.2 (at least on Windows). The latter still would make you loose a couple of gray tones due to sRGB not being exactly Gamma 2.2.
Regarding why it works with GIF: the GIF format has no concept of "gray scales" or ICC profiles, so your image is a 256 color palette image (the 256 colors happen to be 256 shades of gray). On opening a GIF, Java assumes the RGB values are sRGB.
Solution:
Depending on what your actual use case is, the solution for you might be that you access the Raster data of each of your image's pixel (gray=raster.getSample(x,y,0)) and put it into an sRGB image setting R=G=B=gray. There might be an more elegant way, though.
Regarding your trust in java or PNG:
I'm struggling with java ImageIO in many ways due to the implicite color conversions it does. The idea is to have color management built in without the developers need much knowledge about color management. This works to some extend as long as you work with sRGB only (and your input is sRGB, too, or has no color profile and thus could legitimately considered to be sRGB). Trouble starts if you have other color spaces in your input images (for example AdobeRGB). Gray is another thing as well, especially the fact that ImageIO assumes an (unusual) Gray Profile with Gamma=1.0. Now to understand what ImageIO is doing, you don't only need to know your ABC in color management, but also need to figure out what java is doing. I didn't find this info in any documentation! Bottom line: ImageIO does things that certainly could be considered correct. It's just often not what you expect and you might to dig deeper to find out why or to change the behaviour if it isn't what you want to do.
Somehow you have converted the image from a linear grayscale (gamma=1.0) to an sRGB grayscale (gamma=1/2.2). This can be demonstrated with GraphicsMagick. Start with Lenna.png downloaded from Wikipedia, then remove the sRGB chunk to create lena.png, then
gm convert lena.png -colorspace gray -depth 8 -strip lena-gray.png
lena-gray.png has 216 colors
gm convert lena-gray.png -gamma 2.2 -depth 8 -strip lena-gray-gm22.png
lena-gray-gm22.png has 154 colors and appears washed-out or faded.
I'm using a recent beta of graphicsmagick (version 1.4) with libpng-1.6.17.
To count the colors I used ImageMagick:
identify -verbose file.png | grep Colors
I used
pngcheck -v file.png
to verify that Lenna.png contains IHDR, sRGB, IDAT, and IEND chunks, while lena-gray.png and lena-gray-gm22.png contain only IHDR, IDAT, and IEND chunks.

Java: How to work with CMYK image?

I know that RGB is for monitors and CMYK is for printing, but I want to work with CMYK without any conversions. I want to upload a CMYK image (jpeg) and print it. But when I used
com.sun.image.codec.jpeg.JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(is);
return decoder.decodeAsBufferedImage();
I got an inversed colors image. How can I get the same image in CMYK?
There reason your colors are inverted is that you have a special variant of a CMYK JPEG image, namely Adobe CYYK. The strange colors are due to an old Photoshop bug (CMYK values are inverted) that has now become a de-facto standard that's handled by most JPEG software (except Java).
A proper CMYK conversion (handling different variants, using proper color profile etc.) can be found in: https://stackoverflow.com/a/12132630/413337.

Java: Send BufferedImage through Socket with a low bitdepth

The title says enough I think.
I have a full quality BufferedImage and I want to send it through an OutputStream with a low
bitdepth. I don't want an algorithm to change pixel by pixel the quality, so it is still a full-quality.
So, the goal is to write the image (with the full resolution, full size) through the OuputStream which takes a very little number of bytes to write.
Thanks,
Martijn
You need to encode the image data into the format that has the right characteristics for your image. If it's 24-bit color and has a lot of colors and you want to lose no quality, you are probably stuck with PNG, but look into lossless JPEG 2000.
If you can lose some quality, then try
Lossy JPEG 2000 -- much smaller than JPEG for the same quality loss
reducing the number of colors and using a color mapped format
If the image has only gray or black and white data, make sure that you are encoding it as such (8-bit gray or 1-bit black and white). Then, make sure you use an encoder that is tuned for that kind of format (for example TIFF with Group 4 or JBIG2).
Another good option is to remove all of the unwanted meta-data from the image (or make sure that your encoder doesn't put any in).
If you want to stick with what's in Java, you probably have to use TIFF, PNG or JPEG -- there are third party image endcoders (for example, my company, Atalasoft, makes a .NET version of these advanced encoders -- there are Java vendors out there as well)
FYI: reducing bit depth usually means reducing quality (unless the reduction is meaningless). For example if I have a 24-bit color image, but all of the colors are gray (where R==G==B), then reducing to 8-bit gray does not lose quality. This is also true if your image has any collection of 256 different colors and you switch to a color mapped (indexed or paletted) format. You reduce the number of bytes and don't reduce quality.
Have a look at the java.util.zip package for all about compression:
http://java.sun.com/developer/technicalArticles/Programming/compression/
Though JPEG and PNG are already somehow compressed, using the Inflater class, you can reduce byte count while maintaining the exact same quality of your image. Compressing an already compressed format generally are not significant. If you group several images together, though, inflating them is significant (since inflation techniques recognize common patterns in object data, which reduce repetition and thus reduces byte count).

Categories