Получить размер изображения без загрузки изображения в память
Я понимаю, что вы можете получить размер изображения с помощью PIL следующим образом
from PIL import Image
im = Image.open(image_filename)
width, height = im.size
однако, я хотел бы получить ширину и высоту изображения без загрузки образа в память. Это возможно? Я только делаю статистику по размерам изображений и не забочусь о содержимом изображения. Я просто хочу сделать свою обработку быстрее.
6 ответов:
как намекают комментарии, 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
просто возвращает объект file и имя файла при успешном выполнении. Кроме того,docs говорят:открыть(файл, mode= "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
размеры изображения.
Если вы не заботитесь о содержимом изображения, PIL, вероятно, перебор.
Я предлагаю разбор вывода модуля python magic:
>>> 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, которая считывает как можно меньше байтов, чтобы идентифицировать подпись типа файла.
[обновление]
Хм, к сожалению, при нанесении изображения в формате JPEG, выше выдает "данных изображения JPEG, совместимый с EXIF 2.21 стандарт'". Нет размера изображения! – Алекс Флинт
похоже, что jpeg устойчивы к магии. : -)
Я могу понять, почему: чтобы получить размеры изображения для файлов JPEG, вам, возможно, придется прочитать больше байтов, чем любит читать libmagic.
засучил рукава и пришел с это очень непроверенный фрагмент (получить его из GitHub), что не требует никаких сторонних модулей.
#------------------------------------------------------------------------------- # 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('1PNG\r\n2\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('1PNG\r\n2\n'): # older PNGs? w, h = struct.unpack(">LL", data[8:16]) width = int(w) height = int(h) elif (size >= 2) and data.startswith('70'): # 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
Я часто получаю размеры изображений в Интернете. Конечно, вы не можете загрузить изображение, а затем загрузить его для анализа информации. Это отнимает слишком много времени. Мой метод состоит в том, чтобы подавать куски в контейнер изображения и проверять, может ли он анализировать изображение каждый раз. Остановите цикл, когда я получу информацию, которую я хочу.
я извлек ядро моего кода и изменил его для разбора локальных файлов.
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 байт, чтобы получить размер изображения. Надеюсь, это поможет.
еще один короткий способ сделать это в системах Unix. Это зависит от вывода
file
который я не уверен, стандартизирован на всех системах. Это, вероятно, не следует использовать в производственном коде. Кроме того, большинство JPEG не сообщают размер изображения.import subprocess, re image_size = list(map(int, re.findall('(\d+)x(\d+)', subprocess.getoutput("file " + filename))[-1]))
этой ответ имеет еще одно хорошее разрешение, но отсутствует 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
есть пакет на pypi называется
imagesize
что в настоящее время работает для меня, хотя это не похоже, что это очень активно.установка:
pip install imagesize
использование:
import imagesize width, height = imagesize.get("test.png") print(width, height)
Домашняя страница:https://github.com/shibukawa/imagesize_py