Вычислять хеш только данных основного изображения (исключая метаданные) для изображения
Я пишу script для вычисления суммы MD5 изображения, за исключением тега EXIF.
Чтобы сделать это точно, мне нужно знать, где находится тег EXIF в файле (начало, посередине, конец), чтобы я мог его исключить.
Как определить, где в файле находится тег?
Изображения, которые я просматриваю, представлены в формате TIFF, JPG, PNG, BMP, DNG, CR2, NEF и некоторых видеороликах MOV, AVI и MPG.
Ответы
Ответ 1
Один простой способ сделать это - хэш-данные основного изображения. Для PNG вы можете сделать это, посчитав только "критические куски" (т.е. Те, которые начинаются с прописных букв). JPEG имеет аналогичную, но более простую структуру файлов.
Визуальный хэш в ImageMagick распаковывает изображение по мере его хэширования. В вашем случае вы можете сразу удалить данные сжатого изображения, поэтому (если они будут выполнены правильно) a должно быть так же быстро, как хеширование необработанного файла.
Это небольшой Python script, иллюстрирующий эту идею. Он может работать или не работать для вас, но он должен хотя бы дать указание на то, что я имею в виду:)
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"))
Ответ 2
Намного проще использовать библиотеку изображений Python для извлечения данных изображений (пример в iPython):
In [1]: import Image
In [2]: import hashlib
In [3]: im = Image.open('foo.jpg')
In [4]: hashlib.md5(im.tobytes()).hexdigest()
Out[4]: '171e2774b2549bbe0e18ed6dcafd04d5'
Это работает на любом типе изображения, которое может обрабатывать PIL. Метод tobytes
возвращает строку, содержащую данные пикселей.
Кстати, хэш MD5 теперь выглядит довольно слабым. Лучше использовать SHA512:
In [6]: hashlib.sha512(im.tobytes()).hexdigest()
Out[6]: '6361f4a2722f221b277f81af508c9c1d0385d293a12958e2c56a57edf03da16f4e5b715582feef3db31200db67146a4b52ec3a8c445decfc2759975a98969c34'
На моей машине вычисление контрольной суммы MD5 для JPEG 2500x1600 занимает около 0,07 секунды. Использование SHA512 занимает 0,10 секунды. Полный пример:
#!/usr/bin/env python3
from PIL import Image
import hashlib
import sys
im = Image.open(sys.argv[1])
print(hashlib.sha512(im.tobytes()).hexdigest(), end="")
Для фильмов вы можете извлечь кадры из них, например, с помощью ffmpeg, а затем обработать их, как показано выше.
Ответ 3
Я бы использовал стриппер метаданных для предварительной обработки вашего хеширования:
Из пакета ImageMagick у вас есть...
mogrify -strip blah.jpg
и если вы делаете
identify -list format
он, по-видимому, работает со всеми указанными форматами.
Ответ 4
Вы можете использовать поток, который является частью пакета ImageMagick:
$ stream -map rgb -storage-type short image.tif - | sha256sum
d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64 -
или же
$ sha256sum <(stream -map rgb -storage-type short image.tif -)
d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64 /dev/fd/63
Этот пример для файла TIFF, который является RGB с 16 битами на выборку (то есть 48 бит на пиксель). Поэтому я использую map для rgb
и short
тип хранения (вы можете использовать char
здесь, если значения RGB 8-битные).
Этот метод сообщает тот же хэш signature
что подробный Imagemagick identify
отчеты о командах:
$ identify -verbose image.tif | grep signature
signature: d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64
(для ImageMagick v6.x; хэш, сообщаемый при identify
в версии 7, отличается от хэша, полученного с использованием stream
, но последний может быть воспроизведен любым инструментом, способным извлекать необработанные данные растрового изображения - например, dcraw
для некоторых типов изображений.)