it's possible to use Java.awt.Image android application - java

I've written a Java methods ,but i have to use this method in android project,so someone can help me to convert it into android or help me what should i do?
public Image getImage(){
ColorModel cm = grayColorModel() ;
if( n == 1){// in case it's a 8 bit/pixel image
return Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(w, h,cm, pixData, 0, w));
}//endif
}
protected ColorModel grayColorModel()
{
byte[] r = new byte[256] ;
for (int i = 0; i <256 ; i++ )
r[i] = (byte)(i & 0xff ) ;
return (new IndexColorModel(8,256,r,r,r));
}

For instance, to convert a grayscale image (byte array, imageSrc) to drawable:
byte[] imageSrc= [...];
// That's where the RGBA array goes.
byte[] imageRGBA = new byte[imageSrc.length * 4];
int i;
for (i = 0; i < imageSrc.length; i++) {
imageRGBA[i * 4] = imageRGBA[i * 4 + 1] = imageRGBA[i * 4 + 2] = ((byte) ~imageSrc[i]);
// Invert the source bits
imageRGBA[i * 4 + 3] = -1;// 0xff, that's the alpha.
}
// Now put these nice RGBA pixels into a Bitmap object
Bitmap bm = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bm.copyPixelsFromBuffer(ByteBuffer.wrap(imageRGBA));
Code may differ depending of input format.

Related

byte array to bitmap returns null

