`本文紧接上一篇“PYNQ数字图像处理之图像采集”,主要介绍使用PYNQ-Z2开发板中的Python框架实现对图像进行空域处理方法:像素级变换、图像金字塔、直方图操作、模板匹配、空域滤波。
首先介绍图像空域处理的概念。无论是单、双目相机、还是深度相机,采集到的信息最终都是转为单通道或多通道的二维图像数据被处理的,图像空域处理就是在这个二维空间对图像的一个或多个像素点之间的关系进行分析和处理。
本文所有代码与图片素材已制作成压缩包,在文末以附件给出,在Jupyter Notebook下运行非常直观明了,建议网友自己试着运行一遍,修改学习每个关键参数的作用。
一、像素级变换
像素级变换简而言之就是在像素级别上,直接对每个像素值做变换,通过设置某种映射关系T[ ],将原像素映射成为新像素,进而得到一张新的图像。像素级变换的主要应用为阈值化、取反、Gamma矫正、亮度与对比度操作,几种操作的不同之处就在于映射关系T[]的不同,下面依次用程序实现:
阈值化操作的映射T为:g(x,y) = 1 :0 ? g(x,y) > threshold,即当像素值大于设定阈值时,原位置像素转换为二值图像的1,当小于阈值时,像素转换为0。
在OpenCV中,既可以直接使用运算符进行操作,也可以直接调用函数Threshhold()或adaptiveThreshhold(),其中Threshhold()可以自己设置阈值,而adaptiveThreshhold()会使用自适应算法自己选择合适的阈值,在Python中实现的代码为:
- import matplotlib.pyplot as plt
- import numpy as np
- import cv2
- %matplotlib inline
- # 读取图像并转灰度
- board_img = cv2.imread('images/board.jpg',cv2.IMREAD_ANYCOLOR)
- board_img_gray = cv2.cvtColor(board_img, cv2.COLOR_BGR2GRAY)
- # 图像阈值化
- ret,thresh = cv2.threshold(board_img_gray,130,255,cv2.THRESH_BINARY)
- # 显示结果
- plt.subplot(1,3,1)
- plt.imshow(board_img)
- plt.title("origin")
- plt.axis("off")
- plt.subplot(1,3,2)
- plt.imshow(board_img_gray,'gray')
- plt.title("gray")
- plt.axis("off")
- plt.subplot(1,3,3)
- plt.imshow(thresh,'binary')
- plt.title("binary")
- plt.axis("off")
- plt.show()
复制代码
效果图:
对于8位灰度图,图像取反的映射T为:g(x,y) = 255 - g(x,y)。在OpenCV中,既可以直接使用运算符进行操作,也可以直接调用函数bitwise_not()。
- thresh_not = cv2.bitwise_not(thresh)
- plt.imshow(thresh_not,'binary')
- plt.title("bitwise_not")
- plt.axis("off")
- plt.show()
复制代码
Gamma矫正主要用于增强图像的视觉效果。由于相机拍摄到的图像与输入光强呈线性关系,而人眼在低照度下更容易分辨出亮度的变化,随着照度的增加,人眼对亮度的分辨能力会减弱,导致观看时部分细节不明显,此时应用Gamma矫正可以增强视觉效果。
Gamma矫正是一种非线性变换,其映射T为g(x,y) = Ag(x,y)^γ,对于八位灰度图,公式进一步推导为
其中gamma值通常取0.05~5。
代码实现为:
- def gamma_trans(img,gamma):
- #具体做法先归一化到1,然后gamma作为指数值求出新的像素值再还原
- gamma_table = [np.power(x/255.0,gamma)*255.0 for x in range(256)]
- gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)
- #实现映射用的是Opencv的查表函数
- return cv2.LUT(img,gamma_table)
- night_img = cv2.imread('images/gamma_0.jpg',cv2.IMREAD_ANYCOLOR)
- night_img_corrected = gamma_trans(night_img, 0.5)
- # 显示结果
- night_img_disp = cv2.cvtColor(night_img,cv2.COLOR_BGR2RGB)
- plt.imshow(night_img_disp)
- plt.title("night_img")
- plt.axis("off")
- plt.show()
- night_img_corrected_disp = cv2.cvtColor(night_img_corrected,cv2.COLOR_BGR2RGB)
- plt.imshow(night_img_corrected_disp)
- plt.title("night_img_corrected")
- plt.axis("off")
- plt.show()
复制代码
亮度与对比度操作,通过对像素应用变换T:g(i,j) = a * g(i,j) + b进行调整,其中a用来控制对比度的增大与减小,b用来增加或减少亮度。需要注意的是,需要对运算结果进行约束,保证其仍在规定的数值范围。
在OpenCV中,使用直接像素值运算或调用addWeighted(img, alpha, blank, 1-alpha, beta)函数来进行操作,代码如下:
- def Contrast_and_Brightness(img, alpha, beta):
- blank = np.zeros(img.shape, img.dtype)
- dst = cv2.addWeighted(img, alpha, blank, 1-alpha, beta)
- return dst
- lena_img = cv2.imread('images/lena.png',cv2.IMREAD_ANYCOLOR)
- lena_img_dst = Contrast_and_Brightness(lena_img, 1.5, 20)
- # 显示结果
- lena_img_disp = cv2.cvtColor(lena_img,cv2.COLOR_BGR2RGB)
- plt.imshow(lena_img_disp)
- plt.title("lena_img")
- plt.axis("off")
- plt.show()
- lena_img_dst_disp = cv2.cvtColor(lena_img_dst,cv2.COLOR_BGR2RGB)
- plt.imshow(lena_img_dst_disp)
- plt.title("lena_img_dst")
- plt.axis("off")
- plt.show()
复制代码
二、图像金字塔
一般情况下,我们在处理图像的过程中分辨率是固定的。但是做图像识别和图像融合时,我们需要获得同一图像在不同分辨率下的子图像以提取特征或与其他图像大小匹配,这时如果我们把高分辨率的图像放在底部,低分辨率的放在顶部,在结构上看起来就像一座金字塔,因此把这个结构叫做图像金字塔。
[size=13.3333px]
下面以图像融合应用为例对图像金字塔操作进行演示,主要处理步骤如下:
1. 读入两幅图像,苹果和橘子
2. 构建苹果和橘子的高斯金字塔(6 层)
3. 根据高斯金字塔计算拉普拉斯金字塔
4. 在拉普拉斯的每一层进行图像融合(苹果的左边与橘子的右边融合)
5. 根据融合后的图像金字塔重建原始图像。
- A = cv2.imread('images/apple.jpg')
- B = cv2.imread('images/orange.jpg')
- # generate Gaussian pyramid for A
- G = A.copy()
- gpA = [G]
- for i in np.arange(6): #将苹果进行高斯金字塔处理,总共六级处理
- G = cv2.pyrDown(G)
- gpA.append(G)
- # generate Gaussian pyramid for B
- G = B.copy()
- gpB = [G]
- for i in np.arange(6): #将橘子进行高斯金字塔处理,总共六级处理
- G = cv2.pyrDown(G)
- gpB.append(G)
- # generate Laplacian Pyramid for A
- lpA = [gpA[5]]
- for i in np.arange(5,0,-1): #将苹果进行拉普拉斯金字塔处理,总共5级处理
- GE = cv2.pyrUp(gpA[i])
- L = cv2.subtract(gpA[i-1],GE)
- lpA.append(L)
- # generate Laplacian Pyramid for B
- lpB = [gpB[5]]
- for i in np.arange(5,0,-1): #将橘子进行拉普拉斯金字塔处理,总共5级处理
- GE = cv2.pyrUp(gpB[i])
- L = cv2.subtract(gpB[i-1],GE)
- lpB.append(L)
- # Now add left and right halves of images in each level
- #numpy.hstack(tup)
- #Take a sequence of arrays and stack them horizontally
- #to make a single array.
- LS = []
- for la,lb in zip(lpA,lpB):
- rows,cols,dpt = la.shape
复制代码
三、直方图操作
直方图是对图像中的灰度分布的统计信息。通过一张图像直方图我们可以对图像的对比度,亮度,灰度分布等有一个直观的认识,从而使用它对图像的对比度,亮度,灰度分布进行修改。与像素级操作的对比度,亮度,灰度修改不同,基于直方图的修改是针对整幅或一块区域的亮度分布进行修改的。
OpenCV中提供了calcHist() 函数来获取图像的直方图信息,下面我们使用直方图对一幅图片中的对比度分布进行优化。
[size=13.3333px]
- aero_img = cv2.imread('images/aero1.jpg',cv2.IMREAD_GRAYSCALE)
- equ = cv2.equalizeHist(aero_img)
- # 显示结果
- plt.imshow(aero_img,'gray')
- plt.title("Origin Picture")
- plt.axis("off")
- plt.show()
- hist,bins = np.histogram(aero_img.flatten(),256,[0,256])
- # 计算累积分布图
- cdf = hist.cumsum()
- cdf_normalized = cdf * hist.max()/ cdf.max()
- plt.plot(cdf_normalized, color = 'b')
- plt.hist(img.flatten(),256,[0,256], color = 'r')
- plt.xlim([0,256])
- plt.legend(('cdf','histogram'), loc = 'upper left')
- plt.show()
- plt.imshow(equ,'gray')
- plt.title("Processed")
- plt.axis("off")
- plt.show()
- hist,bins = np.histogram(equ.flatten(),256,[0,256])
- # 计算累积分布图
- cdf = hist.cumsum()
- cdf_normalized = cdf * hist.max()/ cdf.max()
- plt.plot(cdf_normalized, color = 'b')
- plt.hist(img.flatten(),256,[0,256], color = 'r')
- plt.xlim([0,256])
- plt.legend(('cdf','histogram'), loc = 'upper left')
- plt.show()
复制代码
四、模板匹配
模板匹配常被用来在一副大图中搜寻查找模版图像位置。和 2D 卷积一样,它也是用模板图像在输入图像(大图)上滑动,并在每一个位置对模板图像和与其对应的输入图像的子区域进行比较。 OpenCV 为我们提供了函数:matchTemplate()。返回的结果是一个灰度图像,每一个像素值表示了此区域与模板的匹配程度,像素值越大,说明当前区域的图像与模板的相似程度越高。
以下图为例,查找目标模板(小图)在原图中的位置。
原图:
模板图:
计算结果:
匹配结果:
- img = cv2.imread('images/TaskForce141.jpg',0)
- img2 = img.copy()
- template = cv2.imread('images/soap.jpg',0)
- w, h = template.shape[::-1]
- img = img2.copy()
- method = 'cv2.TM_CCOEFF_NORMED'
- # 模板匹配
- res = cv2.matchTemplate(img,template,method)
- # 确定坐标
- min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
- top_left = max_loc
- bottom_right = (top_left[0] + w, top_left[1] + h)
- cv2.rectangle(img,top_left, bottom_right, 255, 2)
- plt.imshow(res,cmap = 'gray')
- plt.title('Matching Result'), plt.axis("off")
- plt.show()
- plt.imshow(img,cmap = 'gray')
- plt.title('Detected Point'), plt.axis("off")
- plt.show()
复制代码
五、空域滤波
空域滤波是一种邻域处理方法,通过直接在二维图像空间中对邻域内像素进行处理,达到平滑或锐化图像的作用。其中主要有均值(盒子)滤波、高斯滤波、中值滤波、高斯双边滤波,几种方式各有优劣,视应用场合而定。
均值滤波又称盒子滤波,计算方法为取区域内所有像素的平均值,但是会丢失部分像素信息;高斯滤波使用一个内部数值为高斯分布的卷积核对图像进行卷积,可以有效的从图像中去除高斯噪音;中值滤波计算方法为取区域内所有像素的中位数,可以有效去除图像中的椒盐噪声;高斯双边滤波对高斯滤波进一步优化,可以保持边界清晰的情况下有效的去除噪音。
下面对几种滤波方法进行代码演示:
均值滤波效果,图像变得模糊,也丢失了部分细节:
高斯滤波效果,图像中的高斯噪声得到抑制:
中值滤波效果,图像中的椒盐噪声得到抑制:
双边高斯滤波效果,在抑制高斯噪声的同时,边缘得到增强:
- # 均值滤波
- lena_img = cv2.imread('images/lena.png')
- blur = cv2.blur(lena_img,(5,5))
- lena_img_disp = cv2.cvtColor(lena_img,cv2.COLOR_BGR2RGB)
- plt.subplot(121),plt.imshow(lena_img_disp),plt.title('Original')
- plt.axis("off")
- blur_disp = cv2.cvtColor(blur,cv2.COLOR_BGR2RGB)
- plt.subplot(122),plt.imshow(blur_disp),plt.title('Box Filter')
- plt.axis("off"),plt.show()
- # 高斯滤波
- Gaussian_noise_img = cv2.imread('images/Gaussian_noise.jpg')
- blur = cv2.GaussianBlur(Gaussian_noise_img,(5,5),0)
- Gaussian_noise_img_disp = cv2.cvtColor(Gaussian_noise_img,cv2.COLOR_BGR2RGB)
- plt.subplot(121),plt.imshow(Gaussian_noise_img_disp),plt.title('Original')
- plt.axis("off")
- blur_disp = cv2.cvtColor(blur,cv2.COLOR_BGR2RGB)
- plt.subplot(122),plt.imshow(blur_disp),plt.title('Gaussion Filter')
- plt.axis("off"), plt.show()
- # 中值滤波
- SP_img = cv2.imread('images/Salt_and_pepper_noise_noise.jpg')
- blur = cv2.medianBlur(SP_img,5)
- SP_img_disp = cv2.cvtColor(SP_img,cv2.COLOR_BGR2RGB)
- plt.subplot(121),plt.imshow(SP_img_disp),plt.title('Original')
- plt.axis("off")
- blur_disp = cv2.cvtColor(blur,cv2.COLOR_BGR2RGB)
- plt.subplot(122),plt.imshow(blur_disp),plt.title('Median Filter')
- plt.axis("off"), plt.show()
- # 双边高斯滤波
- blur = cv2.bilateralFilter(Gaussian_noise_img,9,75,75)
- plt.subplot(121),plt.imshow(Gaussian_noise_img_disp),plt.title('Original')
- plt.axis("off")
- blur_disp = cv2.cvtColor(blur,cv2.COLOR_BGR2RGB)
- plt.subplot(122),plt.imshow(blur_disp),plt.title('Median Filter')
- plt.axis("off"), plt.show()
复制代码
`
|