Reading DataMatrix/QR code zxing java - java

The below data matrix is being read well using Barcode Scanner, zxing mobile app. However, the same is not being read by zxing java library.
I have some image transformation code commented. Even transforming the image, rotation or scaling doesn't help.
Ideally, I would like to perform all possible image pre-processing programatically until decoded.
What is the logic the mobile app using, since am scanning the same image from the computer screen and it is working.
Please find below, the code am using for decoding.
public class BarcodeReader {
private static Map<DecodeHintType,Object> hintsMap;
public static void main(String...args){
BufferedImage before = null;
hintsMap = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
hintsMap.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
hintsMap.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.allOf(BarcodeFormat.class));
//hintsMap.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);
try
{
before = ImageIO.read(new File("C:/ocr.jpg"));
decode(before);
/* for(int i=1; i < 1000;i++){
AffineTransform transform = new AffineTransform();
double rad = (double)i/100;
double scale = (double)i/100;
System.out.println("rad "+scale);
//transform.rotate(rad, before.getWidth()/2, before.getHeight()/2);
transform.scale(scale, scale);
BufferedImage after = new BufferedImage(before.getWidth(), before.getHeight(), BufferedImage.TYPE_INT_ARGB);
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
after = op.filter(before, after);
decode(after);
}*/
//tmpBfrImage = tmpBfrImage.getSubimage(200, 100, 800, 800);
}
catch (IOException tmpIoe)
{
tmpIoe.printStackTrace();
}
}
public static void decode(BufferedImage tmpBfrImage){
if (tmpBfrImage == null)
throw new IllegalArgumentException("Could not decode image.");
LuminanceSource tmpSource = new BufferedImageLuminanceSource(tmpBfrImage);
BinaryBitmap tmpBitmap = new BinaryBitmap(new HybridBinarizer(tmpSource));
MultiFormatReader tmpBarcodeReader = new MultiFormatReader();
Result tmpResult;
String tmpFinalResult;
try
{
if (hintsMap != null && ! hintsMap.isEmpty())
tmpResult = tmpBarcodeReader.decode(tmpBitmap, hintsMap);
else
tmpResult = tmpBarcodeReader.decode(tmpBitmap);
// setting results.
tmpFinalResult = String.valueOf(tmpResult.getText());
System.out.println(tmpFinalResult);
System.exit(0);;
}
catch (Exception tmpExcpt)
{
tmpExcpt.printStackTrace();
}
}
}

I had problems at multiple levels. I downloaded zxing source from github and debugged it.
The first problem was adding the below line as hints screws up the recognition hintsMap.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);
Looking at their source code for DataMatrixReader, there was a line doing this
if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE))
So, irrespective of setting PURE_BARCODE true or false, it considers it as true. Ideally hints should not contain the key.
The second problem was with the way the detector for DataMatrix works.
The detector was identifying the 'L' by looking at the number of black and white transitions from each vertices. Ideally, the transitions from Top-Left to Bottom-Left and Bottom-Left to Bottom-Right should have 0 transitions.
However, since the line was drawn closer towards the outer edge of the box, the transitions were not becoming 0. I made changes to move it closer to the center of the Left and Bottom Black Lines. This means moving the vertical red line to the right and the bottom red line a bit upwards. I added a new method Correct Points, that makes the necessary correction. This correction works for me, ideally one should be making the correction a bit more smarter.
ResultPoint pointA = correctPoints(cornerPoints[0], Vertices.TOPLEFT);
ResultPoint pointB = correctPoints(cornerPoints[1], Vertices.BOTTOMLEFT);
ResultPoint pointC = correctPoints(cornerPoints[2], Vertices.TOPRIGHT);
ResultPoint pointD = correctPoints(cornerPoints[3], Vertices.BOTTOMRIGHT);
---
---
private ResultPoint correctPoints(ResultPoint point, Vertices vertice){
if(vertice.equals(Vertices.TOPLEFT))
return new ResultPoint(point.getX()+10, point.getY()+5);
else if(vertice.equals(Vertices.BOTTOMLEFT)){
return new ResultPoint(point.getX()+10, point.getY()-5);
}else if(vertice.equals(Vertices.TOPRIGHT)){
return new ResultPoint(point.getX(), point.getY()+10);
}else{
return new ResultPoint(point.getX()-10, point.getY()-5);
}
}
After making these changes, data matrix detection was working for images that were as bad as or even poorer than these.