I have a small program that takes a Chair.jpg and converts it a bitmap. The reason for this is to change the color type of the pixels to the format of BGR_888 (which I got from this stack overflow post.)
However the bitmap is null. I believe it is because of this reason D/skia: --- Failed to create image decoder with message 'unimplemented'. Looking on online, it maybe because I need to compress this? Im not sure. Can someone help me understand?
public class MainActivity extends AppCompatActivity {
TextView textView;
ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
textView = findViewById(R.id.textView);
Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.chair);
imageView.setImageBitmap(b);
int width = b.getWidth();
int height = b.getHeight();
Log.d("bitmap height", String.valueOf(height));
Log.d("bitmap width", String.valueOf(width));
Bitmap.Config config = b.getConfig();
Log.d("b color format", String.valueOf(config));
byte[] newImage = getImagePixels(b);
Log.d("getImagePixels Result", String.valueOf(newImage));
boolean b2 = isNewBitmapNull(newImage);
Log.d("is my new bitmap null:",String.valueOf(b2));
}
/// <summary>
/// function getImagePixels
/// Purpose: Given the bitmap image return the converted 4 byte ARGB to 3 byte BGR
/// </summary>
/// <param name="image"> Bitmap image object </param>
public byte[] getImagePixels(Bitmap image) {
// calculate how many bytes our image consists of
int bytes = image.getByteCount();
// Create a new buffer
ByteBuffer buffer = ByteBuffer.allocate(bytes);
// Move the byte data to the buffer
image.copyPixelsToBuffer(buffer);
// Get the underlying array containing the data.
byte[] temp = buffer.array();
// Allocate for 3 byte BGR
byte[] pixels = new byte[(temp.length / 4) * 3];
// Copy pixels into place
for (int i = 0; i < (temp.length / 4); i++) {
pixels[i * 3] = temp[i * 4 + 3]; // B
pixels[i * 3 + 1] = temp[i * 4 + 2]; // G
pixels[i * 3 + 2] = temp[i * 4 + 1]; // R
// Alpha is discarded
}
Log.d("check if it is array", String.valueOf(pixels.getClass().isArray()));
Log.d("array object type", String.valueOf(pixels.getClass()));
Log.d("array length", String.valueOf(pixels.length));
return pixels;
}
public static byte[] convertBitmapToByteArrayUncompressed(Bitmap bitmap){
ByteBuffer byteBuffer = ByteBuffer.allocate(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(byteBuffer);
byteBuffer.rewind();
return byteBuffer.array();
}
/// <summary>
/// function copyPixelsToBitmap
/// Purpose: Given the pixel data return a bitmap of size [?,?],PixelFormat=24BGR
/// </summary>
/// <param name="pixels"> Byte array with pixel data </param>
public Bitmap copyPixelsToBitmap(byte[] pixels){
//Here create the Bitmap to the know height, width and format
Bitmap bmp = BitmapFactory.decodeByteArray(pixels, 0, pixels.length);
//Return the bitmap
return bmp;
}
/// <summary>
/// function isNewBitmapNull
/// Purpose: Given the pixel data return T/F is the bitmap was created
/// </summary>
/// <param name="pixels"> Byte array with pixel data </param>
public boolean isNewBitmapNull(byte[] pixels){
//BitmapFactory.Options options = new BitmapFactory.Options();
//options.inMutable = true;
Bitmap bmp = BitmapFactory.decodeByteArray(pixels, 0, pixels.length);
if (bmp == null)
return true;
return false;
}
}
For the isNewBitMapNull method, I have also tried adding the BitMapFactory options but still get a null bitmap:
//BitmapFactory.Options options = new BitmapFactory.Options();
//options.inMutable = true;
Bitmap bmp = BitmapFactory.decodeByteArray(pixels, 0, pixels.length, options);
Here is the output:
D/bitmap height: 1260
D/bitmap width: 1260
D/b color format: ARGB_8888
D/check if it is array: true
D/array object type: class [B
D/array length: 4762800
D/getImagePixels Result: [B#5a36e09
D/skia: --- Failed to create image decoder with message 'unimplemented'
D/is my new bitmap null:: true
It seems that you are trying to create a bitmap that has 3 bytes per pixel, but it is not possible in Android since Bitmap supports 1, 2, 4 or 8 bytes per pixel.
That is why you need to create a new bitmap with the pixel format that is supported. But using BitmapFactory.decodeByteArray doesn't work because it creates a bitmap from compressed image data, such as data that is stored in .jpg file. But you have array of raw pixel values (uncompressed).
While compressing the image and then reading it using decodeByteArray is an option, you will need to implement compression of BGR_888 yourself or use a 3rd party library. The simpler way is to convert BGR_888 back to ARGB_8888 and create a bitmap from this data:
val temp = IntArray(width * height)
for (i in 0 until width * height) {
val red = pixels[i * 3 + 2].toInt() and 0xff
val green = pixels[i * 3 + 1].toInt() and 0xff
val blue = pixels[i * 3].toInt() and 0xff
temp[i] = Color.rgb(red, green, blue)
}
return Bitmap.createBitmap(temp, width, height, Bitmap.Config.ARGB_8888)
Java version:
int[] temp = new int[width * height];
for (int i = 0; i < width * height; ++i) {
int red = (int)pixels[i * 3 + 2] & 0xff;
int green = (int)pixels[i * 3 + 1] & 0xff;
int blue = (int)pixels[i * 3] & 0xff;
temp[i] = Color.rgb(red, green, blue);
}
return Bitmap.createBitmap(temp, width, height, Bitmap.Config.ARGB_8888);

BitmapFactory.decodeByteArray() always returns null (manually-created byte array)

So i'm trying to port some C++ code from a colleague that grabs image data over a Bluetoth serial port (I'm using an Android phone). From the data I will need to generate a bitmap.
Before testing the ported code, I wrote this quick function to suposedly generate a pure red rectangle. However, BitmapFactory.decodeByteArray() always fails and returns with a null bitmap. I've checked for both of the possible exeptions it can throw and neither one is thrown.
byte[] pixelData = new byte[225*160*4];
for(int i = 0; i < 225*160; i++) {
pixelData[i * 4 + 0] = (byte)255;
pixelData[i * 4 + 1] = (byte)255;
pixelData[i * 4 + 2] = (byte)0;
pixelData[i * 4 + 3] = (byte)0;
}
Bitmap image = null;
logBox.append("Creating bitmap from pixel data...\n");
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.outWidth = 225;
options.outHeight = 160;
try {
image = BitmapFactory.decodeByteArray(pixelData, 0, pixelData.length, options);
} catch (IllegalArgumentException e) {
logBox.append(e.toString() + '\n');
}
//pixelData = null;
logBox.append("Bitmap generation complete\n");
decodeByteArray() code:
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
if ((offset | length) < 0 || data.length < offset + length) {
throw new ArrayIndexOutOfBoundsException();
}
Bitmap bm;
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
try {
bm = nativeDecodeByteArray(data, offset, length, opts);
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
setDensityFromOptions(bm, opts);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
}
return bm;
}
I would presume that it's nativeDecodeByteArray() that is failing.
I also notice the log message:
D/skia: --- SkImageDecoder::Factory returned null
Anyone got any ideas?
decodeByteArray of BitmapFactory actually decodes an image, i.e. an image that has been encoded in a format such as JPEG or PNG. decodeFile and decodeStream make a little more sense, since your encoded image would probably be coming from a file or server or something.
You don't want to decode anything. You are trying to get raw image data into a bitmap. Looking at your code it appears you are generating a 225 x 160 bitmap with 4 bytes per pixel, formatted ARGB. So this code should work for you:
int width = 225;
int height = 160;
int size = width * height;
int[] pixelData = new int[size];
for (int i = 0; i < size; i++) {
// pack 4 bytes into int for ARGB_8888
pixelData[i] = ((0xFF & (byte)255) << 24) // alpha, 8 bits
| ((0xFF & (byte)255) << 16) // red, 8 bits
| ((0xFF & (byte)0) << 8) // green, 8 bits
| (0xFF & (byte)0); // blue, 8 bits
}
Bitmap image = Bitmap.createBitmap(pixelData, width, height, Bitmap.Config.ARGB_8888);

