这个系列文章我们来介绍一位海外工程师如何探索 GPUImage 音视频技术,对于想要开始学习音视频技术的朋友,这些文章是份不错的入门资料,本篇介绍 GPUImage 处理原始数据。
——来自公众号“关键帧Keyframe”的分享
GPUImage 不仅擅长利用 GPU 加速处理图片与视频,还提供了直接操作原始像素数据(raw pixel data)的强大能力。无论是集成计算机视觉算法、处理自定义传感器数据,还是实现专用图像管线,掌握 GPUImage 的原始数据接口都能为应用打开更广阔的可能性。
在本文语境中,“原始数据”特指未经封装的字节数组,而非 UIImage 对象或视频帧。使用原始数据可以:
- 从非常规来源(自定义相机、传感器、算法)获取数据
- 通过避免多余的对象创建来优化内存
- 与计算机视觉库无缝衔接(它们通常只接受裸像素缓冲区)
- 对像素数据实现精确控制的自定义处理管线
1、把原始数据送进 GPUImage
GPUImageRawDataInput 是入口类,负责把字节数组上传到 GPU 并生成可供滤镜处理的纹理。
1.1、支持的像素格式与类型
像素格式 (GPUPixelFormat) | 说明 |
|---|---|
GPUPixelFormatBGRA | 蓝、绿、红、Alpha(默认) |
GPUPixelFormatRGBA | 红、绿、蓝、Alpha |
GPUPixelFormatRGB | 红、绿、蓝(无 Alpha) |
GPUPixelFormatLuminance | 灰度 |
像素类型 (GPUPixelType) | 说明 |
|---|---|
GPUPixelTypeUByte | 无符号字节(0–255,默认) |
GPUPixelTypeFloat | 浮点数值 |
1.2、创建原始数据输入
// 创建 640×480 的 BGRA 数据
NSUInteger bytesPerRow = 640 * 4; // 每像素 4 字节
GLubyte *rawBytes = (GLubyte *)calloc(bytesPerRow * 480, sizeof(GLubyte));
// 填充 rawBytes …
GPUImageRawDataInput *rawDataInput =
[[GPUImageRawDataInput alloc] initWithBytes:rawBytes
size:CGSizeMake(640, 480)];
// 数据已复制到纹理,可立即释放原始内存
free(rawBytes);
如需指定格式:
GPUImageRawDataInput *rawDataInput =
[[GPUImageRawDataInput alloc] initWithBytes:rawBytes
size:CGSizeMake(640, 480)
pixelFormat:GPUPixelFormatRGBA
type:GPUPixelTypeFloat];
源码:
GPUImageRawDataInput.h,GPUImageRawDataInput.m#L35-L50
1.3、更新已有数据(帧序列场景)
[rawDataInput updateDataFromBytes:newRawBytes size:CGSizeMake(640, 480)];
[rawDataInput processData]; // 通知下游滤镜处理新帧
该方法在实时视频或流式数据中尤其实用:复用现有 GPU 资源,避免每帧重新创建对象。
源码:
GPUImageRawDataInput.m#L78-L83
1.4、幕后流程
调用 initWithBytes:size: 或 updateDataFromBytes:size: 时,GPUImage 会:
- 绑定 OpenGL 纹理
- 通过
glTexImage2D把字节上传到 GPU - 使纹理可在管线中继续流转
processData 则向下游滤镜广播“新数据就绪”。
源码:
GPUImageRawDataInput.m#L67-L76
2、处理原始数据
与常规输入源完全一致:把 GPUImageRawDataInput 接到任意滤镜即可。
GPUImageSepiaFilter *sepiaFilter = [[GPUImageSepiaFilter alloc] init];
[rawDataInput addTarget:sepiaFilter];
GPUImageView *filterView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, 640, 480)];
[sepiaFilter addTarget:filterView];
[rawDataInput processData];
3、从 GPUImage 取回原始数据
处理完后如需拿回字节,可借助 GPUImageFilter 的帧缓冲读取:
GPUImageFilter *outputFilter = [[GPUImageFilter alloc] init];
[sepiaFilter addTarget:outputFilter];
[rawDataInput processData];
GPUImageFramebuffer *fb = [outputFilter framebufferForOutput];
NSUInteger totalBytes = fb.size.width * fb.size.height * 4;
GLubyte *processedBytes = (GLubyte *)malloc(totalBytes);
[fb readPixelsWithBytesToRead:processedBytes];
// …使用 processedBytes…
free(processedBytes);
4、线程安全与性能
GPUImage 通过以下机制保证线程安全:
- 信号量:防止并发更新
- 异步处理:图像运算在专用后台队列完成
- 输出同步:回调在运算完成后触发
架构示意
应用 → 原始字节 →GPUImageRawDataInput→ GPU 处理 → 帧缓冲 →readPixels()→ 返回字节
源码:
GPUImageRawDataInput.m#L85-L108
5、典型应用场景
| 场景 | 做法 |
|---|---|
| 与 OpenCV 集成 | GPUImage 预处理后 → 取回字节 → 交给 OpenCV 分析 → 结果再回 GPUImage 可视化 |
| 自定义传感器 | 深度、红外、医疗、科研等非标准数据源,直接以字节形式接入管线 |
| 高性能视频 | 跳过 UIImage/CGImage 转换,直接处理相机裸缓冲区,全程保持像素格式一致 |
6、最佳实践
- 内存管理:原始字节数组体积大,用完后立即释放
- 复用对象:用
updateDataFromBytes:而非每帧新建GPUImageRawDataInput - 格式一致:输入像素格式与后续滤镜期望的格式匹配,避免额外转换
- 异步意识:处理在后台线程完成,UI 或后续步骤需等待回调
- 缓冲池:极高帧率场景可预先分配并循环使用缓冲块,减少 malloc/free
7、结语
在 GPUImage 中操作原始数据,让你对图像管线拥有最细粒度的控制,也方便与自定义数据源或外部算法协同。虽然需要更多手动内存管理,但换来的性能收益和灵活性,使其成为高级图像处理应用的利器。
学习和提升音视频开发技术,推荐你加入我们的知识星球:【关键帧的音视频开发圈】

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