本案例为计算机视觉课程【第二章】图像处理基础的配套案例。

1 图像处理简介

图像处理一般指数字图像处理。 数字图像是指用工业相机、摄像机、扫描仪等设备经过拍摄得到的一个大的二维数组,该数组的元素称为像素,其值称为灰度值。

图像处理技术一般包括图像运算、图像几何变换、形态学处理等方面。

2 图像运算

本节介绍图像运算的相关内容。

图像运算指以图像为单位进行的搡作。运算的结果是一幅其灰度分布与原来参与运算图像灰度分布 不同的新图像。

具体的运算主要包括算术和逻辑运算,它们通过改变像素的值来得到图像增强、仿射等效果。

2.1 图像加法、减法运算

首先加载一张图像。

In [51]:
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread("./input/sea.jpg")
img_rgb = img[:,:,::-1] # 将图像转换为RGB
plt.imshow(img_rgb)
plt.show()

对图像进行加法运算:图像各像素加100

In [52]:
M = np.ones(img.shape,dtype="uint8")*100# 与image大小一样的全100矩阵
added = cv2.add(img,M)  # 将图像image与M相加
added_rgb = added[:,:,::-1] # 将图像转换为RGB
plt.imshow(added_rgb)
plt.show()

接着对图像进行减法运算: 原图像每个像素都减去50,小于0的按0处理。

In [53]:
M = np.ones(img.shape,dtype="uint8")*50 # 与image大小一样的全50矩阵
subtracted = cv2.subtract(img,M) # 将图像image与M相减
subtracted_rgb = subtracted[:,:,::-1] # 将图像转换为RGB
plt.imshow(subtracted_rgb)
plt.show()

2.2 图像点乘、点除运算

对图像进行点乘运算:图像的每个像素值乘以3。

In [54]:
M = np.ones(img.shape,dtype="uint8")*3
multiply = cv2.multiply(img, M)
multiply_rgb = multiply[:,:,::-1] # 将图像转换为RGB
plt.imshow(multiply_rgb)
plt.show()

对图像进行点除运算:图像的每个像素值除以3。

In [55]:
M = np.ones(img.shape,dtype="uint8")*3
divide = cv2.divide(img, M)
divide_rgb = divide[:,:,::-1] # 将图像转换为RGB
plt.imshow(divide_rgb)
plt.show()

2.3 图像乘法运算

首先加载两幅图像。

In [56]:
image1 = cv2.imread("./input/sea.jpg") # 加载图像1
image1_rgb = image1[:,:,::-1] # 将图像转换为RGB
plt.imshow(image1_rgb)
plt.show()
In [57]:
image2 = cv2.imread("./input/tree.jpg") # 加载图像2
image2_rgb = image2[:,:,::-1] # 将图像转换为RGB
plt.imshow(image2_rgb)
plt.show()

然后对两幅图像做乘法运算。

In [58]:
multiply_2 = cv2.multiply(image1, image2)  # 两个图像相乘,图像像素大小要相同
multiply_2_rgb = multiply_2[:,:,::-1] # 将图像转换为RGB
plt.imshow(multiply_2_rgb)
plt.show()

2.4 图像按位逻辑运算

两个图像的交集可以表示为:dst = src1 & src2

dst:目标图像

sc1:图像1

sc2: 图像2

In [59]:
bitwise_and = cv2.bitwise_and(image1,image2)
bitwise_and = bitwise_and[:,:,::-1] # 将图像转换为RGB
plt.imshow(bitwise_and)
plt.show()

两个图像的并集为:dst = src1 | src2

In [60]:
bitwise_or = cv2.bitwise_or(image1,image2)
bitwise_or = bitwise_or[:,:,::-1] 
plt.imshow(bitwise_or)
plt.show()

图像的非:dst = - src

In [61]:
bitwise_not = cv2.bitwise_not(image1)
bitwise_not = bitwise_not[:,:,::-1] 
plt.imshow(bitwise_not)
plt.show()

两个图像的异或:dst = src1 ^ src2

In [62]:
bitwise_xor = cv2.bitwise_xor(image1,image2)
bitwise_xor = bitwise_xor[:,:,::-1] 
plt.imshow(bitwise_xor)
plt.show()

3 色彩空间转换

下面介绍RGB图像转换为HSV图像。

首先加载图像。

In [63]:
img = cv2.imread("./input/tree.jpg") 
img = img[:,:,::-1]  # 将图像转换为RGB
plt.imshow(img)
plt.show()

将RGB图像转换为HSV图像。

