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)。
Joachim Sauer、

この問題の原因または修正を見つけましたか?同じメソッドでスレッドをロックしているのと同じ問題が発生しています。
ティモシーチェン

関連する可能性のあるバグがあります:bugs.sun.com/bugdatabase/view_bug.do?bug_id
Adam Schmideg '29

回答:


288

これは非常にシンプルで便利なものです。

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

7
これは非常に長い道のりによる最良の回答であり、あなたの投稿から17日後に他の誰かが投稿した同じ回答によって投票からだまされました。これは一番下の答えではなく一番上の答えになるはずです。
オーバーステア2012年

34
私が読んでいるすべてから、これは画像全体をメモリに読み込みます。これは、幅と高さを取得するためだけに極端です。
マルク

7
悪い方法:あなたは非常に大きな画像とOOMが発生し、メモリに画像全体のラスタをロードする必要があります
yetanothercoder

4
この質問では、ImageIO.readを使用する以外の方法を具体的に求めています。あなたの答えは「Use ImageIO.read」です。なぜこれが良い答えと考えられるのですか?
ssimm 2016

5
これはそれを行うための最悪の方法であり、なぜこれがそれほど高く支持されているのか理解できません。これは、画像全体をヒープにロードします。ヒープは遅く、不要なメモリを大量に消費します。
Patrick Favre 2016年

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 2015年

あなたも使うことができませんでしたprobeContentTypeをと組み合わせるnio.Filesパッケージからjavax.imageio.ImageIO.getImageReadersByMIMEType(mimeType)ファイルの種類の特定になりたい場合は?
EdgeCaseBerg 2015年

3
また、.close()戻る前にストリームで呼び出してはいけませんか?それ以外の場合は、ストリームを開いたままにします
EdgeCaseBerg 2015年

1
@ php_coder_3809625ログはイテレータにあるため、1つのImageReaderが失敗しても、その後は成功する場合があります。すべてが失敗すると、IOExceptionが発生します。
Andrew Taylor

1
org.apache.commons.io.FilenameUtils#getExtensionファイル名拡張子の検出にを使用することを検討してください。
Patrick Bergner、2017

52

画像サイズを読み取る別の方法を見つけました(より一般的)。ImageReadersと連携して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のパフォーマンスとの比較はありません。私は誰かがこれが役に立つことを願っています。


getFileSuffix()nullこの場合、不要なifsが含まれ、で初期化することはお勧めできません。
ジミーT.

2
地獄ええ、これは「基本的に非常に速い」です!あなたはその年の「控えめな年」賞を受賞したと思います。ImageIO.read()CPU時間とメモリ使用量の両面で、完全に水から吹き飛ばされます。
2016

1
public static String getFileSuffix(final String path){if(path!= null && path.lastIndexOf( '。')!= -1){return path.substring(path.lastIndexOf( '。'))。substring(1) ; } nullを返します。}
Nilanchal

47

リストされているさまざまなアプローチのいくつかを使用してパフォーマンスをテストしようとしました。多くの要因が結果に影響を与えるため、厳密なテストを行うことは困難です。2つのフォルダーを準備しました。1つはjpgファイルが330個、もう1つはpngファイルが330個あります。どちらの場合も、平均ファイルサイズは4Mbでした。次に、各ファイルに対してgetDimensionを呼び出しました。getDimensionメソッドの各実装と各画像タイプは個別にテストされました(個別に実行)。ここに私が得た実行時間があります(jpgの最初の数字、pngの2番目の数字):

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

寸法を取得するためにファイル全体をロードするメソッドもあれば、画像からヘッダー情報を読み取るだけで取得するメソッドもあります。これらの数値は、アプリケーションのパフォーマンスが重要な場合に役立つと思います。

このスレッドへの貢献をありがとうございました。


3
これ答えです。よくやった!
ssimm

1
画像をアップロードするときのヒープ領域の使用状況も分析しましたか?また、いずれかのメソッドでこれらのテストを実行しているときにOOMエラーが発生しましたか?
サイバラート

あなたの答えに感謝します。私が持っている(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.

ヘッダーの詳細については、ウィキペディアの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、それは196、200および204を除いて、番号192から207を確認してください
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
これは、幅と高さを知るためにのみ、メモリ内の画像全体を読み取る必要があります。はい、それは単純ですが、多くの画像または巨大な画像のいずれかでパフォーマンスが低下します...
クリントイーストウッド'15

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は不要ですが、Toolkitは必要です。違いはなんですか?
ダイエット

ImageIOは外部依存関係です。ツールキットではない
Karussell、2016

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/を取得するmultipart / form-dataリクエストからの画像サイズを確認する場合など、ローカルファイル(ファイル/文字列)を使用せざるを得ないことに少しイライラしましたInputStream。それで、私はすぐに、それを受け入れる能力に基づいてFileInputStreamおよびを受け入れるバリアントを作成しました。RandomAccessFileImageIO#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

残念ながら、上からのすべての答えを試した後、私は精力的に試してみた後、それらを動作させることができませんでした。それで私は本当のハックをすることに決めました、そして私はこれを私のために働くために行きます。私もそれがあなたのために完璧に機能すると信じています。

この簡単な方法を使用して、アプリで生成された画像の幅を取得し、後で確認のためにアップロードします。

Pls。注意:アクセスストレージのマニフェストで権限を有効にする必要があります。

/ 私はそれを静的にしてGlobalクラスに入れて、1つのソースからのみ参照またはアクセスできるようにしました。変更がある場合は、すべて1か所で行う必要があります。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イメージリーダーなしで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);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.