QVector<quint16>转cv::Mat再转QImage奇数尺寸裁剪出现斜线问题
故障根本原因
这个问题是OpenCV的cv::Mat步长对齐规则与QImage对16位灰度格式的步长计算逻辑不匹配导致的,具体触发逻辑如下:
- cv::Mat默认按4字节对齐分配内存,每行的实际字节数(即
step[0])会向上取整到4的倍数。你使用rowRange/colRange裁剪得到的是原始图像的子视图,不会重新分配内存,所以裁剪后的子矩阵的step仍和原始大矩阵的step一致,不会跟随裁剪后的宽度变化。 - 部分Qt版本在处理
QImage::Format_Grayscale16格式时,会忽略构造函数传入的自定义步长参数,自行按「宽度×单像素字节数」(或仅按2字节对齐规则)计算行步长,不会做4字节对齐。 - 当你裁剪为399像素的奇数宽度时:
- 16位灰度单像素占2字节,一行有效数据为
399*2=798字节 - cv::Mat的
step按4字节对齐为800字节 - Qt自行计算的步长为798字节,两者差2字节(恰好1个16位像素的大小)
- 这就导致QImage读取第二行数据时,会从cv::Mat第一行有效数据的末尾开始读,刚好拿到你裁剪掉的第一行第400个像素,后续每行叠加这个偏移,最终形成斜线。
- 16位灰度单像素占2字节,一行有效数据为
- 当你裁剪为400像素的偶数宽度时:
- 一行有效数据为
400*2=800字节,刚好是4的倍数 - cv::Mat的
step和Qt计算的步长完全一致,因此不会出现错位。
- 一行有效数据为
验证方法
打印裁剪后outImage.step[0]和outImage.cols*2的数值,两者在裁剪奇数宽度时会存在2字节的差值,即可验证上述判断。
修复方案
不要直接用子矩阵的内存构造QImage,裁剪后先把子矩阵拷贝到连续内存的新Mat中,再做格式转换:
/* 裁剪后新增clone操作,生成连续内存的新矩阵 */ cv::Mat outImage = image.rowRange(0, 399).colRange(0, 399).clone();
如果仍有兼容问题,可以手动按行拷贝数据到QImage,彻底规避步长不匹配的问题:
cv::Mat cropped = image.rowRange(0, 399).colRange(0, 399); QImage image(cropped.cols, cropped.rows, QImage::Format_Grayscale16); for(int row = 0; row < cropped.rows; row++){ memcpy(image.scanLine(row), cropped.ptr(row), cropped.cols * 2); }
内容的提问来源于stack exchange,提问作者keven parent
相关产品推荐
相关产品推荐

