本案例的目的是理解如何用Metal實現基於色溫調整白平衡效果濾鏡,主要就是消除或減輕日光下偏藍和白熾燈下偏黃,簡單講把應該是白色的調成白色或接近白色,不使其嚴重偏色;
Demo
實操代碼
// 白平衡濾鏡
let filter = C7WhiteBalance.init(temperature: 4000, tint: -200)
// 方案1:
ImageView.image = try? BoxxIO(element: originImage, filters: [filter, filter2, filter3]).output()
// 方案2:
ImageView.image = originImage.filtering(filter, filter2, filter3)
// 方案3:
ImageView.image = originImage ->> filter ->> filter2 ->> filter3
效果對比圖
- 不同參數下效果
temperature: 4000, tint: -200 | temperature: 4000, tint: 0 | temperature: 4000, tint: 200 |
---|---|---|
temperature: 7000, tint: -200 | temperature: 7000, tint: 0 | temperature: 7000, tint: 100 |
實現原理
- 過濾器
這款濾鏡採用並行計算編碼器設計.compute(kernel: "C7WhiteBalance")
,參數因子[temperature < 5000 ? 0.0004 * (temperature - 5000) : 0.00006 * (temperature - 5000), tint / 100]
;
對外開放參數
-
temperature
: 調整圖像的溫度,4000的值非常涼爽,7000非常溫暖; -
tint
: 調整圖像的色調。-200的值是非常綠色的,200是非常粉紅色的;
/// 白平衡
public struct C7WhiteBalance: C7FilterProtocol {
public static let range: ParameterRange<Float, Self> = .init(min: 4000, max: 7000, value: 5000)
/// The tint to adjust the image by. A value of -200 is very green and 200 is very pink.
public var tint: Float = 0
/// The temperature to adjust the image by, in ºK. A value of 4000 is very cool and 7000 very warm.
/// Note that the scale between 4000 and 5000 is nearly as visually significant as that between 5000 and 7000.
public var temperature: Float = range.value
public var modifier: Modifier {
return .compute(kernel: "C7WhiteBalance")
}
public var factors: [Float] {
return [
temperature < 5000 ? 0.0004 * (temperature - 5000) : 0.00006 * (temperature - 5000),
tint / 100
]
}
public init(temperature: Float = range.value, tint: Float = 0) {
self.temperature = temperature
self.tint = tint
}
}
- 着色器
將圖像從RGB空間轉換到YIQ空間的rgb值,控制藍色值處於(-0.5226...0.5226)區間之間,再將YIQ空間顏色轉換爲RGB空間顏色rgb,對比暖色常量warm獲取到新的rgb,最後混色原色和新的rgb和圖像色溫得到最終的像素rgb;
kernel void C7WhiteBalance(texture2d<half, access::write> outputTexture [[texture(0)]],
texture2d<half, access::read> inputTexture [[texture(1)]],
constant float *temperature [[buffer(0)]],
constant float *tint [[buffer(1)]],
uint2 grid [[thread_position_in_grid]]) {
const half4 inColor = inputTexture.read(grid);
const half3x3 RGBtoYIQ = half3x3({0.299, 0.587, 0.114}, {0.596, -0.274, -0.322}, {0.212, -0.523, 0.311});
const half3x3 YIQtoRGB = half3x3({1.000, 0.956, 0.621}, {1.000, -0.272, -0.647}, {1.000, -1.105, 1.702});
half3 yiq = RGBtoYIQ * inColor.rgb;
yiq.b = clamp(yiq.b + half(*tint) * 0.5226 * 0.1, -0.5226, 0.5226);
const half3 rgb = YIQtoRGB * yiq;
const half3 warm = half3(0.93, 0.54, 0.0);
const half r = rgb.r < 0.5 ? (2.0 * rgb.r * warm.r) : (1.0 - 2.0 * (1.0 - rgb.r) * (1.0 - warm.r));
const half g = rgb.g < 0.5 ? (2.0 * rgb.g * warm.g) : (1.0 - 2.0 * (1.0 - rgb.g) * (1.0 - warm.g));
const half b = rgb.b < 0.5 ? (2.0 * rgb.b * warm.b) : (1.0 - 2.0 * (1.0 - rgb.b) * (1.0 - warm.b));
const half4 outColor = half4(mix(rgb, half3(r, g, b), half(*temperature)), inColor.a);
outputTexture.write(outColor, grid);
}
色溫曲線
- 相同的物體在不同的色溫光源下呈現出相同的顏色,這就稱爲色彩恆常性;
- 白平衡的目的就是使得圖像傳感器在不同色溫光源下拍攝的物體進行一個圖像糾正的過程,還原物體本來的顏色。
- 也可以說是在任意色溫條件下,圖像傳感器所拍攝的標準白色經過白平衡的調整,使之成像後仍然爲白色。
先看看在不同光源下呈現出來的顏色如下圖:
流程原理:
- 在各個色溫下(2500~7500)拍幾張白紙照片,假設拍6張(2500,3500...7500),可以稱作色溫照;
- 把色溫照進行矯正,具體是對R/G/B通道進行轎正,讓偏色的白紙照變成白色,並記錄各個通道的矯正參數;
- 判斷圖像的色溫,是在白天,晚上,室內,室外,是烈日還是夕陽,還是在陽光下的沙灘上,或者是在臥室裏”暖味”的牀頭燈下;
Harbeth功能清單
- 支持ios系統和macOS系統
- 支持運算符函數式操作
- 支持多種模式數據源 UIImage, CIImage, CGImage, CMSampleBuffer, CVPixelBuffer.
- 支持快速設計濾鏡
- 支持合併多種濾鏡效果
- 支持輸出源的快速擴展
- 支持相機採集特效
- 支持視頻添加濾鏡特效
- 支持矩陣卷積
- 支持使用系統 MetalPerformanceShaders.
- 支持兼容 CoreImage.
- 濾鏡部分大致分爲以下幾個模塊:
最後
- 慢慢再補充其他相關濾鏡,喜歡就給我點個星🌟吧。
-
濾鏡Demo地址,目前包含
100+
種濾鏡,同時也支持CoreImage混合使用。 - 再附上一個開發加速庫KJCategoriesDemo地址
- 再附上一個網絡基礎庫RxNetworksDemo地址
- 喜歡的老闆們可以點個星🌟,謝謝各位老闆!!!
✌️.