欢迎光临
我们一直在努力

lingbot-depth-vitl14实战教程:Python OpenCV绘制深度图轮廓线并叠加原图标注

lingbot-depth-vitl14实战教程:Python OpenCV绘制深度图轮廓线并叠加原图标注

1. 引言:从深度图到轮廓标注

深度估计模型能帮我们“看见”世界的三维结构,但一张彩色的深度热力图,有时候并不直观。想象一下,你拿到一张由lingbot-depth-vitl14生成的深度图,上面用不同颜色表示远近,但你想快速知道场景中物体的边界在哪里,或者想把深度信息直接标注在原始图片上,让同事或客户一眼就能看懂。

这就是我们今天要解决的问题:如何用Python和OpenCV,从深度图中提取轮廓线,并把它精准地叠加回原始RGB图像上?

这个功能非常实用。比如在机器人导航中,你可以把障碍物的轮廓直接画在摄像头画面上;在AR应用中,可以把虚拟物体放置的深度边界可视化;在工业检测里,能清晰标注出零件的三维边缘。

lingbot-depth-pretrain-vitl-14(简称lingbot-depth-vitl14)是一个基于DINOv2 ViT-L/14编码器的深度估计模型,有3.21亿参数。它不仅能从单张RGB图片估计深度,还能用RGB+稀疏深度图补全出完整的深度信息。我们今天就用它生成的深度图作为起点,教你一步步实现轮廓提取和叠加标注。

2. 环境准备与深度图获取

在开始画轮廓之前,我们得先有深度图。这里有两种方式:一种是直接用lingbot-depth-vitl14镜像生成,另一种是使用现成的深度图文件。

2.1 部署lingbot-depth-vitl14镜像

