簡體   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