Java kullanarak görüntü yüksekliği ve genişliği nasıl elde edilir?


107

Görüntü yüksekliğini ve genişliğini elde etmek için ImageIO.read kullanmanın dışında başka bir yol var mı ?

Çünkü ipliği kilitleyen bir sorunla karşılaşıyorum.

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)

Bu hata yalnızca bir Sun uygulama sunucusunda görülür ve bu nedenle bunun bir Sun hatası olduğundan şüpheleniyorum.


Ne hatası? Yığın izlemesinin yalnızca bir bölümünü (bu, kaynaklanıyor gibi görünüyor jstack) gösteriyorsunuz.
Joachim Sauer

Bu sorunun nedenini veya çözümünü buldunuz mu? İş parçacığını aynı yöntemle kilitlediği yerde aynı sorunu alıyorum.
Timothy Chen

Yanıtlar:


289

İşte çok basit ve kullanışlı bir şey.

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

7
Bu, çok uzun bir yoldan en iyi cevap ve gönderinizden 17 gün sonra başka biri tarafından gönderilen aynı cevapla oylarınızı kaybetmişsiniz. Bu, en alttaki değil en iyi cevap olmalıdır.
Oversteer

34
Okuduğum her şeye bakılırsa, bu resmin tamamını hafızaya okur. Bu sadece genişlik ve yükseklik elde etmek için aşırı bir şey.
Marc

7
kötü yol: tüm görüntü
taramasını

4
Soru özellikle ImageIO.read kullanmaktan başka bir yol soruyor . Cevabınız "ImageIO.read kullanın". Bu neden iyi bir cevap olarak kabul ediliyor?
ssimm

5
Bunu yapmanın en kötü yolu bu ve neden bu kadar ağır oy verildiğini anlamıyorum. Tüm görüntüyü yavaş olan ve çok fazla gereksiz bellek tüketen yığına yükler.
Patrick Favre

73

Bu, IOException oluşturan ve erken çıkış sağlayan @Kay tarafından yazılan harika gönderinin yeniden yazılmasıdır:

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

Sanırım temsilcim, girdimin bir yanıt olarak kabul edilmesi için yeterince yüksek değil.


Bunun için teşekkürler! ve user194715 tarafından yapılan performans karşılaştırmasını dikkate alarak, performans ve png değerlendirmesi için önerinizi dikkate alacağım! Teşekkür ederim!
ah-shiang han

Dosya türünden emin olmak istiyorsanız , nio.Files paketindeki probeContentType'ı da kullanamaz mısınızjavax.imageio.ImageIO.getImageReadersByMIMEType(mimeType) ?
EdgeCaseBerg

3
Ayrıca, .close()geri dönmeden önce canlı yayını aramanız gerekmez mi ? aksi takdirde akışı açık bırakırsınız
EdgeCaseBerg

1
@ php_coder_3809625 günlük yineleyicidedir, bu nedenle bir Görüntü Okuyucunun başarısız olması, ancak sonraki birinin başarılı olması durumu olabilir. Hepsi başarısız olursa, bir IOException oluşur.
Andrew Taylor

1
org.apache.commons.io.FilenameUtils#getExtensionDosya adı uzantısını tespit etmek için kullanmayı düşünebilirsiniz .
Patrick Bergner

52

Bir resim boyutunu okumanın başka bir yolunu buldum (daha genel). ImageIO sınıfını ImageReaders ile işbirliği içinde kullanabilirsiniz. İşte örnek kod:

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'in "." Olmadan yolun uzantısını döndüren bir yöntem olduğunu unutmayın. örneğin: png, jpg vb. Örnek uygulama:

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

Bu çözüm, tüm görüntü değil dosyadan yalnızca görüntü boyutu okunduğu için çok hızlıdır. Test ettim ve ImageIO.read performansıyla karşılaştırılamaz. Umarım birisi bunu faydalı bulacaktır.


getFileSuffix()gereksiz if'ler içerir ve ile ilklendirme nullbu durumda iyi bir fikir değildir.
Jimmy T.

2
Cehennem evet bu "temelde çok hızlı"! Sanırım bununla 'yılın yetersizliği' ödülüne hak kazandınız. ImageIO.read()Hem CPU süresi hem de bellek kullanımı açısından sudan tamamen esiyor .
aroth

1
public statik String getFileSuffix (son String yolu) {if (yol! = null && yol.lastIndexOf ('.')! = -1) {dönüş yolu.substring (yol.lastIndexOf ('.')). substring (1) ; } boş döndür; }
Nilanchal

47

Listelenen çeşitli yaklaşımlardan bazılarını kullanarak performansı test etmeye çalıştım. Sonucu birçok faktör etkilediği için sıkı bir test yapmak zordur. Biri 330 jpg, diğeri 330 png uzantılı iki klasör hazırladım. Her iki durumda da ortalama dosya boyutu 4Mb idi. Sonra her dosya için getDimension'ı çağırdım. Her getDimension yöntemi uygulaması ve her görüntü türü ayrı ayrı test edildi (ayrı çalıştırma). İşte aldığım yürütme süreleri (jpg için ilk sayı, png için ikinci sayı):

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

Bazı yöntemlerin boyutları elde etmek için tüm dosyayı yüklerken, diğerlerinin yalnızca görüntüden bazı başlık bilgilerini okuyarak aldığı açıktır. Uygulama performansı kritik olduğunda bu sayıların yararlı olabileceğini düşünüyorum.

Herkese bu konuya katkılarından dolayı teşekkür ederiz - çok faydalı.


3
Şimdi bu bir cevap. Aferin!
ssimm

1
Görüntüleri yüklerken yığın alanı kullanımını da analiz ettiniz mi? Ayrıca, bu testleri herhangi bir yöntemde çalıştırırken herhangi bir OOM hatası aldınız mı?
saibharath

Teşekkürler cevabınız bana çok yardımcı oldu. (50k HD fotoğraf)
SüniÚr

17

Jpeg ikili verilerini bir dosya olarak yükleyebilir ve jpeg başlıklarını kendiniz ayrıştırabilirsiniz. Aradığınız şey 0xFFC0 veya Start of Frame başlığıdır:

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.

Başlıklar hakkında daha fazla bilgi için wikipedia'nın jpeg girişine bakın veya yukarıdaki bilgileri buradan edinebilirsiniz .

Güneş forumlarında bu gönderiden aldığım koda benzer bir yöntem kullandım :

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

}


İyi. Ama bunun yerine ==192196, 200 ve 204 hariç 192-207 sayılarını kontrol etmeliyim diye düşünüyorum.
vortexwolf

Veya com.drewnoakes.metadata-extractorbu başlıkları kolayca çıkarmak için kitaplığı kullanabilirsiniz
Victor Petit

9

Basit yol:

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
bunun sadece genişliği ve yüksekliği bilmek için hafızadaki tüm görüntüyü okuması gerekir. Evet, basit, ancak ya birçok görüntüde ya da büyük görüntülerde kötü performans gösterecek ...
Clint Eastwood

4

ImageIO.read ile ilgili sorun, gerçekten yavaş olmasıdır. Tek yapmanız gereken, boyutu elde etmek için resim başlığını okumaktır. ImageIO.getImageReadermükemmel bir aday.

İşte Groovy örneği, ancak aynı şey Java için de geçerli

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

Performans, SimpleImageInfo java kitaplığını kullanmakla aynıydı.

https://github.com/cbeust/personal/blob/master/src/main/java/com/beust/SimpleImageInfo.java


Nedir getReaderByFormat?
Koray Tugay

3

Toolkit'i kullanabilirsiniz, ImageIO'ya gerek yok

