Comment obtenir la hauteur et la largeur de l'image en utilisant Java?

106

Existe-t-il un autre moyen que d'utiliser ImageIO.read pour obtenir la hauteur et la largeur de l'image?

Parce que je rencontre un problème qui bloque le fil.

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)

Cette erreur ne se produit que sur un serveur d'applications Sun et je soupçonne donc qu'il s'agit d'un bogue Sun.

argent est
la source
Quelle erreur? Vous n'affichez qu'une partie d'une trace de pile (qui semble provenir jstack).
Joachim Sauer
Avez-vous trouvé la cause ou la solution de ce problème? J'obtiens le même problème où il verrouille le fil sur cette même méthode.
Timothy Chen
Il y a un bogue qui pourrait être lié: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6791502
Adam Schmideg

Réponses:

288

Voici quelque chose de très simple et pratique.

BufferedImage bimg = ImageIO.read(new File(filename));
int width          = bimg.getWidth();
int height         = bimg.getHeight();
Apurv
la source
7
C'est la meilleure réponse de très loin, et vous avez été trompé de votes par la même réponse publiée par quelqu'un d'autre 17 jours après votre publication. Cela devrait être la réponse du haut et non du bas.
Survirage du
34
De tout ce que je lis, cela lit l'image entière en mémoire. Ce qui est extrême juste pour obtenir la largeur et la hauteur.
Marc
7
mauvaise manière: vous devrez charger le raster d'image entier en mémoire, ce qui provoque OOM avec de très grandes images
codeur
4
La question demande spécifiquement un moyen autre que d'utiliser ImageIO.read. Votre réponse est "utiliser ImageIO.read". Pourquoi est-ce considéré comme une bonne réponse?
ssimm
5
C'est la pire façon de le faire et je ne comprends pas pourquoi c'est si fortement voté. Il charge l'image entière dans le tas qui est lent et consomme beaucoup de mémoire inutile.
Patrick Favre
72

Ceci est une réécriture du grand article de @Kay, qui lance IOException et fournit une sortie anticipée:

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

Je suppose que mon représentant n'est pas assez élevé pour que ma contribution soit considérée comme une réponse.

Andrew Taylor
la source
Merci pour cela! et compte tenu de la comparaison des performances effectuée par user194715, je prendrai votre suggestion pour les performances et la prise en compte des png! Je vous remercie!
ah-shiang han
Ne pourriez-vous pas également utiliser probeContentType du package nio.Files combiné avec javax.imageio.ImageIO.getImageReadersByMIMEType(mimeType)si vous vouliez être certain du type de fichier?
EdgeCaseBerg
3
Aussi, ne devriez-vous pas appeler .close()en streaming avant de revenir? sinon vous laissez le flux ouvert
EdgeCaseBerg
1
@ php_coder_3809625 le journal est dans l'itérateur, il se peut donc qu'un ImageReader échoue, mais qu'un suivant réussisse. S'ils échouent tous, une IOException est déclenchée.
Andrew Taylor
1
Vous pouvez envisager d'utiliser org.apache.commons.io.FilenameUtils#getExtensionpour détecter l'extension de nom de fichier.
Patrick Bergner
52

J'ai trouvé un autre moyen de lire une taille d'image (plus générique). Vous pouvez utiliser la classe ImageIO en coopération avec ImageReaders. Voici l exemple de code:

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

Notez que getFileSuffix est une méthode qui renvoie l'extension du chemin sans "." donc par exemple: png, jpg etc. Exemple d'implémentation:

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

Cette solution est très rapide car seule la taille de l'image est lue à partir du fichier et non de l'image entière. Je l'ai testé et il n'y a aucune comparaison avec les performances d'ImageIO.read. J'espère que quelqu'un trouvera cela utile.

user350756
la source
getFileSuffix()contient des ifs inutiles et l'initialisation avec nulln'est pas une bonne idée dans ce cas.
Jimmy T.
2
Enfer ouais c'est "fondamentalement très rapide"! Je pense que vous vous êtes qualifié pour le prix «euphémisme de l'année» avec celui-là. Souffle ImageIO.read()complètement hors de l'eau, à la fois en termes de temps CPU et d'utilisation de la mémoire.
aroth le
1
public static String getFileSuffix (final String path) {if (path! = null && path.lastIndexOf ('.')! = -1) {return path.substring (path.lastIndexOf ('.')). substring (1) ; } return null; }
Nilanchal
47

J'ai essayé de tester les performances en utilisant certaines des différentes approches répertoriées. Il est difficile de faire un test rigoureux car de nombreux facteurs affectent le résultat. J'ai préparé deux dossiers, un avec 330 fichiers jpg et un autre avec 330 fichiers png. La taille moyenne des fichiers était de 4 Mo dans les deux cas. Ensuite, j'ai appelé getDimension pour chaque fichier. Chaque implémentation de la méthode getDimension et chaque type d'image ont été testés séparément (exécution distincte). Voici les temps d'exécution que j'ai obtenus (premier nombre pour jpg, deuxième nombre pour png):

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

Il est évident que certaines méthodes chargent le fichier entier afin d'obtenir des dimensions tandis que d'autres se contentent de lire certaines informations d'en-tête de l'image. Je pense que ces chiffres peuvent être utiles lorsque les performances des applications sont critiques.

Merci à tous pour la contribution à ce fil - très utile.

