2024年TI杯E题-三子棋游戏装置方案分享-jdk123团队-第三弹视觉模块的封装

机械臂的设计

比赛第一天,我们打算做点提前量

视觉部分任务概述

赛题中,我们应该完成棋盘外棋子的识别,以及棋盘上棋子的识别。对此我们团队的方案为,先利用opencv,对九宫格棋盘进行识别
上图为仿真图
然后依据识别出九宫格的四个角点,来建立一个屏蔽罩,当识别内部棋子的时候,向外屏蔽,当识别外部棋子的时候,向内屏蔽。

内部棋子的识别

import time
import cv2
import numpy as np
import copy














def main7(image_path):
    class BHSBFL:
        def __init__(self, image_path):
            # 读取图像并转换为灰度图
            self.image = cv2.imread(image_path)
            self.gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)

        def find_grid_coordinates(self):
            # 应用边缘检测
            edges = cv2.Canny(self.gray, 50, 150, apertureSize=3)

            # 使用霍夫变换检测直线
            lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=100, maxLineGap=10)

            # 复制原始图像以绘制检测结果(此处不再需要显示)
            image_copy = self.image.copy()

            # 寻找轮廓
            contours_info = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
            contours = contours_info[0] if len(contours_info) == 2 else contours_info[1]

            if contours:
                # 选择最大的轮廓,这通常是九宫格的外框
                contour = max(contours, key=cv2.contourArea)

                # 获取最小的外接矩形
                rect = cv2.minAreaRect(contour)
                box = cv2.boxPoints(rect)
                box = np.int0(box)

                # 计算每个格子的中心并编号
                width, height = rect[1]
                cell_width = width / 3
                cell_height = height / 3

                # 确保顶点顺序为左上、右上、右下、左下
                box = sorted(box, key=lambda x: (x[1], x[0]))

                coordinates = []
                for i in range(3):
                    for j in range(3):
                        # 计算格子中心
                        center_x = int(box[0][0] + j * cell_width + cell_width / 2)
                        center_y = int(box[0][1] + i * cell_height + cell_height / 2)

                        # 返回编号和中心坐标
                        coordinates.append((i * 3 + j + 1, (center_x, center_y)))

                return coordinates
            else:
                print("未找到轮廓")
                return []

    class ChessPieceDetector(BHSBFL):
        def __init__(self, image_path):
            super().__init__(image_path)
            self.image_path = image_path  # 存储图像路径
            self.coordinates = self.find_grid_coordinates()
            self.corner_coordinates = self.find_corners()
            self.image = cv2.imread(image_path)  # 读取图像

        def find_corners(self):
            gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
            edged = cv2.Canny(gray, 30, 150)

            contours_info = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            contours = contours_info[0] if len(contours_info) == 2 else contours_info[1]

            if contours:
                largest_contour = max(contours, key=cv2.contourArea)
                rect = cv2.minAreaRect(largest_contour)
                box = cv2.boxPoints(rect)
                box = np.int0(box)

                return box.tolist()
            return []

        def detect_pieces(self):
            if not self.corner_coordinates:
                print("未找到九宫格角点坐标信息。")
                return []

            mask = np.zeros(self.gray.shape, dtype=np.uint8)
            cv2.fillPoly(mask, [np.array(self.corner_coordinates)], 255)

            masked_gray = cv2.bitwise_and(self.gray, self.gray, mask=mask)

            blurred = cv2.GaussianBlur(masked_gray, (9, 9), 2)

            circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, dp=1.2, minDist=30, param1=50, param2=30,
                                       minRadius=10, maxRadius=95)

            piece_positions = []

            if circles is not None:
                circles = np.round(circles[0, :]).astype("int")

                # 为每个检测到的圆准备盒子和分数
                boxes = np.array([[x - r, y - r, x + r, y + r] for (x, y, r) in circles])
                scores = np.array([1] * len(circles))  # 这里简单设定每个圆的得分为1

                # 执行非极大抑制
                indices = cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(), score_threshold=0.5, nms_threshold=0.5)

                # 解析经过非极大抑制后的圆
                if len(indices) > 0:
                    for i in indices:
                        i = i[0]  # 提取索引
                        x, y, r = circles[i]

                        # 获取颜色
                        color = masked_gray[y, x]
                        piece_color = "黑色" if color < 128 else "白色"

                        closest_distance = float('inf')
                        closest_number = None
                        for number, (cx, cy) in self.coordinates:
                            distance = np.sqrt((x - cx) ** 2 + (y - cy) ** 2)
                            if distance < closest_distance:
                                closest_distance = distance
                                closest_number = number

                        if closest_number is not None:
                            piece_positions.append((closest_number, piece_color))
                            # 标记棋子的位置
                            point_color = (0, 0, 255) if piece_color == "黑色" else (0, 255, 0)  # 红色或绿色圆点
                            cv2.circle(self.image, (x, y), 10, point_color, -1)

            piece_positions.sort(key=lambda x: x[0])

            return piece_positions

        def draw_corners(self):
            if self.corner_coordinates:
                for (x, y) in self.corner_coordinates:
                    cv2.circle(self.image, (int(x), int(y)), 10, (0, 0, 255), -1)  # 红色圆点

                for i in range(4):
                    cv2.line(self.image, tuple(self.corner_coordinates[i]), tuple(self.corner_coordinates[(i + 1) % 4]),
                             (0, 255, 0), 2)

                cv2.imshow('Corners and Pieces', self.image)
                cv2.waitKey(0)
                cv2.destroyAllWindows()
            else:
                print("未能找到九宫格角点坐标信息。")

        def print_corners(self):
            if self.corner_coordinates:
                print("九宫格四个角的坐标信息:")
                for i, (x, y) in enumerate(self.corner_coordinates):
                    print(f"角点 {i + 1}: (x: {x}, y: {y})")
            else:
                print("未能找到九宫格角点坐标信息。")

    detector = ChessPieceDetector(image_path)
    piece_positions = detector.detect_pieces()

    board = [[0 for _ in range(3)] for _ in range(3)]

    for number, color in piece_positions:
        row = (number - 1) // 3
        col = (number - 1) % 3
        if color == "黑色":
            board[row][col] = 1
        elif color == "白色":
            board[row][col] = -1
        print(f"棋子位置编号: {number}, 颜色: {color}")

    detector.print_corners()
    detector.draw_corners()
    print(board)
    return board


