探索 GPUImage 音视频技术(16):处理原始数据

这个系列文章我们来介绍一位海外工程师如何探索 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.hGPUImageRawDataInput.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 会:

  1. 绑定 OpenGL 纹理
  2. 通过 glTexImage2D 把字节上传到 GPU
  3. 使纹理可在管线中继续流转

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 通过以下机制保证线程安全:

  1. 信号量:防止并发更新
  2. 异步处理:图像运算在专用后台队列完成
  3. 输出同步:回调在运算完成后触发

架构示意
应用 → 原始字节 → GPUImageRawDataInput → GPU 处理 → 帧缓冲 → readPixels() → 返回字节

源码:GPUImageRawDataInput.m#L85-L108

5、典型应用场景

场景做法
与 OpenCV 集成GPUImage 预处理后 → 取回字节 → 交给 OpenCV 分析 → 结果再回 GPUImage 可视化
自定义传感器深度、红外、医疗、科研等非标准数据源,直接以字节形式接入管线
高性能视频跳过 UIImage/CGImage 转换,直接处理相机裸缓冲区,全程保持像素格式一致

6、最佳实践

  1. 内存管理:原始字节数组体积大,用完后立即释放
  2. 复用对象:用 updateDataFromBytes: 而非每帧新建 GPUImageRawDataInput
  3. 格式一致:输入像素格式与后续滤镜期望的格式匹配,避免额外转换
  4. 异步意识:处理在后台线程完成,UI 或后续步骤需等待回调
  5. 缓冲池:极高帧率场景可预先分配并循环使用缓冲块,减少 malloc/free

7、结语

在 GPUImage 中操作原始数据,让你对图像管线拥有最细粒度的控制,也方便与自定义数据源或外部算法协同。虽然需要更多手动内存管理,但换来的性能收益和灵活性,使其成为高级图像处理应用的利器。

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

探索 GPUImage 音视频技术(16):处理原始数据

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

(0)

相关推荐

发表回复

登录后才能评论