Opencv:通过Mat遍历图像的5种方法
1. 图片在内存的存储形式
- 灰度图(单通道):
- RGB图(3通道):
2. Mat遍历的几种方法
分别是直接地址遍历访问连续空间(图片存储空间是连续的)、直接地址访问不连续空间、ptr<>()模板函数、at接口、以及迭代器,完整C++代码如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
void PrintMs(const char * text = "") {
//定时器
static long long last = 0;
long long cur = getTickCount();
if (last == 0) {
last = cur;
return;
}
long long ms = 0;
ms = ((double)(cur - last) / getTickFrequency()) * 1000;
if (* text != 0) {
cout <<text<<":" << ms <<"ms"<<endl;
}
last = getTickCount();
}
void main() {
Mat mat;
mat.create(3000, 4000, CV_8UC3); //创建Mat对象,3000*4000的RGB图像
//直接地址遍历访问连续空间
int es = mat.elemSize();
int size = mat.rows * mat.cols * es;
PrintMs();
for (int i = 0; i < size; i+=es)
{
mat.data[i] = 255; //B
mat.data[i+1] = 0; //G
mat.data[i+2] = 0; //R
}
PrintMs("mat.data ms");
//直接地址访问不连续空间
for (int i = 0; i < mat.rows; i++)
{
for (int j = 0; j < mat.cols; j++)
{
(&mat.data[i * mat.step])[j * es] = 0; // B
(&mat.data[i * mat.step])[j * es +1] = 0; // G
(&mat.data[i * mat.step])[j * es +2] = 255; // R
}
}
PrintMs("mat.step ms");
//使用ptr模板函数遍历
for (int row = 0; row < mat.rows; row++)
{
for (int col = 0; col < mat.cols; col++)
{
Vec3b* m = mat.ptr<Vec3b>(row, col);
m->val[0] = 0; //B
m->val[1] = 255; //G
m->val[2] = 0; //R
}
}
PrintMs("mat.ptr ms");
//使用at接口遍历
for (int row = 0; row < mat.rows; row++)
{
for (int col = 0; col < mat.cols; col++)
{
mat.at<Vec3b>(row, col)[0] = 128; //B
mat.at<Vec3b>(row, col)[1] = 128; //G
mat.at<Vec3b>(row, col)[2] = 128; //R
}
}
PrintMs("mat.at ms");
//通过迭代器遍历
auto it = mat.begin<Vec3b>();
auto it_end = mat.end<Vec3b>();
for(;it!=it_end; it++)
{
(*it).val[0] = 0; //B
(*it).val[1] = 222; //G
(*it).val[2] = 0; //R
}
PrintMs("mat iterator ms");
namedWindow("mat");
imshow("mat",mat);
waitKey(0);
}
3. 时间开销
- 3000*4000的RGB图像Mat遍历时间开销:
mat.data ms:18ms
mat.step ms:24ms
mat.ptr ms:19ms
mat.at ms:26ms
mat iterator ms:28ms
- 10000*10000的RGB图像Mat遍历时间开销:
mat.data ms:166ms
mat.step ms:343ms
mat.ptr ms:236ms
mat.at ms:360ms
mat iterator ms:416ms
结论:直接地址访问连续地址>ptr>直接地址访问不连续地址>at>迭代器
- 直接地址访问连续地址:速度最快,但是需要图片存储空间连续。
- ptr:进行图像遍历更加高效,推荐使用。
- 直接地址访问不连续地址:代码复杂,速度也不太行。
- at:不适合用于像素遍历,速度太慢了,比较适合随机访问的方式。
- 迭代器:不需要知道图像的行列大小,但速度最慢。