海天线算法的前世今生

副标题:六点取色、海天线与前生今世------蓝天滤镜里我最磨人的一条线

摘要 :上篇 全流程速通 只交代 mask 结论;这篇细说原理 ------六点怎么取、取完怎么印中间步骤验收、magic number(ROI 0.65 / TOP_BAND)为什么存在、又怎么换成 bottom_up。无 GPU 见 第三篇

素材:同一片 Pexels 海岸固定机位 · 1280×720


0. 上篇留白的地方

上篇说过:A 轮锁主天,B′ 补云洞,合并成 mask。六点怎么点、海天线怎么定、ROI 怎么换------全在这儿。三篇里最磨人的也是这段。

灰蓝天海岸片里,海天交界管两件事:

  • 别把海抠进 mask------白沫、深蓝一进 mask,滤镜就染海。
  • 补云洞时别用 magic 比例------「画面上半屏 47%」不是海平线,换构图就废。

先看 A 轮 mask 叠上去长什么样:

有淡黄色一层的是天,剩下的是海和岩石。验收就一句:海天交界贴不贴眼 。后面所有迭代,都是为这条线服务------但线能不能准,先得六点取色,跑起来还要印中间步骤


1. 六点取色:A 轮锁主天,故意不用 cloud

A 轮的 job 是锁主天盘 + 海天分界 ,在 frame_raw.jpg 上点六个采样色,算出 hsv_threshold_a.json

原计划取色表里有 cloud。我没把 sky_cloud 放进六点------云 H 离群、偏黄,merge 进 A 的 JSON 会把岩石/岛色相拉进区间,海天变脏。取色网站上 cloud 点一点 merge,区间就背叛你。

A 锁的是盘,不是认云;云和主天色域分叉,不能为顶云把 H 拉穿。云洞交给后面的 B′,这是第一次偏离 steps 字面流程,但符合设计。


2. 取色别翻车:换算、坐标、脑子里要有数

每个点其实要在心里有一套标准。可以用取色网站辅助,填 JSON 前先把 HSV 口径对齐

网站 HSV ↔ OpenCV HSV

通道 OpenCV(脚本用的) 网站常见写法 怎么换算
H 0~179(色环 ÷2) 0~360° H_opencv = H_网站 ÷ 2
S 0~255 0~100% S_opencv = S% × 255 ÷ 100
V 0~255 0~100% V_opencv = V% × 255 ÷ 100

网站 S/V 若已是 0~255,直接用。最稳还是在 frame_raw.jpg 1280×720 上用 OpenCV 读像素------和 run_mask_hsv_a.py 一致,别跟 1080 预览坐标混了。

六个点管哪块

管哪块
sky_top_L / sky_top_R 左上角、右上角(光照不均,最容易漏)
sky_mid 中间大块天
sky_blue 偏下一点、相对「更蓝」的一带
sky_gray 云缝 / 灰蒙区(最灰的那块天)
sky_near_horizon 海天交界 正上方(锁分界线)

取点规则(踩坑版)

规则 为什么
必须在 1280×720 原帧上点 1080 预览坐标整体偏,mask 全歪
y 要在「天」里:顶 ~y<200,海天 ~y≈300~360 仍可能是天 点太低就是海
x 顶角要真的在角上 本片 sky_top_L x≈86,sky_top_R x≈1030
避云边、避岛、避码头 边缘像素 H/S 混合,拉区间会脏
sky_near_horizon海平线正上方最后一截天 锁边界;太低吃进海,太高锁不住
右侧近海 不能当 sky_gray xy 对但语义不对------其实是海/反光
太近又太像 不加第 7 点 不撑 min/max、不占新角落

灰蓝天(本片这类)H / S / V 怎么想

H(色相)· OpenCV 0~179

心里预期 本片六点实测
天 ≈ 100~115(青蓝,不是绿、不是黄) 全部 102~105,紧得离谱

不对的信号:H < 95 且点在「像天」的位置 → 多半云边偏黄、反光或岩石;H > 120 少见纯天;网站 0~360 忘了 ÷2 整表废。所以有 CLAMP_H_LOWER = 92------merge 把 H 拉脏时,也不让区间吃到偏黄的 H。

S(饱和度)· 灰天关键轴

