84浏览
查看: 84|回复: 3

[项目] 【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法

[复制链接]
【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图1

什么是 CanMV K230?
CanMV K230是一款高性价比的RISC-V边缘AI平台,凭借低功耗、强视觉处理能力和开放的开发生态,成为嵌入式AI开发的理想选择,尤其适合需要快速部署视觉与AI功能的创客、中小企业及教育场景。CanMV 是一套 AI 视觉开发平台,K230 是其核心芯片。该模块结合了图像采集、AI推理、边缘计算等能力,适合嵌入式视觉应用开发。

CanMV:类似 OpenMV 的图像处理框架,支持 Python 编程,简化视觉识别开发流程。
K230 芯片:嘉楠科技推出的 AIoT SoC,采用 RISC-V 架构,内置第三代 KPU(AI加速单元),算力高达 6 TOPS,性能是 K210 的 13.7 倍。


【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图2

驴友花雕  高级技神
 楼主|

发表于 7 天前

【花雕动手做】CanMV K230 AI视觉识别之三角形检测算法

知识点
三角形检测是计算机视觉中基于多边形拟合的经典任务,核心是从图像中识别满足 “三边闭合、内角和 180°” 几何特征的区域,通过 “轮廓提取 + 多边形逼近 + 几何验证” 实现,适配零件检测、路标识别等场景。

1、核心原理
三角形的核心特征是 “边数为 3 且闭合”,检测逻辑与矩形检测同源,关键步骤:
图像预处理:降噪、边缘检测(如 Canny),突出目标轮廓。
轮廓提取:找到图像中的连续像素区域(前景目标)。
多边形逼近:用approxPolyDP算法将轮廓简化为多边形,保留核心形状。
几何验证:筛选边数 = 3 的多边形,再验证三边长度匹配、内角和接近 180°,排除非三角形(如不规则三边形)。

2、主流算法(按场景适配)

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图1

3、实操示例(OpenCV 轮廓逼近实现)
适配 K230 摄像头实时采集,代码简洁可直接移植:
python
  1. import cv2
  2. import numpy as np
  3. def is_triangle(approx):
  4.     """验证多边形是否为三角形:边数3+内角和接近180°"""
  5.     if len(approx) != 3:
  6.         return False
  7.    
  8.     # 计算三边向量
  9.     vec1 = approx[1][0] - approx[0][0]
  10.     vec2 = approx[2][0] - approx[1][0]
  11.     vec3 = approx[0][0] - approx[2][0]
  12.    
  13.     # 计算三个内角(余弦定理)
  14.     def angle(vec_a, vec_b):
  15.         dot = np.dot(vec_a, vec_b)
  16.         norm_a = np.linalg.norm(vec_a)
  17.         norm_b = np.linalg.norm(vec_b)
  18.         if norm_a == 0 or norm_b == 0:
  19.             return 0
  20.         cos_ang = max(-1.0, min(1.0, dot/(norm_a*norm_b)))
  21.         return np.degrees(np.arccos(cos_ang))
  22.    
  23.     ang1 = angle(vec1, -vec3)  # 第一个内角
  24.     ang2 = angle(vec2, -vec1)  # 第二个内角
  25.     ang3 = angle(vec3, -vec2)  # 第三个内角
  26.     total_ang = ang1 + ang2 + ang3
  27.    
  28.     # 内角和在170°~190°之间(允许轻微偏差)
  29.     return 170 < total_ang < 190
  30. # 初始化K230摄像头
  31. cap = cv2.VideoCapture(0)
  32. cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
  33. cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
  34. if not cap.isOpened():
  35.     print("摄像头打开失败")
  36.     exit()
  37. while True:
  38.     ret, frame = cap.read()
  39.     if not ret:
  40.         break
  41.     img_copy = frame.copy()
  42.     gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  43.    
  44.     # 预处理:降噪+边缘检测
  45.     blur = cv2.GaussianBlur(gray, (5, 5), 0)
  46.     canny = cv2.Canny(blur, 50, 150)
  47.    
  48.     # 提取轮廓(只保留外部轮廓)
  49.     contours, _ = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  50.    
  51.     # 筛选三角形轮廓
  52.     for cnt in contours:
  53.         # 过滤小面积噪声
  54.         if cv2.contourArea(cnt) < 500:
  55.             continue
  56.         # 多边形逼近(epsilon控制精度)
  57.         epsilon = 0.03 * cv2.arcLength(cnt, True)
  58.         approx = cv2.approxPolyDP(cnt, epsilon, True)
  59.         # 验证是否为三角形
  60.         if is_triangle(approx):
  61.             # 绘制三角形(绿色边框+蓝色顶点)
  62.             cv2.drawContours(img_copy, [approx], -1, (0, 255, 0), 2)
  63.             for (x, y) in approx[:, 0]:
  64.                 cv2.circle(img_copy, (x, y), 4, (255, 0, 0), -1)
  65.             # 标注内角和
  66.             cv2.putText(img_copy, f"Ang: {ang1+ang2+ang3:.1f}", (approx[0][0][0], approx[0][0][1]-10),
  67.                         cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)
  68.    
  69.     cv2.imshow("Triangle Detection", img_copy)
  70.     if cv2.waitKey(1) == ord('q'):
  71.         break
  72. cap.release()
  73. cv2.destroyAllWindows()
