须知:图像是RGB,opencv 处理 BGR
定义图像显示函数
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyWindow(name)
显示图像
# 原图
img = cv2.imread('cat.jpg')
cv_show('img', img)
# 灰度图
img = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
cv_show('img', img)
# 保存
cv2.imwrite('saveimg.jpg', img)
显示视频
vc = cv2.VideoCapture('test.mp4')
if vc.isOpened():
open, frame = vc.read()
else:
open = False
while open:
ret, frame = vc.read()
if frame is None:
break
if ret == True:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('reslut', gray)
if cv2.waitKey(34) & 0xFF == 27:
break
vc.release()
cv2.destroyWindow('reslut')
RoI 区域
感兴趣区域,截取图像
img = cv2.imread('cat.jpg')
cat = img[0:200, 0:200] # x为0到200, y为0到200
cv_show('cat', cat)
颜色通道提取
b, g, r = cv2.split(img)
cv_show('b', b)
cv_show('g', g)
cv_show('r', r)
还原
img = cv2.merge((b, g, r))
阈值
ret, dst = cv2.threshold(src, thresh, maxval, type)
ret, 输出 = cv2.threshold(输入, 阈值, maxval, 类型)
maxval 由类型决定;
类型:
cv2.THRESH_BINARY 超过阈值取最大值,否则置0
cv2.THRESH_BINARY_INV 与上相反
cv2.THRESH_TRUNC 超过阈值取阈值,否则不变
cv2.THRESH_TOZERO 超过阈值不变,否则置0
cv2.THRESH_TOZERO_INV 与上相反
import cv2
import matplotlib.pyplot as plt
ret, dst1 = cv2.threshold(src, 127, 255, cv2.THRESH_BINARY)
ret, dst2 = cv2.threshold(src, 127, 255, cv2.THRESH_BINARY_INV)
ret, dst3 = cv2.threshold(src, 127, 255, cv2.THRESH_TRUNC)
ret, dst4 = cv2.threshold(src, 127, 255, cv2.THRESH_TOZERO)
ret, dst5 = cv2.threshold(src, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['original', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [src, dst1, dst2, dst3, dst4, dst5]
for i in range(6):
plt.subplot(2, 3, i+1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
# 灰度
src = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
BINARY:亮点全为白(>127 255),暗点全为黑( <127 0)
BINARY_INV:与上相反(>127 0, <127 255)
TRUNC:降低白点的亮度,暗点保留 (>127 127 , <127 不变)
TOZERO:(>127 不变,<127 0)
TOZERO_INV:(>127 0,<127 不变)
平滑
消除噪点
import cv2
# import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('cat.jpg')
#
# # 均值滤波、方框滤波、高斯滤波、中值滤波
blur = cv2.blur(img, (3, 3))
box = cv2.boxFilter(img, -1, (3, 3), normalize=True)
gaussian = cv2.GaussianBlur(img, (5, 5), 1)
median = cv2.medianBlur(img, 5)
#
# res = np.hstack((blur, box, gaussian, median))
# cv_show('666', res)
titles = ['blur', 'box', 'gaussian', 'median']
images = [blur, box, gaussian, median]
s = [[1, 1], [1, 2], [2, 2], [2, 2], [2, 3], [2, 3], [2, 4], [2, 4], [3, 3]]
for i in range(len(images)):
row, col = s[len(images)-1]
plt.subplot(row, col, i+1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
均值滤波,使用 3 × 3 3\times 3 3×3 滤波器,滤波器的值相同,内积取平均值。
方框滤波,与均值滤波类似
高斯滤波,使用 5 × 5 5\times 5 5×5 滤波器,滤波器中心点为均值,均值最大,远离均值越小。
形态学操作
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('cat.jpg')
# 腐蚀(去刺),膨胀
kernel = np.ones((3, 3), np.uint8) # 3 x 3 滤波器全为1
erosion = cv2.erode(img, kernel, iterations=1) # 腐蚀1次
dilate = cv2.dilate(img, kernel, iterations=1) # 膨胀1次
# 开运算(去刺膨胀),闭运算(膨胀去刺)
kernel2 = np.ones((5, 5), np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel2)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel2)
# 梯度运算(膨胀-腐蚀)
kernel3 = np.ones((7, 7), np.uint8)
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
# 顶帽(输入-开运算),黑帽(闭运算-输入)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
titles = ['original', 'erosion', 'dilate', 'opening=\nerosion+dilate', 'closing=\ndilate+erosion', 'gradient=\ndialte-erosion', 'tophat=\nimg-opening', 'blackhat=\nclosing-img']
images = [img, erosion, dilate, opening, closing, gradient, tophat, blackhat]
for i in range(8):
plt.subplot(2, 4, i+1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
边缘检测
sobel
水平方向梯度:右边 - 左边
G
x
=
[
−
1
0
+
1
−
2
0
+
2
−
1
0
+
1
]
∗
A
G_x=\begin{bmatrix} -1 & 0&+1 \\ -2 & 0& +2\\ -1 & 0 &+1\end{bmatrix}*A
Gx=⎣⎡−1−2−1000+1+2+1⎦⎤∗A
垂直方向梯度:下边 - 上边
G
y
=
[
−
1
−
2
−
1
0
0
0
+
1
+
2
+
1
]
∗
A
G_y=\begin{bmatrix} -1 & -2&-1 \\ 0 & 0& 0\\ +1 & +2 &+1\end{bmatrix}*A
Gy=⎣⎡−10+1−20+2−10+1⎦⎤∗A
#sobel
import matplotlib.pyplot as plt
img = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3) # 水平梯度
sobelx_ = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3) # 垂直梯度
sobely_ = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0) # 0为偏置
titles = ['original', 'sobelx', 'sobelx_abs', 'sobely', 'sobely_abs', 'sobelxy']
images = [img, sobelx, sobelx_, sobely, sobely_, sobelxy]
s = [[1, 1], [1, 2], [2, 2], [2, 2], [2, 3], [2, 3], [2, 4], [2, 4], [3, 3]]
for i in range(len(images)):
row, col = s[len(images)-1]
plt.subplot(row, col, i+1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
scharr
水平方向梯度:右边 - 左边
G
x
=
[
−
3
0
+
3
−
10
0
+
10
−
3
0
+
3
]
∗
A
G_x=\begin{bmatrix} -3 & 0&+3 \\ -10 & 0& +10\\ -3 & 0 &+3\end{bmatrix}*A
Gx=⎣⎡−3−10−3000+3+10+3⎦⎤∗A
垂直方向梯度:下边 - 上边
G
y
=
[
−
3
−
10
−
3
0
0
0
+
3
+
10
+
3
]
∗
A
G_y=\begin{bmatrix} -3 & -10&-3 \\ 0 & 0& 0\\ +3 & +10 &+3\end{bmatrix}*A
Gy=⎣⎡−30+3−100+10−30+3⎦⎤∗A
scharr 的值比 sobel 更大,计算大差异也更大,因此 scharr 对边缘更敏感。
import matplotlib.pyplot as plt
img = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
scharrx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3) # 水平梯度
scharrx_ = cv2.convertScaleAbs(scharrx)
scharry = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3) # 垂直梯度
scharry_ = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0) # 0为偏置
titles = ['original', 'scharrx', 'scharrx_abs', 'scharry', 'scharry_abs', 'scharrxy']
images = [img, scharrx, scharrx_, scharry, scharry_, scharrxy]
s = [[1, 1], [1, 2], [2, 2], [2, 2], [2, 3], [2, 3], [2, 4], [2, 4], [3, 3]]
for i in range(len(images)):
row, col = s[len(images)-1]
plt.subplot(row, col, i+1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
sobel 和 scharr 对比:
拉普拉斯算子
用中心点周围的四个点,如果它不是边缘的话,结果为零,即梯度为零。拉普拉斯算子缺点是对噪声比较敏感。
G
x
=
[
0
1
0
1
−
4
1
0
1
0
]
G_x=\begin{bmatrix} 0 & 1&0 \\ 1 & -4& 1\\ 0 & 1 &0\end{bmatrix}
Gx=⎣⎡0101−41010⎦⎤
laplacian = cv2.Laplacian(img, cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
canny
- 高斯滤波(归一化高斯滤波)
- 计算每个像素的梯度强度 G = G x 2 + G y 2 G = \sqrt{Gx^2+G_y^2} G=Gx2+Gy2 和方向 θ = arctan G X G y \theta = \arctan \frac{G_X}{G_y} θ=arctanGyGX
- 非极大值抑制(NMS)
- 双阈值
- 抑制孤立的弱边缘
G
x
=
S
x
∗
A
G_x=S_x * A
Gx=Sx∗A
G
y
=
S
y
∗
A
G_y=S_y * A
Gy=Sy∗A
S x = [ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] ∗ A S_x=\begin{bmatrix} -1 & 0&+1 \\ -2 & 0& +2\\ -1 & 0 &+1\end{bmatrix}*A Sx=⎣⎡−1−2−1000+1+2+1⎦⎤∗A
S y = [ + 1 + 2 + 1 0 0 0 − 1 − 2 − 1 ] ∗ A S_y=\begin{bmatrix} +1 & +2 &+1\\ 0 & 0& 0\\ -1 & -2&-1 \end{bmatrix}*A Sy=⎣⎡+10−1+20−2+10−1⎦⎤∗A
非极大值抑制
从左到右, c a b
如果在a点处不是边缘,则 a - c 约等于 b - a
如果在a点处是边缘,则 a - c > b - a ,或者 a - c < b - a
双阈值
梯度值大于最大值,为边界
梯度值介于最小值和最大值:如果与边界相连则为边界,否则舍弃
梯度值小于最小值,舍弃
import matplotlib.pyplot as plt
img = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
v1 = cv2.Canny(img, 80, 150)
v2 = cv2.Canny(img, 50, 150)
v3 = cv2.Canny(img, 30, 150)
v4 = cv2.Canny(img, 80, 120)
v5 = cv2.Canny(img, 50, 100)
v6 = cv2.Canny(img, 30, 80)
v7 = cv2.Canny(img, 80, 100)
v8 = cv2.Canny(img, 50, 80)
v9 = cv2.Canny(img, 30, 50)
titles = ['80, 150', '50, 150', '30, 150', '80, 120', '50, 100', '30, 80', '80, 100', '50, 80', '30, 50']
images = [v1, v2, v3, v4, v5, v6, v7, v8, v9]
s = [[1, 1], [1, 2], [2, 2], [2, 2], [2, 3], [2, 3], [2, 4], [2, 4], [3, 3]]
for i in range(len(images)):
row, col = s[len(images)-1]
plt.subplot(row, col, i+1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
轮廓
cv2.findContour(img, mode, method)
mode:轮廓检索模式
RETR_EXTERNAL : 只检索最外面的轮廓
RETR_LIST :检索所有轮廓并保存到链表
RETR_CCOMP :检索所有轮廓,并将他们组织为两层,顶层是各部分的外部边界,第二层是空洞的边界
RETR_TREE :检索所有轮廓,并重构嵌套轮廓的整个层次
method :轮廓逼近方法
CHAIN_APPROX_NONE : 以 Freeman 连链码的方式输出轮廓,所有其他方法输出多边形(顶点顺序)
CHAIN_APPROC_SIMPLE : 保留角点,去掉线
img = cv2.imread('5.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2) # -1 所有的轮廓,(0, 0, 255) BGR 红色,2 线条宽度
cv_show('res', res)
-1所有轮廓
![](https://i-blog.csdnimg.cn/blog_migrate/a5d9c44df3a98d55e6b08793a7f07ed8.png)
0 只有一个轮廓
1
轮廓特征
cnt = contours[0]
cv2.contourArea(cnt)
cv2.arcLength(cnt, True)