心里预期 本片
灰天 S 很低 :大概 10~50 最低 12(海天),最高 45(mid)
真「蓝天」尖儿可以更高 后来 (668,78) S≈91,upper 不够 → 补 sky_blue2

不对的信号:S > 60~70 还说是天 → 多半植被、饱和礁石;S 极低 + V 极高且在云心 → 那是云,交给 B′;sky_gray 要点云缝里仍算「天」的地方,不是随便灰一块。

V(明度)

心里预期 本片
偏亮 :大概 V ≥ 140 六点 186~196

不对的信号:V < 120 → 云影、阴面、深水;海天附近可以 S 很低但 V 仍高sky_near_horizon 就是这种)。

灰天靠 S 宽、V 够亮;H 只信窄带,且用 clamp 保底;点要散在天的不同位置,绝不点云心、海面、岩石。

六点只是起点。真正磨人的,是跑起来之后才发现漏了哪块天、海从哪儿渗进来------下面按我实际踩坑的顺序讲。


3. 取点会翻车:印中间步骤才算数

六点是在 frame_raw.jpg 一张图上 点的。这片海岸固定机位,我赌它代表性强;但赌归赌,点完不等于完工

跑完 A 轮 overlay,顶角漏天、海天吃进海、饱和蓝天抠成洞------全是正常翻车。本片后来还补过点(比如 (668,78)sky_blue2,upper S 不够宽)。只信第一次 merge 出来的 JSON,一定会栽。

我摸索出来的验收法很土,但管用:

  1. 把 A、B 各阶段的中间图打出来 ------inRange 后、ROI 后、morph 后、B′ allow 带、合并前,能存啥存啥。
  2. 叠 overlay 看「还缺哪块天」------缺的是 A 的色域问题,还是云洞没补上。
  3. 分清 A 和 B′ 的锅 :A 锁主天盘;B′ 只补云洞,不补天。低饱和云吃不进 A 的方盒,才交给 B′ 在低 S 规则里长洞。把 B′ 当「再抠一轮天」,海天一定脏。

sky_near_horizon 帮你锁分界,但六点取对了,后面还有 ROI、A″、B′ 围着海天线磨------不印中间步骤,根本不知道卡在哪一道。


4. 第一次海天线:写死的比例(magic number)

没有「海天」这根线之前,海也会进 mask ------白沫、深蓝和天色相重叠,inRange 全图扫一遍,下半片海面会被当成天。

A 轮:ROI 0.65------「下面默认是海」

最早的做法是 ROI :从某条水平线往下,默认当海、不当天 ;主要在上半截里信 mask。

旧方案就是 上面 65%,下面 35% 整片清零 。读代码时注意顺序:inRange 其实先扫了全图 ,ROI 是扫完再砍一刀------不是「只在上半张图里 inRange」。

这不是拍脑袋:我对着很多帧的中间 mask 看过,0.65 在这片视频里能跑通 ------海面误检控得住,真天也没大面积被砍。但它仍是 magic number:海天更高的构图、海占画面更少的时候,0.65 会先误伤真天,后面精裁也救不回来(ROI 清零的像素补不回去)。

B′ 轮:TOP_BAND------画面比例也不是海平线

A 吃不进低饱和云,B′ 用低 S 规则补洞。一开始也想偷懒:只在上半屏 跑,比如 TOP_BAND × h,只让 y < 0.47h 参与。

本片 0.47 还能凑合;拧到 0.60 又开始扫海面;换一支海天更高的视频,整个带就歪了。画面高比例 ≠ 海平线------跟 ROI 0.65 是同一类病:写死比例能兜本片,换构图就废。


5. 换掉 magic number:从 mask 里读线

2026-06-24 半夜想通的:别跟画面百分比较劲,跟 mask 自己说话

ROI:0.65 → bottom_up

看很多张中间 mask:底下仍以黑(海)居多,往上才进天。左 / 中 / 右各取几列,从底向上扫 ,连续几行填充够密,就算进天区------得到动态 y_cap,替换写死的 0.65。读失败才回 0.65 兜底。

所以:旧 ROI = 写死的「下面当海」;新 ROI = 从 mask 估出来的「下面当海」------心里那个模型没变,只是线不再 magic。

