如何使用java获取图像的高度和宽度?

时间:2009-03-23 10:48:34

标签: java javax.imageio

除了使用ImageIO.read获取图像高度和宽度之外还有其他方法吗?

因为我遇到了锁定线程的问题。

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)

此错误仅发生在Sun应用服务器上,因此我怀疑它是Sun漏洞。

13 个答案:

答案 0 :(得分:257)

这是非常简单和方便的东西。

BufferedImage bimg = ImageIO.read(new File(filename));
int width          = bimg.getWidth();
int height         = bimg.getHeight();

答案 1 :(得分:55)

这是@Kay改写了一篇伟大的帖子,它会抛出IOException并提供早期退出:

/**
 * 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());
}

我想我的代表不够高,我的输入被认为是值得回复的。

答案 2 :(得分:41)

我找到了另一种读取图像大小的方法(更通用)。 您可以与ImageReader一起使用ImageIO类。 以下是示例代码:

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

请注意,getFileSuffix是返回路径扩展名而没有“。”的方法。例如:png,jpg等 示例实现是:

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

此解决方案非常快,因为只从文件中读取图像大小而不是整个图像。我测试了它,并没有与ImageIO.read性能进行比较。我希望有人会觉得这很有用。

答案 3 :(得分:39)

我尝试使用列出的各种方法测试性能。由于许多因素会影响结果,因此很难进行严格的测试。我准备了两个文件夹,一个包含330个jpg文件,另一个包含330个png文件。两种情况下的平均文件大小为4Mb。然后我为每个文件调用了getDimension。 getDimension方法的每个实现和每个图像类型分别进行了测试(单独运行)。这是我得到的执行时间(jpg的第一个数字,png的第二个数字):

1(Apurv) - 101454ms, 84611ms
2(joinJpegs) - 471ms, N/A
3(Andrew Taylor) - 707ms, 68ms
4(Karussell, ImageIcon) - 106655ms, 100898ms
5(user350756) - 2649ms, 68ms

很明显,有些方法会加载整个文件以获取尺寸,而其他方法只需从图像中读取一些标题信息即可。我认为当应用程序性能至关重要时,这些数字可能很有用。

感谢大家对此主题的贡献 - 非常有帮助。

答案 4 :(得分:12)

您可以将jpeg二进制数据作为文件加载并自行解析jpeg标头。您要查找的是0xFFC0或帧起始标题:

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.

有关标题的更多信息,请查看维基百科的jpeg条目,或者我获得了上述信息here

我使用的方法类似于下面的代码,我在太阳论坛上从this post获得了这些代码:

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

}

答案 5 :(得分:9)

简单方法:

BufferedImage readImage = null;

try {
    readImage = ImageIO.read(new File(your path);
    int h = readImage.getHeight();
    int w = readImage.getWidth();
} catch (Exception e) {
    readImage = null;
}

答案 6 :(得分:4)

尝试使用ImageInfo免费提供的类,我将它用于同样的目的:

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

答案 7 :(得分:4)

ImageIO.read的问题是它确实很慢。您需要做的就是读取图像标题以获取大小。 ImageIO.getImageReader是最佳人选。

这里是Groovy示例,但同样适用于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)"

性能与使用SimpleImageInfo java库相同。

https://github.com/cbeust/personal/blob/master/src/main/java/com/beust/SimpleImageInfo.java

答案 8 :(得分:3)

您可以使用Toolkit,不需要ImageIO

Image image = Toolkit.getDefaultToolkit().getImage(file.getAbsolutePath());
int width = image.getWidth(null);
int height = image.getHeight(null);

如果您不想处理图像的加载,请执行

ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
int height = imageIcon.getIconHeight();
int width = imageIcon.getIconWidth();

答案 9 :(得分:1)

使用ImageIO.read获取缓冲图像是一种非常繁重的方法,因为它在内存中创建完整的未压缩图像副本。对于png,你也可以使用pngj和代码:

if (png)
    PngReader pngr = new PngReader(file);
    width = pngr.imgInfo.cols;
    height = pngr.imgInfo.rows;
    pngr.close();
}

答案 10 :(得分:1)

使用java可以使用BufferedImage对象获取图像的宽度和高度。

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

答案 11 :(得分:0)

过去几年中,与ImageIO进行了很多斗争,我认为 Andrew Taylor solution是迄今为止最好的折衷方案(快速:不使用{{1 }},用途广泛)。谢谢男人!

但是我被迫使用本地文件(文件/字符串)感到有些沮丧,尤其是在您要检查来自多部分/表单数据请求的图像尺寸的情况下,通常您会检索{ {1}} / ImageIO#read。因此,我根据InputPart的接受能力迅速做出了一个接受InputStreamFileInputStream的变体。

当然,这种带有RandomAccessFile的方法只能保持私有状态,您应根据需要创建尽可能多的多态方法,并调用该方法。在传递给此方法之前,您还可以将ImageIO#createImageInputStream的{​​{1}}和Object input的{​​{1}}接受:

Path

答案 12 :(得分:-1)

因此,不幸的是,在尝试了上面所有的答案之后,经过漫长的尝试,我才没有让他们工作。因此,我决定亲自进行真正的破解,然后为我工作。我相信它也会为您完美地工作。

我正在使用这种简单的方法来获取应用程序生成的图像的宽度,但稍后再上传以进行验证:

请。请注意:您必须在清单中启用访问存储权限。

/ 我将其设置为静态并放入Global类中,因此我只能从一个来源引用或访问它,并且如果有任何修改,则都必须在一个地方完成。只是在Java中维护DRY概念。 (无论如何):) /

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;

}