10浏览
查看: 10|回复: 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

驴友花雕  高级技神
 楼主|

发表于 2 小时前

【花雕动手做】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

回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 2 小时前

【花雕动手做】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. 结果显示

回复

使用道具 举报

驴友花雕  高级技神
 楼主|

发表于 2 小时前

【花雕动手做】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