1:Mat 构造函数
You can do this using the << operator of Mat. Be aware that this only works for two dimensional matrices.
* Mat() Constructor
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl;
* Use C/C++ arrays and initialize via constructor
int sz[3] = {2,2,2};
Mat L(3,sz, CV_8UC(1), Scalar::all(0));
The upper example shows how to create a matrix with more than two dimensions. Specify its dimension, thenpass a pointer containing the size for each dimension and the rest remains the same.
//创建了一个3维的矩阵,具体的是2X2X2的矩阵,类型和值和默认构造函数的相同。
* Create a header for an already existing IplImage pointer:
IplImage* img = cvLoadImage("greatwave.png", 1);
Mat mtx(img); // convert IplImage* -> Mat
* Create() function:
M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << " " << M << endl << endl;
* MATLAB style initializer: zeros(), ones(), eye(). Specify size and data type to use:
Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;
Mat O = Mat::ones(2, 2, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;
Mat Z = Mat::zeros(3,3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
* For small matrices you may use comma separated initializers:
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
*Create a new header for an existing Mat object and clone() or copyTo() it.
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;
* Note: You can fill out a matrix with random values using the randu() function. You need to give the lower and upper
value for the random values:
Mat R = Mat(3, 2, CV_8UC3);
randu(R, Scalar::all(0), Scalar::all(255));
还有一种快速初始化数据的办法,如下:
- double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
- Mat M = Mat(3, 3, CV_64F, m).inv();
2:数据访问方式 Mat mat ;float* lambda = mat.ptr<float>(i)
得到指针 ,访问数据的方式是:lambda[0] , lambda[1]等.
访问图像中的像素
高效的方法:C操作符[ ]
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
int channels = I.channels();
int nRows = I.rows ;
int nCols = I.cols* channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
return I;
}
安全的方法:迭代器iterator
相比用指针直接访问可能出现越界问题,迭代器绝对是非常安全的方法:
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
const int channels = I.channels();
switch(channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
*it = table[*it];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = table[(*it)[0]];
(*it)[1] = table[(*it)[1]];
(*it)[2] = table[(*it)[2]];
}
}
}
return I;
}
这里我们只定义了一个迭代器,用了一个for循环,这是因为在OpenCV里迭代器会访问每一列然后自动跳到下一行,不用管在内存上是否isContinous。另外要注意的是在三通道图像中我们定义的是 <Vec3b>格式的迭代器,如果定义成uchar,则只能访问到B即蓝色通道的值。
这种方式虽然安全,但是挺慢的,一会儿就知道了。
更慢的方法:动态地址计算
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
const int channels = I.channels();
switch(channels)
{
case 1:
{
for( int i = 0; i < I.rows; ++i)
for( int j = 0; j < I.cols; ++j )
I.at<uchar>(i,j) = table[I.at<uchar>(i,j)];
break;
}
case 3:
{
Mat_<Vec3b> _I = I;
for( int i = 0; i < I.rows; ++i)
for( int j = 0; j < I.cols; ++j )
{
_I(i,j)[0] = table[_I(i,j)[0]];
_I(i,j)[1] = table[_I(i,j)[1]];
_I(i,j)[2] = table[_I(i,j)[2]];
}
I = _I;
break;
}
}
return I;
}
我这里测试了三种操作Mat数据的办法,套用流行词,普通青年,文艺青年,为啥第三种我不叫2b青年,大家慢慢往后看咯。
普通青年的操作的办法通常是M.at<float>(i, j)
文艺青年一般会走路线M.ptr<float>( i )[ j ]
暴力青年通常直接强制使用我第40讲提到的M.data这个指针
实验代码如下:
- t = (double)getTickCount();
- Mat img1(1000, 1000, CV_32F);
- for (int i=0; i<1000; i++)
- {
- for (int j=0; j<1000; j++)
- {
- img1.at<float>(i,j) = 3.2f;
- }
- }
- t = (double)getTickCount() - t;
- printf("in %gms\n", t*1000/getTickFrequency());
- //***************************************************************
- t = (double)getTickCount();
- Mat img2(1000, 1000, CV_32F);
- for (int i=0; i<1000; i++)
- {
- for (int j=0; j<1000; j++)
- {
- img2.ptr<float>(i)[j] = 3.2f;
- }
- }
- t = (double)getTickCount() - t;
- printf("in %gms\n", t*1000/getTickFrequency());
- //***************************************************************
- t = (double)getTickCount();
- Mat img3(1000, 1000, CV_32F);
- float* pData = (float*)img3.data;
- for (int i=0; i<1000; i++)
- {
- for (int j=0; j<1000; j++)
- {
- *(pData) = 3.2f;
- pData++;
- }
- }
- t = (double)getTickCount() - t;
- printf("in %gms\n", t*1000/getTickFrequency());
- //***************************************************************
- t = (double)getTickCount();
- Mat img4(1000, 1000, CV_32F);
- for (int i=0; i<1000; i++)
- {
- for (int j=0; j<1000; j++)
- {
- ((float*)img3.data)[i*1000+j] = 3.2f;
- }
- }
- t = (double)getTickCount() - t;
- printf("in %gms\n", t*1000/getTickFrequency());
最后两招可以都看成是暴力青年的方法,因为反正都是指针的操作,局限了各暴力青年手段就不显得暴力了。
在Debug、Release模式下的测试结果分别为:
Debug | Release | |
普通青年 | 139.06ms | 2.51ms |
文艺青年 | 66.28ms | 2.50ms |
暴力青年1 | 4.95ms | 2.28ms |
暴力青年2 | 5.11ms | 1.37ms |
根据测试结果,我觉得箫铭说的是很可信的,普通青年的操作在Debug模式下果然缓慢,他推荐的文艺青年的路线确实有提高。值得注意的是本来后两种办法确实是一种比较2b青年的做法,因为at操作符或者ptr操作符,其实都是有内存检查的,防止操作越界的,而直接使用data这个指针确实很危险。不过从速度上确实让人眼前一亮,所以我不敢称这样的青年为2b,尊称为暴力青年吧。
不过在Release版本下,几种办法的速度差别就不明显啦,都是很普通的青年。所以如果大家最后发行程序的时候,可以不在意这几种操作办法的,推荐前两种哦,都是很好的写法,操作指针的事还是留给大神们用吧。就到这里吧~~
补充:箫铭又推荐了两种文艺青年的处理方案,我也随便测试了一下,先贴代码,再贴测试结果:
- /*********加强版********/
- t = (double)getTickCount();
- Mat img5(1000, 1000, CV_32F);
- float *pData1;
- for (int i=0; i<1000; i++)
- {
- pData1=img5.ptr<float>(i);
- for (int j=0; j<1000; j++)
- {
- pData1[j] = 3.2f;
- }
- }
- t = (double)getTickCount() - t;
- printf("in %gms\n", t*1000/getTickFrequency());
- /*******终极版*****/
- t = (double)getTickCount();
- Mat img6(1000, 1000, CV_32F);
- float *pData2;
- Size size=img6.size();
- if(img2.isContinuous())
- {
- size.width = size.width*size.height;
- size.height = 1;
- }
- size.width*=img2.channels();
- for(int i=0; i<size.height; i++)
- {
- pData2 = img6.ptr<float>(i);
- for(int j=0; j<size.width; j++)
- {
- pData2[j] = saturate_cast<float>(3.2f);
- }
- }
- t = (double)getTickCount() - t;
- printf("in %gms\n", t*1000/getTickFrequency());
测试结果:
Debug | Release | |
加强版文艺青年 | 5.74ms | 2.43ms |
终极版文艺青年 | 40.12ms | 2.34ms |