2023电赛运动目标与跟踪控制系统图像方案(基于树莓派4B——OPENCV解决方案)

 


前言

笔者今天几乎完成了去年电赛图像识别的部分,如果用opencv的话确实会简单不少,我们只需要识别外矩形框以及内矩形框的8个顶点,还有中心点以及激光点的坐标发送给stm32,基本就已经完成了这个题目视觉的部分,在之前笔者进行了opencv的入门,学习一段时间后做了银行卡号识别的项目才开始真正做这道题的图像,希望我的思路可以帮助到大家。

先展示一下效果

022c67241bc742ff8ce2b4c4d217a40e.png

一、识别黑色电工胶带八个顶点(外边框4个内边框4个)

我们识别这个黑色边框的步骤主要就是以下几个步骤

1.图像灰度化

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

2.Canny边缘检测

先二值化图像随后进行边缘检测

lurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 30, 60)

3.轮廓检测

我们需要消除图像中由噪声引起的小面积边缘轮廓。

# 轮廓检测
    contours, _ = cv2.findContours(edges, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    # 去除面积小的轮廓
    filtered_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 50]  # 可根据实际情况调整阈值

 

4.近似多边形

 rect_contours = []
    for cnt in filtered_contours:
        epsilon = 0.02 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)
        if len(approx) == 4:  # 近似为四边形(可能是矩形)
            rect_contours.append(approx)

    detected_rect_contours = rect_contours  # 保存检测到的矩形轮廓

把近似成矩形的轮廓保留下来,其他的全部舍弃。

5.绘制矩形轮廓

# 绘制矩形轮廓
    for i, rect_contour in enumerate(detected_rect_contours):
        if i == index_to_delete or i == index_to_delete_2:  # 如果是要删除的索引,跳过
            continue
        area = cv2.contourArea(rect_contour)

        if area > 40000:
            cv2.drawContours(frame, [rect_contour], -1, (255, 0, 0), 2)  # 面积大于 40000 用蓝色绘制
        else:
            cv2.drawContours(frame, [rect_contour], -1, (0, 255, 0), 2)  # 其他用绿色绘制

这里笔者把轮廓分成了两部分,一部分是外层的一部分是内层的,通过面积进行区分。

6.找到矩形顶点

​
 # 计算并打印要关注的两个矩形的中心点坐标
    center_coords = []
    for i, rect_contour in enumerate(detected_rect_contours):
        if i in [index_to_delete, index_to_delete_2]:
            M = cv2.moments(rect_contour)
            if M["m00"]!= 0:
                cx = int(M["m10"] / M["m00"])
                cy = int(M["m01"] / M["m00"])
                center_coords.append((cx, cy))
                print(f"要关注的矩形 {i} 的中心点坐标: ({cx}, {cy})")

    # 标注两个矩形的顶点
    for i, rect_contour in enumerate(detected_rect_contours):
        if i == index_to_delete or i == index_to_delete_2:
            for point in rect_contour:
                cv2.circle(frame, tuple(point[0]), 5, (255, 255, 0), -1)  # 用黄色标注顶点

​

 

二、找到中心点以及红色激光点(绿色同理)

1.中心点

  # 计算并标注矩形中心点
        M = cv2.moments(rect_contour)
        if M["m00"]!= 0:
            cx = int(M["m10"] / M["m00"])
            cy = int(M["m01"] / M["m00"])
            cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1)  # 用红色标注中心点

2.激光点

