Come ottenere l'altezza e la larghezza dell'immagine usando java?


106

Esiste un altro modo oltre a utilizzare ImageIO.read per ottenere l'altezza e la larghezza dell'immagine?

Perché riscontro un problema che blocca il thread.

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)

Questo errore si verifica solo su un server di app Sun e quindi sospetto che si tratti di un bug di Sun.


Quale errore? Mostra solo una parte di una traccia dello stack (che sembra provenire da jstack).
Joachim Sauer

Hai trovato la causa o risolto questo problema? Sto riscontrando lo stesso problema in cui blocca il thread sullo stesso metodo.
Timothy Chen,

C'è un bug che potrebbe essere correlato: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791502
Adam Schmideg

Risposte:


288

Ecco qualcosa di molto semplice e pratico.

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

7
Questa è la risposta migliore da molto tempo e sei stato truffato dai voti dalla stessa risposta pubblicata da qualcun altro 17 giorni dopo il tuo post. Questa dovrebbe essere la prima risposta, non l'ultima.
Sovrasterzo

34
Da tutto quello che sto leggendo, questo legge l'intera immagine nella memoria. Il che è estremo solo per ottenere larghezza e altezza.
Marc

7
brutto modo: dovrai caricare l'intera immagine raster in memoria, il che causa OOM con immagini molto grandi
codificatore

4
La domanda chiede specificamente un modo diverso dall'utilizzo di ImageIO.read. La tua risposta è "usa ImageIO.read". Perché questa è considerata una buona risposta?
ssimm

5
Questo è il modo peggiore per farlo e non capisco perché questo sia votato così pesantemente. Carica l'intera immagine nell'heap che è lento e consuma molta memoria non necessaria.
Patrick Favre

72

Questa è una riscrittura del fantastico post di @Kay, che genera IOException e fornisce un'uscita anticipata:

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

Immagino che la mia reputazione non sia abbastanza alta perché il mio contributo possa essere considerato degno di risposta.


Grazie per questo! e considerando il confronto delle prestazioni fatto da user194715, terrò in considerazione il tuo suggerimento per le prestazioni e il png! Grazie!
ah-shiang han

Non potresti usare anche probeContentType dal pacchetto nio.Files combinato con javax.imageio.ImageIO.getImageReadersByMIMEType(mimeType)se volessi essere certo del tipo di file?
EdgeCaseBerg

3
Inoltre, non dovresti chiamare .close()in streaming prima di tornare? altrimenti lasci lo stream aperto
EdgeCaseBerg

1
@ php_coder_3809625 il registro è nell'iteratore, quindi potrebbe essere il caso che un ImageReader fallisca, ma un successivo ha esito positivo. Se falliscono tutti, viene generata un'eccezione IOException.
Andrew Taylor

1
Potresti prendere in considerazione l'utilizzo org.apache.commons.io.FilenameUtils#getExtensionper rilevare l'estensione del nome file.
Patrick Bergner

52

Ho trovato un altro modo per leggere una dimensione dell'immagine (più generica). Puoi utilizzare la classe ImageIO in collaborazione con ImageReaders. Ecco il codice di esempio:

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

Notare che getFileSuffix è un metodo che restituisce l'estensione del percorso senza "." quindi, ad esempio: png, jpg ecc. L'implementazione di esempio è:

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

Questa soluzione è molto rapida in quanto solo la dimensione dell'immagine viene letta dal file e non l'intera immagine. L'ho testato e non c'è confronto con le prestazioni di ImageIO.read. Spero che qualcuno lo trovi utile.


getFileSuffix()contiene se non necessari e la sigla con nullnon è una buona idea in questo caso.
Jimmy T.

2
Diavolo sì, questo è "fondamentalmente molto veloce"! Penso che ti sei qualificato per il premio "eufemismo dell'anno" con quello. È ImageIO.read()completamente fuori dall'acqua, sia in termini di tempo della CPU che di utilizzo della memoria.
aroth

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

47

Ho provato a testare le prestazioni utilizzando alcuni dei vari approcci elencati. È difficile fare un test rigoroso poiché molti fattori influenzano il risultato. Ho preparato due cartelle, una con 330 file jpg e un'altra con 330 file png. La dimensione media del file era di 4 MB in entrambi i casi. Quindi ho chiamato getDimension per ogni file. Ogni implementazione del metodo getDimension e ogni tipo di immagine è stata testata separatamente (esecuzione separata). Ecco i tempi di esecuzione che ho ottenuto (primo numero per jpg, secondo numero per png):

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

È ovvio che alcuni metodi caricano l'intero file per ottenere le dimensioni mentre altri lo ottengono semplicemente leggendo alcune informazioni di intestazione dall'immagine. Penso che questi numeri possano essere utili quando le prestazioni dell'applicazione sono fondamentali.

Grazie a tutti per il contributo a questo thread - molto utile.


3
Questa è una risposta. Buon lavoro!
ssimm

