Mat类的数据结构如下:
class CV_EXPORTS Mat
{
public:
// ... a lot of methods ...
...
/*! includes several bit-fields:
- the magic signature
- continuity flag
- depth
- number of channels
*/
int flags;
//! the array dimensionality, >= 2
int dims;
//! the number of rows and columns or (-1, -1) when the array has more than 2 dimensions
int rows, cols;
//! pointer to the data
uchar* data;
//! pointer to the reference counter;
// when array points to user-allocated data, the pointer is NULL
int* refcount;
// other members
...
};
关于Mat ,首先要知道的是你不必再手动地(1)为其开辟空间(2)在不需要时立即将空间释放。但手动地做还是可以的:大多数OpenCV函数仍会手动地为输出数据开辟空间。当传递一个已经存在的 Mat 对象时,开辟好的矩阵空间会被重用。也就是说,我们每次都使用大小正好的内存来完成任务。
基本上讲 Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不 同的维数)的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。因此,当在程序中传递图像并创建拷贝 时,大的开销是由矩阵造成的,而不是信息头。OpenCV是一个图像处理库,囊括了大量的图像处理函数,为了解决问题通常要使用库中的多个函数,因此在函 数中传递图像是家常便饭。同时不要忘了我们正在讨论的是计算量很大的图像处理算法,因此,除非万不得已,我们不应该拷贝 大 的图像,因为这会降低程序速度。
为了搞定这个问题,OpenCV使用引用计数机制。其思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。而拷贝构造函数则 只拷贝信息头和矩阵指针 ,而不拷贝矩阵。
1 2 3 4 5 6 | Mat A, C; // 只创建信息头部分 A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
Mat B(A); // 使用拷贝构造函数
C = A; // 赋值运算符 |
以上代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象。实际 上,不同的对象只是访问相同数据的不同途径而已。这里还要提及一个比较棒的功能:你可以创建只引用部分数据的信息头。比如想要创建一个感兴趣区域( ROI ),你只需要创建包含边界信息的信息头:
1 2 | Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries |
现在你也许会问,如果矩阵属于多个 Mat 对象,那么当不再需要它时谁来负责清理?简单的回答是:最后一个使用它的对象。通过引用计数机制来实现。无论什么时候有人拷贝了一个 Mat 对象的信息头,都会增加矩阵的引用次数;反之当一个头被释放之后,这个计数被减一;当计数值为零,矩阵会被清理。但某些时候你仍会想拷贝矩阵本身(不只是信息头和矩阵指针),这时可以使用函数 clone() 或者 copyTo() 。
1 2 3 | Mat F = A.clone(); Mat G; A.copyTo(G); |
现在改变 F 或者 G 就不会影响 Mat 信息头所指向的矩阵。总结一下,你需要记住的是
OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。
使用OpenCV的C++接口时不需要考虑内存释放问题。
赋值运算符和拷贝构造函数( ctor )只拷贝信息头。
使用函数 clone() 或者 copyTo() 来拷贝一副图像的矩阵。
Mat的初始化与构造
创建一个Mat的方法:
1 直接构造
Mat() 构造函数
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl;
OpenCV中对Mat里面depth,dims,channels,step,data,elemSize和数据地址计算的理解
http://tmjfzy.blog.163.com/blog/static/66447025201261052543349/
2 create
// create by using the create function()
M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << " " << M << endl << endl;
3 利用数组初始化
// create multidimensional matrices
//用二维数组初始化矩阵 double m[2][3] = { {1, 2, 3}, {4, 5, 6} }; Mat M = Mat(2, 3, CV_64F, m);
4 接收指针指向的数据流
void process_video_frame(const unsigned char* pixels,
int width, int height, int step)
{
Mat img(height, width, CV_8UC3, pixels, step);
GaussianBlur(img, img, Size(7,7), 1.5, 1.5);
}
5 MATLAB形式的初始化方式
// Create using MATLAB style eye, ones or zero matrix
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;
访问Mat的数据元素
1 指针高效访问
channel = 1
// compute sum of positive matrix elements
// (assuming that M isa double-precision matrix)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
const double* Mi = M.ptr<double>(i);
for(int j = 0; j < M.cols; j++)
sum += std::max(Mi[j], 0.);
}
channel = 3
int row = src.rows;
int col = src.cols;
Mat dst = Mat(row, col, CV_16UC3);
Mat showmask = Mat::zeros(row, col, CV_8UC1);
for (int i = 0; i < row; ++i)
{
ushort *dataWarpRow = dst.ptr<ushort>(i);
for (int j = 0; j < col; ++j)
{
//Vec3b *dataWarp = &(dst.at<Vec3b>(i, j));
ushort *dataWarpCol = dataWarpRow + j * src.channels();
uchar *mask = &(showmask.at<uchar>(i, j));
if ((dataWarpCol)[0] == 0 && (dataWarpCol)[1] == 0 && (dataWarpCol)[2] == 0)
{
*mask = 255;
}
}
}
2 迭代器安全访问
// compute sum of positive matrix elements, iterator-based variant
double sum=0;
MatConstIterator_<double> it = M.begin<double>(), it_end = M.end<double>();
for(; it != it_end; ++it)
sum += std::max(*it, 0.);
3 一维数据存储访问
// compute the sum of positive matrix elements, optimized variant
double sum=0;
int cols = M.cols, rows = M.rows;
if(M.isContinuous())
{
cols *= rows;
rows = 1;
}
const double* ptr = M.ptr<double>(0);
for(int i = 0; i < cols; i++)
{
sum += std::max(ptr[i], 0.);
}
4 二维单通道数据访问
Mat矩阵中数据指针Mat.data是uchar类型指针,CV_8U系列可以通过计算指针位置快速地定位矩阵中的任意元素。
二维单通道元素可以用Mat::at(i, j)访问,i是行序号,j是列序号。
M.at<double>(i,j) += 1.f
Mat H(100, 100, CV_64F);
for(int i = 0; i < H.rows; i++)
for(int j = 0; j < H.cols; j++)
H.at<double>(i,j)=1./(i+j+1);
但对于多通道的非unsigned char类型矩阵来说,以上方法都不好(注:后来知道可以通过类型转换,用指针访问data数据)。可以用Mat::ptr()来获得指向某行元素的指针,在通过行数与通道数计算相应点的指针。可以通过转换指针类型,访问非uchar类型的Mat元素。例如图像是CV_64FC1格式,可以将Mat.data指针直接转换成double*类型:
// imgMat is a image.
double* pimg = (double*)(imgMat.data)
也可以用C++中的显式转换符static_cast,不过要通过void*类型过渡:
void* pvoid = static_cast<void*>(imgMat.data);
double* pimg = static_cast<double*>(pvoid);
这种方式在Debug模式下速度提升非常显著,但没有任何的边界检查和异常处理,使用时必须十分小心。使用Mat::ptr的速度和直接使用这种方法差不多,多一层保护总比没有保护强。
5 访问彩色图像数据
#define IMG_B(img,y,x) img.at<Vec3b>(y,x)[0] #define IMG_G(img,y,x) img.at<Vec3b>(y,x)[1] #define IMG_R(img,y,x) img.at<Vec3b>(y,x)[2]
Mat的一些数学运算
This is a list of implemented matrix operations that can be combined in arbitrary complex expressions (here A, B stand for matrices ( Mat ), s for a scalar ( Scalar ), alpha for a real-valued scalar ( double )):
- Addition, subtraction, negation: A+B, A-B, A+s, A-s, s+A, s-A, -A
- Scaling: A*alpha
- Per-element multiplication and division: A.mul(B), A/B, alpha/A
- Matrix multiplication: A*B
- Transposition: A.t() (means AT)
- Matrix inversion and pseudo-inversion, solving linear systems and least-squares problems:
A.inv([method]) (~ A-1) , A.inv([method])*B (~ X: AX=B)
- Comparison: A cmpop B, A cmpop alpha, alpha cmpop A, where cmpop is one of : >, >=, ==, !=, <=, <. The result of comparison is an 8-bit single channel mask whose elements are set to 255 (if the particular element or pair of elements satisfy the condition) or 0.
- Bitwise logical operations: A logicop B, A logicop s, s logicop A, ~A, where logicop is one of : &, |, ^.
- Element-wise minimum and maximum: min(A, B), min(A, alpha), max(A, B), max(A, alpha)
- Element-wise absolute value: abs(A)
- Cross-product, dot-product: A.cross(B) A.dot(B)
- Any function of matrix or matrices and scalars that returns a matrix or a scalar, such as norm, mean, sum, countNonZero, trace, determinant, repeat, and others.
- Matrix initializers ( Mat::eye(), Mat::zeros(), Mat::ones() ), matrix comma-separated initializers, matrix constructors and operators that extract sub-matrices (see Mat description).
- Mat_<destination_type>() constructors to cast the result to the proper type.
Mat的一些重要属性和函数
Mat::rows
Mat::cols
Mat::convertTo
Converts an array to another datatype with optional scaling.
C++: void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 ) const
Parameters: |
|
Mat::type
Returns the type of a matrix element.
C++: int Mat::type() const
The method returns a matrix element type. This is an identifier compatible with the CvMat type system, like CV_16SC3 or 16-bit signed 3-channel array, and so on.
Mat::depth
Returns the depth of a matrix element.
C++: int Mat::depth() const
The method returns the identifier of the matrix element depth (the type of each individual channel). For example, for a 16-bit signed 3-channel array, the method returns CV_16S . A complete list of matrix types contains the following values:
- CV_8U - 8-bit unsigned integers ( 0..255 )
- CV_8S - 8-bit signed integers ( -128..127 )
- CV_16U - 16-bit unsigned integers ( 0..65535 )
- CV_16S - 16-bit signed integers ( -32768..32767 )
- CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
- CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
- CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )
Mat::channels
Returns the number of matrix channels.
C++: int Mat::channels() const
The method returns the number of matrix channels.
Mat::ptr
Returns a pointer to the specified matrix row.
C++: uchar* Mat::ptr(int i=0)
C++: const uchar* Mat::ptr(int i=0) const
C++: template<typename _Tp> _Tp* Mat::ptr(int i=0)
C++: template<typename _Tp> const _Tp* Mat::ptr(int i=0) const
Parameters: |
|
The methods return uchar* or typed pointer to the specified matrix row. See the sample in Mat::isContinuous() to know how to use these methods.
Mat Iplimage相互转换
//cv::Mat -> IplImage
Mat srcImg; // Mat type variable .
srcImg = imread("left1.png"); // read image;
IplImage *resIplPtr = NULL; // Initialize by NULL.
resIplPtr = &(IplImage(srcImg)); // Mat to IplImage Pointer
cvShowImage("resIplPtr" ,resIplPtr);
cvWaitKey(0);
//IplImage -> cv::Mat
IplImage* iplimg = cvLoadImage("left1.png");
cv::Mat matimg;
matimg = cv::Mat(iplimg);
namedWindow("mat",0);
imshow("mat",matimg);
waitKey(0);
和图像处理相关的几个函数
Merge
merge¶
Composes a multi-channel array from several single-channel arrays.
C++: void merge(const Mat* mv, size_t count, OutputArray dst)
C++: void merge(const vector<Mat>& mv, OutputArray dst)
Split
split
Divides a multi-channel array into several single-channel arrays.
C++: void split(const Mat& mtx, Mat* mv)
C++: void split(const Mat& mtx, vector<Mat>& mv)
获取图像的ROI
Mat img;
Mat RoiImg = img(Range(rbegin, rend), Range(cbeign, cend));