课程一览:
目录
1.opencv中的绘图函数
1.1 线段绘制
1.2 矩形绘制
1.3 圆、椭圆绘制
1.4 多边形绘制
1.5 添加文字
2.图像的几何变换
2.1 图像平移
2.2 图像缩放
2.3 图像旋转
2.4 图像镜像
2.5 仿射变换
2.6 透视变换
3.图像滤波与增强
3.1线性滤波
3.2 非线性滤波
3.3 图像锐化
3.4 直方图均衡化
3.5 Gamma变化
4.图像形态学操作
4.1 图像腐蚀
4.2 图像膨胀
4.3 开运算
4.4 闭运算
4.5 形态学梯度
4.6 顶帽和黑帽
函数:cv2.line(img,pts,color,thickness,linetype)
参数说明:
#=============================绘制线段============================#
# flag = 0
flag = 1
if flag == 1:
# 创建一张黑色的背景图
img=np.zeros((512,512,3), np.uint8)
cv2.imshow("black", img)
cv2.waitKey(0)
cv2.destroyWindow("black")
# 绘制一条线宽为5的线段
cv2.line(img,(0,0),(200,500),(0,0,255),5)
#定义一个变量
winname = 'example'
#将这个名字作为窗口名
cv2.namedWindow(winname)
cv2.imshow(winname, img)
cv2.waitKey(0)
cv2.destroyWindow(winname)
输出结果:
函数:cv2.rectangle(img,prets,color,thickness,linetype)
参数说明:
#=============================矩形线段============================#
# flag = 0
flag = 1
if flag == 1:
img = np.zeros((512,512,3),np.uint8)
#绘制矩形
#新图像可以单独命名,也可以直接原位操作
img_2 = cv2.rectangle(img,(23,23),(234,134),(19,0,240),-1)
# cv2.rectangle(img, (23, 23), (234, 134), (19, 0, 240), -1)
cv2.imshow("rectangle",img_2)
cv2.waitKey(0)
cv2.destroyWindow()
绘制圆
函数:cv2.circle(img,pts,radius,color,thickness,linetype)
参数说明:
#=============================圆绘制============================#
# flag = 0
flag = 1
if flag == 1:
img = np.zeros((512,512,3),np.uint8)
#绘制圆形
#新图像可以单独命名,也可以直接原位操作
#最后一个参数为1则表示不填充
img_2 = cv2.circle(img,(100,100),78,(19,0,240),-1)
# cv2.rectangle(img, (23, 23), (234, 134), (19, 0, 240), -1)
cv2.imshow("rectangle",img_2)
cv2.waitKey(0)
cv2.destroyWindow()
绘制椭圆
函数:cv2.ellipse()
画椭圆需要的参数比较多,请对照后面的代码理解这几个参数。
参数说明:
#=============================椭圆绘制============================#
# flag = 0
flag = 1
if flag == 1:
img = np.zeros((512,512,3),np.uint8)
#绘制椭圆
#新图像可以单独命名,也可以直接原位操作
#最后一个参数为1则表示不填充
cv2.ellipse(img,(200,100),(78,45),0,30,270,(19,0,240),-1)
cv2.ellipse(img, (200, 250), (78, 45), 0, 0, 360, (19, 0, 240), -1)
cv2.ellipse(img, (200, 400), (78, 45), 30, 180, 360, (19, 0, 240), -1)
# cv2.rectangle(img, (23, 23), (234, 134), (19, 0, 240), -1)
cv2.imshow("picture",img)
cv2.waitKey(0)
cv2.destroyWindow()
函数:cv2.polylines(img,pts,isClosed,color,thickness,lineType)
参数说明:
#=============================多边形绘制============================#
# flag = 0
flag = 1
if flag == 1:
img = np.zeros((512, 512, 3), np.uint8)
# 定义四个顶点坐标
pts = np.array([[200, 200], [250, 210], [100, 280], [230, 100]])
print(pts)
# 顶点个数:4,矩阵变成4*1*2维
pts = pts.reshape((-1, 1, 2))
print(pts)
#绘制多边形
cv2.polylines(img, [pts], False, (19, 0, 240))
cv2.polylines(img, [pts], True, (19, 0, 240))
cv2.imshow("picture", img)
cv2.waitKey(0)
cv2.destroyWindow()
函数:cv2.putText()
参数说明:
#=============================添加文字============================#
# flag = 0
flag = 1
if flag == 1:
img = np.zeros((512, 512, 3), np.uint8)
#添加文字
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'OpenCV',(50,200), font, 3,(0,255,255),5)
cv2.imshow("picture", img)
cv2.waitKey(0)
cv2.destroyWindow()
图像平移:
将图像中所有的点按照指定的平移量水平或垂直移动。
变换公式:
设(x0,y0)为原图像上的一点,图像水平平移量为Tx,垂直平移量为Ty,则平移后的点坐标(x1,y1)变为:
x1 = x0 + Tx; y1 = y0 + Ty
用仿射变换来封装函数
仿射变换函数:cv2.warpAffine(src,M,dsize,flags,borderMode,borderValue)
参数说明:
#=============================图像平移============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('img2.png')
# 构造移动矩阵H
# 在x轴方向移动多少距离,在y轴方向移动多少距离
#[1,0,50]表示在x轴上移动50单位;[0,1,25]表示在y轴上平移25单位
H = np.float32([[1, 0, 50], [0, 1, 25]])
#获取图像的行、列
rows, cols = img.shape[:2]
print(img.shape)
print(rows, cols)
# 注意这里rows和cols需要反置,即先列后行
res = cv2.warpAffine(img, H, (2*cols, 2*rows))
cv2.imshow('origin_picture', img)
cv2.imshow('new_picture', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
下采样:缩小图像称为下采样或降采样
上采样:方法图像称为上采样,主要目的是得到更高的分辨率
图像缩放:图像缩放是指图像大小按照指定比例进行方法或缩小
函数:cv2.resize(src,dsize=None,fx,fy,interpolation)
参数说明:
(1)最近邻插值:最简单的一种插值方法,不需要计算,在待求像素的四邻像素中,将距离待求像素最近的邻像素灰度赋给待求像素。(缺点:输出的图像会有锯齿状)
设i+u,j+v(i,j为正整数,u,v为大于零小于1的小数,下同)为待求像素坐标,则待求像素灰度的值f(i+u,j+v)
公式如下:src为原来的坐标,dst为待求的坐标
如图:图像由原来的3×3变为4×4大小的图像
srcWidth为3;dstWidth为4
以待求图的第一个点(0,0)为例:
srcX = (0*(3/4),0*(3/4))=(0,0)= 234
得出的(0,0)就为原图坐标,则待求点的值就用求出的相对应原图位置的值来代替
以待求图的(3,0)点为例:
srcX = ((3*(3/4),(0*(3/4))=(3*0.75,0)=(2.25)=(2,0)=89
采用四舍五入的方法求最近坐标
(2)线性插值:
单线性插值:
双线性插值:
双线性插值又叫一阶插值法,它要经过三次插值才能获得最终结果,是对最近邻插值法的一种改进,先对两水平方向进行一阶线性插值,然后再在垂直方向进行一阶线性插值。
#=============================图像缩放============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('img2.png')
# 方法一:通过设置缩放比例,来对图像进行放大或缩小
#缩放因子设置为2,放大两倍
res1 = cv2.resize(img, None, fx=2, fy=2,
interpolation=cv2.INTER_CUBIC)
height, width = img.shape[:2]
# 方法二:直接设置图像的大小,不需要缩放因子
#cv2.INTER_NEAREST(最近邻插值) cv2.INTER_AREA (区域插值) cv2.INTER_CUBIC(三次样条插值) cv2.INTER_LANCZOS4(Lanczos插值)
res2 = cv2.resize(img, (int(0.8*width), int(0.8*height)),interpolation=cv2.INTER_LANCZOS4)
cv2.imshow('origin_picture', img)
#|cv2.imshow('res1', res1)
cv2.imshow('res2', res2)
cv2.waitKey(0)
cv2.destroyAllWindows()
图像旋转:以图像的中心为原点,旋转一定的角度,也就是将图像上的所有像素都旋转一个相同的角度。旋转都图像的大小一般会改变,即可以把转出显示区域的图像裁取,或扩大图像范围来显示所有的图像。图像的旋转变换也可以用矩阵变换来表示。
设点逆时针旋转角后的对应点为
那么,旋转后点的坐标是:
利用上述方法进行图像旋转是需要注意以下两点:
(1)图像旋转之前,为了避免信息的丢失,一定要有坐标平移。
(2)图像旋转之后,会出现许多空洞点。对这些空洞点必须进行填充处理,否则画面效果不好,一般也称这种操作为插值处理。
图像的旋转使用仿射变换函数封装:
变换矩阵函数:cv2.getRotationMatrix2D(center,angle,scale)
参数说明:
#=============================图像旋转============================#
# flag = 0
flag = 1
if flag == 1:
img=cv2.imread('img2.png',1)
rows,cols=img.shape[:2]
#参数1:旋转中心,参数2:旋转角度,参数3:缩放因子
#参数3正为逆时针,负值为正时针
#创建一个仿射矩阵
M=cv2.getRotationMatrix2D((cols/2,rows/2),45,1,)
print(M)
#第三个参数是输出图像的尺寸中心
dst=cv2.warpAffine(img,M,(cols,rows))
#dst=cv2.warpAffine(img,M,(cols,rows),borderValue=(255,255,255))
while(1):
cv2.imshow('img', img)
cv2.imshow('img1',dst)
#0xFF==27 ESC
if cv2.waitKey(1)&0xFF==27:
break
cv2.destroyAllWindows()
仿射变换的作用:通过仿射变换对图片进行旋转、平移、缩放等操作以达到数据增强的效果。
线性变换从几何直观上来看有两个要点:变换之前是直线、变换之后依然是直线;直线的比例保持不变。
以下变换分别为:平移、旋转、同比例缩放、不同比例缩放、镜像、剪切
仿射变换:平移、旋转、缩放、剪切、反射
函数:
仿射变换的函数原型如下:
M = cv2.getAffineTransform(pos1,pos2)
pos1表示变换前的位置,三个点
pos2表示变换后的位置,三个点
#=============================仿射变换============================#
# flag = 0
flag = 1
if flag == 1:
src = cv2.imread('bird.png')
# 获取图像大小
rows, cols = src.shape[:2]
# 设置图像仿射变换矩阵
pos1 = np.float32([[50, 50], [200, 50], [50, 200]])
pos2 = np.float32([[10, 100], [200, 50], [100, 250]])
M = cv2.getAffineTransform(pos1, pos2)
print(M)
# 图像仿射变换
result = cv2.warpAffine(src, M, (2 * cols, 2 * rows))
# 显示图像
cv2.imshow("original", src)
cv2.imshow("result", result)
# 等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()
透视变换:本质是将图像投影到一个新的视屏面
函数:
M = cv2.getPerspectiveTransform(pos1,pos2)
参数说明:
cv2.warpPerspective(src,M,(cols,rows))
参数说明:
# =============================透视变换============================#
# flag = 0
flag = 1
if flag == 1:
#读取图片
src = cv2.imread('bird.png')
#获取图像大小
rows, cols = src.shape[:2]
#设置图像透视变换矩阵
pos1 = np.float32([[114, 82], [287, 156],
[8, 100], [143, 177]])
pos2 = np.float32([[0, 0], [188, 0],
[0, 262], [188, 262]])
M = cv2.getPerspectiveTransform(pos1, pos2)
#图像透视变换
result = cv2.warpPerspective(src, M, (2*cols,2*rows))
#显示图像
cv2.imshow("original", src)
cv2.imshow("result", result)
#等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()
2.7 小结
例1:利用图像的变换对文档进行矫正
# =============================图像矫正============================#
# flag = 0
flag = 1
if flag == 1:
#读取图片
src = cv2.imread('paper.png')
#获取图像大小
rows, cols = src.shape[:2]
#将源图像高斯模糊,去除噪声
img = cv2.GaussianBlur(src, (3,3), 0)
#进行灰度化处理
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#边缘检测(检测出图像的边缘信息)
edges = cv2.Canny(gray,50,250,apertureSize = 3)
cv2.imwrite("canny.jpg", edges)
cv2.imshow("canny", edges)
#通过霍夫变换得到A4纸边缘,这是可以在二值图像中检测直线的方法
lines = cv2.HoughLinesP(edges,1,np.pi/180,50,minLineLength=90,maxLineGap=10)
print(lines)
#下面输出的四个点分别为四个顶点
for x1,y1,x2,y2 in lines[0]:
print(x1,y1)
print(x2,y2)
for x3,y3,x4,y4 in lines[1]:
print(x3,y3)
print(x4,y4)
#绘制边缘
for x1,y1,x2,y2 in lines[0]:
cv2.line(gray, (x1,y1), (x2,y2), (0,0,255), 1)
#根据四个顶点设置图像透视变换矩阵
pos1 = np.float32([[114, 82], [287, 156], [8, 322], [216, 333]])
pos2 = np.float32([[0, 0], [188, 0], [0, 262], [188, 262]])
M = cv2.getPerspectiveTransform(pos1, pos2)
# pos1 = np.float32([[114, 82], [287, 156], [8, 322]])
# pos2 = np.float32([[0, 0], [188, 0], [0, 262]])
# M = cv2.getAffineTransform(pos1,pos2)
print(M)
#图像仿射变换
#result = cv2.warpAffine(src, M, (2*cols, 2*rows))
#图像透视变换
result = cv2.warpPerspective(src, M, (190, 272))
#显示图像
cv2.imshow("original", src)
cv2.imshow("result", result)
cv2.imshow("gray", gray)
#等待显示
cv2.waitKey(0)
cv2.destroyAllWindows()
例2:利用几何变换,对图像进行扩增
# =============================图数扩增============================#
# flag = 0
flag = 1
if flag == 1:
# 读取图片
img = cv2.imread('test2.png')
image = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 图像平移矩阵
M = np.float32([[1, 0, 80], [0, 1, 30]])
rows, cols = image.shape[:2]
img1 = cv2.warpAffine(image, M, (cols, rows))
# 图像缩小
img2 = cv2.resize(image, (200, 100))
# 图像放大
img3 = cv2.resize(image, None, fx=1.1, fy=1.1)
# 绕图像的中心旋转
# 源图像的高、宽 以及通道数
rows, cols, channel = image.shape
# 函数参数:旋转中心 旋转度数 scale
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 30, 1)
# 函数参数:原始图像 旋转参数 元素图像宽高
img4 = cv2.warpAffine(image, M, (cols, rows))
# 图像翻转
img5 = cv2.flip(image, 0) # 参数=0以X轴为对称轴翻转
img6 = cv2.flip(image, 1) # 参数>0以Y轴为对称轴翻转
# 图像的仿射
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
M = cv2.getAffineTransform(pts1, pts2)
img7 = cv2.warpAffine(image, M, (rows, cols))
# 图像的透射
pts1 = np.float32([[56, 65], [238, 52], [28, 237], [239, 240]])
pts2 = np.float32([[0, 0], [200, 0], [0, 200], [200, 200]])
M = cv2.getPerspectiveTransform(pts1, pts2)
img8 = cv2.warpPerspective(image, M, (200, 200))
# 循环显示图形
titles = ['source', 'shift', 'reduction', 'enlarge', 'rotation', 'flipX', 'flipY', 'affine', 'transmission']
images = [image, img1, img2, img3, img4, img5, img6, img7, img8]
for i in range(9):
plt.subplot(3, 3, i + 1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
邻域算子:利用给定像素周围的像素值决定此像素的最终输出值得一种算子。
线性滤波:一种常用的邻域算子,像素输出取决于输入像素的加权和。(卷积操作)
(1)方框滤波
方框滤波被封装在一个名为boxFilter的函数中,即boxFilter函数的作用是使用方框滤波器来模糊一张图片,从src输入,从dst输出。
方框滤波核:
normalize = True 与均值滤波相同,归一化
normalize = False 很容易发生溢出
函数:cv2.boxFilter(src,depth,ksize,normalize)
参数说明:
# =============================方框滤波============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('girl2.png',cv2.IMREAD_UNCHANGED)
r = cv2.boxFilter(img, -1 , (7,7) , normalize = 1)
d = cv2.boxFilter(img, -1 , (3,3) , normalize = 0)
cv2.namedWindow('img',cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('r',cv2.WINDOW_AUTOSIZE)
cv2.namedWindow('d',cv2.WINDOW_AUTOSIZE)
cv2.imshow('img',img)
cv2.imshow('r',r)
cv2.imshow('d',d)
cv2.waitKey(0)
cv2.destroyAllWindows()
(2)均值滤波
均值滤波是一种最简单的滤波处理,它取的是卷积核区域内元素的均值,用cv2.blur()实现,如3×3的卷积核:
函数:cv2.blur(src,ksize)
参数说明:
# =============================均值滤波============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('image/opencv.png')
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
blur = cv2.blur(img,(7,7 ))
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.subplot(122),plt.imshow(blur),plt.title('Blurred')
#不显示坐标
plt.xticks([]), plt.yticks([])
plt.show()
(3)高斯滤波
高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛用于图像处理的减噪过程。高斯滤波的卷积核权重并不相同,中间像素点权重最高,越远离中心的像素权重越小。(其原理是一个二维高斯函数)
高斯滤波相比均值滤波效率要慢,但可以有效消除高斯噪声,能保留更多的图像细节,所以经常被称为最有用的滤波器。
函数:cv2.Guassianblur(src,ksize,std) ,表示进行高斯滤波
参数说明:
# =============================高斯滤波============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('image/median.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
blur = cv2.GaussianBlur(img, (7, 7), 7)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()
可以看到噪声消除了一些
(1)中值滤波
中值滤波是一种非线性滤波,是用像素点邻域灰度值的中值代替该点的灰度值,中值滤波可以去除椒盐噪声和斑点噪声。
函数:cv2.medianBlur(img,ksize)
参数说明:
# =============================中值滤波============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('image/median.png')
median = cv2.medianBlur(img, 3)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(median), plt.title('median')
plt.xticks([]), plt.yticks([])
plt.show()
(2)双边滤波
双边滤波是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折中处理,同时考虑空间与信息和灰度相似性,达到保边去噪的目的,具有简单、非迭代、局部处理的特点。
函数:cv2.bilateralFilter(src = image,d,sigmaColor,sigmaSpace)
参数说明:
# =============================双边滤波============================#
# flag = 0
flag = 1
if flag == 1:
'''
关于2个sigma参数:
简单起见,可以令2个sigma的值相等;
如果他们很小(小于10),那么滤波器几乎没有什么效果;
如果他们很大(大于150),那么滤波器的效果会很强,使图像显得非常卡通化;
关于参数d:
过大的滤波器(d>5)执行效率低。 对于实时应用,建议取d=5;
对于需要过滤严重噪声的离线应用,可取d=9;
d>0时,由d指定邻域直径;
d<=0时,d会自动由sigmaSpace的值确定,且d与sigmaSpace成正比;
'''
img = cv2.imread('image/bilateral.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
blur = cv2.bilateralFilter(img, -1, 15, 10)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()
加大参数3和参数4的值可以更好的去除噪声
# =============================图像锐化============================#
# flag = 0
flag = 1
if flag == 1:
def custom_blur_demo(image):
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) #锐化
dst = cv2.filter2D(image, -1, kernel=kernel)
cv2.imshow("custom_blur_demo", dst)
src = cv2.imread("./image/sharpen.png")
cv2.namedWindow("input image", cv2.WINDOW_AUTOSIZE)
cv2.imshow("input image", src)
custom_blur_demo(src)
cv2.waitKey(0)
cv2.destroyAllWindows()
(1)灰度图直方图均衡化
目的:直方图均衡化是将原图通过某种变换,得到一幅灰度直方图为均匀分布的新图像的方法
直方图均衡化方法的基本思想是对在图像中像素个数多的灰度级进行展宽,而对像素个数少的灰度级进行缩减。从而达到清晰图像的目的。
函数:cv2.equalizeHist(img)
参数说明:
步骤:
# =============================直方图均衡化============================#
# flag = 0
flag = 1
if flag == 1:
#直接读为灰度图像
img = cv2.imread('./image/dark.png',0)
cv2.imshow("dark",img)
cv2.waitKey(0)
#调用cv2.equalizeHist函数进行直方图均衡化
img_equal = cv2.equalizeHist(img)
cv2.imshow("img_equal",img_equal)
cv2.waitKey(0)
cv2.destroyAllWindows()
(2)局部直方图均衡化
# flag = 0
flag = 1
if flag == 1:
#局部直方图均衡化
#直接读为灰度图像
img = cv2.imread('./image/dark.png',0)
cv2.imshow("dark",img)
cv2.waitKey(0)
#调用cv2.createCLAHE函数进行局部直方图均衡化
clahe = cv2.createCLAHE(clipLimit=2,tileGridSize=(30,30))
cl1 = clahe.apply(img)
cv2.imshow("img_equal",cl1)
cv2.waitKey(0)
cv2.destroyAllWindows()
(3)彩色图直方图均衡化
# flag = 0
flag = 1
if flag == 1:
#彩图直方图均衡化
img = cv2.imread("./image/dark1.jpg")
cv2.imshow("src", img)
# 彩色图像均衡化,需要分解通道 对每一个通道均衡化
(b, g, r) = cv2.split(img)
bH = cv2.equalizeHist(b)
gH = cv2.equalizeHist(g)
rH = cv2.equalizeHist(r)
# 合并每一个通道
result = cv2.merge((bH, gH, rH))
cv2.imshow("dst", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
亮度提升,但提升效果夸张
Gamma变换是对输入图像灰度值进行的非线性操作,使输出图像灰度值与输入图像灰度值呈指数关系。
目的:Gamma变换就是用来图像增强,其提升了暗部细节,通过非线性变换,让图像从曝光强度的线性响应变得更接近人眼感受的响应,即将漂白(相机曝光)或过暗(曝光不足)的图片,进行矫正。
Gamma > 1,图像变亮
Gamma < 1,图形变暗
# =============================Gamma变换============================#
# flag = 0
flag = 1
if flag == 1:
img=cv2.imread('./image/dark1.jpg')
def adjust_gamma(image, gamma=1.0):
invGamma = 1.0/gamma
table = []
for i in range(256):
table.append(((i / 255.0) ** invGamma) * 255)
table = np.array(table).astype("uint8")
print(table)
return cv2.LUT(image, table)
img_gamma = adjust_gamma(img, 0.8)
#print(img_gamma)
cv2.imshow("img",img)
cv2.imshow("img_gamma",img_gamma)
cv2.waitKey(0)
cv2.destroyAllWindows()
形态学是图像处理中应用最为广泛的技术之一,主要用于从图像中提取对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质的形状特征,如边界和连通区域等。
结构元素:
设有两幅图像B,X。
若X是被处理的对象,而B是用来处理X的,则称B为结构元素,又被形象的称作刷子。
结构原色通常都是一些比较小的图像。
腐蚀和膨胀
图像的膨胀(Dilation)和腐蚀(Erosion)是两种基本的形态学运算,其中膨胀类似于“领域扩张”,将图像中的白色部分(领域)进行扩张,其运行结果图比原图的白色区域更大;腐蚀类似于“领域被蚕食”,将图像中白色部分进行缩减细化,其运行结果图比原图的白色区域更小。
(1)灰度图像的腐蚀操作
腐蚀运算符“—”,其定义如下:
该公式表示图像A用卷积模板B来进行腐蚀处理,通过模板B与图像A进行卷积计算,得出B覆盖区域的像素点最小值,并用这个最小值来代替参考点的像素值。
(2)二值图像的腐蚀操作
把结构元素B平移a后得到Ba,若Ba包含于X,我们记下这个a点,所有满足上述条件的a点组成的集合称作X被B腐蚀的结果。如图所示。
其中X是被处理的对象,B是结构元素。对于任意一个在阴影部分的点a,Ba包含于X,所以X被B腐蚀的结果就是那个阴影部分。阴影部分在X的范围之内,且比X小,就像X被剥掉一层似的。
函数:cv2.erode(src,element,anchor,iterations)
参数说明:
# =============================图像腐蚀============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('./image/morphology.png')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
kernel = np.ones((3,3),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(erosion),plt.title('erosion')
plt.xticks([]), plt.yticks([])
plt.show()
膨胀(dilation)可以看做是腐蚀的对偶运算。
其定义是:把结构元素B平移a后得到Ba,若Ba击中(即B的任何一个点碰到X)X,我们记下这个a点。所有满足上述条件的a点组成的集合称作X被B膨胀的结果。如图所示。
其中X是被处理的对象,B是结构元素,对于任意一个在阴影部分的点a,Ba击中X,所以X被B膨胀的结果就是那个阴影部分。阴影部分包括X的所有范围,就像X膨胀了一圈似的。
# =============================图像膨胀============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('./image/morphology.png')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
#kernel = np.ones((3,),np.uint8)
# kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
# kernel = np.ones((5,5),np.uint8)
# kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (7,7))
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
dilation = cv2.dilate(img,kernel,iterations = 1)
plt.subplot(121),plt.imshow(img),plt.title('opening')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dilation),plt.title('dilation')
plt.xticks([]), plt.yticks([])
plt.show()
开运算 = 先腐蚀运算,再膨胀运算(看上去把细微连在一起的两块目标分开了),开运算的效果图如下所示:
开运算总结:
# =============================开运算(去除噪音)============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('./image/open.png')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
#kernel = np.ones((5,5),np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
opening = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(opening),plt.title('opening')
plt.xticks([]), plt.yticks([])
plt.show()
可以看到,噪声被去掉了
闭运算 = 先膨胀运算,再腐蚀运算(看上去将两个细微连接的图块封闭在一起),闭运算的效果图如图所示:
# =============================开运算(去除噪音)============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('./image/close.png')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
#kernel = np.ones((5,5),np.uint8)
kernel = np.ones((7,7),np.uint8)
closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(closing),plt.title('closing')
plt.xticks([]), plt.yticks([])
plt.show()
闭运算总结:
形态学梯度:
# =============================形态学梯度============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('./image/morphology.png')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
kernel = np.ones((3,3),np.uint8)
gradient = cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(gradient),plt.title('gradient')
plt.xticks([]), plt.yticks([])
plt.show()
顶帽(Top Hat):原图像与开运算图的差值,突出原图像中比周围亮的区域。
黑帽(Black Hat):闭操作图像与原图像的差值,突出原图像比周围暗的区域。
顶帽代码:
# =============================顶帽============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('./image/morphology.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
kernel = np.ones((9, 9), np.uint8)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(tophat), plt.title('tophat')
plt.xticks([]), plt.yticks([])
plt.show()
黑帽代码:
# =============================黑帽============================#
# flag = 0
flag = 1
if flag == 1:
img = cv2.imread('./image/morphology.png')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
kernel = np.ones((9,9),np.uint8)
tophat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(tophat),plt.title('tophat')
plt.xticks([]), plt.yticks([])
plt.show()