Python:从PNG中提取元数据

16

我能够使用R提取所需信息,但为了项目整体的一致性,我希望能够使用Python(最好是Python3)来完成。 我需要获取名为“设置”的单个标签的内容。 该标签包含XML,然后需要进行解析。

在R中获取元数据非常容易:

library(exifr)
library(XML)

path = file.path('path', 'to', 'file')

x = read_exif(file.path(path,'image.png'))
x$Settings

看起来Python无法做到这一点,这让我感到困惑。或者这需要我比目前更了解Python和PNG。我该如何使用Python提取PNG元数据?


以下是我尝试过的方法列表:

PyPng PyPNG似乎很有前途。通过检查每个块的长度,"Settings"标签似乎位于zTXt块中。

import png

filename = "C:\\path\\to\\image.png"

im = png.Reader(filename)

for c in im.chunks():
    print(c[0], len(c[1]))

>>>
IHDR 13
tIME 7
pHYs 9
IDAT 47775
zTXt 714
IEND 0

以上内容取自此文章。 然而,如何提取zTXt数据仍然不清楚。

hachoir3

使用hachoir3包,我尝试了以下操作:

from hachoir.parser import createParser
from hachoir.metadata import extractMetadata

filename = "C:\\path\\to\\file\\image.png"
parser = createParser(filename)
metadata = extractMetadata(parser)

for line in metadata.exportPlaintext():
    print(line)

这给了我以下结果:

Metadata:
- Image width: 1024 pixels
- Image height: 46 pixels
- Bits/pixel: 16
- Pixel format: RGB
- Compression rate: 2.0x
- Image DPI width: 1 DPI
- Image DPI height: 1 DPI
- Creation date: 2016-07-13 19:09:28
- Compression: deflate
- MIME type: image/png
- Endianness: Big endian

我似乎无法获取到我需要的字段,即R代码中引用的“设置”字段。我尝试了其他方法,如metadata.get,但都没有成功。据我所知,这似乎是解析PNG元数据的两个选项。文档中写道:

一些不错的(但并非完美的;-))解析器:

Matroska视频Microsoft RIFF(AVI视频、WAV音频、CDA文件)PNG图片TAR和ZIP归档

也许它只是没有我需要的功能?

Pillow

遵循此帖子中的建议:

from PIL import Image
filename = "C:\\path\\to\\file\\image.png"
im = Image.open(filename)

这段代码读入了图片,但是im.info只返回了{'aspect': (1, 1)}。通过阅读文档,似乎没有任何方法可以获取到元数据。我阅读了这篇帖子中提供的PNG描述。老实说,我不知道如何利用它的信息,也不知道Pillow如何帮助我。

有些帖子暗示我需要的可以做到,但它们并没有起作用。例如,这个帖子建议使用ExifTags库:

from PIL import Image, ExifTags
filename = "C:\\path\\to\\file\\image.png"
im = Image.open(filename)
exif = { ExifTags.TAGS[k]: v for k, v in im._getexif().items() if k in ExifTags.TAGS}

问题在于,AttributeError: 'PngImageFile'对象没有属性'_getexif'。根据文档._getexif特性是实验性的,并且仅适用于JPG。

通过整个Pillow文档的阅读,它似乎只涉及JPG和TIFF。处理PNG文件似乎根本不是该包的一部分。所以像hachoir一样,也许这是做不到的吗?

PIL

显然还有另一个名为PIL的包,Pillow就是从中分叉出来的。看起来它在2009年就被放弃了。


可能是 如何使用Python从图像中提取元数据? 的重复问题。 - briancaffey
PNG标签只有四个字符长,所以那些“设置”实际上不是标签。你是对的,看起来它是压缩数据。你有这样一个PNG文件的链接吗?如果有的话,我可能可以调整我的PyPNG代码来提取它。 - Jongware
好的,这里有一些坏消息。我发现一个由R创建的PNG图像中含有zTXt块,但其中只包含了该块中的配置文件,没有元数据。因此,我仍需要一个包含您设置的图像。 - Jongware
2个回答

14

通过访问已加载图像的信息字典,您可以使用Pillow获取EXIF元数据。

自Pillow 6.0起,可以从PNG图像中读取EXIF数据。然而,与其他图像格式不同,只有在调用load()函数后,才能保证EXIF数据存在于info中。https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#png

from PIL import Image

filename = 'path/to/img'
im = Image.open(filename)
im.load()  # Needed only for .png EXIF data (see citation above)
print(im.info['meta_to_read'])

