OpenCv Mat操作總結


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的基本數據結構

http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/mat%20-%20the%20basic%20image%20container/mat%20-%20the%20basic%20image%20container.html#matthebasicimagecontainer

 

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的數據元素

http://www.opencv.org.cn/opencvdoc/2.3.2/html/modules/core/doc/basic_structures.html?highlight=mat#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:

  • m – Destination matrix. If it does not have a proper size or type before the operation, it is reallocated.
  • rtype – Desired destination matrix type or, rather, the depth since the number of channels are the same as the source has. If rtype is negative, the destination matrix will have the same type as the source.
  • alpha – Optional scale factor.
  • beta – Optional delta added to the scaled values.

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:

  • i – A 0-based row index.

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);

 

和圖像處理相關的幾個函數

http://www.opencv.org.cn/opencvdoc/2.3.2/html/modules/core/doc/operations_on_arrays.html?highlight=split#cv.Split

 

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));

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM