pil - python show image
Obtener tamaño de imagen SIN cargar imagen en la memoria (5)
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 la memoria. ¿Es eso posible? Solo estoy haciendo estadísticas sobre el tamaño de las imágenes y no me importan los contenidos de la imagen. Solo quiero hacer que mi procesamiento sea más rápido.
A menudo obtengo tamaños de imagen en Internet. Por supuesto, no puede descargar la imagen y luego cargarla para analizar la información. Es demasiado lento. Mi método es alimentar trozos a un contenedor de imágenes y probar si puede analizar la imagen cada vez. Detener el ciclo cuando obtengo la información que quiero.
Extraje el núcleo de mi código y lo modifiqué para analizar los 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
Como los comentarios aluden, PIL no carga la imagen en la memoria al llamar a .open
. Mirando los documentos de PIL 1.1.7
, el docstring para .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 docs dicen:
abrir (archivo, modo = "r")
Abre e identifica el archivo de imagen dado.
Esta es una operación floja; esta función identifica el archivo, pero los datos de la imagen real no se leen desde el archivo hasta que intente procesar los datos (o llame al método de carga ).
Profundizando más, vemos que .open
llamadas _open
que es una sobrecarga específica de formato de imagen. Cada una de las implementaciones de _open
se puede encontrar en un archivo nuevo, por ej. Los archivos .jpeg están en JpegImagePlugin.py
. Veamos eso en profundidad.
Aquí las cosas parecen ser un poco complicadas, 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")
Que parece que podría leer todo el archivo si estaba mal formado. Si lee el marcador de información OK, sin embargo, debería aparecer temprano. El handler
funciones finalmente configura self.size
que son las dimensiones de la imagen.
Esta answer tiene otra buena resolución, pero falta el formato pgm . Esta answer ha resuelto el pgm . Y agrego el bmp .
Códigos está debajo
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
Otra forma corta de hacerlo en sistemas Unix. Depende de la salida del file
que no estoy seguro de que esté estandarizado 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]))
Si no te importa el contenido de la imagen, es probable que PIL sea una exageración.
Sugiero analizar el resultado del módulo de magia 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'')
Este es un contenedor de libmagic que lee el menor número posible de bytes para identificar una firma de tipo de archivo.
[actualizar]
Hmmm, desafortunadamente, cuando se aplica a jpegs, lo anterior da "''datos de imagen JPEG, estándar EXIF 2.21'' ''''. ¡Sin tamaño de imagen! - Alex Flint
Parece que los jpegs son resistentes 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 que los que le gustan a libmagic para leer.
Me subí las mangas y llegué con este fragmento no probado (lo obtuve 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