OpenCV多边形填充的3个坑:从坐标转换到抗锯齿优化

从零构建:手把手教你用Python实现LabelMe与Mask图像的智能互转 本文详细介绍了如何使用Python实现LabelMe与Mask图像的智能互转,涵盖JSON到Mask的转换、多类别处理、轮廓提取算法选择等关键技术。通过实战代码示例和性能优化技巧,帮助开发者高效处理图像分割任务中的格式转换问题,提升计算机视觉项目的工作效率。 阅读详情

OpenCV多边形绘制实战:避开三大陷阱,实现像素级精准与视觉平滑

在计算机视觉项目的可视化环节,多边形绘制看似基础,却往往是决定最终输出质量的关键。无论是目标检测的边界框高亮、图像分割的掩码生成,还是数据标注工具的交互绘制,一个边缘平滑、填充完整、坐标精准的多边形,直接关系到算法的可信度与用户体验。然而,许多中高级开发者在调用 cv2.polylines 或 cv2.fillPoly 时,常会陷入一些隐蔽的“坑”中,导致图形断裂、边缘锯齿严重,甚至图形完全消失。这些问题并非源于算法复杂,而是对OpenCV底层像素操作机制和API细节的理解偏差。

今天,我们不谈泛泛而谈的API调用,而是聚焦于三个最折磨人、也最影响效果的典型痛点:坐标格式的隐秘陷阱、填充区域的意外断裂,以及边缘锯齿的视觉灾难。我们将结合Numpy数组的变形技巧、OpenCV的绘图管线原理,以及抗锯齿参数 LINE_AA 的深度优化,为你提供一套从问题根因到高性能解决方案的完整指南。无论你是正在构建高精度标注平台,还是需要为算法结果生成出版级可视化图表,这些经验都能让你事半功倍。

1. 坐标格式的“隐形杀手”:从列表嵌套到维度变换

第一个坑,也是最常见的错误,源于对OpenCV多边形坐标输入格式的误解。很多开发者按照直觉传入一个点列表,结果图形没有出现,控制台也不报错,调试过程令人抓狂。

1.1 理解OpenCV的坐标“语言”

OpenCV的绘图函数本质上是操作像素矩阵。对于多边形,它期望的输入不是一个简单的点列表,而是一个具有特定形状的NumPy数组。常见的错误做法是直接传入一个二维数组:

# 错误示例:图形可能无法绘制
points = np.array([[100, 150], [200, 50], [300, 150]], dtype=np.int32)
cv2.polylines(image, points, True, (0, 255, 0), 2)  # 这里会静默失败或行为异常

问题在于,cv2.polylines 和 cv2.fillPoly 的第一个多边形参数 pts,期望的是一个 “多边形列表”,即使你只画一个多边形。更具体地说,它要求一个Python列表,列表中的每个元素是一个形状为 (N, 1, 2) 的数组,其中 N 是顶点数。

注意:这里的 (N, 1, 2) 形状是OpenCV内部处理连续性和内存布局的遗留要求。最后一个维度 2 代表 (x, y) 坐标,中间的 1 维度在大多数现代OpenCV版本中已不是强制要求,但为了兼容性和避免意外,遵循这个格式是最稳妥的。

1.2 正确的坐标准备流程

要让多边形正确显示,你需要一个两步转换过程。下面是一个清晰的正确示例:

import cv2
import numpy as np

# 1. 定义顶点坐标(可以是列表或数组)
raw_points = [(50, 50), (300, 100), (250, 300), (80, 200)]

# 2. 转换为NumPy数组并指定int32类型(必须)
#    OpenCV内部使用整数坐标进行像素定位,使用float可能导致精度问题或错误
points_array = np.array(raw_points, dtype=np.int32)

# 3. 重塑为 (N, 1, 2) 形状
#    reshape中的-1表示自动计算该维度大小,这里是顶点数量N
points_reshaped = points_array.reshape((-1, 1, 2))

# 4. 用列表包裹这个数组(关键步骤!)
#    即使只画一个多边形,也必须放在列表中
pts = [points_reshaped]

# 5. 现在可以正确绘制
image = np.zeros((400, 400, 3), dtype=np.uint8)
cv2.polylines(image, pts, isClosed=True, color=(0, 255, 0), thickness=2)
cv2.fillPoly(image, pts, color=(255, 0, 0, 128))  # 注意:带透明度的颜色需要4通道图像

为了更直观地理解不同格式的差异,我整理了一个对比表格:

坐标格式示例代码cv2.polylines 是否接受可能的结果
列表嵌套数组(正确)pts = [np.array(..., np.int32).reshape(-1,1,2)]✅ 接受图形正常绘制
二维数组(错误)pts = np.array(..., np.int32)❌ 不接受无图形或报错
三维数组但未列表包裹pts = np.array(..., np.int32).reshape(-1,1,2)⚠️ 可能接受但行为未定义图形可能变形或部分绘制
浮点类型数组pts = [np.array(..., np.float32)]⚠️ 接受但危险坐标舍入导致位置偏移,边缘可能出现“毛刺”

1.3 多边形的批量绘制优化

当你需要绘制多个多边形时,性能问题开始显现。最差的做法是循环调用绘图函数:

# 低效做法:多次调用产生开销
for polygon in polygon_list:
    pts = [np.array(polygon, np.int32).reshape((-1, 1, 2))]
    cv2.fillPoly(image, pts, color)

高效的做法是将所有多边形合并到一个列表中,一次性传入:

# 高效做法:单次调用,显著提升性能
all_polygons = []
for polygon in polygon_list:
    # 每个多边形单独reshape
    shaped_poly = np.array(polygon, np.int32).reshape((-1, 1, 2))
    all_polygons.append(shaped_poly)

# 一次性绘制所有多边形
cv2.fillPoly(image, all_polygons, color)