主要就是将图像转到hsv色彩空间,这样就可以找到红色的激光点。如果找不到阈值可以用我的找阈值代码,也会写在文章里。

    # 处理激光检测的结果
    if len(contours_red) > 0:
        largest_area = 0
        laser_center = None
        for contour_red in contours_red:
            area = cv2.contourArea(contour_red)
            if area > largest_area:
                largest_area = area
                M = cv2.moments(contour_red)
                if M["m00"]!= 0:
                    height, width = frame.shape[:2]
                    cy = height-(int(M["m01"] / M["m00"]))
                    cx = int(M["m10"] / M["m00"])
                    laser_center = (cx, cy)
        if laser_center:
            print("红色激光位置:", laser_center)
            # 用小矩形框框出红色激光点位置
            rect_size = 10  # 矩形框的大小
            x1 = laser_center[0] - rect_size // 2
            y1 = laser_center[1] - rect_size // 2
            x2 = laser_center[0] + rect_size // 2
            y2 = laser_center[1] + rect_size // 2
            cv2.rectangle(frame, (x1, height-y1), (x2, height-y2), (0, 0, 255), 2)  # 绘制矩形框

    return frame

 

 

3.找阈值代码

import cv2

def mouse_click(event, x, y, flags, para):
    if event == cv2.EVENT_LBUTTONDOWN:  # 左边鼠标点击
        print('PIX:', x, y)
        print("BGR:", frame[y, x])
        gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        print("GRAY:", gray_frame[y, x])
        hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        print("HSV:", hsv_frame[y, x])

if __name__ == '__main__':
    cap = cv2.VideoCapture(0)  # 打开默认摄像头(通常为 0)

    cv2.namedWindow("frame")
    cv2.setMouseCallback("frame", mouse_click)

    while True:
        ret, frame = cap.read()  # 读取摄像头帧

        if not ret:
            print("无法获取摄像头帧")
            break

        cv2.imshow('frame', frame)

        if cv2.waitKey(1) == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()

通过鼠标点击某个点就可以得出这个点的颜色信息。效果如下

0096a4e249394f81bfadeff6f0127aca.png


三、全部代码

import cv2
import numpy as np

# 全局变量用于存储检测到的矩形轮廓
detected_rect_contours = []
is_detected = False  # 标志位,初始化为未检测
index_to_delete = 1  # 要删除的第一个矩形轮廓的索引
index_to_delete_2 = 3  # 要删除的第二个矩形轮廓的索引

