Author:: Maddock
Date: 2015-03-23 16:33:49
轉載請注明出處:http://blog.csdn.net/adong76/article/details/40539357
參考
http://blog.csdn.net/ljbkiss/article/details/7381208
http://blog.csdn.net/yang_xian521/article/details/7161335#comments
http://blog.csdn.net/guoming0000/article/details/8629885
http://hahack.com/wiki/opencv-basic.html
http://blog.skyoung.org/2014/03/26/OpenCV%28III%29-How-to-use-Mat/
Mat的基本數據結構
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*類型:
這種方式在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));
