Как получить размер изображения с помощью стандартного класса Python (без использования внешней библиотеки)? - PullRequest
64 голосов
/ 07 ноября 2011

Я использую Python 2.5.И используя стандартные классы из Python, я хочу определить размер изображения файла.

Я слышал PIL (библиотека изображений Python), но для работы требуется установка.

Какможно ли получить размер изображения без использования какой-либо внешней библиотеки, просто используя собственные модули Python 2.5?

Примечание. Я хочу поддерживать распространенные форматы изображений, в частности JPG и PNG.

Ответы [ 9 ]

85 голосов
/ 04 декабря 2013

Вот скрипт на python 3, который возвращает кортеж с высотой и шириной изображения для .png, .gif и .jpeg без использования каких-либо внешних библиотек (то есть того, на что ссылался Курт Макки выше).Должно быть относительно легко перенести его в 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
62 голосов
/ 29 февраля 2012

Куртский ответ нужно немного изменить, чтобы он работал на меня.

Сначала в Ubuntu: sudo apt-get install python-imaging

Затем:

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

Проверьте справочник для получения дополнительной информации.

19 голосов
/ 07 ноября 2011

Хотя можно позвонить по номеру open(filename, 'rb') и проверить размеры заголовков двоичных изображений, кажется, гораздо полезнее установить PIL и потратить время на написание великолепного нового программного обеспечения!Вы получаете большую поддержку форматов файлов и надежность, которая обеспечивается за счет широкого использования. Из документации PIL видно, что код, который вам понадобится для выполнения вашей задачи, будет:

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

Что касается написания кода самостоятельно, я не знаю о модуле встандартная библиотека Python, которая будет делать то, что вы хотите.Вам нужно open() изображение в двоичном режиме и начать декодировать его самостоятельно.О форматах можно прочитать по адресу:

18 голосов
/ 04 февраля 2014

Вот способ получить размеры png-файла без использования стороннего модуля.From 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)

Когда вы запустите это, он вернет:

True
(x, y)

И еще один пример, который также включает обработку JPEG: http://markasread.net/post/17551554979/get-image-size-info-using-pure-python-code

4 голосов
/ 25 мая 2012

Если у вас установлено ImageMagick , тогда вы можете использовать ' identifier '. Например, вы можете назвать это так:

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(',') ]
3 голосов
/ 29 сентября 2016

Относительно Фред Фантастик ответ :

Не каждый маркер JPEG между C0 - CF является SOF маркерами; Я исключил DHT (C4), DNL (C8) и DAC (CC). Обратите внимание, что я не изучал, возможно ли таким образом анализировать любые кадры, кроме C0 и C2. Тем не менее, другие, кажется, довольно редко (лично я не встречал ничего, кроме C0 и C2).

В любом случае, это решает проблему, упомянутую в комментариях Маланди с Bangles.jpg (DHT ошибочно анализируется как SOF).

Другая проблема, упомянутая с 1431588037-WgsI3vK.jpg, связана с тем, что imghdr может обнаружить только заголовки APP0 (EXIF) и APP1 (JFIF).

Это можно исправить, добавив более слабый тест в imghdr (например, просто FFD8 или, может быть, FFD8FF?) Или что-то гораздо более сложное (возможно, даже проверку данных). При более сложном подходе я обнаружил проблемы только с: APP14 (FFEE) (Adobe); первый маркер - DQT (FFDB); и APP2 и проблемы со встроенными ICC_PROFILEs .

Пересмотренный код ниже также немного изменил вызов на 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

Примечание. Создан полный ответ вместо комментария, поскольку мне еще не разрешено.

1 голос
/ 02 ноября 2016

Нашел хорошее решение в другом посте Stackoverflow (используя только стандартные библиотеки + также с jpg): Ответ JohnTESlade

И еще одно решение (быстрый способ) дляте, кто может позволить себе запустить команду ' file ' в python, запустите:

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

Вывод :

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

Все, что вам нужно сделатьТеперь нужно отформатировать вывод для захвата размеров. 352x198 в моем случае.

1 голос
/ 30 января 2014

Этот код выполняет 2 вещи:

  • Получение размера изображения

  • Найти реальноеEOF из файла jpg

Хорошо, когда гуглил, меня больше интересовал последний.Задача состояла в том, чтобы вырезать файл jpg из потока данных.Поскольку я не нашел способа использовать «образ» Питона, чтобы получить EOF файла jpg, я сделал это.

Интересные вещи / изменения / примечания в этом примере:

  • расширение обычного класса файлов Python с помощью метода uInt16, делающего исходный код более читабельным и поддерживаемым.Из-за неуклюжести с struct.unpack () код выглядит некрасиво

  • Заменено на чтение "неинтересных" областей / чанков с поиском

  • Incaseвам просто нравится получать размеры, вы можете удалить строку:

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

    ->, поскольку это важно только при чтении фрагмента данных изображения и комментариях в

    #break
    

    , чтобы остановитьчтение, как только размер был найден.... но улыбается то, что я говорю - вы кодер;)

      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()
    
0 голосов
/ 02 сентября 2016

Наткнулся на этот, но вы можете получить его, используя следующее, если импортируете numpy.

import numpy as np

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

Это работает, потому что вы игнорируете все цвета, кроме одного, а затем изображение просто двухмерное, поэтому форма говорит вам, насколько она выгодна. Все еще новичок в Python, но кажется простым способом сделать это.

...