130

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.

silver est
  • 51
  • 6

14 Answers14

324

Here is something very simple and handy.

BufferedImage bimg = ImageIO.read(new File(filename));
int width          = bimg.getWidth();
int height         = bimg.getHeight();
alex
  • 479,566
  • 201
  • 878
  • 984
Apurv
  • 4,458
  • 2
  • 21
  • 31
  • 8
    This is the best answer by a very long way, and you've been cheated out of votes by the same answer posted by someone else 17 days after your post. This should be the top answer not the bottom. – Oversteer Jun 25 '12 at 11:07
  • I can confirm that this works on Java 7 on Windows 7 (except for TIFF images, however...ImageIO.read() returns null for me). – Vishal Rao Aug 09 '12 at 22:52
  • 1
    @Vishal Rao: I remember putting some additional jars in the classpath for TIFF format files. I think it's jai_imageio.jar – Apurv Aug 10 '12 at 03:07
  • 1
    It was a bit confusing trying to find the right files, there are distribution specific ones but there are also a java platform dependant jar which can be placed in the classpath. At the time of writing the files are in a zip file jai-1_1_3-lib.zip available download.java.net/media/jai/builds/release With instructions download.java.net/media/jai/builds/release/1_1_3/. jai_imageio.jar is also needed, i couldnt find where to get this and end up downloading it as a dependancy from another project http://code.google.com/p/mapgrid/downloads/detail?name=jai_imageio.jar&can=2&q= – Dan675 Aug 23 '12 at 03:16
  • 1
    Works for me in Scala as well. – Marcus Downing Oct 15 '12 at 08:39
  • 59
    From all I'm reading, this reads the entire image into memory. Which is extreme just to get width and height. – Marc May 17 '15 at 20:07
  • 10
    bad way: you'll need to load the whole image raster to memory which causes OOM with very big images – yetanothercoder Oct 27 '15 at 09:41
  • 13
    The question specifically asks for a way *other* than using ImageIO.read. Your answer is "use ImageIO.read". Why is this considered a good answer? – ssimm Feb 25 '16 at 14:04
  • 9
    This is the worst way to do it and I dont get why this is upvoted so heavily. It loads the whole image into the heap which is slow and consumes a lot of unneeded memory. – Patrick Mar 05 '16 at 20:33
  • 1
    This performs really bad. I tried it myself. Definitely not recommended. – Jesus Dec 05 '19 at 18:19
  • 1
    Great solution if you want Denial Of Service attack on your server! – Rok Kralj Dec 08 '19 at 10:51
  • If I have multiple lines to convert into Image then how can I calculate image width and height to create bufferreImage..BufferedImage image = new BufferedImage(x,y,?,?); – surendrapanday Apr 26 '20 at 10:47
  • 2
    Those who are complaining about reading the file into memory are just *assuming* that the user wants to know that piece of information from the question; however, there are valid cases where the file must be in memory anyway (my case) and that is a perfect good answer. – AlexD Mar 19 '21 at 17:59
  • on some images width and height are swapped, really annoying behavior – Asylzat Mar 24 '22 at 08:42
  • `ImageIO.read` returns null for `webp` meme type – mangusta Jul 02 '22 at 23:50
  • there are many libs like these for webp native support, but they're not safe for multi concurrent envs https://github.com/sejda-pdf/webp-imageio – Oleksandr Loushkin Jan 15 '23 at 21:25
97

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.

The Coordinator
  • 13,007
  • 11
  • 44
  • 73
