最近在使用opencv做一些大数据量且快速的图像计算,程序需要经常遍历像素,所以我捡起以前的知识直接用Mat.data这个成员来做指针的操作。有时候会比它重载的运算符计算速度要快一些,而且在一些地方用的挺顺利的,直到我的结果图像出现了意想不到的问题。
过程
1.选取ROI图像 2.对ROI图像使用at()方法进行遍历,结果图像正确。 2.对ROI图像取Mat.data 进行指针遍历,图像出现问题。
原因
自从工作以来,使用opencv我一直都是能用就算,以实践为主,很少去深入研究深层原理。在这里我个人认为问题原因在于:选取的ROI取指针指向的内存是原图的内存位置,或者说ROI图像就是使用了原图的内存,原图并没有消失。 Mat.at()方法可以正确操作是因为此方法是基于生成ROI的索引来操作的,所以能取到目标的像素,而指针操作不正确则是由于它遍历的范围是原图的 p 到 p + N(其中uchar* p = Mat.data,N为像素数,或者说偏移量)的像素范围,所以会发生一个周期性的条纹现象。
简单画了一下图,大概是这个样子的: 图片我后面才想起来加,没有特意去测试,相当于我上面文字的图像表示吧。
解决
把ROI图像克隆到一个新的矩阵里,对这个新的矩阵进行操作。
最后
欢迎指正。
测试代码
#include <iostream>
#include <opencv2/opencv.hpp>
#ifdef _DEBUG
#pragma comment(lib, "opencv_world451d.lib")
#else
#pragma comment(lib, "opencv_world451.lib")
#endif
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat src = imread("bird.jpg", IMREAD_GRAYSCALE);
namedWindow("src", WINDOW_FREERATIO);
imshow("src", src);
waitKey(0);
Rect roi = Rect(500, 500, 2000, 2000);
Mat process = Mat(src, roi);
namedWindow("process", WINDOW_FREERATIO);
imshow("process", process);
waitKey(0);
Mat dst1 = Mat::zeros(2000, 2000, CV_8UC1);
Mat dst2 = Mat::zeros(2000, 2000, CV_8UC1);
Mat dst3 = Mat::zeros(2000, 2000, CV_8UC1);
for (int i = 0; i < 2000; i++)
{
for (int j = 0; j < 2000; j++)
{
dst1.at<uchar>(i, j) = process.at<uchar>(i, j);
}
}
uchar* p_src = process.data;
uchar* p_dst2 = dst2.data;
for (int i = 0; i < 2000 * 2000; i++)
{
*(p_dst2 + i) = *(p_src + i);
}
namedWindow("dst1", WINDOW_FREERATIO);
imshow("dst1", dst1);
namedWindow("dst2", WINDOW_FREERATIO);
imshow("dst2", dst2);
waitKey(0);
Mat process_clone = process.clone();
uchar* p_src2 = process_clone.data;
uchar* p_dst3 = dst3.data;
for (int i = 0; i < 2000 * 2000; i++)
{
*(p_dst3 + i) = *(p_src2 + i);
}
namedWindow("dst1", WINDOW_FREERATIO);
imshow("dst1", dst1);
namedWindow("dst2", WINDOW_FREERATIO);
imshow("dst2", dst2);
namedWindow("dst3", WINDOW_FREERATIO);
imshow("dst3", dst3);
waitKey(0);
return 0;
}
测试结果
原图
取ROI后的图像
三种赋值方法的结果
|