如何通过组合CIFilters在图像编辑APP中显示高光与阴影裁剪?
用CIFilter组合实现高光与阴影裁剪的可行思路
嘿,我刚好在iOS/macOS图像编辑场景里折腾过类似需求,确实没有现成的单一CIFilter能一步到位实现高光与阴影裁剪,但通过几个滤镜的组合完全能搞定!下面是我验证过的两种可行方案,你可以根据自己的需求选:
方案一:蒙版分离法(精准控制,保留色彩)
这种方法会先提取高光/阴影区域的蒙版,再把超出阈值的区域替换成固定亮度值,能精准保留原图像的色彩细节:
步骤1:转线性颜色空间(可选但更准确)
CIFilter的很多颜色操作在线性空间下计算更精准,先把sRGB图像转成线性空间:
let linearImage = originalImage.applyingFilter("CISRGBToneCurveToLinear")
步骤2:生成高光/阴影蒙版
通过颜色矩阵提取亮度通道,再用阈值滤镜生成蒙版:
// 生成高光蒙版:亮度>0.8的区域为白色(需要裁剪的部分) let highlightMask = linearImage .applyingFilter("CIColorMatrix", parameters: [ "inputRVector": CIVector(x: 0.2126, y: 0.7152, z: 0.0722, w: 0), "inputGVector": CIVector(x: 0.2126, y: 0.7152, z: 0.0722, w: 0), "inputBVector": CIVector(x: 0.2126, y: 0.7152, z: 0.0722, w: 0), "inputAVector": CIVector(x: 0, y: 0, z: 0, w: 1) ]) .applyingFilter("CIThreshold", parameters: ["inputThreshold": 0.8]) // 生成阴影蒙版:亮度<0.2的区域为白色(需要裁剪的部分) let shadowMask = linearImage .applyingFilter("CIColorMatrix", parameters: [ "inputRVector": CIVector(x: 0.2126, y: 0.7152, z: 0.0722, w: 0), "inputGVector": CIVector(x: 0.2126, y: 0.7152, z: 0.0722, w: 0), "inputBVector": CIVector(x: 0.2126, y: 0.7152, z: 0.0722, w: 0), "inputAVector": CIVector(x: 0, y: 0, z: 0, w: 1) ]) .applyingFilter("CIThreshold", parameters: ["inputThreshold": 0.2]) .applyingFilter("CIColorInvert") // 反转后阴影区域为白色
步骤3:裁剪高光与阴影
用蒙版混合滤镜,把需要裁剪的区域替换成固定亮度:
// 裁剪高光:将高光区域替换为线性空间下的0.8亮度 let clippedHighlights = linearImage.applyingFilter("CIBlendWithMask", parameters: [ "inputBackgroundImage": CIImage(color: CIColor(red: 0.8, green: 0.8, blue: 0.8)), "inputMaskImage": highlightMask ]) // 裁剪阴影:将阴影区域替换为线性空间下的0.2亮度 let clippedImage = clippedHighlights.applyingFilter("CIBlendWithMask", parameters: [ "inputBackgroundImage": CIImage(color: CIColor(red: 0.2, green: 0.2, blue: 0.2)), "inputMaskImage": shadowMask ])
步骤4:转回sRGB颜色空间
最后把线性图像转回到显示用的sRGB空间:
let finalImage = clippedImage.applyingFilter("CILinearToSRGBToneCurve")
方案二:自定义色调曲线法(简洁高效)
如果不需要极致精准的蒙版控制,直接用CIToneCurve自定义曲线,把高光和阴影的亮度范围截断,步骤更简单:
let toneCurveFilter = CIFilter(name: "CIToneCurve")! toneCurveFilter.setValue(originalImage, forKey: kCIInputImageKey) // 自定义曲线:亮度<0.2的部分固定为0.2,>0.8的部分固定为0.8,中间线性过渡 toneCurveFilter.setValue(CIVector(x: 0, y: 0.2), forKey: "inputPoint0") toneCurveFilter.setValue(CIVector(x: 1/3, y: 0.2 + (0.8-0.2)*(1/3)), forKey: "inputPoint1") toneCurveFilter.setValue(CIVector(x: 2/3, y: 0.2 + (0.8-0.2)*(2/3)), forKey: "inputPoint2") toneCurveFilter.setValue(CIVector(x: 1, y: 0.8), forKey: "inputPoint3") let finalImage = toneCurveFilter.outputImage!
注意事项
- 阈值参数(比如0.2、0.8)可以根据你的需求灵活调整,数值越接近0/1,裁剪的范围越小
- 如果你需要保留原图像的色彩质感,优先选蒙版分离法
- 可以把多个滤镜组合成
CIFilterGroup,提升渲染性能
内容的提问来源于stack exchange,提问作者Gizmodo
相关产品推荐
相关产品推荐

