Wie erhalte ich die Bildgröße mit der Standard-Python-Klasse (ohne externe Bibliothek)?

73

Ich benutze Python 2.5. Und mit den Standardklassen von Python möchte ich die Bildgröße einer Datei bestimmen.

Ich habe PIL (Python Image Library) gehört, aber es erfordert eine Installation, um zu funktionieren.

Wie kann ich die Größe eines Bildes erhalten, ohne eine externe Bibliothek zu verwenden, nur mit Python 2.5-eigenen Modulen?

Hinweis Ich möchte gängige Bildformate unterstützen, insbesondere JPG und PNG.

Eros
quelle
1
Irgendwelche Vorschläge, in welchem Bildformat Sie die Größe lernen möchten?
Larry Lustig
1
gängige Bildformate (PNG und JPG)
Eros
Siehe meine Antwort auf eine andere Frage, wenn Sie sich nicht für die Verwendung externer (aber häufig verwendeter) Bibliotheken interessieren
Martin Thoma

Antworten:

91

Hier ist ein Python 3-Skript, das ein Tupel zurückgibt, das eine Bildhöhe und -breite für .png, .gif und .jpeg enthält, ohne externe Bibliotheken zu verwenden (dh was Kurt McKee oben erwähnt hat). Sollte relativ einfach sein, es auf Python 2 zu übertragen.

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
Fred der Fantastische
quelle
Ihr Code hat in 2.7.3 meistens so funktioniert. Ich musste es umschreiben, weil ich bereits ein dateiähnliches Objekt hatte.
xZise
Es scheint , mit zum Scheitern verurteilt diese .
Krankheit
Und damit. , die zurückgeben sollte (640.480), aber ich bekomme (1281, 1).
Krankheit
Ich habe nur den PNG-Teil davon getestet, aber das funktioniert zumindest gut.
tremby
1
Das bereitgestellte Bild @Malandy ist ein DCT-JEPG-Basisbild , kein ICC / IPTC / JFIF-kompatibles Bild.
Mitoxys
64

Kurts Antwort musste leicht modifiziert werden, um für mich zu arbeiten.

Erstens auf Ubuntu: sudo apt-get install python-imaging

Dann:

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

Weitere Informationen finden Sie im Handbuch .

tjb
quelle
17
Beantwortet die Frage nicht - "(ohne externe Bibliothek zu verwenden)?" wird im Titel angegeben, und die Frage wird dann mit "Ich habe PIL (Python Image Library) gehört, aber es muss die Bibliothek installiert werden" geklärt.
Luna
13
@ RossAllan: Sicher, aber diese Frage ist die Nummer 1 bei Google für Varianten von Python Image dimensions, also +1 von mir für eine Antwort, bei der das Rad nicht neu erfunden werden muss :)
Clément
20

Während es möglich ist, open(filename, 'rb')die Binärbild-Header aufzurufen und nach den Dimensionen zu durchsuchen, scheint es viel nützlicher zu sein, PIL zu installieren und Ihre Zeit damit zu verbringen, großartige neue Software zu schreiben! Sie erhalten eine bessere Unterstützung für Dateiformate und die Zuverlässigkeit, die sich aus der weit verbreiteten Verwendung ergibt. Aus der PIL-Dokumentation geht hervor , dass der Code, den Sie zur Ausführung Ihrer Aufgabe benötigen würden, folgender wäre:

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

Was das Schreiben von Code selbst betrifft, ist mir kein Modul in der Python-Standardbibliothek bekannt, das das tut, was Sie wollen. Sie müssen open()das Bild im Binärmodus bearbeiten und es selbst dekodieren. Sie können über die Formate lesen unter:

Kurt McKee
quelle
2
+1 für die Dateiformatdokumentation, aber meine Anweisung ist nicht, eine externe Bibliothek zu verwenden, nur um die Bildgröße der PNG- und JPG-Bilddatei zu erhalten.
Eros
3
Sie müssen Image.opennicht nur Imageper tjb antworten.
Ghopper21
19

Hier finden Sie eine Möglichkeit, die Abmessungen einer PNG-Datei abzurufen, ohne ein Modul eines Drittanbieters zu benötigen. Von 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 __name__ == '__main__':
    with open('foo.png', 'rb') as f:
        data = f.read()

    print is_png(data)
    print get_image_info(data)

Wenn Sie dies ausführen, wird Folgendes zurückgegeben:

True
(x, y)

Ein weiteres Beispiel, das auch den Umgang mit JPEGs umfasst: http://markasread.net/post/17551554979/get-image-size-info-using-pure-python-code

cbautista
quelle
Ist es nicht ein wenig ineffizient, die gesamten Bilddaten zu lesen, wenn Sie nur die Headerdaten benötigen?
Adam Parkin
2
Um dies zu umgehen get_image_info(), kann man den Dateinamen als Parameter (und nicht als Binärdaten) umgestalten und dann einfach f.read(25)die Header-Informationen lesen.
Adam Parkin
7

