Python 计算机视觉—— OpenCV 进行图像量化与采样
Posted 一马归一码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 计算机视觉—— OpenCV 进行图像量化与采样相关的知识,希望对你有一定的参考价值。
对于信号的采样可以参考我之前的文章:数字信号处理 2.1 — 采样
对于信号的量化可以参考:数字信号处理 2.4 — ADC 中的有限字长效应
在本篇文章中绘图使用到了 matplotlib 库,需要了解学习可以参考我之前写的用来总结这个绘图库的文章:Python 绘图库 Matplotlib
目录
1. 图像量化与采样处理
经典开头读取图像信息:
"""
Author:XiaoMa
date:2021/10/22
"""
#调用所需的包
import matplotlib.pyplot as plt
import cv2
import numpy as np
#读取原始图像的信息
img0 = cv2.imread('E:\\From Zhihu\\For the desk\\ZHM.jpeg') #读取图像
img1 = cv2.resize(img0, fx = 0.5, fy = 0.5, dsize = None) #调整图像大小
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #将图像转化为灰度图像
height = img1.shape[0] #shape[0] 图像第一维度,高度
width = img1.shape[1] #shape[1] 图像第二维度,宽度
print(img1.shape)
print(width, height)
cv2.namedWindow("W0")
cv2.imshow("W0", img1)
cv2.waitKey(delay = 0)
(1)基本概念
此处参考:知乎文章
把一幅图像表示为二元函数形式:f(x, y),那么该函数在不同的坐标点有不同的幅度值,而这些幅度值是连续的,无法使用计算机进行处理,所以我们需要对幅度值进行离散化(数字化),这个过程即为图像量化处理,一般常见的量化方式是将图像用黑白两种颜色表示,称为二值图像
在之前的文章中提到一幅图像可以表示为在 x 轴和 y 轴上连续的信号,但计算机无法处理连续信号,所以我们需要将图像在这两个维度进行离散化处理,该过程就称为图像的采样处理,即对图像的坐标轴进行数字化
(2)量化操作
具体代码如下,都已经添加了注释,理解起来应该没问题,如果有我注释的不清楚的地方可以在评论区提出来,我们一起探讨学习:
"""
Author:XiaoMa
date:2021/10/22
"""
#调用所需的包
import matplotlib.pyplot as plt
import cv2
import numpy as np
#读取原始图像的信息
img0 = cv2.imread('E:\\From Zhihu\\For the desk\\ZHM.jpeg') #读取图像
img1 = cv2.resize(img0, fx = 0.5, fy = 0.5, dsize = None) #调整图像大小
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #将图像转化为灰度图像
height = img1.shape[0] #shape[0] 图像第一维度,高度
width = img1.shape[1] #shape[1] 图像第二维度,宽度
print(img1.shape)
print(width, height)
cv2.namedWindow("W0")
cv2.imshow("W0", img2)
cv2.waitKey(delay = 0)
#创建和原始图像同等大小的矩阵
img3 = np.zeros((width, height, 3), np.uint(8))
img4 = np.zeros((width, height, 3), np.uint(8))
img5 = np.zeros((width, height, 3), np.uint(8))
img6 = np.zeros((width, height, 3), np.uint(8))
img7 = np.zeros((width, height, 3), np.uint(8))
#对原始图像矩阵的值进行操作
img3 = np.uint8(img2/4) * 4
img4 = np.uint8(img2/16) * 16
img5 = np.uint8(img2/32) * 32
img6 = np.uint8(img2/64) * 64
img7 = np.uint8(img2 >= 128) * 128
plt.rcParams['font.family'] = 'SimHei' #将全局中文字体改为黑体
plt.rcParams['axes.unicode_minus'] = False #正常表示负号
#显示得到的图像
title = ['原始图像', '量化为64份', '量化为16份', '量化为8份', '量化为4份', '量化为2份'] #子图标题
img = [img2, img3, img4, img5, img6, img7]
for i in range(6):
plt.subplot(2, 3, i + 1) #python 列表从0开始计数,所以此处 i+1
plt.imshow(img[i], 'gray')
plt.title(title[i])
plt.xticks([]),plt.yticks([])
plt.savefig('E:\\From Zhihu\\For the desk\\ZHM1.jpeg')
plt.show()
然后得到的量化后的图像如下:
(2)采样操作
具体代码如下:
"""
Author:XiaoMa
date:2021/10/22
"""
#调用所需的包
import matplotlib.pyplot as plt
import cv2
import numpy as np
#读取原始图像的信息
img0 = cv2.imread('E:\\From Zhihu\\For the desk\\ZHM.jpeg') #读取图像
img1 = cv2.resize(img0, fx = 0.5, fy = 0.5, dsize = None) #调整图像大小
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #将图像转化为灰度图像
height = img1.shape[0] #shape[0] 图像第一维度,高度
width = img1.shape[1] #shape[1] 图像第二维度,宽度
print(img1.shape)
print(width, height)
cv2.namedWindow("W0")
cv2.imshow("W0", img1)
cv2.waitKey(delay = 0)
plt.rcParams['font.family'] = 'SimHei' #将全局中文字体改为黑体
img8 = img2[0:-1:2, 0:-1:2]
#img8_1 = cv2.resize(img8, dsize = None, fx = 2, fy = 2) #这个注释先不管,后面解释
img9 = img2[0:-1:4, 0:-1:4]
#img9_1 = cv2.resize(img9, dsize = None, fx = 4, fy = 4)
img10 = img2[0:-1:8, 0:-1:8]
#img10_1 = cv2.resize(img10, dsize = None, fx = 8, fy = 8)
img11 = img2[0:-1:16, 0:-1:16]
#img11_1 = cv2.resize(img11, dsize = None, fx = 16, fy = 16)
titles = ['原始图像', '256*256', '128*128', '64*64', '32*32', '16*16']
image = [img1, img2, img8, img9, img10, img11] #此处一定要注意不加引号,别问我为什么知道,心累
for j in range(6):
plt.subplot(2, 3, j + 1)
if j == 0:
plt.imshow(image[j])
else:
plt.imshow(image[j], 'gray')
plt.title(titles[j])
plt.xticks([]), plt.yticks([])
plt.savefig('E:\\From Zhihu\\For the desk\\ZHM2.jpeg')
plt.show()
得到的图像如上,但是第一幅图是不是很怪?其实原因在前面讲过的:
OpenCV 对于图像的处理方式是 BGR 的,而对于 matplotlib 来说是 RGB 的,所以要将通道顺序进行修改:
只要加上这一句代码就可以了,添加的位置是在组成列表之前就行
img_rgb = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
关于前面的代码中的注释部分:图像经过采样之后尺寸大小发生变化,对其尺寸进行调整使其能够正常显示(当然这只会在单独显示图像时用到)
如:未改变尺寸前的 16*16 图像如下(在原图左上角)
经过改变后的图像如下:
2. 图像金字塔
(1)基本概念
概念这方面参考的文章:OpenCV(23)---图像金字塔
图像金字塔是由一副图像的多个不同分辨率的子图所构成的图像集合。该组图像是由单个图像通过不断地降低采样所产生的,最小的图像可能仅仅只有一个像素点。如下图所示,分辨率从低到高,逐渐降低的图像集合。(该段全部摘自上面的文章)
下面说说我的理解:在图像的采样的介绍中我们得到了五张经过采样后的图像,而它们的分辨率都是不同的, 因为在采样的过程中总会失去一些信息,图像中失去的是像素点(这方面不理解可以参考文章头粘贴的关于采样的文章),然后将不同分辨率的图像按照上图中的金子塔从大到小排列上去,就是图像金子塔了
就拿上图举例,所谓的图像金子塔就是将第二幅图放在 Level0 层,将第三幅放在 Level1 层,以此类推,得到最终的图像
(2)操作实现
高斯金子塔下采样
#调用所需的包
import matplotlib.pyplot as plt
import cv2
#读取原始图像的信息
img0 = cv2.imread('E:\\From Zhihu\\For the desk\\ZHM.jpeg') #读取图像
img1 = cv2.resize(img0, fx = 0.5, fy = 0.5, dsize = None) #调整图像大小
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #将图像转化为灰度图像
img12 = cv2.pyrDown(img2) #高斯滤波下采样得到高斯金字塔
cv2.namedWindow("W1")
cv2.imshow("W1", img12)
cv2.waitKey(delay = 0)
将其与原图像对比:
可以看出,经过降采样得到的图像与原图像对比最主观的变化是尺寸大小的变化,当然尺寸大小也可以通过参数进行设置,其实就是对采样率的设置了
拉普拉斯金子塔恢复原图像
通过高斯金子塔下采样得到的图像没办法通过高斯上采样恢复出来,因为按照采样的意义来说已经丢失了那部分的像素点,没办法凭空出现,但丢失的那些信息,刚好就构成了拉普拉斯金字塔,将高斯金字塔加进去就能复原出原图像了
#调用所需的包
import matplotlib.pyplot as plt
import cv2
#读取原始图像的信息
img0 = cv2.imread('E:\\From Zhihu\\For the desk\\ZHM.jpeg') #读取图像
img1 = cv2.resize(img0, fx = 0.5, fy = 0.5, dsize = None) #调整图像大小
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #将图像转化为灰度图像
img12 = cv2.pyrDown(img2) #高斯滤波下采样得到高斯金字塔
cv2.namedWindow("W1")
cv2.imshow("W1", img12)
cv2.waitKey(delay = 0)
img12_1 = img1 - cv2.pyrUp(img12) #得到拉普拉斯金字塔
cv2.namedWindow("W2")
cv2.imshow("W2", img12_1)
cv2.waitKey(delay = 0)
img12_2 = img12_1 + cv2.pyrUp(img12) #恢复原图像
cv2.namedWindow("W3")
cv2.imshow("W3", img12_2)
cv2.waitKey(delay = 0)
W0 中的是原图,W1 是经过下采样得到的高斯金字塔,W2 是拉普拉斯金字塔,W3 是恢复出来的原图像
3. 局部马赛克处理
该段参考文章:图片处理之马赛克
(1)实现原理
a:确定需要生成马赛克的目标区域
b:将目标区域分为许多同等大小的,为了打码效果尽可能的好,划分的数目一般不能过多或过少
c:在划分的区域中随机选择一个像素点,用这个像素点代替所有该区域中的像素点
(2)实战操作
import cv2
#读取原始图像的信息
img0 = cv2.imread('E:\\From Zhihu\\For the desk\\ZHM.jpeg') #读取图像
img1 = cv2.resize(img0, fx = 0.5, fy = 0.5, dsize = None) #调整图像大小
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) #将图像转化为灰度图像
height = img1.shape[0] #shape[0] 图像第一维度,高度
width = img1.shape[1] #shape[1] 图像第二维度,宽度
print(img1.shape)
print(width, height)
cv2.namedWindow("W0")
cv2.imshow("W0", img1)
cv2.waitKey(delay = 0)
for i in range(200, 400, 10): #生产马赛克的目标区域(对图像高度)
for j in range(200, 400, 10): #生成马赛克的目标区域(对图像宽度)
for m in range(0, 10): #区域大小为 10 * 10
for n in range(0, 10):
img1[i+m, j+n] = img1[i, j] #用该区域的第一个像素点替换掉其他的像素点
cv2.namedWindow("W4")
cv2.imshow("W4", img1)
cv2.waitKey(delay = 0)
得到图像如下:
结束语
本篇文章主要总结了图像的采样以及量化的原理以及操作,在此基础上添加了图像的马赛克处理以及图像金子塔,其中参考的一些文章的链接也添加了进去,如果需要更深入的了解和学习可以点进去学习
以上是关于Python 计算机视觉—— OpenCV 进行图像量化与采样的主要内容,如果未能解决你的问题,请参考以下文章
计算机视觉OpenCV 4高级编程与项目实战(Python版):拼接图像
Python深度学习:计算机视觉处理库OpenCVNumpy编辑图片高斯模糊处理(读书笔记)
OpenCV-Python实战(23)——将OpenCV计算机视觉项目部署到云端