web-dev-qa-db-fra.com

Comment obtenir une taille d'image en utilisant la classe standard Python (sans utiliser une bibliothèque externe)?

J'utilise Python 2.5. Et en utilisant les classes standard de Python, je veux déterminer la taille de l'image d'un fichier.

J'ai entendu PIL (Python Image Library), mais cela nécessite une installation pour fonctionner.

Comment puis-je obtenir la taille d'une image sans utiliser aucune bibliothèque externe, en utilisant simplement les modules de Python 2.5?

Remarque Je souhaite prendre en charge les formats d'image courants, notamment JPG et PNG.

65
eros

Voici un script python 3) qui retourne un tuple contenant une hauteur et une largeur d'image pour .png, .gif et .jpeg sans utiliser aucune bibliothèque externe (c'est-à-dire ce que Kurt McKee a mentionné plus haut). Devrait être relativement facile à transférer à Python 2.

import struct
import imghdr

def get_image_size(fname):
    '''Determine the image type of fhandle and return its size.
    from draco'''
    with open(fname, 'rb') as fhandle:
        head = fhandle.read(24)
        if len(head) != 24:
            return
        if imghdr.what(fname) == 'png':
            check = struct.unpack('>i', head[4:8])[0]
            if check != 0x0d0a1a0a:
                return
            width, height = struct.unpack('>ii', head[16:24])
        Elif imghdr.what(fname) == 'gif':
            width, height = struct.unpack('<HH', head[6:10])
        Elif imghdr.what(fname) == 'jpeg':
            try:
                fhandle.seek(0) # Read 0xff next
                size = 2
                ftype = 0
                while not 0xc0 <= ftype <= 0xcf:
                    fhandle.seek(size, 1)
                    byte = fhandle.read(1)
                    while ord(byte) == 0xff:
                        byte = fhandle.read(1)
                    ftype = ord(byte)
                    size = struct.unpack('>H', fhandle.read(2))[0] - 2
                # We are at a SOFn block
                fhandle.seek(1, 1)  # Skip `precision' byte.
                height, width = struct.unpack('>HH', fhandle.read(4))
            except Exception: #IGNORE:W0703
                return
        else:
            return
        return width, height
85
Fred the Fantastic

La réponse des Kurts devait être légèrement modifiée pour fonctionner pour moi.

D'abord sur Ubuntu: Sudo apt-get install python-imaging

Ensuite:

from PIL import Image
im=Image.open(filepath)
im.size # (width,height) Tuple

Consultez le manuel pour plus d'informations.

62
tjb

Bien qu'il soit possible d'appeler open(filename, 'rb') et de vérifier les dimensions des en-têtes d'image binaire, il semble beaucoup plus utile d'installer PIL et de passer votre temps à écrire de nouveaux logiciels super! Vous bénéficiez d'une meilleure prise en charge des formats de fichiers et de la fiabilité résultant d'une utilisation généralisée. d'après la documentation PIL , il apparaît que le code nécessaire pour mener à bien votre tâche serait le suivant:

from PIL import Image
im = Image.open('filename.png')
print 'width: %d - height: %d' % im.size # returns (width, height) Tuple

En ce qui concerne l'écriture de code vous-même, je ne connais pas de module dans la bibliothèque standard Python qui fera ce que vous voulez. Vous devrez open() _ l'image dans en mode binaire et commencez à le décoder vous-même. Vous pouvez en savoir plus sur les formats à:

19
Kurt McKee

Voici un moyen d'obtenir les dimensions d'un fichier png sans avoir besoin d'un module tiers. De http://coreygoldberg.blogspot.com/2013/01/python-verify-png-file-and-get-image.html

import struct

def get_image_info(data):
    if is_png(data):
        w, h = struct.unpack('>LL', data[16:24])
        width = int(w)
        height = int(h)
    else:
        raise Exception('not a png image')
    return width, height

def is_png(data):
    return (data[:8] == '\211PNG\r\n\032\n'and (data[12:16] == 'IHDR'))

if __== '__main__':
    with open('foo.png', 'rb') as f:
        data = f.read()

    print is_png(data)
    print get_image_info(data)

Lorsque vous exécutez ceci, il retournera:

