为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)
注意事项
- 内存优化:如果你的图像是50k×50k级别的,直接拼接会占用大量内存(比如50k×52k×3的图像约7.8GB),如果内存不足,可以选择:
- 缩小图例/颜色条的尺寸
- 将图例/颜色条直接绘制在原图的角落(覆盖部分图像),这样不需要创建新的大数组
- 颜色空间:OpenCV默认使用BGR格式,如果你用matplotlib的colormap,记得把RGB转换为BGR,否则颜色会显示错误
- 参数适配:根据你的图像实际尺寸调整
block_size、font_scale等参数,确保图例和颜色条能清晰可见
备注:内容来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

