it-swarm.com.ru

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

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

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

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

87
Sami A. Haija

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

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

В источнике есть несколько файловых операций:

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

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

открыть (файл, режим = ”r”)

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

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

Если копнуть глубже, мы увидим, что .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")

Что выглядит так может прочитать весь файл, если он был искажен. Однако, если он читает инфо-маркер в порядке, это должно произойти рано. Функция handler в конечном итоге устанавливает self.size, которые являются размерами изображения.

53
Hooked

Если вас не волнует содержание изображения, 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, которая считывает как можно меньше байтов, чтобы идентифицировать сигнатуру типа файла.

Соответствующая версия скрипта:

https://raw.githubusercontent.com/scardine/image_size/master/get_image_size.py

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

Хм, к сожалению, применительно к 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__.__+ msg)
        else:
            raise UnknownImageFormat(
                "Sorry, don't know how to get information from this file."
            )

    return width, height
76
Paulo Scardine

На pypi есть пакет под названием imagesize, который в настоящее время работает для меня, хотя он не выглядит очень активным.

Установка:

pip install imagesize

Использование:

import imagesize

width, height = imagesize.get("test.png")
print(width, height)

Домашняя страница: https://github.com/shibukawa/imagesize_py

PyPi: https://pypi.org/project/imagesize/

6
Jonathan

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

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

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

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

5
user2923419

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

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

Это ответ имеет другое хорошее разрешение, но отсутствует формат 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
0
Yantao Xie