I am trying to take an int array of pixels from a Bitmap on Android and use the int array to make a buffered image. But I'm having some problems. I am able to get the bitmap from android with no problem:
Bitmap b = BitmapFactory.decodeByteArray(data, 0, data.length);
b = Bitmap.createScaledBitmap(b, (int)(b.getWidth() * .3125), (int)(b.getHeight() * .44444444444), false);
int[] arr = new int[b.getHeight() * b.getWidth()];
b.getPixels(arr, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
I scale the bitmap down and then get the pixels and put them into the int array. Then I send them over a network to the PC where I try to recreate it to a BufferedImage (networking isn't important). Here is the code I use to convert the pixel array to a BufferedImage.
int width = 600;
int height = 479;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
bi.setRGB(0, 0, width, height, arr, 0, width);
g2d.drawImage(bi, 10, 50, null);
The variable "arr" is the byte array of pixels. Converting to a BufferedImage gives no error and shows the following image:
Obviously this isn't the picture I am trying to get. When I print out 5 of the pixels in the array (just for testing) I get the following and this is the format of the pixels:
-15528956
-15200766
-13558523
-11718123
-12243954
-13294582
Related
All welcome.
There is such code:
public void onPreviewFrame(byte[] data,Camera camera){
try{
YuvImage yuvimage=new YuvImage(data,ImageFormat.NV21,this.width,this.height,null);
System.out.println("WidthandHeight"+yuvimage.getHeight()+"::"+yuvimage.getWidth());
ByteArrayOutputStream baos=new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0,0,this.width,this.height),100,baos);
}catch(Exception e){
Log.d("parse","errpr");
}
}
It takes the image from the camera and turns it into a jpg image.
But here's the problem:
The image, even if its quality is shrunk to 20 percent, weighs about 2 kb (2000 bytes).
As I understand it, the weight of the image consists of:
width * length * 3 (3 - three channels. R, G, B).
In fact, if you make the image black and white, the formula will be:
width * length * 1 (one channel)
That is, thereby, reducing the size of the image by 3 times!
I tried to do this.
But after checking, it turned out that the image became black-and-old, but the size was as it was and remained about 2 kb.
Code (from RGB to grayscale):
public Bitmap toGrayscale(Bitmap bmpOriginal)
{
int width, height;
height = bmpOriginal.getHeight();
width = bmpOriginal.getWidth();
Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmpGrayscale);
Paint paint = new Paint();
ColorMatrix cm = new ColorMatrix();
cm.setSaturation(0);
ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
paint.setColorFilter(f);
c.drawBitmap(bmpOriginal, 0, 0, paint);
return bmpGrayscale;
}
What could go wrong? Is it possible to reduce the weight of the image in this way?
I have a byte array containing data of the raw grayscale 8bit image, which I need to convert to a BufferedImage. I've tried doing:
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
However, the resulting image object is null which means I'm doing something wrong here.
What's the correct way of making such a conversion?
There are two good ways to do this, depending on your use case.
Either create a new, gray image, and copy the data into it. This will keep the image "managed", which may lead to better rendering performance (ie. on screen). But it will need twice as much memory, and copy the data from your input to the image.
The other, is to create the gray image directly "around" your existing pixel data. This will be faster, and use almost no extra heap, as it avoids copying the pixel data. But the image will not be managed (as the backing array is exposed and mutable).
Both options are demonstrated below:
int w = 640;
int h = 480;
byte[] imageBytes = new byte[w * h];
// 1 Keeps the image "managed" at the expense of twice the memory + a large array copy
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
image.getRaster().setDataElements(0, 0, w, h, imageBytes);
System.out.println("image: " + image);
// 2 Faster, and uses less memory, but will make the image "unmanaged"
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createInterleavedRaster(new DataBufferByte(imageBytes, imageBytes.length), w, h, w, 1, new int[]{0}, null);
BufferedImage image2 = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
System.out.println("image2: " + image2);
If the image data isn't in linear gray color space, one could use an IndexColorModel to map the input into whatever range you want:
// Alternate, using IndexColorModel, if your input isn't in linear gray color space
int[] cmap = new int[256]; // TODO: Add ARGB packed colors here...
IndexColorModel icm = new IndexColorModel(8, 256, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
// As 1
BufferedImage image3 = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED, icm);
image3.getRaster().setDataElements(0, 0, w, h, imageBytes);
System.out.println("image3: " + image3);
// As 2
BufferedImage image4 = new BufferedImage(icm, raster, cm.isAlphaPremultiplied(), null);
System.out.println("image4: " + image4);
I've managed to did the conversion for the 640x480 resolution the following way:
BufferedImage image = new BufferedImage(640,480,BufferedImage.TYPE_BYTE_INDEXED);
int i = 0;
for(int y = 0; y < 480; y++)
{
for(int x = 0; x < 640; x++)
{
int g = imageBytes[i++] & 0xFF;
image.setRGB(x,y,new Color(g,g,g).getRGB());
}
}
EDIT: removed useless code (thanks to Marco13)
Java
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
image.getRaster().setDataElements(0, 0, width, height, array));
Kotlin
val image = BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY)
image.raster.setDataElements(0, 0, width, height, byteArray )
Currently working on Image manipulation in Java
I have the byte array(PPM) of size 921600 (640*480*3)
byte[] image; // PPM
BufferedImage image = ImageIO.read(new ByteArrayInputStream(image));
image is null.
Tried with ImageMagic and JAI libraries. But it does not help me.
Is it possible to get the RGB components from byte array and convert it to JPG file.
Any help is appreciated.
Below is the Code (which will convert PPM(byte array to Buffered image and you can save buffered image to the file)
// Method Call
BufferedImage image = ppm(width, height, 255, byte[]);
//Method Definition
static public BufferedImage ppm(int width, int height, int maxcolval, byte[] data){
if(maxcolval<256){
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
int r,g,b,k=0,pixel;
if(maxcolval==255){ // don't scale
for(int y=0;y<height;y++){
for(int x=0;(x<width)&&((k+3)<data.length);x++){
r=data[k++] & 0xFF;
g=data[k++] & 0xFF;
b=data[k++] & 0xFF;
pixel=0xFF000000+(r<<16)+(g<<8)+b;
image.setRGB(x,y,pixel);
}
}
}
else{
for(int y=0;y<height;y++){
for(int x=0;(x<width)&&((k+3)<data.length);x++){
r=data[k++] & 0xFF;r=((r*255)+(maxcolval>>1))/maxcolval; // scale to 0..255 range
g=data[k++] & 0xFF;g=((g*255)+(maxcolval>>1))/maxcolval;
b=data[k++] & 0xFF;b=((b*255)+(maxcolval>>1))/maxcolval;
pixel=0xFF000000+(r<<16)+(g<<8)+b;
image.setRGB(x,y,pixel);
}
}
}
return image;
}
else{
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
int r,g,b,k=0,pixel;
for(int y=0;y<height;y++){
for(int x=0;(x<width)&&((k+6)<data.length);x++){
r=(data[k++] & 0xFF)|((data[k++] & 0xFF)<<8);r=((r*255)+(maxcolval>>1))/maxcolval; // scale to 0..255 range
g=(data[k++] & 0xFF)|((data[k++] & 0xFF)<<8);g=((g*255)+(maxcolval>>1))/maxcolval;
b=(data[k++] & 0xFF)|((data[k++] & 0xFF)<<8);b=((b*255)+(maxcolval>>1))/maxcolval;
pixel=0xFF000000+(r<<16)+(g<<8)+b;
image.setRGB(x,y,pixel);
}
}
return image;
}
}
You can use a WritableRaster to set the pixels in the image for you:
For a grayscale image, you will have a byte array like this:
byte[] arr = new byte[width * height];
To make an image, use:
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
img.getRaster().setDataElements(0, 0, width, height, arr);
For a color image, you will have an array like this:
byte[] arr = new byte[width * height * 3];
So, to make an image, use:
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
img.getRaster().setDataElements(0, 0, width, height, arr);
You may need to mess this the TYPE in the first line. See here to choose which type your image is.
What in Java matches to C# PixelFormat's members.
i.e. Format24bppRgb matches to BufferedImage.TYPE_INT_RGB?
Here is my code. I got an image which has .Net's PixelFormat = Format32bppArgb. I am creating BufferedImage like this:
int sizeBytes = width * height;
DataBufferByte dataBuffer = new DataBufferByte(myImageBytes, sizeBytes);
WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, // dataBuffer
width, // width
height, // height
width * 4, // scanlineStride
4, // pixelStride
new int[]{0, 1, 2, 3}, // bandOffsets
null); // location
java.awt.image.ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), // ColorSpace
new int[]{8, 8, 8, 8}, // bits
true, // hasAlpha
false, // isPreMultiplied
ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);
BufferedImage result = new BufferedImage(colorModel, raster, false, null);
After I create a bufferedImage red and blue colors are swapped in it.
Next, I tried to create an image as follow
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
WritableRaster r = result.getRaster();
int[] pixels = byteToInt(bytes);
r.setPixels(0, 0, width, height , pixels); // ! Here an exception occures, because after I converted the byte array to int one the width becomes too long.
Byte array was convert by this method
private int[] byteToInt(byte[] pixels) {
int[] ints = new int[pixels.length / 3];
int byteIdx = 0;
for (int pixel = 0; pixel < ints.length; pixel++) {
int red = (int) pixels[byteIdx++] & 0xFF;
int green = (int) pixels[byteIdx++] & 0xFF;
int blue = (int) pixels[byteIdx++] & 0xFF;
int rgb = (red << 16) | (green << 8) | blue;
ints[pixel] = rgb;
}
return ints;
}
The colors look fine now, but I got the exception
java.lang.ArrayIndexOutOfBoundsException: 27600
at sun.awt.image.ByteInterleavedRaster.setPixels(ByteInterleavedRaster.java:1106)
If I use the smaller width (e.g. width / 3) the colors look fine but the picture itself shrinks.
BufferedImage is definately a good place to start. Many of the values in PixelFormat will match up to values in BufferedImage - they each have 24-bit and 32-bit RGB/ARGB values, both have 5-5-5 and 5-6-5 combinations, etc.
If you're having trouble, post some code and we'll have a look at it, try to help. The thing I would recommend would be to play around with the byte ordering (in the pixel ints) until you get the result that you expect - try drawing the BufferedImage onto a GUI object like JPanel so you can see what it looks like.
If you've got an array of int[] for your pixel values, this is the code I usually use for displaying the array as an image...
int[] pixels;
ColorModel model = new DirectColorModel(32,0x00ff0000,0x0000ff00,0x000000ff,0xff000000);
Image image = new JLabel().createImage(new MemoryImageSource(width,height,model,pixels,0,width));
I have an MxN array of ints representing colors (say RGBA format, but that is easily changeable). I would like to convert them to an MxN Bitmap or something else (such as an OpenGL texture) that I can render to the screen. Is there a fast way to do this? Looping through the array and drawing them to the canvas is far too slow.
Try this, it will give you the bitmap:
// You are using RGBA that's why Config is ARGB.8888
bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
// vector is your int[] of ARGB
bitmap.copyPixelsFromBuffer(IntBuffer.wrap(vector));
Or you can generate IntBuffer from the following native method:
private IntBuffer makeBuffer(int[] src, int n) {
IntBuffer dst = IntBuffer.allocate(n*n);
for (int i = 0; i < n; i++) {
dst.put(src[i]);
}
dst.rewind();
return dst;
}
Why not use Bitmap.setPixel? It's even API level 1:
int[] array = your array of pixels here...
int width = width of "array"...
int height = height of "array"...
// Create bitmap
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// Set the pixels
bitmap.setPixels(array, 0, width, 0, 0, width, height);
You can play with offset/stride/x/y as needed.
No loops. No additional allocations.
Yeah, sounds like you have all the info you need. If M is the width and N is the height, you can create a new bitmap with Bitmap.createBitmap, and you can fill in the ARGB values with the setPixels method which takes an int array.
Bitmap.createBitmap
Bitmap.setPixels