Andrew Taylor
  • 1,368
  • 1
  • 11
  • 8
  • Thanks for this! and considering the performance comparison done by user194715, i'll take your suggestion for performance and png consideration! Thank you! – ah-shiang han Nov 02 '15 at 05:50
  • Couldn't you also use [probeContentType](http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#probeContentType(java.nio.file.Path)) from the nio.Files package combined with `javax.imageio.ImageIO.getImageReadersByMIMEType(mimeType)` if you wanted to be certain of the type of file? – EdgeCaseBerg Nov 10 '15 at 19:14
  • 5
    Also, shouldn't you call `.close()` on stream before you return? otherwise you leave the stream open – EdgeCaseBerg Nov 10 '15 at 19:31
  • Helpful, but I can't wrap my head around the code catching an IOException then logging that in a logger when the method signature already has `throws IOException`. – php_coder_3809625 Jun 26 '16 at 13:06
  • 1
    @php_coder_3809625 the log is in the iterator, so it could be the case that one ImageReader fails, but a subsequent succeeds. If they all fail, then an IOException is raised. – Andrew Taylor Jun 27 '16 at 18:48
  • I'm getting an error using this method with some jpeg files "javax.imageio.IIOException: Not a JPEG file: starts with 0x89 0x50". Switching to the solution by @apurv fixed that error, even if it's more memory intensive. – Paul Jul 12 '17 at 05:19
  • 2
    You might consider using `org.apache.commons.io.FilenameUtils#getExtension` for detecting the file name extension. – Patrick Bergner Aug 29 '17 at 14:23
  • @EdgeCaseBerg the method call for dispose should handle that, as the stream is now part fo the reader object. – Gautam Tyagi Feb 23 '23 at 11:59
64

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.

mp31415
  • 6,531
  • 1
  • 44
  • 34
54

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.

silver est
  • 51
  • 6
user350756
  • 549
  • 4
  • 3
  • `getFileSuffix()` contains unnecessary ifs and initialing with `null` is no good idea in this case. – Jimmy T. Dec 12 '13 at 22:23
  • 2
    Hell yeah this is "basically very quick"! I think you've qualified for 'understatement of the year' award with that one. Blows `ImageIO.read()` completely out of the water, both in terms of CPU time and memory usage. – aroth Feb 04 '16 at 05:41
  • 1
    public static String getFileSuffix(final String path) { if (path != null && path.lastIndexOf('.') != -1) { return path.substring(path.lastIndexOf('.')).substring(1); } return null; } – Nilanchala Mar 30 '16 at 22:47
20

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])));
}

}

joinJpegs
  • 1,287
  • 3
  • 14
  • 21
10

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;
}
alex
  • 479,566
  • 201
  • 878
  • 984
user1215499
  • 125
  • 1
  • 2
  • 3
    this needs to read the whole image in memory only for knowing the width and height. Yes, it's simple, but will perform badly for either many images or huge ones... – Clint Eastwood Sep 15 '16 at 15:43
7

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");
      }
    }
  }
Fabien M
  • 181
  • 2
  • 3
5

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();
Karussell
  • 17,085
  • 16
  • 97
  • 197
  • 2
    No need for ImageIO but need for Toolkit. What is the difference? – alex Jan 31 '16 at 11:13
  • ImageIO is an external dependency. Toolkit not – Karussell Feb 01 '16 at 13:20
  • 1
    ImageIO is part of Java since 1.4 https://docs.oracle.com/javase/7/docs/api/javax/imageio/package-summary.html – alex Feb 01 '16 at 13:32
  • Yes, maybe this will work, sorry if this was confusing then. When I tried it I needed for some parts the JAI thing (maybe to read other formats?): http://stackoverflow.com/questions/1209583/using-java-advanced-imaging-with-maven – Karussell Feb 01 '16 at 16:48
4

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

Aleksander Rezen
  • 877
  • 7
  • 14
3

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();
}
riddle_me_this
  • 8,575
  • 10
  • 55
  • 80
KIBOU Hassan
  • 381
  • 4
  • 13
1

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();
}
Jessi
  • 91
  • 1
  • 3
0
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.

riddle_me_this
  • 8,575
  • 10
  • 55
  • 80
-1

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;

}

AppEmmanuel
  • 144
  • 1
  • 6
  • Hello @gpasch how so ? have you tried it ? This is working perfectly for me. None of the other examples worked for me. Some required me to make some weird imports of classes and others that caused issues. I really want to learn more from what your challenge(s) were. Standing by . . . – AppEmmanuel Aug 01 '19 at 01:33
-2

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);
}
Viktor Sirotin
  • 169
  • 1
  • 8