I was having similar problems using ZXing to decode DataMatrix barcodes. From what I can see, ZXing doesn't traverse the entire image you send it, but rather starts from the middle and expands out until it has found a barcode. So, if the DataMatrix barcode isn't centered in the image, ZXing will not be able to reliably find it. I implemented (a rather slow) workaround that fixes this problem, by creating different cropped versions of the image:
My core decode method is similar to that of the original post. My image traversal logic is as follows:
// Read the original image
final BufferedImage image = ImageIO.read(...);
final int width = image.getWidth();
final int height = image.getHeight();
// Try detect codes using different sections of the image.
//
// +------+------+
// | ##|## |
// | ##|## |
// | ##|## |
// +------+------+
// | | |
// | | |
// | | |
// +------+------+
//
// We create 9 cropped versions of the image, with each cropped
// version being 1/4 of the original image. We traverse the
// original image from left-to-right, top-to-bottom, and create
// 9 sub-images that we try to decode in turn.
for (int i=0; i<3; i++) {
for (int j=0; j<3; j++) {
final int x = i * width / 4;
final int y = j * height / 4;
final BufferedImage crop = image.getSubimage(x, y, width / 2, height / 2);
decoded(crop);
}
}

Related

BufferedImage causes a program freeze on MacOs but not on Windows

I'm using a piece of code to grab a screenshot of my application screen for a group project. On my Macbook Pro the code freezes the screen whereas on my teammates's PC's (all Windows) it runs just fine and exports a .png file in their root dir.
The code
public void screenShot(){
//Creating an rbg array of total pixels
int[] pixels = new int[WIDTH * HEIGHT];
int bindex;
// allocate space for RBG pixels
ByteBuffer fb = ByteBuffer.allocateDirect(WIDTH * HEIGHT * 3);
// grab a copy of the current frame contents as RGB
glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, fb);
// convert RGB data in ByteBuffer to integer array
for (int i=0; i < pixels.length; i++) {
bindex = i * 3;
pixels[i] =
((fb.get(bindex) << 16)) +
((fb.get(bindex+1) << 8)) +
((fb.get(bindex+2) << 0));
}
//Allocate colored pixel to buffered Image
BufferedImage imageIn = null;
try{
//THIS LINE
imageIn = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
//THIS LINE ^^^^^
imageIn.setRGB(0, 0, WIDTH, HEIGHT, pixels, 0 , WIDTH);
} catch (Exception e) {
e.printStackTrace();
}
The problem
When debugging I can see that when stepping in at this line
imageIn = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
the debugger doesn't go to the BufferedImage constructor but to GLFWKeyCallbackI.callback() and after that to GLFWCursorEnterCallbackI.callback(). After this it stops altogether.
What I tried
In my main class above all the rest of the code making a buffered Image as such:
BufferedImage imageIn = new BufferedImage(100,100,BufferedImage.TYPE_INT_RGB);
It also freezes the simulation but it does seems to actually execute the line.
I'm not sure what else I could try, I saw a few other posts ranging between 2005 and today asking similar Mac questions without an answer.
I delved a bit deeper and discovered the issue. As mentioned in a comment here if I provide this VM option "-Djava.awt.headless=true" it seems to fix the issue.

Java BufferedImage gray to RGB conversion

