scikit pil from book python image image-processing

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