1
Hai anche analizzato l'utilizzo dello spazio heap durante il caricamento delle immagini? Inoltre, hai ricevuto errori OOM durante l'esecuzione di questi test su uno qualsiasi dei metodi?
saibharath

Grazie la tua risposta mi ha aiutato molto. Ho (50k foto HD)
SüniÚr

17

Puoi caricare i dati binari jpeg come file e analizzare tu stesso le intestazioni jpeg. Quello che stai cercando è l'intestazione 0xFFC0 o Start of Frame:

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.

Per maggiori informazioni sulle intestazioni controlla la voce jpeg di wikipedia o ho ottenuto le informazioni sopra qui .

Ho usato un metodo simile al codice riportato di seguito che ho ricevuto da questo post sui forum del sole:

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

}


Buona. Ma penso che invece ==192dovrebbe controllare i numeri 192-207, tranne 196, 200 e 204.
vortexwolf

Oppure puoi usare la com.drewnoakes.metadata-extractorlibreria per estrarre facilmente queste intestazioni
Victor Petit

9

Modo semplice:

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
questo ha bisogno di leggere l'intera immagine in memoria solo per conoscere la larghezza e l'altezza. Sì, è semplice, ma funzionerà male per molte immagini o per quelle enormi ...
Clint Eastwood,

4

Il problema con ImageIO.read è che è molto lento. Tutto quello che devi fare è leggere l'intestazione dell'immagine per ottenere la dimensione. ImageIO.getImageReaderè il candidato perfetto.

Ecco l'esempio di Groovy, ma la stessa cosa vale per 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)"

Le prestazioni erano le stesse dell'utilizzo della libreria Java SimpleImageInfo.

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


Che cos'è getReaderByFormat?
Koray Tugay

3

Potresti usare il Toolkit, senza bisogno di ImageIO

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

Se non vuoi gestire il caricamento dell'immagine fallo

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

1
Non è necessario ImageIO ma è necessario Toolkit. Qual è la differenza?
Dieter

ImageIO è una dipendenza esterna. Toolkit not
Karussell


Sì, forse funzionerà, scusate se questo era confuso allora. Quando l'ho provato avevo bisogno per alcune parti della cosa JAI (forse per leggere altri formati?): Stackoverflow.com/questions/1209583/…
Karussell

3

Puoi ottenere la larghezza e l'altezza dell'immagine con l'oggetto BufferedImage usando java.

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

questo caricherà l'intera immagine nella memoria, molto dispendioso e molto lento.
argoden

1

Per ottenere un'immagine bufferizzata con ImageIO.read è un metodo molto pesante, poiché crea una copia completa non compressa dell'immagine in memoria. Per png puoi anche usare pngj e il codice:

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

0

Avendo lottato ImageIOmolto negli ultimi anni, penso che la soluzione di Andrew Taylor sia di gran lunga il miglior compromesso (veloce: non utilizzabile e versatile). Grazie uomo!!ImageIO#read

Ma ero un po 'frustrato per essere costretto a utilizzare un file locale (File / Stringa), specialmente nei casi in cui si desidera controllare le dimensioni dell'immagine provenienti, ad esempio, da una richiesta multipart / form-data da cui di solito si recuperano InputPart/ InputStream. Così ho subito fatto una variante che accetta File, InputStreame RandomAccessFile, in base alla capacità di ImageIO#createImageInputStreamfarlo.

Ovviamente, un tale metodo con Object input, può solo rimanere privato e creerai tutti i metodi polimorfici necessari, chiamando questo. Puoi anche accettare Pathcon Path#toFile()e URLcon URL#openStream()prima di passare a questo metodo:

  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

Quindi sfortunatamente, dopo aver provato tutte le risposte dall'alto, non le ho fatte funzionare dopo instancabili tempi di tentativi. Quindi ho deciso di fare il vero hack da solo e vado a lavorare per me. Spero che funzionerebbe perfettamente anche per te.

Sto usando questo semplice metodo per ottenere la larghezza di un'immagine generata dall'app e ancora da caricare in un secondo momento per la verifica:

Pls. prendi nota: dovresti abilitare le autorizzazioni nel manifest per l'accesso allo storage.

/ L' ho reso statico e inserito nella mia classe Global in modo da poterlo fare riferimento o accedervi da una sola fonte e se ci sono modifiche, tutto dovrebbe essere fatto in un solo posto. Mantenere solo un concetto DRY in Java. (comunque) :) /

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;

}


Ciao @gpasch come mai? l'hai provato ? Funziona perfettamente per me. Nessuno degli altri esempi ha funzionato per me. Alcuni mi hanno richiesto di fare alcune strane importazioni di classi e altri che hanno causato problemi. Voglio davvero imparare di più da quello che sono state le tue sfide. In attesa . . .
AppEmmanuel

-2

Per ottenere la dimensione del file emf senza EMF Image Reader è possibile utilizzare il codice:

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.