驴友花雕 发表于 7 天前

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



什么是 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 倍。



驴友花雕 发表于 7 天前

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

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

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

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



3、实操示例(OpenCV 轮廓逼近实现)
适配 K230 摄像头实时采集,代码简洁可直接移植:
python
import cv2

import numpy as np



def is_triangle(approx):

    """验证多边形是否为三角形:边数3+内角和接近180°"""

    if len(approx) != 3:

      return False

   

    # 计算三边向量

    vec1 = approx - approx

    vec2 = approx - approx

    vec3 = approx - approx

   

    # 计算三个内角(余弦定理)

    def angle(vec_a, vec_b):

      dot = np.dot(vec_a, vec_b)

      norm_a = np.linalg.norm(vec_a)

      norm_b = np.linalg.norm(vec_b)

      if norm_a == 0 or norm_b == 0:

            return 0

      cos_ang = max(-1.0, min(1.0, dot/(norm_a*norm_b)))

      return np.degrees(np.arccos(cos_ang))

   

    ang1 = angle(vec1, -vec3)# 第一个内角

    ang2 = angle(vec2, -vec1)# 第二个内角

    ang3 = angle(vec3, -vec2)# 第三个内角

    total_ang = ang1 + ang2 + ang3

   

    # 内角和在170°~190°之间(允许轻微偏差)

    return 170 < total_ang < 190



# 初始化K230摄像头

cap = cv2.VideoCapture(0)

cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)

cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)



if not cap.isOpened():

    print("摄像头打开失败")

    exit()



while True:

    ret, frame = cap.read()

    if not ret:

      break

    img_copy = frame.copy()

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

   

    # 预处理:降噪+边缘检测

    blur = cv2.GaussianBlur(gray, (5, 5), 0)

    canny = cv2.Canny(blur, 50, 150)

   

    # 提取轮廓(只保留外部轮廓)

    contours, _ = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

   

    # 筛选三角形轮廓

    for cnt in contours:

      # 过滤小面积噪声

      if cv2.contourArea(cnt) < 500:

            continue

      # 多边形逼近(epsilon控制精度)

      epsilon = 0.03 * cv2.arcLength(cnt, True)

      approx = cv2.approxPolyDP(cnt, epsilon, True)

      # 验证是否为三角形

      if is_triangle(approx):

            # 绘制三角形(绿色边框+蓝色顶点)

            cv2.drawContours(img_copy, , -1, (0, 255, 0), 2)

            for (x, y) in approx[:, 0]:

                cv2.circle(img_copy, (x, y), 4, (255, 0, 0), -1)

            # 标注内角和

            cv2.putText(img_copy, f"Ang: {ang1+ang2+ang3:.1f}", (approx, approx-10),

                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 255), 2)

   

    cv2.imshow("Triangle Detection", img_copy)

    if cv2.waitKey(1) == ord('q'):

      break



cap.release()

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

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

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







驴友花雕 发表于 7 天前

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

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

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

# 优化的三角形检测算法 / Optimized Triangle Detection Algorithm
# 专门针对黑色实心三角形在白色背景上的检测 / Specifically for black solid triangles on white background

import time, os, sys, math
from media.sensor import *
from media.display import *
from media.media import *

# 图像分辨率设置 / Image resolution settings
PICTURE_WIDTH = 640# 降低分辨率提高帧率 / Lower resolution for better FPS
PICTURE_HEIGHT = 480

# 摄像头配置 / Camera configuration
sensor = None

# 显示模式选择 / Display mode selection
DISPLAY_MODE = "LCD"

# 绘制控制参数 / Drawing control parameters
DRAW_GENERAL_TRIANGLES = False# 是否绘制普通三角形(绿色)/ Whether to draw general triangles (green)

# 根据显示模式设置分辨率 / Set resolution based on display mode
if DISPLAY_MODE == "VIRT":
    DISPLAY_WIDTH = ALIGN_UP(1920, 16)
    DISPLAY_HEIGHT = 1080
