基于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
相关产品推荐
相关产品推荐

