背景
你是否曾经想要给你的照片增添一些特殊效果,让它们更加引人注目?或者你是否对那些充满艺术感的滤镜照片羡慕不已,但却认为它们需要专业的图像处理技巧?今天,我将向你揭示一个令人惊讶的事实——图片加滤镜竟如此简单。
滤镜是一种强大而又简单的工具,它可以改变图片的色彩、亮度、对比度和纹理,以及位置大小等特性,让你的照片脱颖而出。
在本文中,我将向你展示如何简单使用滤镜属性为你的图片增添滋味。让我们一起揭开图片加滤镜的神秘面纱,发掘那些隐藏在简单代码背后的惊艳效果。让你的照片焕发新生,让你的创造力在滤镜的魔力下绽放,让我们开始吧。
熟悉iOS的朋友,如果提到滤镜,那么第一时间就会想到 GPUImage,确实很好用,不过这个库已经太久不更新维护。
于是乎我这边基于Swift + Metal
制作一款全新的滤镜库,这边在编写的时候,当然也有借鉴GPUImage
很多滤镜算法,再次感谢大神!!!
- 先贴链接地址:github.com/yangKJ/Harb…
Animated | Still | Mix |
---|---|---|
主要功能
Harbeth 是基于GPU快速实现图片or视频注入滤镜特效,代码零侵入实现图像显示and视频导出功能,支持iOS系统和macOS系统。👒👒👒
支持多种数据源,可以和很多系统库兼容注入滤镜,例如:VFCabbage、AR、CIDetector(人脸识别)、相机采集、视频实时播放等等。
话不多说,先简单介绍滤镜库 Harbeth 支持功能:
- 支持macOS和iOS平台系统,也支持SwiftUI使用;
- 高性能在如下数据源快速添加过滤器效果:UIImage,NSImage,CIImage,CGImage,CMSampleBuffer,CVPixelBuffer等;
- 支持两种查找滤镜 LUTs 和 Cube 来定制专属滤镜;
- 支持使用系统 MetalPerformanceShaders 滤镜和兼容 CoreImage 滤镜混合使用;
- 支持快速设计滤镜和扩展输出源;
- 支持合并多种滤镜效果并且支持运算符函数式操作;
- 支持相机采集特效 和 视频添加滤镜特效;
- 支持使用 Kakapos 库对已有视频添加滤镜并导出;
- Metal内核滤镜部分大致分为以下几个模块:
- Blend:图像融合技术
- Blur:模糊效果
- Pixel:图像的基本像素颜色处理
- Coordinate:图像坐标叠加修改效果处理
- Lookup:查找表过滤器
- Matrix: 矩阵卷积滤波器
- Shape:图像形状大小相关
- Generator: 生成滤镜,例如纯色图像
- Combination: 组合滤镜效果
大致统计下来,这边目前也已经拥有超150
多种内置滤镜提供使用,并且这边也支持兼容系统CoreImage
,这样一算那滤镜就太多了!
实现方案
该库主要分为以下几个板块,基础配置模块、Metal内核滤镜、CoreImage模块、MPS模块
基础模块又主要分为以下部分:
- Core: 该模块主要处理配置Metal信息,和CoreImage兼容转换;
- Extensions: 该模块主要处理各类型资源和MTLTexture互相转换方法;
- Matrix: 该模块主要包含矩阵相关,常用矩阵卷积内核和常用颜色矩阵;
- Outputs: 该模块主要包含对外转换,快速向源添加过滤器;
- Setup: 该模块主要包含配置信息,该库使用的小工具等;
如何使用
- 代码完全可以做到零侵入对图像注入滤镜功能,然后显示在UIImageView控件;
🚗 - 原始代码:
lazy var ImageView: UIImageView = {
let imageView = UIImageView(image: originImage)
imageView.layer.borderColor = R.color("background2")?.cgColor
imageView.layer.borderWidth = 0.5
return imageView
}()
ImageView.image = originImage
图像加滤镜
🎷 - 注入滤镜代码:
let filter1 = C7ColorMatrix4x4(matrix: Matrix4x4.Color.sepia)
let filter2 = C7Granularity(grain: 0.8)
let filter3 = C7SoulOut(soul: 0.7)
let filters = [filter1, filter2, filter3]
简单使用`Outputable`🚗 🚗 🚗
ImageView.image = try? originImage.makeGroup(filters: filters)
多种多样数据源
- 可以高性能在这些数据源快速添加过滤器,例如:NSImage、UIImage、CIImage、CGImage、CMSampleBuffer, CVPixelBuffer;
- 其中CMSampleBuffer主要为相机采集回来的缓冲数据,CVPixelBuffer则可以简单用于播放视频添加滤镜;
也可数据源模式使用
let dest = BoxxIO.init(element: originImage, filters: filters)
// 同步处理
ImageView.image = try? dest.output()
异步处理
该模式为推荐模式,可以使性能达到最优;
// 异步处理
dest.transmitOutput(success: { [weak self] image in
DispatchQueue.main.async {
self?.ImageView.image = image
}
})
运算符
或者运算符操作
ImageView.image = originImage -->>> filters
甚至不定参数使用
ImageView.image = originImage.filtering(filter, filter2, filter3)
总之这么多种方案,怎么使用就看你的心情了!!!🫤
如何设计滤镜
这边完全采用协议方式,用于自定义各种不同类型滤镜,同时也兼容和CoreImage混合使用;
滤镜类型
- 主要包含以下几种类型滤镜;
public enum Modifier {
/// 基于`MTLComputeCommandEncoder`并行计算编码器,可直接生成图片
case compute(kernel: String)
/// 基于`MTLRenderCommandEncoder`渲染 3D 编码器,需配合`MTKView`方能显示
case render(vertex: String, fragment: String)
/// 基于`MTLBlitCommandEncoder`位图复制编码器,拷贝纹理同时也能生成贴图
case blit
/// 基于`CoreImage`,直接生成图片
case coreimage(CIName: String)
/// 基于`MetalPerformanceShaders`着色器
case mps(performance: MPSUnaryImageKernel)
}
主要包括下面几个协议,
- C7FilterPorotocol: 基础协议,包含滤镜类型Modifier、参数因子、其他输入纹理源、以及修改输出纹理尺寸大小;
- ComputeFiltering: 该协议主要用于基于内核模式,传递特殊参数因子;
- CoreImageFiltering: 该协议主要用于基于CoreImage,兼容使用系统CoreImage滤镜;
- RenderFiltering: 该协议主要作用于基于片元着色器和顶点着色器;
- MPSKernelProtocol: 该协议主要就是使用系统MetalPerformanceShaders着色器;
- CombinationProtocol: 该协议主要用于组合滤镜之间传递金属纹理;
基础 C7FilterProtocal 协议:
public protocol C7FilterProtocol {
/// 编码器类型和对应的函数名
///
/// 计算需要对应的`kernel`函数名
/// 渲染需要一个`vertex`着色器函数名和一个`fragment`着色器函数名
var modifier: Modifier { get }
/// 制作缓冲区
/// 设置修改参数因子,需要转换为`Float`。
var factors: [Float] { get }
/// 多输入源扩展
/// 包含 `MTLTexture` 的数组
var otherInputTextures: C7InputTextures { get }
/// 改变输出图像的大小
func outputSize(input size:C7Size) -> C7Size
}
设计滤镜
- 遵循协议基础
C7FilterProtocol
和可选其他协议 - 配置额外的所需纹理
- 配置传递参数因子,仅支持
Float
类型 - 配置输出纹理尺寸
- 编写基于并行计算的核函数着色器
- 最后提交到缓冲区,渲染输出纹理
- 简单使用,由于我这边设计的是基于并行计算管道,所以可以快速生成图像
基于内核模式的查找滤镜
kernel void C7LookupTable(texture2d<half, access::write> outputTexture [[texture(0)]],
texture2d<half, access::read> inputTexture [[texture(1)]],
texture2d<half, access::sample> lookupTexture [[texture(2)]],
constant float *intensity [[buffer(0)]],
uint2 grid [[thread_position_in_grid]]) {
const half4 inColor = inputTexture.read(grid);
const half blueColor = inColor.b * 63.0h; // 蓝色部分[0, 63] 共64种
half2 quad1;
quad1.y = floor(floor(blueColor) / 8.0h);
quad1.x = floor(blueColor) - (quad1.y * 8.0h);
half2 quad2;
quad2.y = floor(ceil(blueColor) / 8.0h);
quad2.x = ceil(blueColor) - (quad2.y * 8.0h);
const float A = 0.125;
const float B = 0.5 / 512.0;
const float C = 0.125 - 1.0 / 512.0;
float2 texPos1; // 计算颜色(r,b,g)在第一个正方形中对应位置
texPos1.x = A * quad1.x + B + C * inColor.r;
texPos1.y = A * quad1.y + B + C * inColor.g;
float2 texPos2;
texPos2.x = A * quad2.x + B + C * inColor.r;
texPos2.y = A * quad2.y + B + C * inColor.g;
constexpr sampler quadSampler(mag_filter::linear, min_filter::linear);
const half4 newColor1 = lookupTexture.sample(quadSampler, texPos1);
const half4 newColor2 = lookupTexture.sample(quadSampler, texPos2);
const half4 newColor = mix(newColor1, newColor2, fract(blueColor));
const half4 outColor = half4(mix(inColor, half4(newColor.rgb, inColor.a), half(*intensity)));
outputTexture.write(outColor, grid);
}
视频导出
- 对于已有视频添加滤镜,然后导出链接地址
URL
即可; - 该模块已单独抽离成新库 Kakapos 来使用;
- 支持网络和本地网址以及专辑视频,支持所有能转换
Buffer
的滤镜库,例如GPUImage、Harbeth、CoreImage
等。 - 简单讲就是只需要对
CVPixelBuffer
进行注入滤镜即可。
创建使用
// 创建转换工具
let exporter = Exporter(asset: asset, delegate: self)
// 滤镜
let martix = C7ColorMatrix4x4(matrix: Matrix4x4.Color.gray)
// 转换注入滤镜
exporter.export(outputURL: outputURL) { [weak self] in
let dest = BoxxIO(element: $0, filter: martix)
return (try? dest.output()) ?? $0
}
导出协议
主要包含成功和失败两个协议方法
public protocol ExporterDelegate: NSObjectProtocol {
/// Video export successed.
/// - Parameters:
/// - exporter: VideoExporter
/// - videoURL: Export the successful video url, Be equivalent to outputURL.
func export(_ exporter: Exporter, success videoURL: URL)
/// Video export failure.
/// - Parameters:
/// - exporter: VideoExporter
/// - error: Failure error message.
func export(_ exporter: Exporter, failed error: Exporter.Error)
}
到此基本就已经实现简单的视频导出并且添加滤镜功能,这边如果需要使用其他库,例如GPUImage
也是同样的原理,对CVPixelBuffer
进行处理;
展示图
- 给部分效果展示
色相融合 | 透明度融合 | 亮度融合 |
---|---|---|
总结
本文对于如何使用Harbth滤镜库做了简单介绍,和如何设计滤镜等等;
欢迎大家来使用该框架,然后指正修改亦或者大家有什么需求也可提出来,后续慢慢补充完善;
也欢迎大神来帮忙使用优化此库,再次感谢!!!
对于如何使用和设计原理先简单介绍出来,关于后续功能和优化再慢慢介绍!
觉得有帮助的铁子,就给我点个星🌟支持一哈,谢谢铁子们~
本文滤镜框架传送门 Harbeth 地址。
有什么问题也可以直接联系我,邮箱 yangkj310@gmail.com