Advertisement

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

请添加图片描述

全部评论 (0)

还没有任何评论哟~