复制代码

4、关键优化技巧(提升准确率)
预处理强化:
复杂背景用阈值分割(如二值化、自适应阈值),分离前景三角形。
用形态学操作(膨胀 + 腐蚀)修复断裂轮廓,增强边缘连续性。
参数调优:
轮廓逼近:epsilon设为轮廓长度的 0.02~0.04 倍,避免将三角形逼近为四边形。
面积阈值:根据目标大小调整(640x480 图像设为 500~2000 像素),过滤小噪声。
内角验证:放宽内角和范围至 170°~190°,适配轻微变形的三角形。
后处理去重:合并重叠或嵌套的三角形检测结果,保留面积最大的有效轮廓。

5、K230 平台适配:
用 NPU 加速 Canny 边缘检测和轮廓提取,CPU 专注多边形逼近和几何验证,单帧延迟 < 80ms。
针对 MIPI 摄像头采集的图像,通过 ISP 调整白平衡,避免因光照导致的轮廓模糊。

6、典型应用场景
工业质检:检测三角形状的零件(如三角支架、齿轮齿形)是否合格。
智能交通:识别三角形交通标志(如警告标志),辅助自动驾驶决策。
机器人视觉:定位三角形目标(如积木、零件),规划抓取路径。
文档识别:提取图纸中的三角形图形,实现工程图纸结构化分析。

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图3

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图2

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图4

回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 7 天前

【花雕动手做】CanMV K230 AI视觉识别之三角形检测算法

