探索 GPUImage 音视频技术(13):滤镜链

这个系列文章我们来介绍一位海外工程师如何探索 GPUImage 音视频技术,对于想要开始学习音视频技术的朋友,这些文章是份不错的入门资料,本篇介绍 GPUImage 滤镜链。

——来自公众号“关键帧Keyframe”的分享

滤镜链是 GPUImage 最强大的功能之一,它让你能够将多个滤镜连接起来,构建出单一滤镜难以实现的复杂图像与视频处理流水线。本指南将介绍 GPUImage 中滤镜链的工作原理,并示范如何创建并管理高效的滤镜链。

1、架构概览

GPUImage 采用基于“目标(target)”的架构,图像数据以 OpenGL 纹理的形式从源头流经一系列滤镜,最终到达输出端。每个组件各司其职:

  • 数据源(Sources):如 GPUImageVideoCameraGPUImageStillCameraGPUImagePictureGPUImageMovie,负责提供纹理格式的图像数据。
  • 滤镜(Filters):接收纹理、处理后输出新纹理(实现了 GPUImageInput 协议)。
  • 输出端(Outputs):负责显示或保存最终图像,例如 GPUImageView 用于屏幕显示,或导出为 UIImage / 视频文件。

得益于此设计,你可以按需自由组合这些组件,搭建任何想要的处理流水线。

2、基本的线性滤镜链

最简单的情况是一条直线:数据源 → 滤镜1 → 滤镜2 → … → 输出端。

2.1、示例:相机 → 棕褐色 → 亮度 → 屏幕显示

// 1. 创建摄像头
GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc]
    initWithSessionPreset:AVCaptureSessionPreset640x480
    cameraPosition:AVCaptureDevicePositionBack];
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;

// 2. 创建滤镜
GPUImageSepiaFilter *sepiaFilter = [[GPUImageSepiaFilter alloc] init];
GPUImageBrightnessFilter *brightnessFilter = [[GPUImageBrightnessFilter alloc] init];
brightnessFilter.brightness = 0.1;

// 3. 创建显示视图
GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, width, height)];

// 4. 建立链
[videoCamera addTarget:sepiaFilter];           // 摄像头 → 棕褐色滤镜
[sepiaFilter addTarget:brightnessFilter];      // 棕褐色 → 亮度滤镜
[brightnessFilter addTarget:filteredVideoView];// 亮度 → 显示视图

// 5. 开始采集
[videoCamera startCameraCapture];

3、分支滤镜链

GPUImage 允许把一个组件的输出同时送到多个目标,实现“分叉”效果。

3.1、示例:摄像头同时输出到棕褐色滤镜和高斯模糊滤镜

GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480
                                                                       cameraPosition:AVCaptureDevicePositionBack];

GPUImageSepiaFilter *sepiaFilter = [[GPUImageSepiaFilter alloc] init];
GPUImageGaussianBlurFilter *blurFilter = [[GPUImageGaussianBlurFilter alloc] init];

GPUImageView *sepiaView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, width, height/2)];
GPUImageView *blurView  = [[GPUImageView alloc] initWithFrame:CGRectMake(0, height/2, width, height/2)];

// 分叉:摄像头同时连到两个滤镜
[videoCamera addTarget:sepiaFilter];
[videoCamera addTarget:blurFilter];

// 各自显示
[sepiaFilter addTarget:sepiaView];
[blurFilter addTarget:blurView];

[videoCamera startCameraCapture];

4、多输入滤镜(链合并)

某些效果需要把多条图像流合并,例如混合模式、合成操作或自定义多输入滤镜。GPUImage 提供了 GPUImageTwoInputFilter 等专用滤镜。

4.1、示例:摄像头实时画面与一张 PNG 做 Alpha 混合

// 1. 摄像头
GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480
                                                                       cameraPosition:AVCaptureDevicePositionBack];

// 2. 静态图片
UIImage *overlayImage = [UIImage imageNamed:@"Overlay.png"];
GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:overlayImage];

