Im using xuggler to put a transparent mask in a video.
Im trying to open a valid png using the ImageIO.read(), but when it renders, theres always a white backgroun in my picture.
This is my code for reading.
url = new URL(stringUrl);
imagem = ImageIO.read(url);
boolean hasAlpha = imagem.getColorModel().hasAlpha();
This boolean is always false.
And in Xuggler when i make the render
mediaReader
.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR);
What im doing wrong?
The "problem" with your original image file, is that it is a 24 bit RGB (3 channel) PNG, with a tRNS chunk.
The tRNS chunk specifies a color (255, 255, 255, or white, in this case) that is to be replaced with fully transparent pixels. In effect, applying this transparency works kind of like a bit mask, where all pixels are either fully transparent or fully opaque. However, being an "ancillary" (optional, or non-critical) chunk according to the PNG specificiation, PNG decoders may choose to ignore this information.
Unfortunately, it seems like the default PNGImageReader (the "PNG plugin") for ImageIO does not apply this bit mask, and instead keeps the image in fully opaque RGB (which is still spec compliant behavior).
Your second image file is a 32 bit RGBA (4 channels incl. alpha). In this case, any reader must (and does) decode all channels, including the full 8 bit alpha channel. ImageIO's PNGImageReader does decode this with the expected transparency.
PS: It is probably possibly to "fix" the transparency from your first image, using the meta data.
The meta data for your image contains the following information in the native PNG format:
<javax_imageio_png_1.0>
....
<tRNS>
<tRNS_RGB red="255" green="255" blue="255"/>
</tRNS>
</javax_imageio_png_1.0>
After parsing this, you could convert the image, by first creating a new 4 channel image (like TYPE_4BYTE_ABGR), copying the R, G and B channels to this new image, and finally set the alpha channel to either 0 for (all pixels that match the transparent color), or 255 (for all others).
PPS: You probably want to perform these operations directly on the Rasters, as using the BufferedImage.getRGB(..)/setRGB(..) methods will convert the values to an sRGB color profile, which may not exactly match the RGB value from the tRNS chunk.
There you go! :-)
Related
I need to read and process a large number of PNG files that are grayscale. By that I mean that if they are opened in either Photoshop or GIMP, the image mode is Grayscale - not an RGB image with grayscale values.
ImageIO does not seem to achieve this. It appears to treat all image files as sRGB. This mangles grayscale values. I need to read and process these PNG files where (in my code) each pixel has exactly the same value as if I had opened the grayscale file in Photoshop or GIMP. Does anybody know of some open source software that can achieve this, please? Or better how to achieve this using ImageIO.
Additional Information:
I am using getRGB() on a BufferedImage. The underlying pixel in the image file is 0x86. I understand that this does not necessarily correspond to an ARGB pixel containing 0xFF868686, as this depends upon luminance/gamma. However, in the absence of a getter with a gamma type argument, I would have expected the default mapping to be to ARGB=0xFF868686. If I use GIMP or Photoshop to convert a grayscale image containing a pixel with the value of 0x86 to RGB then the pixel becomes 0xFF868686. This is the obvious default.
However, ImageIO seems to use a weird gamma (whether you like it or not) with grayscale image files that makes the grayscale pixels very, very light after mapping to ARGB. In this case, 0x86 maps to 0xFFC0C0C0. This is not only very light, it can also result in considerable data loss as many grayscale values can be mapped to fewer ARGB values. The only time that this distortion will not result in data loss is for very dark grayscale images. An appropriate Gamma is context dependent, different physical media will distort luminance differently. However, in the absence of a context, the mapping: 0x86 --> 0xFF868686 makes most sense - witness the choices made for GIMP and Photoshop.
Leaving the getRGB() issue to one side, having loaded the grayscale image (using ImageIO.read( imageFile )), the getType() method of BufferedImage returns Type=0 (Custom) and not Type=10 (TYPE_BYTE_GRAY) as I would have expected.
In short, ImageIO does not seem to provide a nice and simple high level way of reading and manipulating existing grayscale images. I had hoped not to have to mess around under the covers with Rasters, ICC, sampling etc. Nor do I want to have to physically convert all the grayscale image files to RGB. All I wanted was an API load() method for BufferedImage that works just like open file does in GIMP or Photoshop. I have not been able to achieve this. I am hoping that this is my ignorance and not a limitation of Java ImageIO.
Possible Solution:
After digging around I have the following to offer as a possible technique for accessing the underlying grayscale values:
final File imageFile = new File( "test.png" );
final BufferedImage image = ImageIO.read( imageFile );
// --- Confirm that image has ColorSpace Type is GRAY and PixelSize==16
final Raster raster = image.getData();
// --- Confirm that: raster.getTransferType() == DataBuffer.TYPE_BYTE
for( int x=0, xLimit=image.getWidth(); x < xLimit; x++ ) {
for( int y=0, yLimit=image.getHeight(); y < yLimit; y++ ) {
final Object dataObject = raster.getDataElements( x, y, null );
// --- Confirm that dataObject is instance of byte[]
final byte[] pixelData = (byte[]) dataObject;
// --- Confirm that: pixelData.length == 2
final int grayscalePixelValue = pixelData[0] & 0xFF;
final int grayscalePixelAlpha = pixelData[1] & 0xFF;
// --- Do something with the grayscale pixel data
}
}
The javadoc is not great, so I cannot guarantee that this is correct, but it seems to work for me.
In case you want to try a third party (mine) lib: https://github.com/leonbloy/pngj/
If you are certain that the image is plain grayscale (8 bits, no alpha, no palette, no profile), it's quite simple:
PngReaderByte pngr = new PngReaderByte(new File(filename)); //
if (pngr.imgInfo.channels!=1 || pngr.imgInfo.bitDepth != 8 || pngr.imgInfo.indexed)
throw new RuntimeException("This method is for gray images");
for (int row = 0; row < pngr.imgInfo.rows; row++) {
ImageLineByte line = pngr.readRowByte();
byte [] buf = line.getScanlineByte();
// do what you want
}
pngr.end();
Java's ImageIO is known to be broken on images with a grayscale palette.
Java ImageIO Grayscale PNG Issue
javax.imageio.ImageIO reading incorrect RGB values on grayscale images
My batch jpg resizer works with color images, but grayscale ones become washed out
Wrong brightness converting image to grayscale in Java
Oracle: JDK-5051418 : Grayscale TYPE_CUSTOM BufferedImages are rendered lighter than TYPE_BYTE_GRAY
Oracle: JDK-6467250 : BufferedImage getRGB(x,y) problem
i am performing operations on a grayscale image, and the resultant image of these operations has the same extension as the input image. for an example if the input image is .jpg or .png the output image is either .jpg or .png respectively.
and I am converting the image into grayscale as follows:
ImgProc.cvtColor(mat, grayscale, ImgProc.COLOR_BGR2GRAY),
and I am checking the channels count using:
.channels()
the problem is when I wnat to know how many channels the image contain, despit it is a grayscale image, i always receive umber of channels = 3!!
kindly please let me know why that is happening
The depth (or better color depth) is the number of bits used to represent a color value. a color depth of 8 usually means 8-bits per channel (so you have 256 color values - or better: shades of grey- per channel - from 0 to 255) and 3 channels mean then one pixel value is composed of 3*8=24 bits.
However, this also depends on nomenclature. Usually you will say
"Color depth is 8-bits per channel"
but you also could say
"The color depth of the image is 32-bits"
and then mean 8 bits per RGBA channel or
"The image has a color depth of 24-bits"
and mean 8-bits per R,G and B channels.
The grayscale image has three channels because technically it is not a grayscale image. It is a colored image with the same values for all the three channels (r, g, b) in every pixel. Therefore, visually it looks like a grayscale image.
To check the channels in the image, use-
img.getbands()
I have a color indexed TIFF image (8-bits) and I want to convert it to a RGB 24-bits image (not indexed). What would be the way to do that?
I'm using JMagick. In a weird way, it works fine for indexed 8-bits images that are grayscale when i use:
image.transformRgbImage(info.getColorspace());
even if the image, though not indexed any more, is still 8-bits after that, which is lucky as it is grayscale and should actually be 8-bits. The weird stuff is that the transformRgbImage() performs that although I'd rather expect it to convert the image to a 24-bits one. Anyway...
The same way doesn't work for a color indexed 8-bits image. I just don't know how to use the JMagick API to achieve that goal. I tried setting:
image.setDepth(24);
or:
info.setDepth(24);
but both result in an EXCEPTION_ACCESS_VIOLATION. When I set:
info.setDepth(32);
no exception is raised, 1) but the image is 32-bits, which shouldn't be, and 2) it is all black (1 unique color). Why does the setDepth(24) raises such an exception?? How should I do?
Thanks in advance for your help.
I dont know about jmagick, but generally once you created an image object its properties are fixed (size and color model).
You don't change an images properties, you create a new image with the desired target properties and paint your original image into the new image. In plain core java you would simply do it like this:
public BufferedImage toRGB(Image i) {
BufferedImage rgb = new BufferedImage(i.getWidth(null), i.getHeight(null), BufferedImage.TYPE_INT_RGB);
rgb.createGraphics().drawImage(i, 0, 0, null);
return rgb;
}
Short;
I need to get the value of a specific pixel from a supplied high color depth image.
Details:
I am currently using Processing to make a Slit-scanning program.
Essentially, I am using a greyscale image to pick frames from an animation, and using pixels from those frames to make a new image.
For example if the greyscale image has a black pixel, it takes the same pixel in the first frame, and adds it to an image.
If its a white pixel, it does the same with the last frame.
Anything inbetween, naturally, picks the frames inbetween.
The gist is, if supplied a horizontal gradient, and a video of a sunset, then youd have the start of the sunset on the left, slowly transitioning to the end on the right.
My problem is, when using Processing, I seem to be only able to get greyscale values of
0-255 using the default library.
Black = 0
White = 255
This limits me to using only 256 frames for the source animation, or to put up with a pixaly, unsmooth end image.
I really need to be able to supply, and thus get, pixel values in a much bigger range.
Say,
Black = 0
White = 65025
Is there any Java lib that can do this? That I can supply, say, a HDR Tiff or TGA image file, and be able to read the full range of color out of it?
Thanks,
Ok, found a great library for this;
https://code.google.com/p/pngj/
Supports the full PNG feature set - including 16 bit greyscale or full color images.
Allows me to retrieve rows from a image, then pixels from those rows.
PngReader pngr = new PngReader(new File(filename));
tmrows = pngr.readRows();
ImageLineInt neededline = (ImageLineInt)tmrows.getImageLine(y);
if (neededline.imgInfo.greyscale==true){
//get the right pixel for greyscale
value = neededline.getScanline()[x];
} else {
//get the right pixel for RGB
value = neededline.getScanline()[x*3];
}
You simply multiply by 3 as the scanline consists of RGBRGBRGB (etc) for a full color image without alpha.
I'm using JAI to add a "border" to an image. Eg
ParameterBlock pb = new ParameterBlock();
pb.addSource(r);
pb.add(leftPad);
pb.add(rightPad);
pb.add(topPad);
pb.add(bottomPad);
pb.add(new BorderExtenderConstant(consts));
r = JAI.create("border", pb);
The value of 'consts' above depends on the ColorModel. When using ComponentColorModel each pixel has its own color so I don't have to mess with a palette. When an image has a palette (GIFs, PNGs, ...) the ColorModel is IndexColorModel.
When IndexColorModel is being used then 'consts' is a double[] array, with the size of one. The value in the array is the index in the color palette.
I have found how to modify the palette by creating a new IndexColorModel but the logic I would have to code would be insane! (Eg. How many colors I can have in the palette depends on many factors. Additionally if I need to remove a color from the palette in order to add the new one, I would need logic that determines which color would be best to remove and then modify all pixels previously referencing that color -- wow, that's a lot of code!)
So, my question is, how does one add a color to the palette? Is there an existing library? Or should I be using something from ioimage? (To be honest I'm a little confused where jai "ends" and ioimage "starts".)
Also, side question, for some reason my test images that only have about 10 colors in the palette are read in as having 256 colors. When I then save the image with jai all the 256 colors are saved (11 through 255 are all black). Any idea why it's doing this?
Thanks!
David
The best solution I've been able to come up with to my question above is to convert the image from IndexColorModel to ComponentColorModel. (Each pixel of a ComponentColorModel specifies its own color so there is no pallet you have to work with -- you can easily use any color you want.)
I cam up with this simple solution after reading http://java.sun.com/products/java-media/jai/forDevelopers/jaifaq.html#palette
This is what I am doing after reading an image in:
if(image.getColorModel() instanceof IndexColorModel) {
IndexColorModel icm = (IndexColorModel)image.getColorModel();
byte[][] data = new byte[4][icm.getMapSize()];
icm.getReds(data[0]);
icm.getGreens(data[1]);
icm.getBlues(data[2]);
icm.getAlphas(data[3]);
LookupTableJAI lut = new LookupTableJAI(data);
image = JAI.create("lookup", image, lut);
}
Once doing the manipulation you can then covert the image back. I haven't spent the time to figure that out. If someone else wants to figure it out you might want to read this: http://www.java.net/node/675577