elif DISPLAY_MODE == "LCD":
    DISPLAY_WIDTH = 640
    DISPLAY_HEIGHT = 480
else:
    raise ValueError("Unknown DISPLAY_MODE, please select 'VIRT', 'LCD'")

# 创建时钟对象用于FPS计算 / Create clock object for FPS calculation
clock = time.clock()

def distance(p1, p2):
    """计算两点间距离 / Calculate distance between two points"""
    return math.sqrt((p1 - p2)**2 + (p1 - p2)**2)

def calculate_angle(p1, p2, p3):
    """计算三点构成的角度 / Calculate angle formed by three points"""
    try:
      # 向量 p2->p1 和 p2->p3 / Vectors p2->p1 and p2->p3
      v1 = (p1 - p2, p1 - p2)
      v2 = (p3 - p2, p3 - p2)

      # 计算向量长度 / Calculate vector lengths
      len1 = math.sqrt(v1**2 + v1**2)
      len2 = math.sqrt(v2**2 + v2**2)

      if len1 == 0 or len2 == 0:
            return 0

      # 计算夹角余弦值 / Calculate cosine of the angle
      cos_angle = (v1*v2 + v1*v2) / (len1 * len2)
      cos_angle = max(-1, min(1, cos_angle))# 限制在[-1,1]范围内 / Clamp to [-1,1] range

      # 转换为角度 / Convert to degrees
      angle = math.acos(cos_angle) * 180 / math.pi
      return angle
    except:
      return 0

def is_equilateral_triangle(corners, side_tolerance=0.2, angle_tolerance=20):
    """
    判断三个角点是否构成等边三角形 / Check if three corners form an equilateral triangle

    参数 / Parameters:
      corners: 三个角点的坐标列表 / List of three corner coordinates
      side_tolerance: 边长相对误差容忍度 / Side length relative tolerance (0.15 = 15%)
      angle_tolerance: 角度误差容忍度 / Angle tolerance in degrees

    返回 / Returns:
      bool: 是否为等边三角形 / Whether it's an equilateral triangle
    """
    if len(corners) != 3:
      return False

    # 计算三边长度 / Calculate three side lengths
    side1 = distance(corners, corners)
    side2 = distance(corners, corners)
    side3 = distance(corners, corners)

    # 检查边长是否过小 / Check if sides are too small
    if side1 < 10 or side2 < 10 or side3 < 10:
      return False

    # 方法1: 检查三边长度是否相等(允许相对误差)/ Method 1: Check if three sides are equal (with relative tolerance)
    avg_side = (side1 + side2 + side3) / 3
    side1_diff = abs(side1 - avg_side) / avg_side
    side2_diff = abs(side2 - avg_side) / avg_side
    side3_diff = abs(side3 - avg_side) / avg_side

    sides_equal = (side1_diff <= side_tolerance and
                   side2_diff <= side_tolerance and
                   side3_diff <= side_tolerance)

    # 方法2: 检查三个内角是否都接近60度 / Method 2: Check if three angles are close to 60 degrees
    angle1 = calculate_angle(corners, corners, corners)
    angle2 = calculate_angle(corners, corners, corners)
    angle3 = calculate_angle(corners, corners, corners)

    angles_equal = (abs(angle1 - 60) <= angle_tolerance and
                  abs(angle2 - 60) <= angle_tolerance and
                  abs(angle3 - 60) <= angle_tolerance)

    # 两种方法都满足才认为是等边三角形 / Both methods must be satisfied
    return sides_equal and angles_equal

