You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何将一个CVPixelBuffer从中间分割为两个独立的CVPixelBuffer?

无需转换其他图像类型,直接分割CVPixelBuffer为两个独立缓冲区的实现方案

完全可以做到,不需要将CVPixelBuffer转换为UIImage、CGImage等其他格式,直接基于原缓冲区的内存操作就能分割出两个独立的CVPixelBuffer,下面是两种可行的实现思路:

方案一:共享原内存(高效无复制)

这种方式不需要复制像素数据,直接基于原缓冲区的内存区域创建新的CVPixelBuffer,性能最优,但需要注意原缓冲区的生命周期管理——必须保证新缓冲区存在期间,原缓冲区不被释放。

步骤:

  1. 提取原缓冲区的核心参数:
    size_t totalWidth = CVPixelBufferGetWidth(sourceBuffer);
    size_t totalHeight = CVPixelBufferGetHeight(sourceBuffer);
    size_t singleHeight = totalHeight / 2;
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(sourceBuffer);
    OSType pixelFormat = CVPixelBufferGetPixelFormatType(sourceBuffer);
    
  2. 获取原缓冲区的内存基地址,计算两张子图的起始地址:
    void *baseAddr = CVPixelBufferGetBaseAddress(sourceBuffer);
    void *topImageAddr = baseAddr;
    void *bottomImageAddr = baseAddr + singleHeight * bytesPerRow;
    
  3. 分别创建两个共享内存的CVPixelBuffer:
    // 创建顶部图像的缓冲区
    CVPixelBufferRef topBuffer = NULL;
    CVPixelBufferCreateWithBytes(NULL, totalWidth, singleHeight, pixelFormat, topImageAddr, bytesPerRow, NULL, NULL, NULL, &topBuffer);
    
    // 创建底部图像的缓冲区
    CVPixelBufferRef bottomBuffer = NULL;
    CVPixelBufferCreateWithBytes(NULL, totalWidth, singleHeight, pixelFormat, bottomImageAddr, bytesPerRow, NULL, NULL, NULL, &bottomBuffer);
    
    注意:如果原缓冲区是只读的,创建时需要指定kCVPixelBufferLockFlags_ReadOnly相关属性,避免写入错误。

方案二:复制像素数据(独立安全)

如果不想依赖原缓冲区的生命周期,或者原缓冲区是不可共享的只读内存,可以通过复制对应区域的像素数据,创建完全独立的新CVPixelBuffer。

步骤:

  1. 同样先提取原缓冲区的核心参数(同方案一)。
  2. 创建两个空的目标缓冲区,确保参数和原缓冲区匹配:
    NSDictionary *bufferAttributes = @{
        (id)kCVPixelBufferPixelFormatTypeKey: @(pixelFormat),
        (id)kCVPixelBufferBytesPerRowAlignmentKey: @(bytesPerRow),
        (id)kCVPixelBufferWidthKey: @(totalWidth),
        (id)kCVPixelBufferHeightKey: @(singleHeight)
    };
    
    CVPixelBufferRef topBuffer = NULL;
    CVPixelBufferCreate(NULL, totalWidth, singleHeight, pixelFormat, (__bridge CFDictionaryRef)bufferAttributes, &topBuffer);
    
    CVPixelBufferRef bottomBuffer = NULL;
    CVPixelBufferCreate(NULL, totalWidth, singleHeight, pixelFormat, (__bridge CFDictionaryRef)bufferAttributes, &bottomBuffer);
    
  3. 锁定所有缓冲区的内存,执行数据复制:
    // 锁定内存(必须操作,确保内存可访问)
    CVPixelBufferLockBaseAddress(sourceBuffer, 0);
    CVPixelBufferLockBaseAddress(topBuffer, 0);
    CVPixelBufferLockBaseAddress(bottomBuffer, 0);
    
    // 复制顶部图像数据
    memcpy(CVPixelBufferGetBaseAddress(topBuffer), CVPixelBufferGetBaseAddress(sourceBuffer), singleHeight * bytesPerRow);
    // 复制底部图像数据
    memcpy(CVPixelBufferGetBaseAddress(bottomBuffer), CVPixelBufferGetBaseAddress(sourceBuffer) + singleHeight * bytesPerRow, singleHeight * bytesPerRow);
    
    // 解锁内存
    CVPixelBufferUnlockBaseAddress(sourceBuffer, 0);
    CVPixelBufferUnlockBaseAddress(topBuffer, 0);
    CVPixelBufferUnlockBaseAddress(bottomBuffer, 0);
    

注意事项

  • 像素格式兼容性:如果原缓冲区是YUV等多平面格式(比如NV12),需要针对每个平面分别处理分割逻辑,而不是直接按整体字节数分割。
  • 字节对齐:新缓冲区的bytesPerRow必须和原缓冲区保持一致,否则会出现图像错位、花屏等问题。
  • 内存权限:如果原缓冲区标记为只读,方案一中创建的新缓冲区也只能是只读的,不能进行写入操作。

内容的提问来源于stack exchange,提问作者Burkay Sabırsız

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 19:01:14