// 3. Alpha 混合滤镜
GPUImageAlphaBlendFilter *blendFilter = [[GPUImageAlphaBlendFilter alloc] init];
blendFilter.mix = 0.5; // 50% 混合

// 4. 显示视图
GPUImageView *displayView = [[GPUImageView alloc] initWithFrame:self.view.bounds];

// 5. 连接
[videoCamera addTarget:blendFilter];   // 第一输入
[stillImageSource addTarget:blendFilter]; // 第二输入
[blendFilter addTarget:displayView];

// 6. 启动
[videoCamera startCameraCapture];
[stillImageSource processImage];

对于 GPUImageTwoInputFilter 等多输入滤镜,目标的添加顺序决定了主次输入;不同滤镜对输入顺序有不同要求。

5、复杂滤镜链示例

将“分叉”与“合并”组合,可搭建更高级的流水线:

GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480
                                                                       cameraPosition:AVCaptureDevicePositionBack];

GPUImageContrastFilter *contrastFilter = [[GPUImageContrastFilter alloc] init];
GPUImageGaussianBlurFilter *blurFilter = [[GPUImageGaussianBlurFilter alloc] init];
GPUImageSobelEdgeDetectionFilter *edgeFilter = [[GPUImageSobelEdgeDetectionFilter alloc] init];
GPUImageAlphaBlendFilter *blendFilter = [[GPUImageAlphaBlendFilter alloc] init];
blendFilter.mix = 0.5;
GPUImageView *displayView = [[GPUImageView alloc] initWithFrame:self.view.bounds];

/* 构造链
   摄像头 → 对比度 ─┬─> 模糊 ─┐
                     └─> 边缘 ─┴─> 混合 → 显示
*/
[videoCamera addTarget:contrastFilter];
[contrastFilter addTarget:blurFilter];
[contrastFilter addTarget:edgeFilter];
[blurFilter addTarget:blendFilter];
[edgeFilter addTarget:blendFilter];
[blendFilter addTarget:displayView];

[videoCamera startCameraCapture];

6、动态链管理

滤镜链并非一成不变,可在运行时增删组件或调节参数。

6.1、增删滤镜

// 追加新滤镜
GPUImageSepiaFilter *newFilter = [[GPUImageSepiaFilter alloc] init];
[existingFilter addTarget:newFilter];
[newFilter addTarget:finalOutput];

// 替换滤镜
[sourceFilter removeTarget:filterToRemove];
[sourceFilter addTarget:replacementFilter];
[replacementFilter addTarget:finalOutput];

6.2、实时调整参数

// 带动画地调整模糊强度
CGFloat startValue = 0.0;
CGFloat endValue   = 1.0;
CGFloat duration   = 2.0;

[UIView animateWithDuration:duration animations:^{
    blurFilter.blurSize = endValue;
}];

实际应用中推荐使用 CADisplayLink 等机制以获得更平滑的动画。

7、性能注意事项

  1. 减少 CPU↔GPU 数据往返:纹理上传/下载开销大,尽量全程在 GPU 完成。
  2. 滤镜顺序:把计算量大的滤镜放在链后端,或在降分辨率后执行。
  3. 合理分辨率:并非所有环节都要全尺寸;可在关键位置插入缩放滤镜。
  4. 复用滤镜:创建滤镜有开销,尽量复用而非频繁新建。
  5. 测试帧率:链路过长或过于复杂会影响性能,务必在目标设备实测。

8、结语

GPUImage 的滤镜链机制让你能够像搭积木一样构建任意复杂度的图像/视频处理流水线。掌握“线性链—分叉—合并”三大模式,再结合动态管理,即可充分发挥 GPU 加速的威力,从简单照片滤镜到高级计算机视觉应用,皆可轻松实现。

学习和提升音视频开发技术,推荐你加入我们的知识星球:【关键帧的音视频开发圈】

探索 GPUImage 音视频技术(13):滤镜链

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论