
简介opencv颜色识别.zip 提供了一套基于 C# OpenCvSharp 的 HSV 颜色识别示例面向刚接触 OpenCV 的桌面开发者也适合需要在自动化、机器人或物体检测中快速筛选颜色的工程人员。压缩包内共 59 个文件包含 17 个 C# 源文件、8 个动态链接库、7 个资源文件、6 个 resx 窗体资源另附可执行程序、PDB 调试符号、配置文件、示例图片等整体约 35.14MB。已有 1718 人学习下载具备一定参考基础。工程采用 WinForms 多窗体结构涵盖主界面、HSV 阈值设置、图像显示与放大等模块运行后可实时调整色调、饱和度、亮度上下限观察原始图、掩码图与提取结果的联动变化。借助 OpenCvSharp 的 CvtColor、InRange、CopyTo 等核心接口读者能够完整走通 RGB 转 HSV、二值掩码生成、按颜色提取区域的处理链路并可将思路迁移到颜色分拣、目标跟踪等实际场景适合作为 OpenCV 与 C# 结合的颜色识别入门模板。1. opencv颜色识别能做什么不只是取色更是视觉分拣的地基做自动化的人大概率遇到过这样的需求产线上要按颜色把零件分类或者自己写的脚本要在屏幕截图里找到某个按钮。与其用PhotoShop一张张取色不如让OpenCV直接读摄像头或图片把目标颜色的区域框出来——这就是opencv颜色识别。它本质上是图像处理里最基础的一种“语义分割”给定一个颜色范围把图像里所有符合的像素挑出来变成一张黑白mask再通过轮廓分析拿到位置、面积和形状。这个方案不需要训练模型配置简单新手半小时内就能跑通第一版适合工业分拣、视觉定位、交通灯检测、数据标注辅助这类场景。但“能跑”和“能稳定用”是两码事光照一变、相机一换颜色范围就要重新调。这篇笔记我把环境配置、HSV原理、最小代码和踩坑记录都过一遍你照着做就能搭出一个属于自己的颜色识别工具。2. 环境先行解压zip、装好OpenCV先跑通一条最小命令2.1 拿到zip先别乱解压看清目录结构和入口脚本我拿到一个“opencv颜色识别.zip”第一步不是双击解压而是先右键看看压缩包的大小和文件数量。常见的做法是把它放到一个纯英文路径下比如D:\cv_projects\color_demo因为有些OpenCV旧版本和Python在中文路径下读文件容易出幺蛾子。解压后一般会看到几个.py文件、一组测试图片可能还有一份requirements.txt或README.md。我的习惯是先用tree命令看一眼结构。Windows里没有tree命令的话用dir /s也行Linux下直接tree -L 2cd color_demo tree -L 2这个命令输出的是目录层级能帮你快速找到入口脚本通常是main.py或color_detect.py。如果压缩包里有requirements.txt那就说明作者把依赖写清楚了直接按它装环境。如果包里没有这个文件你就需要自己装OpenCV和NumPy。不要一上来就双击主脚本多半会报ModuleNotFoundError然后才开始找依赖浪费时间。2.2 安装OpenCVWindows用pipUbuntu用apt或源码编译大部分颜色识别项目用Python调OpenCV就够。Windows用户直接开命令行python -m pip install opencv-python numpy装机量最大的包是opencv-python它带了CPU版本的OpenCV颜色识别这种任务用不到CUDA加速没必要去装opencv-contrib-python。注意别只装opencv那个包在PyPI上是个老古董版本还停在远古时期。安装完成后验证一下python -c import cv2; print(cv2.__version__)能打印出版本号就代表装好了。如果你看到的是4.5.x或4.6.x都是正常的有些老教程让你装3.4.1的MinGW编译版那是给C用户用的Python绑定没有这个需求。Ubuntu的用户则有两种常见路线。第一种是省事的aptsudo apt update sudo apt install python3-opencv python3-numpy这条命令装的OpenCV版本由系统源决定可能偏旧但胜在稳定很多服务器上的视觉项目都用它。第二种是编译安装适合需要定制模块的场景但编译一次够喝一壶的依赖要装libgtk2.0-dev、libavcodec-dev等一长串。我的建议是颜色识别项目不需要特殊模块直接用apt或pip别学那些“从源码编译OpenCV”的教程除非你确实要改底层源码。装完之后把解压出来的脚本跑一下。如果脚本里写了import cv2这时import就不会报错了。如果还报ModuleNotFoundError: No module named cv2说明你用的是系统Python而pip装到了另一个Python环境。Windows用户检查一下命令是不是在同一个终端里执行的Ubuntu用户试试python3而不是python。这是新手最常见的安装坑。3. 识别原理为什么颜色识别里HSV比RGB靠谱3.1 RGB阈值为什么容易翻车三个通道相互牵连很多人第一次做颜色识别直觉都是用RGB。比如红色就是R高G低B低于是设R200, G50, B50。表面上看没问题但换一张光线偏暗的照片红色物体的R可能只有150阈值就失灵了。更麻烦的是在RGB空间里“红色的深浅变化”会同时影响三个通道物体的阴影区域G和B会涨R会跌你没法用一组固定阈值框住同一种颜色的所有深浅。环境光一变RGB三个数值一起漂移阈值怎么设都不对。这就是为什么颜色识别领域基本都推荐用HSV。HSV把颜色信息拆成了色相H、饱和度S、亮度V其中H表示“这是什么颜色”和光线强弱的耦合比RGB小得多。对同一种物体来说无论处在阴影还是强光下H值大致稳定变的更多是V和S。所以我们只要把H的范围框住再放宽S和V的上下限就能识别出同一类颜色。3.2 HSV三个分量怎么说H、S、V各管什么事OpenCV里HSV的数值范围和别的软件不一样。H是0到179S和V是0到255别用Photoshop那种0到360、0到100的思维来看。H决定色相0是红色30是黄色60是绿色120是蓝色S是饱和度颜色越灰S越低V是亮度纯黑V就是0。识别思路很简单先把BGR图像转成HSV再用inRange函数设定每个通道的上下限。下面这一小段代码把图像里的蓝色目标提取出来import cv2 import numpy as np img cv2.imread(test.png) # 读取BGR图像 img_hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 转到HSV空间 lower np.array([100, 80, 60]) # H100(蓝左右), S至少80, V至少60 upper np.array([130, 255, 255]) # H上限130, S和V放开 mask cv2.inRange(img_hsv, lower, upper) cv2.imshow(mask, mask) cv2.waitKey(0)这里的lower和upper就是你要调的核心参数。H下限100、上限130框住了蓝色区域S下限80排除了接近灰白的像素V下限60排除了太暗的阴影。如果画面里有很多灰蓝色墙壁就把S下限提高到100。如果阴影区老被漏掉就把V下限降到40。OpenCV的inRange对每个像素做区间判断落在范围内的变成255否则是0生成的mask直接就能当二值图用。3.3 常见颜色的HSV范围参考表我积累的一张表作为起点很好用。颜色识别本来就是经验活儿别指望一张表走天下但你可以从这里开始调目标颜色H下限H上限S下限S上限V下限V上限备注红色08100255100255红色在H0附近还要考虑H170-179红色深红085025550255对饱和度宽容一点橙黄色103580255100255适合交通灯、橘子绿色50856025550255植物绿色较宽蓝色951308025550255深蓝要降V下限紫色1301606025550255紫色和品红容易混白色0180030180255白的是低饱和高亮度H没意义黑色01800255046黑色是高饱和低亮度注意红色有个特殊之处它在H轴的两端都有分布0附近是正红170附近是品红。如果你只设H:0-8会漏掉偏紫的红色。所以识别红色时我一般分两段0-8和170-179分别生成mask再按位或。3.4 用代码试出精确范围从图像里取像素的HSV值拿到一张图不知道目标颜色的HSV范围别瞎猜。写个脚本用鼠标点击图片打印该点的HSV值多采几个点就能归纳出范围import cv2 img cv2.imread(sample.jpg) img_hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) def on_mouse(event, x, y, flags, param): if event cv2.EVENT_LBUTTONDOWN: hsv_value img_hsv[y, x] bgr_value img[y, x] print(f像素({x},{y}) BGR{bgr_value} HSV{hsv_value}) cv2.imshow(image, img) cv2.setMouseCallback(image, on_mouse) cv2.waitKey(0)点击暗部、亮部、边缘各取几个点你会看到BGR数值跳来跳去但H值通常维持在一个窄区间内。这就是HSV适合作颜色识别的原因。要注意的是图像边缘混合了背景色H会向背景色偏移取点时避开物体边缘。只要目标内部的H值稳定S和V允许一定范围内的变化你的HSV阈值就立住了。4. 跑通一个最小颜色识别程序从mask到轮廓4.1 第一步读取图像并转到HSV颜色空间我先给你一套完整可运行的最小程序。假设你要在一张照片里识别所有绿色物体并画出它们的包围盒。代码不多但每一步都有讲究分四个步骤走。import cv2 import numpy as np # 读取图片注意路径里不能有中文 img cv2.imread(scene.jpg) if img is None: print(图片读取失败检查路径和文件名) exit() # 缩小图片速度更快噪声也更少 scale 0.8 img cv2.resize(img, None, fxscale, fyscale, interpolationcv2.INTER_LINEAR) # 转HSV img_hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV)这里先做resize有个好处轮廓检测在大图上非常耗时而且小目标噪声多。颜色识别通常不在乎那几个像素的差别缩放到0.8倍甚至0.5倍都能接受。如果图片原本就是4K我先缩到1/4再说。cvtColor的COLOR_BGR2HSV是从BGR转到HSVOpenCV读图默认是BGR别搞反成RGB。4.2 第二步用inRange框定目标颜色接着定义绿色阈值并生成mask# 绿色范围在3.3节的参考表基础上放宽一点 lower_green np.array([45, 60, 60]) upper_green np.array([90, 255, 255]) mask cv2.inRange(img_hsv, lower_green, upper_green)inRange输出的是单通道二值图255代表目标0代表背景。看一下mask的白色区域是否和目标重合。如果白色区域比目标大说明阈值太宽如果漏了很多说明阈值太窄。这时你要做的是回到上一节提取像素值把S和V的下限往下放、H的上限范围收窄或放宽而不是盲目加各种滤波。4.3 第三步形态学处理让目标更干净mask上常见的问题是目标区域里有小黑洞内部噪点或者背景里散布着小白点。用形态学操作处理一下# 用一个5x5的椭圆核做开运算先腐蚀后膨胀去掉小白点 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations1) # 再做一次闭运算先膨胀后腐蚀填补内部黑洞 mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations2)开运算适合去掉孤立噪点闭运算适合填充目标内部的空腔。MORPH_ELLIPSE比矩形核更圆滑不会给轮廓留下尖锐的边角。核大小和迭代次数看mask的噪声程度噪声多就调大kernel到7x7但别太大否则两个相邻目标会连成一片。4.4 第四步找轮廓、画框、输出面积最后用findContours拿到所有白色区域的轮廓按照面积过滤掉小目标再画框# 只保留面积大于500像素的轮廓 contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for contour in contours: area cv2.contourArea(contour) if area 500: continue # 用包围盒框住目标 x, y, w, h cv2.boundingRect(contour) cv2.rectangle(img, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText(img, farea{int(area)}, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(result, img) cv2.waitKey(0) cv2.destroyAllWindows()RETR_EXTERNAL只取外部轮廓不会把目标内部的纹理轮廓带出来CHAIN_APPROX_SIMPLE压缩轮廓点减少内存占用。面积过滤用的是绝对像素值如果图片缩放变了这个阈值要跟着变。我一般先把图片固定到统一宽度比如resize到800宽这样面积阈值才有一致性。boundingRect返回的四个值是左上角坐标和宽高直接拿它画框就行。这套流程对绝大多数颜色识别项目都通用转HSV、inRange、形态学、轮廓。你只需要替换颜色阈值和处理逻辑就能应用到从摄像头抓帧、视频文件分析或屏幕截图的场景。5. 颜色识别避坑指南光照、噪声、参数漂移的四个翻车现场5.1 现象一识别区域忽大忽小同一个物体隔几秒就变了我在做产线分拣时就遇到过传输带上同一个红色零件前半段识别正常过个十几秒突然识别区域小了三分之一再过一阵又恢复。原因是车间顶灯频闪或室外云层飘过导致亮度V在循环跳动。解决的方法是找一帧画面统计亮度动态调整V的上下限。简单做法是抓取mask之前先计算整帧的HSV中位V值根据它把V门槛上下平移v_median np.median(img_hsv[:, :, 2]) offset int(v_median - 128) # 假设128是基准亮度 lower_green[2] min(255, max(0, 60 offset - 20)) upper_green[2] 255这样当画面变暗时V下限跟着降低避免目标被当成阴影吞掉。如果环境光变化剧烈更稳的方案是换硬件比如给相机加偏振片或固定补光灯纯软件是治标不治本。5.2 现象二mask上全是小噪点目标区域像被撒了一把芝麻低端USB摄像头压缩比很高画面上全是块状噪声inRange会把灰度接近目标颜色的背景小块也选进来。我见过最夸张的一次一张桌子上密密麻麻白点面积过滤阈值加到2000了还有一堆。原因有两个一是摄像头采集本身带了噪声二是阈值S设得太低灰暗背景的饱和度低但H碰巧落在目标范围里。解决顺序是先调高S下限比如从60提到100看噪点是否明显减少再用5x5的核做开运算。如果还不够就在inRange前先对原图做一次高斯模糊img_blur cv2.GaussianBlur(img, (5, 5), 0) img_hsv cv2.cvtColor(img_blur, cv2.COLOR_BGR2HSV)高斯模糊会让颜色过渡圆滑减少边缘的杂散H值。但模糊半径别超过7否则目标边缘会虚mark区域会缩水。5.3 现象三换了摄像头同一套参数全部失效同一个颜色在A相机下红得发亮在B相机下变成了暗红。尤其换了不同品牌的工业相机或手机前置/后置摄像头白平衡算法各异H值能漂移十几甚至二十多。这是一个非常常见的“参数漂移”问题。我的做法是给每个摄像头单独存一份配置文件用YAML或JSON保存HSV上下限。换相机时切换配置而不是改代码。另外如果相机支持手动白平衡尽量关闭自动白平衡固定到一个色温值这样颜色识别参数的稳定性会大幅提升。如果是普通网络摄像头可以在软件里加一层白平衡校准抓一帧白色参考物反向修正BGR增益但这属于进阶玩法新手不建议一上来就用容易把整个流程搞复杂。5.4 现象四程序一运行就报ModuleNotFoundError或者cv2版本对不上新手最容易碰到的异常有两类。一类是ModuleNotFoundError: No module named cv2通常是pip装到了conda的base环境但运行脚本用的是别的Python环境。检查方式是用python -c import cv2; print(cv2.__version__)确认你的解释器能import到。Ubuntu下还要注意如果你用了pip install --user但apt的Python和pip的Python不是同一个也会冲突。另一类是同一个项目里有人用OpenCV 3.x的接口有人用4.x。比如findContours在OpenCV 3里返回三个值4.x里返回两个值。我早期写代码就栽过拿两个返回值去解包3.x的元组直接报ValueError: not enough values to unpack。解决方法是统一版本读取cv2.__version__确认大版本然后在调用findContours时都写成contours, hierarchy cv2.findContours(...)这样在4.x下没问题3.x下就会报错。如果你必须兼容旧版本要么统一环境要么加个版本判断。我的建议是直接升级到4.x别再碰3.x的老代码。6. 让颜色识别更可靠动态调参、多颜色识别与验证技巧6.1 用trackbar在运行中调HSV范围调参最烦的是每一次都要改代码再重跑。我一般会加一组OpenCV的trackbar让HSV上下限在窗口中实时调节。核心是给每个通道建一个createTrackbarcv2.createTrackbar(H_min, control, 45, 179, nothing) cv2.createTrackbar(H_max, control, 90, 179, nothing)然后在循环里读当前trackbar的值动态生成lower和upper。这样你把窗口和原图并排动一下滑块结果立刻变十分钟就能调出一组不错的阈值。调好之后把参数写进配置文件再回到正常识别逻辑里用固定值。6.2 多颜色识别一次循环处理多组阈值如果场景里要同时识别红、绿、蓝三种颜色不要写三套重复代码。把颜色阈值放在一个字典里循环处理colors { red: ([0, 100, 100], [8, 255, 255]), green: ([45, 60, 60], [90, 255, 255]), blue: ([95, 80, 60], [130, 255, 255]), } for name, (lower, upper) in colors.items(): mask cv2.inRange(img_hsv, np.array(lower), np.array(upper)) # 继续同样的形态学和轮廓处理…这样后续要加新颜色只需要在字典里多填一行。注意一种颜色有两个H区间的比如红色就在字典里存两组阈值分别生成mask后再cv2.bitwise_or合并。6.3 验证识别效果mask交并比与像素占比识别效果好不好不能只靠肉眼。我在测试时会把预测mask和人工标注的mask算一下IoU交并比。如果你的zip包里带了一个标准答案文件夹这个验证方式很适合拿去评估intersection np.logical_and(predict_mask, gt_mask).sum() union np.logical_or(predict_mask, gt_mask).sum() iou intersection / union if union ! 0 else 0IoU大于0.7就算可用大于0.85说明参数很准。如果IoU总是偏低不是你H范围的问题就是光照影响了边缘。另外也可以用目标像素占整幅图的比例做粗验证比如绿色占画面的10%到30%超出范围直接告警提示参数需要重调。这些年做颜色识别项目我最大的教训就是永远不要高估参数的通用性。同一个方案从白天办公室搬到晚上仓库或者从这台机器换到另一台机器都要重新调一遍。只有把调参流程固化下来比如动态trackbar加配置外置才算真正“会”了这个技术。颜色识别本身不复杂但稳定运行靠的是一套能应对环境变化的机制。希望这篇文章帮你在做opencv颜色识别时少走弯路尽快把项目从“能跑”推到“能用”。本文还有配套的精品资源点击获取