【花雕动手做】CanMV K230 AI 视觉识别模块之优化的三角形检测算法
项目测试实验代码

  1. #【花雕动手做】CanMV K230 AI 视觉识别模块之检测三角形
  2. # 优化的三角形检测算法 / Optimized Triangle Detection Algorithm
  3. # 专门针对黑色实心三角形在白色背景上的检测 / Specifically for black solid triangles on white background
  4. import time, os, sys, math
  5. from media.sensor import *
  6. from media.display import *
  7. from media.media import *
  8. # 图像分辨率设置 / Image resolution settings
  9. PICTURE_WIDTH = 640  # 降低分辨率提高帧率 / Lower resolution for better FPS
  10. PICTURE_HEIGHT = 480
  11. # 摄像头配置 / Camera configuration
  12. sensor = None
  13. # 显示模式选择 / Display mode selection
  14. DISPLAY_MODE = "LCD"
  15. # 绘制控制参数 / Drawing control parameters
  16. DRAW_GENERAL_TRIANGLES = False  # 是否绘制普通三角形(绿色)/ Whether to draw general triangles (green)
  17. # 根据显示模式设置分辨率 / Set resolution based on display mode
  18. if DISPLAY_MODE == "VIRT":
  19.     DISPLAY_WIDTH = ALIGN_UP(1920, 16)
  20.     DISPLAY_HEIGHT = 1080
  21. elif DISPLAY_MODE == "LCD":
  22.     DISPLAY_WIDTH = 640
  23.     DISPLAY_HEIGHT = 480
  24. else:
  25.     raise ValueError("Unknown DISPLAY_MODE, please select 'VIRT', 'LCD'")
  26. # 创建时钟对象用于FPS计算 / Create clock object for FPS calculation
  27. clock = time.clock()
  28. def distance(p1, p2):
  29.     """计算两点间距离 / Calculate distance between two points"""
  30.     return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
  31. def calculate_angle(p1, p2, p3):
  32.     """计算三点构成的角度 / Calculate angle formed by three points"""
  33.     try:
  34.         # 向量 p2->p1 和 p2->p3 / Vectors p2->p1 and p2->p3
  35.         v1 = (p1[0] - p2[0], p1[1] - p2[1])
  36.         v2 = (p3[0] - p2[0], p3[1] - p2[1])
  37.         # 计算向量长度 / Calculate vector lengths
  38.         len1 = math.sqrt(v1[0]**2 + v1[1]**2)
  39.         len2 = math.sqrt(v2[0]**2 + v2[1]**2)
  40.         if len1 == 0 or len2 == 0:
  41.             return 0
  42.         # 计算夹角余弦值 / Calculate cosine of the angle
  43.         cos_angle = (v1[0]*v2[0] + v1[1]*v2[1]) / (len1 * len2)
  44.         cos_angle = max(-1, min(1, cos_angle))  # 限制在[-1,1]范围内 / Clamp to [-1,1] range
  45.         # 转换为角度 / Convert to degrees
  46.         angle = math.acos(cos_angle) * 180 / math.pi
  47.         return angle
  48.     except:
  49.         return 0
  50. def is_equilateral_triangle(corners, side_tolerance=0.2, angle_tolerance=20):
  51.     """
  52.     判断三个角点是否构成等边三角形 / Check if three corners form an equilateral triangle
  53.     参数 / Parameters:
  54.         corners: 三个角点的坐标列表 / List of three corner coordinates
  55.         side_tolerance: 边长相对误差容忍度 / Side length relative tolerance (0.15 = 15%)
  56.         angle_tolerance: 角度误差容忍度 / Angle tolerance in degrees
  57.     返回 / Returns:
  58.         bool: 是否为等边三角形 / Whether it's an equilateral triangle
  59.     """
  60.     if len(corners) != 3:
  61.         return False
  62.     # 计算三边长度 / Calculate three side lengths
  63.     side1 = distance(corners[0], corners[1])
  64.     side2 = distance(corners[1], corners[2])
  65.     side3 = distance(corners[2], corners[0])
  66.     # 检查边长是否过小 / Check if sides are too small
  67.     if side1 < 10 or side2 < 10 or side3 < 10:
  68.         return False
  69.     # 方法1: 检查三边长度是否相等(允许相对误差)/ Method 1: Check if three sides are equal (with relative tolerance)
  70.     avg_side = (side1 + side2 + side3) / 3
  71.     side1_diff = abs(side1 - avg_side) / avg_side
  72.     side2_diff = abs(side2 - avg_side) / avg_side
  73.     side3_diff = abs(side3 - avg_side) / avg_side
  74.     sides_equal = (side1_diff <= side_tolerance and
  75.                    side2_diff <= side_tolerance and
  76.                    side3_diff <= side_tolerance)
  77.     # 方法2: 检查三个内角是否都接近60度 / Method 2: Check if three angles are close to 60 degrees
  78.     angle1 = calculate_angle(corners[0], corners[1], corners[2])
  79.     angle2 = calculate_angle(corners[1], corners[2], corners[0])
  80.     angle3 = calculate_angle(corners[2], corners[0], corners[1])
  81.     angles_equal = (abs(angle1 - 60) <= angle_tolerance and
  82.                     abs(angle2 - 60) <= angle_tolerance and
  83.                     abs(angle3 - 60) <= angle_tolerance)
  84.     # 两种方法都满足才认为是等边三角形 / Both methods must be satisfied
  85.     return sides_equal and angles_equal
  86. def is_valid_triangle(corners, min_area=100, max_area=10000, angle_tolerance=20):
  87.     """
  88.     判断三个角点是否构成有效三角形 / Check if three corners form a valid triangle
  89.     参数 / Parameters:
  90.         corners: 三个角点的坐标列表 / List of three corner coordinates
  91.         min_area: 最小面积阈值 / Minimum area threshold
  92.         max_area: 最大面积阈值 / Maximum area threshold
  93.         angle_tolerance: 角度容忍度 / Angle tolerance
  94.     返回 / Returns:
  95.         bool: 是否为有效三角形 / Whether it's a valid triangle
  96.     """
  97.     if len(corners) != 3:
  98.         return False
  99.     # 计算三边长度 / Calculate three side lengths
  100.     side1 = distance(corners[0], corners[1])
  101.     side2 = distance(corners[1], corners[2])
  102.     side3 = distance(corners[2], corners[0])
  103.     # 计算面积 / Calculate area using cross product
  104.     area = abs((corners[1][0] - corners[0][0]) * (corners[2][1] - corners[0][1]) -
  105.                (corners[2][0] - corners[0][0]) * (corners[1][1] - corners[0][1])) / 2
  106.     # 计算三个内角 / Calculate three interior angles
  107.     angle1 = calculate_angle(corners[0], corners[1], corners[2])
  108.     angle2 = calculate_angle(corners[1], corners[2], corners[0])
  109.     angle3 = calculate_angle(corners[2], corners[0], corners[1])
  110.     # 检查角度是否合理(三角形内角和应该接近180度)/ Check if angles are reasonable
  111.     angle_sum = angle1 + angle2 + angle3
  112.     if abs(angle_sum - 180) > 30:  # 允许一定误差 / Allow some error
  113.         return False
  114.     # 检查是否有过小的角度 / Check for too small angles
  115.     if angle1 < 20 or angle2 < 20 or angle3 < 20:
  116.         return False
  117.     return True
  118. def find_triangles_from_contours(img):
  119.     """
  120.     使用轮廓检测寻找三角形 / Find triangles using contour detection
  121.     参数 / Parameters:
  122.         img: 输入图像 / Input image
  123.     返回 / Returns:
  124.         list: 检测到的三角形列表 / List of detected triangles
  125.     """
  126.     triangles = []
  127.     try:
  128.         # 转换为灰度图像进行边缘检测 / Convert to grayscale for edge detection
  129.         img_gray = img.to_grayscale()
  130.         # 使用自适应阈值进行二值化 / Use adaptive threshold for binarization
  131.         # 针对黑色三角形,使用反向阈值 / For black triangles, use inverted threshold
  132.         img_binary = img_gray.binary([(0, 100)], invert=True)  # 检测黑色区域 / Detect black regions
  133.         # 寻找矩形作为候选区域 / Find rectangles as candidate regions
  134.         rects = img_binary.find_rects(threshold=1000, roi=None)
  135.         for rect in rects:
  136.             corners = rect.corners()
  137.             if corners is not None and len(corners) >= 3:
  138.                 # 尝试所有可能的三点组合 / Try all possible three-point combinations
  139.                 for i in range(len(corners)):
  140.                     for j in range(i+1, len(corners)):
  141.                         for k in range(j+1, len(corners)):
  142.                             triangle_corners = [corners[i], corners[j], corners[k]]
  143.                             if is_valid_triangle(triangle_corners):
  144.                                 center = ((triangle_corners[0][0] + triangle_corners[1][0] + triangle_corners[2][0]) // 3,
  145.                                          (triangle_corners[0][1] + triangle_corners[1][1] + triangle_corners[2][1]) // 3)
  146.                                 area = abs((triangle_corners[1][0] - triangle_corners[0][0]) *
  147.                                           (triangle_corners[2][1] - triangle_corners[0][1]) -
  148.                                           (triangle_corners[2][0] - triangle_corners[0][0]) *
  149.                                           (triangle_corners[1][1] - triangle_corners[0][1])) / 2
  150.                                 # 检查是否为等边三角形 / Check if it's an equilateral triangle
  151.                                 is_equilateral = is_equilateral_triangle(triangle_corners)
  152.                                 triangles.append({
  153.                                     'corners': triangle_corners,
  154.                                     'center': center,
  155.                                     'area': area,
  156.                                     'is_equilateral': is_equilateral,
  157.                                     'type': 'equilateral' if is_equilateral else 'general'
  158.                                 })
  159.                                 break
  160.     except Exception as e:
  161.         pass
  162.     return triangles
  163. def find_triangles_from_blobs(img):
  164.     """
  165.     使用blob检测寻找三角形 / Find triangles using blob detection
  166.     参数 / Parameters:
  167.         img: 输入图像 / Input image
  168.     返回 / Returns:
  169.         list: 检测到的三角形列表 / List of detected triangles
  170.     """
  171.     triangles = []
  172.     try:
  173.         # 检测黑色blob / Detect black blobs
  174.         blobs = img.find_blobs([(0, 50, -128, 127, -128, 127)],
  175.                               pixels_threshold=200,
  176.                               area_threshold=500,
  177.                               merge=True)
  178.         for blob in blobs:
  179.             # 获取blob的边界框 / Get blob bounding box
  180.             x, y, w, h = blob.rect()
  181.             # 检查长宽比是否合理 / Check if aspect ratio is reasonable
  182.             aspect_ratio = max(w, h) / min(w, h)
  183.             if aspect_ratio > 3:  # 过于细长的形状可能不是三角形 / Too elongated shape may not be triangle
  184.                 continue
  185.             # 使用blob的角点信息 / Use blob corner information
  186.             if hasattr(blob, 'corners') and callable(blob.corners):
  187.                 corners = blob.corners()
  188.                 if corners is not None and len(corners) >= 3:
  189.                     # 选择最合适的三个角点 / Select the most suitable three corners
  190.                     best_triangle = None
  191.                     best_score = 0
  192.                     for i in range(len(corners)):
  193.                         for j in range(i+1, len(corners)):
  194.                             for k in range(j+1, len(corners)):
  195.                                 triangle_corners = [corners[i], corners[j], corners[k]]
  196.                                 if is_valid_triangle(triangle_corners):
  197.                                     # 计算三角形质量分数 / Calculate triangle quality score
  198.                                     area = abs((triangle_corners[1][0] - triangle_corners[0][0]) *
  199.                                              (triangle_corners[2][1] - triangle_corners[0][1]) -
  200.                                              (triangle_corners[2][0] - triangle_corners[0][0]) *
  201.                                              (triangle_corners[1][1] - triangle_corners[0][1])) / 2
  202.                                     # 分数基于面积和形状规整度 / Score based on area and shape regularity
  203.                                     score = area
  204.                                     if score > best_score:
  205.                                         best_score = score
  206.                                         best_triangle = triangle_corners
  207.                     if best_triangle:
  208.                         center = ((best_triangle[0][0] + best_triangle[1][0] + best_triangle[2][0]) // 3,
  209.                                  (best_triangle[0][1] + best_triangle[1][1] + best_triangle[2][1]) // 3)
  210.                         # 检查是否为等边三角形 / Check if it's an equilateral triangle
  211.                         is_equilateral = is_equilateral_triangle(best_triangle)
  212.                         triangles.append({
  213.                             'corners': best_triangle,
  214.                             'center': center,
  215.                             'area': best_score,
  216.                             'is_equilateral': is_equilateral,
  217.                             'type': 'equilateral' if is_equilateral else 'general'
  218.                         })
  219.     except Exception as e:
  220.         print(f"Error in blob detection: {e}")
  221.     return triangles
  222. def find_triangles_optimized(img):
  223.     """
  224.     优化的三角形检测主函数 / Optimized main triangle detection function
  225.     参数 / Parameters:
  226.         img: 输入图像 / Input image
  227.     返回 / Returns:
  228.         list: 检测到的三角形列表 / List of detected triangles
  229.     """
  230.     all_triangles = []
  231.     # 方法1: 轮廓检测 / Method 1: Contour detection
  232.     triangles1 = find_triangles_from_contours(img)
  233.     all_triangles.extend(triangles1)
  234.     # 方法2: Blob检测 / Method 2: Blob detection
  235.     triangles2 = find_triangles_from_blobs(img)
  236.     all_triangles.extend(triangles2)
  237.     # 去重和筛选 / Remove duplicates and filter
  238.     unique_triangles = []
  239.     for triangle in all_triangles:
  240.         is_duplicate = False
  241.         for existing in unique_triangles:
  242.             # 检查中心点距离 / Check center point distance
  243.             center_dist = distance(triangle['center'], existing['center'])
  244.             if center_dist < 20:  # 如果中心点很近,认为是重复的 / If centers are close, consider duplicate
  245.                 is_duplicate = True
  246.                 break
  247.         if not is_duplicate:
  248.             unique_triangles.append(triangle)
  249.     # 按面积排序,优先返回较大的三角形 / Sort by area, prioritize larger triangles
  250.     unique_triangles.sort(key=lambda x: x['area'], reverse=True)
  251.     return unique_triangles[:5]  # 最多返回5个三角形 / Return at most 5 triangles
  252. def process_triangles(img, triangles):
  253.     """处理检测到的三角形 / Process detected triangles"""
  254.     print("【三角形检测结果 / Triangle Detection Results】")
  255.     equilateral_count = 0
  256.     general_count = 0
  257.     for i, triangle in enumerate(triangles):
  258.         corners = triangle['corners']
  259.         center = triangle['center']
  260.         area = triangle['area']
  261.         is_equilateral = triangle.get('is_equilateral', False)
  262.         triangle_type = triangle.get('type', 'general')
  263.         # 统计三角形类型 / Count triangle types
  264.         if is_equilateral:
  265.             equilateral_count += 1
  266.         else:
  267.             general_count += 1
  268.         # 根据三角形类型和控制参数决定是否绘制 / Draw based on triangle type and control parameters
  269.         should_draw = False
  270.         if is_equilateral:
  271.             should_draw = True
  272.             line_color = (255, 255, 0)  # 黄色表示等边三角形 / Yellow for equilateral triangles
  273.             corner_color = (255, 165, 0)  # 橙色角点 / Orange corner points
  274.             center_color = (255, 0, 255)  # 紫色中心点 / Purple center point
  275.             label = "EQUI"
  276.         elif DRAW_GENERAL_TRIANGLES:
  277.             should_draw = True
  278.             line_color = (0, 255, 0)  # 绿色表示普通三角形 / Green for general triangles
  279.             corner_color = (255, 0, 0)  # 红色角点 / Red corner points
  280.             center_color = (0, 0, 255)  # 蓝色中心点 / Blue center point
  281.             label = "GEN"
  282.         if should_draw:
  283.             # 绘制三角形边框 / Draw triangle outline
  284.             for j in range(3):
  285.                 start = corners[j]
  286.                 end = corners[(j + 1) % 3]
  287.                 img.draw_line(start[0], start[1], end[0], end[1], color=line_color, thickness=2)
  288.             # 绘制角点 / Draw corner points
  289.             for corner in corners:
  290.                 img.draw_circle(corner[0], corner[1], 4, color=corner_color, thickness=2)
  291.             # 绘制中心点 / Draw center point
  292.             img.draw_circle(center[0], center[1], 3, color=center_color, thickness=2)
  293.             # 在三角形旁边显示类型标签 / Display type label next to triangle
  294.             img.draw_string_advanced(center[0] + 10, center[1] - 10, 12, label, color=line_color, scale=1)
  295.         # 计算边长 / Calculate side lengths
  296.         side1 = distance(corners[0], corners[1])
  297.         side2 = distance(corners[1], corners[2])
  298.         side3 = distance(corners[2], corners[0])
  299.         # 计算三个内角用于验证 / Calculate three angles for verification
  300.         angle1 = calculate_angle(corners[0], corners[1], corners[2])
  301.         angle2 = calculate_angle(corners[1], corners[2], corners[0])
  302.         angle3 = calculate_angle(corners[2], corners[0], corners[1])
  303.         print(f"Triangle {i+1} ({triangle_type.upper()}): Center({center[0]}, {center[1]}), Area: {area:.1f}")
  304.         print(f"  Corners: {corners}")
  305.         print(f"  Side lengths: {side1:.1f}, {side2:.1f}, {side3:.1f}")
  306.         if is_equilateral:
  307.             print(f"  Angles: {angle1:.1f}°, {angle2:.1f}°, {angle3:.1f}°")
  308.             print(f"  ★ EQUILATERAL TRIANGLE DETECTED! ★")
  309.     print(f"Total triangles found: {len(triangles)} (Equilateral: {equilateral_count}, General: {general_count})")
  310.     print("【==============================】")
  311. try:
  312.     # 初始化摄像头 / Initialize camera
  313.     sensor = Sensor()
  314.     sensor.reset()
  315.     # 设置图像分辨率和格式 / Set image resolution and format
  316.     sensor.set_framesize(width=PICTURE_WIDTH, height=PICTURE_HEIGHT, chn=CAM_CHN_ID_0)
  317.     sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)
  318.     # 初始化显示器 / Initialize display
  319.     if DISPLAY_MODE == "VIRT":
  320.         Display.init(Display.VIRT, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, fps=60)
  321.     elif DISPLAY_MODE == "LCD":
  322.         Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)
  323.     # 初始化媒体管理器 / Initialize media manager
  324.     MediaManager.init()
  325.     sensor.run()
  326.     # 计算显示偏移量以居中显示 / Calculate display offsets for center alignment
  327.     x_offset = (DISPLAY_WIDTH - PICTURE_WIDTH) // 2
  328.     y_offset = (DISPLAY_HEIGHT - PICTURE_HEIGHT) // 2
  329.     print("三角形检测已启动,请将黑色三角形放在白色背景前...")
  330.     print("Triangle detection started, please place black triangles on white background...")
  331.     while True:
  332.         os.exitpoint()
  333.         clock.tick()  # 开始计时 / Start timing
  334.         # 捕获图像 / Capture image
  335.         img = sensor.snapshot(chn=CAM_CHN_ID_0)
  336.         # 寻找三角形 / Find triangles
  337.         triangles = find_triangles_optimized(img)
  338.         # 处理检测到的三角形 / Process detected triangles
  339.         equilateral_count = 0
  340.         general_count = 0
  341.         if len(triangles) > 0:
  342.             process_triangles(img, triangles)
  343.             # 统计等边三角形数量 / Count equilateral triangles
  344.             for triangle in triangles:
  345.                 if triangle.get('is_equilateral', False):
  346.                     equilateral_count += 1
  347.                 else:
  348.                     general_count += 1
  349.         # 显示FPS / Display FPS
  350.         fps = clock.fps()
  351.         print(f"FPS: {fps:.1f}")
  352.         # 在图像上显示信息 / Display information on image
  353.         img.draw_string_advanced(10, 10, 15, f"FPS: {fps:.1f}", color=(255, 255, 255), scale=2)
  354.         img.draw_string_advanced(10, 30, 15, f"Total: {len(triangles)}", color=(255, 255, 255), scale=2)
  355.         if equilateral_count > 0:
  356.             img.draw_string_advanced(10, 50, 15, f"Equilateral: {equilateral_count}", color=(255, 255, 0), scale=2)
  357.         if general_count > 0 and DRAW_GENERAL_TRIANGLES:
  358.             img.draw_string_advanced(10, 70, 15, f"General: {general_count}", color=(0, 255, 0), scale=2)
  359.         # 居中显示图像 / Display image centered
  360.         Display.show_image(img, x=x_offset, y=y_offset)
  361. except KeyboardInterrupt as e:
  362.     print("User Stop: ", e)
  363. except BaseException as e:
  364.     print(f"Exception: {e}")
  365. finally:
  366.     # 清理资源 / Cleanup resources
  367.     if isinstance(sensor, Sensor):
  368.         sensor.stop()
  369.     Display.deinit()
  370.     os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
  371.     time.sleep_ms(100)
  372.     MediaManager.deinit()