True
(x, y)

Et un autre exemple incluant la gestion des fichiers JPEG: http://markasread.net/post/17551554979/get-image-size-info-using-pure-python-code

18
cbautista

Concernant la réponse de Fred the Fantastic :

Tous les marqueurs JPEG compris entre C0 -CF ne sont pas SOF marqueurs; J'ai exclu DHT (C4), DNL (C8) Et DAC (CC). Notez que je n'ai pas cherché à savoir s'il était même possible d'analyser des images autres que C0 Et C2 De cette manière. Cependant, les autres semblent être assez rares (je n'ai personnellement rencontré personne d'autre que C0 Et C2).

Quoi qu'il en soit, cela résout le problème mentionné dans les commentaires de Malandy avec Bangles.jpg (DHT analysé à tort comme un fichier SOF).

L'autre problème mentionné avec 1431588037-WgsI3vK.jpg Est dû au fait que imghdr ne peut détecter que les en-têtes APP0 (EXIF) et APP1 (JFIF).

Cela peut être corrigé en ajoutant un test plus laxiste à imghdr (par exemple, simplement FFD8 Ou peut-être FFD8FF?) Ou quelque chose de beaucoup plus complexe (éventuellement même la validation des données). Avec une approche plus complexe, je n'ai trouvé que des problèmes avec: APP14 (FFEE) (Adobe); le premier marqueur est DQT (FFDB); et APP2 et problèmes liés aux fichiers ICC_PROFILE intégrés .

Le code révisé ci-dessous a également modifié légèrement l'appel à imghdr.what():

import struct
import imghdr

def test_jpeg(h, f):
    # SOI APP2 + ICC_PROFILE
    if h[0:4] == '\xff\xd8\xff\xe2' and h[6:17] == b'ICC_PROFILE':
        print "A"
        return 'jpeg'
    # SOI APP14 + Adobe
    if h[0:4] == '\xff\xd8\xff\xee' and h[6:11] == b'Adobe':
        return 'jpeg'
    # SOI DQT
    if h[0:4] == '\xff\xd8\xff\xdb':
        return 'jpeg'
imghdr.tests.append(test_jpeg)

def get_image_size(fname):
    '''Determine the image type of fhandle and return its size.
    from draco'''
    with open(fname, 'rb') as fhandle:
        head = fhandle.read(24)
        if len(head) != 24:
            return
        what = imghdr.what(None, head)
        if what == 'png':
            check = struct.unpack('>i', head[4:8])[0]
            if check != 0x0d0a1a0a:
                return
            width, height = struct.unpack('>ii', head[16:24])
        Elif what == 'gif':
            width, height = struct.unpack('<HH', head[6:10])
        Elif what == 'jpeg':
            try:
                fhandle.seek(0) # Read 0xff next
                size = 2
                ftype = 0
                while not 0xc0 <= ftype <= 0xcf or ftype in (0xc4, 0xc8, 0xcc):
                    fhandle.seek(size, 1)
                    byte = fhandle.read(1)
                    while ord(byte) == 0xff:
                        byte = fhandle.read(1)
                    ftype = ord(byte)
                    size = struct.unpack('>H', fhandle.read(2))[0] - 2
                # We are at a SOFn block
                fhandle.seek(1, 1)  # Skip `precision' byte.
                height, width = struct.unpack('>HH', fhandle.read(4))
            except Exception: #IGNORE:W0703
                return
        else:
            return
        return width, height

Remarque: Création d'une réponse complète au lieu d'un commentaire, car je ne suis pas encore autorisé à le faire.

5
Dagh Bunnstad

Si vous avez ImageMagick installé, vous pouvez utiliser ' identifier '. Par exemple, vous pouvez l'appeler comme ceci:

path = "//folder/image.jpg"
dim = subprocess.Popen(["identify","-format","\"%w,%h\"",path], stdout=subprocess.PIPE).communicate()[0]
(width, height) = [ int(x) for x in re.sub('[\t\r\n"]', '', dim).split(',') ]
4
jensph

Ce code accomplit 2 choses:

  • Obtenir le dimension de l'image

  • Trouver le vrai EOF d'un fichier jpg

