Obtener el tamaño de la imagen SIN cargar la imagen en la memoria
Entiendo que puede obtener el tamaño de la imagen usando PIL de la siguiente manera
from PIL import Image
im = Image.open(image_filename)
width, height = im.size
Sin embargo, me gustaría obtener el ancho y alto de la imagen sin tener que cargar la imagen en memoria. Es eso posible? Solo estoy haciendo estadísticas sobre tamaños de imagen y no me importa el contenido de la imagen. Sólo quiero que mi procesamiento sea más rápido.
6 answers
Como aluden los comentarios, PIL no carga la imagen en memoria al llamar a .open
. Mirando los documentos de PIL 1.1.7
, la cadena de documentos de .open
dice:
def open(fp, mode="r"):
"Open an image file, without loading the raster data"
Hay algunas operaciones de archivo en la fuente como:
...
prefix = fp.read(16)
...
fp.seek(0)
...
Pero estos difícilmente constituyen la lectura de todo el archivo. De hecho, .open
simplemente devuelve un objeto de archivo y el nombre de archivo en caso de éxito. Además, los documentos dicen:
Open (file, mode="r")
Abre e identifica la archivo de imagen.
Esta es una operación perezosa; esta función identifica el archivo, pero los datos reales de la imagen no se leen desde el archivo hasta que intente procesar los datos (o llame al método load).
Cavando más profundo, vemos que .open
llama a _open
que es una sobrecarga específica del formato de imagen. Cada una de las implementaciones de _open
se puede encontrar en un nuevo archivo, por ejemplo. .los archivos jpeg están en JpegImagePlugin.py
. Veamos eso en profundidad.
Aquí las cosas parecen ponerse un poco complicado, en él hay un bucle infinito que se rompe cuando se encuentra el marcador 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")
Como puede leer todo el archivo si era incorrecto. Sin embargo, si lee el marcador de información OK, debería salir temprano. La función handler
finalmente establece self.size
cuáles son las dimensiones de la imagen.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-26 18:14:17
Si no te importa el contenido de la imagen, PIL es probablemente un exceso.
Sugiero analizar la salida del módulo 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')
Esta es una envoltura alrededor de libmagic que lee tan pocos bytes como sea posible para identificar una firma de tipo de archivo.
[actualización]
Hmmm, desafortunadamente, cuando se aplica a jpegs, lo anterior da "'JPEG image data, EXIF standard 2.21'". Sin tamaño de imagen! - Alex Flint
Parece que los jpeg son resistente a la magia. :-)
Puedo ver por qué: para obtener las dimensiones de la imagen para los archivos JPEG, es posible que tenga que leer más bytes de los que a libmagic le gusta leer.
Me subí la manga y vino con este fragmento muy no probado (consíguelo de GitHub) que no requiere módulos de terceros.
#-------------------------------------------------------------------------------
# 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-07 01:26:52
A menudo busco tamaños de imagen en Internet. Por supuesto, no puede descargar la imagen y luego cargarla para analizar la información. Lleva demasiado tiempo. Mi método es alimentar trozos a un contenedor de imágenes y probar si puede analizar la imagen cada vez. Detén el bucle cuando tenga la información que quiero.
Extraje el núcleo de mi código y lo modifiqué para analizar archivos locales.
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)
Salida:
(2240, 1488)
38912
El tamaño real del archivo es de 1,543,580 bytes y solo lee 38.912 bytes para obtener el tamaño de la imagen. Espero que esto ayude.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-04 19:11:18
Otra forma corta de hacerlo en sistemas Unix. Depende de la salida de file
que no estoy seguro está estandarizada en todos los sistemas. Esto probablemente no debería usarse en el código de producción. Además, la mayoría de los archivos JPEG no informan el tamaño de la imagen.
import subprocess, re
image_size = list(map(int, re.findall('(\d+)x(\d+)', subprocess.getoutput("file " + filename))[-1]))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-13 09:57:51
Esta respuesta tiene otra buena resolución, pero falta el formato pgm. Este respuesta ha resuelto el pgm. Y añado el bmp .
Los códigos están debajo de
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:14
Hay un paquete en pypi llamado imagesize
que actualmente funciona para mí, aunque no parece que esté muy activo.
Instalar:
pip install imagesize
Uso:
import imagesize
width, height = imagesize.get("test.png")
print(width, height)
Página de inicio: https://github.com/shibukawa/imagesize_py
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-13 20:18:58