I have a 8 bit gray scale bitmap on which I need to do some pattern recognition. I created a first test frame work in java and it worked fine.
After that I ported everything to C++ and found out that my patterns are no longer found.
After some investigation I realized that in the java code there was a "hidden" format change from TYPE_BYTE_GRAY to TYPE_3BYTE_BGR.
I could brake it down to the following test function:
public static void ConvertFiles(File dir, String format)
{
File[] images = getOrderedFiles(dir, format);
for (int i = 0; i < images.length; i++)
{
try
{
BufferedImage img = ImageIO.read(images[i]);
BufferedImage dst = new BufferedImage(img.getWidth() , img.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
for (int xTarget = 0; xTarget <img.getWidth(); xTarget++)
{
for (int yTarget = 0; yTarget <img.getHeight(); yTarget++)
{
int val = img.getRGB(xTarget, yTarget);
dst.setRGB(xTarget,yTarget, val);
}
}
ImageIO.write(dst, "bmp", new File(correctSlash(images[i].getParent()) + "Convert\\" + images[i].getName()));
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
The resulting file seems to be "brighter" (can't post the images, sorry. I can sen them on request). When I use TYPE_BYTE_GRAY when creating the new image no change appears, so it is obvious how to avoid the effect in Java.
Problem is now, I like the "brightened" image better and would like to know what is happening here so I can reproduce this as some kind of image enhancement.
Thx in advance.
Found it by digging into the java code (getRGB()) with a colleague (thank you Holger).
There is a conversion applied to the originally gray value by a look up table which is generated like this:
l8Tos8 = new byte[256];
float input, output;
// algorithm for linear RGB to nonlinear sRGB conversion
// is from the IEC 61966-2-1 International Standard,
// Colour Management - Default RGB colour space - sRGB,
// First Edition, 1999-10,
// avaiable for order at http://www.iec.ch
for (int i = 0; i <= 255; i++) {
input = ((float) i) / 255.0f;
if (input <= 0.0031308f) {
output = input * 12.92f;
} else {
output = 1.055f * ((float) Math.pow(input, (1.0 / 2.4)))
- 0.055f;
}
l8Tos8[i] = (byte) Math.round(output * 255.0f);
}
So at least in parts gamma correction.
I can apply this on the C++ side an get the same result.

Java: saving image as JPEG skew problem

I am trying to save an image to JPEG. The code below works fine when image width is a multiple of 4, but the image is skewed otherwise. It has something to do with padding. When I was debugging I was able to save the image as a bitmap correctly, by padding each row with 0s. However, this did not work out with the JPEG.
Main point to remember is my image is represented as bgr (blue green red 1 byte each) byte array which I receive from a native call.
byte[] data = captureImage(OpenGLCanvas.getLastFocused().getViewId(), x, y);
if (data.length != 3*x*y)
{
// 3 bytes per pixel
return false;
}
// create buffered image from raw data
DataBufferByte buffer = new DataBufferByte(data, 3*x*y);
ComponentSampleModel csm = new ComponentSampleModel(DataBuffer.TYPE_BYTE, x, y, 3, 3*x, new int[]{0,1,2} );
WritableRaster raster = Raster.createWritableRaster(csm, buffer, new Point(0,0));
BufferedImage buff_image = new BufferedImage(x, y, BufferedImage.TYPE_INT_BGR); // because windows goes the wrong way...
buff_image.setData(raster);
//save the BufferedImage as a jpeg
try
{
File file = new File(file_name);
FileOutputStream out = new FileOutputStream(file);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(buff_image);
param.setQuality(1.0f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(buff_image);
out.close();
// or JDK 1.4
// ImageIO.write(image, "JPEG", out);
}
catch (Exception ex)
{
// Write permissions on "file_name"
return false;
}
I also looked on creating the JPEG in C++ but there was even less material on that, but it is still an option.
Any help greatly apprecieated.
Leon
Thanks for your suggestions, but I have managed to work it out.
To capture the image I was using WINGDIAPI HBITMAP WINAPI CreateDIBSection in C++, then OpenGL would draw to that bitmap. Unbeknown to be, there was padding added to the bitmap automatically the width was not a multiple of 4.
Therefore Java was incorrectly interpreting the byte array.
Correct way is to interpret bytes is
byte[] data = captureImage(OpenGLCanvas.getLastFocused().getViewId(), x, y);
int x_padding = x%4;
BufferedImage buff_image = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB);
int val;
for (int j = 0; j < y; j++)
{
for (int i = 0; i < x; i++)
{
val = ( data[(i + j*x)*3 + j*x_padding + 2]& 0xff) +
((data[(i + j*x)*3 + j*x_padding + 1]& 0xff) << 8) +
((data[(i + j*x)*3 + j*x_padding + 0]& 0xff) << 16);
buff_image.setRGB(i, j, val);
}
}
//save the BufferedImage as a jpeg
try
{
File file = new File(file_name);
FileOutputStream out = new FileOutputStream(file);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(buff_image);
param.setQuality(1.0f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(buff_image);
out.close();
}
The JPEG standard is extremely complex. I am thinking it may be an issue with padding the output of the DCT somehow. The DCT is done to transform the content from YCrCb 4:2:2 to signal space with one DCT for each channel, Y,Cr, and Cb. The DCT is done on a "Macroblock" or "minimum coded block" depending on your context. JPEG usually has 8x8 macroblocks. When on the edge and there are not enough pixel it clamps the edge value and "drags it across" and does a DCT on that.
I am not sure if this helps, but it sounds like a non standard conforming file. I suggest you use JPEGSnoop to find out more. There are also several explanations about how JPEG compression works.
One possibility is that the sample rate may be encoded incorrectly. It might be something exotic such as 4:2:1 So you might be pulling twice as many X samples as there really are, thus distorting the image.
it is an image I capture from the screen
Maybe the Screen Image class will be easier to use.

Lossless JPEG Rotate (90/180/270 degrees) in Java?

Is there a Java library for rotating JPEG files in increments of 90 degrees, without incurring image degradation?
I found this: http://mediachest.sourceforge.net/mediautil/
API: http://mediachest.sourceforge.net/mediautil/javadocs/mediautil/image/jpeg/LLJTran.html
Building on Henry's answer, here's an example of how to use MediaUtil to perform lossless JPEG rotation based on the EXIF data:
try {
// Read image EXIF data
LLJTran llj = new LLJTran(imageFile);
llj.read(LLJTran.READ_INFO, true);
AbstractImageInfo<?> imageInfo = llj.getImageInfo();
if (!(imageInfo instanceof Exif))
throw new Exception("Image has no EXIF data");
// Determine the orientation
Exif exif = (Exif) imageInfo;
int orientation = 1;
Entry orientationTag = exif.getTagValue(Exif.ORIENTATION, true);
if (orientationTag != null)
orientation = (Integer) orientationTag.getValue(0);
// Determine required transform operation
int operation = 0;
if (orientation > 0
&& orientation < Exif.opToCorrectOrientation.length)
operation = Exif.opToCorrectOrientation[orientation];
if (operation == 0)
throw new Exception("Image orientation is already correct");
OutputStream output = null;
try {
// Transform image
llj.read(LLJTran.READ_ALL, true);
llj.transform(operation, LLJTran.OPT_DEFAULTS
| LLJTran.OPT_XFORM_ORIENTATION);
// Overwrite original file
output = new BufferedOutputStream(new FileOutputStream(imageFile));
llj.save(output, LLJTran.OPT_WRITE_ALL);
} finally {
IOUtils.closeQuietly(output);
llj.freeMemory();
}
} catch (Exception e) {
// Unable to rotate image based on EXIF data
...
}
Regarding the issue of EXIF data not necessarily being handled correctly, since EXIF data is irrelevant in many situations, here's example code demonstrating only the LLJTran lossless JPEG rotation feature (with thanks to user113215):
final File SrcJPEG = new File("my-input.jpg");
final File DestJPEG = new File("my-output.jpg");
final FileInputStream In = new FileInputStream(SrcJPEG);
try {
final LLJTran LLJT = new LLJTran(In);
LLJT.read(LLJTran.READ_ALL, true);
LLJT.transform(LLJTran.ROT_90);
final FileOutputStream Out = new FileOutputStream(DestJPEG);
try {
LLJT.save(Out, LLJTran.OPT_WRITE_ALL);
} finally {
Out.close();
}
} finally {
In.close();
}
If you make the input and output File objects refer to the same file, you can run this over and over again, and observe that the image does not degrade, no matter how many iterations it is put through.
For Android specifically, I found this fork:
https://github.com/juanitobananas/AndroidMediaUtil
Benefits over upstream:
Gradle/Android Studio project
Compatible with jitpack.io
It might even be usable on normal Java, as the code does not import any Android-specific package (I haven't tried though).
You don't need an external library for this kind of thing, it's all built into SE. The easiest being the rotate() function of the Graphics2D object.
For example:
Image rotatedImage = new BufferedImage(imageToRotate.getHeight(null), imageToRotate.getWidth(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) rotatedImage.getGraphics();
g2d.rotate(Math.toRadians(90.0));
g2d.drawImage(imageToRotate, 0, -rotatedImage.getWidth(null), null);
g2d.dispose();
no loss!
Or, if you want to be extra careful, just use BufferedImage.getRGB(x,y), and translate it pixel by pixel on to the new image.

How to get image height and width using java?

Is there any other way besides using ImageIO.read to get image height and width?
Because I encounter an issue that locks up the thread.
at com.sun.medialib.codec.jpeg.Decoder.njpeg_decode(Native Method)
at com.sun.medialib.codec.jpeg.Decoder.decode(Decoder.java:87)
at com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader.decode(CLibJPEGImageReader.java:73)
- locked <0xd96fb668> (a com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader)
at com.sun.media.imageioimpl.plugins.clib.CLibImageReader.getImage(CLibImageReader.java:320)
- locked <0xd96fb668> (a com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader)
at com.sun.media.imageioimpl.plugins.clib.CLibImageReader.read(CLibImageReader.java:384)
- locked <0xd96fb668> (a com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader)
at javax.imageio.ImageIO.read(ImageIO.java:1400)
at javax.imageio.ImageIO.read(ImageIO.java:1322)
This error only occurs on a Sun app server and therefore I suspect that it is a Sun bug.
Here is something very simple and handy.
BufferedImage bimg = ImageIO.read(new File(filename));
int width = bimg.getWidth();
int height = bimg.getHeight();
This is a rewrite of the great post by #Kay, which throws IOException and provides an early exit:
/**
* Gets image dimensions for given file
* #param imgFile image file
* #return dimensions of image
* #throws IOException if the file is not a known image
*/
public static Dimension getImageDimension(File imgFile) throws IOException {
int pos = imgFile.getName().lastIndexOf(".");
if (pos == -1)
throw new IOException("No extension for file: " + imgFile.getAbsolutePath());
String suffix = imgFile.getName().substring(pos + 1);
Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
while(iter.hasNext()) {
ImageReader reader = iter.next();
try {
ImageInputStream stream = new FileImageInputStream(imgFile);
reader.setInput(stream);
int width = reader.getWidth(reader.getMinIndex());
int height = reader.getHeight(reader.getMinIndex());
return new Dimension(width, height);
} catch (IOException e) {
log.warn("Error reading: " + imgFile.getAbsolutePath(), e);
} finally {
reader.dispose();
}
}
throw new IOException("Not a known image file: " + imgFile.getAbsolutePath());
}
I guess my rep is not high enough for my input to be considered worthy as a reply.
I tried to test performance using some of the various approaches listed. It's hard to make a rigorous test as many factors affect the result. I prepared two folders, one with 330 jpg files and another one with 330 png files. The average file size was 4Mb in both cases. Then I called getDimension for each file. Each implementation of getDimension method and each image type was tested separately (separate run). Here is the execution times that I got (first number for jpg, second number for png):
1(Apurv) - 101454ms, 84611ms
2(joinJpegs) - 471ms, N/A
3(Andrew Taylor) - 707ms, 68ms
4(Karussell, ImageIcon) - 106655ms, 100898ms
5(user350756) - 2649ms, 68ms
It's obvious that some methods load the whole file in order to get dimensions while others get by just reading some header information from the image. I think these numbers may be useful when application performance is critical.
Thank you everyone for the contribution to this thread - very helpful.
I have found another way to read an image size (more generic).
You can use ImageIO class in cooperation with ImageReaders.
Here is the sample code:
private Dimension getImageDim(final String path) {
Dimension result = null;
String suffix = this.getFileSuffix(path);
Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffix);
if (iter.hasNext()) {
ImageReader reader = iter.next();
try {
ImageInputStream stream = new FileImageInputStream(new File(path));
reader.setInput(stream);
int width = reader.getWidth(reader.getMinIndex());
int height = reader.getHeight(reader.getMinIndex());
result = new Dimension(width, height);
} catch (IOException e) {
log(e.getMessage());
} finally {
reader.dispose();
}
} else {
log("No reader found for given format: " + suffix));
}
return result;
}
Note that getFileSuffix is method that returns extension of path without "." so e.g.: png, jpg etc.
Example implementation is:
private String getFileSuffix(final String path) {
String result = null;
if (path != null) {
result = "";
if (path.lastIndexOf('.') != -1) {
result = path.substring(path.lastIndexOf('.'));
if (result.startsWith(".")) {
result = result.substring(1);
}
}
}
return result;
}
This solution is very quick as only image size is read from the file and not the whole image. I tested it and there is no comparison to ImageIO.read performance. I hope someone will find this useful.
You can load jpeg binary data as a file and parse the jpeg headers yourself. The one you are looking for is the 0xFFC0 or Start of Frame header:
Start of frame marker (FFC0)
* the first two bytes, the length, after the marker indicate the number of bytes, including the two length bytes, that this header contains
* P -- one byte: sample precision in bits (usually 8, for baseline JPEG)
* Y -- two bytes
* X -- two bytes
* Nf -- one byte: the number of components in the image
o 3 for color baseline JPEG images
o 1 for grayscale baseline JPEG images
* Nf times:
o Component ID -- one byte
o H and V sampling factors -- one byte: H is first four bits and V is second four bits
o Quantization table number-- one byte
The H and V sampling factors dictate the final size of the component they are associated with. For instance, the color space defaults to YCbCr and the H and V sampling factors for each component, Y, Cb, and Cr, default to 2, 1, and 1, respectively (2 for both H and V of the Y component, etc.) in the Jpeg-6a library by the Independent Jpeg Group. While this does mean that the Y component will be twice the size of the other two components--giving it a higher resolution, the lower resolution components are quartered in size during compression in order to achieve this difference. Thus, the Cb and Cr components must be quadrupled in size during decompression.
For more info about the headers check out wikipedia's jpeg entry or I got the above info here.
I used a method similar to the code below which I got from this post at the sun forums:
import java.awt.Dimension;
import java.io.*;
public class JPEGDim {
public static Dimension getJPEGDimension(File f) throws IOException {
FileInputStream fis = new FileInputStream(f);
// check for SOI marker
if (fis.read() != 255 || fis.read() != 216)
throw new RuntimeException("SOI (Start Of Image) marker 0xff 0xd8 missing");
Dimension d = null;
while (fis.read() == 255) {
int marker = fis.read();
int len = fis.read() << 8 | fis.read();
if (marker == 192) {
fis.skip(1);
int height = fis.read() << 8 | fis.read();
int width = fis.read() << 8 | fis.read();
d = new Dimension(width, height);
break;
}
fis.skip(len - 2);
}
fis.close();
return d;
}
public static void main(String[] args) throws IOException {
System.out.println(getJPEGDimension(new File(args[0])));
}
}
Simple way:
BufferedImage readImage = null;
try {
readImage = ImageIO.read(new File(your path);
int h = readImage.getHeight();
int w = readImage.getWidth();
} catch (Exception e) {
readImage = null;
}
Having struggled with ImageIO a lot in the past years, I think Andrew Taylor's solution is by far the best compromise (fast: not using ImageIO#read, and versatile). Thanks man!!
But I was a little frustrated to be compelled to use a local file (File/String), especially in cases where you want to check image sizes coming from, say, a multipart/form-data request where you usually retrieve InputPart/InputStream's. So I quickly made a variant that accepts File, InputStream and RandomAccessFile, based on the ability of ImageIO#createImageInputStream to do so.
Of course, such a method with Object input, may only remain private and you shall create as many polymorphic methods as needed, calling this one. You can also accept Path with Path#toFile() and URL with URL#openStream() prior to passing to this method:
private static Dimension getImageDimensions(Object input) throws IOException {
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) { // accepts File, InputStream, RandomAccessFile
if(stream != null) {
IIORegistry iioRegistry = IIORegistry.getDefaultInstance();
Iterator<ImageReaderSpi> iter = iioRegistry.getServiceProviders(ImageReaderSpi.class, true);
while (iter.hasNext()) {
ImageReaderSpi readerSpi = iter.next();
if (readerSpi.canDecodeInput(stream)) {
ImageReader reader = readerSpi.createReaderInstance();
try {
reader.setInput(stream);
int width = reader.getWidth(reader.getMinIndex());
int height = reader.getHeight(reader.getMinIndex());
return new Dimension(width, height);
} finally {
reader.dispose();
}
}
}
throw new IllegalArgumentException("Can't find decoder for this image");
} else {
throw new IllegalArgumentException("Can't open stream for this image");
}
}
}
You could use the Toolkit, no need for ImageIO
Image image = Toolkit.getDefaultToolkit().getImage(file.getAbsolutePath());
int width = image.getWidth(null);
int height = image.getHeight(null);
If you don't want to handle the loading of the image do
ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
int height = imageIcon.getIconHeight();
int width = imageIcon.getIconWidth();
Problem with ImageIO.read is that it is really slow. All you need to do is to read image header to get the size. ImageIO.getImageReader is perfect candidate.
Here is the Groovy example, but the same thing applies to Java
def stream = ImageIO.createImageInputStream(newByteArrayInputStream(inputStream))
def formatReader = ImageIO.getImageWritersByFormatName(format).next()
def reader = ImageIO.getImageReader(formatReader)
reader.setInput(stream, true)
println "width:reader.getWidth(0) -> height: reader.getHeight(0)"
The performance was the same as using SimpleImageInfo java library.
https://github.com/cbeust/personal/blob/master/src/main/java/com/beust/SimpleImageInfo.java
You can get width and height of image with BufferedImage object using java.
public void setWidthAndHeightImage(FileUploadEvent event) {
byte[] imageTest = event.getFile().getContents();
baiStream = new ByteArrayInputStream(imageTest);
BufferedImage bi = ImageIO.read(baiStream);
//get width and height of image
int imageWidth = bi.getWidth();
int imageHeight = bi.getHeight();
}
To get a Buffered Image with ImageIO.read is a very heavy method, as it's creating a complete uncompressed copy of the image in memory. For png's you may also use pngj and the code:
if (png)
PngReader pngr = new PngReader(file);
width = pngr.imgInfo.cols;
height = pngr.imgInfo.rows;
pngr.close();
}
public static Optional<Dimension> getImageDimensions(Path imageFile) {
Optional<String> suffixOpt = getExtension(imageFile);
Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix(suffixOpt.orElse(""));
while (iter.hasNext()) {
ImageReader reader = iter.next();
try (ImageInputStream stream = new FileImageInputStream(imageFile.toFile())) {
reader.setInput(stream);
return Optional.of(new Dimension(reader.getWidth(reader.getMinIndex()),
reader.getHeight(reader.getMinIndex())));
} catch (IOException e) {
log.warn("Error reading: " + imageFile, e); //or however you want to handle the exception
} finally {
reader.dispose();
}
}
return Optional.empty();
}
public static Optional<String> getExtension(Path file) {
int pos = file.getFileName().toString().lastIndexOf(".");
if (pos == -1) {
return Optional.empty();
}
return Optional.of(file.getFileName().toString().substring(pos + 1));
}
Revised the method by #Andrew Taylor to use Optionals.
Also uses the Java's NIO Path to make the transition to Path.getExt easier in Java 21 (the second method can be removed and getExtension(imageFile) can be replaced with imageFile.getExtension()).
Also uses the try-with-resources design from Java.
One could instead use an external library in place of the second method if that's preferable.
Using a Spliterator could be another way, though in the end the code became more verbose as little is gained by converting from an Iterator.
So unfortunately, after trying all the answers from above, I did not get them to work after tireless times of trying. So I decided to do the real hack myself and I go this to work for me. I trust it would work perfectly for you too.
I am using this simple method to get the width of an image generated by the app and yet to be upload later for verification :
Pls. take note : you would have to enable permissions in manifest for access storage.
/I made it static and put in my Global class so I can reference or access it from just one source and if there is any modification, it would all have to be done at just one place. Just maintaining a DRY concept in java. (anyway) :)/
public static int getImageWidthOrHeight(String imgFilePath) {
Log.d("img path : "+imgFilePath);
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgFilePath, o);
int width_tmp = o.outWidth, height_tmp = o.outHeight;
Log.d("Image width : ", Integer.toString(width_tmp) );
//you can decide to rather return height_tmp to get the height.
return width_tmp;
}
To get size of emf file without EMF Image Reader you can use code:
Dimension getImageDimForEmf(final String path) throws IOException {
ImageInputStream inputStream = new FileImageInputStream(new File(path));
inputStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
// Skip magic number and file size
inputStream.skipBytes(6*4);
int left = inputStream.readInt();
int top = inputStream.readInt();
int right = inputStream.readInt();
int bottom = inputStream.readInt();
// Skip other headers
inputStream.skipBytes(30);
int deviceSizeInPixelX = inputStream.readInt();
int deviceSizeInPixelY = inputStream.readInt();
int deviceSizeInMlmX = inputStream.readInt();
int deviceSizeInMlmY = inputStream.readInt();
int widthInPixel = (int) Math.round(0.5 + ((right - left + 1.0) * deviceSizeInPixelX / deviceSizeInMlmX) / 100.0);
int heightInPixel = (int) Math.round(0.5 + ((bottom-top + 1.0) * deviceSizeInPixelY / deviceSizeInMlmY) / 100.0);
inputStream.close();
return new Dimension(widthInPixel, heightInPixel);
}

Categories

Resources