复制代码

代码解读:
程序总体功能
这是一个基于CanMV K230的高级实时三角形检测与分类系统,专门针对黑色实心三角形在白色背景上的检测,能够区分等边三角形和普通三角形。

系统架构设计
核心处理流程
text
图像采集 → 双算法检测 → 几何验证 → 等边分类 → 去重排序 → 可视化输出
1. 双算法融合架构
python
  1. def find_triangles_optimized(img):
  2.     triangles1 = find_triangles_from_contours(img)  # 轮廓检测
  3.     triangles2 = find_triangles_from_blobs(img)     # 区域检测
复制代码

双重检测策略:
轮廓检测:基于形状边界的精确几何检测
Blob检测:基于区域特征的快速区域检测
优势互补:提高不同场景下的检测成功率

核心技术组件详解
1. 几何验证系统
等边三角形验证算法
python
  1. def is_equilateral_triangle(corners, side_tolerance=0.2, angle_tolerance=20):
复制代码

双重验证机制:
边长验证:三边长度相对误差 ≤ 20%
python
  1. side1_diff = abs(side1 - avg_side) / avg_side ≤ 0.2
复制代码

角度验证:三个内角接近60° ± 20°

python
  1. abs(angle1 - 60) ≤ 20
复制代码

必须同时满足:确保几何特征的严格性

