Получить размер изображения БЕЗ загрузки изображения в память
Я понимаю, что вы можете получить размер изображения с помощью PIL следующим образом
from PIL import Image
im = Image.open(image_filename)
width, height = im.size
Однако я хотел бы получить ширину и высоту изображения без необходимости загружать изображение в память. Это возможно? Я только делаю статистику по размерам изображений и не забочусь о содержании изображения. Я просто хочу сделать свою обработку быстрее.
Ответы
Ответ 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]))