# 处理帧的函数
def process_frame(frame):
    global detected_rect_contours, is_detected, index_to_delete, index_to_delete_2

    # 每次都重新检测矩形轮廓
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    edges = cv2.Canny(blurred, 30, 60)
    # 轮廓检测
    contours, _ = cv2.findContours(edges, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    # 去除面积小的轮廓
    filtered_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 50]  # 可根据实际情况调整阈值

    rect_contours = []
    for cnt in filtered_contours:
        epsilon = 0.02 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)
        if len(approx) == 4:  # 近似为四边形(可能是矩形)
            rect_contours.append(approx)

    detected_rect_contours = rect_contours  # 保存检测到的矩形轮廓

    # 绘制矩形轮廓
    for i, rect_contour in enumerate(detected_rect_contours):
        if i == index_to_delete or i == index_to_delete_2:  # 如果是要删除的索引,跳过
            continue
        area = cv2.contourArea(rect_contour)

        if area > 40000:
            cv2.drawContours(frame, [rect_contour], -1, (255, 0, 0), 2)  # 面积大于 40000 用蓝色绘制
        else:
            cv2.drawContours(frame, [rect_contour], -1, (0, 255, 0), 2)  # 其他用绿色绘制

        # 计算并标注矩形中心点
        M = cv2.moments(rect_contour)
        if M["m00"]!= 0:
            cx = int(M["m10"] / M["m00"])
            cy = int(M["m01"] / M["m00"])
            cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1)  # 用红色标注中心点

        # 打印矩形顶点坐标
        for point in rect_contour:
            print(f"矩形 {i} 的顶点坐标: {point[0]}")

    # 计算并打印要关注的两个矩形的中心点坐标
    center_coords = []
    for i, rect_contour in enumerate(detected_rect_contours):
        if i in [index_to_delete, index_to_delete_2]:
            M = cv2.moments(rect_contour)
            if M["m00"]!= 0:
                cx = int(M["m10"] / M["m00"])
                cy = int(M["m01"] / M["m00"])
                center_coords.append((cx, cy))
                print(f"要关注的矩形 {i} 的中心点坐标: ({cx}, {cy})")

    # 标注要删除的两个矩形的顶点
    for i, rect_contour in enumerate(detected_rect_contours):
        if i == index_to_delete or i == index_to_delete_2:
            for point in rect_contour:
                cv2.circle(frame, tuple(point[0]), 5, (255, 255, 0), -1)  # 用黄色标注顶点

    # 检测红色激光点
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    # 设定红色的范围
    lower_red = np.array([165, 30, 120])
    upper_red = np.array([185, 150, 255])

    # 提取红色部分
    mask = cv2.inRange(hsv, lower_red, upper_red)

    # 寻找红色激光的轮廓
    contours_red, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # 处理激光检测的结果
    if len(contours_red) > 0:
        largest_area = 0
        laser_center = None
        for contour_red in contours_red:
            area = cv2.contourArea(contour_red)
            if area > largest_area:
                largest_area = area
                M = cv2.moments(contour_red)
                if M["m00"]!= 0:
                    height, width = frame.shape[:2]
                    cy = height-(int(M["m01"] / M["m00"]))
                    cx = int(M["m10"] / M["m00"])
                    laser_center = (cx, cy)
        if laser_center:
            print("红色激光位置:", laser_center)
            # 用小矩形框框出红色激光点位置
            rect_size = 10  # 矩形框的大小
            x1 = laser_center[0] - rect_size // 2
            y1 = laser_center[1] - rect_size // 2
            x2 = laser_center[0] + rect_size // 2
            y2 = laser_center[1] + rect_size // 2
            cv2.rectangle(frame, (x1, height-y1), (x2, height-y2), (0, 0, 255), 2)  # 绘制矩形框

    return frame

cap = cv2.VideoCapture(0)  # 0 表示默认摄像头

while True:
    ret, frame = cap.read()

    if not ret:
        break

    processed_frame = process_frame(frame)

    cv2.imshow('Enhanced Edge Detection with Rectangular Contours', processed_frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

四、总结

在这道题上边opencv确实起到了对openmv等视觉模块的降维打击,把坐标点发送到stm32后即可让stm32通过舵机去控制激光点的走向,可以通过8个顶点计算出两个轮廓中线的路径以及坐标。用opencv的话一个上午就可以写出解决方案,我认为对于电赛的视觉识别方面帮助也是特别大的,可以节省很多时间。并且也有很多便宜的开发板可以运行opencv,用到的都是传统算法。希望这篇文章可以帮助到大家。

 

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
树莓派4B安装OpenCV可以采用一键安装方法或手动安装方法。对于一键安装方法,可以参考引用中提到的教程,使用一键安装脚本来安装OpenCV。该方法适用于树莓派4B系统,不再需要编译安装。 另一种方法是手动安装OpenCV。首先,根据引用中提到的建议,安装特定版本的OpenCV,可以使用命令"pip install opencv-contrib-python=4.5.4.60"来安装。此外,还需要安装numpy库,可以使用命令"pip install numpy"来安装。 如果想使用系统自带的OpenCV,可以使用引用中提到的命令在命令行中进行安装,命令为"sudo apt install python3-opencv -y"。需要注意的是,这种方法只支持Python 3。 综上所述,树莓派4B安装OpenCV可以通过一键安装方法或手动安装方法来实现。具体选择哪种方法可以根据个人需求和偏好来决定。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [树莓派4B系统一键安装opencv,再也不要用编译安装啦!(支持buster、bullseye)](https://blog.csdn.net/qq_41866091/article/details/128530569)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [树莓派4b安装Opencv(实测有效)](https://blog.csdn.net/zhoutan001/article/details/130563427)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值