Eh bien, quand googler j'étais plus intéressé par le dernier. La tâche consistait à découper un fichier jpg dans un flux de données. Depuis que je n'ai trouvé aucun moyen d'utiliser "l'image" de Pythons pour obtenir le EOF de so jpg-File, j'ai inventé ceci.

Choses/changements/notes intéressants dans cet exemple:

  • extension de la classe de fichiers Python) normale avec la méthode uInt16 rendant le code source plus lisible et facile à gérer.

  • Remplacé les zones/morceaux non lus avec la lecture

  • Si vous souhaitez simplement obtenir les dimensions, vous pouvez supprimer la ligne:

    hasChunk = ord(byte) not in range( 0xD0, 0xDA) + [0x00] 
    

    -> dans la mesure où cela n’a d’importance que lors de la lecture du bloc de données d’image et du commentaire dans

    #break
    

    pour arrêter de lire dès que la dimension a été trouvée. ... mais souriez ce que je dis - vous êtes le codeur;)

      import struct
      import io,os
    
      class myFile(file):
    
          def byte( self ):
               return file.read( self,  1);
    
          def uInt16( self ):
               tmp = file.read( self,  2)
               return struct.unpack( ">H", tmp )[0];
    
      jpeg = myFile('grafx_ui.s00_\\08521678_Unknown.jpg', 'rb')
    
      try:
          height = -1
          width  = -1
          EOI    = -1
    
          type_check = jpeg.read(2)
          if type_check != b'\xff\xd8':
            print("Not a JPG")
    
          else:
    
            byte = jpeg.byte()
    
            while byte != b"":
    
              while byte != b'\xff': byte = jpeg.byte()
              while byte == b'\xff': byte = jpeg.byte()
    
    
              # FF D8       SOI Start of Image
              # FF D0..7  RST DRI Define Restart Interval inside CompressedData
              # FF 00           Masked FF inside CompressedData
              # FF D9       EOI End of Image
              # http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure
              hasChunk = ord(byte) not in range( 0xD0, 0xDA) + [0x00]
              if hasChunk:
                   ChunkSize   =  jpeg.uInt16()  - 2
                   ChunkOffset =  jpeg.tell()
                   Next_ChunkOffset = ChunkOffset + ChunkSize
    
    
              # Find bytes \xFF \xC0..C3 That marks the Start of Frame
              if (byte >= b'\xC0' and byte <= b'\xC3'):
    
                # Found  SOF1..3 data chunk - Read it and quit
                jpeg.seek(1, os.SEEK_CUR)
                h = jpeg.uInt16()
                w = jpeg.uInt16()
    
    
                #break
    
    
              Elif (byte == b'\xD9'):
                   # Found End of Image
                   EOI = jpeg.tell()
                   break
              else:
                  # Seek to next data chunk
                 print "Pos: %.4x %x" % (jpeg.tell(), ChunkSize)
    
              if hasChunk:       
                 jpeg.seek(Next_ChunkOffset)
    
              byte = jpeg.byte()
    
            width  = int(w)
            height = int(h)
    
            print("Width: %s, Height: %s  JpgFileDataSize: %x" % (width, height, EOI))
      finally:
          jpeg.close()
    
1
Nadu

J'ai trouvé une solution intéressante dans un autre article de Stackoverflow (en n'utilisant que des bibliothèques standard + traitant également de jpg): réponse de JohnTESlade

Et une autre solution (le moyen rapide) pour ceux qui peuvent se permettre d’exécuter la commande ' du fichier ' au sein de python, exécutez:

import os
info = os.popen("file foo.jpg").read()
print info

Sortie :

foo.jpg: JPEG image data...density 28x28, segment length 16, baseline, precision 8, 352x198, frames 3

Tout ce que vous devez faire maintenant, c'est formater la sortie pour capturer les dimensions. 352x198 dans mon cas.

1
Mercury

Cela dépend de la sortie du fichier dont je ne suis pas sûr est normalisé sur tous les systèmes. Certains JPEG ne signalent pas la taille de l'image

import subprocess, re
image_size = list(map(int, re.findall('(\d+)x(\d+)', subprocess.getoutput("file" + filename))[-1]))
0
Safvan CK