def is_valid_triangle(corners, min_area=100, max_area=10000, angle_tolerance=20):
    """
    判断三个角点是否构成有效三角形 / Check if three corners form a valid triangle

    参数 / Parameters:
      corners: 三个角点的坐标列表 / List of three corner coordinates
      min_area: 最小面积阈值 / Minimum area threshold
      max_area: 最大面积阈值 / Maximum area threshold
      angle_tolerance: 角度容忍度 / Angle tolerance

    返回 / Returns:
      bool: 是否为有效三角形 / Whether it's a valid triangle
    """
    if len(corners) != 3:
      return False

    # 计算三边长度 / Calculate three side lengths
    side1 = distance(corners, corners)
    side2 = distance(corners, corners)
    side3 = distance(corners, corners)

    # 计算面积 / Calculate area using cross product
    area = abs((corners - corners) * (corners - corners) -
               (corners - corners) * (corners - corners)) / 2

    # 计算三个内角 / Calculate three interior angles
    angle1 = calculate_angle(corners, corners, corners)
    angle2 = calculate_angle(corners, corners, corners)
    angle3 = calculate_angle(corners, corners, corners)

    # 检查角度是否合理(三角形内角和应该接近180度)/ Check if angles are reasonable
    angle_sum = angle1 + angle2 + angle3
    if abs(angle_sum - 180) > 30:# 允许一定误差 / Allow some error
      return False

    # 检查是否有过小的角度 / Check for too small angles
    if angle1 < 20 or angle2 < 20 or angle3 < 20:
      return False

    return True

