OpenCV版本:4.6.0.66
算法实现思路:
- 颜色识别(红色)
- 形态学去噪
- 轮廓检测
- 多边形拟合
- 透视矫正
代码实现:
-
import cv2
-
import numpy
as np
-
-
-
# 可视化
-
def
img_show(
name, img):
-
cv2.namedWindow(name,
0)
-
cv2.resizeWindow(name,
1000,
500)
-
cv2.imshow(name, img)
-
cv2.waitKey(
0)
-
-
-
def
color_warped(
path):
-
img = cv2.imread(path)
-
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
-
-
# 颜色识别(红色),过滤红色区域
-
lower_red1 = np.array([
0,
43,
46])
# 红色阈值下界
-
higher_red1 = np.array([
10,
255,
255])
# 红色阈值上界
-
mask_red1 = cv2.inRange(hsv, lower_red1, higher_red1)
-
lower_red2 = np.array([
156,
43,
46])
# 红色阈值下界
-
higher_red2 = np.array([
180,
255,
255])
# 红色阈值上界
-
mask_red2 = cv2.inRange(hsv, lower_red2, higher_red2)
-
mask_red = cv2.add(mask_red1, mask_red2)
# 拼接过滤后的mask
-
img_show(
'mask_red', mask_red)
-
-
# 形态学去噪,cv2.MORPH_OPEN先腐蚀再膨胀,cv2.MORPH_CLOSE先膨胀再腐蚀
-
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (
5,
5))
-
mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel, iterations=
1)
-
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (
7,
7))
-
mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel, iterations=
3)
-
img_show(
'mask_red', mask_red)
-
-
# 轮廓检测,找出线条的轮廓
-
draw_cnt = img.copy()
-
cnts = cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[
0]
-
cv2.drawContours(draw_cnt, cnts, -
1, (
0,
255,
0),
2)
-
img_show(
'draw_cnt', draw_cnt)
-
-
# 四边形拟合,找到相应的的顶点
-
draw_approx = img.copy()
-
point1, point2 =
list(),
list()
-
for cnt
in cnts:
-
for epsilon
in
range(
50):
-
approx = cv2.approxPolyDP(cnt, epsilon,
True)
-
if
len(approx) ==
4:
-
break
-
cv2.polylines(draw_approx, [approx],
True, (
0,
0,
255),
2)
# 绘制边
-
for i
in approx:
-
cv2.circle(draw_approx, i[
0],
6, (
0,
0,
0), -
1)
# 绘制顶点
-
-
approx = [i[
0]
for i
in approx.tolist()]
-
approx =
sorted(approx, key=
lambda k: k[
1], reverse=
False)
# 按y坐标排序,升序
-
-
point1.extend(approx[:
2])
# 存放上顶点坐标
-
point2.extend(approx[
2:])
# 存放下顶点坐标
-
point1.sort(key=
lambda k: k[
0], reverse=
False)
# 按x坐标排序,升序
-
point2.sort(key=
lambda k: k[
0], reverse=
False)
-
img_show(
'draw_approx', draw_approx)
-
-
# 透视矫正目标区域
-
w, h =
900,
300
-
rect = [point1[
0], point1[-
1], point2[-
1], point2[
0]]
# 顺序为第一个四边形的左上,第四个四边形的右上,第四个四边形的右下,第一个四边形的左下
-
pts1 = np.array(rect, dtype=
"float32")
-
pts2 = np.array([rect[
0], [rect[
0][
0] + w, rect[
0][
1]],
-
[rect[
0][
0] + w, rect[
0][
1] + h], [rect[
0][
0], rect[
0][
1] + h]], dtype=
"float32")
-
M = cv2.getPerspectiveTransform(pts1, pts2)
# 变换矩阵
-
img_warped = cv2.warpPerspective(img, M, (
1500,
500))
# 透视变换
-
img_show(
'img_warped1', img_warped)
-
-
img_warped = img_warped[rect[
0][
1]: rect[
0][
1] + h, rect[
0][
0]: rect[
0][
0] + w]
# 抠出变换后的区域
-
img_show(
'img_warped2', img_warped)
-
-
-
if __name__ ==
'__main__':
-
path =
'data/picture/18.jpg'
-
color_warped(path)
原图:
颜色识别(红色)
-
img = cv2.imread(path)
-
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
颜色识别使用的通道是HSV通道。使用HSV通道识别颜色会比使用BGR通道更容易区分。H表示Hue(色调、色相,取值:0-180),S表示Saturation(饱和度、色彩纯净度,取值:0-255),V表示Value(亮度,取值:0-255),详情可见:HSV颜色空间。各颜色的取值范围如下:
可见红色的取值范围有两个,大部分情况下这两个范围刚好是互补的,可根据实际情况调整。
-
# 颜色识别(红色),过滤红色区域
-
lower_red1 = np.array([
0,
43,
46])
# 红色阈值下界
-
higher_red1 = np.array([
10,
255,
255])
# 红色阈值上界
-
mask_red1 = cv2.inRange(hsv, lower_red1, higher_red1)
-
lower_red2 = np.array([
156,
43,
46])
# 红色阈值下界
-
higher_red2 = np.array([
180,
255,
255])
# 红色阈值上界
-
mask_red2 = cv2.inRange(hsv, lower_red2, higher_red2)
-
mask_red = cv2.add(mask_red1, mask_red2)
# 拼接过滤后的mask
过滤出来的二值图如下:
形态学去噪
-
# 形态学去噪,cv2.MORPH_OPEN先腐蚀再膨胀,cv2.MORPH_CLOSE先膨胀再腐蚀
-
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (
5,
5))
-
mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_OPEN, kernel, iterations=
1)
-
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (
7,
7))
-
mask_red = cv2.morphologyEx(mask_red, cv2.MORPH_CLOSE, kernel, iterations=
3)
颜色识别有时候会有部分噪声被一起识别,这时候可以用形态学进行去噪。在使用cv2.morphologyEx进行形态学去噪之前,需要由cv2.getStructuringElement先定义卷积核的形状和大小。
cv2.getStructuringElement(shape, ksize)常用参数如下:
- shape:核的形状,cv2.MORPH_RECT(矩形),cv2.MORPH_CROSS(十字形),cv2.MORPH_ELLIPSE(椭圆形)
- ksize:核的大小,格式为(width, height)
cv2.morphologyEx(src, op, kernel, iterations) 常用参数如下:
- src:输入图像
- op:形态学操作的类型,cv2.MORPH_ERODE(腐蚀),cv2.MORPH_DILATE(膨胀),cv2.MORPH_OPEN(开运算),cv2.MORPH_CLOSE(闭运算),cv2.MORPH_GRADIENT(梯度运算),cv2.MORPH_TOPHAT(礼帽运算),cv2.MORPH_BLACKHAT(黑帽运算),详情可见:形态学操作
- kernel:进行形态学操作的卷积核
- iterations:形态学操作迭代的次数,默认值为1
效果如下:
轮廓检测
-
# 轮廓检测,找出线条的轮廓
-
draw_cnt = img.copy()
-
cnts = cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[
0]
-
cv2.drawContours(draw_cnt, cnts, -
1, (
0,
255,
0),
2)
使用cv2.findContours(image, mode, method)检测轮廓,常用参数如下:
- image:二值图
- mode:轮廓的检索模式
cv2.RETR_EXTERNAL 只检测外轮廓 cv2.RETR_LIST 检测的轮廓不建立等级关系 cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层 cv2.RETR_TREE 建立一个等级树结构的轮廓
- method:轮廓的近似方法
cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1 cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标
使用cv2.drawContours绘制轮廓,效果如下:
近似多边形
-
# 四边形拟合,找到相应的的顶点
-
draw_approx = img.copy()
-
point1, point2 =
list(),
list()
-
for cnt
in cnts:
-
for epsilon
in
range(
50):
-
approx = cv2.approxPolyDP(cnt, epsilon,
True)
-
if
len(approx) ==
4:
-
break
-
cv2.polylines(draw_approx, [approx],
True, (
0,
0,
255),
2)
# 绘制边
-
for i
in approx:
-
cv2.circle(draw_approx, i[
0],
6, (
0,
0,
0), -
1)
# 绘制顶点
使用cv2.approxPolyDP(curve, epsilon, closed)近似多边形,常用参数如下:
- curve:输入二维点集的数组
- epsilon:近似结果的精度。这是原曲线和其近似曲线之间的最大距离。epsilon越小,近似结果的折线形状越“接近”曲线。详情可见:近似多边形
- closed:True或False。True表示近似曲线是闭合的(第一个顶点与最后一个顶点相互连接),False表示近似曲线是不闭合的
cv2.approxPolyDP返回近似多边形的顶点坐标,这里用范围内的值遍历epsilon精度,当返回的顶点坐标数量为4时,即为四边形。
对每个轮廓进行四边形拟合,使用cv2.polylines和cv2.circle分别绘制边和顶点,效果如下:
四边形拟合完成后,对每个四边形的顶点进行排序。
透视矫正
-
# 透视矫正目标区域
-
w, h =
900,
300
-
rect = [point1[
0], point1[-
1], point2[-
1], point2[
0]]
# 顺序为第一个四边形的左上,第四个四边形的右上,第四个四边形的右下,第一个四边形的左下
-
pts1 = np.array(rect, dtype=
"float32")
-
pts2 = np.array([rect[
0], [rect[
0][
0] + w, rect[
0][
1]],
-
[rect[
0][
0] + w, rect[
0][
1] + h], [rect[
0][
0], rect[
0][
1] + h]], dtype=
"float32")
-
M = cv2.getPerspectiveTransform(pts1, pts2)
# 变换矩阵
-
img_warped = cv2.warpPerspective(img, M, (
1500,
500))
# 透视变换
使用cv2.warpPerspective进行透视变换之前,需要先由cv2.getPerspectiveTransform获取变换矩阵。
cv2.getPerspectiveTransform(src, dst)常用参数如下:
- src:变换前图像四边形的顶点坐标(数组),其中任意三点不共线
- dst:变换后图像四边形的顶点坐标(数组),其中任意三点不共线
这里输入变换前四边形的顶点坐标,分别为第一个四边形的左上坐标,第四个四边形的右上坐标,第四个四边形的右下坐标,第一个四边形的左下坐标;输入变换后四边形的顶点坐标,是以第一个四边形的左上坐标延长宽和高。
cv2.getPerspectiveTransform(src, M, dsize)常用参数如下:
- src:输入图像
- M:变换矩阵
- dsize:输出图像大小
变换效果如下:
img_warped = img_warped[rect[0][1]: rect[0][1] + h, rect[0][0]: rect[0][0] + w] # 抠出变换后的区域
将变换后的区域抠出来,效果如下:
参考链接
第8章 形态学操作 -- 8.3 核函数 cv2.getStructuringElement()_Enzo 想砸电脑的博客-CSDN博客
Python +OpenCV CH9:形态学操作(morphologyEx扩展)_liguoxin1990的博客-CSDN博客
Python OpenCV approxPolyDP()函数
多边形逼近cv2.approxPolyDP和Douglas-Peucker算法_00000cj的博客-CSDN博客_多边形逼近算法
转载:https://blog.csdn.net/weixin_41611054/article/details/127456711