Image image = Toolkit.getDefaultToolkit().getImage(file.getAbsolutePath());
int width = image.getWidth(null);
int height = image.getHeight(null);

Görüntünün yüklenmesini istemiyorsanız,

ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
int height = imageIcon.getIconHeight();
int width = imageIcon.getIconWidth();

1
ImageIO'ya gerek yok, Araç Takımına ihtiyaç var. Fark ne?
dieter

ImageIO, harici bir bağımlılıktır. Toolkit not
Karussell


Evet, belki bu işe yarar, o zaman kafa karıştırıcıysa özür dilerim. Bunu denediğimde bazı kısımlar için JAI şeyine ihtiyacım vardı (belki başka formatları okumak için?): Stackoverflow.com/questions/1209583/…
Karussell

3

Java kullanarak BufferedImage nesnesiyle görüntünün genişliğini ve yüksekliğini elde edebilirsiniz.

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

bu tüm görüntüyü belleğe yükleyecek, çok savurgan ve çok yavaş.
argoden

1

ImageIO.read ile Tamponlanmış Görüntü elde etmek, bellekte görüntünün tam bir sıkıştırılmamış kopyasını oluşturduğu için çok ağır bir yöntemdir. PNG'ler için pngj ve kodu da kullanabilirsiniz:

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

0

Mücadele ederek geçiren ImageIOgeçmiş yıllarda çok galiba Andrew Taylor bireyin çözüm çok iyi uzlaşma gereğidir (hızlı: kullanmayan ImageIO#readçok yönlü ve). Teşekkürler dostum!!

Ancak yerel bir dosya (Dosya / Dize) kullanmaya mecbur kaldığım için biraz hayal kırıklığına uğradım, özellikle de genellikle InputPart/ InputStream'leri aldığınız çok parçalı / form veri talebinden gelen görüntü boyutlarını kontrol etmek istediğiniz durumlarda . Bu yüzden çabuk kabul eden bir varyantını yapılan File, InputStreamve RandomAccessFilebecerisine dayanarak, ImageIO#createImageInputStreambunu.

Elbette, böyle bir yöntem Object inputyalnızca özel kalabilir ve buna göre gerektiği kadar çok polimorfik yöntem yaratacaksınız. Ayrıca kabul edebilir Pathile Path#toFile()ve URLile URL#openStream()önceki bu yönteme geçirmeden:

  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

Bu yüzden ne yazık ki, yukarıdan gelen tüm cevapları denedikten sonra, yorulmak bilmeyen denemelerden sonra onları işe alamadım. Bu yüzden gerçek hack'i kendim yapmaya karar verdim ve bunu kendim için çalıştırdım. Senin için de mükemmel çalışacağına inanıyorum.

Uygulama tarafından oluşturulan ve daha sonra doğrulama için yüklenecek bir görüntünün genişliğini elde etmek için bu basit yöntemi kullanıyorum:

Lütfen. not al: erişim depolaması için manifestte izinleri etkinleştirmeniz gerekir.

/ Statik hale getirdim ve Global sınıfıma koydum, böylece ona tek bir kaynaktan başvurabilir veya erişebilirim ve herhangi bir değişiklik varsa, hepsinin tek bir yerde yapılması gerekirdi. Sadece java'da DRY konseptini sürdürmek. (neyse) :) /

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;

}


Merhaba @gpasch nasıl yani? Bunu denediniz mi ? Bu benim için mükemmel çalışıyor. Diğer örneklerin hiçbiri benim için işe yaramadı. Bazıları benden bazı garip sınıf ithalatı yapmamı istedi ve diğerleri sorunlara neden oldu. Karşılaştığınız zorluklardan gerçekten daha fazlasını öğrenmek istiyorum. Beklemede. . .
AppEmmanuel

-2

EMF Görüntü Okuyucu olmadan emf dosyasının boyutunu almak için şu kodu kullanabilirsiniz:

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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.