系列: 上篇 成文/番外-饲养员厨房-上|标准简餐
上篇只是介绍了我做简餐的水平:把题面备好,按批端给深度考生,最终让模型给我干活。
但真正的艺术家往往坚持手搓。就算我自己单打独斗,也是很有水平的。
本篇就是介绍一下我的看家本领------滤波、边缘、轮廓、阈值这些工艺手法 。有时直接交卷,有时帮人写出 mask / 轮廓;不是深度学习专用配菜。
本篇可以配合 LearnOpenCV · Getting Started with OpenCV 一起食用------那边按读图、几何、滤波、阈值、边缘、轮廓一路拆;这边讲这些手法在工程师灶台上怎么用、和深度考生怎么分家。
0. 澄清误会:三条路,别混
| 路 | 在干啥 | 例子 |
|---|---|---|
| A · 备餐给深度模型 | 把图变成考生能吃的张量(几何+数值) | resize/crop、ToTensor、Normalize ------ 上篇 |
| B · 古典灶台直接出菜 | 不用深度模型,传统法直接交卷 | 海天:阈值/形态学 → mask 就是答案 |
| C · 帮人出/核答案 | 辅助标注、伪标签、质检可视化 | 边缘帮忙描轮廓;阈值出粗糙 mask 再人工改 |
所以:
- 不全是「训练数据的一部分」------很多时候古典法自己就是考生。
- 可以是「制作答案」------B、C 常这样;A 主要是做题面。
- 喂 AlexNet 认 ImageNet 时:主路径是 A ;滤波边缘不是必经主菜。
核心还是那句:工程师选择何时手工、何时让模型打工。
1. 滤波 / 模糊:先清理案板
古法大餐第一步,往往不是直接切菜------先清理案板 :抖掉不该在台上的东西,只留下这次要用的食材和工具。
滤波 / 模糊干的就是这事:去噪、抹掉多余细节,让后面的边缘、阈值少踩坑。它常常是流水线开头,不是整道菜本身。
用一个小矩阵(卷积核)在图上滑动,每个像素用邻域算出新值。
和深度考生体内那种「可学习的核」像,但这边核是人定死的------不靠 loss 反传改。
常见三招(邻域多半是 3×3 / 5×5 这类小窗):
| 邻域怎么算新像素 | kernel 长什么样 | 去什么噪 | 特点 | |
|---|---|---|---|---|
| 均值 | 算术平均 | 全 1,再归一化 | 一般随机噪 | 快,边缘糊 |
| 高斯 | 加权平均(中心权重大) | 中间大、边上小(钟形) | 高斯噪 | Canny 前常用 |
| 中值 | 排序取中间值 | 不用这种卷积核写法 | 椒盐噪 | 孤立黑白点压得住 |
均值、高斯:小窗滑过去,用核里的权重加权求和 → 典型卷积核。
中值:还是在 3×3 / 5×5 里动手,但把邻域像素排个序,取中间那个当新像素------不是「乘核再加总」那一套,所以一般不说它是卷积核。
清完案板,不等于大餐出锅------更不等于「深度预处理完成」。后面边缘、轮廓、阈值,才是古法流水线往下走。
2. 边缘:进入雕刻状态
案板清完,下一刀不是染色------是雕刻。
边缘这一步,开头先转灰度。不是不关心颜色,是这时候脑子里要的不是「多鲜艳」,而是最终会留下的形状 :墙和门的交界、海和天的接缝、物件轮廓该落在哪。
彩色三通道吵来吵去,不好统一问「这里亮暗变没变」;灰度每个像素一个亮度,只问坡有多陡。
第一个概念:梯度
把一行灰度像素想成一串数字:
markdown
... 100 102 105 200 198 196 ...
↑这里从 ~105 跳到 200
梯度 ≈ 亮度变化有多猛 (坡有多陡)。
平坦≈0,不像边;猛跳→有可能是边。还不是「一定是边」------后面还要筛、还要连。
2D 图上要看两个方向:
| 在比什么 | |
|---|---|
| Gx | 左右亮暗差 → 对竖边敏感 |
| Gy | 上下亮暗差 → 对横边敏感 |
合起来是向量 (Gx, Gy):往哪变最猛、有多猛。
Sobel:先横着刨一刀,再竖着刨一刀
Sobel 就是用固定核把 Gx、Gy 算出来。记住两样:
- xy 方向上的梯度 (分开看 / 分开
imshow都很有戏) - 梯度幅值
√(Gx² + Gy²)→ 不管横竖,只要亮暗差大就亮
python
gx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
gy = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
mag = cv2.magnitude(gx, gy) # 幅值;这才是「边有多强」那张热力图
坑一句:OpenCV 里若 dx=1, dy=1 同时开,那是混合二阶导数,不是上面这个幅值。要「边有多强」,用幅值,别混。
Sobel 像粗坯:边在哪大概看见了,但常常又粗又糊------真边在图上往往占好几像素宽,过渡带里幅值都偏高。要更干净的线,轮到 Canny。
Canny:四步精修(雕刻真正开始讲究)
Canny 比 Sobel 麻烦,但可以记成四步流水线:
① 降噪(高斯) → ② 强度梯度 → ③ 非极大抑制 → ④ 滞后阈值
① 降噪
用的就是上一节案板上的高斯模糊。噪点是小尖峰,不糊一下,梯度图会到处冒假边。
② 强度梯度
先把三个名字分清------后面 NMS 全靠它们:
| 名字 | 是什么 | 直觉 |
|---|---|---|
| 梯度(向量) | (Gx, Gy) |
往哪变最猛 + 有多猛 |
| 梯度幅值 | √(Gx² + Gy²) |
边有多强(和 Sobel 合成图一路货) |
| 梯度方向 | atan2(Gy, Gx) |
最陡的上坡方向 |
幅值大 → 像边;方向 → 坡往哪升,第③步要用 。
Canny ② 的幅值公式和 Sobel 一样;它多算方向,还往下做 ③④。
③ 非极大抑制(NMS)------只留脊线,不留山坡
问题:Sobel 热力图边又粗又糊。
办法:对每个像素,用自己的 Gx、Gy 算出方向,把角度归到最近的 0° / 45° / 90° / 135°,只跟「梯度方向上那一对邻居」比幅值------
- 当前 ≥ 两个邻居 → 保留(局部极大,坡顶)
- 否则 → 幅值置 0
人话:同一条边上,只留最陡那一线,胖边削成细线。
④ 滞后阈值(Hysteresis)------人类可拧的两颗旋钮
前面多半是算法算死的;这里你加两个阈值,决定「多严才算边」:
| 幅值 | 名称 | 怎么处理 |
|---|---|---|
| ≥ 高阈值 T_high | 强边 | 肯定保留 |
| T_low ~ T_high | 弱边 | 只有挨着强边才保留(连边) |
| < 低阈值 T_low | 太弱 | 直接丢掉 |
强边是主刀痕;弱边是不是跟着留下来,看它有没有搭上主刀痕------孤零零的毛刺,滚蛋。
雕完一圈,手里常常是一张边线图------中间结果 ,还不叫交卷。
后面轮廓把它收成封闭形状;分割、可视化、帮人描答案,都可能从这儿接着走。深度考生认 ImageNet?主路径照样可以不吃这一步。
3. 轮廓与形状:择菜,把主角抠出来
边散着,还不叫物件。轮廓这一步,其实像择菜:把可食用的主角抠出来,杂叶背景先搁一边。
经常听「背景」「前景」,我也是一下没反应过来。嘿嘿。
人话:前景 = 这一步你关心、要留下的;背景 = 不要的。谁算前景不是图自带的,是任务定的------红球、蓝天、手机外框,都可以是前景。
这回多半还是先变灰度,依旧先不看颜色。这边可以收一句:
找边缘、追轮廓时,常常用灰度更好分割------问的是亮暗结构,不是「多鲜艳」。
先有二值图,才有轮廓可追
findContours 吃的是二值图(白/黑)。二值从哪来,轮廓意味就不一样:
| 来源 | 二值图长什么样 | 轮廓意味 |
|---|---|---|
| threshold / Otsu | 物体整块白、背景黑 | 物体的 外边界(区域) |
| Canny | 边线是白、其余黑 | 边缘线(通常约 1 像素宽) |
Canny 那路的高低阈值,上一节滞后阈值讲过------人拧两颗旋钮。
threshold 那路的砍线,下一节形态学那边还会展开。
两条路不是「必须先阈再 Canny 再轮廓」,常是二选一或组合。
二值图质量很重要:糊成一片或碎成椒盐,后面 findContours / drawContours 都会跟着倒霉。
找轮廓、画轮廓
OpenCV 里主菜就两口:findContours 找,drawContours 画。
压缩方式差在「点留多少」:
| 模式 | 人话 |
|---|---|
CHAIN_APPROX_NONE |
边界上点几乎都留着,密 |
CHAIN_APPROX_SIMPLE |
直线段压成端点,点少一点,常用够了 |
还有一层容易忘:轮廓有层级。手机外框是一圈;摄像头开孔又是里面一圈------外轮廓套内轮廓,不是只有一条线。
轮廓收成封闭形状之后,可以量面积、周长、外接矩形,甚至粗分「这坨大概像啥」。对我来说常干两件事:
- B 路:形状本身就是要交的答案(或答案的一部分)
- C 路:帮标注员描一圈,人工再改
4. 阈值与形态学:少许调味,再下锅定型
阈值:一笔带过
前面边缘里的高低阈值、轮廓前的二值砍线,其实都碰过「阈值」这味调料了。这边不展开成专章------只要记住:它是常用的人工干预手法,按真实数据可以不断拧。
像老师傅嘴里的「少许」:菜谱写得含糊,干多了你自己会总结------这道菜大概几勺。
砍高了,主角缺一块;砍低了,背景糊进盘里。产出常常是 mask:哪里是目标,哪里不是。
形态学:油炸变大,炒制缩小
二值 mask 往往还不能直接上桌------毛刺、小洞、断边。形态学就是下锅定型:不断控制形状,最后才是要的样子。
这里不用小刀精雕(那是边缘的活)。膨胀、腐蚀、开、闭,更像整块食材在锅里胖瘦:
| 手法 | 锅感 | 图上大概在干啥 |
|---|---|---|
| 膨胀 dilation | 油炸------外边焦、体积胀大 | 白区往外扩,小洞容易补上,断边容易连上 |
| 腐蚀 erosion | 炒制收汁------缩水变紧 | 白区往里收,小毛刺容易烧掉 |
| 开运算 open | 先缩再胀(先腐蚀后膨胀) | 去小白点噪,外形大致还在 |
| 闭运算 close | 先胀再缩(先膨胀后腐蚀) | 补小黑洞,外形大致还在 |
开闭是组合拳,不是又两把小刀。分割任务里,阈值 + 形态学出场率极高:mask 从「勉强能看」变成「能交差」。
有时后面还接染色、量尺寸、数个数------但核心答案往往已经是那张 mask。
5. 海天小高潮:古典法直接出 mask = 答案
说个我自己灶台上干过的活------视频流里分天空、找海天线。
大致流水线:
css
视频帧
→ HSV 采点(再补蓝天点,扩大包络)
→ inRange 得 sky mask
→ 形态学修边
→ 按算法向上扫 → 海天线
→ 后处理:仅在 sky mask 内染成好天气的蓝
为啥用 HSV 不硬刚 RGB?蓝天在 RGB 里曝光一变就漂;HSV 里 H 相对稳住「是什么蓝」,更好画包围盒。
采点 + 补点 = 把「天」的颜色范围圈住;inRange 吐出的 mask 就是答案的骨架------不是拿去喂 AlexNet 当口粮。
两阶段别混:前面圈天空;最后染蓝是在已知 mask 里改颜色。
这就是 B 路:深度考生根本没上场。我自己是考生,古典灶台直接交卷。
完整走读见掘金前文:标题待填。
6. 和深度考生的关系
别误会:写完古法大餐,不是说深度考生从此失业。
灶台上其实常搭伙。我先用阈值搓一版粗糙 mask,当伪标签丢给分割模型练手------他负责往后抬精度,我负责先有个能吃的坯。同一道题,我古典交一版、他深度交一版,验收时两盘并排端,谁糊谁清楚。边线、阈值图也可以当质检灯:模型懵的时候,人眼先看见「他卡在哪道工序」。
但有一件事必须说死------
喂他家那位认一千类、走 ImageNet 那条主路时,滤波、边缘、轮廓不是必经配菜 。上篇那套标准简餐:几何切配 + ToTensor + Normalize,就够进门。你在 OpenCV 教程里学过的工艺,默认不要翻译成「深度学习预处理清单」;那是把大厨刀法,误认成简餐流水线上的必选步骤。
何时手搓、何时让模型打工------菜单权在饲养员,不在考生。
7. 收束
下篇厨房 = 对照班灶台 。
有时我们自己就是考生。
三条路再念一遍:备题面 / 古典交卷 / 辅助出答案 ------勿混。
核心不是考生有多神,是工程师选择何时手工、何时让模型打工。
正文三部讲清「模型打工时发生了什么」。
厨房上下篇把镜头扳回我:我为什么养他、我自己还能怎么做。
厨房上下篇到这儿收工。下一篇番外随缘更新------也许是模型家族聚会,也许是别的灶台。