def find_triangles_from_contours(img):
    """
    使用轮廓检测寻找三角形 / Find triangles using contour detection

    参数 / Parameters:
      img: 输入图像 / Input image

    返回 / Returns:
      list: 检测到的三角形列表 / List of detected triangles
    """
    triangles = []

    try:
      # 转换为灰度图像进行边缘检测 / Convert to grayscale for edge detection
      img_gray = img.to_grayscale()

      # 使用自适应阈值进行二值化 / Use adaptive threshold for binarization
      # 针对黑色三角形,使用反向阈值 / For black triangles, use inverted threshold
      img_binary = img_gray.binary([(0, 100)], invert=True)# 检测黑色区域 / Detect black regions

      # 寻找矩形作为候选区域 / Find rectangles as candidate regions
      rects = img_binary.find_rects(threshold=1000, roi=None)

      for rect in rects:
            corners = rect.corners()
            if corners is not None and len(corners) >= 3:
                # 尝试所有可能的三点组合 / Try all possible three-point combinations
                for i in range(len(corners)):
                  for j in range(i+1, len(corners)):
                        for k in range(j+1, len(corners)):
                            triangle_corners = , corners, corners]
                            if is_valid_triangle(triangle_corners):
                              center = ((triangle_corners + triangle_corners + triangle_corners) // 3,
                                       (triangle_corners + triangle_corners + triangle_corners) // 3)
                              area = abs((triangle_corners - triangle_corners) *
                                          (triangle_corners - triangle_corners) -
                                          (triangle_corners - triangle_corners) *
                                          (triangle_corners - triangle_corners)) / 2

                              # 检查是否为等边三角形 / Check if it's an equilateral triangle
                              is_equilateral = is_equilateral_triangle(triangle_corners)

                              triangles.append({
                                    'corners': triangle_corners,
                                    'center': center,
                                    'area': area,
                                    'is_equilateral': is_equilateral,
                                    'type': 'equilateral' if is_equilateral else 'general'
                              })
                              break
    except Exception as e:
      pass

    return triangles

def find_triangles_from_blobs(img):
    """
    使用blob检测寻找三角形 / Find triangles using blob detection

    参数 / Parameters:
      img: 输入图像 / Input image

    返回 / Returns:
      list: 检测到的三角形列表 / List of detected triangles
    """
    triangles = []

    try:
      # 检测黑色blob / Detect black blobs
      blobs = img.find_blobs([(0, 50, -128, 127, -128, 127)],
                              pixels_threshold=200,
                              area_threshold=500,
                              merge=True)

      for blob in blobs:
            # 获取blob的边界框 / Get blob bounding box
            x, y, w, h = blob.rect()

            # 检查长宽比是否合理 / Check if aspect ratio is reasonable
            aspect_ratio = max(w, h) / min(w, h)
            if aspect_ratio > 3:# 过于细长的形状可能不是三角形 / Too elongated shape may not be triangle
                continue

            # 使用blob的角点信息 / Use blob corner information
            if hasattr(blob, 'corners') and callable(blob.corners):
                corners = blob.corners()
                if corners is not None and len(corners) >= 3:
                  # 选择最合适的三个角点 / Select the most suitable three corners
                  best_triangle = None
                  best_score = 0

                  for i in range(len(corners)):
                        for j in range(i+1, len(corners)):
                            for k in range(j+1, len(corners)):
                              triangle_corners = , corners, corners]
                              if is_valid_triangle(triangle_corners):
                                    # 计算三角形质量分数 / Calculate triangle quality score
                                    area = abs((triangle_corners - triangle_corners) *
                                             (triangle_corners - triangle_corners) -
                                             (triangle_corners - triangle_corners) *
                                             (triangle_corners - triangle_corners)) / 2

                                    # 分数基于面积和形状规整度 / Score based on area and shape regularity
                                    score = area
                                    if score > best_score:
                                        best_score = score
                                        best_triangle = triangle_corners

                  if best_triangle:
                        center = ((best_triangle + best_triangle + best_triangle) // 3,
                                 (best_triangle + best_triangle + best_triangle) // 3)

                        # 检查是否为等边三角形 / Check if it's an equilateral triangle
                        is_equilateral = is_equilateral_triangle(best_triangle)

                        triangles.append({
                            'corners': best_triangle,
                            'center': center,
                            'area': best_score,
                            'is_equilateral': is_equilateral,
                            'type': 'equilateral' if is_equilateral else 'general'
                        })
    except Exception as e:
      print(f"Error in blob detection: {e}")

    return triangles

def find_triangles_optimized(img):
    """
    优化的三角形检测主函数 / Optimized main triangle detection function

    参数 / Parameters:
      img: 输入图像 / Input image

    返回 / Returns:
      list: 检测到的三角形列表 / List of detected triangles
    """
    all_triangles = []

    # 方法1: 轮廓检测 / Method 1: Contour detection
    triangles1 = find_triangles_from_contours(img)
    all_triangles.extend(triangles1)

    # 方法2: Blob检测 / Method 2: Blob detection
    triangles2 = find_triangles_from_blobs(img)
    all_triangles.extend(triangles2)

    # 去重和筛选 / Remove duplicates and filter
    unique_triangles = []
    for triangle in all_triangles:
      is_duplicate = False
      for existing in unique_triangles:
            # 检查中心点距离 / Check center point distance
            center_dist = distance(triangle['center'], existing['center'])
            if center_dist < 20:# 如果中心点很近,认为是重复的 / If centers are close, consider duplicate
                is_duplicate = True
                break

      if not is_duplicate:
            unique_triangles.append(triangle)

    # 按面积排序,优先返回较大的三角形 / Sort by area, prioritize larger triangles
    unique_triangles.sort(key=lambda x: x['area'], reverse=True)

    return unique_triangles[:5]# 最多返回5个三角形 / Return at most 5 triangles

def process_triangles(img, triangles):
    """处理检测到的三角形 / Process detected triangles"""
    print("【三角形检测结果 / Triangle Detection Results】")
    equilateral_count = 0
    general_count = 0

    for i, triangle in enumerate(triangles):
      corners = triangle['corners']
      center = triangle['center']
      area = triangle['area']
      is_equilateral = triangle.get('is_equilateral', False)
      triangle_type = triangle.get('type', 'general')

      # 统计三角形类型 / Count triangle types
      if is_equilateral:
            equilateral_count += 1
      else:
            general_count += 1

      # 根据三角形类型和控制参数决定是否绘制 / Draw based on triangle type and control parameters
      should_draw = False
      if is_equilateral:
            should_draw = True
            line_color = (255, 255, 0)# 黄色表示等边三角形 / Yellow for equilateral triangles
            corner_color = (255, 165, 0)# 橙色角点 / Orange corner points
            center_color = (255, 0, 255)# 紫色中心点 / Purple center point
            label = "EQUI"
      elif DRAW_GENERAL_TRIANGLES:
            should_draw = True
            line_color = (0, 255, 0)# 绿色表示普通三角形 / Green for general triangles
            corner_color = (255, 0, 0)# 红色角点 / Red corner points
            center_color = (0, 0, 255)# 蓝色中心点 / Blue center point
            label = "GEN"

      if should_draw:
            # 绘制三角形边框 / Draw triangle outline
            for j in range(3):
                start = corners
                end = corners[(j + 1) % 3]
                img.draw_line(start, start, end, end, color=line_color, thickness=2)

            # 绘制角点 / Draw corner points
            for corner in corners:
                img.draw_circle(corner, corner, 4, color=corner_color, thickness=2)

            # 绘制中心点 / Draw center point
            img.draw_circle(center, center, 3, color=center_color, thickness=2)

            # 在三角形旁边显示类型标签 / Display type label next to triangle
            img.draw_string_advanced(center + 10, center - 10, 12, label, color=line_color, scale=1)

      # 计算边长 / Calculate side lengths
      side1 = distance(corners, corners)
      side2 = distance(corners, corners)
      side3 = distance(corners, corners)

      # 计算三个内角用于验证 / Calculate three angles for verification
      angle1 = calculate_angle(corners, corners, corners)
      angle2 = calculate_angle(corners, corners, corners)
      angle3 = calculate_angle(corners, corners, corners)

      print(f"Triangle {i+1} ({triangle_type.upper()}): Center({center}, {center}), Area: {area:.1f}")
      print(f"Corners: {corners}")
      print(f"Side lengths: {side1:.1f}, {side2:.1f}, {side3:.1f}")
      if is_equilateral:
            print(f"Angles: {angle1:.1f}°, {angle2:.1f}°, {angle3:.1f}°")
            print(f"★ EQUILATERAL TRIANGLE DETECTED! ★")

    print(f"Total triangles found: {len(triangles)} (Equilateral: {equilateral_count}, General: {general_count})")
    print("【==============================】")

try:
    # 初始化摄像头 / Initialize camera
    sensor = Sensor()
    sensor.reset()

    # 设置图像分辨率和格式 / Set image resolution and format
    sensor.set_framesize(width=PICTURE_WIDTH, height=PICTURE_HEIGHT, chn=CAM_CHN_ID_0)
    sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)

    # 初始化显示器 / Initialize display
    if DISPLAY_MODE == "VIRT":
      Display.init(Display.VIRT, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, fps=60)
    elif DISPLAY_MODE == "LCD":
      Display.init(Display.ST7701, width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, to_ide=True)

    # 初始化媒体管理器 / Initialize media manager
    MediaManager.init()
    sensor.run()

    # 计算显示偏移量以居中显示 / Calculate display offsets for center alignment
    x_offset = (DISPLAY_WIDTH - PICTURE_WIDTH) // 2
    y_offset = (DISPLAY_HEIGHT - PICTURE_HEIGHT) // 2

    print("三角形检测已启动,请将黑色三角形放在白色背景前...")
    print("Triangle detection started, please place black triangles on white background...")

    while True:
      os.exitpoint()
      clock.tick()# 开始计时 / Start timing

      # 捕获图像 / Capture image
      img = sensor.snapshot(chn=CAM_CHN_ID_0)

      # 寻找三角形 / Find triangles
      triangles = find_triangles_optimized(img)

      # 处理检测到的三角形 / Process detected triangles
      equilateral_count = 0
      general_count = 0
      if len(triangles) > 0:
            process_triangles(img, triangles)
            # 统计等边三角形数量 / Count equilateral triangles
            for triangle in triangles:
                if triangle.get('is_equilateral', False):
                  equilateral_count += 1
                else:
                  general_count += 1

      # 显示FPS / Display FPS
      fps = clock.fps()
      print(f"FPS: {fps:.1f}")

      # 在图像上显示信息 / Display information on image
      img.draw_string_advanced(10, 10, 15, f"FPS: {fps:.1f}", color=(255, 255, 255), scale=2)
      img.draw_string_advanced(10, 30, 15, f"Total: {len(triangles)}", color=(255, 255, 255), scale=2)
      if equilateral_count > 0:
            img.draw_string_advanced(10, 50, 15, f"Equilateral: {equilateral_count}", color=(255, 255, 0), scale=2)
      if general_count > 0 and DRAW_GENERAL_TRIANGLES:
            img.draw_string_advanced(10, 70, 15, f"General: {general_count}", color=(0, 255, 0), scale=2)

      # 居中显示图像 / Display image centered
      Display.show_image(img, x=x_offset, y=y_offset)

