【OpenCV】(七)形态学
系列内容:OpenCV概述与环境配置,OpenCV基础知识和绘制图形,图像的算数与位运算,图像视频的加载和显示,图像基本变换,滤波器,形态学,图像轮廓,图像直方图,车辆统计项目,特征检测和匹配,图像查找和拼接,虚拟计算器项目,信用卡识别项目,图像的分割与修复,人脸检测与车牌识别,目标追踪,答题卡识别判卷与文档ocr扫描识别,光流估计
(一)形态学概述
什么是形态学
- 指一系列处理图像形状特征的图像处理技术。
- 形态学的基本思想是利用一种特殊的结构元(本质上就是卷积核)来测量或提取输入图像中相应的形状或特征,以便进一步进行图像分析和目标识别。
- 这些处理方法基本是对二进制图像进行处理, 即黑白图像
- 卷积核决定着图像处理后的效果
形态学常用基本操作有:
- 膨胀和腐蚀
- 开运算
- 闭运算
- 顶帽
- 黑帽
(二)图像全局二值化
二值化: 将图像的每个像素变成两种值, 比如0, 255.
threshold(src, thresh, maxval, type[, dst])
- src 最好是灰度图
- thresh: 阈值
- maxval: 最大值, 最大值不一定是255
- type: 操作类型, 常见操作类型如下图7.1所示:
示例代码,图像全局二值化
#形态学
import cv2
import numpy as np
#导入图片
img=cv2.imread('dog1.jpg')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#返回两个结果,一个是阈值,另一个是处理后的图片
ret,dst=cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
cv2.imshow('dog',np.hstack((gray,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
图像全局二值化,如图7.2所示:
(三)自适应阈值二值化
在前面的部分我们使用是全局阈值,整幅图像采用同一个数作为阈值。当时这种方法并不适应与所有情况,尤其是当同一幅图像上的不同部分具有不同亮度时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)
这种方法需要我们指定六个参数,返回值只有一个。
- Adaptive Method - 指定计算阈值的方法。
- cv2.ADPTIVE_THRESH_MEAN_C:阈值取自相邻区域的平均值
- cv2.ADPTIVE_THRESH_GAUSSIAN_C:阈值取值相邻区域的加权和,权重为一个高斯窗口。
- Block Size - 邻域大小(用来计算阈值的区域大小)。
- C - 这就是一个常数,阈值就等于的平均值或者加权平均值减去这个常数。
示例代码,自适应阈值二值化
(1)图像自适应阈值二值化处理
import cv2
import numpy as np
#导入图片
img=cv2.imread('lena1.png')
# cv2.namedWindow('img',cv2.WINDOW_NORMAL)
# cv2.resizeWindow('img',1920,1080)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
dst=cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,9,0)
cv2.imshow('dog',np.hstack((gray,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
lena图像自适应阈值二值化处理,如图7.3所示:
(2)图像自适应阈值二值化处理对比
不图像自适应阈值二值化处理
#数学题例子
import cv2
import numpy as np
#导入图片
img=cv2.imread('math1.jpg')
cv2.namedWindow('img',cv2.WINDOW_NORMAL)
cv2.resizeWindow('img',640,480)
#二值化操作是对灰度图片操作,把照片变成灰度图像
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#我们发现适应全局阈值二值化,对图片光线不好的地方效果不好
thresh,dst=cv2.threshold(gray,175,255,cv2.THRESH_BINARY)
print(thresh)
cv2.imshow('img',np.hstack((gray,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
数学题例子,不图像自适应阈值二值化处理,如图7.4所示:
数学题,使用图像自适应阈值二值化处理
#数学题例子
import cv2
import numpy as np
#导入图片
img=cv2.imread('math1.jpg')
cv2.namedWindow('img',cv2.WINDOW_NORMAL)
cv2.resizeWindow('img',1080,720)
#二值化操作是对灰度图片操作,把照片变成灰度图像
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#适应全局阈值二值化
dst=cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY_INV,5,0)
cv2.imshow('img',np.hstack((gray,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
数学题用图像自适应阈值二值化处理,如图7.5所示:
(四)腐蚀操作
腐蚀操作也就是用卷积核扫描图像,只不过腐蚀操作的卷积一般都是1,如果卷积核内所有的像素点都是白色,那么锚点即为白色.如图7.6所示:
大部分的时候,腐蚀操作都使用的是全为1的卷积核,如图7.7所示:
erode(src,kernel[,dst[,anchor[,iteration[,borderType[,borderValue]]]]])
iteration是腐蚀操作的迭代函数,次数越多,腐蚀操作执行的次数越多,腐蚀效果越明显
import cv2
import numpy as np
#导入图片
img=cv2.imread('erode1.png')
kernel=np.ones((3,3),np.uint8)
dst=cv2.erode(img,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
卷积核为1时,处理结果如图7.8所示:
卷积核为2时,处理结果如图7.9所示:
(五)获取形态学卷积核
OpenCV提供了获取卷积核的API,用户无需手工创建卷积核。
getStructuringElement(shape, ksize[, anchor])
shape参数用于指定卷积核的形状,它代表卷积核中1的排列形状,而非卷积核的长宽。
可选的卷积核形状包括:
- MORPH_RECT:卷积核中的1呈矩形排列,这是常用的形状。
- MORPH_ELLIPSE:卷积核中的1呈椭圆排列。
- MORPH_CROSS:卷积核中的1呈十字形排列。
1.卷积核形状
(1)椭圆
import cv2
import numpy as np
kernel=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
print(kernel)
卷积核如图7.10所示:
(2)矩形
import cv2
import numpy as np
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
print(kernel)
卷积核如图7.11所示:
(3)十字
import cv2
import numpy as np
kernel=cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
print(kernel)
卷积核如图7.12所示:
2.对字符"i"进行腐蚀
(1)矩形处理
import cv2
import numpy as np
#导入图片
img=cv2.imread('i.png')
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
print(kernel)
#腐蚀
dst=cv2.erode(img,kernel)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
矩形处理结果如图7.13所示:
(2)椭圆处理
import cv2
import numpy as np
#导入图片
img=cv2.imread('i.png')
kernel=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
print(kernel)
#腐蚀
dst=cv2.erode(img,kernel)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
椭圆处理结果如图7.14所示:
(3)十字处理
import cv2
import numpy as np
#导入图片
img=cv2.imread('i.png')
kernel=cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
print(kernel)
#腐蚀
dst=cv2.erode(img,kernel)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
十字处理结果如图7.15所示:
(六)膨胀操作
膨胀是腐蚀的相反操作,基本原理是只要保证卷积核的锚点是非0值,周边无论是0还是非0值,都变成非0值.如图7.16所示:
dilate(img,kernel,iterations=1)
(1)矩形膨胀
import cv2
import numpy as np
#导入图片
img=cv2.imread('i.png')
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
print(kernel)
#腐蚀
dst=cv2.dilate(img,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
矩形膨胀操作结果,如图7.17所示:
(2)椭圆膨胀
import cv2
import numpy as np
#导入图片
img=cv2.imread('i.png')
kernel=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
print(kernel)
#腐蚀
dst=cv2.dilate(img,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
椭圆膨胀操作结果,如图7.18所示:
(3)十字膨胀
import cv2
import numpy as np
#导入图片
img=cv2.imread('i.png')
kernel=cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
print(kernel)
#腐蚀
dst=cv2.dilate(img,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
十字膨胀操作结果,如图7.19所示:
应用:腐蚀之后膨胀还原
import cv2
import numpy as np
#导入图片
img=cv2.imread('erode1.png')
kernel=np.ones((3,3),np.uint8)
#先腐蚀
dst1=cv2.erode(img,kernel,iterations=2)
#再膨胀
dst=cv2.dilate(dst1,kernel,iterations=2)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
腐蚀之后膨胀还原效果,如图7.20所示:
(七)开运算
开运算和闭运算都是腐蚀和膨胀的基本应用
开运算=腐蚀+膨胀
morphologyEx(img, MORPH_OPEN, kernel)
- MORPH_OPEN 表示形态学的开运算
- kernel如果噪点比较多,会选择大一点的kernel,如果噪点比较小,可以选择小点的kernel
实例,处理黑白噪声图片
import cv2
import numpy as np
#导入图片
img=cv2.imread('dogi.png')
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
# #先腐蚀
# dst1=cv2.erode(img,kernel,iterations=2)
# #再膨胀
# dst=cv2.dilate(dst1,kernel,iterations=2)
dst=cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
开运算处理黑白噪声图片,效果如图7.21所示:
(八)闭运算
闭运算=膨胀+腐蚀
morphologyEx(img, MORPH_CLOSE, kernel)
- MORPH_CLOSE 表示形态学的开运算
- kernel如果噪点比较多,会选择大一点的kernel,如果噪点比较小,可以选择小点的kernel
实例:白色内部有噪点
import cv2
import numpy as np
#导入图片
img=cv2.imread('dog1i.png')
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
# #先腐蚀
# dst1=cv2.erode(img,kernel,iterations=2)
# #再膨胀
# dst=cv2.dilate(dst1,kernel,iterations=2)
dst=cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
闭运算处理黑白噪声图片,效果如图7.22所示:
(九)形态学梯度
梯度=原图-腐蚀
腐蚀之后原图边缘变小了,原图-腐蚀可以得到腐蚀掉的部分,即边缘.
morphologyEx(img,cv2.MORPH_GRADIENT,kernel,iterations)
import cv2
import numpy as np
#导入图片
img=cv2.imread('i2.png')
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
# #先腐蚀
# dst1=cv2.erode(img,kernel,iterations=2)
# #再膨胀
# dst=cv2.dilate(dst1,kernel,iterations=2)
dst=cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
一次梯度,效果如图7.23所示:
二次梯度,效果如图7.24所示:
(十)顶帽操作
顶帽=原图-开运算
开运算的效果是去除图像外的噪点,原图-开运算就得到了去掉的噪点.
morphologyEx(img,cv2.MORPH_TOPHAT,kernel,iterations)
import cv2
import numpy as np
#导入图片
img=cv2.imread('dogi.png')
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
dst=cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
顶帽操作,效果如图7.25所示:
(十一)黑帽操作
黑帽=原图-闭运算
闭运算的效果是去除图像内的噪点,原图-闭运算就得到了去掉的噪点.
morphologyEx(img,cv2.MORPH_BLACKHAT,kernel,iterations)
import cv2
import numpy as np
#导入图片
img=cv2.imread('dog1i.png')
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
dst=cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel,iterations=1)
cv2.imshow('img',np.hstack((img,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
黑帽操作,效果如图7.26所示:









