OpenCV保存CV_16U灰度视频丢失右半部分问题求助
回答
核心问题分析
画面右半部分丢失的直接原因是16位单通道(CV_16U)图像的内存步长(step)与VideoWriter的读取逻辑不匹配。你直接用aImage.data()构造cv::Mat时,如果原数据的行字节数不是mWidth * 2(CV_16U每个像素占2字节),会导致Mat的内存布局错位,VideoWriter读取时会把后续行的错误数据填充到当前行,最终出现画面截断。
日志显示FFmpeg插件加载正常,排除后端初始化故障;画质差是MJPEG有损压缩的特性,属于次要问题。
解决方法
方法1:修复Mat的内存布局(保留16位信息)
构造Mat时明确指定行步长,或者通过clone()创建内存连续的Mat,确保每一行的字节数符合VideoWriter的预期:
// 方式1:明确指定行步长(假设原数据每行字节数为mWidth*2) const cv::Mat imgDisplay16(mHeight, mWidth, CV_16U, aImage.data(), mWidth * 2); // 方式2:克隆Mat以强制内存连续对齐 const cv::Mat imgDisplay16 = cv::Mat(mHeight, mWidth, CV_16U, aImage.data()).clone(); // 初始化VideoWriter后务必检查是否成功打开 mWriter = std::make_unique<cv::VideoWriter>( "file.avi", cv::VideoWriter::fourcc('M','J','P','G'), 10, imgDisplay16.size(), false // 单通道必须设为false ); if (mWriter->isOpened()) { mWriter->write(imgDisplay16); // 记得在完成写入后释放资源 mWriter.reset(); }
方法2:转换为8位单通道写入(兼容多数编码,改善画质)
如果不需要保留16位的高动态范围,可以将图像归一化到8位后写入,同时MJPEG对8位图像的压缩效果更稳定,画质问题也会缓解:
cv::Mat img8U; // 将16位图像归一化到0-255的8位范围 cv::normalize(imgDisplay16, img8U, 0, 255, cv::NORM_MINMAX, CV_8U); mWriter = std::make_unique<cv::VideoWriter>( "file.avi", cv::VideoWriter::fourcc('M','J','P','G'), 10, img8U.size(), false ); if (mWriter->isOpened()) { mWriter->write(img8U); mWriter.reset(); }
额外建议
- 若追求更好的画质,可尝试无损或低损编码,比如
cv::VideoWriter::fourcc('F','L','V','1')或cv::VideoWriter::fourcc('A','V','C','1')(需FFmpeg支持); - 始终通过
isOpened()验证VideoWriter的初始化状态,避免无效写入操作; - 若原数据的行步长确实不等于
mWidth*2,必须在构造Mat时明确传入正确的步长参数,否则内存布局错误会持续存在。
内容的提问来源于stack exchange,提问作者David Levy
相关产品推荐
相关产品推荐

