Wie erhalte ich mit Java Bildhöhe und -breite?

105

Gibt es eine andere Möglichkeit als ImageIO.read , um Bildhöhe und -breite zu ermitteln?

Weil ich auf ein Problem stoße, das den Thread blockiert.

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)

Dieser Fehler tritt nur auf einem Sun App-Server auf und daher vermute ich, dass es sich um einen Sun-Fehler handelt.

Silber est
quelle
Welcher Fehler? Sie zeigen nur einen Teil eines Stack-Trace (der anscheinend von stammt jstack).
Joachim Sauer
Haben Sie die Ursache gefunden oder dieses Problem behoben? Ich erhalte das gleiche Problem, bei dem der Thread mit derselben Methode gesperrt wird.
Timothy Chen
Es gibt einen Fehler, der verwandt sein könnte: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791502
Adam Schmideg

Antworten:

287

Hier ist etwas sehr einfaches und handliches.

BufferedImage bimg = ImageIO.read(new File(filename));
int width          = bimg.getWidth();
int height         = bimg.getHeight();
Apurv
quelle
7
Dies ist bei weitem die beste Antwort, und Sie wurden durch dieselbe Antwort, die 17 Tage nach Ihrem Beitrag von einer anderen Person gepostet wurde, um die Stimmen betrogen. Dies sollte die obere Antwort sein, nicht die untere.
Übersteuern
32
Nach allem, was ich lese, liest dies das gesamte Bild in den Speicher. Welches ist extrem, nur um Breite und Höhe zu bekommen.
Marc
7
schlechter Weg: Sie müssen das gesamte Bildraster in den Speicher laden, was OOM mit sehr großen Bildern verursacht
anderer Codierer
4
In der Frage wird speziell nach einer anderen Möglichkeit als der Verwendung von ImageIO.read gefragt. Ihre Antwort lautet "benutze ImageIO.read". Warum wird dies als gute Antwort angesehen?
SSimm
5
Dies ist der schlechteste Weg, und ich verstehe nicht, warum dies so stark positiv bewertet wird. Es lädt das gesamte Bild in den Heap, der langsam ist und viel nicht benötigten Speicher verbraucht.
Patrick Favre
72

Dies ist eine Neufassung des großartigen Beitrags von @Kay, der IOException auslöst und einen frühen Exit bietet:

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

Ich denke, mein Repräsentant ist nicht hoch genug, um meine Eingabe als Antwort als würdig zu betrachten.

Andrew Taylor
quelle
Danke dafür! und unter Berücksichtigung des Leistungsvergleichs von user194715 werde ich Ihren Vorschlag für Leistung und PNG-Berücksichtigung berücksichtigen! Danke dir!
Ah-Shiang Han
Könnten Sie nicht auch probeContentType aus dem nio.Files-Paket in Kombination mit verwenden, javax.imageio.ImageIO.getImageReadersByMIMEType(mimeType)wenn Sie sich über den Dateityp sicher sein möchten ?
EdgeCaseBerg
3
Sollten Sie nicht .close()vor Ihrer Rückkehr den Stream anrufen ? Andernfalls lassen Sie den Stream offen
EdgeCaseBerg
1
@ php_coder_3809625 Das Protokoll befindet sich im Iterator. Es kann also vorkommen, dass ein ImageReader ausfällt, ein nachfolgender jedoch erfolgreich ist. Wenn alle fehlschlagen, wird eine IOException ausgelöst.
Andrew Taylor
1
Sie können org.apache.commons.io.FilenameUtils#getExtensiondie Dateinamenerweiterung verwenden.
Patrick Bergner
52

Ich habe einen anderen Weg gefunden, um eine Bildgröße zu lesen (allgemeiner). Sie können die ImageIO-Klasse in Zusammenarbeit mit ImageReaders verwenden. Hier ist der Beispielcode:

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

Beachten Sie, dass getFileSuffix eine Methode ist, die die Erweiterung des Pfads ohne "." Zurückgibt. also zB: png, jpg usw. Beispielimplementierung ist:

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

Diese Lösung ist sehr schnell, da nur die Bildgröße aus der Datei und nicht das gesamte Bild gelesen wird. Ich habe es getestet und es gibt keinen Vergleich mit der Leistung von ImageIO.read. Ich hoffe, jemand wird dies nützlich finden.