if __name__ == '__main__':
    main7('7plus.png')


在这里插入图片描述
棋子位置编号: 1, 颜色: 白色
棋子位置编号: 3, 颜色: 黑色
棋子位置编号: 5, 颜色: 黑色
九宫格四个角的坐标信息:
角点 1: (x: 1357, y: 958)
角点 2: (x: 612, y: 958)
角点 3: (x: 612, y: 215)
角点 4: (x: 1357, y: 215)

外部棋子的识别

import cv2
import numpy as np
from bhde import BHSBFL  # 导入 BHSBFL 类

class ChessPieceDetector(BHSBFL):
    def __init__(self, image_path):
        super().__init__(image_path)
        self.image_path = image_path  # 存储图像路径
        self.coordinates = self.find_grid_coordinates()
        self.corner_coordinates = self.find_corners()
        self.image = cv2.imread(image_path)  # 读取图像
        self.masked_image = self.image.copy()  # 复制图像用于处理

    def find_corners(self):
        # 将图像转换为灰度图像
        gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        # 应用边缘检测
        edged = cv2.Canny(gray, 30, 150)

        # 查找轮廓
        _,contours, _ = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        if contours:
            # 选择最大轮廓
            largest_contour = max(contours, key=cv2.contourArea)
            # 获取轮廓的最小外接矩形
            rect = cv2.minAreaRect(largest_contour)
            box = cv2.boxPoints(rect)
            box = np.int0(box)  # 转换为整数

            return box.tolist()  # 返回四个角点坐标
        return []

    def mask_rectangle(self):
        if self.corner_coordinates:
            # 创建一个全黑的遮罩,尺寸与图像相同
            mask = np.zeros(self.image.shape[:2], dtype=np.uint8)
            # 将角点坐标转换为 NumPy 数组
            points = np.array(self.corner_coordinates, dtype=np.int32)
            # 填充多边形区域
            cv2.fillPoly(mask, [points], 255)  # 填充为白色(255)

            # 使用 bitwise_not 反转遮罩
            mask_inv = cv2.bitwise_not(mask)

            # 将遮罩应用到图像上
            self.masked_image = cv2.bitwise_and(self.image, self.image, mask=mask_inv)
        else:
            print("未能找到九宫格角点坐标信息。")

    def detect_pieces(self):
        # 高斯模糊
        blurred = cv2.GaussianBlur(cv2.cvtColor(self.masked_image, cv2.COLOR_BGR2GRAY), (9, 9), 2)

        # 霍夫圆变换检测圆
        dp = cv2.getTrackbarPos('dp', 'Adjustments') / 10
        minDist = cv2.getTrackbarPos('minDist', 'Adjustments')
        param1 = cv2.getTrackbarPos('param1', 'Adjustments')
        param2 = cv2.getTrackbarPos('param2', 'Adjustments')
        minRadius = cv2.getTrackbarPos('minRadius', 'Adjustments')
        maxRadius = cv2.getTrackbarPos('maxRadius', 'Adjustments')

        circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, dp=dp, minDist=minDist, param1=param1, param2=param2, minRadius=minRadius, maxRadius=maxRadius)

        # 存储棋子位置
        piece_positions = []

        if circles is not None:
            circles = np.round(circles[0, :]).astype("int")

            # 非极大抑制
            boxes = np.array([[x - r, y - r, x + r, y + r] for x, y, r in circles])
            scores = np.array([r for x, y, r in circles])
            indices = cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(), score_threshold=0, nms_threshold=0.5)

            for i in indices:
                x, y, r = circles[i[0]]

                # 提取圆区域的颜色(使用中点的灰度值)
                color = cv2.cvtColor(self.masked_image, cv2.COLOR_BGR2GRAY)[y, x]

                # 判断是黑色棋子还是白色棋子(假设黑色棋子颜色较深,白色棋子颜色较浅)
                piece_color = "黑色" if color < 128 else "白色"

                # 存储棋子的位置和颜色
                piece_positions.append(((x, y), piece_color))

        # 按照 y 坐标从下到上排序
        piece_positions.sort(key=lambda pos: (pos[0][1], pos[0][0]))

        return piece_positions

    def draw_pieces(self):
        if self.corner_coordinates:
            # 绘制矩形角点
            for (x, y) in self.corner_coordinates:
                cv2.circle(self.image, (int(x), int(y)), 10, (0, 0, 255), -1)  # 红色圆点

            # 绘制角点之间的线
            for i in range(4):
                cv2.line(self.image, tuple(self.corner_coordinates[i]), tuple(self.corner_coordinates[(i + 1) % 4]),
                         (0, 255, 0), 2)  # 绿色线条

        # 分开标号黑白棋子,并按从下往上的顺序编号
        piece_positions = self.detect_pieces()
        black_counter = 1
        white_counter = 1

        for (x, y), color in piece_positions:
            if color == "黑色":
                cv2.circle(self.image, (x, y), 10, (255, 0, 0), -1)  # 蓝色圆点
                text_color = (255, 255, 255)  # 白色文本
                cv2.putText(self.image, str(black_counter), (x - 10, y + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, text_color, 1, cv2.LINE_AA)
                black_counter += 1
            else:
                cv2.circle(self.image, (x, y), 10, (128, 0, 128), -1)  # 紫色圆点
                text_color = (255, 255, 255)  # 白色文本
                cv2.putText(self.image, str(white_counter), (x - 10, y + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, text_color, 1, cv2.LINE_AA)
                white_counter += 1

        # 显示图像
        cv2.imshow('Detected Pieces', self.image)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

        # 保存绘制好的图像到文件
        cv2.imwrite(output_path, self.image)
        print(f"检测结果已保存到 {output_path}")

        cv2.waitKey(0)
        cv2.destroyAllWindows()

    def print_corners(self):
        if self.corner_coordinates:
            print("九宫格四个角的坐标信息:")
            for i, (x, y) in enumerate(self.corner_coordinates):
                print(f"角点 {i + 1}: (x: {x}, y: {y})")
        else:
            print("未能找到九宫格角点坐标信息。")

def update(x):
    # 更新回调函数
    detector.draw_pieces()

# 使用示例
if __name__ == "__main__":
    detector = ChessPieceDetector('22.jpg')
    detector.mask_rectangle()  # 屏蔽九宫格区域
    detector.print_corners()  # 打印九宫格四个角的坐标信息

    # 创建调整窗口
    cv2.namedWindow('Adjustments')

    # 创建滑动条
    cv2.createTrackbar('dp', 'Adjustments', 12, 20, update)
    cv2.createTrackbar('minDist', 'Adjustments', 30, 100, update)
    cv2.createTrackbar('param1', 'Adjustments', 50, 200, update)
    cv2.createTrackbar('param2', 'Adjustments', 30, 100, update)
    cv2.createTrackbar('minRadius', 'Adjustments', 10, 100, update)
    cv2.createTrackbar('maxRadius', 'Adjustments', 89, 100, update)



    detector.draw_pieces()  # 初始绘制
    cv2.waitKey(0)
    cv2.destroyAllWindows()

在这里插入图片描述
值得一提的是,由于仿真与实际的不同,特意在代码里面加入了调节阈值的窗口。
并且,由于算法的缺陷性,起初在检测的时候,棋子的中央会出现多个识别点。
这里我们团队创新性的引用了YOLOV5中的非极大值抑制(Non-Maximum Suppression)NMS算法。过滤掉不必要的边框。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值