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

如何从旋转后的CVPixelBufferRef正确创建OpenCV矩阵?

解决Portrait模式下CVPixelBuffer转OpenCV Mat的像素错乱问题

问题核心:当你把AVCaptureConnection的videoOrientation设为Portrait时,捕获的CVPixelBuffer内存布局会和Landscape Right模式不同——不仅宽高逻辑上交换,行字节数(stride)也可能和你预期的width*4不一致,原代码直接用videoRect的宽高和默认stride=0,会导致内存读取错位,出现左上角小区域损坏、整体像素错乱的问题。

下面分两种你提到的像素格式给出修复代码:

1. 处理kCVPixelFormatType_32BGRA格式

CVPixelBufferLockBaseAddress(pixelBuffer, 0);

// 1. 获取buffer的实际宽高(不要用videoRect的,Portrait模式下buffer的物理宽高是交换的)
size_t bufferWidth = CVPixelBufferGetWidth(pixelBuffer);
size_t bufferHeight = CVPixelBufferGetHeight(pixelBuffer);
// 2. 获取正确的行字节数(关键!硬件捕获的buffer stride通常不等于width*4)
size_t stride = CVPixelBufferGetBytesPerRow(pixelBuffer);

// 3. 创建正确的OpenCV Mat,使用buffer的实际宽高和stride
cv::Mat tempMat(bufferHeight, bufferWidth, CV_8UC4, CVPixelBufferGetBaseAddress(pixelBuffer), stride);

// 4. 旋转Mat到Portrait方向(和MLModel的检测坐标对齐)
cv::rotate(tempMat, mat, cv::ROTATE_90_CLOCKWISE);

CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

2. 处理kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange格式(双平面YUV)

这种格式是分离的Y、CbCr两个平面,不能直接按单平面读取,需要分别提取后转换为BGRA:

CVPixelBufferLockBaseAddress(pixelBuffer, 0);

size_t yWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
size_t yHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
size_t uvWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
size_t uvHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);

size_t yStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
size_t uvStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);

// 创建Y和UV通道的Mat
cv::Mat yMat(yHeight, yWidth, CV_8UC1, CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0), yStride);
cv::Mat uvMat(uvHeight, uvWidth, CV_8UC2, CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1), uvStride);

// 合并YUV通道并转换为BGRA格式
cv::Mat yuvMat;
cv::merge(std::vector<cv::Mat>{yMat, uvMat}, yuvMat);
cv::cvtColor(yuvMat, mat, cv::COLOR_YUV2BGRA_NV12);

// 旋转到Portrait方向匹配MLModel坐标
cv::rotate(mat, mat, cv::ROTATE_90_CLOCKWISE);

CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

关键修复点说明

  • 不要依赖videoRect的宽高:AVCaptureConnection设置videoOrientation后,输出的CVPixelBuffer物理宽高是传感器原生的(通常为Landscape),逻辑方向通过旋转实现,必须用CVPixelBufferGetWidth/Height获取实际维度。
  • 必须使用正确的stride:硬件加速的像素缓冲区为了内存对齐,行字节数会大于width*通道数,直接用0会让OpenCV计算错误的stride,导致内存读取错位。
  • 旋转Mat对齐坐标:你设置videoOrientation为Portrait是为了让MLModel的归一化坐标和屏幕显示对齐,所以转换后的Mat需要旋转90度顺时针,才能和检测结果匹配。

内容的提问来源于stack exchange,提问作者StainlessSteelRat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 18:42:19