tags:

views:

4193

answers:

3

Is there any other way beside using imageIO.read to get image height and width?

Because I encounter some issue that will lockup 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 sun app server I suspect that it is a sun bug

+2  A: 

Try using the ImageInfo freely available class, I've used it for the same purpose:

http://linux.softpedia.com/get/Multimedia/Graphics/ImageInfo-19792.shtml

karim79
That link is broken, but I found a class named ImageInfo at http://kickjava.com/src/imageinfo/ImageInfo.java.htm and it worked well for my purposes.
heycam
+3  A: 

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
This helped me a LOT man! thanks!
Orr Matarasso
+2  A: 

I have found another way of reading image size (more generic). You can use ImageIO class with cooperation with ImageReaders. Sample code is here:

    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 basically very quick bacause only image size is read from the file, not whole image. I tested it and there is no comparition with ImageIO.read performance. I hope someone will find this usefull.