我正在使用来自 conda 软件库的 Python 3.7 和 Pillow 7.1.2。


3
这里有一个不太优雅、有些笨拙但是可行的解决方案。
此代码改编自以下链接:https://motherboard.vice.com/en_us/article/aekn58/hack-this-extra-image-metadata-using-python
您可以在 Python 中调用命令行 exiftools 应用程序,然后解析结果。
以下是在 Ubuntu 16.04 下运行 Python 3.6.3 的代码:
import subprocess

result = subprocess.run(['exiftool', '-h', '/home/jason/Pictures/kitty_mask.png'], stdout=subprocess.PIPE)
print (type(result))
print ("\n\n",result.stdout)
normal_string = result.stdout.decode("utf-8")
print("\n\n", normal_string)

以下是我测试图像的结果:
> <class 'subprocess.CompletedProcess'>
> 
> 
>  b'<!-- /home/jason/Pictures/kitty_mask.png
> -->\n<table>\n<tr><td>ExifTool Version Number</td><td>10.80</td></tr>\n<tr><td>File
> Name</td><td>kitty_mask.png</td></tr>\n<tr><td>Directory</td><td>/home/jason/Pictures</td></tr>\n<tr><td>File
> Size</td><td>25 kB</td></tr>\n<tr><td>File Modification
> Date/Time</td><td>2018:07:02 09:35:00+01:00</td></tr>\n<tr><td>File
> Access Date/Time</td><td>2018:07:09
> 16:23:24+01:00</td></tr>\n<tr><td>File Inode Change
> Date/Time</td><td>2018:07:02 09:35:00+01:00</td></tr>\n<tr><td>File
> Permissions</td><td>rw-r--r--</td></tr>\n<tr><td>File
> Type</td><td>PNG</td></tr>\n<tr><td>File Type
> Extension</td><td>png</td></tr>\n<tr><td>MIME
> Type</td><td>image/png</td></tr>\n<tr><td>Image
> Width</td><td>2448</td></tr>\n<tr><td>Image
> Height</td><td>3264</td></tr>\n<tr><td>Bit
> Depth</td><td>8</td></tr>\n<tr><td>Color
> Type</td><td>RGB</td></tr>\n<tr><td>Compression</td><td>Deflate/Inflate</td></tr>\n<tr><td>Filter</td><td>Adaptive</td></tr>\n<tr><td>Interlace</td><td>Noninterlaced</td></tr>\n<tr><td>Image
> Size</td><td>2448x3264</td></tr>\n<tr><td>Megapixels</td><td>8.0</td></tr>\n</table>\n'
> 
> 
>  <!-- /home/jason/Pictures/kitty_mask.png --> <table> <tr><td>ExifTool
> Version Number</td><td>10.80</td></tr> <tr><td>File
> Name</td><td>kitty_mask.png</td></tr>
> <tr><td>Directory</td><td>/home/jason/Pictures</td></tr> <tr><td>File
> Size</td><td>25 kB</td></tr> <tr><td>File Modification
> Date/Time</td><td>2018:07:02 09:35:00+01:00</td></tr> <tr><td>File
> Access Date/Time</td><td>2018:07:09 16:23:24+01:00</td></tr>
> <tr><td>File Inode Change Date/Time</td><td>2018:07:02
> 09:35:00+01:00</td></tr> <tr><td>File
> Permissions</td><td>rw-r--r--</td></tr> <tr><td>File
> Type</td><td>PNG</td></tr> <tr><td>File Type
> Extension</td><td>png</td></tr> <tr><td>MIME
> Type</td><td>image/png</td></tr> <tr><td>Image
> Width</td><td>2448</td></tr> <tr><td>Image
> Height</td><td>3264</td></tr> <tr><td>Bit Depth</td><td>8</td></tr>
> <tr><td>Color Type</td><td>RGB</td></tr>
> <tr><td>Compression</td><td>Deflate/Inflate</td></tr>
> <tr><td>Filter</td><td>Adaptive</td></tr>
> <tr><td>Interlace</td><td>Noninterlaced</td></tr> <tr><td>Image
> Size</td><td>2448x3264</td></tr>
> <tr><td>Megapixels</td><td>8.0</td></tr> </table>

6
如果你要调用exiftool,我建议去掉“-h”选项。这将给你原始输出,因此你不需要解析HTML,只需要解析你所需的标签。 - StarGeek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接