Python rawkit如何从RAW文件中读取元数据值?

Python rawkit how read metadata values from RAW file?

我正在编写 python 脚本,我需要从原始照片文件(例如 .CR2)中获取 exif 信息。

我发现 Python Rawkit 提供了这样做的能力。

with Raw(filename=image_path) as raw:
  print raw.metadata

Metadata(aperture=-1.2095638073643314e+38, timestamp=4273602232L,
         shutter=-1.1962713245823862e+38, flash=True, 
         focal_length=-1.2228562901462766e+38, height=3753, 
         iso=-1.182978841800441e+38,
         make='Canon', model='EOS 5D Mark II', 
         orientation=0, width=5634)

但我有点困惑,如何读取这些值?。例如,我期待 iso 值像 100/200/400 但什么是 -1.182978841800441e+38 ?

我的问题不是针对 iso,它也针对快门、光圈...

我检查了 libraw 和 rawkit 文档,但无法找到如何读取/转换这种值。

文档中的这部分不是很详细:

float iso_speed;
ISO sensitivity.

float shutter;
Shutter speed.

谁能帮我理解如何读取这些值?

谢谢

[更新]

按照neo的建议,我会使用ExifRead。事实上这是一个更好的选择,我正在编写一个 python 脚本。使用 ExifRead 不需要额外的 C 库依赖。

我能够打开佳能原始文件并解析 Exif,但不幸的是遇到了错误的光圈值:

EXIF ApertureValue (Ratio): 3
# My photo was taken in 2.8 (maybe a rounded value on this flag ?)

快速回答 : 使用 Fnumber 标志

EXIF FNumber (Ratio): 14/5 
14/5 is in fact 2.8 (do the math)

长答案(我是如何发现/调试的):

阅读这篇精彩的文章 link 了解佳能 RAW .CR2 文件中存储的内容,如何以及为什么 (http://lclevy.free.fr/cr2/) 我决定自己解码并了解发生了什么。

这 link 送我 graal 解码原始文件 cr2_poster.pdf 从那以后,我认为最好的价值似乎在我的佳能特定 MakerNote 部分的 FNumber 值上。 (所有值的描述都在这里canon_tags

Tag Id : 3 (In fact 0x0003 that you write 0x3) 
Name : FNumber

我用 Hexa 编辑器 (hexedit) 打开我的文件......我完全迷失了。

关键事项:

这样发现 MakeNote 部分很简单。

快速的方法是直接搜索包含 MakerNote 部分地址的 0x927c MarkerNote(因此在文件 7C 92 中)标志。 如果您无法找到它,请通过 IFD 部分找到 EXIF subsection。然后在该小节中,您将找到 MakerNote 部分

Tag     Type   Count        Value
7C 92   07 00  B8 A0 00 00  84 03 00 00

偏移量:84 03 00 00 -> 00 00 03 840x384 地址)

转到此地址并在 MakerNote 部分搜索 FNumber 0x3

Tag     Type   Count        Value
03 00   03 00  04 00 00 00  C8 05  00 00

去偏移量0x5C8找到我们的值(count 4 x type 3 ushort, 16 bits)

0x0x5C8 : 00 00 00 00  00 00 00 00

而且...失败了,事实上我的佳能没有填写这部分。

正在阅读 http://www.exiv2.org/tags.html FNumber 可以在 EXIF 小节中找到。

执行相同的过程以找到 EXIF 子部分和标签“0x829d Exif.Image.FNumber type 5 Rational” 有理数类型由 64 位组成(分子和分母 ulongs)Rational_data_type

Tag     Type   Count        Value
9D 82   05 00  01 00 00 00  34 03 00 00

然后读取0x334偏移量

1C 00 00 00  0A 00 00 00

正如我们在 Hexa 中看到的那样:0x1C / 0XA 用十进制计算:28/10 = 14/5 = 2.8

验证我在 ExifRead 中有这个值

EXIF.py 100EOS5D/IMG_8813.CR2 -vv | grep -i 14/5
EXIF FNumber (Ratio): 14/5

瞧!

我正在寻找 2.8 浮点数,这个值以分数格式存储。所以图书馆不做数学运算,只简化分数。

这就是为什么我们有 14/5 而不是预期的 2.8

我建议您使用专注于 EXIF 读取的库。 libraw/rawkit 中可用的东西真的只是一个不错的额外功能。我可以推荐 ExifRead 库。它是纯粹的 Python,而且速度也非常快。它让你更好地理解价值观。

如果与多种格式的兼容性比性能对您来说更重要,您可以调用 exiftool 作为带有 -j 选项的子进程,为您提供一个 json 字符串,您可以将其转换为字典。

这应该让您为大多数原始格式甚至根本不是图像的东西做好准备。它将从文件中挤出所有最后一点 exif 信息。然而,与其他选项相比,它相当缓慢(比如慢 200 倍):

from PIL import Image
import PIL.ExifTags
import subprocess
import json
import datetime
import exifread
filePath = "someImage.jpg"
filePath = "someRawImage.CR2"
filePath = "someMovie.mov"
filePath = "somePhotoshopImage.psd"


try:
    start = datetime.datetime.now()
    img = Image.open(filePath)
    exif_0 = {
        PIL.ExifTags.TAGS[k]: v
        for k, v in img.getexif().items()
        if k in PIL.ExifTags.TAGS
        }
    end = datetime.datetime.now()

    print("Pillow time:")
    print(end-start)
    print(str(len(exif_0)), "tags retrieved")
    print (exif_0, "\n")
except:
    pass

try:
    start = datetime.datetime.now()
    exif_1 = json.loads(subprocess.run(["/usr/local/bin/exiftool", "-j", filePath], stdout=subprocess.PIPE).stdout.decode("utf-8"))
    end = datetime.datetime.now()

    print("subprocess time:")
    print(end-start)
    print(str(len(exif_1[0])), "tags retrieved")
    print(exif_1, "\n")
except:
    pass

try:
    start = datetime.datetime.now()
    f = open(filePath, "rb")
    exif_2 = exifread.process_file(f)
    end = datetime.datetime.now()

    print("Exifread time:")
    print(end-start)
    print(str(len(exif_2)), "tags retrieved")
    print(exif_2, "\n")
except:
    pass