Подтвердить что ты не робот

Получить размер изображения БЕЗ загрузки изображения в память

Я понимаю, что вы можете получить размер изображения с помощью PIL следующим образом

from PIL import Image
im = Image.open(image_filename)
width, height = im.size

Однако я хотел бы получить ширину и высоту изображения без необходимости загружать изображение в память. Это возможно? Я только делаю статистику по размерам изображений и не забочусь о содержании изображения. Я просто хочу сделать свою обработку быстрее.

4b9b3361

Ответ 1

Как отмечают комментарии, PIL не загружает изображение в память при вызове .open. Глядя на документы PIL 1.1.7, docstring для .open говорит:

def open(fp, mode="r"):
    "Open an image file, without loading the raster data"

В источнике есть несколько операций с файлами, например:

 ...
 prefix = fp.read(16)
 ...
 fp.seek(0)
 ...

но они вряд ли составляют чтение всего файла. Фактически .open просто возвращает объект файла и имя файла при успешном завершении. Кроме того, docs говорят:

open (файл, режим = "r" )

Открывает и идентифицирует данный файл изображения.

Это ленивая операция; эта функция идентифицирует файл, но фактические данные изображения не считываются из файла до тех пор, пока вы не попытаетесь обработать данные (или вызовите метод load).

Копаем глубже, мы видим, что .open вызывает _open, который является специфической перегрузкой в ​​формате изображения. Каждая из реализаций _open может быть найдена в новом файле, например..jpeg находятся в JpegImagePlugin.py. Давайте посмотрим на это в глубину.

Здесь, кажется, немного сложно, в нем есть бесконечный цикл, который вырывается из того, когда найден маркер jpeg:

    while True:

        s = s + self.fp.read(1)
        i = i16(s)

        if i in MARKER:
            name, description, handler = MARKER[i]
            # print hex(i), name, description
            if handler is not None:
                handler(self, i)
            if i == 0xFFDA: # start of scan
                rawmode = self.mode
                if self.mode == "CMYK":
                    rawmode = "CMYK;I" # assume adobe conventions
                self.tile = [("jpeg", (0,0) + self.size, 0, (rawmode, ""))]
                # self.__offset = self.fp.tell()
                break
            s = self.fp.read(1)
        elif i == 0 or i == 65535:
            # padded marker or junk; move on
            s = "\xff"
        else:
            raise SyntaxError("no marker found")

Похоже, он мог прочитать весь файл, если он был искажен. Если он читает маркер информации OK, он должен выйти раньше. Функция handler в конечном итоге устанавливает self.size, которые являются размерами изображения.

Ответ 2

Если вы не заботитесь о содержимом изображения, PIL, вероятно, является излишним.

Я предлагаю разбор выходного кода магического модуля python:

>>> t = magic.from_file('teste.png')
>>> t
'PNG image data, 782 x 602, 8-bit/color RGBA, non-interlaced'
>>> re.search('(\d+) x (\d+)', t).groups()
('782', '602')

Это оболочка libmagic, которая читает как можно меньше байтов, чтобы идентифицировать подпись типа файла.

[обновление]

Хм, к сожалению, при применении к jpegs, приведенное выше дает "данные изображения JPEG, стандарт EXIF ​​2.21". Нет изображения! - Алекс Флинт

Кажется, что jpegs обладают маской.: -)

Я могу понять, почему: для получения размеров изображения для файлов JPEG вам может потребоваться прочитать больше байтов, чем нравится libmagic.

Свернул мои рукава и пришел с этот очень непроверенный фрагмент (получить его от GitHub), который не требует сторонних модулей.

Look, Ma! No deps!

#-------------------------------------------------------------------------------
# Name:        get_image_size
# Purpose:     extract image dimensions given a file path using just
#              core modules
#
# Author:      Paulo Scardine (based on code from Emmanuel VAÏSSE)
#
# Created:     26/09/2013
# Copyright:   (c) Paulo Scardine 2013
# Licence:     MIT
#-------------------------------------------------------------------------------
#!/usr/bin/env python
import os
import struct

