You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为OpenCV绘制的大尺寸点云图像添加图例与颜色条的实现方案咨询

OpenCV绘制的大尺寸点云图像添加图例与颜色条的实现方案咨询

嘿,针对你这个给大尺寸OpenCV点云图加图例和颜色条的需求,我整理了一套实用的实现方案,考虑到大图像的内存压力,尽量用高效的方式来处理,一起来看看吧!

离散图例(Legends)实现

首先是离散图例的函数,核心思路是先单独绘制一个图例面板,再把它拼接到原图的指定位置(比如右上角),这样不会破坏原图的主要内容,也能适配大尺寸图像的比例。

import cv2
import numpy as np

def add_legends(image, legend_color_list, legend_label, 
                block_size=(200, 200), font_scale=5, thickness=2, 
                margin=20, spacing=10, position="top-right"):
    """
    给OpenCV绘制的图像添加离散图例
    参数:
        image: 输入图像(NumPy数组,BGR格式)
        legend_color_list: 颜色列表,每个元素是BGR三元组,比如[(0,0,255), (0,255,0)]
        legend_label: 对应颜色的标签列表,比如["Class A", "Class B"]
        block_size: 图例色块的尺寸,(宽度, 高度),根据图像大小调整
        font_scale: 字体缩放比例,大图像需要更大的值
        thickness: 字体厚度
        margin: 图例面板的内边距
        spacing: 色块和标签之间的间距
        position: 图例放置位置,可选"top-right", "top-left", "bottom-right", "bottom-left"
    返回:
        添加了图例的新图像
    """
    # 检查颜色和标签数量是否匹配
    if len(legend_color_list) != len(legend_label):
        raise ValueError("颜色列表和标签列表长度必须一致")
    
    # 计算每个标签的宽度,确定图例面板的总宽度
    label_widths = []
    for label in legend_label:
        text_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness)[0]
        label_widths.append(text_size[0])
    max_label_width = max(label_widths)
    legend_width = block_size[0] + max_label_width + spacing + 2 * margin
    legend_height = len(legend_color_list) * (block_size[1] + spacing) + 2 * margin
    
    # 创建空白图例面板,白色背景
    legend = np.ones((legend_height, legend_width, 3), dtype=np.uint8) * 255
    
    # 逐个绘制色块和标签
    for i, (color, label) in enumerate(zip(legend_color_list, legend_label)):
        # 计算色块的位置
        block_x = margin
        block_y = margin + i * (block_size[1] + spacing)
        # 绘制色块
        cv2.rectangle(legend, (block_x, block_y), 
                      (block_x + block_size[0], block_y + block_size[1]), 
                      color, -1)
        # 计算标签的位置
        text_x = block_x + block_size[0] + spacing
        text_y = block_y + block_size[1] // 2 + font_scale * 10  # 垂直居中
        # 绘制标签
        cv2.putText(legend, label, (text_x, text_y), 
                    cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0,0,0), thickness)
    
    # 根据位置拼接原图和图例
    h, w = image.shape[:2]
    new_h = max(h, legend_height) if position in ["top-left", "top-right"] else h + legend_height
    new_w = max(w, legend_width) if position in ["bottom-left", "top-left"] else w + legend_width
    
    result = np.ones((new_h, new_w, 3), dtype=np.uint8) * 255
    
    if position == "top-right":
        result[:h, :w] = image
        result[:legend_height, w:w+legend_width] = legend
    elif position == "top-left":
        result[:h, legend_width:] = image
        result[:legend_height, :legend_width] = legend
    elif position == "bottom-right":
        result[:h, :w] = image
        result[h:h+legend_height, w:w+legend_width] = legend
    elif position == "bottom-left":
        result[:h, legend_width:] = image
        result[h:h+legend_height, :legend_width] = legend
    
    return result

连续颜色条(Colorbar)实现

对于连续颜色的颜色条,我们先生成一个渐变的颜色带,再添加刻度标签,最后拼接到原图边缘。这里会用到你提供的cmap_func来生成对应颜色。

