Python opencv进行矩形识别
该文本介绍了使用Python OpenCV进行矩形识别的方法,包括以下关键步骤:
高斯模糊:通过高斯滤波消除图像噪声,以提高识别效果。使用cv2.GaussianBlur函数,设置5x5的高斯核和标准差为0。
图像格式转换:将原图从RGB格式转换为HLS颜色空间,HLS更适合颜色识别。通过cv2.cvtColor函数完成转换。
通道分离:从HLS图像中分离出亮度(L)、色调(H)和饱和度(S)通道。主要使用亮度通道进行后续处理。
二值化:对亮度通道进行二值化处理,使用cv2.threshold函数,阈值设为170,二值化模式为反向二值化。
查找轮廓:使用cv2.findContours函数查找图像中的轮廓,筛选出面积较大的矩形。
矩形识别:通过计算轮廓的面积和顶点数,筛选出符合条件的矩形。使用cv2.minAreaRect函数获取矩形的最小包围框,并通过绘制包围框和圆心来定位矩形。
颜色筛选:通过提取矩形中心区域的平均颜色值,判断矩形的颜色,并计算矩形的旋转角度。
结果输出:将识别出的矩形信息(中心坐标、旋转角度、颜色值)存储在列表中,并输出结果。
代码中还包含了对输入图片的读取、处理、显示以及时间计算等完整流程。
Python opencv进行矩形识别
图像识别中,圆形和矩形识别是图像处理中最为常见的两种类型,上一篇详细介绍了圆形识别的方法,本例文将深入探讨矩形识别的技巧。最后的结果显示,该算法能够精准地识别出圆心坐标、四个顶点位置,并通过示例图进行具体说明。图中左侧为原始图像,右侧为经算法处理后的识别结果。在搭载Intel i5 10400处理器的设备上运行,该算法的处理速度达到了每秒8000个图像的水平。

识别出结果后,计算任意三个顶点之间的距离,共获得三个数据,其中数值最小值对应的两个顶点是短边的端点,数值最大值对应的两个顶点是矩形对角线的端点,中间值对应的两个顶点是长边的端点。如果是正方形,则最小值和中间值相等,表明所有边长相等。知道了长边的两个端点,也就很容易求得矩形的旋转方向,在机器人捡物过程中,末端执行器的旋转角度值也随之确定下来。
深入解析关键函数的运作机制与应用价值【
1 高斯模糊
blur = cv2.GaussianBlur(resized, (5, 5), 0) # 高斯模糊, 窗口为5x5, 标准差为0
高斯模糊,也称为高斯滤波,其中resized表示输入图像的像素数组,blur表示输出图像的像素数组。其中,(5×5)表示一个5×5的卷积核,大小可选但必须为奇数尺寸。这一步骤旨在消除图像噪声,避免其对识别结果产生影响。
2 图像格式转换
RGB格式
img = cv2.imread(".\ img\ save2.jpg")
图像加载后,默认就是RGB格式。
常见的照片文件格式多为RGB或RGBA格式,这通常包括红、绿、蓝三个通道层,再加上透明度通道层。在图像识别任务中,若关注的颜色信息较为明显,例如需要识别红色物体,可以选择使用RGB格式,并从中提取红色通道层进行处理。例如图中,在红色通道层中,红色物体的亮度值最高,其他颜色通道中的亮度值相对较低。然而,如图所示,在红色通道层中提取红色物体较为困难,因为红色通道中的红色物体与背景颜色相近,难以实现前景与背景的分离。相比之下,绿色和蓝色通道对红色物体的识别度更高。

HLS格式
lab = cv2.cvtColor(blur, cv2.COLOR_RGB2HLS) # 转为HLS图像模式, 此模式区分颜色简单, 同时处理速度快
当与颜色无关时,在需要识别时,可以考虑采用HLS色彩模式进行。这个色彩模型由色调、饱和度和亮度3个通道构成,以下为详细说明:

