118

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

4

13 回答 13

316

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

BufferedImage bimg = ImageIO.read(new File(filename));
int width          = bimg.getWidth();
int height         = bimg.getHeight();
于 2012-01-31T17:17:39.340 回答
81

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

我想我的代表还不够高,我的意见不足以被认为是值得回复的。

于 2012-08-28T16:57:34.307 回答
56

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

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

感谢大家对这个线程的贡献 - 非常有帮助。

于 2013-02-15T04:08:38.253 回答
53

我找到了另一种读取图像大小的方法(更通用)。您可以将 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 性能没有可比性。我希望有人会发现这很有用。

于 2010-05-26T09:46:18.990 回答
18

您可以将 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])));
}

}

于 2009-03-28T01:59:39.970 回答
10

简单的方法:

BufferedImage readImage = null;

try {
    readImage = ImageIO.read(new File(your path);
    int h = readImage.getHeight();
    int w = readImage.getWidth();
} catch (Exception e) {
    readImage = null;
}
于 2012-02-17T06:02:24.273 回答
4

您可以使用工具包,无需 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();
于 2013-02-04T21:02:00.823 回答
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

于 2018-06-20T18:06:08.137 回答
3

在过去的几年中挣扎ImageIO了很多,我认为Andrew Taylor解决方案是迄今为止最好的折衷方案(快速:不使用ImageIO#read,并且用途广泛)。谢啦!!

但是我对被迫使用本地文件(文件/字符串)感到有点沮丧,特别是在您想要检查图像大小的情况下,例如,您通常检索InputPart/的多部分/表单数据请求InputStream。所以我很快做了一个变体,接受File,InputStreamRandomAccessFile,基于ImageIO#createImageInputStream这样做的能力。

当然,这样一个带有Object input, 的方法可能只保持私有,你应该根据需要创建尽可能多的多态方法,调用这个方法。您还可以在传递给此方法之前接受PathwithPath#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");
      }
    }
  }
于 2020-06-18T15:40:20.397 回答
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();
    }
于 2017-04-27T09:57:39.867 回答
1

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

if (png)
    PngReader pngr = new PngReader(file);
    width = pngr.imgInfo.cols;
    height = pngr.imgInfo.rows;
    pngr.close();
}
于 2015-02-04T10:03:04.943 回答
-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;

}

于 2019-07-30T15:48:21.090 回答
-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);
}
于 2015-11-23T09:56:15.557 回答