In [64]:
hsv_image = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
# HSV_image = HSV_image[:,:,::-1]  # 将图像转换为RGB
plt.imshow(hsv_image)
plt.show()

接着介绍RGB图像转换为HLS图像。

In [65]:
hls_image = cv2.cvtColor(img, cv2.COLOR_RGB2HLS) 
plt.imshow(hls_image)
plt.show()

4 图像几何变换

4.1 仿射变换

本节介绍图像的仿射变换,首先介绍图像的平移。

原始图片为:

In [66]:
img = cv2.imread("./input/bread.jpg") 
img = img[:,:,::-1] 
plt.imshow(img)
plt.show()

接着对图像进行平移操作。按照向左平移100,向下平移50操作。

In [67]:
img_grey = cv2.imread("./input/bread.jpg",0) # 读入灰度图片
rows,cols = img_grey.shape
m = 100 ; n = 50 # 平移的长度
M = np.float32([[1,0,m],[0,1,n]])
translation = cv2.warpAffine(img,M,(cols,rows)) # 平移
plt.imshow(translation)
plt.show()

再对原始图像进行缩放操作。宽度放大2倍,长度放大1.5倍。

In [68]:
resize = cv2.resize(img,None,fx =2 ,fy = 1.5) # 宽度放大2倍,长度放大1.5倍
plt.imshow(resize)
plt.show()

最后对原始图像进行旋转操作。

将图像相对于中心旋转90度而不进行任何缩放。

In [69]:
M = cv2.getRotationMatrix2D(((cols-1)/2.0,(rows-1)/2.0),90,1)
rotation = cv2.warpAffine(img,M,(cols,rows))
plt.imshow(rotation)
plt.show()

4.2 透视变换

对于透视变换,需要一个3x3变换矩阵。

要找到此变换矩阵,需要输入图像上的4个点的坐标,以及这4个点在输出图像上对应的坐标。在这4个点中,其中3个不应该共线。特殊情况如:四个点变换前后的坐标相同,则变换前后得到的图像相同。

然后可以通过函数cv2. getPerspectiveTransform找到变换矩阵。再将cv2. warpPerspective应用于此3x3变换矩阵。

In [70]:
# 随机选取出原图像中四个点的坐标
pts1 = np.float32([[56, 65],[368 ,52] , [28 ,387],[389 ,390]] ) 
# 给定经过透视变换后,上述四个点的新的坐标
pts2 = np.float32([[0,0], [300,0],[0,300],[300,300]])
# 根据转换前后的四对点的坐标,计算出变换矩阵
M = cv2.getPerspectiveTransform(pts1,pts2)
dst = cv2.warpPerspective(img ,M, (500,900))  # (500,900)是输出图像的大小
plt.imshow(dst)
plt.show()

5 图像平滑

本节介绍图像平滑的内容,包括高斯平滑、中值平滑等。

5.1 二维离散卷积

“离散卷积”是两个离散序列 f(n)h(n) 之间按照一定的规则,将它们的有关序列值分别两两相乘再相加的一种特殊的运算。

5.2 均值平滑

首先加载原始图片。

In [71]:
img = cv2.imread("./input/flower.jpg") 
img = img[:,:,::-1] 
plt.imshow(img)
plt.show()

对图像进行均值平滑。

In [72]:
blur=cv2.blur(img, ksize = (5,5)) # ksize: 均值算子的尺寸,Size(宽,高)
plt.imshow(blur)
plt.show()

5.3 高斯平滑

OpenCV提供的高斯平滑函数:

dst = cv.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])

ksize: 高斯卷积核的大小、宽、高均为奇数,且可以不同

sigmaX: 一维水平方向高斯卷积核的标准差

sigmaY: 一维垂直方向高斯卷积核的标准差,默认值为0

borderType: 边界扩充方式

In [73]:
gaussian_blur = cv2.GaussianBlur(img, ksize = (25,25), sigmaX = 9)
plt.imshow(gaussian_blur)
plt.show()

5.4 中值平滑

OpenCV中使用medianBlur()函数,用中值滤波器来平滑(模糊)处理一张图片。

In [74]:
medianblur = cv2.medianBlur(img, ksize = 11)
# int类型的ksize,孔径的线性尺寸(aperture linear size).
# 注意这个参数必须是大于1的奇数,比如:3,5,7,9 ...
plt.imshow(medianblur)
plt.show()

5.5 双边滤波

双边滤波是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,达到保边去噪的目的。

具有简单、非迭代、局部处理的特点。