三角形有效性检查
python
  1. def is_valid_triangle(corners):
复制代码

有效性标准:
角度和验证:180° ± 30°(三角形基本定理)
最小角度限制:每个角 ≥ 20°(避免过于尖锐)
面积计算:使用向量叉积法精确计算

2. 双检测算法实现
轮廓检测方法
python
  1. def find_triangles_from_contours(img):
复制代码

处理流程:
灰度转换:img.to_grayscale()
二值化:反向阈值 (0, 100), invert=True 检测黑色区域
矩形检测:find_rects(threshold=1000) 获取候选区域
角点组合:从矩形角点中尝试所有三点组合
几何验证:对每个组合进行三角形验证

Blob检测方法
python
  1. def find_triangles_from_blobs(img):
复制代码

处理流程:
颜色空间检测:在LAB颜色空间中检测黑色区域
区域过滤:基于像素数、面积、长宽比过滤
角点分析:从blob角点中选择最佳三点组合
质量评分:基于面积选择最优三角形

3. 数学计算核心
距离计算
python
  1. def distance(p1, p2):
  2.     return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
复制代码

欧几里得距离公式
用于计算三角形边长

角度计算
python
  1. def calculate_angle(p1, p2, p3):
复制代码

向量几何方法:
向量构造:v1 = p1→p2, v2 = p3→p2
点积计算:v1·v2 = |v1||v2|cosθ
角度求解:θ = arccos((v1·v2)/(|v1||v2|))

