开启成长之旅!这是我参加「日新方案 12 月更文应战」的第27天,点击检查活动详情
本案例的目的是了解如何用Metal完成色彩空间转化作用滤镜,转化在不同色彩空间生成的图画;
Demo
- HarbethDemo地址
- iDay每日共享文档地址
实操代码
// 色彩空间转化滤镜
let filter = C7ColorSpace.init(with: .rgb_to_yuv)
// 方案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
作用对比图
- 不同参数下作用
rgb_to_yiq | yiq_to_rgb | rgb_to_yuv |
---|---|---|
完成原理
- 过滤器
这款滤镜采用并行计算编码器设计.compute(kernel: type.rawValue)
;
/// 色彩空间转化
public struct C7ColorSpace: C7FilterProtocol {
public enum SwapType: String, CaseIterable {
case rgb_to_yiq = "C7ColorSpaceRGB2YIQ"
case yiq_to_rgb = "C7ColorSpaceYIQ2RGB"
case rgb_to_yuv = "C7ColorSpaceRGB2YUV"
case yuv_to_rgb = "C7ColorSpaceYUV2RGB"
}
private let type: SwapType
public var modifier: Modifier {
return .compute(kernel: type.rawValue)
}
public init(with type: SwapType) {
self.type = type
}
}
- 着色器
每条通道乘以各自偏移求和得到Y,用Y作为新的像素rgb;
kernel void C7ColorSpaceRGB2Y(texture2d<half, access::write> outputTexture [[texture(0)]],
texture2d<half, access::read> inputTexture [[texture(1)]],
uint2 grid [[thread_position_in_grid]]) {
const half4 inColor = inputTexture.read(grid);
const half Y = half((0.299 * inColor.r) + (0.587 * inColor.g) + (0.114 * inColor.b));
const half4 outColor = half4(Y, Y, Y, inColor.a);
outputTexture.write(outColor, grid);
}
// See: https://en.wikipedia.org/wiki/YIQ
kernel void C7ColorSpaceRGB2YIQ(texture2d<half, access::write> outputTexture [[texture(0)]],
texture2d<half, access::read> inputTexture [[texture(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 half3 yiq = RGBtoYIQ * inColor.rgb;
const half4 outColor = half4(yiq, inColor.a);
outputTexture.write(outColor, grid);
}
kernel void C7ColorSpaceYIQ2RGB(texture2d<half, access::write> outputTexture [[texture(0)]],
texture2d<half, access::read> inputTexture [[texture(1)]],
uint2 grid [[thread_position_in_grid]]) {
const half4 inColor = inputTexture.read(grid);
const half3x3 YIQtoRGB = half3x3({1.0, 0.956, 0.621}, {1.0, -0.272, -0.647}, {1.0, -1.105, 1.702});
const half3 rgb = YIQtoRGB * inColor.rgb;
const half4 outColor = half4(rgb, inColor.a);
outputTexture.write(outColor, grid);
}
// See: https://en.wikipedia.org/wiki/YUV
kernel void C7ColorSpaceRGB2YUV(texture2d<half, access::write> outputTexture [[texture(0)]],
texture2d<half, access::read> inputTexture [[texture(1)]],
uint2 grid [[thread_position_in_grid]]) {
const half4 inColor = inputTexture.read(grid);
const half3x3 RGBtoYUV = half3x3({0.299, 0.587, 0.114}, {-0.299, -0.587, 0.886}, {0.701, -0.587, -0.114});
const half3 yuv = RGBtoYUV * inColor.rgb;
const half4 outColor = half4(yuv, inColor.a);
outputTexture.write(outColor, grid);
}
kernel void C7ColorSpaceYUV2RGB(texture2d<half, access::write> outputTexture [[texture(0)]],
texture2d<half, access::read> inputTexture [[texture(1)]],
uint2 grid [[thread_position_in_grid]]) {
const half4 inColor = inputTexture.read(grid);
const half3x3 YUVtoRGB = half3x3({1.0, 0.0, 1.28033}, {1.0, -0.21482, -0.38059}, {1.0, 2.21798, 0.0});
const half3 rgb = YUVtoRGB * inColor.rgb;
const half4 outColor = half4(rgb, inColor.a);
outputTexture.write(outColor, grid);
}
色彩空间
- YIQ
在YIQ体系中,是NTSC(National Television Standards Committee)电视体系规范;
- Y是供给是非电视及彩色电视的亮度信号Luminance,即亮度Brightness;
- I代表In-phase,色彩从橙色到青色;
- Q代表Quadrature-phase,色彩从紫色到黄绿色;
转化公式如下:
- YUV
YUV是在工程师想要在是非基础设施中运用彩色电视时发明的。他们需要一种信号传输办法,既能与是非 (B&W) 电视兼容,又能添加色彩。亮度重量现已作为是非信号存在;他们将紫外线信号作为解决方案添加到其中。
由于 U 和 V 是色差信号,因此在直接 R 和 B 信号上挑选色度的 UV 表明。换句话说,U 和 V 信号告知电视在不改动亮度的情况下改动某个点的色彩。
或许 U 和 V 信号告知显示器以献身另一种色彩为代价使一种色彩更亮,以及它应该移动多少。
U 和 V 值越高(或负值越低),斑点的饱和度(色彩)就越高。
U 值和 V 值越接近零,色彩偏移越小,这意味着红、绿和蓝光的亮度会更均匀,然后产生更灰的点。
这是运用色差信号的优点,即不是告知色彩有多少红色,而是告知红色比绿色或蓝色多多少。
反过来,这意味着当 U 和 V 信号为零或不存在时,它只会显示灰度图画。
假如运用 R 和 B,即使在是非场景中,它们也将具有非零值,需要一切三个数据承载信号。
这在前期的彩色电视中很重要,因为旧的是非电视信号没有 U 和 V 信号,这意味着彩色电视开箱后只会显示为是非电视。
此外,是非接收器能够接收 Y' 信号并疏忽 U 和 V 色彩信号,使 YUV 向后兼容一切现有的是非设备、输入和输出。
假如彩色电视规范不运用色差信号,这或许意味着彩色电视会从 B& 中产生风趣的色彩 W 播送,否则需要额定的电路将是非信号转化为彩色。
有必要为色度通道分配较窄的带宽,因为没有可用的额定带宽。
假如某些亮度信息是经过色度通道到达的(假如运用 RB 信号而不是差分 UV 信号,就会呈现这种情况),是非分辨率就会受到影响。
YUV 模型界说了一个亮度重量 (Y),表明物理线性空间亮度,以及两个色度重量,别离称为 U(蓝色投影)和 V(红色投影)。它可用于在 RGB 模型之间进行转化,并具有不同的色彩空间
转化公式如下:
Harbeth功用清单
- 支撑ios体系和macOS体系
- 支撑运算符函数式操作
- 支撑多种形式数据源 UIImage, CIImage, CGImage, CMSampleBuffer, CVPixelBuffer.
- 支撑快速设计滤镜
- 支撑合并多种滤镜作用
- 支撑输出源的快速扩展
- 支撑相机收集特效
- 支撑视频添加滤镜特效
- 支撑矩阵卷积
- 支撑运用体系 MetalPerformanceShaders.
- 支撑兼容 CoreImage.
- 滤镜部分大致分为以下几个模块:
- Blend:图画融合技能
- Blur:模糊作用
- Pixel:图画的根本像素色彩处理
- Effect:作用处理
- Lookup:查找表过滤器
- Matrix: 矩阵卷积滤波器
- Shape:图画形状大小相关
- Visual: 视觉动态特效
- MPS: 体系 MetalPerformanceShaders.
最终
- 慢慢再弥补其他相关滤镜,喜欢就给我点个星吧。
-
滤镜Demo地址,目前包括
100+
种滤镜,一起也支撑CoreImage混合运用。 - 再附上一个开发加速库KJCategoriesDemo地址
- 再附上一个网络基础库RxNetworksDemo地址
- 喜欢的老板们能够点个星,谢谢各位老板!!!
✌️.