user350756
quelle
getFileSuffix()enthält unnötige ifs und das Initialisieren mit nullist in diesem Fall keine gute Idee.
Jimmy T.
2
Hölle ja das ist "im Grunde sehr schnell"! Ich denke, Sie haben sich damit für die Auszeichnung "Understatement of the Year" qualifiziert. Bläst ImageIO.read()völlig aus dem Wasser, sowohl in Bezug auf die CPU-Zeit als auch in Bezug auf die Speichernutzung.
aroth
1
public static String getFileSuffix (endgültiger String-Pfad) {if (path! = null && path.lastIndexOf ('.')! = -1) {return path.substring (path.lastIndexOf ('.')). substring (1) ;; } return null; }
Nilanchal
47

Ich habe versucht, die Leistung mit einigen der verschiedenen aufgeführten Ansätze zu testen. Es ist schwierig, einen strengen Test durchzuführen, da viele Faktoren das Ergebnis beeinflussen. Ich habe zwei Ordner vorbereitet, einen mit 330 JPG-Dateien und einen mit 330 PNG-Dateien. Die durchschnittliche Dateigröße betrug in beiden Fällen 4 MB. Dann habe ich für jede Datei getDimension aufgerufen. Jede Implementierung der getDimension-Methode und jeder Bildtyp wurde separat getestet (separater Lauf). Hier sind die Ausführungszeiten, die ich erhalten habe (erste Nummer für JPG, zweite Nummer für PNG):

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

Es ist offensichtlich, dass einige Methoden die gesamte Datei laden, um Dimensionen zu erhalten, während andere nur einige Header-Informationen aus dem Bild lesen. Ich denke, diese Zahlen können nützlich sein, wenn die Anwendungsleistung kritisch ist.

Vielen Dank an alle für den Beitrag zu diesem Thread - sehr hilfreich.

mp31415
quelle
2
Nun , das ist eine Antwort. Gut gemacht!
SSimm
1
Haben Sie auch die Heap-Speicherplatznutzung beim Hochladen von Bildern analysiert? Haben Sie beim Ausführen dieser Tests mit einer der Methoden OOM-Fehler erhalten?
Saibharath
Danke, deine Antwort hat mir sehr geholfen. Ich habe (50k HD Bild)
SüniÚr
16

Sie können JPEG-Binärdaten als Datei laden und die JPEG-Header selbst analysieren. Der gesuchte ist der 0xFFC0- oder Start of Frame-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.

Weitere Informationen zu den Headern finden Sie im JPEG-Eintrag von Wikipedia, oder ich habe die obigen Informationen hier erhalten .

Ich habe eine Methode verwendet, die dem Code ähnelt, den ich aus diesem Beitrag in den Sonnenforen erhalten habe:

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

}}

joinJpegs
quelle
Gut. Aber ich denke, dass stattdessen ==192die Nummern 192-207 überprüft werden sollten, außer 196, 200 und 204.
Wirbelwolf
Oder Sie können die com.drewnoakes.metadata-extractorBibliothek verwenden, um diese Überschriften einfach zu extrahieren
Victor Petit
9

Einfacher Weg:

BufferedImage readImage = null;

try {
    readImage = ImageIO.read(new File(your path);
    int h = readImage.getHeight();
    int w = readImage.getWidth();
} catch (Exception e) {
    readImage = null;
}
user1215499
quelle
3
Dies muss das gesamte Bild nur im Speicher lesen, um die Breite und Höhe zu kennen. Ja, es ist einfach, aber es wird entweder für viele oder große Bilder schlecht
Clint Eastwood
4

Das Problem mit ImageIO.read ist, dass es sehr langsam ist. Sie müssen lediglich den Bildheader lesen, um die Größe zu ermitteln. ImageIO.getImageReaderist der perfekte Kandidat.

Hier ist das Groovy-Beispiel, aber das Gleiche gilt für 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)"

Die Leistung war die gleiche wie bei der Verwendung der Java-Bibliothek SimpleImageInfo.

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

Argoden
quelle
Was ist getReaderByFormat?
Koray Tugay
3