面积计算
python
  1. area = abs((x2-x1)*(y3-y1) - (x3-x1)*(y2-y1)) / 2
复制代码

基于向量叉积的三角形面积公式
准确且计算高效

性能优化策略
1. 多级过滤机制
text
原始候选 → 几何验证 → 等边分类 → 中心点去重 → 面积排序 → 数量限制
2. 智能参数配置
python
  1. # 检测参数
  2. side_tolerance=0.2      # 20%边长误差容忍度
  3. angle_tolerance=20      # 20°角度误差容忍度
  4. pixels_threshold=200    # Blob最小像素数
  5. area_threshold=500      # 最小区域面积
  6. # 显示控制
  7. DRAW_GENERAL_TRIANGLES = False  # 只显示等边三角形,减少视觉干扰
  8. 3. 结果优化处理
  9. python
  10. # 去重:基于中心点距离
  11. center_dist = distance(triangle['center'], existing['center']) < 20
  12. # 排序:按面积降序
  13. unique_triangles.sort(key=lambda x: x['area'], reverse=True)
  14. # 限制:最多返回5个
  15. return unique_triangles[:5]
复制代码

可视化系统设计
颜色编码体系
python
  1. # 等边三角形视觉元素
  2. line_color = (255, 255, 0)      # 黄色边框 - 突出显示
  3. corner_color = (255, 165, 0)    # 橙色角点 - 关键特征
  4. center_color = (255, 0, 255)    # 紫色中心 - 重心位置
  5. label = "EQUI"                  # 类型标识
  6. # 普通三角形视觉元素(可选显示)
  7. line_color = (0, 255, 0)        # 绿色边框
  8. corner_color = (255, 0, 0)      # 红色角点  
  9. center_color = (0, 0, 255)      # 蓝色中心