红:固定比例 y≈468 · 绿:bottom_up y≈359(贴海天)

本片实测两张 overlay 叠上去肉眼差别不大------看线比看 overlay 清楚。换构图时,固定 0.65 会先砍真天,那时候才分得开。

B′:跟 mask_a 的海岸线走

B′ 不再用 TOP_BAND,改成跟 mask_a 自己的天际线------按列扫白色结束行,低 S 云洞只长在 allow 带里,再和 A 合并。

A″:海面误检,B′ 救不了

换掉 ROI 之后,还有一类问题:海面已经进 A 了。B′ 只能管洞长在哪儿,retro-fix 不了 A 里的误检。

inRange 偶发把白沫、深蓝吃进来。做法是 morph 之后再 按列 cap 一刀 ------从上往下扫中央带,连续几行天像素很少就算过海天;再按列找每列 mask 最靠下的天行,cap 以下砍掉。这一步叫 A″ ,和 B′ 分工:A″ 修 A 里的海面,B′ 补云洞

完整顺序(读 run_mask_hsv_a.py 用)

text 复制代码
① inRange        六点 JSON → 色域方盒,第一次「像天的颜色」
② ROI            粗门帘:y_cap 以下不信(bottom_up 或 0.65 兜底)
③ morph          形态学修边修洞
④ A′ exclude     可选手抠矩形扣岛/码头
⑤ A″             精修刀:按 mask 形状削海面误检
→ mask_hsv_a.png
     ↓
B′ 低 S 补云洞(跟 mask_a 海天线,不补天)→ 合并

调试时最容易搅混 ROI 和 A″ :都带「海天」,时机完全不同------ROI 在 morph 粗挡,A″ 在 morph 精裁。0.65 是这片能跑的 ROI 兜底,不是 A″ 本身。


6. 迭代时间线

text 复制代码
六点取色(一张图,会补点)→ 印中间步骤验收
A 轮:ROI 0.65(magic)→ bottom_up(读 mask)
B′:TOP_BAND(magic)→ 跟 mask_a 海天线
A″:morph 后裁海面误检(一直有)
日期 我干了啥
06-20 六点不用 cloud · B′ 跟 mask_a · 毙 TOP_BAND
06-21 A″ 海天裁切
06-24 bottom_up 替换 ROI 0.65

方案不必一次成熟。0.65 和 TOP_BAND 都是本片能跑的兜底;一步步从 mask 里读出规律换掉 magic number,比假装一开始完美更真实。


7. 系列导航

内容
上篇 · 甲方吐槽天不够蓝 全流程速通
本篇 六点取色 + 海天线
下篇 · 甲方设备过烂 无 GPU 快速出效果

8. 收尾

三篇里,这篇最磨人------六点怎么点才不算海、取完还要印中间步骤、magic number 为什么存在、ROI 和 A″ 别搅混、梦里 bottom_up 落地,来来回回看 overlay 和画线。

每次做完项目我都想写一篇文章------记录当时的方案,也是记录当时的自己。参数和脚本可以进仓库,但「B′ 只补云不补天」「为什么 0.65 是兜底不是算法」「ROI 清零的像素 A″ 救不回」------这些判断不写下来,过几个月就只剩一张 mask,想不起当时怎么想的。

就像许愿变成 Ice King 的 Simon:愿望成真,也失去了自己。我不想只留能跑的 mask,忘了为什么从 magic number 走到读 mask 规律。

相关推荐
韩师傅1 小时前
当你的甲方设备过烂,要如何快速出效果?
python·计算机视觉
Warson_L1 小时前
LangGraph的MessageState and HumanMessage
python
韩师傅2 小时前
当你的甲方吐槽天空不够蓝,你应该如何应对
python·计算机视觉
Warson_L2 小时前
python的类&继承
python
Warson_L2 小时前
类型标注/type annotation
python
ThreeS5 小时前
手搓MiniVLA全实战教程-一步一步用pytorch解释原理与思路
人工智能·python
金銀銅鐵6 小时前
[Python] 模 n 乘法的逆元计算器
python·数学·游戏
aqi007 小时前
15天学会AI应用开发(十)把文本嵌入模型换成国产模型
人工智能·python·ai编程