这种批量处理方式尤其在地理信息系统(GIS)可视化或同时渲染数百个目标框时,性能提升可达数倍。我曾在处理卫星图像建筑物轮廓时,将渲染时间从秒级降低到毫秒级,关键就在于避免了不必要的函数调用开销。

2. 填充区域的“断裂之谜”:闭合性与顶点顺序

第二个坑出现在填充多边形时:你明明指定了填充,但区域内部出现了奇怪的空白或只有部分被填充。这通常不是API使用错误,而是几何逻辑问题。

2.1 闭合性:隐式与显式

多边形的“闭合”有两个层面:绘图函数的 isClosed 参数,以及顶点序列的物理闭合。cv2.polylines 的 isClosed=True 只会在最后一个点和第一个点之间画一条线,让图形“看起来”闭合。但 cv2.fillPoly 的填充逻辑不依赖这个参数,它完全基于你提供的顶点序列来计算填充区域。

如果填充出现断裂,首先要检查顶点序列:第一个点和最后一个点是否相同?即使 isClosed=True,填充算法也需要一个物理上闭合的轮廓。一个常见的错误是:

# 可能导致填充不完整的顶点序列
points = [(100, 100), (200, 100), (200, 200), (100, 200)]  # 四个点,未闭合
# fillPoly会尝试填充,但结果取决于算法实现,可能异常

正确的做法是确保序列闭合:

# 方法1:复制第一个点作为最后一个点
points = [(100, 100), (200, 100), (200, 200), (100, 200), (100, 100)]

# 方法2:使用np.array时直接构造闭合序列
points = np.array([[100, 100], [200, 100], [200, 200], [100, 200], [100, 100]], np.int32)

2.2 顶点顺序与自相交

即使序列闭合,如果顶点顺序混乱或多边形自相交,填充也会出问题。OpenCV的填充算法通常基于扫描线算法,它要求多边形是简单多边形(不自交),且顶点顺序一致(全部顺时针或全部逆时针)。

检查顶点顺序的一个实用技巧是计算多边形的有向面积。对于二维多边形,如果顶点按逆时针顺序排列,有向面积为正;顺时针则为负。你可以用以下函数快速验证:

def polygon_area(vertices):
    """计算多边形的有向面积(鞋带公式)"""
    x = vertices[:, 0]
    y = vertices[:, 1]
    return 0.5 * np.sum(x * np.roll(y, -1) - y * np.roll(x, -1))

# 示例:检查并纠正顶点顺序
vertices = np.array(points, dtype=np.float32)
area = polygon_area(vertices)

if area < 0:
    print("顶点为顺时针顺序,正在反转...")
    corrected_vertices = vertices[::-1]  # 反转数组使其逆时针
else:
    corrected_vertices = vertices

对于复杂形状(如凹多边形、带孔洞的多边形),cv2.fillPoly 仍然可以处理,但你需要将外轮廓和内轮廓(孔洞)作为不同的多边形传入,且内轮廓的顶点顺序应与外轮廓相反。例如,绘制一个带矩形孔洞的多边形:

# 创建画布
image = np.ones((400, 400, 3), dtype=np.uint8) * 255

# 外轮廓(大矩形,逆时针)
outer_contour = np.array([[50, 50], [350, 50], [350, 350], [50, 350], [50, 50]], np.int32)

# 内轮廓/孔洞(小矩形,顺时针)
inner_contour = np.array([[150, 150], [250, 150], [250, 250], [150, 250], [150, 150]], np.int32)

# 注意:两个轮廓都要reshape并放入列表
outer_reshaped = outer_contour.reshape((-1, 1, 2))
inner_reshaped = inner_contour.reshape((-1, 1, 2))

# 同时绘制外轮廓和孔洞
cv2.fillPoly(image, [outer_reshaped, inner_reshaped], color=(200, 200, 200))

这里的关键是:fillPoly 会填充所有多边形,但后续多边形会“挖掉”先前填充的区域,前提是它们有重叠且顺序正确。这种特性正好可以用来创建带孔洞的复杂形状。

2.3 填充算法的边界情况

在某些边缘情况下,即使顶点顺序正确且闭合,填充仍可能出现问题:

  • 极薄多边形:当多边形某个部分宽度小于1个像素时,扫描线算法可能无法正确识别内部区域。
  • 退化多边形:包含共线点或角度极小的锐角,数值精度问题可能导致填充异常。
  • 坐标超出画布:部分顶点在图像边界外,OpenCV会进行裁剪,但裁剪后的多边形可能变得不闭合。

对于这些问题,一个实用的解决方案是预先对多边形进行“膨胀”处理,确保所有线段都有足够的像素宽度:

def safe_fill_polygon(image, polygon, color, margin=1):
    """
    安全填充多边形,避免边界问题
    margin: 对多边形进行轻微膨胀的像素数,防止因精度问题导致的填充缺失
    """
    # 将多边形转换为掩码(单通道)
    mask = np.zeros(image.shape[:2], dtype=np.uint8)
    reshaped_poly = polygon.reshape((-1, 1, 2))
    
    # 先填充一个稍大的区域
    cv2.fillPoly(mask, [reshaped_poly], 255)
    
    # 如果需要,进行形态学膨胀(处理薄区域)
    if margin > 0:
        kernel = np.ones((margin*2+1, margin*2+1), np.uint8)
        mask = cv2.dilate(mask, kernel, iterations=1)
    
    # 应用掩码到原图
    image[mask > 0] = color
    return image

这种方法虽然增加了一些计算开销,但在生成高质量可视化结果时,能确保填充的完整性,特别是在处理算法生成的、可能不完美的多边形时。

3. 边缘锯齿的视觉灾难与抗锯齿优化

第三个坑是美学问题,也是专业性的体现:锯齿边缘。默认情况下,OpenCV的绘图函数使用 LINE_8(8连通)线型,这在斜线或曲线上会产生明显的锯齿(阶梯效应)。对于需要出版或展示的视觉输出,这种锯齿是不可接受的。

3.1 抗锯齿原理与 LINE_AA 参数

LINE_AA(抗锯齿)是OpenCV提供的一种线型,它使用高斯滤波在边缘像素之间创建平滑过渡。其原理不是绘制更细的线,而是通过调整边缘像素的透明度(alpha值)来模拟亚像素精度。

启用抗锯齿非常简单,只需在绘图函数中添加 lineType=cv2.LINE_AA 参数:

# 无抗锯齿 vs 抗锯齿对比
image_no_aa = np.ones((300, 400, 3), dtype=np.uint8) * 255
image_with_aa = image_no_aa.copy()

polygon = np.array([[50, 50], [300, 80], [250, 250], [80, 200]], np.int32)
polygon = polygon.reshape((-1, 1, 2))

# 无抗锯齿
cv2.polylines(image_no_aa, [polygon], True, (0, 0, 255), thickness=3, lineType=cv2.LINE_8)

# 有抗锯齿
cv2.polylines(image_with_aa, [polygon], True, (0, 0, 255), thickness=3, lineType=cv2.LINE_AA)

实际效果差异明显:LINE_8 的边缘呈阶梯状,而 LINE_AA 的边缘则是平滑的渐变。但要注意一个重要限制:LINE_AA 只对 cv2.polylines 的边框绘制有效,cv2.fillPoly 没有直接的抗锯齿参数。

3.2 填充多边形的抗锯齿技巧

那么,如何实现填充多边形的抗锯齿边缘呢?这里有几个实用方案:

方案一:边框叠加法 先绘制一个稍粗的抗锯齿边框,再填充内部区域(边框内缩一点以避免重叠瑕疵):

def draw_smooth_filled_polygon(image, polygon, fill_color, border_color, border_width=2):
    """
    绘制带平滑边缘的填充多边形
    原理:先画粗的抗锯齿边框,再填充稍小的内部区域
    """
    # 计算多边形的中心点,用于等比例缩放
    polygon = polygon.reshape(-1, 2)
    center = polygon.mean(axis=0)
    
    # 创建稍小的多边形用于填充(避免边框重叠)
    # 这里使用简单的向中心收缩,复杂多边形可能需要更精确的偏移算法
    scale = 0.95  # 收缩比例
    inner_polygon = (polygon - center) * scale + center
    inner_polygon = inner_polygon.astype(np.int32).reshape((-1, 1, 2))
    outer_polygon = polygon.astype(np.int32).reshape((-1, 1, 2))
    
    # 先填充内部
    cv2.fillPoly(image, [inner_polygon], fill_color)
    
    # 再绘制抗锯齿边框
    cv2.polylines(image, [outer_polygon], True, border_color, 
                  thickness=border_width, lineType=cv2.LINE_AA)
    
    return image

方案二:掩码模糊法 先绘制填充多边形,然后单独生成一个边缘掩码,对其进行高斯模糊后叠加:

def draw_soft_edge_polygon(image, polygon, color, blur_radius=3):
    """
    通过边缘模糊实现抗锯齿效果
    适合对性能要求不高但需要高质量输出的场景
    """
    # 创建单通道掩码
    mask = np.zeros(image.shape[:2], dtype=np.uint8)
    reshaped_poly = polygon.reshape((-1, 1, 2))
    
    # 在掩码上绘制多边形
    cv2.fillPoly(mask, [reshaped_poly], 255)
    
    # 对掩码边缘进行高斯模糊
    blurred_mask = cv2.GaussianBlur(mask, (blur_radius*2+1, blur_radius*2+1), 0)
    
    # 将模糊后的掩码归一化到0-1范围
    alpha_mask = blurred_mask.astype(np.float32) / 255.0
    
    # 应用颜色(考虑原图内容)
    for c in range(3):
        image[:, :, c] = (1 - alpha_mask) * image[:, :, c] + alpha_mask * color[c]
    
    return image

这种方法能产生非常平滑的边缘,但计算量较大,适合静态图像或预处理阶段。

3.3 性能与质量的权衡

在实际项目中,你需要在视觉质量和渲染性能之间做出权衡。以下是我的经验建议:

场景推荐方案理由
实时视频标注LINE_AA边框 + 普通填充平衡质量和速度,用户交互时帧率最重要
静态报告生成掩码模糊法质量优先,处理时间可以稍长
大规模批处理普通填充,后处理时统一抗锯齿保持处理流水线高效,最后统一美化
Web或移动端展示考虑使用矢量图形(SVG)而非栅格化无限缩放无质量损失,但需要额外转换

一个常见的误区是过度使用抗锯齿。对于非常小的多边形(如图标大小),抗锯齿可能使边缘过于模糊,反而降低清晰度。这时,可以动态调整抗锯齿策略:

def adaptive_draw_polygon(image, polygon, color, min_size=50):
    """
    自适应绘制:小多边形用普通方式,大多边形用抗锯齿
    min_size: 启用抗锯齿的最小多边形尺寸(对角线长度)
    """
    polygon_array = polygon.reshape(-1, 2)
    
    # 计算多边形边界框对角线长度
    x_min, y_min = polygon_array.min(axis=0)
    x_max, y_max = polygon_array.max(axis=0)
    diag_length = np.sqrt((x_max - x_min)**2 + (y_max - y_min)**2)
    
    reshaped_poly = polygon.reshape((-1, 1, 2))
    
    if diag_length < min_size:
        # 小多边形:普通绘制
        cv2.fillPoly(image, [reshaped_poly], color)
        cv2.polylines(image, [reshaped_poly], True, color, 1, cv2.LINE_8)
    else:
        # 大多边形:抗锯齿边框
        cv2.fillPoly(image, [reshaped_poly], color)
        cv2.polylines(image, [reshaped_poly], True, color, 2, cv2.LINE_AA)

这种自适应方法在绘制地图上的不同尺度要素时特别有用,既能保证大区域的美观,又能保持小要素的清晰。

4. 高级应用:从基础绘制到专业可视化