In Bezug auf die Antwort von Fred the Fantastic :

Nicht jeder JPEG-Marker zwischen C0- CFsind SOFMarker; Ich habe DHT ( C4), DNL ( C8) und DAC ( CC) ausgeschlossen. Beachten Sie, dass ich nicht untersucht habe, ob es überhaupt möglich ist, andere Frames als C0und C2auf diese Weise zu analysieren . Die anderen scheinen jedoch ziemlich selten zu sein (ich persönlich bin keinem anderen als C0und begegnet C2).

In jedem Fall löst dies das Problem, das in den Kommentaren von Malandy mit Bangles.jpg(DHT fälschlicherweise als SOF analysiert) erwähnt wurde.

Das andere erwähnte Problem 1431588037-WgsI3vK.jpgbesteht darin, dass imghdrnur die Header APP0 (EXIF) und APP1 (JFIF) erkannt werden können.

Dies kann behoben werden, indem imghdr ein lockererer Test hinzugefügt wird (z. B. einfach FFD8oder vielleicht FFD8FF?) Oder etwas viel komplexeres (möglicherweise sogar Datenvalidierung). Bei einem komplexeren Ansatz habe ich nur Probleme gefunden mit: APP14 ( FFEE) (Adobe); der erste Marker ist DQT ( FFDB); und APP2 und Probleme mit eingebetteten ICC_PROFILEs .

Der überarbeitete Code unten hat den Aufruf ebenfalls imghdr.what()geringfügig geändert :

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

Hinweis: Es wurde eine vollständige Antwort anstelle eines Kommentars erstellt, da dies noch nicht zulässig ist.

Dagh Bunnstad
quelle
4

Wenn Sie ImageMagick installiert haben, können Sie " Identifizieren " verwenden. Zum Beispiel können Sie es so nennen:

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(',') ]
jensph
quelle
Dies ist eine gute Idee, aber es ist nicht erforderlich, die Regex-Maschinerie oder das Listenverständnis aufzurufen: width, height = list( map( int, dim.decode('utf-8').strip('"').split(',')))
Giacomo Lacava
2

In einem anderen Stackoverflow-Beitrag eine gute Lösung gefunden (nur mit Standardbibliotheken + auch mit JPG ): JohnTESlade-Antwort

Und eine andere Lösung (der schnelle Weg) für diejenigen, die es sich leisten können , den Befehl ' file ' in Python auszuführen, führen Sie Folgendes aus:

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

Ausgabe :

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

Jetzt müssen Sie nur noch die Ausgabe formatieren, um die Abmessungen zu erfassen. 352x198 in meinem Fall.

Merkur
quelle
1

Dieser Code erfüllt zwei Dinge:

  • Abrufen der Bildgröße

  • Finden Sie den tatsächlichen EOF einer JPG-Datei

Nun, beim googeln war ich mehr an dem späteren interessiert. Die Aufgabe bestand darin, eine JPG-Datei aus einem Datenstrom auszuschneiden. Da ich keine Möglichkeit gefunden habe, Pythons 'Bild' zu verwenden, um den EOF einer solchen JPG-Datei zu erhalten, habe ich mir das ausgedacht.

Interessante Dinge / Änderungen / Notizen in diesem Beispiel:

  • Erweitern der normalen Python-Dateiklasse mit der Methode uInt16, um den Quellcode besser lesbar und wartbar zu machen. Wenn Sie mit struct.unpack () herumspielen, sieht der Code schnell hässlich aus

  • Ersetzt durch "uninteressante" Bereiche / Chunk durch Suchen

  • Wenn Sie nur die Abmessungen erhalten möchten, können Sie die Linie entfernen:

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

    -> da dies nur beim Lesen des Bilddatenblocks und Kommentierens wichtig wird

    #break
    

    aufhören zu lesen, sobald die Dimension gefunden wurde. ... aber lächle was ich erzähle - du bist der Kodierer;)

      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()
    
Nadu
quelle
0

Es hängt von der Ausgabe der Datei ab, von der ich nicht sicher bin, ob sie auf allen Systemen standardisiert ist. Einige JPEGs geben die Bildgröße nicht an

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

Stolperte über dieses, aber Sie können es erhalten, indem Sie das Folgende verwenden, solange Sie numpy importieren.

import numpy as np

[y, x] = np.shape(img[:,:,0])

Es funktioniert, weil Sie alle bis auf eine Farbe ignorieren und das Bild dann nur noch 2D ist. Die Form gibt also an, wie hoch das Gebot ist. Python ist noch ein bisschen neu, scheint aber eine einfache Möglichkeit zu sein.

Parker
quelle