except KeyboardInterrupt as e:
    print("User Stop: ", e)
except BaseException as e:
    print(f"Exception: {e}")
finally:
    # 清理资源 / Cleanup resources
    if isinstance(sensor, Sensor):
      sensor.stop()
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    MediaManager.deinit()
代码解读:
程序总体功能
这是一个基于CanMV K230的高级实时三角形检测与分类系统,专门针对黑色实心三角形在白色背景上的检测,能够区分等边三角形和普通三角形。

系统架构设计
核心处理流程
text
图像采集 → 双算法检测 → 几何验证 → 等边分类 → 去重排序 → 可视化输出
1. 双算法融合架构
python
def find_triangles_optimized(img):

    triangles1 = find_triangles_from_contours(img)# 轮廓检测

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

核心技术组件详解
1. 几何验证系统
等边三角形验证算法
python
def is_equilateral_triangle(corners, side_tolerance=0.2, angle_tolerance=20):
双重验证机制:
边长验证:三边长度相对误差 ≤ 20%
python
side1_diff = abs(side1 - avg_side) / avg_side ≤ 0.2
角度验证:三个内角接近60° ± 20°

python
abs(angle1 - 60) ≤ 20
必须同时满足:确保几何特征的严格性

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

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

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

3. 数学计算核心
距离计算
python
def distance(p1, p2):

    return math.sqrt((p1 - p2)**2 + (p1 - p2)**2)
