PNG图像chunk概述&Python获取PNG图片的offset

时间:2024-03-13 15:59:52

通过本篇文章可以了解到如下知识点:

PNG图片编码; Python图片库; ImageMagick命令行工具简单使用

背景

公司产品同事反馈有个商品的缩略图都不正常,缩略之后核心内容没有位于图片的正中间,导致图片不知所云,具体表现如下

问题现场

原图:PNG图像chunk概述&Python获取PNG图片的offset
缩略图:PNG图像chunk概述&Python获取PNG图片的offset
这个缩略图正确的缩略结果应该是缩略出一张100x100且裁剪位置位于位于图片的正中间,这样就能保证缩略图也能基本展示商品,但是这个缩略结果明显就是不对的。那么问题出现在哪里呢?

定位问题

这里推荐一个工具ImageMagick的命令行工具集 magickdoc,这里我们使用其中的identify工具查看原图的详细信息

identify -verbose https://s3.mogucdn.com/mlcdn/55cf19/190127_01fi6dbl0ea2eib450g46ckga1igh_640x960.png

结果如下:
其他结果都不重要,主要看Properties段
PNG图像chunk概述&Python获取PNG图片的offset
我们可以看到这张原图在x轴上向右偏移了302个像素,在y轴上向上便宜了276个单位(正数代表向右向上,负数代表向左向下),正是由于这个x_off和y_off导致了缩略图的异常

解决方案

问题定位之后,那么解决方案就是对于一张PNG图片,先检测其是否有offset,再去纠正这个offset。这里给出Python的解决方案,先介绍一下Python常用的几个图片库:

PIL(Python Imaging Library)doc
png(pypng)doc
cv2(opencv)doc
imagemagick(文档较少,不过可以直接看源码用)

PIL: 基本上算是Python的图像处理标准库了。功能强大,API简单易用。
png: png图像进行解码和反解码的,主要是用来读图片信息
cv2: opencv是一个比较通用的库,主要是用来修改图片的,比如说裁剪,缩略,修改颜色等等
imagemagick: 是对imagemagick C语言库的封装,imagemagick的C语言库功能十分强大,专业。腾讯云的万象优图就是使用imagemagick的C库进行缩略。感兴趣的可以了解下,不过学习难度较大。

检测offset

检测时我们选用pypng这个库来对PNG图片解码,这里先大概讲解下PNG图片的编码结构,PNG图片整体的编码结构详细内容可以在这里看。
PNG图片编码内容分成了很多块(chunk),不同chunk描述了PNG图像的不同属性,大致如下:
核心chunk

块名 作用
IHDR 描述了图像的尺寸,色彩模式(sRGB等)(bit depth等,这些有点过于深入了,展开来讲又是一片文章)
PLTE 调色板数据块
IDAT 图像数据块,决定图像内容
IEND 图像结束块

辅助chunk

块名 作用
cHRM 基色和白色点数据块
gAMA 图像γ数据块

扩展chunk

块名 作用
oFFs 标记偏移
其它

主要关注下oFFs这个chunk
PNG图像chunk概述&Python获取PNG图片的offset
上面是官方文档里对oFFs的描述,根据文档的描述,oFFs是当一张大图被分割为多个小图时,oFFs可以标记每个小图的绝对位置(absolute position)。例如当我们需要将一张图片在很大的海报上打印出来时,并且通常是和pHYs chunk配合使用。
所以我们首先可以根据一个PNG是否包含oFFs段来判断是PNG图像是否有偏移,其次通过文档可以知道oFFs端中前4个字节存储了x_off的值,接下来4个字节存储了y_off的值,最后1个字节存储了一个叫做Unit specifier的值。那么只需将前8个字节取出来就可以知道这个图片的x_offy_off值分别是什么了。下面上代码!!

import png
im = png.Reader(img_file_path)

    for c in im.chunks():
    	# 判断是否存在oFFs chunk
        if c[0] == "oFFs":
        	# 16进制格式输出oFFs chunk的值
            print(":".join("{:02x}".format(ord(ch)) for ch in c[1]))
            return True

    return False

结果如下

00:00:01:2e:00:00:01:14:00

将前8个字节转换成2个整数值,就是302和276(这里注意这个值是补码,如果是负数的话,需要把补码转换成原码)
到这里,我们就能判断出一个PNG图像是否偏移了,如果偏移了,也能知道offset是多少。接下来就是如何纠正offset了。

纠正offset

待续…