mp31415
la source
3
Maintenant , c'est une réponse. Bon travail!
ssimm
1
Avez-vous également analysé l'utilisation de l'espace de tas lorsque vous téléchargiez des images? De plus, avez-vous rencontré des erreurs MOO lors de l'exécution de ces tests sur l'une des méthodes?
saibharath
Merci votre réponse m'a beaucoup aidé. J'ai (photo HD 50k)
SüniÚr
17

Vous pouvez charger des données binaires jpeg sous forme de fichier et analyser vous-même les en-têtes jpeg. Celui que vous recherchez est l'en-tête 0xFFC0 ou 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.

Pour plus d'informations sur les en-têtes, consultez l'entrée jpeg de wikipedia ou j'ai obtenu les informations ci-dessus ici .

J'ai utilisé une méthode similaire au code ci-dessous que j'ai obtenu de ce post sur les forums 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])));
}

}

joinJpegs
la source
Bien. Mais je pense qu'au lieu de ==192cela, il faudrait vérifier les numéros 192-207, sauf 196, 200 et 204.
vortexwolf
Ou vous pouvez utiliser la com.drewnoakes.metadata-extractorbibliothèque pour extraire facilement ces en
Victor Petit
9

Manière simple:

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
la source
3
cela a besoin de lire l'image entière en mémoire uniquement pour connaître la largeur et la hauteur. Oui, c'est simple, mais cela fonctionnera mal pour de nombreuses images ou pour de grandes images ...
Clint Eastwood
4

Le problème avec ImageIO.read est qu'il est vraiment lent. Tout ce que vous avez à faire est de lire l'en-tête de l'image pour obtenir la taille. ImageIO.getImageReaderest un candidat parfait.

Voici l'exemple Groovy, mais la même chose s'applique à 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)"

Les performances étaient identiques à celles de l'utilisation de la bibliothèque java SimpleImageInfo.

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

argoden
la source
Qu'est-ce que c'est getReaderByFormat?
Koray Tugay
3

Vous pouvez utiliser la boîte à outils, pas besoin d'ImageIO

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

Si vous ne souhaitez pas gérer le chargement de l'image, faites

ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
int height = imageIcon.getIconHeight();
int width = imageIcon.getIconWidth();
Karussell
la source
1
Pas besoin d'ImageIO mais besoin de Toolkit. Quelle est la différence?
dieter
ImageIO est une dépendance externe. Toolkit not
Karussell
ImageIO fait partie de Java depuis la version 1.4 docs.oracle.com/javase/7/docs/api/javax/imageio/…
dieter
Oui, peut-être que cela fonctionnera, désolé si cela était déroutant alors. Quand je l'ai essayé, j'avais besoin pour certaines parties du truc JAI (peut-être pour lire d'autres formats?): Stackoverflow.com/questions/1209583/…
Karussell
3

Vous pouvez obtenir la largeur et la hauteur de l'image avec l'objet BufferedImage en utilisant 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();
    }
KIBOU Hassan
la source
ceci chargera l'image entière dans la mémoire, très gaspilleuse et très lente.
argoden
1

Obtenir une image tamponnée avec ImageIO.read est une méthode très lourde, car elle crée une copie complète non compressée de l'image en mémoire. Pour les png, vous pouvez également utiliser pngj et le code:

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

Ayant ImageIObeaucoup lutté ces dernières années, je pense que la solution d' Andrew Taylor est de loin le meilleur compromis (rapide: non utilisable et polyvalente). Merci mec!!ImageIO#read

Mais j'étais un peu frustré d'être obligé d'utiliser un fichier local (File / String), en particulier dans les cas où vous voulez vérifier les tailles d'image provenant, par exemple, d'une requête multipart / form-data où vous récupérez habituellement les InputPart/ InputStream. J'ai donc rapidement fait une variante qui accepte File, InputStreamet RandomAccessFile, basée sur la capacité deImageIO#createImageInputStream le faire.

Bien sûr, une telle méthode avec Object input , ne peut rester que privée et vous devrez créer autant de méthodes polymorphes que nécessaire, en appelant celle-ci. Vous pouvez également accepter Pathavec Path#toFile()et URLavec URL#openStream()avant de passer à cette méthode:

  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
la source
-1

Donc, malheureusement, après avoir essayé toutes les réponses ci-dessus, je ne les ai pas mises au travail après des tentatives inlassables. J'ai donc décidé de faire le vrai hack moi-même et je vais travailler pour moi. J'espère que cela fonctionnerait parfaitement pour vous aussi.

J'utilise cette méthode simple pour obtenir la largeur d'une image générée par l'application et que je dois télécharger plus tard pour vérification:

Pls. prenez note: vous devrez activer les autorisations dans le manifeste pour accéder au stockage.

/ Je l'ai rendu statique et mis dans ma classe Global afin que je puisse le référencer ou y accéder à partir d'une seule source et s'il y a une modification, tout devrait être fait à un seul endroit. Simplement maintenir un concept DRY en java. (de toute façon) :) /

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
la source
Bonjour @gpasch comment ça? L'as tu essayé ? Cela fonctionne parfaitement pour moi. Aucun des autres exemples n'a fonctionné pour moi. Certains m'ont obligé à faire des importations étranges de classes et d'autres qui ont causé des problèmes. Je veux vraiment en savoir plus sur vos défis. Debout . . .
AppEmmanuel
-2

Pour obtenir la taille du fichier emf sans lecteur d'images EMF, vous pouvez utiliser le code:

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
la source