如果你还没有深度图,可以快速部署一个lingbot-depth-vitl14实例:

  • 在镜像市场搜索 ins-lingbot-depth-vitl14-v1
  • 选择 insbase-cuda124-pt250-dual-v7 这个底座
  • 点击部署,等待1-2分钟实例启动
  • 访问 http://<你的实例IP>:7860 打开Web界面
  • 在Web界面上传一张RGB图片,选择“Monocular Depth”模式,点击生成,就能得到深度图了。你可以直接下载PNG格式的伪彩色深度图,也可以调用REST API获取原始数据。

    2.2 准备Python环境

    我们需要安装几个关键的Python库:

    pip install opencv-python numpy matplotlib

    如果你打算直接从API获取深度数据,还需要安装requests:

    pip install requests

    2.3 获取深度图的两种方式

    方式一:从WebUI下载后使用

    这是最简单的方式。在WebUI生成深度图后,点击下载按钮,保存为depth_colormap.png。同时保存你的原始RGB图片,比如original.jpg。

    方式二:通过REST API获取原始数据

    如果你需要更精确的深度值(单位是米,不是伪彩色),可以通过API获取:

    import requests
    import numpy as np
    import cv2
    import base64
    import json

    # 你的实例IP和端口
    API_URL = "http://<实例IP>:8000/predict"

    # 准备请求数据
    def get_depth_from_api(image_path):
    # 读取并编码图片
    with open(image_path, "rb") as f:
    image_bytes = f.read()
    image_b64 = base64.b64encode(image_bytes).decode('utf-8')

    # 构造请求
    payload = {
    "image": image_b64,
    "mode": "monocular", # 单目深度估计模式
    "return_npy": True # 返回原始npy数据
    }

    # 发送请求
    response = requests.post(API_URL, json=payload)

    if response.status_code == 200:
    result = response.json()

    # 获取深度图数据(npy格式的base64)
    depth_npy_b64 = result.get("depth_npy")
    if depth_npy_b64:
    # 解码npy数据
    depth_bytes = base64.b64decode(depth_npy_b64)
    depth_array = np.frombuffer(depth_bytes, dtype=np.float32)

    # 从metadata获取图像尺寸并reshape
    metadata = result.get("metadata", {})
    height = metadata.get("height", 480)
    width = metadata.get("width", 640)

    depth_map = depth_array.reshape((height, width))
    return depth_map
    else:
    print(f"API请求失败: {response.status_code}")
    return None

    # 使用示例
    depth_map = get_depth_from_api("your_image.jpg")
    if depth_map is not None:
    print(f"深度图尺寸: {depth_map.shape}")
    print(f"深度范围: {depth_map.min():.2f}m ~ {depth_map.max():.2f}m")

    3. 深度图预处理:为轮廓提取做准备

    直接从模型得到的深度图不能直接用来提取轮廓,我们需要做一些预处理。深度图可能有噪声,深度值范围也可能不合适,这些都会影响轮廓提取的效果。

    3.1 读取和显示深度图

    首先,我们看看怎么读取不同格式的深度图:

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt

    def load_depth_image(depth_path, is_colormap=True):
    """
    加载深度图
    :param depth_path: 深度图文件路径
    :param is_colormap: 是否为伪彩色图(True)或原始灰度图(False)
    :return: 深度值数组(单位:米)
    """
    # 读取图像
    depth_img = cv2.imread(depth_path, cv2.IMREAD_UNCHANGED)

    if depth_img is None:
    raise ValueError(f"无法读取图像: {depth_path}")

    if is_colormap:
    # 伪彩色图通常是3通道的BGR图像
    # 我们需要将其转换为单通道的深度值
    # 这里假设深度图使用INFERNO色彩映射(lingbot-depth默认)
    # 实际应用中,你可能需要根据色彩映射反向计算深度值
    print("检测到伪彩色深度图,建议使用原始npy数据获取精确深度值")
    # 临时方案:转换为灰度图作为近似
    gray = cv2.cvtColor(depth_img, cv2.COLOR_BGR2GRAY)
    # 简单归一化到0-1范围(这不是真实的深度值,仅用于演示)
    depth_normalized = gray.astype(np.float32) / 255.0
    return depth_normalized
    else:
    # 如果是原始深度数据(单通道浮点图)
    if len(depth_img.shape) == 2:
    return depth_img.astype(np.float32)
    else:
    # 如果是3通道但存储的是深度值
    return depth_img[:, :, 0].astype(np.float32)

    def visualize_depth(depth_map, title="深度图"):
    """
    可视化深度图
    """
    plt.figure(figsize=(12, 5))

    # 原始深度图
    plt.subplot(1, 2, 1)
    plt.imshow(depth_map, cmap='inferno')
    plt.colorbar(label='深度值(归一化)')
    plt.title(f"{title} – 伪彩色显示")

    # 深度值直方图
    plt.subplot(1, 2, 2)
    plt.hist(depth_map.flatten(), bins=50, alpha=0.7, color='blue')
    plt.xlabel('深度值')
    plt.ylabel('像素数量')
    plt.title('深度值分布')
    plt.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

    # 使用示例
    depth_map = load_depth_image("depth_colormap.png", is_colormap=True)
    print(f"深度图尺寸: {depth_map.shape}")
    print(f"深度值范围: {depth_map.min():.4f} ~ {depth_map.max():.4f}")

    visualize_depth(depth_map)

    3.2 深度图归一化与滤波

    深度图通常会有噪声,特别是边缘区域。我们可以用一些滤波技术来平滑噪声,同时保持边缘清晰:

    def preprocess_depth(depth_map, filter_size=5, normalize=True):
    """
    预处理深度图:滤波和归一化
    :param depth_map: 原始深度图
    :param filter_size: 中值滤波的核大小
    :param normalize: 是否归一化到0-255
    :return: 处理后的深度图
    """
    # 1. 中值滤波去除椒盐噪声
    if filter_size > 0:
    depth_filtered = cv2.medianBlur(depth_map.astype(np.float32), filter_size)
    else:
    depth_filtered = depth_map.copy()

    # 2. 双边滤波(保边去噪)
    # 注意:双边滤波计算较慢,对小图像效果更好
    if depth_filtered.shape[0] < 1000: # 只对小图像使用
    depth_filtered = cv2.bilateralFilter(
    depth_filtered.astype(np.float32),
    d=9, # 邻域直径
    sigmaColor=75, # 颜色空间标准差
    sigmaSpace=75 # 坐标空间标准差
    )

    # 3. 归一化到0-255(8位无符号整数)
    if normalize:
    depth_min = depth_filtered.min()
    depth_max = depth_filtered.max()

    # 避免除零
    if depth_max – depth_min > 1e-6:
    depth_normalized = ((depth_filtered – depth_min) /
    (depth_max – depth_min) * 255)
    else:
    depth_normalized = depth_filtered * 0

    depth_uint8 = depth_normalized.astype(np.uint8)
    return depth_uint8, depth_min, depth_max
    else:
    return depth_filtered, depth_filtered.min(), depth_filtered.max()

    # 使用示例
    depth_uint8, depth_min, depth_max = preprocess_depth(depth_map, filter_size=5)

    print(f"原始深度范围: {depth_map.min():.4f} ~ {depth_map.max():.4f}")
    print(f"处理后深度范围: {depth_min:.4f} ~ {depth_max:.4f}")
    print(f"归一化后范围: 0 ~ 255 (uint8)")

    # 显示处理前后的对比
    plt.figure(figsize=(10, 4))
    plt.subplot(1, 2, 1)
    plt.imshow(depth_map, cmap='inferno')
    plt.title('原始深度图')
    plt.colorbar()

    plt.subplot(1, 2, 2)
    plt.imshow(depth_uint8, cmap='gray')
    plt.title('处理后深度图')
    plt.colorbar()
    plt.show()

    4. 轮廓提取:从深度到边界

    有了预处理好的深度图,我们现在可以提取轮廓了。OpenCV提供了多种轮廓检测方法,我们将探索几种最实用的。

    4.1 基础轮廓提取方法

    def extract_depth_contours(depth_image, method='canny', threshold1=30, threshold2=100):
    """
    从深度图中提取轮廓
    :param depth_image: 预处理后的深度图(uint8)
    :param method: 轮廓提取方法 ('canny', 'sobel', 'laplacian', 'threshold')
    :param threshold1: Canny低阈值或二值化阈值
    :param threshold2: Canny高阈值
    :return: 轮廓图像和轮廓列表
    """
    contours_img = np.zeros_like(depth_image)

    if method == 'canny':
    # Canny边缘检测 – 最常用的方法
    edges = cv2.Canny(depth_image, threshold1, threshold2)
    contours_img = edges

    elif method == 'sobel':
    # Sobel算子 – 对深度渐变敏感
    sobel_x = cv2.Sobel(depth_image, cv2.CV_64F, 1, 0, ksize=3)
    sobel_y = cv2.Sobel(depth_image, cv2.CV_64F, 0, 1, ksize=3)
    sobel_magnitude = np.sqrt(sobel_x**2 + sobel_y**2)
    sobel_normalized = cv2.normalize(sobel_magnitude, None, 0, 255, cv2.NORM_MINMAX)
    contours_img = sobel_normalized.astype(np.uint8)

    elif method == 'laplacian':
    # Laplacian算子 – 检测二阶导数过零点
    laplacian = cv2.Laplacian(depth_image, cv2.CV_64F)
    laplacian_abs = np.absolute(laplacian)
    laplacian_normalized = cv2.normalize(laplacian_abs, None, 0, 255, cv2.NORM_MINMAX)
    contours_img = laplacian_normalized.astype(np.uint8)

    elif method == 'threshold':
    # 阈值分割 – 简单但有效
    _, binary = cv2.threshold(depth_image, threshold1, 255, cv2.THRESH_BINARY)
    contours_img = binary

    # 查找轮廓(仅用于Canny和阈值方法)
    if method in ['canny', 'threshold']:
    # 注意:findContours会修改输入图像,所以使用副本
    contours, hierarchy = cv2.findContours(
    contours_img.copy(),
    cv2.RETR_EXTERNAL, # 只检测外部轮廓
    cv2.CHAIN_APPROX_SIMPLE # 简化轮廓点
    )
    return contours_img, contours
    else:
    return contours_img, []

    # 测试不同方法
    methods = ['canny', 'sobel', 'laplacian', 'threshold']
    fig, axes = plt.subplots(2, 2, figsize=(12, 10))

    for idx, method in enumerate(methods):
    row = idx // 2
    col = idx % 2

    contours_img, contours = extract_depth_contours(
    depth_uint8,
    method=method,
    threshold1=50 if method == 'threshold' else 30,
    threshold2=150
    )

    axes[row, col].imshow(contours_img, cmap='gray')
    axes[row, col].set_title(f'{method.upper()} 方法')

    if method in ['canny', 'threshold'] and contours:
    # 在原图上绘制轮廓
    contour_overlay = cv2.cvtColor(depth_uint8, cv2.COLOR_GRAY2BGR)
    cv2.drawContours(contour_overlay, contours, -1, (0, 255, 0), 2)
    axes[row, col].imshow(cv2.cvtColor(contour_overlay, cv2.COLOR_BGR2RGB))

    axes[row, col].axis('off')

    plt.tight_layout()
    plt.show()

    4.2 自适应轮廓提取

    深度图的不同区域可能需要不同的参数。我们可以使用自适应阈值来改进轮廓提取:

    def adaptive_contour_extraction(depth_image,
    block_size=11,
    c_value=2,
    use_morphology=True):
    """
    自适应轮廓提取
    :param depth_image: 深度图(uint8)
    :param block_size: 自适应阈值的邻域大小(必须为奇数)
    :param c_value: 从均值减去的常数
    :param use_morphology: 是否使用形态学操作
    :return: 轮廓图像和轮廓列表
    """
    # 1. 自适应阈值
    binary = cv2.adaptiveThreshold(
    depth_image,
    255,
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C, # 高斯加权
    cv2.THRESH_BINARY,
    block_size,
    c_value
    )

    # 2. 形态学操作(可选)
    if use_morphology:
    # 闭操作填充小孔洞
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
    binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

    # 开操作去除小噪声
    binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)

    # 3. 查找轮廓
    contours, hierarchy = cv2.findContours(
    binary,
    cv2.RETR_TREE, # 检索所有轮廓
    cv2.CHAIN_APPROX_SIMPLE
    )

    # 4. 过滤小轮廓(根据面积)
    min_contour_area = depth_image.shape[0] * depth_image.shape[1] * 0.001 # 0.1%的图像面积
    filtered_contours = []

    for contour in contours:
    area = cv2.contourArea(contour)
    if area > min_contour_area:
    filtered_contours.append(contour)

    # 创建轮廓图像
    contour_img = np.zeros_like(depth_image)
    cv2.drawContours(contour_img, filtered_contours, -1, 255, 1)

    return contour_img, filtered_contours

    # 使用自适应方法
    adaptive_contours_img, adaptive_contours = adaptive_contour_extraction(
    depth_uint8,
    block_size=15,
    c_value=3,
    use_morphology=True
    )

    print(f"找到轮廓数量: {len(adaptive_contours)}")

    # 显示结果
    plt.figure(figsize=(10, 5))
    plt.subplot(1, 2, 1)
    plt.imshow(depth_uint8, cmap='gray')
    plt.title('预处理后的深度图')
    plt.axis('off')

    plt.subplot(1, 2, 2)
    plt.imshow(adaptive_contours_img, cmap='gray')
    plt.title(f'自适应提取的轮廓 ({len(adaptive_contours)}个轮廓)')
    plt.axis('off')
    plt.show()

    4.3 基于深度梯度的轮廓提取

    深度图的轮廓通常对应深度不连续的区域。我们可以直接计算深度梯度来找到这些边界:

    def gradient_based_contours(depth_map, gradient_threshold=0.1):
    """
    基于深度梯度提取轮廓
    :param depth_map: 原始深度图(浮点数,单位:米)
    :param gradient_threshold: 梯度阈值(深度变化率)
    :return: 轮廓掩码
    """
    # 计算深度梯度
    grad_x = cv2.Sobel(depth_map, cv2.CV_64F, 1, 0, ksize=3)
    grad_y = cv2.Sobel(depth_map, cv2.CV_64F, 0, 1, ksize=3)

    # 计算梯度幅值
    gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)

    # 计算深度变化率(梯度/深度,避免远处小梯度被误判)
    # 添加小值避免除零
    depth_with_epsilon = depth_map.copy()
    depth_with_epsilon[depth_with_epsilon < 0.01] = 0.01 # 避免除零
    relative_gradient = gradient_magnitude / depth_with_epsilon

    # 创建轮廓掩码
    contour_mask = (relative_gradient > gradient_threshold).astype(np.uint8) * 255

    # 细化轮廓
    kernel = np.ones((3, 3), np.uint8)
    contour_mask = cv2.morphologyEx(contour_mask, cv2.MORPH_CLOSE, kernel)
    contour_mask = cv2.morphologyEx(contour_mask, cv2.MORPH_OPEN, kernel)

    return contour_mask, gradient_magnitude, relative_gradient

    # 使用梯度方法
    # 注意:这里需要原始的深度图(单位:米),不是归一化的uint8图
    # 假设我们有原始深度数据
    if 'depth_map' in locals(): # 如果depth_map是原始深度数据
    contour_mask, grad_mag, rel_grad = gradient_based_contours(
    depth_map,
    gradient_threshold=0.15
    )

    # 可视化结果
    fig, axes = plt.subplots(2, 2, figsize=(12, 10))

    axes[0, 0].imshow(depth_map, cmap='inferno')
    axes[0, 0].set_title('原始深度图')
    axes[0, 0].axis('off')

    axes[0, 1].imshow(grad_mag, cmap='hot')
    axes[0, 1].set_title('深度梯度幅值')
    axes[0, 1].axis('off')

    axes[1, 0].imshow(rel_grad, cmap='hot')
    axes[1, 0].set_title('相对深度梯度')
    axes[1, 0].axis('off')

    axes[1, 1].imshow(contour_mask, cmap='gray')
    axes[1, 1].set_title('梯度提取的轮廓')
    axes[1, 1].axis('off')

    plt.tight_layout()
    plt.show()

    5. 轮廓叠加:将深度边界标注到原图

    现在到了最关键的一步:把我们提取的轮廓叠加到原始RGB图像上。这里有几个技巧可以让标注更加清晰和有用。

    5.1 基础叠加方法

    def overlay_contours_on_image(original_image, contours, contour_color=(0, 255, 0),
    thickness=2, alpha=0.7):
    """
    将轮廓叠加到原始图像上
    :param original_image: 原始RGB图像(BGR格式)
    :param contours: 轮廓列表
    :param contour_color: 轮廓颜色(B, G, R)
    :param thickness: 轮廓线粗细
    :param alpha: 轮廓透明度(0-1)
    :return: 叠加后的图像
    """
    # 创建副本
    overlay = original_image.copy()
    result = original_image.copy()

    # 绘制所有轮廓
    cv2.drawContours(overlay, contours, -1, contour_color, thickness)

    # 透明叠加
    cv2.addWeighted(overlay, alpha, result, 1 – alpha, 0, result)

    return result

    def overlay_mask_on_image(original_image, contour_mask, contour_color=(0, 255, 0),
    thickness=2, alpha=0.7):
    """
    将轮廓掩码叠加到原始图像上
    :param original_image: 原始RGB图像
    :param contour_mask: 轮廓掩码(二值图)
    :param contour_color: 轮廓颜色
    :param thickness: 轮廓线粗细(如果为-1则填充)
    :param alpha: 透明度
    :return: 叠加后的图像
    """
    # 从掩码中提取轮廓
    contours, _ = cv2.findContours(
    contour_mask,
    cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE
    )

    # 叠加轮廓
    return overlay_contours_on_image(
    original_image,
    contours,
    contour_color,
    thickness,
    alpha
    )

    # 示例:加载原始图像
    original_img = cv2.imread("original_image.jpg") # 替换为你的原始图像路径
    if original_img is None:
    # 如果没有原始图像,用深度图模拟
    original_img = cv2.cvtColor(depth_uint8, cv2.COLOR_GRAY2BGR)
    original_img = cv2.applyColorMap(original_img, cv2.COLORMAP_JET)

    # 使用自适应提取的轮廓
    if 'adaptive_contours' in locals() and len(adaptive_contours) > 0:
    # 方法1:直接绘制轮廓
    result_with_contours = overlay_contours_on_image(
    original_img.copy(),
    adaptive_contours,
    contour_color=(0, 255, 0), # 绿色
    thickness=2,
    alpha=0.6
    )

    # 方法2:使用掩码
    if 'adaptive_contours_img' in locals():
    result_with_mask = overlay_mask_on_image(
    original_img.copy(),
    adaptive_contours_img,
    contour_color=(255, 0, 0), # 蓝色
    thickness=2,
    alpha=0.6
    )

    # 显示结果
    plt.figure(figsize=(15, 5))

    plt.subplot(1, 3, 1)
    plt.imshow(cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB))
    plt.title('原始图像')
    plt.axis('off')

    plt.subplot(1, 3, 2)
    plt.imshow(cv2.cvtColor(result_with_contours, cv2.COLOR_BGR2RGB))
    plt.title('轮廓叠加(绿色)')
    plt.axis('off')

    if 'result_with_mask' in locals():
    plt.subplot(1, 3, 3)
    plt.imshow(cv2.cvtColor(result_with_mask, cv2.COLOR_BGR2RGB))
    plt.title('掩码叠加(蓝色)')
    plt.axis('off')

    plt.tight_layout()
    plt.show()

    5.2 智能轮廓标注:按深度分层着色

    一个更高级的技巧是根据深度值为轮廓着色,这样不仅能显示边界,还能显示远近关系:

    def depth_colored_contours(original_image, depth_map, contours,
    colormap=cv2.COLORMAP_JET,
    thickness=2, alpha=0.7):
    """
    根据深度值为轮廓着色
    :param original_image: 原始图像
    :param depth_map: 深度图(与原始图像同尺寸)
    :param contours: 轮廓列表
    :param colormap: OpenCV色彩映射
    :param thickness: 轮廓线粗细
    :param alpha: 透明度
    :return: 着色叠加的图像
    """
    # 创建叠加层
    overlay = original_image.copy()
    result = original_image.copy()

    # 归一化深度图用于着色
    depth_normalized = cv2.normalize(depth_map, None, 0, 255, cv2.NORM_MINMAX)
    depth_uint8 = depth_normalized.astype(np.uint8)

    # 应用色彩映射
    depth_colored = cv2.applyColorMap(depth_uint8, colormap)

    # 为每个轮廓计算平均深度并着色
    for contour in contours:
    if len(contour) > 0:
    # 创建轮廓掩码
    mask = np.zeros_like(depth_map, dtype=np.uint8)
    cv2.drawContours(mask, [contour], -1, 255, -1) # 填充轮廓

    # 计算轮廓内的平均深度
    mean_depth = cv2.mean(depth_map, mask=mask)[0]

    # 根据平均深度获取颜色
    depth_value = int((mean_depth – depth_map.min()) /
    (depth_map.max() – depth_map.min()) * 255)
    depth_value = np.clip(depth_value, 0, 255)

    # 从色彩映射图中获取颜色
    color = depth_colored[depth_value, 0] # 取色彩映射中的颜色

    # 绘制轮廓
    cv2.drawContours(overlay, [contour], -1, color.tolist(), thickness)

    # 透明叠加
    cv2.addWeighted(overlay, alpha, result, 1 – alpha, 0, result)

    return result

    def multi_level_contours(original_image, depth_map, num_levels=5,
    thickness=2, alpha=0.6):
    """
    多层级轮廓标注:按深度范围分层显示
    :param original_image: 原始图像
    :param depth_map: 深度图
    :param num_levels: 深度分层数量
    :param thickness: 轮廓线粗细
    :param alpha: 透明度
    :return: 分层标注的图像
    """
    # 计算深度范围
    depth_min = depth_map.min()
    depth_max = depth_map.max()

    # 创建深度分层
    depth_levels = np.linspace(depth_min, depth_max, num_levels + 1)

    # 预定义颜色(从近到远:红->黄->绿->青->蓝)
    colors = [
    (0, 0, 255), # 红色 – 最近
    (0, 165, 255), # 橙色
    (0, 255, 255), # 黄色
    (255, 255, 0), # 青色
    (255, 0, 0) # 蓝色 – 最远
    ]

    # 确保颜色数量足够
    if len(colors) < num_levels:
    colors = colors * (num_levels // len(colors) + 1)
    colors = colors[:num_levels]

    # 创建结果图像
    result = original_image.copy()

    # 为每个深度层级提取和绘制轮廓
    for i in range(num_levels):
    # 创建当前深度层级的掩码
    if i == 0:
    level_mask = (depth_map >= depth_levels[i]) & (depth_map <= depth_levels[i+1])
    else:
    level_mask = (depth_map > depth_levels[i]) & (depth_map <= depth_levels[i+1])

    level_mask = level_mask.astype(np.uint8) * 255

    # 提取轮廓
    contours, _ = cv2.findContours(
    level_mask,
    cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE
    )

    # 过滤小轮廓
    min_area = original_image.shape[0] * original_image.shape[1] * 0.0005
    filtered_contours = []
    for contour in contours:
    if cv2.contourArea(contour) > min_area:
    filtered_contours.append(contour)

    # 绘制轮廓
    if filtered_contours:
    overlay = original_image.copy()
    cv2.drawContours(overlay, filtered_contours, -1, colors[i], thickness)
    cv2.addWeighted(overlay, alpha, result, 1 – alpha, 0, result)

    # 添加图例
    legend_text = f"Level {i+1}: {depth_levels[i]:.2f}-{depth_levels[i+1]:.2f}m"
    cv2.putText(result, legend_text,
    (10, 30 + i * 30),
    cv2.FONT_HERSHEY_SIMPLEX,
    0.6, colors[i], 2)

    return result

    # 使用深度着色轮廓
    if 'depth_map' in locals() and 'adaptive_contours' in locals():
    # 方法1:根据深度值着色
    colored_result = depth_colored_contours(
    original_img.copy(),
    depth_map,
    adaptive_contours,
    colormap=cv2.COLORMAP_JET,
    thickness=2,
    alpha=0.6
    )

    # 方法2:多层级轮廓
    multi_level_result = multi_level_contours(
    original_img.copy(),
    depth_map,
    num_levels=4,
    thickness=2,
    alpha=0.6
    )

    # 显示结果
    plt.figure(figsize=(15, 5))

    plt.subplot(1, 3, 1)
    plt.imshow(cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB))
    plt.title('原始图像')
    plt.axis('off')

    plt.subplot(1, 3, 2)
    plt.imshow(cv2.cvtColor(colored_result, cv2.COLOR_BGR2RGB))
    plt.title('深度着色轮廓')
    plt.axis('off')

    plt.subplot(1, 3, 3)
    plt.imshow(cv2.cvtColor(multi_level_result, cv2.COLOR_BGR2RGB))
    plt.title('多层级轮廓标注')
    plt.axis('off')

    plt.tight_layout()
    plt.show()

    5.3 实用技巧:轮廓平滑与优化

    有时候提取的轮廓可能不够平滑,我们可以用一些技巧来优化:

    def smooth_contours(contours, epsilon_factor=0.01):
    """
    平滑轮廓(减少点数,使轮廓更平滑)
    :param contours: 原始轮廓列表
    :param epsilon_factor: 近似精度因子(轮廓周长的百分比)
    :return: 平滑后的轮廓列表
    """
    smoothed_contours = []

    for contour in contours:
    if len(contour) > 0:
    # 计算轮廓周长
    perimeter = cv2.arcLength(contour, True)

    # Douglas-Peucker算法简化轮廓
    epsilon = epsilon_factor * perimeter
    approx = cv2.approxPolyDP(contour, epsilon, True)

    smoothed_contours.append(approx)

    return smoothed_contours

    def enhance_contour_visibility(original_image, contour_mask,
    glow_effect=True, shadow_effect=True):
    """
    增强轮廓可见性(发光效果、阴影效果)
    :param original_image: 原始图像
    :param contour_mask: 轮廓掩码
    :param glow_effect: 是否添加发光效果
    :param shadow_effect: 是否添加阴影效果
    :return: 增强后的图像
    """
    result = original_image.copy()

    # 从掩码提取轮廓
    contours, _ = cv2.findContours(
    contour_mask,
    cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE
    )

    if not contours:
    return result

    # 创建轮廓图层
    contour_layer = np.zeros_like(original_image)

    # 绘制轮廓(白色)
    cv2.drawContours(contour_layer, contours, -1, (255, 255, 255), 2)

    # 发光效果
    if glow_effect:
    # 创建模糊的轮廓作为发光层
    glow_layer = cv2.GaussianBlur(contour_layer, (15, 15), 0)

    # 叠加发光效果(黄色光晕)
    glow_color = np.array([0, 200, 255], dtype=np.uint8) # BGR: 黄色
    glow_colored = np.zeros_like(original_image)
    glow_colored[:, :] = glow_color

    # 使用发光层作为alpha通道
    glow_alpha = glow_layer[:, :, 0] / 255.0 * 0.3 # 30%透明度
    for c in range(3):
    result[:, :, c] = result[:, :, c] * (1 – glow_alpha) + glow_colored[:, :, c] * glow_alpha

    # 阴影效果
    if shadow_effect:
    # 创建阴影层(轮廓向右下偏移)
    shadow_layer = np.zeros_like(original_image)
    cv2.drawContours(shadow_layer, contours, -1, (0, 0, 0), 3)

    # 应用偏移
    M = np.float32([[1, 0, 2], [0, 1, 2]]) # 向右下偏移2像素
    shadow_layer = cv2.warpAffine(shadow_layer, M,
    (shadow_layer.shape[1], shadow_layer.shape[0]))

    # 模糊阴影
    shadow_layer = cv2.GaussianBlur(shadow_layer, (5, 5), 0)

    # 叠加阴影(黑色,20%透明度)
    shadow_alpha = shadow_layer[:, :, 0] / 255.0 * 0.2
    for c in range(3):
    result[:, :, c] = result[:, :, c] * (1 – shadow_alpha)

    # 最后绘制清晰的轮廓
    cv2.drawContours(result, contours, -1, (0, 255, 0), 2) # 绿色轮廓

    return result

    # 使用轮廓优化
    if 'adaptive_contours_img' in locals():
    # 平滑轮廓
    smoothed_contours = smooth_contours(adaptive_contours, epsilon_factor=0.01)

    # 创建平滑轮廓的掩码
    smoothed_mask = np.zeros_like(adaptive_contours_img)
    cv2.drawContours(smoothed_mask, smoothed_contours, -1, 255, 1)

    # 增强可见性
    enhanced_result = enhance_contour_visibility(
    original_img.copy(),
    smoothed_mask,
    glow_effect=True,
    shadow_effect=True
    )

    # 显示对比
    plt.figure(figsize=(15, 5))

    plt.subplot(1, 3, 1)
    # 原始轮廓
    original_with_contours = overlay_contours_on_image(
    original_img.copy(), adaptive_contours,
    contour_color=(0, 255, 0), thickness=2, alpha=0.6
    )
    plt.imshow(cv2.cvtColor(original_with_contours, cv2.COLOR_BGR2RGB))
    plt.title('原始轮廓')
    plt.axis('off')

    plt.subplot(1, 3, 2)
    # 平滑后轮廓
    smoothed_with_contours = overlay_contours_on_image(
    original_img.copy(), smoothed_contours,
    contour_color=(255, 0, 0), thickness=2, alpha=0.6
    )
    plt.imshow(cv2.cvtColor(smoothed_with_contours, cv2.COLOR_BGR2RGB))
    plt.title('平滑后轮廓')
    plt.axis('off')

    plt.subplot(1, 3, 3)
    plt.imshow(cv2.cvtColor(enhanced_result, cv2.COLOR_BGR2RGB))
    plt.title('增强效果轮廓')
    plt.axis('off')

    plt.tight_layout()
    plt.show()

    6. 完整实战案例:室内场景深度轮廓标注

    让我们用一个完整的例子,把前面所有步骤串起来。假设我们有一个室内场景的RGB图像,我们要用lingbot-depth-vitl14估计深度,然后提取轮廓并标注。

    6.1 完整代码实现

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from pathlib import Path

    class DepthContourAnnotator:
    """
    深度轮廓标注器:完整的深度图轮廓提取与标注流程
    """

    def __init__(self, depth_model=None):
    """
    初始化标注器
    :param depth_model: 深度估计模型(可选)
    """
    self.depth_model = depth_model
    self.original_image = None
    self.depth_map = None
    self.contours = None

    def estimate_depth(self, image_path, use_api=True, api_url=None):
    """
    估计深度(如果提供了深度模型或API)
    :param image_path: 图像路径
    :param use_api: 是否使用API
    :param api_url: API地址
    :return: 深度图
    """
    # 读取原始图像
    self.original_image = cv2.imread(image_path)
    if self.original_image is None:
    raise ValueError(f"无法读取图像: {image_path}")

    # 如果有深度模型,直接估计深度
    if self.depth_model is not None:
    print("使用本地深度模型估计深度…")
    # 这里需要根据具体模型实现深度估计
    # self.depth_map = self.depth_model.predict(self.original_image)
    pass
    elif use_api and api_url:
    print("通过API获取深度估计…")
    self.depth_map = self._get_depth_from_api(self.original_image, api_url)
    else:
    print("使用示例深度图…")
    # 生成模拟深度图(仅用于演示)
    self.depth_map = self._generate_demo_depth(self.original_image)

    return self.depth_map

    def _get_depth_from_api(self, image, api_url):
    """
    从API获取深度图
    """
    # 这里简化实现,实际需要根据API调整
    # 假设API返回的是归一化的深度图
    height, width = image.shape[:2]

    # 模拟深度图:中间近,四周远
    y, x = np.ogrid[:height, :width]
    center_y, center_x = height / 2, width / 2

    # 创建径向深度梯度
    dist_from_center = np.sqrt((x – center_x)**2 + (y – center_y)**2)
    max_dist = np.sqrt(center_x**2 + center_y**2)

    # 深度值:中心最近(0.5米),边缘最远(10米)
    depth = 0.5 + (dist_from_center / max_dist) * 9.5

    # 添加一些物体形状
    # 模拟一个矩形物体
    obj_y1, obj_y2 = int(height * 0.3), int(height * 0.7)
    obj_x1, obj_x2 = int(width * 0.4), int(width * 0.6)
    depth[obj_y1:obj_y2, obj_x1:obj_x2] = 2.0 # 物体深度2米

    # 模拟另一个物体
    obj2_y1, obj2_y2 = int(height * 0.1), int(height * 0.3)
    obj2_x1, obj2_x2 = int(width * 0.1), int(width * 0.3)
    depth[obj2_y1:obj2_y2, obj2_x1:obj2_x2] = 1.5 # 物体深度1.5米

    return depth

    def _generate_demo_depth(self, image):
    """
    生成演示用的深度图
    """
    height, width = image.shape[:2]

    # 创建更复杂的深度图
    depth = np.ones((height, width), dtype=np.float32) * 5.0 # 基础深度5米

    # 添加深度变化
    x = np.linspace(0, 1, width)
    y = np.linspace(0, 1, height)
    X, Y = np.meshgrid(x, y)

    # 添加一些深度模式
    depth += 2.0 * np.sin(5 * X) * np.cos(5 * Y) # 波浪模式
    depth += 3.0 * (X**2 + Y**2) # 径向渐变

    # 添加几个物体
    # 物体1:矩形
    obj1_mask = (X > 0.3) & (X < 0.5) & (Y > 0.4) & (Y < 0.7)
    depth[obj1_mask] = 2.0

    # 物体2:圆形
    center_x, center_y = 0.7, 0.3
    radius = 0.15
    obj2_mask = (X – center_x)**2 + (Y – center_y)**2 < radius**2
    depth[obj2_mask] = 1.5

    # 物体3:三角形
    for i in range(height):
    for j in range(width):
    if 0.1 < X[i, j] < 0.25 and 0.7 < Y[i, j] < 0.9:
    if Y[i, j] < 0.8 + 0.5 * (X[i, j] – 0.1):
    depth[i, j] = 3.0

    # 添加一些噪声
    noise = np.random.normal(0, 0.1, depth.shape)
    depth += noise

    # 确保深度为正
    depth = np.clip(depth, 0.5, 10.0)

    return depth

    def extract_contours(self, method='adaptive', **kwargs):
    """
    提取轮廓
    :param method: 提取方法 ('canny', 'adaptive', 'gradient')
    :param kwargs: 方法参数
    :return: 轮廓列表
    """
    if self.depth_map is None:
    raise ValueError("请先估计深度")

    # 预处理深度图
    depth_uint8, _, _ = preprocess_depth(self.depth_map, filter_size=5)

    if method == 'canny':
    # Canny边缘检测
    threshold1 = kwargs.get('threshold1', 30)
    threshold2 = kwargs.get('threshold2', 100)
    edges = cv2.Canny(depth_uint8, threshold1, threshold2)
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    elif method == 'adaptive':
    # 自适应阈值
    block_size = kwargs.get('block_size', 11)
    c_value = kwargs.get('c_value', 2)
    contour_img, contours = adaptive_contour_extraction(
    depth_uint8, block_size, c_value
    )

    elif method == 'gradient':
    # 梯度方法
    gradient_threshold = kwargs.get('gradient_threshold', 0.1)
    contour_mask, _, _ = gradient_based_contours(
    self.depth_map, gradient_threshold
    )
    contours, _ = cv2.findContours(
    contour_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
    )

    else:
    raise ValueError(f"不支持的轮廓提取方法: {method}")

    # 过滤小轮廓
    min_area = self.original_image.shape[0] * self.original_image.shape[1] * 0.0005
    self.contours = []
    for contour in contours:
    if cv2.contourArea(contour) > min_area:
    self.contours.append(contour)

    print(f"提取到 {len(self.contours)} 个轮廓")
    return self.contours

    def annotate_image(self, annotation_type='simple', **kwargs):
    """
    标注图像
    :param annotation_type: 标注类型 ('simple', 'colored', 'multi_level', 'enhanced')
    :param kwargs: 标注参数
    :return: 标注后的图像
    """
    if self.original_image is None or self.contours is None:
    raise ValueError("请先加载图像并提取轮廓")

    if annotation_type == 'simple':
    # 简单轮廓标注
    color = kwargs.get('color', (0, 255, 0)) # 绿色
    thickness = kwargs.get('thickness', 2)
    alpha = kwargs.get('alpha', 0.6)

    result = overlay_contours_on_image(
    self.original_image.copy(),
    self.contours,
    contour_color=color,
    thickness=thickness,
    alpha=alpha
    )

    elif annotation_type == 'colored':
    # 深度着色轮廓
    colormap = kwargs.get('colormap', cv2.COLORMAP_JET)
    thickness = kwargs.get('thickness', 2)
    alpha = kwargs.get('alpha', 0.6)

    result = depth_colored_contours(
    self.original_image.copy(),
    self.depth_map,
    self.contours,
    colormap=colormap,
    thickness=thickness,
    alpha=alpha
    )

    elif annotation_type == 'multi_level':
    # 多层级轮廓
    num_levels = kwargs.get('num_levels', 4)
    thickness = kwargs.get('thickness', 2)
    alpha = kwargs.get('alpha', 0.6)

    result = multi_level_contours(
    self.original_image.copy(),
    self.depth_map,
    num_levels=num_levels,
    thickness=thickness,
    alpha=alpha
    )

    elif annotation_type == 'enhanced':
    # 增强效果轮廓
    # 先创建轮廓掩码
    contour_mask = np.zeros_like(self.depth_map, dtype=np.uint8)
    cv2.drawContours(contour_mask, self.contours, -1, 255, 1)

    glow = kwargs.get('glow_effect', True)
    shadow = kwargs.get('shadow_effect', True)

    result = enhance_contour_visibility(
    self.original_image.copy(),
    contour_mask,
    glow_effect=glow,
    shadow_effect=shadow
    )

    else:
    raise ValueError(f"不支持的标注类型: {annotation_type}")

    return result

    def visualize_results(self, save_path=None):
    """
    可视化所有结果
    :param save_path: 保存路径(可选)
    """
    if self.original_image is None or self.depth_map is None:
    raise ValueError("请先加载图像和深度图")

    # 创建不同标注类型的结果
    simple_result = self.annotate_image('simple')
    colored_result = self.annotate_image('colored')
    multi_level_result = self.annotate_image('multi_level', num_levels=4)
    enhanced_result = self.annotate_image('enhanced')

    # 创建可视化
    fig, axes = plt.subplots(2, 3, figsize=(18, 12))

    # 原始图像
    axes[0, 0].imshow(cv2.cvtColor(self.original_image, cv2.COLOR_BGR2RGB))
    axes[0, 0].set_title('原始图像')
    axes[0, 0].axis('off')

    # 深度图
    depth_display = cv2.normalize(self.depth_map, None, 0, 255, cv2.NORM_MINMAX)
    axes[0, 1].imshow(depth_display, cmap='inferno')
    axes[0, 1].set_title('深度图(伪彩色)')
    axes[0, 1].axis('off')

    # 简单轮廓标注
    axes[0, 2].imshow(cv2.cvtColor(simple_result, cv2.COLOR_BGR2RGB))
    axes[0, 2].set_title('简单轮廓标注')
    axes[0, 2].axis('off')

    # 深度着色轮廓
    axes[1, 0].imshow(cv2.cvtColor(colored_result, cv2.COLOR_BGR2RGB))
    axes[1, 0].set_title('深度着色轮廓')
    axes[1, 0].axis('off')

    # 多层级轮廓
    axes[1, 1].imshow(cv2.cvtColor(multi_level_result, cv2.COLOR_BGR2RGB))
    axes[1, 1].set_title('多层级轮廓标注')
    axes[1, 1].axis('off')

    # 增强效果轮廓
    axes[1, 2].imshow(cv2.cvtColor(enhanced_result, cv2.COLOR_BGR2RGB))
    axes[1, 2].set_title('增强效果轮廓')
    axes[1, 2].axis('off')

    plt.tight_layout()

    if save_path:
    plt.savefig(save_path, dpi=150, bbox_inches='tight')
    print(f"结果已保存到: {save_path}")

    plt.show()

    return fig

    def save_annotated_image(self, output_path, annotation_type='simple', **kwargs):
    """
    保存标注后的图像
    :param output_path: 输出路径
    :param annotation_type: 标注类型
    :param kwargs: 标注参数
    """
    result = self.annotate_image(annotation_type, **kwargs)
    cv2.imwrite(output_path, result)
    print(f"标注图像已保存到: {output_path}")

    # 使用示例
    def main():
    # 创建标注器
    annotator = DepthContourAnnotator()

    # 1. 估计深度(这里使用模拟数据,实际使用时替换为真实图像)
    # 如果没有真实图像,我们创建一个示例图像
    example_image = np.zeros((480, 640, 3), dtype=np.uint8)
    example_image[:] = (100, 150, 200) # 蓝色背景

    # 添加一些形状模拟室内场景
    cv2.rectangle(example_image, (100, 100), (300, 300), (200, 100, 50), -1) # 矩形物体
    cv2.circle(example_image, (450, 150), 80, (50, 200, 100), -1) # 圆形物体
    cv2.putText(example_image, "Indoor Scene", (200, 400),
    cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)

    # 保存示例图像
    cv2.imwrite("example_indoor_scene.jpg", example_image)

    # 2. 使用示例图像
    try:
    annotator.estimate_depth("example_indoor_scene.jpg", use_api=False)

    # 3. 提取轮廓
    annotator.extract_contours(method='adaptive', block_size=15, c_value=3)

    # 4. 可视化结果
    annotator.visualize_results("depth_contour_results.png")

    # 5. 保存不同风格的标注结果
    annotator.save_annotated_image("simple_annotation.jpg", 'simple')
    annotator.save_annotated_image("colored_annotation.jpg", 'colored')
    annotator.save_annotated_image("enhanced_annotation.jpg", 'enhanced')

    print("处理完成!")

    except Exception as e:
    print(f"处理过程中出错: {e}")

    if __name__ == "__main__":
    main()

    6.2 实际应用:机器人导航场景

    让我们看一个更实际的例子。假设我们有一个机器人导航场景,需要从深度图中提取障碍物轮廓:

    def robot_navigation_example():
    """
    机器人导航场景示例:从深度图提取障碍物轮廓
    """
    print("=== 机器人导航场景:障碍物轮廓提取 ===")

    # 模拟机器人摄像头看到的场景
    # 创建一个简单的室内场景:地板、墙壁、几个障碍物
    height, width = 480, 640
    scene = np.zeros((height, width, 3), dtype=np.uint8)

    # 地板(绿色)
    scene[300:, :] = (0, 100, 0)

    # 墙壁(蓝色)
    scene[:300, :] = (100, 100, 200)

    # 障碍物1:箱子(棕色)
    cv2.rectangle(scene, (150, 200), (250, 300), (50, 50, 150), -1)
    cv2.rectangle(scene, (150, 200), (250, 300), (0, 0, 0), 2) # 边框

    # 障碍物2:圆柱体(红色)
    cv2.circle(scene, (450, 250), 60, (0, 0, 200), -1)
    cv2.circle(scene, (450, 250), 60, (0, 0, 0), 2) # 边框

    # 障碍物3:三角形障碍(黄色)
    pts = np.array([[350, 100], [500, 100], [425, 200]], np.int32)
    cv2.fillPoly(scene, [pts], (0, 200, 200))
    cv2.polylines(scene, [pts], True, (0, 0, 0), 2)

    # 添加文字说明
    cv2.putText(scene, "Robot View", (20, 30),
    cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
    cv2.putText(scene, "Floor", (50, 450),
    cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
    cv2.putText(scene, "Box", (180, 280),
    cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
    cv2.putText(scene, "Cylinder", (420, 220),
    cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
    cv2.putText(scene, "Triangle", (380, 150),
    cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)

    # 保存场景图像
    cv2.imwrite("robot_scene.jpg", scene)

    # 创建对应的深度图(模拟lingbot-depth-vitl14的输出)
    depth_map = np.ones((height, width), dtype=np.float32) * 5.0 # 基础深度5米

    # 地板:从近到远
    for i in range(300, height):
    depth_map[i, :] = 1.0 + (i – 300) / (height – 300) * 4.0

    # 墙壁:恒定深度
    depth_map[:300, :] = 3.0

    # 障碍物深度(更近)
    depth_map[200:300, 150:250] = 1.5 # 箱子
    # 圆形障碍物
    y, x = np.ogrid[:height, :width]
    mask = (x – 450)**2 + (y – 250)**2 < 60**2
    depth_map[mask] = 1.8
    # 三角形障碍物
    triangle_mask = np.zeros((height, width), dtype=bool)
    for i in range(height):
    for j in range(width):
    if (350 <= j <= 500 and 100 <= i <= 200 and
    i <= 100 + (j – 350) * 100 / 150 and # 左边线
    i <= 100 + (500 – j) * 100 / 150): # 右边线
    triangle_mask[i, j] = True
    depth_map[triangle_mask] = 2.2

    # 添加一些噪声(模拟深度传感器噪声)
    noise = np.random.normal(0, 0.05, depth_map.shape)
    depth_map += noise
    depth_map = np.clip(depth_map, 0.5, 10.0)

    # 使用标注器处理
    annotator = DepthContourAnnotator()
    annotator.original_image = scene
    annotator.depth_map = depth_map

    # 提取轮廓(使用梯度方法,对深度不连续更敏感)
    contour_mask, grad_mag, _ = gradient_based_contours(depth_map, gradient_threshold=0.08)
    contours, _ = cv2.findContours(contour_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # 过滤小轮廓
    min_area = height * width * 0.001
    annotator.contours = []
    for contour in contours:
    if cv2.contourArea(contour) > min_area:
    annotator.contours.append(contour)

    print(f"检测到 {len(annotator.contours)} 个障碍物轮廓")

    # 创建导航可视化
    fig, axes = plt.subplots(2, 3, figsize=(15, 10))

    # 1. 原始场景
    axes[0, 0].imshow(cv2.cvtColor(scene, cv2.COLOR_BGR2RGB))
    axes[0, 0].set_title('机器人视觉场景')
    axes[0, 0].axis('off')

    # 2. 深度图
    depth_display = cv2.normalize(depth_map, None, 0, 255, cv2.NORM_MINMAX)
    axes[0, 1].imshow(depth_display, cmap='inferno')
    axes[0, 1].set_title('深度估计结果')
    axes[0, 1].axis('off')

    # 3. 深度梯度
    axes[0, 2].imshow(grad_mag, cmap='hot')
    axes[0, 2].set_title('深度梯度(边缘检测)')
    axes[0, 2].axis('off')

    # 4. 提取的轮廓
    contour_overlay = scene.copy()
    cv2.drawContours(contour_overlay, annotator.contours, -1, (0, 255, 0), 2)
    axes[1, 0].imshow(cv2.cvtColor(contour_overlay, cv2.COLOR_BGR2RGB))
    axes[1, 0].set_title('提取的障碍物轮廓')
    axes[1, 0].axis('off')

    # 5. 安全区域分析(基于深度)
    safety_map = np.ones_like(scene) * 255
    # 红色:危险区域(深度 < 2米)
    danger_mask = depth_map < 2.0
    safety_map[danger_mask] = [0, 0, 255] # 红色

    # 黄色:警告区域(2米 <= 深度 < 3米)
    warning_mask = (depth_map >= 2.0) & (depth_map < 3.0)
    safety_map[warning_mask] = [0, 255, 255] # 黄色

    # 绿色:安全区域(深度 >= 3米)
    safe_mask = depth_map >= 3.0
    safety_map[safe_mask] = [0, 255, 0] # 绿色

    # 叠加轮廓
    cv2.drawContours(safety_map, annotator.contours, -1, (0, 0, 0), 2)

    axes[1, 1].imshow(cv2.cvtColor(safety_map, cv2.COLOR_BGR2RGB))
    axes[1, 1].set_title('安全区域分析\\n红:危险, 黄:警告, 绿:安全')
    axes[1, 1].axis('off')

    # 6. 导航路径规划(简单示例)
    nav_display = scene.copy()

    # 标记起点和终点
    start_point = (50, 400) # 左下角
    end_point = (600, 100) # 右上角

    cv2.circle(nav_display, start_point, 10, (255, 0, 0), -1) # 蓝色起点
    cv2.circle(nav_display, end_point, 10, (0, 0, 255), -1) # 红色终点
    cv2.putText(nav_display, "Start", (start_point[0]-20, start_point[1]-15),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
    cv2.putText(nav_display, "Goal", (end_point[0]-15, end_point[1]-15),
    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)

    # 简单的避障路径(实际需要路径规划算法)
    # 这里只是画一条绕过障碍物的折线
    path_points = [
    start_point,
    (50, 300), # 向上
    (200, 300), # 向右
    (200, 150), # 向上
    (400, 150), # 向右
    (400, 250), # 向下(绕过圆形障碍)
    (500, 250), # 向右
    (500, 100), # 向上
    end_point
    ]

    for i in range(len(path_points)-1):
    cv2.line(nav_display, path_points[i], path_points[i+1], (0, 255, 255), 2)

    # 绘制路径点
    for point in path_points:
    cv2.circle(nav_display, point, 5, (0, 255, 255), -1)

    axes[1, 2].imshow(cv2.cvtColor(nav_display, cv2.COLOR_BGR2RGB))
    axes[1, 2].set_title('基于深度轮廓的路径规划')
    axes[1, 2].axis('off')

    plt.tight_layout()
    plt.savefig("robot_navigation_analysis.png", dpi=150, bbox_inches='tight')
    plt.show()

    print("机器人导航分析完成!结果已保存到 robot_navigation_analysis.png")

    return scene, depth_map, annotator.contours

    # 运行示例
    if __name__ == "__main__":
    robot_navigation_example()

    7. 总结

    通过这篇教程,我们完整地走了一遍从深度图生成到轮廓提取,再到最终标注的整个流程。lingbot-depth-vitl14为我们提供了高质量的深度估计,而OpenCV和Python则让我们能够灵活地处理和可视化这些深度信息。

    7.1 关键要点回顾

  • 深度图获取:你可以通过lingbot-depth-vitl14的WebUI或REST API获取深度图,建议使用API获取原始浮点数据以获得更高精度。

  • 预处理很重要:深度图通常需要滤波和归一化处理,中值滤波和双边滤波能有效去除噪声同时保持边缘。

  • 轮廓提取方法多样:

    • Canny边缘检测:最常用,效果稳定
    • 自适应阈值:对光照变化鲁棒
    • 深度梯度:直接利用深度不连续性,对物体边界敏感
  • 智能标注技巧:

    • 深度着色:用颜色表示远近,直观展示三维信息
    • 多层级标注:按深度范围分层,适合复杂场景
    • 效果增强:发光、阴影效果让轮廓更醒目
  • 实际应用广泛:从机器人导航到AR应用,深度轮廓标注都能提供有价值的空间信息。

  • 7.2 实用建议

  • 参数调优:不同的场景可能需要调整轮廓提取的参数。室内场景和室外场景的最佳参数可能不同。

  • 性能考虑:对于实时应用,可以选择计算量较小的Canny算法;对于精度要求高的离线分析,梯度方法可能更合适。

  • 结合其他信息:深度轮廓可以与其他传感器数据(如语义分割、实例分割)结合,获得更丰富的场景理解。

  • 错误处理:在实际应用中,要添加适当的错误处理,比如检查图像尺寸是否匹配、深度值是否有效等。

  • 7.3 下一步探索

    如果你对这个技术感兴趣,可以进一步探索:

  • 实时处理:尝试将整个流程优化,实现实时深度估计和轮廓标注
  • 3D重建:将2D轮廓与深度信息结合,进行简单的3D场景重建
  • 多帧融合:利用视频序列的时间一致性,提高轮廓提取的稳定性
  • 深度学习轮廓:使用深度学习模型(如DeepLab、Mask R-CNN)直接提取语义轮廓
  • 深度信息是计算机视觉中的宝贵资源,而轮廓标注让这些信息变得更加直观和有用。希望这篇教程能帮助你在自己的项目中更好地利用深度数据!


    获取更多AI镜像

    想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

    赞(0)
    未经允许不得转载:171主机测评 » lingbot-depth-vitl14实战教程:Python OpenCV绘制深度图轮廓线并叠加原图标注
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址