Sie können das Toolkit verwenden, ohne ImageIO zu benötigen

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

Wenn Sie das Laden des Bildes nicht übernehmen möchten, tun Sie dies

ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
int height = imageIcon.getIconHeight();
int width = imageIcon.getIconWidth();
Karussell
quelle
1
ImageIO wird nicht benötigt, aber Toolkit. Was ist der Unterschied?
Dieter
ImageIO ist eine externe Abhängigkeit. Toolkit nicht
Karussell
ImageIO ist seit 1.4 Teil von Java docs.oracle.com/javase/7/docs/api/javax/imageio/…
dieter
Ja, vielleicht funktioniert das, sorry, wenn das damals verwirrend war. Als ich es versuchte, brauchte ich für einige Teile das JAI-Ding (vielleicht um andere Formate zu lesen?): Stackoverflow.com/questions/1209583/…
Karussell
3

Sie können die Breite und Höhe des Bildes mit dem BufferedImage-Objekt mithilfe von Java ermitteln.

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();
    }
KIBOU Hassan
quelle
Dadurch wird das gesamte Bild in den Speicher geladen, sehr verschwenderisch und sehr langsam.
Argoden
1

Das Abrufen eines gepufferten Bildes mit ImageIO.read ist eine sehr umfangreiche Methode, da eine vollständige unkomprimierte Kopie des Bilds im Speicher erstellt wird. Für PNGs können Sie auch PNGJ und den Code verwenden:

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

Nachdem mit gerungen ImageIOviel in den letzten Jahren, ich denke , Andrew Taylor ‚s Lösung bei weitem der beste Kompromiss ist (schnell: nicht mit ImageIO#readund vielseitig). Danke, Mann!!

Aber ich war ein wenig gezwungen zu werden , frustriert eine lokale Datei (File / String) zu verwenden, insbesondere in Fällen , in denen Sie Bildgrößen aus Richtung überprüfen wollen, sagen wir, eine multipart / form-data Anfrage , wo Sie in der Regel abrufen InputPart/ InputStreams. Also habe ich schnell eine Variante gemacht, die akzeptiert File, InputStreamund RandomAccessFilebasierend auf der Fähigkeit ImageIO#createImageInputStream, dies zu tun.

Natürlich kann eine solche Methode mit Object inputnur privat bleiben, und Sie müssen so viele polymorphe Methoden wie nötig erstellen und diese aufrufen. Sie können auch Pathmit Path#toFile()und URLmit akzeptieren, URL#openStream()bevor Sie zu dieser Methode übergehen:

  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");
      }
    }
  }
Fabien M.
quelle
-1

Nachdem ich alle Antworten von oben ausprobiert hatte, brachte ich sie nach unermüdlichen Versuchen leider nicht zum Arbeiten. Also habe ich mich entschlossen, den richtigen Hack selbst zu machen und ich gehe, um für mich zu arbeiten. Ich vertraue darauf, dass es auch für Sie perfekt funktionieren würde.

Ich verwende diese einfache Methode, um die Breite eines von der App generierten Bildes zu ermitteln und es später noch zur Überprüfung hochzuladen:

Pls. Beachten Sie: Sie müssten Berechtigungen im Manifest für den Zugriffsspeicher aktivieren.

/ Ich habe es statisch gemacht und in meine globale Klasse eingefügt, damit ich von nur einer Quelle aus darauf verweisen oder darauf zugreifen kann. Wenn Änderungen vorgenommen werden, muss alles an nur einer Stelle durchgeführt werden. Nur ein DRY-Konzept in Java beibehalten. (sowieso) :) /

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;

}}

AppEmmanuel
quelle
Hallo @gpasch wie so? Hast du es versucht ? Das funktioniert perfekt für mich. Keines der anderen Beispiele hat bei mir funktioniert. Einige verlangten von mir einige seltsame Importe von Klassen und andere, die Probleme verursachten. Ich möchte wirklich mehr aus Ihren Herausforderungen lernen. Bereitstehen. . .
AppEmmanuel
-2

Um die Größe der EMF-Datei ohne EMF Image Reader zu ermitteln, können Sie folgenden Code verwenden:

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);
}
Viktor Sirotin
quelle