掌握了避开三大陷阱的技巧后,我们可以将这些知识应用到更复杂的实际场景中。多边形绘制不仅仅是画个形状,更是数据可视化、算法调试和用户交互的基础。

4.1 动态交互式标注工具

构建一个标注工具时,用户体验直接取决于多边形绘制的流畅性和准确性。下面是一个增强版的交互示例,包含了实时预览和智能吸附功能:

import cv2
import numpy as np

class PolygonAnnotator:
    def __init__(self, image_path):
        self.image = cv2.imread(image_path)
        if self.image is None:
            self.image = np.ones((600, 800, 3), dtype=np.uint8) * 255
            cv2.putText(self.image, "Interactive Polygon Tool", (100, 300), 
                       cv2.FONT_HERSHEY_SIMPLEX, 1.5, (100, 100, 100), 3)
        
        self.clone = self.image.copy()
        self.points = []
        self.drawing = False
        self.snap_threshold = 15  # 像素吸附阈值
        
    def mouse_callback(self, event, x, y, flags, param):
        current_display = self.image.copy()
        
        # 实时显示鼠标位置
        cv2.circle(current_display, (x, y), 3, (0, 0, 255), -1)
        
        # 检查是否吸附到现有点
        snapped = False
        for i, (px, py) in enumerate(self.points):
            distance = np.sqrt((x - px)**2 + (y - py)**2)
            if distance < self.snap_threshold:
                x, y = px, py  # 吸附到最近的点
                snapped = True
                cv2.circle(current_display, (x, y), 8, (255, 255, 0), 2)
                break
        
        if event == cv2.EVENT_LBUTTONDOWN:
            self.points.append((x, y))
            self.drawing = True
            
        elif event == cv2.EVENT_MOUSEMOVE and self.drawing:
            # 实时预览当前多边形
            if len(self.points) > 0:
                temp_points = self.points + [(x, y)]
                if len(temp_points) > 1:
                    # 绘制已确定的边
                    for i in range(len(temp_points)-1):
                        cv2.line(current_display, temp_points[i], temp_points[i+1], 
                                (0, 255, 0), 2, cv2.LINE_AA)
                    
                    # 绘制当前鼠标位置的临时边
                    cv2.line(current_display, temp_points[-2], (x, y), 
                            (0, 200, 200), 2, cv2.LINE_AA)
                    
                    # 绘制闭合预览(如果点数足够)
                    if len(temp_points) > 2:
                        cv2.line(current_display, temp_points[0], (x, y), 
                                (200, 0, 200), 1, cv2.LINE_AA)
        
        elif event == cv2.EVENT_RBUTTONDOWN and len(self.points) >= 3:
            # 完成多边形并填充
            pts_array = np.array(self.points, dtype=np.int32).reshape((-1, 1, 2))
            
            # 创建半透明填充层
            overlay = self.clone.copy()
            cv2.fillPoly(overlay, [pts_array], (0, 255, 255))
            cv2.addWeighted(overlay, 0.3, self.clone, 0.7, 0, self.clone)
            
            # 绘制抗锯齿边框
            cv2.polylines(self.clone, [pts_array], True, (0, 200, 200), 2, cv2.LINE_AA)
            
            # 标记顶点
            for i, (px, py) in enumerate(self.points):
                cv2.circle(self.clone, (px, py), 5, (0, 0, 255), -1)
                cv2.putText(self.clone, str(i+1), (px+5, py-5), 
                          cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
            
            self.image = self.clone.copy()
            self.points = []
            self.drawing = False
        
        # 显示吸附提示
        if snapped:
            cv2.putText(current_display, "SNAP", (x+10, y-10), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 100, 0), 2)
        
        cv2.imshow("Polygon Annotator", current_display)
    
    def run(self):
        cv2.namedWindow("Polygon Annotator")
        cv2.setMouseCallback("Polygon Annotator", self.mouse_callback)
        
        print("交互说明:")
        print("  左键点击:添加顶点(靠近已有顶点时会自动吸附)")
        print("  鼠标移动:实时预览多边形")
        print("  右键点击:完成并填充当前多边形(至少3个顶点)")
        print("  按 'r' 键:重置当前绘制")
        print("  按 's' 键:保存当前标注")
        print("  按 ESC 键:退出")
        
        while True:
            key = cv2.waitKey(1) & 0xFF
            
            if key == 27:  # ESC
                break
            elif key == ord('r'):
                self.points = []
                self.image = self.clone.copy()
            elif key == ord('s'):
                cv2.imwrite("annotated_result.png", self.clone)
                print("标注结果已保存为 'annotated_result.png'")
        
        cv2.destroyAllWindows()

# 使用示例
if __name__ == "__main__":
    annotator = PolygonAnnotator("your_image.jpg")
    annotator.run()

这个工具实现了几个关键特性:

  1. 实时预览:鼠标移动时动态显示多边形轮廓
  2. 智能吸附:新顶点靠近已有顶点时自动对齐,提高标注精度
  3. 抗锯齿绘制:使用 LINE_AA 获得平滑边缘
  4. 半透明填充:使用 addWeighted 实现非破坏性标注
  5. 顶点编号:标注后显示顶点顺序,便于后续处理

4.2 算法结果的可视化优化

在目标检测或图像分割项目中,我们经常需要将算法生成的多边形叠加到原图上。这里有几个提升可视化专业性的技巧:

技巧一:层次化颜色编码 不同类别的对象使用不同颜色,并通过透明度区分置信度:

def visualize_detections(image, detections, class_colors, alpha=0.5):
    """
    可视化检测结果,支持多类别和置信度透明度
    detections: 列表,每个元素为 [polygon, class_id, confidence]
    class_colors: 字典,class_id -> (B, G, R) 颜色
    """
    result = image.copy()
    overlay = image.copy()
    
    for polygon, class_id, confidence in detections:
        # 根据类别获取颜色
        base_color = class_colors.get(class_id, (0, 255, 255))
        
        # 根据置信度调整透明度
        fill_alpha = alpha * confidence
        border_alpha = min(1.0, confidence + 0.3)
        
        # 准备多边形数据
        pts = np.array(polygon, dtype=np.int32).reshape((-1, 1, 2))
        
        # 在覆盖层上填充
        cv2.fillPoly(overlay, [pts], base_color)
        
        # 绘制边框(更深的颜色,更高的不透明度)
        border_color = tuple(int(c * 0.7) for c in base_color)
        cv2.polylines(result, [pts], True, border_color, 2, cv2.LINE_AA)
    
    # 合并填充层
    cv2.addWeighted(overlay, alpha, result, 1 - alpha, 0, result)
    
    # 添加图例
    y_offset = 30
    for class_id, color in class_colors.items():
        cv2.rectangle(result, (10, y_offset-20), (30, y_offset), color, -1)
        cv2.putText(result, f"Class {class_id}", (40, y_offset), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
        y_offset += 30
    
    return result

技巧二:关注点可视化 对于关键点检测或需要突出多边形特定区域的情况:

def highlight_polygon_corners(image, polygon, corner_radius=8):
    """
    高亮显示多边形的角点和中心点
    """
    result = image.copy()
    pts = polygon.reshape(-1, 2)
    
    # 绘制多边形边框
    cv2.polylines(result, [polygon], True, (0, 180, 0), 2, cv2.LINE_AA)
    
    # 绘制角点
    for i, (x, y) in enumerate(pts):
        # 不同角点使用不同颜色
        color_ratio = i / len(pts)
        color = (int(255 * color_ratio), 100, int(255 * (1 - color_ratio)))
        
        cv2.circle(result, (x, y), corner_radius, color, -1)
        cv2.circle(result, (x, y), corner_radius, (255, 255, 255), 2)
        
        # 添加角点编号
        cv2.putText(result, str(i+1), (x+10, y-5), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
    
    # 计算并绘制中心点
    center_x = int(pts[:, 0].mean())
    center_y = int(pts[:, 1].mean())
    cv2.circle(result, (center_x, center_y), 6, (255, 0, 0), -1)
    cv2.circle(result, (center_x, center_y), 6, (255, 255, 255), 1)
    
    # 从中心到每个角点画线
    for (x, y) in pts:
        cv2.line(result, (center_x, center_y), (x, y), (255, 200, 0), 1, cv2.LINE_AA)
    
    return result

4.3 性能优化与大规模渲染

当需要渲染数百甚至数千个多边形时(如地图瓦片渲染、大规模点云可视化),性能成为关键考虑因素。以下是一些优化策略:

策略一:批量处理与预计算

class PolygonBatchRenderer:
    def __init__(self, image_size):
        self.image_size = image_size
        self.polygons = []  # 存储预处理的polygon数据
        self.colors = []    # 对应的颜色
        
    def add_polygon(self, vertices, color):
        """添加多边形到批处理列表,预先进行reshape"""
        pts = np.array(vertices, dtype=np.int32).reshape((-1, 1, 2))
        self.polygons.append(pts)
        self.colors.append(color)
    
    def render(self, background_color=(255, 255, 255)):
        """批量渲染所有多边形"""
        # 创建画布
        canvas = np.ones((self.image_size[1], self.image_size[0], 3), 
                        dtype=np.uint8) * background_color
        
        # 按面积排序(可选:先画大的,再画小的,避免过度覆盖)
        if len(self.polygons) > 100:  # 数量多时才排序
            areas = [cv2.contourArea(p) for p in self.polygons]
            sorted_indices = np.argsort(areas)[::-1]  # 从大到小
            
            sorted_polygons = [self.polygons[i] for i in sorted_indices]
            sorted_colors = [self.colors[i] for i in sorted_indices]
        else:
            sorted_polygons = self.polygons
            sorted_colors = self.colors
        
        # 批量填充(所有多边形一次性填充)
        for polygon, color in zip(sorted_polygons, sorted_colors):
            cv2.fillPoly(canvas, [polygon], color)
        
        # 批量绘制边框(如果需要)
        for polygon, color in zip(sorted_polygons, sorted_colors):
            # 使用稍深的颜色作为边框
            border_color = tuple(max(0, c-30) for c in color)
            cv2.polylines(canvas, [polygon], True, border_color, 1, cv2.LINE_AA)
        
        return canvas
    
    def clear(self):
        """清空批处理列表"""
        self.polygons.clear()
        self.colors.clear()

策略二:使用 fillConvexPoly 加速凸多边形渲染 对于凸多边形,OpenCV提供了更快的 cv2.fillConvexPoly 函数:

def render_convex_polygons_fast(polygons, colors, image_size):
    """快速渲染凸多边形(性能比fillPoly高30-50%)"""
    canvas = np.zeros((image_size[1], image_size[0], 3), dtype=np.uint8)
    
    for vertices, color in zip(polygons, colors):
        # 检查是否为凸多边形
        pts = np.array(vertices, dtype=np.int32)
        
        # 使用fillConvexPoly(要求多边形是凸的)
        cv2.fillConvexPoly(canvas, pts, color)
        
        # 绘制边框
        cv2.polylines(canvas, [pts.reshape((-1, 1, 2))], True, 
                     tuple(max(0, c-30) for c in color), 1, cv2.LINE_AA)
    
    return canvas

注意:fillConvexPoly 只适用于凸多边形。对于凹多边形,它会产生错误结果。在实际使用前,可以用 cv2.isContourConvex() 检查多边形是否为凸的。

策略三:多线程渲染 对于超大规模渲染,可以考虑将画布分区,使用多线程并行渲染:

from concurrent.futures import ThreadPoolExecutor
import threading

class ParallelPolygonRenderer:
    def __init__(self, num_threads=4):
        self.num_threads = num_threads
        self.canvas_lock = threading.Lock()
    
    def render_chunk(self, polygons_chunk, colors_chunk, canvas, start_x, start_y, chunk_size):
        """渲染画布的一个区块"""
        chunk_canvas = np.zeros((chunk_size[1], chunk_size[0], 3), dtype=np.uint8)
        
        for polygon, color in zip(polygons_chunk, colors_chunk):
            # 将多边形坐标转换到区块坐标系
            local_polygon = polygon.copy()
            local_polygon[:, :, 0] -= start_x
            local_polygon[:, :, 1] -= start_y
            
            # 只在区块内绘制
            cv2.fillPoly(chunk_canvas, [local_polygon], color)
        
        # 将区块内容复制到主画布
        with self.canvas_lock:
            canvas[start_y:start_y+chunk_size[1], start_x:start_x+chunk_size[0]] = chunk_canvas
    
    def parallel_render(self, polygons, colors, image_size):
        """并行渲染多边形"""
        canvas = np.zeros((image_size[1], image_size[0], 3), dtype=np.uint8)
        
        # 将多边形分配到不同的空间区域(简单按边界框划分)
        # 这里简化处理,实际可以根据多边形位置智能分区
        chunk_width = image_size[0] // 2
        chunk_height = image_size[1] // 2
        
        with ThreadPoolExecutor(max_workers=self.num_threads) as executor:
            futures = []
            
            # 四个区块
            for i in range(2):
                for j in range(2):
                    start_x = i * chunk_width
                    start_y = j * chunk_height
                    
                    # 筛选属于当前区块的多边形(简化:按中心点判断)
                    chunk_polygons = []
                    chunk_colors = []
                    
                    for polygon, color in zip(polygons, colors):
                        # 计算多边形中心
                        center_x = polygon[:, :, 0].mean()
                        center_y = polygon[:, :, 1].mean()
                        
                        if (start_x <= center_x < start_x + chunk_width and 
                            start_y <= center_y < start_y + chunk_height):
                            chunk_polygons.append(polygon)
                            chunk_colors.append(color)
                    
                    # 提交渲染任务
                    future = executor.submit(
                        self.render_chunk, 
                        chunk_polygons, chunk_colors, canvas,
                        start_x, start_y, (chunk_width, chunk_height)
                    )
                    futures.append(future)
            
            # 等待所有任务完成
            for future in futures:
                future.result()
        
        return canvas

这种并行渲染策略在处理数万个多边形时,可以将渲染时间从数秒减少到数百毫秒。不过要注意线程安全和内存开销,特别是在多边形数量极大时。

在实际项目中,我通常根据多边形数量动态选择渲染策略:少于100个用简单循环,100-1000个用批量处理,超过1000个考虑并行渲染。这种分层策略在保持代码简洁的同时,确保了大规模数据下的性能表现。

python+opencv填充图像不规则区域:fillPoly和fillConvexPoly区别、半透明填充方法 目录 一、用某种颜色填充 1)cv2.fillConvexPoly 2)cv2.fillPoly 二、半透明填充 一、用某种颜色填充 1)cv2.fillConvexPoly cv2.fillConvexPoly()函数,只能用来填充凸多边形。 只需要提供凸多边形的顶点即可。 例: img = np.zeros((1080, 1920, 3), np.uint8) trian... 阅读详情