RAW image data to Android Bitmap

I've been working on an extension of a current application to stream webcam data to an Android device. I can obtain the raw image data, in the form of a RGB byte array. The color space is sRGB. I need to send that array over the network to an Android client, who constructs it into a Bitmap image to display on the screen. My problem is that the color data is skewed. The arrays have the same hashcode before and after being sent, so I'm positive this isn't a data loss problem. I've attached a sample image of how the color looks, you can see that skin tones and darker colors reconstruct okay, but lighter colors end up with a lot of yellow/red artifacts.
Server (Windows 10) code :
while(socket.isConnected()) {
byte[] bufferArray = new byte[width * height * 3];
ByteBuffer buff = cam.getImageBytes();
for(int i = 0; i < bufferArray.length; i++) {
bufferArray[i] = buff.get();
}
out.write(bufferArray);
out.flush();
}
Client (Android) code :
while(socket.isConnected()) {
int[] colors = new int[width * height];
byte[] pixels = new byte[(width * height) * 3];
int bytesRead = 0;
for(int i = 0; i < (width * height * 3); i++) {
int temp = in.read();
if(temp == -1) {
Log.d("WARNING", "Problem reading");
break;
}
else {
pixels[i] = (byte) temp;
bytesRead++;
}
}
int colorIndex = 0;
for(int i = 0; i < pixels.length; i += 3 ) {
int r = pixels[i];
int g = pixels[i + 1];
int b = pixels[i + 2];
colors[colorIndex] = Color.rgb( r, g, b);
colorIndex++;
}
Bitmap image = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
publishProgress(image);
}
The cam.getImageBytes() is from an external library, but I have tested it and it works properly. Reconstructing the RAW data into a BufferedImage works perfectly, using the code :
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
image.getRaster().setPixels(0,0,width,height, pixels);
But, of course, BufferedImages are not supported on Android.
I'm about to tear my hair out with this one, I've tried everything I can think of, so any and all insight would be extremely helpful!

Java RenderedImage implementation with custom raw DataBuffer