双边滤波之所以能够达到保边去噪的滤波效果,是因为滤波器由两个函数构成:一个函数是由几何空间距离决定滤波器系数,另一个是由像素差值决定滤波器系数。

In [75]:
bilateral_filter = cv2.bilateralFilter(img, d = 23,sigmaColor = 11,sigmaSpace = 13)
plt.imshow(bilateral_filter)
plt.show()

6 形态学处理

本节介绍形态学处理。包括膨胀与腐蚀等处理方式。膨胀与腐蚀能实现多种多样的功能,主要如下:

消除噪声、分割出独立的图像元素、在图像中连接相邻的元素、寻找图像中的明显的极大值区域或极小值区域、求出图像的梯度。

6.1 腐蚀

腐蚀是在原图的每一个小区域里取最小值。由于是二值化图像,只要有一个点为0,则都为0,可以达到瘦身的目的。因此在下面的例子中,我们可以使用腐蚀来去掉图片中的一些毛刺或很细小的东西。

首先读取原始图片。

In [76]:
img = cv2.imread("./input/earth.jpg") 
img = img[:,:,::-1] 
plt.imshow(img)
plt.show()

接下来对图像进行腐蚀处理。

In [77]:
kernel = np.ones((3,3),np.uint8) 
# kernel: 腐蚀操作的核。当为 NULL 时,表示的是使用参考点位于中心,大小 3×3 的核。
erosion = cv2.erode(img,kernel,iterations = 1)
# iteration的值为整数,它的值越高,图像的模糊程度(腐蚀程度)就越高。二者呈正相关关系
plt.imshow(erosion)
plt.show()

6.2 膨胀

膨胀操作和腐蚀操作正好相反,是取核中像素值的最大值代替锚点位置的像素值。

这样会使图像中较亮的区域增大,较暗的区域减小。

如果是一张黑底,白色前景的二值图,就会使白色的前景物体的颜色面积变大,就像膨胀了一样。

In [78]:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3,5))
dilate = cv2.dilate(img,kernel,iterations = 1)
plt.imshow(dilate)
plt.show()

6.3 开运算

开运算(Opening Operation),就是先腐蚀后膨胀的操作。

作用主要为:去除噪声、消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积、在二值化没有处理好的情况下,图像有较多白色噪点,经过开运算可去除。

In [79]:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(10,10))
opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
plt.imshow(opening)
plt.show()

6.4 闭运算

闭运算就是膨胀之后再腐蚀,用于去除黑点。

首先读取原始图片。

In [80]:
img = cv2.imread("./input/bread.jpg") 
img = img[:,:,::-1] 
plt.imshow(img)
plt.show()

接下来对图像进行闭运算处理。

In [81]:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(10,10))
close = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
plt.imshow(close)
plt.show()

7 边缘检测

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识图像中亮度变化明显的点。

首先读取原始图片。

In [82]:
img = cv2.imread("./input/flower.jpg") 
img = img[:,:,::-1] 
plt.imshow(img)
plt.show()

接着用Canny边缘检测处理图像。

In [83]:
# canny(): 边缘检测
img1 = cv2.GaussianBlur(img,(3,3),0)
canny = cv2.Canny(img1, 50, 150)
plt.imshow(canny)
plt.show()

8 直方图处理

8.1 直方图的含义

直方图广泛应用于许多计算机视觉应用中,是图像中像素强度分布的图形表达方式。 

直方图统计了每一个强度值所具有的像素个数。

8.2 绘制直方图

本节尝试绘制直方图。

首先加载原始图片。

In [84]:
img = cv2.imread("./input/sea.jpg") 
img = img[:,:,::-1] 
plt.imshow(img)
plt.show()

接下来绘制原始图片的直方图。

In [85]:
plt.hist(img.ravel(),256,[0,256]);
plt.show()

在其他用法,如进行基于颜色的图像检索时,我们需要的是BGR直方图。

绘制原理类似,统计时使用cv2.calcHist()函数。

In [86]:
color = ('b','g','r')
for i,col in enumerate(color):
    histr = cv2.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color = col)
    plt.xlim([0,256])
plt.show()

8.3 直方图均衡化

直方图均衡化用于提高图像的质量。

In [87]:
img_grey = cv2.imread("./input/sea.jpg",0)
dst = cv2.equalizeHist(img_grey)
plt.imshow(dst)
plt.show()

绘值均衡化后的图像的直方图。

In [88]:
plt.hist(dst.ravel(),256,[0,256]);
plt.show()