可以看出,L通道的轮廓最明显,因此可以使用该通道来做图像识别。
同样地,对于需要提取的红色物体,可以在识别出矩形后,取其中心点(更优的方式是取中心区域的平均值)的颜色值,再自行判断颜色,即可筛选出所需的目标红色矩形。
3 通道分离
这其实是对numpy数组的操作
channel_h = lab[:, :, 0] # 提取H通道 色相
channel_l = lab[:, :, 1] # 提取L通道 明度
channel_s = lab[:, :, 2] # 提取S通道 饱和度
4 二值化
ret_val, bin_channel_l = cv2.threshold(channel_l.copy(), 170, 255, cv2.THRESH_BINARY_INV) # 复制L通道, 并二值化
第1个参数为复制L通道一次,第2个参数为170,该值为阈值,第3个参数为255,该值为填充值,第4个参数为模式设置,可选择二值化或反二值化,默认为反二值化。
5 查找轮廓
contours = cv2.findContours(bin_channel_l, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
第一个设置项是图像数组
第二个设置项是查找模式,目前设置为查找外围轮廓,而内部的轮廓则不被查找
第三个设置项负责输出所有轮廓点的信息
在本研究中,搜索的轮廓点被划分为多个集合,每个集合的面积大小不一。本例仅关注四个关键点的轮廓形状,即为矩形图形。
我们可以设定一个面积阈值,将那些面积小于该阈值的矩形排除在外。通过计算该矩形的像素数量,我们就可以确定其面积大小。如果该矩形的面积超过该阈值,则满足我们的筛选条件。
最后一步,如果需要对颜色进行筛选,则可以从矩形的中心提取一个5×5的矩形区域。对于该区域的RGB值,计算每个分量的总和,然后将总和除以25(即5×5的区域面积),从而得到该区域的颜色平均值。
完整代码
请将代码粘贴到开发环境中,即可开始运行。同时,确保您已正确安装了python、opencv和numpy等依赖项。
# coding: UTF-8
# 作者: 李佳
# Email: 36566686@qq.com
# WeChat: laolidesenlin
# Date: 2020/4/1 20:09
# Filename:detect_rect.PY
# SoftTool:PyCharm
__author__ = '李佳'
import cv2
import time
import numpy as np
def draw_rect(img, rect_point):
cv2.line(img, rect_point[0], rect_point[1], (255, 0, 255), 3)
cv2.line(img, rect_point[1], rect_point[2], (255, 0, 255), 3)
cv2.line(img, rect_point[2], rect_point[3], (255, 0, 255), 3)
cv2.line(img, rect_point[3], rect_point[0], (255, 0, 255), 3)
def detect_rect(img):
parameter = [None] # [[], [], [], []] # x, y, 角度, 颜色值(0-255)
counter = 0
start = time.perf_counter() # 程序计时
img = cv2.imread(".\ img\ save2.jpg")
# resized = imutils.resize(img, width=300) # 缩小尺寸
# ratio = img.shape[0] / float(resized.shape[0]) # 计算缩小的比例
# x, y = img.shape[0:2]
# resized = cv2.resize(img, (int(y / 2), int(x / 2))) # 缩小为一半尺寸
resized = img.copy() # 不缩小尺寸
blur = cv2.GaussianBlur(resized, (5, 5), 0) # 高斯模糊, 窗口为5x5, 标准差为0
hls = cv2.cvtColor(blur, cv2.COLOR_RGB2HLS) # 转为HLS图像模式, 此模式区分颜色最简单, 同时处理速度最快
channel_b = img[:, :, 0]
channel_g = img[:, :, 1]
channel_r = img[:, :, 2]
cv2.imshow("R", channel_r)
cv2.imshow("G", channel_g)
cv2.imshow("B", channel_b)
channel_h = hls[:, :, 0] # 提取H通道 色相
channel_l = hls[:, :, 1] # 提取L通道 明度
channel_s = hls[:, :, 2] # 提取S通道 饱和度 目前没有使用这个通道
cv2.imshow("H", channel_h)
cv2.imshow("L", channel_l)
cv2.imshow("S", channel_s)
# cv2.imwrite(".\ img\ channel_H.jpg", channel_h)
ret_val, bin_channel_l = cv2.threshold(channel_l.copy(), 170, 255, cv2.THRESH_BINARY_INV) # 复制L通道, 并二值化
contours = cv2.findContours(bin_channel_l, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for c in contours[0]:
# ##########################################################
# 识别矩形
area = cv2.contourArea(c) # 求轮廓的面积
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * peri, True)
if area < 5000: # 实际方片的面积约为40000, 该阈值可以过滤掉小的噪点区域
# print("面积小于阈值")
pass
elif len(approx) == 4: # 轮廓的顶点数为4, 即矩形, 再次过滤掉干扰
# print(len(approx))
min_rect = cv2.minAreaRect(approx)
# print(min_rect)
box = cv2.boxPoints(min_rect)
box = box.astype(int)
# print(box)
# cv2.drawContours(img, c, -1, (255, 0, 255), 3)
cv2.line(img, (box[0][0], box[0][1]), (box[1][0], box[1][1]), (0, 0, 0), 3) # 绘制包络矩形
cv2.line(img, (box[1][0], box[1][1]), (box[2][0], box[2][1]), (0, 0, 0), 3)
cv2.line(img, (box[2][0], box[2][1]), (box[3][0], box[3][1]), (0, 0, 0), 3)
cv2.line(img, (box[3][0], box[3][1]), (box[0][0], box[0][1]), (0, 0, 0), 3)
center_x, center_y = min_rect[0]
center_x = round(center_x)
center_y = round(center_y)
cv2.circle(img, (center_x, center_y), 5, (0, 0, 0), thickness=2) # 绘制圆心
# print(center_x, center_y)
# ##########################################################
# 识别颜色
# for x in (center_x-5, center_x+5):
# for y in (center_y-5, center_y+5):
# # mean_a =
# # mean_b =
# pass
mask = np.zeros(channel_l.shape[:2], dtype="uint8") # 按输入的图像大小, 构建一个MASK区域
cv2.drawContours(mask, [c], -1, 255, -1) # 在区域中绘制轮廓
mask = cv2.erode(mask, None, iterations=2) # 腐蚀2个像素
# cv2.imshow("mask", mask)
mean = cv2.mean(channel_h, mask=mask) # 求MASK的平均值
# print(mean)
angle = min_rect[2]
color = round(mean[0])
parameter.append([center_x, center_y, angle, color]) # x, y, 角度, 颜色值(0-255)
mean = 0
# cv2.imshow(str(counter), img)
# cv2.waitKey(0)
end = time.perf_counter() - start
org = cv2.imread(".\ img\ save2.jpg")
cv2.imshow("in", org)
cv2.imshow("out", img)
cv2.waitKey(0)
print(parameter[1:])
# print(len(parameter[1:]))
print("程序运行时间为 ", str(abs(int(end - start))), "us")
# cv2.waitKey(0)
return parameter[1:]
if __name__ == '__main__':
filename = ".\ img\ save2.jpg"
img = cv2.imread(filename)
detect_rect(img)
配套的JPG文件
右键保存为 save2.jpeg

