我正在编写一段脚本来计算图像的MD5校验和,但需要排除EXIF标签。
为了准确地做到这一点,我需要知道文件中EXIF标签的位置(开头、中间、结尾),以便将其排除在外。
如何确定标签在文件中的位置?
我扫描的图像格式包括TIFF、JPG、PNG、BMP、DNG、CR2,还有一些视频格式如MOV、AVI和MPG。
我正在编写一段脚本来计算图像的MD5校验和,但需要排除EXIF标签。
为了准确地做到这一点,我需要知道文件中EXIF标签的位置(开头、中间、结尾),以便将其排除在外。
如何确定标签在文件中的位置?
我扫描的图像格式包括TIFF、JPG、PNG、BMP、DNG、CR2,还有一些视频格式如MOV、AVI和MPG。
使用Python Imaging Library提取图片数据要容易得多(在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'
在我的电脑上,对一张2500x1600像素的JPEG图像计算MD5校验和大约需要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从中提取帧,然后像上面展示的那样进行处理。
一种简单的方法是对核心图像数据进行哈希。对于PNG格式,您可以通过仅计算“关键块”(即以大写字母开头的块)来完成此操作。JPEG具有类似但更简单的文件结构。
ImageMagick中的视觉哈希在哈希时将图像解压缩。在您的情况下,您可以立即对压缩的图像数据进行哈希,因此(如果正确实现)它应该与哈希原始文件一样快。
这是一个小型Python脚本,说明了这个想法。它可能适用于您,也可能不适用,但至少应该说明我的意思 :)
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"))
您可以使用stream,它是ImageMagick套件的一部分:
$ stream -map rgb -storage-type short image.tif - | sha256sum
d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64 -
或者
$ sha256sum <(stream -map rgb -storage-type short image.tif -)
d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64 /dev/fd/63
rgb
,并使用short
的存储类型(如果RGB值是8位,则可以在此处使用char
)。identify
命令报告的相同的signature
哈希值:$ identify -verbose image.tif | grep signature
signature: d39463df1060efd4b5a755b09231dcbc3060e9b10c5ba5760c7dbcd441ddcd64
identify
报告的哈希与使用stream
获取的哈希不同,但后者可以由任何能够提取原始位图数据的工具(例如dcraw
对于某些图像类型)复制。)-map
和-storage-type
选项的方法吗?只给出typeOfHash
和文件就能得到哈希值。此外,正如你所注意到的,即使他们说在v7上也会更改identify
签名,但如何复制它仍不清楚。 - Pablo Bianchiexiftool -PhotometricInterpretation -BitsPerSample
),而对于JPEG,则需要使用-ColorMode
。关于ImageMagick哈希更改,这仅适用于identify
报告的值,而不适用于问题所示的stream
输出。我能够使用一些线性DNG文件上的dcraw
生成与stream
相同的哈希,因此我很高兴stream
输出可以使用另一个工具进行再现。 - starfryidentify -verbose
works as promised (a signature is the same for the same image with different metadata), but it is four times as slow compared to stream
- sanmaimogrify -strip blah.jpg
如果你这样做
identify -list format
它显然可以与所有引用的格式一起使用。