资讯动态

OpenCV色差法目标提取:HSV阈值、形态学与轮廓筛选实战

发布时间:2026/10/23 10:52:09 来源:尧图企业网站定制
用 OpenCV 做目标提取最省事、见效最快的一条路往往不是训练模型而是基于色差的阈值分割。这个方法的朴素版本十几行代码就能跑通读图、转颜色空间、卡阈值、做形态学、找轮廓。它解决的问题很具体——当目标物体在颜色上和背景有稳定区分度时不需要样本、不需要标注、不需要显卡一台普通笔记本就能实时处理视频流。适合谁看做工业视觉初筛的、写自动化脚本的、玩树莓派小车巡线的、做简单质检 Demo 的朋友以及刚学 OpenCV 想找个能立刻出结果的小项目练手的人。但我也得说实话色差法是把双刃剑参数调对了丝滑得让人上瘾参数没调对就是一屏噪点改到你怀疑人生。这篇就把我自己反复折腾出来的那套流程完整摊开——阈值怎么标、颜色空间怎么挑、形态学核开多大、轮廓怎么筛、光照一变怎么补救全部给到可复现的代码和参数区间。1. 先把色差这件事想明白适用边界与选型逻辑1.1 色差法到底在算什么先把概念捋清楚。所谓基于色差的目标提取本质是在某个颜色空间里给目标颜色划一个允许范围落在范围内的像素判为前景落在范围外的判为背景最后得到一张二值掩膜mask。整个过程没有任何理解成分它不认识杯子也不认识螺丝只知道这个像素的三元组数值在不在我给的区间里。这带来两个直接后果。好处是速度极快一张 1080p 图在普通 CPU 上跑一遍inRange通常在个位数毫秒量级比任何神经网络都轻。坏处是它对颜色的定义是绝对的、逐像素的、无空间上下文的同一块红色在阴影里和在强光下数值能差出一大截于是掩膜就碎了。所以我在动手之前一定会先问自己一个问题这个场景里目标的颜色是不是相对稳定的如果目标本身是均匀的、单一的、和背景有明显色相差异的那色差法就是性价比最高的选择如果目标是多色的、渐变的、和背景灰度接近的那趁早换思路别在这条路上耗时间。注意色差法从来不是识别物体是什么它只回答哪些像素长得像我要的颜色。把它当成定位工具而不是分类工具你的期望值就不会跑偏。1.2 三类适合上手的场景和三类别硬上的场景我把实际遇到的情况大致归了两类判断标准很简单就看目标与背景的色相夹角够不够大。适合上的场景通常是这样的目标颜色饱和度高、表面哑光、光照条件基本可控。比如流水线上分拣固定颜色的塑料件比如地面胶带的巡线识别比如从浅色背景里抠出高饱和的物料块。这类场景里目标颜色的 H 通道非常集中阈值区间可以收得很窄掩膜几乎一次成型。不适合的场景也很典型目标颜色和背景色相接近只是明暗不同比如深灰料框里的黑色工件这类直接卡 HSV 会连带把背景一起选进来。另一种是强反光表面的金属件高光区域 V 通道爆表、S 通道塌陷H 值完全失真。还有一种是有彩色光源照射的场景画面整体偏色标定时好好的换了班次光源一换就全废。这两种情况的处理方式不一样前者说明该用亮度差或边缘特征后者说明该先把光源固化下来——加遮光罩、换直流光源、锁定相机曝光和白平衡硬件解决比算法解决便宜得多。我踩过最深的一次坑就是在一个偏黄光的环境里死磕参数调了三天不如给光源加个柔光罩管用。1.3 颜色空间怎么挑RGB、HSV、LAB 的实测对比这是新手最容易含糊的地方。直接给结论绝大多数情况用 HSV需要抗光照变化用 LABRGB 基本只在做色差距离计算时才有意义。原因在于三个通道的物理含义不一样。RGB 三个通道都同时受亮度和色度影响你把红色物体的亮度砍一半R、G、B 三个值会一起掉你没法用一个固定的三维盒子同时框住亮红和暗红。HSV 把亮度和颜色拆开了H 是色相只在 0 到 179 之间循环OpenCV 为了放进 uint8 做了折半S 是饱和度V 是明度。你想忽略明暗只认颜色就把 H 和 S 卡死、V 放宽这是 RGB 做不到的。颜色空间H/L 含义抗亮度变化适合的操作主要坑点RGB/BGR三通道混叠差计算参考色距离、做加权赋值亮度一变全区间失效HSVH色相,S饱和度,V明度中单色阈值分割、快速抠图H 在红色附近跨 0/180 断裂LABL亮度,a/b色度好色差距离匹配、抗偏色数值不直观不好用滑块调LAB 的用法和 HSV 不太一样。它更适合算色差——取目标区域的平均 a、b 值当参考点然后算每个像素的 (a,b) 到参考点的欧氏距离距离小于阈值就算前景。这种写法对亮度的鲁棒性明显更好因为 L 通道被单独摘出去了。缺点是阈值不好用眼睛估得靠试。import cv2 import numpy as np def lab_color_diff_mask(img_bgr, ref_ab, thresh): lab cv2.cvtColor(img_bgr, cv2.COLOR_BGR2LAB).astype(np.int16) diff lab[:, :, 1:3] - np.array(ref_ab, dtypenp.int16) dist np.sqrt(np.sum(diff ** 2, axis2)) mask (dist thresh).astype(np.uint8) * 255 return mask这段代码里ref_ab是从目标区域采样得到的 a、b 均值thresh一般从 12 开始试20 左右是比较宽的容差。我实测下来同色系不同光照下这个距离值会漂移 5 到 10所以阈值别卡得比 8 还紧否则一换环境就崩。2. 阈值不是拍脑袋从采样到区间的参数标定2.1 HSV 三个通道各自管什么调阈值之前必须清楚每个通道被拧动时画面会发生什么变化否则就是瞎试。H 通道控制选哪个颜色。0 附近是红30 左右是黄绿60 是绿90 是青120 是蓝150 是品红179 又回到红。这个通道的宽度决定了你容许多大范围的色相偏差一般单色目标给正负 8 到 15 就够给太宽会把邻近颜色拉进来。S 通道控制颜色有多浓。这个通道特别关键也是新手最容易留着 0 不管的地方——如果你把 S 的下限设成 0那所有灰白色的像素包括白墙、反光、白纸都会因为 H 值随意而乱入。我基本上从不让 S 低于 60多数情况从 80 到 100 起手。V 通道控制有多亮。这个通道是抗光照的主力通常设 40 到 70 的下限就够上限 255 全开。刻意压低 V 上限能干掉高光斑这是个很实用的小技巧。提示三个通道的调节顺序建议是 V → S → H。先把明显过暗的部分切掉再把低饱和的灰度切掉最后才精调色相宽度。反过来调会让你在噪点里迷失方向。2.2 用鼠标点几下就拿到初始区间手写[20, 90, 70]这种数组纯靠猜是低效的。我的做法是写一个十几行的取色小工具鼠标点哪儿就打印哪儿的 HSV点十几个点取最小最大值直接得到初始区间。import cv2 img cv2.imread(sample.jpg) if img is None: raise FileNotFoundError(图片没读到检查路径) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) info {h: [], s: [], v: []} def on_mouse(event, x, y, flags, param): if event cv2.EVENT_LBUTTONDOWN: h, s, v hsv[y, x] info[h].append(int(h)) info[s].append(int(s)) info[v].append(int(v)) print(f点({x},{y}) - H{h} S{s} V{v}) if len(info[h]) 3: print(当前区间:, [min(info[h]), min(info[s]), min(info[v])], [max(info[h]), max(info[s]), max(info[v])]) cv2.namedWindow(pick, cv2.WINDOW_NORMAL) cv2.setMouseCallback(pick, on_mouse) cv2.imshow(pick, img) cv2.waitKey(0) cv2.destroyAllWindows()采样的位置有讲究不能只点最亮的那一块。我的习惯是在目标上点三类位置正常照到的中间调、偏暗的阴影边、以及靠近高光的部分。三类点都采到得到的区间才是能覆盖全貌的而不是只覆盖理想区域的。采完之后把 min 再往外放一点余量H 放 5 到 10S 和 V 放 10 到 20这就是你的起始阈值。2.3 区间收放的手感宽一点还是窄一点阈值宽窄的取舍本质上是在召回和准确之间找平衡。放宽了目标区域覆盖得全但背景容易漏进来收窄了掩膜干净但目标可能被切掉一块、边缘残缺。我的经验是先宽后窄。第一轮把区间放到能完整包住目标哪怕背景进来一堆确认目标确实百分百被选中了第二轮开始逐步收紧每次只动一个通道观察掩膜变化直到背景开始大量渗入的前一格停下。这个前一格就是最优点附近。另外要专门处理红色的跨零问题。因为 H 在 0 和 179 两端都是红单个inRange是框不住的必须拆成两段再做或运算mask_a cv2.inRange(hsv, np.array([0, 100, 60]), np.array([10, 255, 255])) mask_b cv2.inRange(hsv, np.array([170, 100, 60]), np.array([179, 255, 255])) mask cv2.bitwise_or(mask_a, mask_b)橙红色同理如果采样点里 H 的 min 接近 0、max 接近 179那基本可以判定这个目标的色相是跨零的必须拆两段。这一点很多教程会漏掉新手照着抄红色提取代码发现只能抓一半八成就是这里出的问题。3. 掩膜的后处理把脏的结果洗干净3.1 开运算、闭运算与核大小的选择inRange出来的原始掩膜基本没法直接用的一定有零散噪点和内部空洞。形态学操作就是干这个的但顺序和核大小都要讲究。开运算是先腐蚀后膨胀作用是去掉小的孤岛噪点代价是目标边缘会小幅收缩。闭运算是先膨胀后腐蚀作用是填补目标内部的小孔洞和裂缝代价是边缘会小幅外扩。我的固定套路是先开一次去掉噪点再闭一到两次补洞。核大小这个参数最需要手感。核越大效果越强但形状失真也越明显。经验值1080p 图像上用 3x3 到 7x7小目标用 3x3大目标用 5x5 或 7x7尽量不要超过 9x9否则边角会被磨成圆弧。核的形状用椭圆MORPH_ELLIPSE比矩形更自然矩形核处理斜边时容易出现方块状的锯齿。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)这里iterations我一般开运算给 1闭运算给 1 到 2。开运算给多了会把小目标整个吃掉尤其是面积本来就小的目标这个坑我踩过一次——本来是进去除噪的结果连目标一起清了排查了半天才发现是迭代次数开到了 3。3.2 中值滤波与高斯滤波的取舍二值掩膜上还能再叠一层滤波但要注意区分用途。中值滤波对椒盐型的小噪点极其有效而且不像均值滤波那样把边缘糊掉它在二值图上用是完全合理的核一般给 3 或 5。高斯滤波我在二值掩膜上基本不用因为它会把 0 和 255 的边缘变成一片灰阶后续找轮廓时反而引入了半吊子边界。真正适合上高斯滤波的位置是在原始图像上、转 HSV 之前。如果原始画面噪点多比如夜间或高感光先在 BGR 图上做一个 3x3 或 5x5 的GaussianBlur能显著减少后续掩膜里的雪花点而且不会影响颜色判断的大方向。img cv2.GaussianBlur(img, (5, 5), 0) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) mask cv2.inRange(hsv, lower, upper) mask cv2.medianBlur(mask, 5)顺序很关键先模糊原图再转空间再卡阈值最后中值去噪。把顺序搞反了比如先卡阈值再高斯那你就得面对一片灰蒙蒙的边缘无从下手。3.3 轮廓筛选面积、长宽比、实心度掩膜干净了不代表结果就干净还是会有一些面积不大但形状可疑的残留。这一步靠轮廓属性来做二次过滤把不符合目标特征的候选剔除掉。我常用的三个属性是面积、长宽比、实心度。面积用来过滤太小噪点和太大背景误判的块长宽比用来排除形状明显不对的比如找圆形件时长宽比大于 3 的直接扔实心度是轮廓面积除以外接凸包面积用来判断这个块是不是实心的一块对于区分粘连目标和独立目标很有用。这三项里最值得说的是面积阈值不要写死成 500 像素这种绝对值。图像分辨率一变这个值就失效了。正确做法是写相对值用总面积的比例来表示h, w mask.shape[:2] area_total h * w min_area area_total * 0.0005 max_area area_total * 0.5这样 720p 和 4K 下同一套参数都能用移植到别的相机上也不用重调。这个习惯我是吃过教训才养成的早前写死像素值的脚本换个相机全崩从头调了一遍。4. 完整实操一套可直接复现的提取流程4.1 环境准备与目录结构依赖就三样Python 3.8 以上、opencv-python、numpy。装的时候有个小细节opencv-python是带 GUI 的精简版opencv-contrib-python带扩展模块做基本色差提取用前者就够了后者体积大一倍但用不上。pip install opencv-python numpy目录我习惯这样分方便批量跑project/ ├── data/ # 输入图片 ├── out/ # 输出结果 ├── pick_color.py # 取色标定脚本 └── extract.py # 主处理脚本调参阶段我强烈建议先拿几张有代表性的静态图跑通确认参数稳了再去接视频流。直接上摄像头调参是折磨因为画面一直在动你根本分不清是参数问题还是运动模糊问题。4.2 标定脚本交互式取色上一节给了单点取色的版本这里补一个更顺手的带实时预览掩膜的标定脚本改阈值的同时就能看到效果不用反复跑主程序。import cv2 import numpy as np img cv2.imread(data/sample.jpg) assert img is not None, 图没读到 hsv cv2.cvtColor(cv2.GaussianBlur(img, (5, 5), 0), cv2.COLOR_BGR2HSV) def nothing(x): pass cv2.namedWindow(tune, cv2.WINDOW_NORMAL) for name, val, mx in [(Hmin, 20, 179), (Hmax, 40, 179), (Smin, 90, 255), (Smax, 255, 255), (Vmin, 60, 255), (Vmax, 255, 255)]: cv2.createTrackbar(name, tune, val, mx, nothing) while True: lo np.array([cv2.getTrackbarPos(Hmin, tune), cv2.getTrackbarPos(Smin, tune), cv2.getTrackbarPos(Vmin, tune)]) hi np.array([cv2.getTrackbarPos(Hmax, tune), cv2.getTrackbarPos(Smax, tune), cv2.getTrackbarPos(Vmax, tune)]) mask cv2.inRange(hsv, lo, hi) preview cv2.bitwise_and(img, img, maskmask) cv2.imshow(tune, np.hstack([img, preview])) if cv2.waitKey(1) 0xFF 27: print(最终阈值 lower , lo.tolist()) print(最终阈值 upper , hi.tolist()) break cv2.destroyAllWindows()这种滑块调试的效率比改代码重跑高一个数量级我个人是把这套脚本当成常备工具每换一个新场景就先跑一遍它。滑块拖到满意后按 ESC终端会打印最终的 lower/upper 数组直接复制进主脚本。4.3 主流程脚本下面是完整可跑的主流程包含了前面讲的所有环节注释写得很密方便对着改。import cv2 import numpy as np # ---------- 1. 参数区 ---------- LOWER np.array([20, 90, 60]) UPPER np.array([40, 255, 255]) MIN_AREA_RATIO 0.0005 # 最小面积占比 MAX_AREA_RATIO 0.50 # 最大面积占比 MIN_ASPECT 0.2 # 长宽比下限 MAX_ASPECT 5.0 # 长宽比上限 # ---------- 2. 预处理 ---------- img cv2.imread(data/scene.jpg) assert img is not None, 检查图片路径 blur cv2.GaussianBlur(img, (5, 5), 0) hsv cv2.cvtColor(blur, cv2.COLOR_BGR2HSV) # ---------- 3. 阈值分割 ---------- mask cv2.inRange(hsv, LOWER, UPPER) # ---------- 4. 形态学 ---------- 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) mask cv2.medianBlur(mask, 5) # ---------- 5. 轮廓筛选 ---------- h, w mask.shape[:2] area_total h * w contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) out img.copy() count 0 for c in contours: area cv2.contourArea(c) if area area_total * MIN_AREA_RATIO: continue if area area_total * MAX_AREA_RATIO: continue x, y, bw, bh cv2.boundingRect(c) if bh 0 or bw 0: continue aspect bw / float(bh) if aspect MIN_ASPECT or aspect MAX_ASPECT: continue # 实心度过滤 hull cv2.convexHull(c) hull_area cv2.contourArea(hull) solidity area / hull_area if hull_area 0 else 0 if solidity 0.5: continue count 1 cx, cy x bw // 2, y bh // 2 cv2.rectangle(out, (x, y), (x bw, y bh), (0, 255, 0), 2) cv2.circle(out, (cx, cy), 5, (0, 0, 255), -1) print(f检出目标数量: {count}) cv2.imwrite(out/result.jpg, out) cv2.imwrite(out/mask.png, mask)这段代码在 1080p 图上跑一轮纯 CPU 大概 20 到 30 毫秒。真要上视频流把预处理阶段的GaussianBlur换成对彩色图做一次resize降采样再处理速度能再提一截代价是定位精度略降。4.4 参数速查表与调参顺序调参这件事我把它固化成一张表和一套顺序避免每次都从零摸索。步骤参数起始值调整方向观察指标1V 下限60噪点多则升高背景灰度是否被切干净2S 下限90白色反光多则升高高光区是否还残留3H 区间±10目标残缺则放宽目标是否被完整覆盖4开运算核5x5噪点多则加大孤立小点是否消失5闭运算迭代2有孔洞则加次数目标内部是否连续6面积下限0.05%误检多则升高小碎块是否被剔除7实心度0.5形状不规则则降低粘连目标是否被误删调参顺序严格按表格从上往下一次只动一个参数。同时动两个参数出了问题你都不知道是哪个导致的。这个原则听起来笨但能省掉大量返工。5. 踩坑记录与排查手册5.1 光照一变全废白平衡与曝光锁定色差法最普遍的死因就是光照漂移。早上调好的阈值下午太阳斜射进来掩膜直接崩。这不是算法的问题是阈值本来就是针对某一组光照条件标定的。软件层面的补救是先把相机的曝光和白平衡锁死。用cv2.VideoCapture打开摄像头后尽量关掉自动曝光和自动白平衡cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25) # 部分后端支持 cap.set(cv2.CAP_PROP_EXPOSURE, -6)不过这些属性在不同平台上支持度参差不齐能不能设上得实际试。设不上的话就走硬件路线固定光源、加遮光罩、避免环境光直射。我在一个半开放环境下做小件分拣的时候最终是靠一个简单的纸板遮光罩解决问题的成本几乎为零效果比调三天参数好得多。如果真的没法控制光照那就得考虑自适应方案每隔若干帧重新估计一次参考色或者把 V 通道的下限做成随时间缓慢自适应的变量。这类方案能做但复杂度上来了值不值得要看你的项目预算。5.2 相近颜色串味多区间叠加与二次判别另一个高频问题是背景里有个颜色相近的东西阈值无论怎么收都会带进来一块。这时候别死磕单一阈值了换思路。第一招是多区间叠加。用bitwise_or把几个互不重叠的 H 区间合起来覆盖目标在不同光照下的色相偏移同时刻意避开背景那个色相。m1 cv2.inRange(hsv, np.array([15, 90, 60]), np.array([25, 255, 255])) m2 cv2.inRange(hsv, np.array([30, 90, 60]), np.array([38, 255, 255])) mask cv2.bitwise_or(m1, m2)第二招是加位置约束。既然掩膜已经出来了可以用 ROI 做一次空间过滤只保留画面中特定区域内的候选。这一招在固定工位场景里特别有效因为目标出现的位置本来就相对固定。第三招是在掩膜之后再做一次二次判别比如取候选区域的均值和目标参考色的距离超过就丢弃。这已经超出纯色差的范围了但代价很小值得加。5.3 边缘毛刺与孔洞边缘毛刺的来源一般有两个一是原图噪声二是阈值卡在色相过渡带上。前者靠模糊和中值滤波解决后者只能靠收窄阈值或者扩大形态学核。孔洞则常见于目标表面有反光或者有贴纸、划痕的情况。这类孔洞用闭运算补最直接但核大了会失真我一般用分步法先用小核闭两次如果还有大洞再用floodFill从外部填充。ff mask.copy() h_, w_ ff.shape m2 np.zeros((h_ 2, w_ 2), np.uint8) cv2.floodFill(ff, m2, (0, 0), 255) mask_filled mask | cv2.bitwise_not(ff)floodFill从左上角灌水把外部背景变成 255取反之后就得到被轮廓围住的内部区域再和原掩膜或一下孔洞就补上了。注意起点必须落在真正的背景上如果左上角本身就是目标这招会失效。5.4 常见问题速查表下面这张表是我自己积累的遇到问题先扫一眼能省不少时间。现象最可能原因快速验证处理办法掩膜几乎全黑阈值区间写错或空间搞混打印几个像素的 HSV 值确认 COLOR_BGR2HSV检查通道顺序掩膜几乎全白S 下限过低或 H 区间过宽把 S 下限提到 120 看变化收紧通道重新采样目标只选中一半H 跨零未拆分看采样点是否同时接近 0 和 179拆成两段 inRange 再或满屏雪花点原图噪声大观察原图暗部先高斯模糊再中值滤波目标内部有大洞表面高光或污渍看原图对应位置闭运算 floodFill 补洞多个目标粘成一块目标间颜色连线看掩膜连通的区域提高 S/V 下限加开运算换相机后全失效参数是绝对像素值对比分辨率面积改相对占比重标阈值白天正常夜间崩光照变化对比两个时段采样值锁曝光白平衡或做自适应6. 从掩膜到可用结果目标框、计数与批处理6.1 外接矩形与中心点输出拿到筛选后的轮廓下一步通常是输出结构化结果而不是只存一张图。最常用的是外接矩形加中心点这套数据格式能直接喂给下游的控制逻辑或者记录系统。results [] for c in contours: x, y, bw, bh cv2.boundingRect(c) results.append({ cx: x bw / 2.0, cy: y bh / 2.0, w: bw, h: bh, area: cv2.contourArea(c) }) results.sort(keylambda r: r[cx])最后那句按 x 排序是个小细节但很有用。同一个视频流里目标可能无序出现排序之后输出顺序就稳定了下游做位置比对时不用再处理乱序问题。如果目标排布是多行的就按(y//50, x)这样的复合键排先把行分出来再按列排。另外一个建议是把像素坐标换算成物理坐标一并输出。换算不需要标定板只要在画面里放一把尺子量一段已知长度算出每像素对应的毫米数即可。这个比例系数记录在配置里后续所有输出都可以直接用物理单位。6.2 批量处理与日志单张图调通之后通常会面临几百上千张图要跑的情况。这时候别写循环读文件就完事一定要带日志否则出了问题你根本不知道是哪张图出的。import glob import os import csv rows [] for path in sorted(glob.glob(data/*.jpg)): img cv2.imread(path) if img is None: rows.append({file: os.path.basename(path), count: -1, note: read_fail}) continue # ... 处理逻辑 ... rows.append({file: os.path.basename(path), count: count, note: ok}) with open(out/report.csv, w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[file, count, note]) writer.writeheader() writer.writerows(rows)日志里我至少会记三样文件名、检出数量、异常标记。有了这三样跑完一批之后先扫一眼数量分布哪张图数量明显偏离中位数就单独拎出来看效率比一张张肉眼看高得多。这个习惯是从一次跑两千张图、结果发现有三十张是空图开始的——如果当时有日志五分钟就能定位实际上花了一下午。6.3 什么时候该换方法最后说说边界。色差法不是万能的遇到下面几种情况我会果断换路子。一是目标多色且颜色分布不固定比如一筐混杂零件。这种情况色差法只能一个个颜色试维护成本高得离谱不如换成模板匹配或者轻量的目标检测。二是背景极其杂乱、和目标有大量同色区域。这时候不管怎么调阈值都是拆东墙补西墙我会转向边缘特征或者轮廓形状特征用几何信息而不是颜色信息来定位。三是对精度要求很高的测量类任务。色差法得到的边界本质上是被阈值切出来的误差在几个像素是常态做定位可以做尺寸测量就勉强了。我个人在实际项目里的判断标准是如果同一套参数在连续采集的五十帧里掩膜面积波动能控制在正负百分之十以内那我就认为这个场景是稳的可以继续往下做如果波动超过百分之三十说明光照或者目标本身的颜色就不稳定这时候再怎么调参数都是治标该考虑换方案或者改硬件了。这个经验阈值不一定适用于所有人但作为一个快速判断的尺子它帮我省掉了很多在错误方向上死磕的时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