I receive 16-bit grayscale images from a device, the images are delivered in an uncompressed raw format
, here is a 8 bytes example of how 2X2 image will look like using this format (MSB first) :
21 27 33 F6 28 F3 27 F2
----- ----- ----- -----
pixel 0,0(x,y) pixel 1,0 pixel 1,0 pixel 1,1
I need to compress the images using Kakadu JPEG2000 library that expose a Java
ImageWriter implementation, the ImageWriter.write method expect a RenderedImage as input, I'm using the following code to create a BufferedImage from the raw image data :
int[] rasterData = new int[width * height];
int rawBufferOffset = 0;
for(int i=0;i<rasterData.length;i++) {
rasterData[i] = ((int) rawBuffer[rawBufferOffset + 1] << 8) | ((int) rawBuffer[rawBufferOffset] & 0xFF);
rawBufferOffset += 2;
}
BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_USHORT_GRAY);
image.getRaster().setPixels(0, 0, width, height, rasterData);
The code works but it's obviously not the best method to this conversion,
I was thinking about creating a RenderedImage implementation that uses the rawBuffer as the image raster data source, can anyone suggest how to do so or suggest any other method for this conversion?
The most straight forward way, is probably to use a ByteBuffer to swap the byte order, and create a new short array to hold the pixel data.
Then wrap the (short) pixel data in a DataBufferUShort. Create a matching WritableRaster and ColorModel, and finally create a BufferedImage from this. This image should be identical to the image in your code above (BufferedImage.TYPE_USHORT_GRAY), but be slightly faster to create, as you only copy the pixels once (as opposed to twice in your code).
int w = 2;
int h = 2;
int stride = 1;
byte[] rawBytes = {0x21, 0x27, 0x33, (byte) 0xF6, 0x28, (byte) 0xF3, (byte) 0x27, (byte) 0xF2};
short[] rawShorts = new short[rawBytes.length / 2];
ByteBuffer.wrap(rawBytes)
.order(ByteOrder.LITTLE_ENDIAN)
.asShortBuffer()
.get(rawShorts);
DataBuffer dataBuffer = new DataBufferUShort(rawShorts, rawShorts.length);
WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, w, h, w * stride, stride, new int[]{0}, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
Another, slightly more convoluted, but probably faster way (as you don't copy the backing pixel array at all), is to create a custom SampleModel that works with MSB (little endian) byte data, but exposes them as TYPE_USHORT. This will create a TYPE_CUSTOM image.
int w = 2, h = 2, stride = 2;
byte[] rawBytes = {0x21, 0x27, 0x33, (byte) 0xF6, 0x28, (byte) 0xF3, (byte) 0x27, (byte) 0xF2};
DataBuffer dataBuffer = new DataBufferByte(rawBytes, rawBytes.length);
SampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_USHORT, w, h, stride, w * stride, new int[] {0}) {
#Override
public Object getDataElements(int x, int y, Object obj, DataBuffer data) {
if ((x < 0) || (y < 0) || (x >= width) || (y >= height)) {
throw new ArrayIndexOutOfBoundsException("Coordinate out of bounds!");
}
// Simplified, as we only support TYPE_USHORT
int numDataElems = getNumDataElements();
int pixelOffset = y * scanlineStride + x * pixelStride;
short[] sdata;
if (obj == null) {
sdata = new short[numDataElems];
}
else {
sdata = (short[]) obj;
}
for (int i = 0; i < numDataElems; i++) {
sdata[i] = (short) (data.getElem(bankIndices[i], pixelOffset + bandOffsets[i] + 1) << 8|
data.getElem(bankIndices[i], pixelOffset + bandOffsets[i]));
}
return sdata;
}
};
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, null);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
I don't really see a reason for creating a RenderedImage subclass for this.

Converting `BufferedImage` to `Mat` in OpenCV

