You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python的L型棋盘格检测、外推及线段长度测算需求

问题描述

我有一个3D打印的L型棋盘格图案,每个方格尺寸为10×10mm。需开发Python代码实现以下功能:

  • 检测上传图像中的该棋盘格(支持任意朝向、含畸变/视差的多角度场景)
  • 基于检测结果在整幅图像上生成人工网格,以体现畸变与比例
  • 支持用户在图像上绘制线段,并基于人工网格测算线段的实际长度(单位:mm)

预期输出为:叠加网格的原图、单独的网格图、测算的线段长度。

我尝试过多种代码,但均无法精准检测棋盘格,线段长度测算误差极大,以下是我最新使用的代码:

import cv2
import numpy as np
import matplotlib.pyplot as plt
from tkinter import Tk, Canvas, PhotoImage
from tkinter.filedialog import askopenfilename
from tkinter.simpledialog import askinteger

class Rectangle:
    def __init__(self, x, y, width, height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height

    def intersects(self, other):
        return not (self.x + self.width < other.x or
                    other.x + other.width < self.x or
                    self.y + self.height < other.y or
                    other.y + other.height < self.y)

def load_image():
    Tk().withdraw()  # 隐藏主窗口
    file_path = askopenfilename(title="选择图像文件", filetypes=[("图像文件", "*.png;*.jpg;*.jpeg")])
    if not file_path:
        print("未选择文件,程序退出。")
        exit()
    return cv2.imread(file_path)

def detect_white_rectangles(image):
    # 转为灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 阈值分割检测白色区域
    _, thresholded = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)

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

    # 过滤面积过小的轮廓,得到矩形列表
    rectangles = [cv2.boundingRect(contour) for contour in contours if cv2.contourArea(contour) > 500]

    return rectangles

def plot_rectangles(image, rectangles):
    image_with_rectangles = image.copy()
    for (x, y, w, h) in rectangles:
        cv2.rectangle(image_with_rectangles, (x, y), (x + w, y + h), (0, 255, 0), 2)

    plt.imshow(cv2.cvtColor(image_with_rectangles, cv2.COLOR_BGR2RGB))
    plt.title("检测到的白色矩形")
    plt.show()

def plot_detected_squares(image, rectangles):
    image_with_rectangles = image.copy()
    for (x, y, w, h) in rectangles:
        cv2.rectangle(image_with_rectangles, (x, y), (x + w, y + h), (0, 255, 0), 2)

    plt.imshow(cv2.cvtColor(image_with_rectangles, cv2.COLOR_BGR2RGB))
    plt.title("检测到的白色方格")
    plt.show()

def extrapolate_pattern(image, rectangles):
    pattern_image = np.zeros_like(image)
    alternate = True
    for (x, y, w, h) in rectangles:
        if alternate:
            pattern_image[y:y + h, x:x + w] = [255, 255, 255]
        else:
            pattern_image[y:y + h, x:x + w] = [0, 0, 0]
        alternate = not alternate

    plt.imshow(cv2.cvtColor(pattern_image, cv2.COLOR_BGR2RGB))
    plt.title("检测方格的外推图案")
    plt.show()

def calculate_line_length(x1, y1, x2, y2, scale_factor=1.0):
    # 计算两点间欧氏距离
    length = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) * scale_factor
    return length

def draw_line(image, x1, y1, x2, y2):
    canvas = Canvas()
    canvas.create_line(x1, y1, x2, y2, fill="red", width=2)
    photo = PhotoImage(width=image.shape[1], height=image.shape[0])
    canvas.create_image((image.shape[1] / 2, image.shape[0] / 2), image=photo, state="normal")
    canvas.update()

def intersected_squares(line, rectangles):
    intersected = 0
    for rect in rectangles:
        if line.intersects(rect):
            intersected += 1
    return intersected

def generate_artificial_squares(image, pattern_size=30):
    artificial_image = np.zeros_like(image)

    # 根据图像尺寸和图案大小计算行列数
    rows = image.shape[0] // pattern_size
    cols = image.shape[1] // pattern_size

    for i in range(rows):
        for j in range(cols):
            x = j * pattern_size
            y = i * pattern_size
            cv2.rectangle(artificial_image, (x, y), (x + pattern_size, y + pattern_size), (255, 255, 255), -1)

    return artificial_image

def main():
    # 加载图像
    image = load_image()

    # 检测白色矩形
    rectangles = detect_white_rectangles(image)

    # 显示带检测矩形的原图
    plot_rectangles(image, rectangles)

    # 显示带检测方格的原图
    plot_detected_squares(image, rectangles)

    # 外推方格图案到整幅图像
    extrapolate_pattern(image, rectangles)

    # 生成同图案的人工方格
    artificial_squares = generate_artificial_squares(image)

    # 显示人工方格图
    plt.imshow(cv2.cvtColor(artificial_squares, cv2.COLOR_BGR2RGB))
    plt.title("同图案的人工方格")
    plt.show()

    # 允许用户绘制线段并计算长度
    print("点击图像上的两个点绘制线段。")
    print("按'q'键结束绘制并计算长度。")
    
    # 创建用于绘制的图像副本
    image_to_draw = image.copy()

    # 初始化线段坐标变量
    line_points = []

    # 鼠标事件回调函数
    def draw_line_callback(event, x, y, flags, param):
        nonlocal line_points
        if event == cv2.EVENT_LBUTTONDOWN:
            line_points.append((x, y))
            if len(line_points) == 2:
                # 在图像上绘制线段
                cv2.line(image_to_draw, line_points[0], line_points[1], (0, 0, 255), 2)
                # 计算线段长度
                scale_factor = 1.0 / 10.0  # 基于方格边长10mm的缩放因子
                length = calculate_line_length(line_points[0][0], line_points[0][1], line_points[1][0], line_points[1][1], scale_factor)
                print(f"绘制线段的实际长度约为 {length:.2f} mm。")
                # 重置线段坐标列表以绘制下一条线段
                line_points = []

    # 创建窗口并设置鼠标回调
    cv2.namedWindow("绘制线段")
    cv2.setMouseCallback("绘制线段", draw_line_callback)

    while True:
        # 显示带绘制线段的图像
        cv2.imshow("绘制线段", image_to_draw)
        key = cv2.waitKey(1) & 0xFF

        # 按'q'键退出
        if key == ord('q'):
            break

    # 关闭窗口并清理
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

内容的提问来源于stack exchange,提问作者Karl Stücker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:53:14