I first tried to convert jpg into array of rgb values and then tried to revert same array into jpg
picw = selectedImage.getWidth();
pich = selectedImage.getHeight();
int[] pix = new int[picw * pich];
selectedImage.getPixels(pix, 0, picw, 0, 0, picw, pich);
int R, G, B;
for (int y = 0; y < pich; y++) {
for (int x = 0; x < picw; x++) {
int index = y * picw + x;
R = (pix[index] >> 16) & 0xff;
G = (pix[index] >> 8) & 0xff;
B = pix[index] & 0xff;
pix[index] = (R << 16) | (G << 8) | B;
}
}
Untill this point all things are fine(i checked by Loging the array), but when i create bitmap to compress it in jpg, the output is of black image.
Bitmap bmp = Bitmap.createBitmap(pix, picw, pich,Bitmap.Config.ARGB_8888);
File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File file = new File(folder,"Wonder.jpg");
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Please help me to move Further, Thanks
First let me explain how this data is stored per pixel:
Each pixel has 32bits of data to store a value for: Alpha, Red, Green and Blue. Each of these values is just 8 bits (or a byte). (there are a lot of other formats to store color information, but the one you specified is ARGB_8888).
In this format, white is 0xffffffff and black is 0xff000000.
So, like i said in the comments, the alpha seems to be missing. A red pixel without any alpha like 0x00ff0000 is not going to be visible.
Alpha can be added by first storing it:
A = (pix[index] >> 24) & 0xff;
Although the value is probably going to be 255 (because JPEG doesn't have alpha), i think it would be wise to use it like this in case you decide to use another format that does have alpha.
Then you should put the alpha back in:
pix[index] = (A << 24) | (R << 16) | (G << 8) | B;
This should write the exact same value to pix[index] which it already contains, not changing anything. But it will leave you with the original image instead of just black.
Related
I'm currently having an issue with alpha channels when reading PNG files with ImageIO.read(...)
fileInputStream = new FileInputStream(path);
BufferedImage image = ImageIO.read(fileInputStream);
//Just copying data into an integer array
int[] pixels = new int[image.getWidth() * image.getHeight()];
image.getRGB(0, 0, width, height, pixels, 0, width);
However, when trying to read values from the pixel array by bit shifting as seen below, the alpha channel is always returning -1
int a = (pixels[i] & 0xff000000) >> 24;
int r = (pixels[i] & 0xff0000) >> 16;
int g = (pixels[i] & 0xff00) >> 8;
int b = (pixels[i] & 0xff);
//a = -1, the other channels are fine
By Googling the problem I understand that the BufferedImage type needs to be defined as below to allow for the alpha channel to work:
BufferedImage image = new BufferedImage(width, height BufferedImage.TYPE_INT_ARGB);
But ImageIO.read(...) returns a BufferedImage without giving the option to specify the image type. So how can I do this?
Any help is much appreciated.
Thanks in advance
I think, your "int unpacking" code might be wrong.
I used (pixel >> 24) & 0xff (where pixel is the rgba value of a specific pixel) and it worked fine.
I compared this with the results of java.awt.Color and they worked fine.
I "stole" the "extraction" code directly from java.awt.Color, this is, yet another reason, I tend not to perform these operations this way, it's to easy to screw them up
And my awesome test code...
BufferedImage image = ImageIO.read(new File("BYO image"));
int width = image.getWidth();
int height = image.getHeight();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = image.getRGB(x, y);
//value = 0xff000000 | rgba;
int a = (pixel >> 24) & 0xff;
Color color = new Color(pixel, true);
System.out.println(x + "x" + y + " = " + color.getAlpha() + "; " + a);
}
}
nb: Before some one tells that this is inefficient, I wasn't going for efficiency, I was going for quick to write
You may also want to have a look at How to convert get.rgb(x,y) integer pixel to Color(r,g,b,a) in Java?, which I also used to validate my results
I think the problem is that you're using arithmetic shift (>>) instead of logical shift (>>>). Thus 0xff000000 >> 24 becomes 0xffffffff (i.e. -1)
Why here I setRGB () with three RGB colors (125, 126, 127), but when getRGB it returns another value that is (125, 126, 128).
For (122, 126, 127), it returns true (122, 126, 127).
Why?
And:
Input: image.setRGB (0, 0, 5072962) // 77 ----- 104 ----- 66 ------- 5072962
Output: 78 ------ 104 ------ 69 ------- 5138501
(With rgb = (red << 16) | (green << 8) | blue;)
My code:
// 77-------104-------66------5072962
final static int rgb = 5072962;
public static void main(String[] args) {
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
//image.setRGB(0, 0, 5072962);
Color c = new Color(125, 126, 127);
image.setRGB(0, 0, c.getRGB());
File outputFile = new File("123.jpg");
try {
ImageIO.write(image, "jpg", outputFile);
File f = new File("123.jpg");
BufferedImage bi = ImageIO.read(f);
for (int h = 0; h < 1; h++) {
for (int w = 0; w < 1; w++) {
int pixel = bi.getRGB(w, h);
Color c2 = new Color(pixel);
int red = c2.getRed();
int green = c2.getGreen();
int blue = c2.getBlue();
int rgb = (red << 16) | (green << 8) | blue;
System.out.printf("%-8d%-8d%-8d%-8d", red, green, blue, rgb);
System.out.println("");
}
}
} catch (IOException ex) {
System.out.println("Error output File image!");
}
}
At first, Did you Know about Image file formats ? I have the same problem before in similar case. I would like to suggest you the following.
When you use jpg for the image write operation to store in your storage, RGB values changes slightly because jpg format is a lossy compressed file format. This compression allows you for the optimal space to store the file but it doesn't store the information of RGB values you want.
So, If file format is not a big issue for you , then simply use PNG format. PNG format is a lossless compression file format so that you can retrieve RGB values you have set before in the program.
Hope it might help you.
Im having trouble getting pixel data.
My program takes screenshots, every loop it will store the previous screenshot.
My goal is to do a comparison at the pixel level between the current screenshot and the old one.
Ive ran this code which tells me what format the screenshots are in:
System.out.println(image.getType());
The output of this (for my program) is 1 meaning its a BufferedImage.TYPE_INT_RGB
From what ive read, the types determine what order the pixel values are in the byte array.
I'm using this code to convert my Buffered image to a byte array (The buffered image is created using awt.Robot class):
public byte[] convertToByteArray(BufferedImage img){
byte[] imageInByte = null;
try {
// convert BufferedImage to byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "png", baos);
baos.flush();
imageInByte = baos.toByteArray();
baos.close();
} catch (IOException ex) {
Logger.getLogger(OverlayWindow.class.getName()).log(Level.SEVERE, null, ex);
}
return imageInByte;
}
Finally i use a comparison method to check the byte array. For now this only prints the color values of the array:
final byte[] pixels = convertToByteArray(image);
final int pixelLength = 3;
for (int pixel = 0, row = 0, col = 0; pixel < 1; pixel += pixelLength) {
int argb = 0;
argb += -16777216; // 255 alpha
argb += ((int) pixels[pixel] & 0xff); // blue
argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green
argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red
int r = (argb >> 16) & 0xff, g = (argb >> 8)& 0xff, b = argb & 0xff;
System.out.println("R = " + r);
System.out.println("G = " + g);
System.out.println("B = " + b);
col++;
if (col == width) {
col = 0;
row++;
}
}
My issue with this code is that even though i take a screenshot of a solid color, the pixel values are all over the place. Im expecting each pixel to have the same color values.
-Edit-
I'm avoiding using getRGB for performance reasons. Iterating through two large images calling getRGB each time is very costly in my application.
The easiest way to access the pixels values in a BufferedImage is to use the Raster:
BufferedImage image = ...
for (int y=0 ; y < image.getHeight() ; y++)
for (int x=0 ; x < image.getWidth() ; x++)
for (int c=0 ; c < image.getRaster().getNumBands() ; c++)
final int value = image.getRaster().getSample(x, y, c) ; // Returns the value of the channel C of the pixel (x,y)
The raster will take care of the encoding for you, making it the easiest way to access the pixel values. However, the fastest way is to use the DataBuffer, but then you have to manage all the encodings.
/* This method takes a BufferedImage encoded with TYPE_INT_ARGB and copies the pixel values into an image encoded with TYPE_4BYTE_ABGR.*/
public static void IntToByte(BufferedImage source, BufferedImage result)
{
final byte[] bb = ((DataBufferByte)result.getRaster().getDataBuffer()).getData() ;
final int[] ib = ((DataBufferInt)source.getRaster().getDataBuffer()).getData() ;
switch ( source.getType() )
{
case BufferedImage.TYPE_INT_ARGB :
for (int i=0, b=0 ; i < ib.length ; i++, b+=4)
{
int p = ib[i] ;
bb[b] = (byte)((p & 0xFF000000) >> 24) ;
bb[b+3] = (byte)((p & 0xFF0000) >> 16) ;
bb[b+2] = (byte)((p & 0xFF00) >> 8) ;
bb[b+1] = (byte)( p & 0xFF) ;
}
break ;
// Many other case to manage...
}
}
I've been trying for two days to find a way to perfectly convert a CMYK image to a RGB one in Java. I went through a lot of different ways to do it, all found on the Web, some of them on Stackoverflow, but I couldn't just find the way that would do it simply and without this awful color fading that is typical to such conversions. I know that tools like Photoshop or Irfanview do it perfectly in two clicks but I wanted it to be Java coded.
Well, long story short, I found a way, and here it is.
Thank you for your feedbacks.
Whome, I tried your way but it gave me either inverted or very strange colors whether I saved the image using ImageIO.write() or JAI.create().
haraldk, I haven't try your code yet. I had a look at it and it does not seem straightforward to me. I'll give it a try later.
Meanwhile, allow me to post my own way, that's actually made up of other people ways (this guy: https://stackoverflow.com/a/9470843/2435757 and that other guy: http://www.coderanch.com/t/485449/java/java/RGB-CMYK-Image, among others). It works although, as a new BufferedImage is created, information such as the resolution, or the compression method (for a TIFF image) are lost and must be reset, which this method does not (I think that the only non-JRE lib required here is Apache common xmlgraphics):
BufferedImage img = null;
try {
img = ImageIO.read(new File("cmyk.jpg"));
} catch (IOException e) {}
ColorSpace cmyk = DeviceCMYKColorSpace.getInstance();
int w = img.getWidth(), h = img.getHeight();
BufferedImage image = null;
byte[] buffer = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
int pixelCount = buffer.length;
byte[] new_data = new byte[pixelCount / 4 * 3];
float lastC = -1, lastM = -1, lastY = -1, lastK = -1;
float C, M, Y, K;
float[] rgb = new float[3];
// loop through each pixel changing CMYK values to RGB
int pixelReached = 0;
for (int i = 0 ; i < pixelCount ; i += 4) {
C = (buffer[i] & 0xff) / 255f;
M = (buffer[i + 1] & 0xff) / 255f;
Y = (buffer[i + 2] & 0xff) / 255f;
K = (buffer[i + 3] & 0xff) / 255f;
if (lastC == C && lastM == M && lastY == Y && lastK == K) {
//use existing values if not changed
} else { //work out new
rgb = cmyk.toRGB(new float[] {C, M, Y, K});
//cache values
lastC = C;
lastM = M;
lastY = Y;
lastK = K;
}
new_data[pixelReached++] = (byte) (rgb[0] * 255);
new_data[pixelReached++] = (byte) (rgb[1] * 255);
new_data[pixelReached++] = (byte) (rgb[2] * 255);
}
// turn data into RGB image
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int[] l_bandoff = {0, 1, 2};
PixelInterleavedSampleModel l_sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_INT, w, h, 3, w * 3, l_bandoff);
image.setData(new ByteInterleavedRaster(l_sm, new DataBufferByte(new_data, new_data.length), new Point(0, 0)));
// write
ImageIO.write(image, "jpg", new File("rgb.jpg"));
The above code gives me excellent results for both JPEG and TIFF images, although I happened to get a very strange result with a particular image.
Here is another, much simpler and straightforward, way by JMagick:
ImageInfo info = new ImageInfo("cmyk.tif");
MagickImage image = new MagickImage(info);
image.transformRgbImage(ColorspaceType.CMYKColorspace);
image.setFileName("rgb.tif");
image.writeImage(info);
Couldn't be shorter, could it? Also works like a charm for both JPEG and TIFF.
And no, haraldk, I didn't use any reference to a color profile. That seems quite weird to me too. I can only assume that both ways use a default color profile and that I've been lucky enough for it to work fine in all cases so far.
I am waiting for your feedbacks on this.
Cheers.
PS: I would be more than glad to give you links to the images I use, but Stackoverflow says I'm not reliable enough :-) In another post maybe, if you require them.
What SO answers did you try and found not working properly?
Did any of them gave this example code. Does it create color fading? Would you please share an example image link creating a problem?
/**
* ImageIO cannot read CMYK-jpegs, it throws IIOException(Unsupported Image Type).
* This method tries to read cmyk image.
* #param file
* #return image TYPE_4BYTE_ABGR
* #throws Exception
*/
public static BufferedImage readCMYKImage(File file) throws Exception {
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
ImageReader reader = null;
while(readers.hasNext()) {
reader = readers.next();
if(reader.canReadRaster())
break;
}
FileInputStream fis = new FileInputStream(file);
try {
ImageInputStream input = ImageIO.createImageInputStream(fis);
reader.setInput(input); // original CMYK-jpeg stream
Raster raster = reader.readRaster(0, null); // read image raster
BufferedImage image = new BufferedImage(raster.getWidth(), raster.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
image.getRaster().setRect(raster);
return image;
} finally {
try { fis.close(); } catch(Exception ex) {}
}
}
I need to create a simple demo for image manipulation in Java. My code is swing based. I don't have to do anything complex, just show that the image has changed in some way. I have the image read as byte[]. Is there anyway that I can manipulate this byte array without corrupting the bytes to show some very simple manipulation. I don't wish to use paint() etc. Is there anything that I can do directly to the byte[] array to show some change?
edit:
I am reading jpg image as byteArrayInputStream using apache io library. The bytes are read ok and I can confirm it by writing them back as jpeg.
You can try to convert your RGB image to Grayscale. If the image as 3 bytes per pixel rapresented as RedGreenBlue you can use the followinf formula: y=0.299*r+0.587*g+0.114*b.
To be clear iterate over the byte array and replace the colors. Here an example:
byte[] newImage = new byte[rgbImage.length];
for (int i = 0; i < rgbImage.length; i += 3) {
newImage[i] = (byte) (rgbImage[i] * 0.299 + rgbImage[i + 1] * 0.587
+ rgbImage[i + 2] * 0.114);
newImage[i+1] = newImage[i];
newImage[i+2] = newImage[i];
}
UPDATE:
Above code assumes you're using raw RGB image, if you need to process a Jpeg file you can do this:
try {
BufferedImage inputImage = ImageIO.read(new File("input.jpg"));
BufferedImage outputImage = new BufferedImage(
inputImage.getWidth(), inputImage.getHeight(),
BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < inputImage.getWidth(); x++) {
for (int y = 0; y < inputImage.getHeight(); y++) {
int rgb = inputImage.getRGB(x, y);
int blue = 0x0000ff & rgb;
int green = 0x0000ff & (rgb >> 8);
int red = 0x0000ff & (rgb >> 16);
int lum = (int) (red * 0.299 + green * 0.587 + blue * 0.114);
outputImage
.setRGB(x, y, lum | (lum << 8) | (lum << 16));
}
}
ImageIO.write(outputImage, "jpg", new File("output.jpg"));
} catch (IOException e) {
e.printStackTrace();
}