How can I convert a BufferedImage to a Mat in OpenCV?
I'm using the JAVA wrapper for OpenCV(not JavaCV). As I am new to OpenCV I have some problems understanding how Mat works.
I want to do something like this. (Based on Ted W. reply):
BufferedImage image = ImageIO.read(b.getClass().getResource("Lena.png"));
int rows = image.getWidth();
int cols = image.getHeight();
int type = CvType.CV_16UC1;
Mat newMat = new Mat(rows, cols, type);
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
newMat.put(r, c, image.getRGB(r, c));
}
}
Highgui.imwrite("Lena_copy.png", newMat);
This doesn't work. Lena_copy.png is just a black picture with the correct dimensions.
I also was trying to do the same thing, because of need to combining image processed with two libraries. And what I’ve tried to do is to put byte[] in to Mat instead of RGB value. And it worked! So what I did was:
1.Converted BufferedImage to byte array with:
byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
2. Then you can simply put it to Mat if you set type to CV_8UC3
image_final.put(0, 0, pixels);
Edit:
Also you can try to do the inverse as on this answer
Don't want to deal with big pixel array? Simply use this
BufferedImage to Mat
public static Mat BufferedImage2Mat(BufferedImage image) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", byteArrayOutputStream);
byteArrayOutputStream.flush();
return Imgcodecs.imdecode(new MatOfByte(byteArrayOutputStream.toByteArray()), Imgcodecs.CV_LOAD_IMAGE_UNCHANGED);
}
Mat to BufferedImage
public static BufferedImage Mat2BufferedImage(Mat matrix)throws IOException {
MatOfByte mob=new MatOfByte();
Imgcodecs.imencode(".jpg", matrix, mob);
return ImageIO.read(new ByteArrayInputStream(mob.toArray()));
}
Note, Though it's very negligible. However, in this way, you can get a reliable solution but it uses encoding + decoding. So you lose some performance. It's generally 10 to 20 milliseconds. JPG encoding loses some image quality also it's slow (may take 10 to 20ms). BMP is lossless and fast (1 or 2 ms) but requires little more memory (negligible). PNG is lossless but a little more time to encode than BMP. Using BMP should fit the most cases I think.
This one worked fine for me, and it takes from 0 to 1 ms to be performed.
public static Mat bufferedImageToMat(BufferedImage bi) {
Mat mat = new Mat(bi.getHeight(), bi.getWidth(), CvType.CV_8UC3);
byte[] data = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
mat.put(0, 0, data);
return mat;
}
I use following code in my program.
protected Mat img2Mat(BufferedImage in) {
Mat out;
byte[] data;
int r, g, b;
if (in.getType() == BufferedImage.TYPE_INT_RGB) {
out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC3);
data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
for (int i = 0; i < dataBuff.length; i++) {
data[i * 3] = (byte) ((dataBuff[i] >> 0) & 0xFF);
data[i * 3 + 1] = (byte) ((dataBuff[i] >> 8) & 0xFF);
data[i * 3 + 2] = (byte) ((dataBuff[i] >> 16) & 0xFF);
}
} else {
out = new Mat(in.getHeight(), in.getWidth(), CvType.CV_8UC1);
data = new byte[in.getWidth() * in.getHeight() * (int) out.elemSize()];
int[] dataBuff = in.getRGB(0, 0, in.getWidth(), in.getHeight(), null, 0, in.getWidth());
for (int i = 0; i < dataBuff.length; i++) {
r = (byte) ((dataBuff[i] >> 0) & 0xFF);
g = (byte) ((dataBuff[i] >> 8) & 0xFF);
b = (byte) ((dataBuff[i] >> 16) & 0xFF);
data[i] = (byte) ((0.21 * r) + (0.71 * g) + (0.07 * b));
}
}
out.put(0, 0, data);
return out;
}
Reference: here
I found a solution here.
The solution is similar to Andriys.
Camera c;
c.Connect();
c.StartCapture();
Image f2Img, cf2Img;
c.RetrieveBuffer(&f2Img);
f2Img.Convert( FlyCapture2::PIXEL_FORMAT_BGR, &cf2Img );
unsigned int rowBytes = (double)cf2Img.GetReceivedDataSize()/(double)cf2Img.GetRows();
cv::Mat opencvImg = cv::Mat( cf2Img.GetRows(), cf2Img.GetCols(), CV_8UC3, cf2Img.GetData(),rowBytes );
To convert from BufferedImage to Mat I use the method below:
public static Mat img2Mat(BufferedImage image) {
image = convertTo3ByteBGRType(image);
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
mat.put(0, 0, data);
return mat;
}
Before converting into Mat, I change the type of bufferedImage to TYPE_3BYTE_BGR, because to some types BufferedImages the method ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); may return int[] and that would break the code.
Below is the method for converting to TYPE_3BYTE_BGR.
private static BufferedImage convertTo3ByteBGRType(BufferedImage image) {
BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_3BYTE_BGR);
convertedImage.getGraphics().drawImage(image, 0, 0, null);
return convertedImage;
}
When you use as JavaCP wrapper bytedeco library (version 1.5.3) then you can use Java2DFrameUtils.
Simple usage is:
import org.bytedeco.javacv.Java2DFrameUtils;
...
BufferedImage img = ImageIO.read(new File("some/image.jpg");
Mat mat = Java2DFrameUtils.toMat(img);
Note: don't mix different wrappers, bytedeco Mat is different than opencv Mat.
One simple way would be to create a new using
Mat newMat = Mat(rows, cols, type);
then get the pixel values from your BufferedImage and put into newMat using
newMat.put(row, col, pixel);
You can do it in OpenCV as follows:
File f4 = new File("aa.png");
Mat mat = Highgui.imread(f4.getAbsolutePath());

Categories