Получить размер изображения без загрузки изображения в память
Я понимаю, что вы можете получить размер изображения с помощью 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
