繁体   English   中英

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

[英]How to get image height and width using java?

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

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

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

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

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

我找到了另一种读取图像大小的方法(更通用)。 您可以与 ImageReaders 配合使用 ImageIO class。 这是示例代码:

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 性能没有可比性。 我希望有人会发现这很有用。

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

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

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

您可以将 jpeg 二进制数据作为文件加载并自己解析 jpeg 标头。 您正在寻找的是 0xFFC0 或帧开始 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.

有关标题的更多信息,请查看 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])));
}

}

简单的方法:

BufferedImage readImage = null;

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

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

ImageIO.read 的问题在于它真的很慢。 您需要做的就是读取图像 header 以获取大小。 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

您可以使用 java 通过 BufferedImage object 获取图像的宽度和高度。

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

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

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

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

但是我对被迫使用本地文件(文件/字符串)感到有点沮丧,特别是在您想要检查来自多部分/表单数据请求的图像大小的情况下,您通常会在该请求中检索InputPart / InputStream ' s。 因此,我基于ImageIO#createImageInputStream的能力,快速制作了一个接受FileInputStreamRandomAccessFile的变体。

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

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

所以不幸的是,在尝试了上面的所有答案之后,经过不知疲倦的尝试,我没有让它们工作。 所以我决定自己做真正的黑客,我 go 这对我有用。 我相信它也适合你。

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

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

/我把它做成 static 并放入我的 Global class 以便我可以从一个来源引用或访问它,如果有任何修改,都必须在一个地方完成。 只是在 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;

}

byte[] bytes = file.getBytes();
InputStream inputStream = new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(inputStream);
imageWidth = image.getWidth();
imageHeight = image.getHeight();
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));
}

修改了@Andrew Taylor 的方法以使用 Optionals。

在 Java 21 中还使用 Java 的 NIO Path Path.getExt的转换更容易(可以删除第二种方法,并且可以将getExtension(imageFile)替换为imageFile.getExtension() )。

还使用来自 Java 的try-with-resources设计。

如果愿意的话,可以使用外部库代替第二种方法。

使用Spliterator可能是另一种方式,尽管最终代码变得更加冗长,因为从 Iterator 转换的结果很少。

要在没有 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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM