Python图像编码插入DPI信息

Python中的图像编解码过程

OpenCV图像解码

在openCV中常见的编码格式有png , webp, jpg,bmp等常见的数据编码方式也有avif等非常见的数据格式。这里主要介绍前三种。图像编码格式的应用。下面我将以python代码段来介绍Opencv中的图像编码过程。

import cv2
import numpy as np
# 读取图像
im = cv2.imread("./example.png")
# 当路径中存在中文时采用imread方法返回为None
# 采用nunpy读取数据
data = np.fromfile("./例子.png", dtype=np.uint8)
# 这里我们获取了图像的原始数据,并将其转换成了numpy的一维的array对象,采用opencv将其解码成与imread一致的数据
im = cv2.imdecode(data, cv2.IMREAD_COLOR)
# 第二个参数为标志未指定转换的格式,也可以采用cv2.IMREAD_UNCHANGED替代
# 实际上图形的存储是采用二进制的方式进行存储的,因此对于含有中文路径的文件我们还能以文件的形式打开
with open("./例子.png", "rb") as f:
	data = f.read()
# 这里读取的数据类型为bytes, 即以二进制的方式读取的数据
data = np.asarray(bytearray(data), dtype='uint8')
# 到这里即转换成了np.fromfile()读取的数据类型,之后的解码就和上述过程一致了解码成一个(h, w, c)的numpy数组

至此采用opencv和numpy解码图像数据的过程就完成了,上述代码解释了从磁盘文件解析图像数据的过程。对网络数据其实解析过程也大同小异通过urllib库去请求。

from urllib import request

response = request.urlopen(url_path)
data = response.read()
# 这里读取的数据即为二进制的图像的文件,后续的解析方式可以按照上述的二进制文件的解析方式转换成numpy数组

注:上述过程只能解析OpenCV支持解码的数据格式,如avif的数据格式的图像数据是无法正常解析的。这类数据的解析我将在之后提到。

OpenCV编码numpy图像数据以及保存

import cv2
import numpy as np
# 创建图像
im = np.zeros((256, 256, 3),np.uint8)
# 采用webp编码数据
_, out = cv2.imencode('.webp', im, [cv2.IMWRITE_WEBP_QUALITY, 100]])
# imencode有三个参数,第一个是指定编码图像的编码格式,第二个是输入的BGR图像数组, 第三个参数指定压缩等级如webp图像是[0,100].数值越小压缩等级越高。画质越差,占用的磁盘空间越小。
# 返回为一个列表,第一位为标志符即一个布尔值。第二位是编码后的数据,实际上与np.fromfile()读取的数据类型一致
out = cv2.imencode('.jpeg', im, [cv2.IMWRITE_JPEG_QUALITY, 95])[1]
# jpeg压缩编码,压缩等级和webp类似
out = cv2.imencode('.png', im, [cv2.IMWRITE_PNG_COMPRESSION, 8])[1]
# png压缩编码,压缩等级[0,10]
# opencv还包含其它的图像编码格式可以参考相关文档。
# 既然编码的数据与numpy读取的一致那么编码后的图像数据可以采用numpy进行存储
out.tofile("./测试.png")
# 这种方案路径中可以存在中文,除了上述过程还可以再通过opencv解码成标准的BGR图像数组进行保存
im1 = = cv2.imdecode(out, cv2.IMREAD_COLOR)
cv2.imwrite("./test.png", im1)
# 这种方案实际上是进行了二次压缩,压缩方式和保存的尾缀一致,且保存路径中不能有中文路径容易出现保存返回False的情况
cv2.imwrite("./test.png", im1, [cv2.IMWRITE_JPEG_QUALITY, 95])
# 当然也可指定压缩方式,采用param参数实现,前面的png只是文件名并不影响存储的jpeg图像
# 除了调用库函数进行保存也可将数据转换成二进制文件直接进行保存
bytes_im = out.tobytes()
with open("./测试.png", "wb") as f:
	f.write(bytes_im)

OpenCV编码的信息插入图像DPI信息

DPI信息是指打开图像时每英寸上显示的像素个数。OpenCV编码的图像均采用其默认参数,对于对图像编码不了解的我们很难修改,我在找了一圈也只找到在png编码的图像数据中插入dpi信息的方法:

import struct
import zlib
import cv2
def writePNGwithdpi(im, filename, dpi=(72,72)):
   """Save the image as PNG with embedded dpi"""

   # Encode as PNG into memory
   retval, buffer = cv2.imencode(".png", im)
   s = buffer.tostring()
   # Find start of IDAT chunk
   IDAToffset = s.find(b'IDAT') - 4

   # Create our lovely new pHYs chunk - https://www.w3.org/TR/2003/REC-PNG-20031110/#11pHYs
   pHYs = b'pHYs' + struct.pack('!IIc',int(dpi[0]/0.0254),int(dpi[1]/0.0254),b"\x01" ) 
   pHYs = struct.pack('!I',9) + pHYs + struct.pack('!I',zlib.crc32(pHYs))
   with open(filename, "wb") as out:
       out.write(buffer[0:IDAToffset])
       out.write(pHYs)
       out.write(buffer[IDAToffset:])

那么对于其它压缩格式的图像我们怎样去插入dpi信息呢?后面我们将采用pillow库来实现。

Pillow解析图像数据

pillow库函数中有专门读取图像数据的方法即Image模块

from PIL import Image
# 注意Image可以直接打开含中文路径的文件
im = Image.open("./测试.png")

与opencv的方案不同pillow读取的图像的方式更接近python打开文件的方式,会完整的获取图像的所有信息,并创建一个Image的对象。该对象包含图像的宽高,大小和dpi和像素信息等。我们能通过它获取真实的图像编码格式等信息。而非简单的通过尾缀判断图像的编码格式。以下是一些简单的信息获取方式。

# 获取图像的宽和高
im.height
im.width
im.size
# 获取图像的模式(RGB或L)
im.mode
# 获取图像的dpi等信息
im.info
# 查看图像的编码格式
im.format
# 计算图像的信息熵
im.entropy()
# 其它信息可参考官方文档

正常而言pillow读取的数据是RGB颜色排列顺序的,而OpenCV中则是BGR的顺序。二者通过numpy可以相互转换

import numpy as np
import cv2
from PIL import Image

im = Image.open("./test.png")
numpy_array = np.asarray(im, np.uint8)
cv_im = cv2.imread("./test.png")
# 到这里cv_im和numpy_array并不相等,因为颜色空间顺序不同
# 采用opencv和数组操作均能实现颜色顺序的调整如
numpy_array = cv2.cvtColor(numpy_array, cv2.COLOR_RGB2BGR)
# 转换成BGR图像与cv_im一致
numpy_array = numpy_array[:,:,::-1]
numpy_array = numpy_array[:,:,(2,1,0)]
# 上述两种方法均为常见的rgb和bgr图像转换的方案
# 当然numpy数组也能转换成Image对象,可以看看由numpy构建的Image对象和直接读取的图像数据的差异
im2 = Image.fromarray(numpy_array)

与opencv中介绍的类似pillow也可以从bytes中直接解析图像,但是需要知道图像的一些信息,因此这里我们换一种方式进行解析

import numpy as np
import cv2
from PIL import Image
from io import BytesIO

data_dir = "./test.png"
with open(data_dir, "rb") as f:
    data = f.read()
# 这里data实际为读取的二进制文件
# 创建一个虚拟的io空间
fp = BytesIO(data)
im = Image.open(fp)

上面我们介绍了avif格式的图像无法通过OpenCV进行解析我们这里找到一个pillow插件能够解析这种格式的图像将其转换成Image对象
这里顺带提一下本文需要安装的依赖库的安装命令:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python
# 上述命令采用清华源加速下载会同时安装依赖的numpy库
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pillow
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pillow-avif-plugin

下载后可以通过引入pillow-avif

import numpy as np
import cv2
from PIL import Image
from io import BytesIO
import pillow_avif
# 这里必须引入pillow_avif库用于将avif的解码器注册到Image中

data_dir = "./test.avif"
with open(data_dir, "rb") as f:
    data = f.read()
# 这里data实际为读取的二进制文件
# 创建一个虚拟的io空间
fp = BytesIO(data)
im = Image.open(fp)

Pillow图像编码与保存

与OpenCV一样Pillow可以通过图像保存的方法进行编码,图像的编码可以通过format参数进行指定。

import numpy as np
import cv2
from PIL import Image
from io import BytesIO
import pillow_avif
# 这里必须引入pillow_avif库用于将avif的解码器注册到Image中

data_dir = "./test.png"
with open(data_dir, "rb") as f:
    data = f.read()
data = np.asarray(bytearray(data), dtype='uint8')
im = cv2.imdecode(data, cv2.IMREAD_COLOR)# convert image into numpy array
fp = BytesIO()
# 指定编码格式为JPEG, 压缩等级为95, dpi信息为72
im.save(fp, format="JPEG", quality=95, dpi=(72, 72))
# 指定编码格式为webp, 压缩等级为95, dpi信息为72
im.save(fp, format="WEBP", quality=95, dpi=(72, 72))#
# 其它压缩类型可以自行尝试如avif 和png等
image_bytes = im_bytes.getvalue()
image_bytes = np.asarray(bytearray(image_bytes), dtype='uint8')


至此简单的介绍了一下Python中常见的图像编码的过程,其实也可以参考每一种图像格式的原始编码器进行图像的编码如webp,avif等可以安装相应的库莱直接编码和解码。可以通过以下命令安装对应的库。

pip install webp
pip install webp
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值