首先一张图的识别为例:
第一步:读取图像并查看验证
代码:
import cv2
import numpy as np
# 读取图片路径
image = cv2.imread("P1_No005.jpg")
# 设置窗口大小自适应比例
cv2.namedWindow("image",cv2.WINDOW_KEEPRATIO)
# 在窗口中显示图像
cv2.imshow("image", image)
# 等待一个键盘的输入(按任意键继续程序)
cv2.waitKey(0)
运行结果:
第二步:对图像进行灰度转换并进行高斯滤波(用来降噪,即模糊图像,减少图像的细节)后续我们需要使用cv2.findContours()进行轮廓检测,因此我们需要获取图像的二值图(黑白图)(cv2.findContours()函数接受的参数为二值图(不是灰度图))
代码:
# 对灰度图进行高斯滤波
image = cv2.resize(image, (600, 600)) # 改变图像尺寸
gs_frame = cv2.GaussianBlur(image, (11, 11), 0) # 高斯滤波获取轮廓
# 将图像转换成灰度图像
gray = cv2.cvtColor(gs_frame, cv2.COLOR_BGR2GRAY) # 原彩色图片转换成灰度图
ret, binary = cv2.threshold(gray, 145, 255, cv2.THRESH_BINARY_INV) # 二值化
# 设置窗口大小自适应比例
cv2.namedWindow("binary", cv2.WINDOW_KEEPRATIO)
# 在窗口中显示图像
cv2.imshow("binary", binary)
# 等待一个键盘的输入(按任意键继续程序)
cv2.waitKey(0)
运行结果:
第三、四步进行闭运算(先膨胀后腐蚀,将图像相连为一个无突起的连通域)
第三步:对图像进行膨胀并进行迭代(自定义执行多次膨胀操作,扩大图像主要特征)
代码:
kernel = np.ones((40, 40), dtype=np.uint8) # 膨胀图像
dilate = cv2.dilate(binary, kernel, 1) # 迭代膨胀操作
# 设置窗口大小自适应比例
cv2.namedWindow("dilate", cv2.WINDOW_KEEPRATIO)
# 在窗口中显示图像
cv2.imshow("dilate", dilate)
# 等待一个键盘的输入(按任意键继续程序)
cv2.waitKey(0)
运行结果(可以看到主要特征被放大了):
第四步:对图像进行腐蚀(通过更改np.ones的第一个参数即shape来变动腐蚀程度)
代码:
# 进行腐蚀
kernel = np.ones((40, 40), dtype=np.uint8) # 腐蚀图像
# kernel = np.ones((20, 20), dtype=np.uint8) # 腐蚀图像
erosion = cv2.erode(dilate, kernel, iterations=1)
# 设置窗口大小自适应比例
cv2.namedWindow("erosion", cv2.WINDOW_KEEPRATIO)
# 在窗口中显示图像
cv2.imshow("erosion", erosion)
# 等待一个键盘的输入(按任意键继续程序)
cv2.waitKey(0)
运行结果(可以对比40与20的区别,左40,右20):
第五步:绘制矩形
代码:
# 注意:cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),所以读取的图像要先转成灰度的,再转成二值图。
contours, hierarchy = cv2.findContours(erosion, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 轮廓检测,寻找边框
x = [] # 存储横坐标
y = [] # 存储纵坐标
for i in range(600):
for j in range(600):
if erosion[i, j] != 0: # 如果该像素点不等于0(为白,不为黑),存储下来
# print(f"{i} {j}")
x.append(i)
y.append(j)
area = []
area_num = []
for i in range(len(contours)):
a = cv2.contourArea(contours[i], True)
area.append(abs(a))
if len(area) > 1:
for i in range(len(area)):
area_num.append(i)
area_num.remove(area.index(max(area)))
for i in range(len(area_num)):
erosion = cv2.drawContours(erosion, contours, area_num[i], 0, cv2.FILLED)
# print(erosion[599,263]) # 为255
# print(erosion[0, 0]) # 为0
c1 = min(x)
c2 = max(x)
c3 = min(y)
c4 = max(y)
if erosion[c1, c3] == 255 and erosion[c2, c3] == 0:
# 若左上到右下为路径(左上顶点为白,右上顶点为黑),则为左
print('left')
if erosion[c1, c4] == 255 and erosion[c2, c4] == 0:
# 若左下到右上为路径(左下顶点为白,右上顶点为黑),则为右
print('right')
if erosion[c1, c3] == 255 and erosion[c1, c4] == 255: # 均为白,则为直
print('Go straight')
# 绘制图框
cv2.rectangle(image, (c3, c1), (c4, c2), (0, 255, 0), 2) # 图像上绘制矩形
cv2.imshow("result", image)
cv2.waitKey(0)
运行结果:
即可
完整代码:
import cv2
import numpy as np
# 读取图片路径
image = cv2.imread("P1_No005.jpg")
# 对灰度图进行高斯滤波
image = cv2.resize(image, (600, 600)) # 改变图像尺寸
gs_frame = cv2.GaussianBlur(image, (11, 11), 0) # 高斯滤波获取轮廓
# 将图像转换成灰度图像
gray = cv2.cvtColor(gs_frame, cv2.COLOR_BGR2GRAY) # 原彩色图片转换成灰度图
ret, binary = cv2.threshold(gray, 145, 255, cv2.THRESH_BINARY_INV) # 二值化
kernel = np.ones((40, 40), dtype=np.uint8) # 膨胀图像
dilate = cv2.dilate(binary, kernel, 1) # 迭代膨胀操作
# 进行腐蚀
kernel = np.ones((20, 20), dtype=np.uint8) # 腐蚀图像
erosion = cv2.erode(dilate, kernel, iterations=1)
# 注意:cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),所以读取的图像要先转成灰度的,再转成二值图。
contours, hierarchy = cv2.findContours(erosion, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 查询角点
x = [] # 存储横坐标
y = [] # 存储纵坐标
for i in range(600):
for j in range(600):
if erosion[i, j] != 0: # 如果该像素点不等于0(为白,不为黑),存储下来
# print(f"{i} {j}")
x.append(i)
y.append(j)
c1 = min(x)
c2 = max(x)
c3 = min(y)
c4 = max(y)
if erosion[c1, c3] == 255 and erosion[c2, c3] == 0: # 若左上到右下为路径(左上顶点为白,右上顶点为黑),则为左
print('left')
if erosion[c1, c4] == 255 and erosion[c2, c4] == 0: # 若左下到右上为路径(左下顶点为白,右上顶点为黑),则为右
print('right')
if erosion[c1, c3] == 255 and erosion[c1, c4] == 255: # 均为白,则为直
print('Go straight')
# 绘制图框
cv2.rectangle(image, (c3, c1), (c4, c2), (0, 255, 0), 2) # 图像上绘制矩形
cv2.imshow("result", image)
cv2.waitKey(0)
将代码加入循环即可进行多次检测:
import cv2
import numpy as np
import os
for filename in os.listdir('mydata/test'): # 遍历路径下所有图片
print(filename)
image = cv2.imread('mydata/test' + '/' + filename)
# 读取图片路径
# # 设置窗口大小自适应比例
# cv2.namedWindow("image", cv2.WINDOW_KEEPRATIO)
# # 在窗口中显示图像
# cv2.imshow("image", image)
# # 等待一个键盘的输入(按任意键继续程序)
# cv2.waitKey(0)
# 对灰度图进行高斯滤波
image = cv2.resize(image, (600, 600)) # 改变图像尺寸
gs_frame = cv2.GaussianBlur(image, (11, 11), 0) # 高斯滤波获取轮廓
# 将图像转换成灰度图像
gray = cv2.cvtColor(gs_frame, cv2.COLOR_BGR2GRAY) # 原彩色图片转换成灰度图
ret, binary = cv2.threshold(gray, 145, 255, cv2.THRESH_BINARY_INV) # 二值化
# # 设置窗口大小自适应比例
# cv2.namedWindow("binary", cv2.WINDOW_KEEPRATIO)
# # 在窗口中显示图像
# cv2.imshow("binary", binary)
# # 等待一个键盘的输入(按任意键继续程序)
# cv2.waitKey(0)
kernel = np.ones((40, 40), dtype=np.uint8) # 膨胀图像
dilate = cv2.dilate(binary, kernel, 1) # 迭代膨胀操作
# # 设置窗口大小自适应比例
# cv2.namedWindow("dilate", cv2.WINDOW_KEEPRATIO)
# # 在窗口中显示图像
# cv2.imshow("dilate", dilate)
# # 等待一个键盘的输入(按任意键继续程序)
# cv2.waitKey(0)
# 进行腐蚀
kernel = np.ones((40, 40), dtype=np.uint8) # 腐蚀图像
erosion = cv2.erode(dilate, kernel, iterations=1)
# # 设置窗口大小自适应比例
# cv2.namedWindow("erosion", cv2.WINDOW_KEEPRATIO)
# # 在窗口中显示图像
# cv2.imshow("erosion", erosion)
# # 等待一个键盘的输入(按任意键继续程序)
# cv2.waitKey(0)
# 注意:cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),所以读取的图像要先转成灰度的,再转成二值图。
contours, hierarchy = cv2.findContours(erosion, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 轮廓检测,寻找边框
# print(contours)
# print(hierarchy)
area = []
area_num = []
for i in range(len(contours)):
a = cv2.contourArea(contours[i], True)
area.append(abs(a))
if len(area) > 1:
for i in range(len(area)):
area_num.append(i)
area_num.remove(area.index(max(area)))
for i in range(len(area_num)):
erosion = cv2.drawContours(erosion, contours, area_num[i], 0, cv2.FILLED)
# 查询角点
x = [] # 存储横坐标
y = [] # 存储纵坐标
for i in range(600):
for j in range(600):
if erosion[i, j] != 0: # 如果该像素点不等于0(为白,不为黑),存储下来
# print(f"{i} {j}")
x.append(i)
y.append(j)
# print(erosion[599,263]) # 为255
# print(erosion[0, 0]) # 为0
c1 = min(x)
c2 = max(x)
c3 = min(y)
c4 = max(y)
if erosion[c2, c4] == 255 and erosion[c2, c3] == 0: # 若左上到右下为路径(左上顶点为白,右上顶点为黑),则为左
print('left')
if erosion[c1, c4] == 255 and erosion[c1, c3] == 0: # 若左下到右上为路径(左下顶点为白,右上顶点为黑),则为右
print('right')
if erosion[c1, c3] == 0 and erosion[c1, c4] == 0: # 均为白,则为直
print('Go straight')
print(f"{erosion[c1, c3]} {erosion[c2, c3]}")
print(f"{erosion[c1, c4]} {erosion[c2, c4]}")
print(f"{erosion[c1, c3]} {erosion[c1, c4]}")
# 绘制图框
cv2.rectangle(image, (c3, c1), (c4, c2), (0, 255, 0), 2) # 图像上绘制矩形
cv2.imshow("result", image)
cv2.waitKey(0)