OpenCV warpAffine旋转YUV420视频帧画面扭曲问题求解
问题描述
尝试使用OpenCV的warpAffine()接口,对libcamera-apps组件中libcamera-vid工具采集的视频帧执行任意角度旋转操作,经确认采集到的视频帧为YUV420 planar格式。
核心实现逻辑如下:
#include <opencv2/imgproc.hpp> #include <opencv2/core.hpp> cv::Point2f Ycenter = cv::Point2f(width / 2.0f, height / 2.0f); cv::Point2f UVcenter = cv::Point2f(width / 4.0f, height / 4.0f); double rotation = 0; cv::Mat Ytransform = cv::getRotationMatrix2D(Ycenter, rotation, 1.0); cv::Mat UVtransform = cv::getRotationMatrix2D(UVcenter, rotation, 1.0); int Uoffset = height*width; int Voffset = 5*info.height*info.width/4; cv::Size Ysize(info.height, info.width); cv::Size UVsize(info.height / 2, info.width / 2); for (unsigned int count = 0; ; count++) { // ... // 等待帧到达 // ... // 获取存储帧的缓冲区 uint8_t* buffer = getFrameBuffer(); double rot = floor(count / 10); if (10*rot != rotation) { rotation = 10*rot; Ytransform = cv::getRotationMatrix2D(Ycenter, rotation, 1.0); UVtransform = cv::getRotationMatrix2D(UVcenter, rotation, 1.0); } cv::Mat Y(Ysize.height, Ysize.width, CV_8UC1, buffer); cv::Mat U(UVsize.height, UVsize.width, CV_8UC1, buffer + Uoffset); cv::Mat V(UVsize.height, UVsize.width, CV_8UC1, buffer + Voffset); cv::warpAffine(Y, Y, Ytransform, Ysize); cv::warpAffine(U, U, UVtransform, UVsize); cv::warpAffine(V, V, UVtransform, UVsize); sendFrameToEncoder(buffer); }
上述代码中height、width参数分别对应视频帧的原始高度与宽度。
程序运行后输出画面存在异常扭曲,仅能观察到部分内容发生旋转,整体显示效果完全不符合预期,不同旋转角度的效果如下:
- rotation = 0时的输出效果:

- rotation = 10时的输出效果:

- rotation = 20时的输出效果:

- rotation = 30时的输出效果:

运行环境:树莓派Zero W,搭载Raspberry Pi OS Bullseye系统,使用OpenCV 4.5.1版本。
故障原因
代码存在两处核心错误,直接导致画面异常:
cv::Size参数顺序传反
OpenCV中cv::Size构造函数的参数顺序为(宽度, 高度),代码中定义Ysize时传入(info.height, info.width)、定义UVsize时传入(info.height/2, info.width/2),完全颠倒了宽高值。这直接导致Y、U、V三个平面的Mat对象内存步长计算错误,像素地址映射完全错位,哪怕旋转角度为0,画面也会出现扭曲。warpAffine非法原地操作cv::warpAffine不支持输入、输出指向同一块内存的原地运算。仿射变换过程中需要读取原始帧的完整像素数据计算目标像素位置,直接把输出写回原始Mat,会提前覆盖还未参与采样计算的原始像素,最终导致变换结果错乱,旋转角度越大,扭曲现象越明显。
修复方案
- 修正尺寸定义,保证宽高参数顺序正确:
cv::Size Ysize(info.width, info.height); cv::Size UVsize(info.width / 2, info.height / 2); // 中心坐标直接对应平面尺寸计算,避免硬编码出错 cv::Point2f Ycenter = cv::Point2f(Ysize.width / 2.0f, Ysize.height / 2.0f); cv::Point2f UVcenter = cv::Point2f(UVsize.width / 2.0f, UVsize.height / 2.0f); - 为
warpAffine准备独立的输出内存,不要直接在原始帧缓冲区上写入变换结果,变换完成后再将结果拷贝到待编码的缓冲区,保证运算过程中原始像素不会被提前覆盖。
内容的提问来源于stack exchange,提问作者mandeala
相关产品推荐
相关产品推荐