复制代码

信息显示层级
python
  1. # 实时性能监控
  2. img.draw_string_advanced(10, 10, 15, f"FPS: {fps:.1f}", color=(255,255,255))
  3. # 检测结果统计
  4. img.draw_string_advanced(10, 30, 15, f"Total: {len(triangles)}", color=(255,255,255))
  5. # 等边三角形计数(突出显示)
  6. img.draw_string_advanced(10, 50, 15, f"Equilateral: {equilateral_count}", color=(255,255,0))
复制代码

控制台详细输出
text
【三角形检测结果】
Triangle 1 (EQUI): Center(320, 240), Area: 1250.5
  Corners: [(300,200), (340,200), (320,280)]
  Side lengths: 40.0, 44.7, 44.7
  Angles: 60.5°, 59.8°, 59.7°
  ★ EQUILATERAL TRIANGLE DETECTED! ★

算法工作流程
实时处理流水线
text
1. 图像采集 → 2. 双算法并行检测 → 3. 几何验证 →
4. 等边分类 → 5. 去重排序 → 6. 可视化渲染 → 7. 结果显示

回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 7 天前

【花雕动手做】CanMV K230 AI视觉识别之三角形检测算法

实验串口返回情况

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图1

实验场景图

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图2

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图3

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图5

【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法图4


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail