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


106

除了使用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错误。


什么错 您仅显示堆栈跟踪的一部分(似乎来自jstack)。
约阿希姆·绍尔

您是否找到原因或解决此问题的方法?我在将线程锁定在同一方法上时遇到了同样的问题。
Timothy Chen 2010年

有一个可能与之相关的错误:bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791502
Adam Schmideg

Answers:


288

这很简单方便。

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

7
从长远来看,这是最好的答案,您的帖子发布17天后,您的答案就已经被其他人发布的相同答案从投票中骗走了。这应该是最高答案,而不是最低答案。
转向过度

34
从我正在阅读的所有内容中,这会将整个图像读入内存。仅获得宽度和高度是极端的。
2015年

7
坏方法:您需要将整个图像栅格加载到内存中,这会导致OOM具有非常大的图像
yetanothercoder 2015年

4
该问题专门要求使用除ImageIO.read之外的其他方法。您的答案是“使用ImageIO.read”。为什么这被认为是一个好答案?
ssimm '16

5
这是最糟糕的方法,我不明白为什么如此强烈地反对。它将整个映像加载到堆中,这很慢,并且会消耗大量不必要的内存。
Patrick Favre

72

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

我想我的代表不够高,因此我的意见不值得作为回应。


谢谢你!并考虑到user194715进行的性能比较,我将考虑您对性能和png的建议!谢谢!
ah-shiang han

如果您想确定文件的类型,也不能同时使用nio.Files包中的probeContentType javax.imageio.ImageIO.getImageReadersByMIMEType(mimeType)吗?
EdgeCaseBerg,2015年

3
另外,您不应该.close()在返回之前致电直播吗?否则,您将流保持打开状态
EdgeCaseBerg,2015年

1
@ php_coder_3809625日志位于迭代器中,因此可能是一个ImageReader失败,但随后的成功。如果它们全部失败,则引发IOException。
Andrew Taylor

1
您可能会考虑使用org.apache.commons.io.FilenameUtils#getExtension来检测文件扩展名。
帕特里克·伯格纳

52

我发现了另一种读取图像大小的方法(更通用)。您可以将ImageIO类与ImageReaders一起使用。这是示例代码:

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性能进行比较。我希望有人会觉得有用。


getFileSuffix()null在这种情况下,包含不必要的if和initialing 并不是一个好主意。
Jimmy T.

2
是的,这是“基本上非常快”!我认为您已经获得了“低估年度”奖项的资格。打击ImageIO.read()完全露出水面,无论是在CPU时间和内存使用方面。
aroth'2

1
公共静态字符串getFileSuffix(最终字符串路径){如果(路径!=空&& path.lastIndexOf('。')!= -1){返回path.substring(path.lastIndexOf('。'))。substring(1) ; 返回null;}
Nilanchal

47

我尝试使用列出的各种方法来测试性能。由于许多因素会影响结果,因此很难进行严格的测试。我准备了两个文件夹,一个文件夹包含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

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

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


2
现在就是答案。做得好!
ssimm '16

1
上传图像时,您是否还分析了堆空间使用情况?另外,在任何方法上运行这些测试时,您是否遇到任何OOM错误?
saibharath

谢谢您的回答对我有很大帮助。我有(50K HD PIC)
SüniÚr

17

您可以将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.

有关标题的更多信息,请查看Wikipedia的jpeg条目,或者我在此处获得了上述信息。

我使用了一种类似于以下代码的方法,该代码是我在sun论坛上从本文中获得的:

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

}


好。但我认为,不是==192应该检查192-207号,除了196,200和204
vortexwolf

或者,您可以使用该com.drewnoakes.metadata-extractor库轻松提取这些标头
Victor Petit

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

3
这仅需要了解内存的宽度和高度即可读取内存中的整个图像。是的,它很简单,但是对于许多图像或大型图像都会表现不佳...
Clint Eastwood'Sep

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


什么getReaderByFormat
Koray Tugay,

3

您可以使用工具包,无需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();

1
不需要ImageIO,但需要工具包。有什么区别?
节食者

ImageIO是外部依赖项。不是工具包
Karussell

ImageIO的是,因为1.4的Java的一部分docs.oracle.com/javase/7/docs/api/javax/imageio/...
迪特尔

是的,也许这可以工作,但是如果这令人困惑,那就对不起。当我尝试它时,我需要某些部分的JAI东西(也许要阅读其他格式?):stackoverflow.com/questions/1209583/…–
Karussell

3

您可以使用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();
    }

这会将整个图像加载到内存中,非常浪费而且非常缓慢。
argoden

1

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

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

0

ImageIO过去几年中经历了很多苦苦挣扎之后,我认为Andrew Taylor解决方案是迄今为止最好的折衷方案(快速:不使用ImageIO#read,功能多样)。谢啦!!

但是我被迫使用本地文件(文件/字符串)感到有些沮丧,尤其是在您要检查图像大小的情况下,例如,通常从InputPart/ 那里获取的多部分/表单数据请求InputStream。于是我迅速作出接受一个变体FileInputStream并且RandomAccessFile,基于能力ImageIO#createImageInputStream这样做。

当然,带有的这种方法Object input只能保留私有状态,您将根据需要创建尽可能多的多态方法,并调用此方法。您还可以在传递给此方法之前接受Pathwith Path#toFile()URLwith URL#openStream()

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

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

}


你好@gpasch怎么回事?你试过了吗?这对我来说是完美的。没有其他例子对我有用。有些要求我输入一些奇怪的类,而另一些则引起问题。我真的想从您的挑战中学到更多。站在 。。。
AppEmmanuel

-2

要获得没有EMF Image Reader的emf文件的大小,可以使用以下代码:

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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.