자바를 사용하여 이미지 높이와 너비를 얻는 방법은 무엇입니까?


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

이 문제의 원인을 찾거나 수정 했습니까? 동일한 방법으로 스레드를 잠그는 동일한 문제가 발생합니다.
Timothy Chen

관련 될 수있는 버그가 있습니다. bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791502
Adam Schmideg 2011 년

답변:


288

여기에 매우 간단하고 편리한 것이 있습니다.

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

7
이것은 매우 오랜 시간 동안 최고의 답변이며, 게시물 17 일 후 다른 사람이 게시 한 동일한 답변에 의해 속임수를당했습니다. 이것은 하단이 아닌 상위 답변이어야합니다.
Oversteer 2012-06-25

34
내가 읽는 모든 것에서 이것은 전체 이미지를 메모리로 읽습니다. 너비와 높이를 얻기 위해 극단적입니다.
Marc

7
나쁜 방법 : 전체 이미지 래스터를 메모리에로드해야하므로 매우 큰 이미지가있는 OOM이 발생합니다
yetanothercoder

4
질문은 특히 ImageIO.read를 사용 하는 것 이외 의 방법을 요구합니다 . 귀하의 대답은 "ImageIO.read 사용"입니다. 이것이 좋은 대답으로 간주되는 이유는 무엇입니까?
ssimm

5
이것은 최악의 방법이며 이것이 왜 그렇게 많이 upvoted되는지 이해하지 못합니다. 느리고 불필요한 메모리를 많이 사용하는 힙에 전체 이미지를로드합니다.
Patrick Favre 2016 년

72

이것은 IOException을 발생시키고 조기 종료를 제공하는 @Kay의 훌륭한 게시물을 재 작성한 것입니다.

/**
 * 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

파일 유형을 확인 하고 싶다면 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파일 이름 확장명을 감지하는 데 사용 하는 것을 고려할 수 있습니다 .
Patrick Bergner

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()불필요한 if를 포함하고 이니셜을 사용하는 null것은 좋은 생각이 아닙니다.
Jimmy T.

2
네, 이것은 "기본적으로 매우 빠릅니다"! 나는 당신이 그것으로 '올해의 과소 평가'상을받을 자격이 있다고 생각합니다. ImageIO.read()CPU 시간과 메모리 사용량 측면에서 완전히 물 밖으로 날아 갑니다.
aroth

1
public static String getFileSuffix (final String path) {if (path! = null && path.lastIndexOf ( '.')! = -1) {return path.substring (path.lastIndexOf ( '.')). substring (1) ; } return null; }
Nilanchal 2016 년

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

일부 메서드는 크기를 얻기 위해 전체 파일을로드하는 반면 다른 메서드는 이미지에서 일부 헤더 정보를 읽어서 가져 오는 것이 분명합니다. 이 수치는 애플리케이션 성능이 중요 할 때 유용 할 수 있다고 생각합니다.

이 스레드에 기여 해주신 모든 분들께 감사드립니다. 매우 유용합니다.


3
이제 그게 답입니다. 잘 했어!
ssimm

1
이미지를 업로드 할 때 힙 공간 사용량도 분석 했습니까? 또한 방법 중 하나에서 이러한 테스트를 실행하는 동안 OOM 오류가 발생 했습니까?
saibharath

귀하의 답변에 감사드립니다. I have (50k HD pic)
SüniÚr 2018-08-20

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

}


좋은. 하지만 그 대신 ==192196, 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
이것은 너비와 높이를 알기 위해서만 메모리의 전체 이미지를 읽어야합니다. 예, 그것은 ... 간단하지만, 많은 이미지 나 큰 것들 중 하나 나쁘게 수행
클린트 이스트 우드

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은 필요합니다. 차이점은 무엇입니까?
dieter

ImageIO는 외부 종속성입니다. Toolkit not
Karussell

ImageIO는 1.4 이후 Java의 일부입니다. docs.oracle.com/javase/7/docs/api/javax/imageio/…
dieter

예, 아마도 이것이 작동 할 것입니다. 혼란 스러웠다면 죄송합니다. 내가 그것을 시도했을 때 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's를 검색하는 다중 부분 / 양식 데이터 요청에서 오는 이미지 크기를 확인하려는 경우에 그렇습니다 . 그래서 저는 의 능력을 기반으로 File, InputStream및 을 받아들이는 변형을 빠르게 만들었습니다.RandomAccessFileImageIO#createImageInputStream 그렇게 .

물론,와 같은 메서드 Object input는 비공개로만 유지 될 수 있으며이를 호출하여 필요한만큼의 다형성 메서드를 만들어야합니다. 당신은 또한 받아 들일 수 PathPath#toFile()URL함께 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 클래스에 넣어서 하나의 소스에서 참조하거나 액세스 할 수 있으며 수정이 있으면 모두 한곳에서 수행해야합니다. 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);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.