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.
Related
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.
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.
Is there any way to know in advance if an image used as an input to a system is in RGB or BGR format?
I am using OpenCV with java API and I would like to convert an input image into grayscale or L*a*b* color space, but in OpenCV you have to specify first whether the image you want to convert is in RGB or BGR.
The type of the image I am using is either .jpg or .png.
If you are reading in the image file, or you have access to the code that reads in the file, know it is:
BGR order if you used cv2.imread()
RGB order if you used mpimg.imread() (assuming import matplotlib.image as mpimg)
If you don't know how the file was opened, the accepted answer BufferedImage is great for Java.
I would like to find a way to do the same in Python!
When you use opencv (imread, VideoCapture), the images are loaded in the BGR color space.
If your image is a BufferedImage then you can ask for his type with getType(), and test against the several constants (see: BufferedImage).
We find that there always exists some RGB accuracy problems whlie getting RGB from a jpeg file using bufferedImage in Java(ImageIO.read(file))..
Does anyone know some alternatives solutions?
What do you mean by accuracy problems? Does the JPEG you save and the one you load with Java look different? How are the colors different?
I'm going to go on a limb here and assume that you are using a JPEG with a color profile, meaning that you are reading RGB values just right but, your original application takes also the color profile and uses it to map the RGB values extracted from your JPEG into the RGB values that get shown on your monitor. Have a look with a good image viewer, and see what the embedded profile in your image is (look for things like sRGB or AdobeRGB) and see if your problems are consistent with the different color profiles
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).