欧几里得距离公式
用于计算三角形边长

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

面积计算
python
area = abs((x2-x1)*(y3-y1) - (x3-x1)*(y2-y1)) / 2
基于向量叉积的三角形面积公式
准确且计算高效

性能优化策略
1. 多级过滤机制
text
原始候选 → 几何验证 → 等边分类 → 中心点去重 → 面积排序 → 数量限制
2. 智能参数配置
python
# 检测参数

side_tolerance=0.2      # 20%边长误差容忍度

angle_tolerance=20      # 20°角度误差容忍度

pixels_threshold=200    # Blob最小像素数

area_threshold=500      # 最小区域面积



# 显示控制

DRAW_GENERAL_TRIANGLES = False# 只显示等边三角形,减少视觉干扰

3. 结果优化处理

python

# 去重:基于中心点距离

center_dist = distance(triangle['center'], existing['center']) < 20



# 排序:按面积降序

unique_triangles.sort(key=lambda x: x['area'], reverse=True)



# 限制:最多返回5个

return unique_triangles[:5]
可视化系统设计
颜色编码体系
python
# 等边三角形视觉元素

line_color = (255, 255, 0)      # 黄色边框 - 突出显示

corner_color = (255, 165, 0)    # 橙色角点 - 关键特征

center_color = (255, 0, 255)    # 紫色中心 - 重心位置

label = "EQUI"                  # 类型标识



# 普通三角形视觉元素(可选显示)

line_color = (0, 255, 0)      # 绿色边框

corner_color = (255, 0, 0)      # 红色角点

center_color = (0, 0, 255)      # 蓝色中心
信息显示层级
python
# 实时性能监控

img.draw_string_advanced(10, 10, 15, f"FPS: {fps:.1f}", color=(255,255,255))



# 检测结果统计

img.draw_string_advanced(10, 30, 15, f"Total: {len(triangles)}", color=(255,255,255))



# 等边三角形计数(突出显示)

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视觉识别之三角形检测算法

实验串口返回情况



实验场景图










页: [1]
查看完整版本: 【花雕动手做】CanMV K230 AI 视觉识别之三角形检测算法