class UnknownImageFormat(Exception):
    pass

def get_image_size(file_path):
    """
    Return (width, height) for a given img file content - no external
    dependencies except the os and struct modules from core
    """
    size = os.path.getsize(file_path)

    with open(file_path) as input:
        height = -1
        width = -1
        data = input.read(25)

        if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
            # GIFs
            w, h = struct.unpack("<HH", data[6:10])
            width = int(w)
            height = int(h)
        elif ((size >= 24) and data.startswith('\211PNG\r\n\032\n')
              and (data[12:16] == 'IHDR')):
            # PNGs
            w, h = struct.unpack(">LL", data[16:24])
            width = int(w)
            height = int(h)
        elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'):
            # older PNGs?
            w, h = struct.unpack(">LL", data[8:16])
            width = int(w)
            height = int(h)
        elif (size >= 2) and data.startswith('\377\330'):
            # JPEG
            msg = " raised while trying to decode as JPEG."
            input.seek(0)
            input.read(2)
            b = input.read(1)
            try:
                while (b and ord(b) != 0xDA):
                    while (ord(b) != 0xFF): b = input.read(1)
                    while (ord(b) == 0xFF): b = input.read(1)
                    if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
                        input.read(3)
                        h, w = struct.unpack(">HH", input.read(4))
                        break
                    else:
                        input.read(int(struct.unpack(">H", input.read(2))[0])-2)
                    b = input.read(1)
                width = int(w)
                height = int(h)
            except struct.error:
                raise UnknownImageFormat("StructError" + msg)
            except ValueError:
                raise UnknownImageFormat("ValueError" + msg)
            except Exception as e:
                raise UnknownImageFormat(e.__class__.__name__ + msg)
        else:
            raise UnknownImageFormat(
                "Sorry, don't know how to get information from this file."
            )

    return width, height

Ответ 3

Я часто получаю размеры изображений в Интернете. Конечно, вы не можете загрузить изображение, а затем загрузить его для анализа информации. Это слишком много времени. Мой метод заключается в том, чтобы подавать куски в контейнер изображений и проверять, может ли он каждый раз анализировать изображение. Остановите цикл, когда я получу нужную информацию.

Я извлек ядро ​​моего кода и модифицировал его для анализа локальных файлов.

from PIL import ImageFile

ImPar=ImageFile.Parser()
with open(r"D:\testpic\test.jpg", "rb") as f:
    ImPar=ImageFile.Parser()
    chunk = f.read(2048)
    count=2048
    while chunk != "":
        ImPar.feed(chunk)
        if ImPar.image:
            break
        chunk = f.read(2048)
        count+=2048
    print(ImPar.image.size)
    print(count)

Вывод:

(2240, 1488)
38912

Фактический размер файла составляет 1543 580 байт, и вы получите только 38 912 байтов, чтобы получить размер изображения. Надеюсь, это поможет.

Ответ 4

Этот ответ имеет другое хорошее разрешение, но отсутствует формат pgm. Этот ответ разрешил pgm. И я добавляю bmp.

Коды ниже

import struct, imghdr, re, magic

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(32)
        if len(head) != 32:
            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
        elif imghdr.what(fname) == 'pgm':
            header, width, height, maxval = re.search(
                b"(^P5\s(?:\s*#.*[\r\n])*"
                b"(\d+)\s(?:\s*#.*[\r\n])*"
                b"(\d+)\s(?:\s*#.*[\r\n])*"
                b"(\d+)\s(?:\s*#.*[\r\n]\s)*)", head).groups()
            width = int(width)
            height = int(height)
        elif imghdr.what(fname) == 'bmp':
            _, width, height, depth = re.search(
                b"((\d+)\sx\s"
                b"(\d+)\sx\s"
                b"(\d+))", str).groups()
            width = int(width)
            height = int(height)
        else:
            return
        return width, height

Ответ 5

Еще один короткий способ сделать это на Unix-системах. Это зависит от вывода file, который я не уверен, стандартизирован для всех систем. Вероятно, это не должно быть использовано в производственном коде. Более того, большинство JPEG не сообщают размер изображения.

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