I am trying to use this new feature of CameraX Image Analysis (version 1.1.0-alpha08): using setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888), images sent to the analyzer will have RGBA format.
See this for reference: https://developer.android.com/reference/androidx/camera/core/ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888
I need to turn the image sent to the analyzer into a Bitmap so that I can input it to a TensorFlow classifier.
Without this new feature I would receive the image in the standard YUV_420_888 format then I would have to use one of the several solutions that can be googled in order to turn YUV_420_888 to RGBA then to Bitmap. Like this: https://blog.minhazav.dev/how-to-convert-yuv-420-sp-android.media.Image-to-Bitmap-or-jpeg/.
I assume getting the Media Image directly in RGBA format should help me avoid implementing those painfull solutions (that I have actually tried and do not seem to work very well for me so far).
Problem is I don't know how to turn this RGBA Media Image into a Bitmap. I have noticed that calling mediaImage.getFormat() returns 1 which is not an ImageFormat value but a PixelFormat one, the one logically corresponding to RGBA_8888 format, which is in line with the documentation: "All ImageProxy sent to ImageAnalysis.Analyzer.analyze(ImageProxy) will have format PixelFormat.RGBA_8888".
I have tried this:
private Bitmap toBitmapRGBA(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
buffer.rewind();
int size = buffer.remaining();
byte[] bytes = new byte[size];
buffer.get(bytes);
Bitmap bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
return bitmapImage;
}
This returns null indicating the decodeByteArray does not work. (I notice the image has got only one plane).
private Bitmap toBitmapRGBA2(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
buffer.rewind();
bitmap.copyPixelsFromBuffer(buffer);
return bitmap;
}
This returns a Bitmap that looks noting but noise.
Please help!
Kind regards
Mickael
I actually found a solution myself, so I post it here if anyone is interested:
private Bitmap toBitmap(Image image) {
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * image.getWidth();
Bitmap bitmap = Bitmap.createBitmap(image.getWidth()+rowPadding/pixelStride,
image.getHeight(), Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
return bitmap;
}
if you want to process the pixel array further on without creating a bitmap object you can do something like this:
val data = imageProxy.planes[0].buffer.toByteArray()
val pixels = IntArray(data.size / imageProxy.planes[0].pixelStride) {
var index = it * imageProxy.planes[0].pixelStride
(data[index++].toInt() and 0xff.shl(16)) or
(data[index++].toInt() and 0xff).shl(8) or
(data[index++].toInt() and 0xff).shl(0) or
(data[index].toInt() and 0xff).shl(24)
}
And then you can create bitmap this way:
Bitmap.createBitmap(
pixels,
0,
imageProxy.planes[0].rowStride / imageProxy.planes[0].pixelStride,
imageProxy.width,
imageProxy.height,
Bitmap.Config.ARGB_8888
)
I am using CameraX for capturing images then feeding into mlkit scanner to scan barcodes, I have created a crop rect and applied to the media.image but it doesn't seem cropping the frame...
#androidx.camera.core.ExperimentalGetImage
override fun analyze(imageProxy: ImageProxy) {
imageProxy.image ?. let {
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
val imageHeight = it.height
val imageWidth = it.width
val cropRect = Rect(0, 0, imageWidth, imageHeight)
cropRect.inset(
(imageWidth/ 2),
(imageHeight/ 2)
)
it.cropRect = cropRect
val image = InputImage.fromMediaImage(it, imageProxy.imageInfo.rotationDegrees)
}
}
I tried saving the media.Image fed to scanner to a bitmap and that is not cropped too.. Let me know what I am doing wrong.
I checked this answer but it is explaining with bitmaps and I am working with media.Image
Android: How to crop images using CameraX?
ImageProxy.setCropRect()is just metadata that is passed along with the Image, and it is up to the consumer of the Image to apply the crop rect while accessing the pixels. ImageProxy.setCropRect() does not affect the crop rect of the underlying android.media.Image, it only changes the crop rect metadata for the ImageProxy instance.
how do I get the resolution of an image.
BufferedImage theBufferedImage = ImageIO.read(new File("/tmp/foo.jpg"));
int width = theBufferedImage.getWidth();
int height = theBufferedImage.getHeight();
int resolution = ???
Regards,
saromba
You can't get DPI or any other such information out of BufferedImage, you'll have to examine the original JPEG file for it. It's just a raster image without any metadata. If you're writing a BufferedImage to a file, you can set the DPI. But BufferedImage itself has no concept of DPI, it's just pixels.
I have a requirement, like drawing something in canvas and saving it to a larger image. As of now whatever I draw inside onDraw() method and save, it gives device provided image/canvas size, say something around 538(w)/852(h). I need image of almost double size, around 1000(w)/1500(h) without losing resolution. Any sample code, reference link would help definitely. Thanks in advance.
One way is to create Bitmap of desired size and draw on it using canvas:
int w = 1000, h = 1500;
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bmp = Bitmap.createBitmap(w, h, conf);
Canvas canvas = new Canvas(bmp);
Finally you will have you bmp with your drawing and necessary dimentions.
EDIT::
#Override
protected void onDraw(Canvas canvas) {
int w = 1000, h = 1500;
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bmp = Bitmap.createBitmap(w, h, conf);
Canvas mycanvas = new Canvas(bmp);
super.onDraw(mycanvas);
...
}
First get bitmap of your view using the function getDrawingCache(), then scale the bitmap as per your requirements.
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
//bitmap = bitmap.createScaledBitmap(..as your requirements..);
EDIT
You are saying that you want image in higher quality. You can't simply get a higher quality image from a lower quality image. However you can apply bitmap filtering to get slightly better quality.
Bitmap bitmap = getDrawingCache();
Bitmap output = Bitmap.createBitmap(/*width, height, bla bla bla*/);
Canvas canvas = new Canvas(output);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
Rect srcRect = new Rect(0,0,bitmap.getWidth(), bitmap.getHeight());
Rect destRect = new Rect(0,0,output.getWidth(), output.getHeight());
canvas.drawBitmap(bitmap, srcRect, destRect, paint);
I have a web application written in Java (Spring, Hibernate/JPA, Struts2) where users can upload images and store them in the file system. I would like to scale those images so that they are of a consistent size for display on the site. What libraries or built in functions will offer the best results? I will consider the following criteria in making my decision (in this order):
Free/Open Source (essential)
Easy to implement
Quality of results
Performance
Size of executable
I would really recommend giving imgscalr a look.
It is released under an Apache 2 license, hosted on GitHub, been deployed in a handful of web applications already, has a very simple, but pedantically documented API, has code that works around 2 major image bugs in the JDK for you transparently that you'll only ever notice if you suddenly start getting "black" images after a scale operation or horrible-looking results, gives you the best possible looking results available in Java, is available via Maven as well as a ZIP and is just a single class.
Basic use looks like this:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, 320);
This is the simplest call where the library will make a best-guess at the quality, honor your image proportions, and fit the result within a 320x320 bounding box. NOTE, the bounding box is just the maximum W/H used, since your image proportions are honored, the resulting image would still honor that, say 320x200.
If you want to override the automatic mode and force it to give you the best-looking result and even apply a very mild anti-alias filter to the result so it looks even better (especially good for thumbnails), that call would look like:
BufferedImage img = ImageIO.read(...); // load image
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY,
150, 100, Scalr.OP_ANTIALIAS);
These are all just examples, the API is broad and covers everything from super-simple use cases to very specialized. You can even pass in your own BufferedImageOps to be applied to the image (and the library automatically fixes the 6-year BufferedImageOp JDK bug for you!)
There is a lot more to scaling images in Java successfully than the library does for you, for example always keeping the image in one of the best supported RGB or ARGB image types while operating on it. Under the covers the Java2D image processing pipeline falls back to an inferior software pipeline if the image type used for any image operations is poorly supported.
If all that sounded like a lot of headaches, it sort of is... that's why I wrote the library and open-sourced it, so folks could just resize their images and move on with their lives without needing to worry about it.
Hope that helps.
Have a look at the Java Image I/O API to read/write the image. Then use AffineTransform to resize.
Also, here's a complete example using java.awt.Image.
Look into also to java-image-scaling library. It created better quality images that ImageIO.
The best tool for image editing is ImageMagick and it is open source.
There are two interfaces for the Java Language:
JMagick which uses JNI interface to ImageMagick
and
im4java what is a command line interface for ImageMagick
Found this to be faster:
public static BufferedImage getScaledInstance(final BufferedImage img, final int targetWidth, final int targetHeight,
final Object hint) {
final int type = BufferedImage.TYPE_INT_RGB;
int drawHeight = targetHeight;
int drawWidth = targetWidth;
final int imageWidth = img.getWidth();
final int imageHeight = img.getHeight();
if ((imageWidth <= targetWidth) && (imageHeight <= targetHeight)) {
logger.info("Image " + imageWidth + "/" + imageHeight + " within desired scale");
return img;
}
final double sar = ((double) imageWidth) / ((double) imageHeight);
if (sar != 0) {
final double tar = ((double) targetWidth) / ((double) targetHeight);
if ((Math.abs(tar - sar) > .001) && (tar != 0)) {
final boolean isSoureWider = sar > (targetWidth / targetHeight);
if (isSoureWider) {
drawHeight = (int) (targetWidth / sar);
}
else {
drawWidth = (int) (targetHeight * sar);
}
}
}
logger.info("Scaling image from " + imageWidth + "/" + imageHeight + " to " + drawWidth + "/" + drawHeight);
final BufferedImage result = new BufferedImage(drawWidth, drawHeight, type);
try {
final Graphics2D g2 = result.createGraphics();
try {
if (hint != null) {
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
}
g2.drawImage(img, 0, 0, drawWidth, drawHeight, null);
}
finally {
g2.dispose();
}
return result;
}
finally {
result.flush();
}
}
I know this is a very old question, but I got my own solution for this using standard Java API
import java.awt.*;
import java.awt.event.*;
import javax.imageio.*
import java.awt.image.*;
BufferedImage im, bi, bi2;
Graphics2D gfx;
int imWidth, imHeight, dstWidth, dstHeight;
int DESIRED_WIDTH = 500, DESIRED_HEIGHT = 500;
im = ImageIO.read(new File(filePath));
imWidth = im.getWidth(null);
imHeight = im.getHeight(null);
dstWidth = DESIRED_WIDTH;
dstHeight = (dstWidth * imHeight) / imWidth;
bi = new BufferedImage(dstWidth, dstHeight, im.getType());
gfx = bi.createGraphics();
gfx.drawImage(im, 0, 0, dstWidth, dstHeight, 0, 0, imWidth, imHeight, null);
bi2 = new BufferedImage(DESIRED_WIDTH, DESIRED_HEIGHT, im.getType());
gfx = bi2.createGraphics();
gfx.drawImage(bi, 0, 0, DESIRED_WIDTH, DESIRED_HEIGHT, null);
ImageIO.write(bi2, "jpg", new File(filePath));
I am sure it can be improved and adapted.
I tried imgscalr comparing to standard Java 1.6 and I cannot say it is better.
What I've tried is
BufferedImage bufferedScaled = Scalr.resize(sourceImage, Method.QUALITY, 8000, height);
and
Image scaled = sourceImage.getScaledInstance(-1, height, Image.SCALE_SMOOTH);
BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB);
bufferedScaled.getGraphics().drawImage(scaled, 0, 0, null);
some 5 minute testing by my eye got impression that second thing (pure Java 1.6) produces better results.