python - exiftool - Calcular el hash de solo los datos de la imagen central(excluyendo los metadatos) para una imagen
exiftool python (4)
Estoy escribiendo un script para calcular la suma MD5 de una imagen excluyendo la etiqueta EXIF.
Para hacer esto con precisión, necesito saber dónde se encuentra la etiqueta EXIF en el archivo (inicio, medio, fin) para poder excluirla.
¿Cómo puedo determinar dónde está ubicada la etiqueta en el archivo?
Las imágenes que estoy escaneando están en el formato TIFF, JPG, PNG, BMP, DNG, CR2, NEF y algunos videos MOV, AVI y MPG.
Es mucho más fácil usar la biblioteca de imágenes de Python para extraer los datos de la imagen (ejemplo en iPython):
In [1]: import Image
In [2]: import hashlib
In [3]: im = Image.open(''foo.jpg'')
In [4]: hashlib.md5(im.tostring()).hexdigest()
Out[4]: ''171e2774b2549bbe0e18ed6dcafd04d5''
Esto funciona en cualquier tipo de imagen que PIL pueda manejar. El método tostring
devuelve una cadena que contiene los datos de píxeles.
Por cierto, el hash MD5 ahora se ve como bastante débil. Mejor usar SHA512:
In [6]: hashlib.sha512(im.tostring()).hexdigest()
Out[6]: ''6361f4a2722f221b277f81af508c9c1d0385d293a12958e2c56a57edf03da16f4e5b715582feef3db31200db67146a4b52ec3a8c445decfc2759975a98969c34''
En mi máquina, el cálculo de la suma de comprobación MD5 para un JPEG de 2500x1600 toma alrededor de 0.07 segundos. Usando SHA512, toma 0,10 segundos.
Para películas, puede extraer cuadros de ellos con, por ejemplo, ffmpeg , y luego procesarlos como se muestra arriba.
Puede utilizar la stream que es parte de la suite ImageMagick :
$ stream -map rgb -storage-type short image.tif - | sha256sum
d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64 -
o
$ sha256sum <(stream -map rgb -storage-type short image.tif -)
d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64 /dev/fd/63
Este ejemplo es para un archivo TIFF que es RGB con 16 bits por muestra (es decir, 48 bits por píxel). Así que uso map to rgb
y un storage-type short
(puede usar char
aquí si los valores RGB son de 8 bits).
Este método reporta el mismo hash de signature
que los informes de comando detallados de Imagemagick identify
:
$ identify -verbose image.tif | grep signature
signature: d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64
(para ImageMagick v6.x; el hash reportado por identify
en la versión 7 es different al obtenido usando stream
, pero esta última puede ser reproducida por cualquier herramienta capaz de extraer los datos de mapa de bits en bruto, como dcraw
para algunos tipos de imágenes).
Una forma sencilla de hacerlo es hacer un hash de los datos de la imagen central. Para PNG, puede hacer esto contando solo los "fragmentos críticos" (es decir, los que comienzan con letras mayúsculas). JPEG tiene una estructura de archivos similar pero más simple.
El hash visual en ImageMagick descomprime la imagen a medida que lo hace. En su caso, podría codificar los datos de la imagen comprimida de inmediato, por lo que (si se implementa correctamente) debería ser tan rápido como el hash del archivo en bruto.
Este es un pequeño script de Python que ilustra la idea. Puede o no funcionar para usted, pero al menos debería dar una indicación de lo que quiero decir :)
import struct
import os
import hashlib
def png(fh):
hash = hashlib.md5()
assert fh.read(8)[1:4] == "PNG"
while True:
try:
length, = struct.unpack(">i",fh.read(4))
except struct.error:
break
if fh.read(4) == "IDAT":
hash.update(fh.read(length))
fh.read(4) # CRC
else:
fh.seek(length+4,os.SEEK_CUR)
print "Hash: %r" % hash.digest()
def jpeg(fh):
hash = hashlib.md5()
assert fh.read(2) == "/xff/xd8"
while True:
marker,length = struct.unpack(">2H", fh.read(4))
assert marker & 0xff00 == 0xff00
if marker == 0xFFDA: # Start of stream
hash.update(fh.read())
break
else:
fh.seek(length-2, os.SEEK_CUR)
print "Hash: %r" % hash.digest()
if __name__ == ''__main__'':
png(file("sample.png"))
jpeg(file("sample.jpg"))
Yo usaría un separador de metadatos para preprocesar su hashing:
Desde el paquete ImageMagick tienes ...
mogrify -strip blah.jpg
y si lo haces
identify -list format
Aparentemente funciona con todos los formatos citados.