def add_colorbar(image, cmap_func, bar_width=200, font_scale=3, thickness=2, 
                 tick_interval=50, position="right"):
    """
    给OpenCV绘制的图像添加连续颜色条
    参数:
        image: 输入彩色图像(NumPy数组,BGR格式)
        cmap_func: 颜色映射函数,输入0-255的灰度值,输出BGR三元组
        bar_width: 颜色条的宽度
        font_scale: 刻度字体缩放比例
        thickness: 字体厚度
        tick_interval: 刻度间隔
        position: 颜色条放置位置,可选"right", "left", "top", "bottom"
    返回:
        添加了颜色条的新图像
    """
    h, w = image.shape[:2]
    
    # 创建颜色条的基础画布
    if position in ["right", "left"]:
        bar_h = h
        bar_w = bar_width
        colorbar = np.zeros((bar_h, bar_w, 3), dtype=np.uint8)
        # 生成渐变颜色,从上到下对应0到255
        for y in range(bar_h):
            gray_val = int(255 * (1 - y / bar_h))  # 顶部是255,底部是0,可根据需求调整
            color = cmap_func(gray_val)
            colorbar[y, :] = color
        # 添加刻度
        for val in range(0, 256, tick_interval):
            y_pos = int(h * (1 - val / 255))
            # 绘制刻度线
            cv2.line(colorbar, (0, y_pos), (bar_w//5, y_pos), (0,0,0), thickness)
            # 绘制刻度文字
            text = str(val)
            text_size = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness)[0]
            text_x = bar_w//5 + 5
            text_y = y_pos + text_size[1]//2
            cv2.putText(colorbar, text, (text_x, text_y), 
                        cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0,0,0), thickness)
    else:
        # 水平颜色条(top/bottom)
        bar_h = bar_width
        bar_w = w
        colorbar = np.zeros((bar_h, bar_w, 3), dtype=np.uint8)
        # 生成渐变颜色,从左到右对应0到255
        for x in range(bar_w):
            gray_val = int(255 * x / bar_w)
            color = cmap_func(gray_val)
            colorbar[:, x] = color
        # 添加刻度
        for val in range(0, 256, tick_interval):
            x_pos = int(w * val / 255)
            cv2.line(colorbar, (x_pos, 0), (x_pos, bar_h//5), (0,0,0), thickness)
            text = str(val)
            text_size = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness)[0]
            text_x = x_pos - text_size[0]//2
            text_y = bar_h//5 + 5 + text_size[1]
            cv2.putText(colorbar, text, (text_x, text_y), 
                        cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0,0,0), thickness)
    
    # 拼接原图和颜色条
    if position == "right":
        result = np.concatenate([image, colorbar], axis=1)
    elif position == "left":
        result = np.concatenate([colorbar, image], axis=1)
    elif position == "top":
        result = np.concatenate([colorbar, image], axis=0)
    elif position == "bottom":
        result = np.concatenate([image, colorbar], axis=0)
    
    return result

测试示例

用你提供的测试场景来验证一下(为了方便测试,我把图像尺寸缩小了,实际使用可以改回10k×20k级别):

# 测试离散图例
image1 = np.random.randint(0, 5, (1000, 2000, 3)) * 50
# 定义颜色和标签
legend_colors = [(0,0,255), (0,255,0), (255,0,0), (255,255,0), (0,255,255)]
legend_labels = ["Point Type 1", "Point Type 2", "Point Type 3", "Point Type 4", "Point Type 5"]
image1_with_legend = add_legends(image1, legend_colors, legend_labels)
cv2.imwrite("image_with_legend.jpg", image1_with_legend)

# 测试连续颜色条
from matplotlib import cm
def cmap_func(x):
    # 把0-255的灰度值转换成BGR颜色(matplotlib的cmap输出是RGB,所以要反转)
    rgba = cm.viridis(x / 255.0)
    return (int(rgba[2]*255), int(rgba[1]*255), int(rgba[0]*255))

image2 = np.random.randint(0, 255, (1000, 2000))
# 批量转换为彩色图,比vectorize更高效
vals = image2.flatten()
colors = np.array([cmap_func(v) for v in vals], dtype=np.uint8)
image2_colored = colors.reshape(image2.shape[0], image2.shape[1], 3)

image2_with_colorbar = add_colorbar(image2_colored, cmap_func)
cv2.imwrite("image_with_colorbar.jpg", image2_with_colorbar)

注意事项

  1. 内存优化:如果你的图像是50k×50k级别的,直接拼接会占用大量内存(比如50k×52k×3的图像约7.8GB),如果内存不足,可以选择:
    • 缩小图例/颜色条的尺寸
    • 将图例/颜色条直接绘制在原图的角落(覆盖部分图像),这样不需要创建新的大数组
  2. 颜色空间:OpenCV默认使用BGR格式,如果你用matplotlib的colormap,记得把RGB转换为BGR,否则颜色会显示错误
  3. 参数适配:根据你的图像实际尺寸调整block_size、font_scale等参数,确保图例和颜色条能清晰可见

备注:内容来源于stack exchange,提问作者Leo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:19:35