相关推荐

别再纠结了!手把手教你根据项目需求选对目标检测框架(MMDetection/Detectron2/YOLOv8实战对比)

本文详细对比了三大目标检测框架MMDetection、Detectron2和YOLOv8的适用场景与实战技巧,帮助开发者根据项目需求精准选型。从毕业设计到工业落地,分析不同场景下的核心需求与框架优势,提供决策树和避坑指南,确保技术选型与项目目标高度匹配。

weixin_30342209的博客 556

从Shapefile到训练标签:用Python将矢量数据自动转为遥感图像分割掩码

本文详细解析了如何利用Python工具链(geopandas、rasterio和OpenCV)将Shapefile矢量数据自动转换为遥感图像分割掩码的全流程。从环境配置、坐标系对齐到矢量栅格化,再到内存优化和实战案例,帮助从业者高效生成高质量AI训练标签,提升遥感图像处理效率。

weixin_29061509的博客 218

保姆级教程:用Cesium的Primitive API实现火箭发射全流程动画(含代码避坑)

本文详细介绍了如何使用Cesium的Primitive API实现火箭发射全流程动画,包括模型控制、关节动画和精细时序管理。通过实战代码示例和性能优化技巧,帮助开发者高效构建高复杂度航天仿真系统,显著提升三维可视化效果与运行效率。

weixin_42565652的博客 215

cv2.polylines、cv2.fillPoly 和 多边形绘制分割结果Python函数(一)

.

先说一下,就简单地写点儿氵文,不是博客,只是我的笔记本/工具书 1万+

【论文阅读】2021-(Pidinet)Pixel Difference Networks for Efficient Edge Detection

文章目录摘要1. Introduction 摘要 motivations: 基于CNN的边缘检测的高性能是通过一个大的预训练CNN主干来实现的,该主干占用大量内存和能量。传统边缘检测器(如Canny、Sobel和LBP)很少被研究。 contributions: 提出了一种简单、轻量级但有效的架构,称为像素差分网络(PiDiNet),用于有效的边缘检测。PiDiNet采用了新颖的像素差卷积,将传统的边缘检测算子集成到现代CNN中流行的卷积运算中,以增强任务性能。 experiments: 在BSDS500

轻舟的万重重山 1万+

Ubuntu运行vmware时报错Vmware Unable to install all modules!Use shipped Linux kernel AIO access library解决。

Ubuntu运行vmware时报错Vmware Unable to install all modules! vmware --version 提示下面内容: [AppLoader] Use shipped Linux kernel AIO access library. An up-to-date "libaio" or "libaio1" package from your system is preferred. VMware Workstation 16.1.2 build-17966106

wto882dim的博客 2850

opencv 3.0 填充矩形 填充多边形 fillPoly(图像填充)

参考链接: https://blog.csdn.net/billbliss/article/details/43968291

weixin_38410551的博客 5786

海上风电经柔性直流送出系统的阻抗建模与稳定性分析

摘要 本文针对海上风电经柔性直流送出系统的稳定性问题,提出了一种基于阻抗建模的分析方法。研究建立了直驱永磁同步发电机(PMSG)的序阻抗解析模型和模块化多电平换流器(MMC)的交流侧小信号阻抗模型,通过Matlab/Simulink平台实现了完整的仿真验证系统。采用谐波线性化原理和端口阻抗分析法,揭示了风电机组与柔直换流器之间的阻抗匹配特性对系统稳定性的影响规律。研究结果表明,控制参数设置会显著改变系统阻抗特性,进而影响稳定裕度。文中提出的阻抗建模方法为分析海上风电柔直送出系统的宽频振荡问题提供了有效工具,

huanghm88的专栏 235

opencv之绘制多边形----cv2.polylines, cv2.fillPoly

代码展示 # -*- coding:utf-8 -*- import cv2 import numpy as np img = np.zeros((200, 200, 3), dtype=np.uint8) pts = np.array([[10, 10], [100, 10], [100, 100], [10, 100]], np.int32) # 数据类型必须为 int32 pts = pts.reshape((-1, 1, 2)) # 绘制未填充的多边形 # cv2.polylines(img

ancy的博客 1万+

【银河麒麟桌面系统】自定义ISO镜像操作实践

本文详细介绍了麒麟操作系统Kylin-Desktop-V10-SP1的ISO镜像定制流程。通过挂载原始镜像、解压文件系统、进入chroot环境进行系统定制(包括文件替换和软件管理),最终重构squashfs并生成新ISO。关键步骤包括:挂载镜像到mnt目录、解压filesystem.squashfs、替换源配置文件、在chroot环境中清理系统、重新生成squashfs文件以及使用mkisofs命令创建新镜像。文章特别标注了x86和ARM架构的不同命令参数,并提供了安装测试验证方法。整个过程展示了从原始镜像

信创运维工程师 · 银河麒麟 & 容器实战 | 金融生产环境迁移·等保加固 | 142篇原创·17万+阅读 2342

SAP 供应商寄售(VMI)完整实现方案

摘要:本文详细讲解了SAP系统中供应商寄售(Consignment)和VMI(供应商管理库存)的实现方法。核心逻辑是物料所有权属于供应商,仅在领用时才触发采购。文章包含配置步骤(物料主数据、供应商设置、定价协议、会计科目等)、端到端操作流程(收货、库存查询、领用、结算、付款)、关键差异比较(与普通采购的区别)、核心事务码和数据表。重点说明了寄售流程无需采购订单,通过信息记录维护价格,领用时才产生应付账款的特点,并提供了常见问题解决方案和VMI进阶优化建议。

金牌架构师 1047

opencv fillConvexPoly深究

Opencv FillconvexPoly函数给定一个多边形的角点(按顺序),就可以把角点内的多边形填充成想要的颜色,但是一个问题是角点之间的连线填充不填充,为了验证这个问题,写了几行脚本加以验证,又到了大家最喜欢的上代码时间了:import cv2 import numpy as np import matplotlib.pyplot as plt img = np.zeros((100, ...

newxuyangcao 8753

手把手教你学simulink(13.2)--构建一个单相并网逆变器Simulink仿真模型

基于Simulink的单相并网逆变器项目的详细仿真实例。这个项目将重点放在逆变器的详细建模、最大功率点跟踪(MPPT)控制策略、并网控制和仿真分析上。我们将使用MATLAB/Simulink来构建整个系统,并通过具体的代码和模块来实现各个功能。

MHD0815的博客 546

cv2.fillPoly 和 cv2.fillConvexPoly:非凸任意形状填充和凸填充

cv2.fillConvexPoly: 官方文档:The function fillConvexPoly draws a filled convex polygon. This function is much faster than the function fillPoly . It can fill not only convex polygons but any monotonic po...

sunyao_123的博客 3万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

用OpenCV制作游戏贴图遮罩:手把手教你用Python多边形生成透明通道(附完整代码)

本文详细介绍了如何使用OpenCV的Python库生成游戏贴图的透明通道遮罩。通过cv2.fillPoly和cv2.fillConvexPoly函数,开发者可以高效创建精确的多边形遮罩,适用于角色贴图、UI组件等游戏美术资源。文章包含完整代码示例,涵盖顶点处理、批量操作和性能优化等实用技巧,帮助提升游戏开发中的视觉效果制作效率。

star5的专栏 153

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain&#39;s apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath(&#39;DeepLearnToolbox&#39;));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand(&#39;state&#39;,0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W&#39;); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand(&#39;state&#39;,0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = &#39;sigm&#39;;%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, &#39;Too big error&#39;);```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand(&#39;state&#39;,0)sae = saesetup([784 100]);sae.ae{1}.activation_function = &#39;sigm&#39;;sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)&#39;)% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;;nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, &#39;Too big error&#39;);```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x&#39;,28,28,60000))/255;test_x = double(reshape(test_x&#39;,28,28,10000))/255;train_y = double(train_y&#39;);test_y = double(test_y&#39;);%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you&#39;ll get around 1.2% errorrand(&#39;state&#39;,0)cnn.layers = { struct(&#39;type&#39;, &#39;i&#39;) %input layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 6, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %sub sampling layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 12, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, &#39;Too big error&#39;);```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, &#39;Too big error&#39;);%% ex2 neural net with L2 weight decayrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex3 neural net with dropoutrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex4 neural net with sigmoid activation functionrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex5 plotting functionalityrand(&#39;state&#39;,0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = &#39;softmax&#39;; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand(&#39;state&#39;,0)nn = nnsetup([784 20 10]); nn.output = &#39;softmax&#39;; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Matlab栅格地图构建:从基础绘制到交互式设计全解析

栅格地图作为机器人路径规划、游戏AI寻路和物流仿真的基础,通过将连续空间离散化为矩阵形式,为算法提供了可计算的环境表示。其核心原理是将环境划分为网格单元,每个单元赋予通行或障碍状态,形成二维矩阵数据结构。在工程实践中,栅格地图的价值在于实现了环境建模与算法解耦,便于快速原型开发和算法测试。应用场景广泛覆盖机器人导航、自动驾驶仿真和游戏地图生成等领域。本文聚焦于使用Matlab进行栅格地图构建,详细介绍了从代码生成、交互式绘制到图像导入的全流程方法,特别针对路径规划中的地图预处理和数据结构优化提供了实用解决方

weixin_33736649的博客 367

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【Python-Opencv】cv2.fillPoly为非凸任意形状填充,cv2.fillConvexPoly为凸填充

cv2.fillConvexPoly(他只能处理凸的图形) 官方文档:The function fillConvexPoly draws a filled convex polygon. This function is much faster than the function fillPoly . It can fill not only convex polygons but any mo...

mjiansun的专栏 7712

OpenCV学习笔记之十 (cvFillPoly&cvPolyLine)

void CALEXDlg::fillArbitaryRectangle(IplImage* src,CvPoint pt1,CvPoint pt2,CvPoint pt3,CvPoint pt4) { int arr[1]; arr[0] = 4; CvPoint ** pt = new CvPoint*[1]; pt[0] = new CvPoint[4]; pt[0

u014488388的专栏 2197

基于深度学习的猫狗目标检测(Python+YOLO26m+Django)实现详解

详解基于深度学习的猫狗目标检测系统:YOLO26m迁移学习训练、数据划分与错标检测、ONNX导出、Energy阈值拒识、Django+Vue网站部署,附后端核心代码。

WEIXIN_JSJBSZDS的博客 378

基于 YOLOv8 的轴承缺陷检测(Web 版)

本项目是轴承表面缺陷检测模型的 Web 端实现。围绕 YOLOv8 在轴承表面缺陷数据集上完成四类缺陷的检测模型训练后,使用 FastAPI 把模型能力封装为可直接登录使用的 Web 检测平台。平台覆盖从数据准备、模型训练、指标评估,到推理服务、历史沉淀、统计分析的完整链路。除了常规的图片、批量、视频、摄像头检测,还把模型训练搬到了网页上:可以在浏览器里配置超参数发起真实训练,训练中在线调整学习率、查看实时曲线与日志,训练完成后把权重一键应用到检测服务,形成「训练 → 评估 → 检测」的闭环。

Michael_Jay的博客 289

航拍水葫芦检测数据集深度学习 目标检测 无人机航拍 水葫芦 水域监测 YOLO格式 数据集发布9031期

航拍水葫芦检测数据集深度学习 目标检测 无人机航拍 水葫芦 水域监测 YOLO格式 数据集发布9031期

qq_2529413168的博客 300

目标检测 16 道高频思考题全解——从 Faster R-CNN 到 YOLO v1 的复习手册

无论哪个版本,loss、输出形状、一次前向都只反映训练/前向过程,真正衡量检测好坏仍要靠独立验证集上的 Precision、Recall、AP、mAP——这是模型"检得准不准"的唯一可靠口径。随机初始化的 RPN 可能没提出与真实实例 IoU 达标(≥0.5)的正 RoI,那么即使图像里有有效 mask,这个 batch 也没有可用的正样本 RoI,loss 自然为 0。课程项目用的是 7×7 网格。把"在哪里"和"是什么"分开,每个候选区域都能拿到充分的特征计算资源做精细分类与回归,精度天花板更高。

anew___的博客 271

目标检测上机实战手册——四套环境、COCO 数据坑与训练闭环验证全清单

跑目标检测项目,难的不是"看懂原理",而是"跑通并确认它真的跑对了"。这份手册把 Faster R-CNN、Mask R-CNN、YOLO v3、YOLO v1 四个上机项目里所有工程层面的坑一次性整理出来:环境怎么隔离、COCO 标注怎么转、训练一 batch 到底在验证什么、哪些数字能看哪些不能信。

anew___的博客 165

无人机航拍垃圾检测数据集深度学习 目标检测 无人机航拍 垃圾检测 环境卫生 YOLO格式 数据集发布9029期

无人机航拍垃圾检测数据集深度学习 目标检测 无人机航拍 垃圾检测 环境卫生 YOLO格式 数据集发布9029期

qq_2529413168的博客 228

【无标题】无人机雾霾场景目标检测数据集深度学习 目标检测 无人机航拍 雾霾场景 车辆检测 YOLO格式 数据集发布9039期

无人机雾霾场景目标检测数据集深度学习 目标检测 无人机航拍 雾霾场景 车辆检测 YOLO格式 数据集发布9039期

qq_2529413168的博客 143

YOLO算法室内环境玻璃瓶目标检测数据8979张:类别与标注速览

该数据集聚焦于室内环境中各类玻璃瓶的识别与定位,涵盖多种材质、颜色和形状的玻璃容器,适用于家庭、仓储、零售等场景下的物品管理与自动化识别需求。该数据集包含训练集7812张、验证集818张、测试集349张,共计8979张图像,分布结构合理,能够有效支持模型训练、调优与性能评估。落地场景大致有智能仓储管理系统、自动分拣设备、零售商品识别系统以及家庭安防监控等领域,尤其适用于需要对玻璃制品进行分类与追踪的工业与消费场景,具备良好的落地潜力与实用前景。数据量、场景复杂度、标注质量三样都过关,这份数据可以直接开训;

小秋的博客 254
上一篇: SpringBoot整合虹软ArcFace实现RGB活体检测:3分钟搞定人脸识别安全升级
下一篇: 立创EDA新手必看:从原理图到PCB的完整设计流程(附快捷键大全)
普通网友
博客等级 码龄27年 205粉丝 1502原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值