【图像处理】饱和度——颜色的浓淡与灰度化
饱和度为 0图像变成灰色。饱和度为 1颜色恢复原样。看似简单的一个滑块背后是颜色空间的混合运算——而直接灰度化并不总是最好的选择。一、饱和度的直觉在 HSB或 HSL色彩模型里颜色由三个维度描述HHue色相 → 颜色的种类红/绿/蓝/黄… SSaturation饱和度→ 颜色的浓淡鲜艳 ↔ 灰白 BBrightness亮度 → 颜色的明暗亮 ↔ 暗调整饱和度就是在原始颜色和同亮度的灰色之间做插值饱和度 1.0鲜艳的红色 (255, 0, 0) 饱和度 0.5粉灰色 (191, 128, 128) ← 向灰色靠近 50% 饱和度 0.0纯灰色 (128, 128, 128) ← 完全变成灰色二、数学灰度化 插值CIColorControls的inputSaturation本质上是两步Step 1计算该像素对应的灰度值gray 0.299 × R 0.587 × G 0.114 × B这是 Rec.601 亮度公式Day 3 详细讲过权重来自人眼对红绿蓝的感知灵敏度差异。Step 2在原色和灰度之间线性插值R gray × (1 - s) R × s G gray × (1 - s) G × s B gray × (1 - s) B × s 其中 s inputSaturation0.0 ~ 1.0验证s 1.0输出 原始 RGB完全保留颜色s 0.0输出 (gray, gray, gray)完全灰度化s 0.5输出 原始和灰度各占一半具体计算示例鲜红色(255, 0, 0)计算s 0.3gray 0.299 × 255 0.587 × 0 0.114 × 0 76.2 ≈ 76 R 76 × 0.7 255 × 0.3 53.2 76.5 129.7 ≈ 130 G 76 × 0.7 0 × 0.3 53.2 0.0 53.2 ≈ 53 B 76 × 0.7 0 × 0.3 53.2 0.0 53.2 ≈ 53 s0.3 后的颜色(130, 53, 53) ← 带点红色调的暗灰三、完全灰度化s0.0并不总是最好的初看之下OCR 场景应该直接灰度化数字本身是无色的颜色只是干扰。但实测有两个副作用3.1 Vision 丢失颜色线索Vision OCR 是多模态模型训练时见过大量彩色图像。颜色是它区分相似形状的辅助信号之一例深蓝背景上的白色 LOGO 文字 彩色图深蓝(30, 80, 180) vs 白色(255, 255, 255) → 颜色对比强Vision 轻松分割 灰度图亮度 ≈ 0.25 vs 亮度 ≈ 1.0 → 亮度对比也够没问题 例金色数字在米白色背景上 彩色图金色(255, 215, 0) vs 米白(255, 250, 220) → 颜色略有差异Vision 可感知 灰度图金色亮度 ≈ 0.81 vs 米白亮度 ≈ 0.97 → 亮度差仅 0.16对比度很弱结论当颜色对比强于亮度对比时降饱和会损失有效信息。3.2 特定颜色组合在灰度下消失问题场景橙色数字在青色背景上 橙色 RGB(255, 140, 0) gray 0.299×255 0.587×140 0.114×0 76.2 82.2 158 青色 RGB(0, 200, 200) gray 0.299×0 0.587×200 0.114×200 0 117.4 22.8 140 灰度对比度 |158 - 140| / 255 ≈ 7% ← 几乎看不见 但彩色对比度色相完全不同非常鲜明这就是为什么银行卡预处理不能无脑灰度化不同颜色组合需要不同策略。四、各场景的 saturation 策略s 1.0 完全保留颜色 适用彩色背景对 OCR 有益或颜色是区分字符的关键线索 例深色底变体不降饱和颜色通道提供额外边缘信息 s 0.7 轻度降饱和 适用颜色有一定干扰但不希望完全失去颜色线索 例全卡自适应预处理的基线值正常/低对比度场景 s 0.3 保留少量颜色 适用背景颜色复杂但担心某些卡面的颜色对比消失 例ROI 浅底提亮灰变体在大 radius USM 之后颜色已大幅弱化保留 30% 兜底 s 0.0 完全灰度化 适用已确认亮度对比足够强颜色只是噪声 例ROI 背景减除变体大 radius USM 已经消除低频背景颜色无意义 ROI 超对比灰变体高 contrast 专门压暗处理颜色干扰大于收益五、saturation 与 contrast 的联动降低饱和度会改变有效亮度对比度两者需要协调。5.1 降饱和降低了可用对比度原始颜色对比 橙色 (255,140,0) 亮度 0.62 青色 (0,200,200) 亮度 0.55 颜色对比很明显亮度对比只有 0.07 s0.0 灰度化后 橙色 灰度 0.62 青色 灰度 0.55 仅靠亮度对比度只有 7%需要 contrast 补偿 contrast 需要多高理论上需要 1/0.07 ≈ 14 才能将对比拉至全范围 实际上 contrast 最多用到 2.0因此这种组合用灰度化会失败5.2 contrast 依赖饱和度处理后的亮度分布推荐处理顺序 CIColorControls(saturation → contrast → brightness) 这三个参数在同一个 CIFilter 里同时生效内部顺序由 Core Image 决定。 实际等效于先降饱和得到亮度均等的图再拉伸亮度分布。5.3 选择 saturation 值的决策树开始 │ ├─ 背景是否为复杂图案风景/纹理/渐变 │ ├─ 是 → 用 CIUnsharpMask 大 radius25px消背景 → s0.0 灰度化 │ └─ 否 ↓ │ ├─ 数字颜色是否与背景亮度接近亮度差 0.2 │ ├─ 是 → 颜色对比是主要线索 → s0.7~1.0同时提高 contrast │ └─ 否 ↓ │ ├─ 背景颜色是否鲜艳高饱和 │ ├─ 是 → 颜色干扰 Vision → s0.0~0.3 降饱和 │ └─ 否 ↓ │ └─ 默认s0.7保留颜色线索轻度降低颜色干扰六、Swift 实现手动实现适合 MLBitmappublicstructSaturationFilter:ImageFilter{/// 饱和度系数0.0 完全灰度化1.0 原色publicletsaturation:Floatpublicfuncapply(to bitmap:MLBitmap)-MLBitmap{varresultbitmapforiinstride(from:0,to:result.pixels.count,by:4){letrFloat(result.pixels[i])letgFloat(result.pixels[i1])letbFloat(result.pixels[i2])// Rec.601 亮度letgray0.299*r0.587*g0.114*b// 在原色和灰度之间插值result.pixels[i]UInt8(clamping:Int(gray*(1-saturation)r*saturation))result.pixels[i1]UInt8(clamping:Int(gray*(1-saturation)g*saturation))result.pixels[i2]UInt8(clamping:Int(gray*(1-saturation)b*saturation))// i 3 Alpha不变}returnresult}}通过 CIColorControls适合 CIImage 管线funcadjustSaturation(_cgImage:CGImage,saturation:Float)-CGImage?{letinputCIImage(cgImage:cgImage)guardletfilterCIFilter(name:CIColorControls)else{returnnil}filter.setValue(input,forKey:kCIInputImageKey)filter.setValue(saturation,forKey:kCIInputSaturationKey)// contrast / brightness 不传时保持默认1.0 / 0.0returnfilter.outputImage.flatMap{context.createCGImage($0,from:$0.extent)}}两种方式等效CIColorControls 在 GPU 上运行大图时更快。七、降饱和不等于灰度化的证明之前讲的灰度化gray 0.299R 0.587G 0.114B 输出像素 (gray, gray, gray)CIColorControls(saturation0.0)的结果R gray × 1 R × 0 gray G gray × 1 G × 0 gray B gray × 1 B × 0 gray 输出像素 (gray, gray, gray)两者数学上完全等价都是 Rec.601 加权灰度化。区别在于saturation0.0是完整 CIColorControls 管线的一个参数可以同时调整 contrast/brightness手动灰度化Day 3 的方法是独立的 CPU 操作更灵活但更慢八、小结saturations的本质 s1.0 → 原色 s0.0 → Rec.601 加权灰度 中间值 → 两者线性插值 何时降饱和 ✅ 背景颜色是噪声复杂图案、鲜艳彩色背景 ✅ 已用 CIUnsharpMask 消除背景颜色不再有意义 ✅ 处于超对比模式需要纯亮度信息 何时保留颜色s0.5~1.0 ✅ 数字颜色与背景亮度接近颜色对比 亮度对比 ✅ Vision 需要颜色线索区分相似字形 ✅ 保底措施不确定时先用 s0.7再根据识别结果调整如果这篇对你有一点启发点个赞让更多人少踩一个坑 转发给那个正在纠结的人也欢迎关注我—— 我们一起把认知变成长期复利。往期推荐一图了解饱和度控制色彩鲜艳程度的关键一图了解OCR的处理流程及相关图像处理技术一图了解二值化与阈值从灰度到黑白的决策一张图了解图像处理中的亮度、对比度与实现颜色科学与灰度化从图片到内存——你真正理解图像处理的第一天iPhone相册背后的图像处理知识下iPhone相册背后的图像处理知识中iPhone相册背后的图像处理知识上一张图了解图像处理的本质图像到底是什么图像处理技术概要图AI时代软件工程师必备概念全景图