如何高效检测目标物体颜色?YOLO裁剪后非深度学习快速方案
高效目标颜色检测方案(基于裁剪后的目标区域)
需求背景
已通过YOLO模型得到目标边界框并裁剪出目标区域,需要高效、准确的颜色检测/分类方案,要求速度极快,优先使用纯Python、OpenCV等非深度学习工具。此前尝试过两种方案均不理想:
- 训练YOLO检测10类颜色,但双模型运行速度无法满足实时需求;
- 将图像缩至1x1像素取色,但精度极差、结果偏差大。
解决方案
一、HSV空间直方图峰值法(极致速度+高鲁棒性)
RGB空间的颜色分布不适合直接统计,转HSV后,H通道(色调)直接对应人眼感知的颜色类别,统计H通道的直方图峰值即可快速得到主色调,再通过S(饱和度)和V(明度)过滤低饱和、过暗/过亮的干扰像素。
import cv2 import numpy as np def get_dominant_color_hsv(cropped_img): # 转HSV颜色空间(OpenCV默认BGR格式) hsv = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2HSV) # 过滤低饱和、过暗/过亮的像素(阈值可根据实际场景调整) mask = cv2.inRange(hsv, (0, 30, 30), (179, 255, 255)) hsv_filtered = hsv[mask > 0] if len(hsv_filtered) == 0: # 无有效像素时取H通道均值 h_mean = np.mean(hsv[:, :, 0]) dominant_h = int(h_mean) else: # 统计H通道直方图,取峰值对应的H值 hist = cv2.calcHist([hsv_filtered], [0], None, [180], [0, 180]) dominant_h = np.argmax(hist) # 映射为你的10类颜色(根据需求调整区间) color_map = { (0, 10): "红色", (10, 25): "橙色", (25, 35): "黄色", (35, 77): "绿色", (77, 90): "青色", (90, 127): "蓝色", (127, 150): "紫色", (150, 160): "粉色", (160, 179): "红色", (0, 0): "黑色" # 补充你需要的颜色类别 } for (h_min, h_max), color_name in color_map.items(): if h_min <= dominant_h <= h_max: return color_name, dominant_h return "未知", dominant_h # 示例调用(cropped_img为YOLO裁剪后的BGR图像) # cropped_img = cv2.imread("cropped_target.jpg") # color_name, h_value = get_dominant_color_hsv(cropped_img) # print(f"主颜色:{color_name},H值:{h_value}")
二、K-Means聚类法(更高精度)
如果目标区域颜色有轻微渐变或少量杂色,可使用K-Means对像素聚类,取占比最高的簇中心作为主色,速度远快于深度学习,适合对精度要求较高的实时场景。
import cv2 import numpy as np from sklearn.cluster import KMeans def get_dominant_color_kmeans(cropped_img, n_clusters=2): # 转RGB格式并扁平化像素数组 rgb_img = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2RGB) pixels = rgb_img.reshape(-1, 3) # 过滤低饱和像素(减少干扰) hsv = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2HSV) s_mask = hsv[:, :, 1] > 30 pixels_filtered = pixels[s_mask.flatten()] if len(pixels_filtered) < 10: # 有效像素过少时取全局均值 mean_color = np.mean(pixels, axis=0).astype(int) dominant_color = tuple(mean_color) else: # K-Means聚类(n_init=10避免局部最优) kmeans = KMeans(n_clusters=n_clusters, n_init=10, random_state=42) kmeans.fit(pixels_filtered) # 取占比最高的簇中心 counts = np.bincount(kmeans.labels_) dominant_color = kmeans.cluster_centers_[np.argmax(counts)].astype(int) dominant_color = tuple(dominant_color) # 映射为你的10类颜色(根据RGB阈值调整) r, g, b = dominant_color if r > 200 and g < 100 and b < 100: return "红色", dominant_color elif r > 200 and g > 150 and b < 100: return "橙色", dominant_color # 补充其他颜色判断逻辑... return "未知", dominant_color # 示例调用 # cropped_img = cv2.imread("cropped_target.jpg") # color_name, rgb_color = get_dominant_color_kmeans(cropped_img) # print(f"主颜色:{color_name},RGB值:{rgb_color}")
三、优化你的1x1取色法(简单场景可用)
原方法直接缩至1x1会受边缘噪点、阴影影响,可通过模糊处理或取中心区域均值优化:
from PIL import Image, ImageFilter import numpy as np def get_dominant_color_optimized(pil_img): img = pil_img.copy().convert("RGB") # 方法1:先高斯模糊再缩小,过滤噪点 img_blur = img.filter(ImageFilter.GaussianBlur(radius=2)) img_blur = img_blur.resize((1, 1), resample=Image.Resampling.BILINEAR) dominant_color = img_blur.getpixel((0, 0)) # 方法2:取中心1/3区域的均值,避免边缘干扰 # w, h = img.size # center_w, center_h = w//3, h//3 # center_region = img.crop((center_w, center_h, w-center_w, h-center_h)) # pixels = list(center_region.getdata()) # dominant_color = tuple(np.mean(pixels, axis=0).astype(int)) # 颜色映射逻辑 r, g, b = dominant_color if r > 180 and g < 80 and b < 80: return "红色", dominant_color # 补充其他颜色判断... return "未知", dominant_color # 示例调用 # image = Image.open("cropped_target.jpg") # color_name, rgb_color = get_dominant_color_optimized(image) # print(f"主颜色:{color_name},RGB值:{rgb_color}")
选择建议
- 极致速度优先:选HSV直方图峰值法,单帧处理耗时在毫秒级,完全满足实时要求;
- 高精度优先:选K-Means聚类法,注意设置
n_clusters=2或3控制计算量,结合HSV过滤可进一步提升效率; - 简单均匀场景:用优化后的1x1取色法,仅适合颜色无渐变、无杂色的目标。
内容的提问来源于stack exchange,提问作者angel_30
相关产品推荐
相关产品推荐

