通过本篇文章可以了解到如下知识点:
PNG图片编码; Python图片库; ImageMagick命令行工具简单使用
公司产品同事反馈有个商品的缩略图都不正常,缩略之后核心内容没有位于图片的正中间,导致图片不知所云,具体表现如下
原图:
缩略图:
这个缩略图正确的缩略结果应该是缩略出一张100x100且裁剪位置位于位于图片的正中间,这样就能保证缩略图也能基本展示商品,但是这个缩略结果明显就是不对的。那么问题出现在哪里呢?
这里推荐一个工具ImageMagick
的命令行工具集 magick
doc,这里我们使用其中的identify
工具查看原图的详细信息
identify -verbose https://s3.mogucdn.com/mlcdn/55cf19/190127_01fi6dbl0ea2eib450g46ckga1igh_640x960.png
结果如下:
其他结果都不重要,主要看Properties段
我们可以看到这张原图在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库进行缩略。感兴趣的可以了解下,不过学习难度较大。
检测时我们选用pypng这个库来对PNG图片解码,这里先大概讲解下PNG图片的编码结构,PNG图片整体的编码结构详细内容可以在这里看。
PNG图片编码内容分成了很多块(chunk),不同chunk描述了PNG图像的不同属性,大致如下:
核心chunk
块名 | 作用 |
---|---|
IHDR | 描述了图像的尺寸,色彩模式(sRGB等)(bit depth等,这些有点过于深入了,展开来讲又是一片文章) |
PLTE | 调色板数据块 |
IDAT | 图像数据块,决定图像内容 |
IEND | 图像结束块 |
辅助chunk
块名 | 作用 |
---|---|
cHRM | 基色和白色点数据块 |
gAMA | 图像γ数据块 |
扩展chunk
块名 | 作用 |
---|---|
oFFs | 标记偏移 |
其它 |
主要关注下oFFs这个chunk
上面是官方文档里对oFFs的描述,根据文档的描述,oFFs是当一张大图被分割为多个小图时,oFFs可以标记每个小图的绝对位置(absolute position)。例如当我们需要将一张图片在很大的海报上打印出来时,并且通常是和pHYs chunk配合使用。
所以我们首先可以根据一个PNG是否包含oFFs段来判断是PNG图像是否有偏移,其次通过文档可以知道oFFs端中前4个字节存储了x_off
的值,接下来4个字节存储了y_off
的值,最后1个字节存储了一个叫做Unit specifier的值。那么只需将前8个字节取出来就可以知道这个图片的x_off
和y_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了。
待续…