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

<p>я понимаю, что вы можете получить размер изображения с помощью pil следующим образом</p><pre><code>from pil import image im = image.open(image\_filename) width, height = im.size </code></pre><p>тем не менее, я хотел бы получить ширину и высоту изображения <em>без</em> необходимость загрузки изображения в память. это возможно? я только делаю статистику по размерам изображения и не заботюсь о содержании изображения. я просто хочу ускорить процесс обработки.</p>

<p>как отмечают комментарии, pil не загружает изображение в память при вызове <code>.open</code>. глядя на документы <code>pil 1.1.7</code>, строка документа для <code>.open</code> говорит:</p><pre><code>def open(fp, mode="r"): "open an image file, without loading the raster data" </code></pre><p>в источнике есть несколько файловых операций:</p><pre><code> ... prefix = fp.read(16) ... fp.seek(0) ... </code></pre><p>но они вряд ли составляют чтение всего файла. на самом деле <code>.open</code> просто возвращает объект файла и имя файла в случае успеха. кроме того, <a href="https://pillow.readthedocs.org/en/1.7.8/pythondoc-pil.image.html">документы</a> говорят:</p><blockquote> <p>открыть (файл, режим = ”r”)</p> <p>открывает и идентифицирует данный файл изображения.</p> <p>это ленивая операция; эта функция идентифицирует файл, но фактические данные изображения не считываются из файла, пока вы не попытаетесь обработать данные (или не вызовете <strong> метод загрузки </strong>).</p> </blockquote><p>если копнуть глубже, мы увидим, что <code>.open</code> вызывает <code>\_open</code>, что является перегрузкой, специфичной для формата изображения. каждую из реализаций <code>\_open</code> можно найти в новом файле, например. файлы .jpeg находятся в <code>jpegimageplugin.py</code>. давайте посмотрим на это подробно.</p><p>здесь все кажется немного сложным, в нем есть бесконечный цикл, который прерывается при обнаружении маркера jpeg:</p><pre><code> 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") </code></pre><p>что выглядит так <em> может </em> прочитать весь файл, если он был искажен. однако, если он читает инфо-маркер в порядке, это должно произойти рано. функция <code>handler</code> в конечном итоге устанавливает <code>self.size</code>, которые являются размерами изображения.</p>

<p>если вас не волнует содержание изображения, pil, вероятно, является излишним.</p><p>я предлагаю проанализировать вывод магического модуля python:</p><pre><code>>>> 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') </code></pre><p>это обертка вокруг libmagic, которая считывает как можно меньше байтов, чтобы идентифицировать сигнатуру типа файла.</p><p>соответствующая версия скрипта:</p><p> <a href="https://raw.githubusercontent.com/scardine/image\_size/master/get\_image\_size.py">https://raw.githubusercontent.com/scardine/image\_size/master/get\_image\_size.py</a> </p><p>[обновление]</p><blockquote> <p>хм, к сожалению, применительно к jpegs, вышеприведенное дает "данные изображения jpeg, стандарт exif 2.21". нет размера изображения! - алекс флинт</p> </blockquote><p>похоже, jpegs устойчивы к магии. :-)</p><p>я понимаю, почему: чтобы получить размеры изображений для файлов jpeg, вам, возможно, придется прочитать больше байтов, чем любит читать libmagic.</p><p>закатал мои рукава и пришел с <a href="https://github.com/scardine/image\_size">этот очень непроверенный фрагмент (получите его от github)</a> , который не требует сторонних модулей.</p><p><img src="https://i.stack.imgur.com/dfowd.png" alt="look, ma! no deps!"></p><pre><code>#------------------------------------------------------------------------------- # 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 </code></pre>

<p>на pypi есть пакет под названием <code>imagesize</code>, который в настоящее время работает для меня, хотя он не выглядит очень активным.</p><p>установка:</p><pre><code>pip install imagesize </code></pre><p>использование:</p><pre><code>import imagesize width, height = imagesize.get("test.png") print(width, height) </code></pre><p>домашняя страница: <a href="https://github.com/shibukawa/imagesize\_py">https://github.com/shibukawa/imagesize\_py</a> </p><p>pypi: <a href="https://pypi.org/project/imagesize/">https://pypi.org/project/imagesize/</a> </p>

<p>я часто выбираю размеры изображений в интернете. конечно, вы не можете загрузить изображение, а затем загрузить его для анализа информации. это слишком много времени. мой метод состоит в том, чтобы передать куски в контейнер изображения и проверить, может ли он анализировать изображение каждый раз. остановите цикл, когда я получу нужную информацию.</p><p>я извлек ядро своего кода и изменил его для анализа локальных файлов.</p><pre><code>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) </code></pre><p>результат:</p><pre><code>(2240, 1488) 38912 </code></pre><p>фактический размер файла составляет 1 543 580 байт, и вы только прочитали 38 912 байт, чтобы получить размер изображения. надеюсь, это поможет.</p>

<p>еще один короткий способ сделать это в системах unix. это зависит от вывода <code>file</code>, который, я не уверен, стандартизирован во всех системах. это, вероятно, не следует использовать в производственном коде. более того, большинство jpeg-файлов не сообщают размер изображения.</p><pre><code>import subprocess, re image\_size = list(map(int, re.findall('(\d+)x(\d+)', subprocess.getoutput("file " + filename))[-1])) </code></pre>

<p>это <a href="https://stackoverflow.com/a/20380514/153487">ответ</a> имеет другое хорошее разрешение, но отсутствует формат <em> pgm </em>. этот <a href="https://stackoverflow.com/a/7369986/153487">ответ</a> разрешил <em> пгм </em>. и я добавляю <em> bmp </em>.</p><p>коды ниже</p><pre><code>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 </code></pre>