【opencv4.3.0教程】03之Mat類詳解

目錄

一、前言

二、Mat定義

1、你眼中的世界VS電腦眼中的世界

2、Mat是個啥

3、Mat代碼定義

三、常用成員及方法介紹

1、常用成員

1.flags

2.dims

3.rows&cols

4.data

2、構造函數

1.基本:尺寸 + 類型

2.基本 + 顏色

3.基本+數據

4.Mat+其他

3、其他方法

1.行與列

2.拷貝與轉換

3.基本運算

4.生成圖像矩陣

5.圖像基本信息

四、Mat使用

1、Mat創建對象

1.聲明對象

2.構造函數生成對象

3.拷貝生成對象

2、Mat對象的簡單使用

3、Mat注意事項

五、Mat與CVMat,IplImage

1、爲何引入Mat

2、類型轉換


一、前言

上一篇文章,我們講了OpenCV的基本使用,前面的基本使用,也是我們以後每次課程幾乎都會涉及到的。想要真正掌握,其實就一個辦法:多練!

從這節課開始,我們進入OpenCV的核心模塊,來了解OpenCV中最基礎,最核心的東西。這節課將帶領大家一起來了解OpenCV最基本的結構Mat類。接下來就跟我一起走進Mat的世界吧!

二、Mat定義

1、你眼中的世界VS電腦眼中的世界

我們眼中的世界是什麼樣的呢?

我想,可能是這樣:

​可能是這樣:

​還可能是這樣:

​但是在計算機的眼中,這個世界是什麼樣的呢?

是這個樣子的

​ 到了計算機中都是一堆數字,這些數字就是圖像的像素,上面這個圖是一個灰度圖像,數組代表像素值,0表示純黑,255表示純白,數字越大,顏色越淺。

2、Mat是個啥

上面我們瞭解到在計算機中,他們的世界就是一堆數字,那他們怎麼存儲呢?在OpenCV中,我們使用一個類Mat來存放圖像,我們怎麼理解容器呢?

大家已經學到這裏,我想大家應該至少了解一門其他編程語言,大家都應該學過變量類型,變量類型可以用來聲明一個變量。類也是如此,類是一組具有共同特徵的物體的抽象表達。它有比變量更加豐富多樣化的功能,類生成的實例我們稱之爲對象。類不僅有自己的數據,還有自己的方法。Mat類負責在OpenCV中聲明一個圖像類型的變量。用來存放圖像數據。

3、Mat代碼定義

在OpenCV中,Mat類的定義如下:

class CV_EXPORTS Mat
{
public:    
    Mat();
    
    Mat(int rows, int cols, int type);
    
    Mat(Size size, int type);
    
    Mat(int rows, int cols, int type, const Scalar& s);
    
    Mat(Size size, int type, const Scalar& s);
    
    Mat(int ndims, const int* sizes, int type);
    
    Mat(const std::vector<int>& sizes, int type);
    
    Mat(int ndims, const int* sizes, int type, const Scalar& s);
   
    Mat(const std::vector<int>& sizes, int type, const Scalar& s);

    Mat(const Mat& m);
    
    Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);
    
    Mat(Size size, int type, void* data, size_t step=AUTO_STEP);

    Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0);

    Mat(const std::vector<int>& sizes, int type, void* data, const size_t* steps=0);

    Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all());

    Mat(const Mat& m, const Rect& roi);

    Mat(const Mat& m, const Range* ranges);

    Mat(const Mat& m, const std::vector<Range>& ranges);

    template<typename _Tp> explicit Mat(const std::vector<_Tp>& vec, bool copyData=false);

    template<typename _Tp, typename = typename std::enable_if<std::is_arithmetic<_Tp>::value>::type>
    explicit Mat(const std::initializer_list<_Tp> list);
   
    template<typename _Tp> explicit Mat(const std::initializer_list<int> sizes, const std::initializer_list<_Tp> list);
    
    template<typename _Tp, size_t _Nm> explicit Mat(const std::array<_Tp, _Nm>& arr, bool copyData=false);

    template<typename _Tp, int n> explicit Mat(const Vec<_Tp, n>& vec, bool copyData=true);

    template<typename _Tp, int m, int n> explicit Mat(const Matx<_Tp, m, n>& mtx, bool copyData=true);

    template<typename _Tp> explicit Mat(const Point_<_Tp>& pt, bool copyData=true);

    template<typename _Tp> explicit Mat(const Point3_<_Tp>& pt, bool copyData=true);

    template<typename _Tp> explicit Mat(const MatCommaInitializer_<_Tp>& commaInitializer);

    //! download data from GpuMat
    explicit Mat(const cuda::GpuMat& m);

    //! destructor - calls release()
    ~Mat();
   
    Mat& operator = (const Mat& m);

    Mat& operator = (const MatExpr& expr);

    //! retrieve UMat from Mat
    UMat getUMat(AccessFlag accessFlags, UMatUsageFlags usageFlags = USAGE_DEFAULT) const;

    Mat row(int y) const;

    Mat col(int x) const;

    Mat rowRange(int startrow, int endrow) const;

    Mat rowRange(const Range& r) const;

    Mat colRange(int startcol, int endcol) const;

    Mat colRange(const Range& r) const;

    Mat diag(int d=0) const;

    static Mat diag(const Mat& d);

    Mat clone() const CV_NODISCARD;

    void copyTo( OutputArray m ) const;

    void copyTo( OutputArray m, InputArray mask ) const;

    void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;

    void assignTo( Mat& m, int type=-1 ) const;

    Mat& operator = (const Scalar& s);
 
    Mat& setTo(InputArray value, InputArray mask=noArray());

    Mat reshape(int cn, int rows=0) const;

    /** @overload */
    Mat reshape(int cn, int newndims, const int* newsz) const;

    /** @overload */
    Mat reshape(int cn, const std::vector<int>& newshape) const;
   
    MatExpr t() const;

    MatExpr inv(int method=DECOMP_LU) const;

    MatExpr mul(InputArray m, double scale=1) const;

    Mat cross(InputArray m) const;

    double dot(InputArray m) const;

    static MatExpr zeros(int rows, int cols, int type);

    static MatExpr zeros(Size size, int type);

    static MatExpr zeros(int ndims, const int* sz, int type);

    static MatExpr ones(int rows, int cols, int type);

    static MatExpr ones(Size size, int type);

    static MatExpr ones(int ndims, const int* sz, int type);

    static MatExpr eye(int rows, int cols, int type);

    static MatExpr eye(Size size, int type);

    void create(int rows, int cols, int type);

    void create(Size size, int type);

    void create(int ndims, const int* sizes, int type);

    void create(const std::vector<int>& sizes, int type);

    void addref();

    void release();

    //! internal use function, consider to use 'release' method instead; deallocates the matrix data
    void deallocate();
    //! internal use function; properly re-allocates _size, _step arrays
    void copySize(const Mat& m);

    void reserve(size_t sz);

    void reserveBuffer(size_t sz);

    void resize(size_t sz);

    void resize(size_t sz, const Scalar& s);

    //! internal function
    void push_back_(const void* elem);

    template<typename _Tp> void push_back(const _Tp& elem);

    template<typename _Tp> void push_back(const Mat_<_Tp>& elem);

    template<typename _Tp> void push_back(const std::vector<_Tp>& elem);

    void push_back(const Mat& m);

    void pop_back(size_t nelems=1);

    void locateROI( Size& wholeSize, Point& ofs ) const;

    Mat& adjustROI( int dtop, int dbottom, int dleft, int dright );

    Mat operator()( Range rowRange, Range colRange ) const;

    Mat operator()( const Rect& roi ) const;

    Mat operator()( const Range* ranges ) const;

    Mat operator()(const std::vector<Range>& ranges) const;

    template<typename _Tp> operator std::vector<_Tp>() const;
    template<typename _Tp, int n> operator Vec<_Tp, n>() const;
    template<typename _Tp, int m, int n> operator Matx<_Tp, m, n>() const;

    template<typename _Tp, std::size_t _Nm> operator std::array<_Tp, _Nm>() const;

    bool isContinuous() const;

    //! returns true if the matrix is a submatrix of another matrix
    bool isSubmatrix() const;

    size_t elemSize() const;

    size_t elemSize1() const;

    int type() const;

    int depth() const;

    int channels() const;

    size_t step1(int i=0) const;

    bool empty() const;

    size_t total() const;

    size_t total(int startDim, int endDim=INT_MAX) const;

    int checkVector(int elemChannels, int depth=-1, bool requireContinuous=true) const;

    uchar* ptr(int i0=0);
    /** @overload */
    const uchar* ptr(int i0=0) const;

    uchar* ptr(int row, int col);

    const uchar* ptr(int row, int col) const;

    /** @overload */
    uchar* ptr(int i0, int i1, int i2);
    /** @overload */
    const uchar* ptr(int i0, int i1, int i2) const;

    /** @overload */
    uchar* ptr(const int* idx);
    /** @overload */
    const uchar* ptr(const int* idx) const;
    /** @overload */
    template<int n> uchar* ptr(const Vec<int, n>& idx);
    /** @overload */
    template<int n> const uchar* ptr(const Vec<int, n>& idx) const;
    /** @overload */
    template<typename _Tp> _Tp* ptr(int i0=0);
    /** @overload */
    template<typename _Tp> const _Tp* ptr(int i0=0) const;
    template<typename _Tp> _Tp* ptr(int row, int col);
    template<typename _Tp> const _Tp* ptr(int row, int col) const;
    /** @overload */
    template<typename _Tp> _Tp* ptr(int i0, int i1, int i2);
    /** @overload */
    template<typename _Tp> const _Tp* ptr(int i0, int i1, int i2) const;
    /** @overload */
    template<typename _Tp> _Tp* ptr(const int* idx);
    /** @overload */
    template<typename _Tp> const _Tp* ptr(const int* idx) const;
    /** @overload */
    template<typename _Tp, int n> _Tp* ptr(const Vec<int, n>& idx);
    /** @overload */
    template<typename _Tp, int n> const _Tp* ptr(const Vec<int, n>& idx) const;
    template<typename _Tp> _Tp& at(int i0=0);
    template<typename _Tp> const _Tp& at(int i0=0) const;

    template<typename _Tp> _Tp& at(int row, int col);

    template<typename _Tp> const _Tp& at(int row, int col) const;

    template<typename _Tp> _Tp& at(int i0, int i1, int i2);

    template<typename _Tp> const _Tp& at(int i0, int i1, int i2) const;

    template<typename _Tp> _Tp& at(const int* idx);

    template<typename _Tp> const _Tp& at(const int* idx) const;

    /** @overload */
    template<typename _Tp, int n> _Tp& at(const Vec<int, n>& idx);
    /** @overload */
    template<typename _Tp, int n> const _Tp& at(const Vec<int, n>& idx) const;

    template<typename _Tp> _Tp& at(Point pt);

    template<typename _Tp> const _Tp& at(Point pt) const;

    template<typename _Tp> MatIterator_<_Tp> begin();
    template<typename _Tp> MatConstIterator_<_Tp> begin() const;

    template<typename _Tp> MatIterator_<_Tp> end();
    template<typename _Tp> MatConstIterator_<_Tp> end() const;

    template<typename _Tp, typename Functor> void forEach(const Functor& operation);
    /** @overload */
    template<typename _Tp, typename Functor> void forEach(const Functor& operation) const;

    Mat(Mat&& m);
    Mat& operator = (Mat&& m);

    enum { MAGIC_VAL  = 0x42FF0000, AUTO_STEP = 0, CONTINUOUS_FLAG = CV_MAT_CONT_FLAG, SUBMATRIX_FLAG = CV_SUBMAT_FLAG };
    enum { MAGIC_MASK = 0xFFFF0000, TYPE_MASK = 0x00000FFF, DEPTH_MASK = 7 };

    int flags;
    //! the matrix dimensionality, >= 2
    int dims;
    //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
    int rows, cols;
    //! pointer to the data
    uchar* data;

    //! helper fields used in locateROI and adjustROI
    const uchar* datastart;
    const uchar* dataend;
    const uchar* datalimit;

    //! custom allocator
    MatAllocator* allocator;
    //! and the standard allocator
    static MatAllocator* getStdAllocator();
    static MatAllocator* getDefaultAllocator();
    static void setDefaultAllocator(MatAllocator* allocator);

    //! internal use method: updates the continuity flag
    void updateContinuityFlag();

    //! interaction with UMat
    UMatData* u;

    MatSize size;
    MatStep step;

protected:
    template<typename _Tp, typename Functor> void forEach_impl(const Functor& operation);
};

着實有點多,接下來我們把最常用的分類給大家詳細講解,讓大家有個系統認識

三、常用成員及方法介紹

上面那個是整體,確實太多啦,所以在這裏,給大家講解一下常用的成員及方法。

1、常用成員

第一個要說的就是常用成員,主要有如下幾個:

class CV_EXPORTS Mat
{
public: 
   
    /*flags指定圖像的顏色空間  
      flags > 0 3通道的彩色圖像
      flags = 0 灰度圖像
      flags < 0 不作改變
    */
    int flags;
         
    /*數據的維數*/
    int dims;
    
    /*行和列的數量;數組超過2維時爲(-1,-1)*/
    int rows, cols; 
   
    uchar* data; // 圖像數據

};

接下來我們詳細說一下:

1.flags

flags用於指定圖像的顏色空間,flags有三個類型的取值:

如果flags>0,那麼圖像就是三通道的彩色圖像;

如果flags=0,那麼圖像就是灰度圖像;

如果flags<0,那麼不做改變;

我們通過幾個代碼來看一下效果:

        Mat src;
	src = imread("./image/cat.jpg");
	
	imshow("src", src);
	cout << "src.flags = " << src.flags << endl;

	src.flags = 0;
	imshow("src.flags = 0", src);

我們讀取一張圖像之後,顯示一下圖像,並查看該圖像的flags:

src.flags = 1124024336

如果我們將圖像的flags設爲0,那麼圖像變爲:

​大家也可以自己嘗試一下如果設爲其他正值,圖像會是什麼樣子,我取值爲1,得到的如下:

​如果設爲負值,就會報異常。

2.dims

dims表示的是數據的維數,因爲我們做的是二維圖像,所以我們的維數是2,如果我們創建一個三維的數據,那我們得到的就是三維:

        Mat src;
	src = imread("./image/cat.jpg");
	cout << "src.flags = " << src.flags << endl;
	cout << "src.dims = " << src.dims << endl;

上面我們是獲取一個二維圖像,那我們得到的結果應該是2:

如果我們創建一個三維的數據,那我們看一下效果。在這裏,我們要使用Mat的構造方法,不用擔心,後面我們會講到常用的構造方法:

        Mat src;
	src = imread("./image/cat.jpg");
	cout << "src.flags = " << src.flags << endl;
	cout << "src.dims = " << src.dims << endl;

上面我們是生成了一個三維圖像,那我們得到的結果應該是3。

 

3.rows&cols

這兩個就比較簡單了,得到的是圖像的行和列。而且大家不要記混了:

rows是行,cols是列。

這樣,我們每次加載一個圖像,就能清楚知道它的尺寸啦!代碼如下:

        Mat src;
	src = imread("./image/cat.jpg");
	cout << "src.rows = " << src.rows << endl;
	cout << "src.cols = " << src.cols << endl;

結果如下:

 

4.data

 uchar類型的指針,指向Mat數據矩陣的首地址。這個指的是圖像數據,在輸出過程中,如果我們想輸出其對應的值,我們需要使用強制類型轉換爲int類型,代碼如下:(爲了方便查看輸出結果,我們截取圖像的某一部分)

	Mat src;
	src = imread("./image/cat.jpg");
	
	if (!src.data)
	{
		cout << "ERROR : could not load image.\n";
		waitKey(0);
		return -1;
	}
	src = Mat(src, Rect(280, 100, 20, 20));

	cout << "src.rows = " << src.rows << endl;
	cout << "src.cols = " << src.cols << endl;
	cout << "src.data = " << endl;
	for (int i = 0; i < src.cols*src.rows; i++) {
		cout << (int)src.data[i] << " ";
		if (i%src.rows == src.rows - 1) cout << endl;
	}

結果如下:

 

2、構造函數

Mat有很多構造函數,下面是Mat定義中的構造函數和析構函數:

class CV_EXPORTS Mat
{
public:    
    Mat();
    
    Mat(int rows, int cols, int type);
    
    Mat(Size size, int type);
    
    Mat(int rows, int cols, int type, const Scalar& s);
    
    Mat(Size size, int type, const Scalar& s);
    
    Mat(int ndims, const int* sizes, int type);
    
    Mat(const std::vector<int>& sizes, int type);
    
    Mat(int ndims, const int* sizes, int type, const Scalar& s);
   
    Mat(const std::vector<int>& sizes, int type, const Scalar& s);

    Mat(const Mat& m);
    
    Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);
    
    Mat(Size size, int type, void* data, size_t step=AUTO_STEP);

    Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0);

    Mat(const std::vector<int>& sizes, int type, void* data, const size_t* steps=0);

    Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all());

    Mat(const Mat& m, const Rect& roi);

    Mat(const Mat& m, const Range* ranges);

    Mat(const Mat& m, const std::vector<Range>& ranges);

    template<typename _Tp> explicit Mat(const std::vector<_Tp>& vec, bool copyData=false);

    template<typename _Tp, typename = typename std::enable_if<std::is_arithmetic<_Tp>::value>::type>
    explicit Mat(const std::initializer_list<_Tp> list);
   
    template<typename _Tp> explicit Mat(const std::initializer_list<int> sizes, const std::initializer_list<_Tp> list);
    
    template<typename _Tp, size_t _Nm> explicit Mat(const std::array<_Tp, _Nm>& arr, bool copyData=false);

    template<typename _Tp, int n> explicit Mat(const Vec<_Tp, n>& vec, bool copyData=true);

    template<typename _Tp, int m, int n> explicit Mat(const Matx<_Tp, m, n>& mtx, bool copyData=true);

    template<typename _Tp> explicit Mat(const Point_<_Tp>& pt, bool copyData=true);

    template<typename _Tp> explicit Mat(const Point3_<_Tp>& pt, bool copyData=true);

    template<typename _Tp> explicit Mat(const MatCommaInitializer_<_Tp>& commaInitializer);

    //! download data from GpuMat
    explicit Mat(const cuda::GpuMat& m);

    //! destructor - calls release()
    ~Mat();

};

但其實我們一般能用到的並不多,主要有如下幾類:

1.基本:尺寸 + 類型

第一種類型就是尺寸 + 類型,尺寸有的通過一個參數定義,比如:

    Mat(Size size, int type);    

有的通過多個參數定義,比如:

    Mat(int rows, int cols, int type);

類型定義如下:

    int type;

所以這一類型主要有:

class CV_EXPORTS Mat
{
public:    

    Mat(int rows, int cols, int type);
    
    Mat(Size size, int type);
    
    Mat(int ndims, const int* sizes, int type);
    
    Mat(const std::vector<int>& sizes, int type);
    
};

2.基本 + 顏色

第二種類型在基本類型的基礎上增加了顏色,也就是尺寸 + 類型 + 顏色

其中表示顏色的是:

    const Scalar& s;

這一類型主要有:

class CV_EXPORTS Mat
{
public: 
    
    Mat(int rows, int cols, int type, const Scalar& s);
    
    Mat(Size size, int type, const Scalar& s);
    
    Mat(int ndims, const int* sizes, int type, const Scalar& s);
   
    Mat(const std::vector<int>& sizes, int type, const Scalar& s);

};

3.基本+數據

第三種類型就是在基本類型中添加數據data,也就是尺寸 + 類型 + 數據

其中表示數據的是:

    void* data;

這一類型主要有:

class CV_EXPORTS Mat
{
public:    
    
    Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);
    
    Mat(Size size, int type, void* data, size_t step=AUTO_STEP);

    Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0);

    Mat(const std::vector<int>& sizes, int type, void* data, const size_t* steps=0);

};

4.Mat+其他

第四種類型就是在Mat類型加上其他參數,也就是Mat + 其他

這一類型主要有:

class CV_EXPORTS Mat
{
public:    

    Mat(const Mat& m);

    Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all());

    Mat(const Mat& m, const Rect& roi);

    Mat(const Mat& m, const Range* ranges);

    Mat(const Mat& m, const std::vector<Range>& ranges);

};

這種類型中最常用的是第三個,也就是可以獲取圖像中某個矩形區域內的圖像,例如:

	Mat src, src_roi;
	src = imread("./image/cat.jpg");
	
	if (!src.data)
	{
		cout << "ERROR : could not load image.\n";
		waitKey(0);
		return -1;
	}

	imshow("input image", src);
	src_roi = Mat(src, Rect(100, 100, 300, 200));
	imshow("roi image", src_roi);

我們的輸入圖像爲:

使用Mat構造函數 Mat(const Mat& m, const Rect& roi); 後的圖像爲:

 

3、其他方法

除了構造函數,Mat類還支持很多其他方法,在這裏把一些常用的方法介紹給大家。

1.行與列

第一類是跟圖像的行列相關,主要有:

class CV_EXPORTS Mat
{
public:    
    
    Mat row(int y) const;

    Mat col(int x) const;

    Mat rowRange(int startrow, int endrow) const;

    Mat rowRange(const Range& r) const;

    Mat colRange(int startcol, int endcol) const;

    Mat colRange(const Range& r) const;

};

2.拷貝與轉換

第二種類型是通過一個圖像拷貝到另一個圖像,或者轉換後拷貝到另一個圖像,主要有:

class CV_EXPORTS Mat
{
public:    

    Mat clone() const CV_NODISCARD;

    void copyTo( OutputArray m ) const;

    void copyTo( OutputArray m, InputArray mask ) const;

    void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;

    void assignTo( Mat& m, int type=-1 ) const;
 
    Mat& setTo(InputArray value, InputArray mask=noArray());

};

3.基本運算

第三種類型是基本運算,因爲我們知道,二維圖像其實就是二維矩陣,對二維圖像的操作包括修改形狀尺寸,圖像矩陣求逆,兩個圖像矩陣相乘等等,主要有:

class CV_EXPORTS Mat
{
public:    

    Mat reshape(int cn, int rows=0) const;

    Mat reshape(int cn, int newndims, const int* newsz) const;

    Mat reshape(int cn, const std::vector<int>& newshape) const;

    MatExpr inv(int method=DECOMP_LU) const;

    MatExpr mul(InputArray m, double scale=1) const;

    Mat cross(InputArray m) const;

    double dot(InputArray m) const;

    void resize(size_t sz);

    void resize(size_t sz, const Scalar& s);

};

4.生成圖像矩陣

第四種類型是生成全一、全零、對角矩陣或者按類型生成矩陣,主要有:

class CV_EXPORTS Mat
{
public:    

    static MatExpr zeros(int rows, int cols, int type);

    static MatExpr zeros(Size size, int type);

    static MatExpr zeros(int ndims, const int* sz, int type);

    static MatExpr ones(int rows, int cols, int type);

    static MatExpr ones(Size size, int type);

    static MatExpr ones(int ndims, const int* sz, int type);

    static MatExpr eye(int rows, int cols, int type);

    static MatExpr eye(Size size, int type);

    void create(int rows, int cols, int type);

    void create(Size size, int type);

    void create(int ndims, const int* sizes, int type);

    void create(const std::vector<int>& sizes, int type);

};

5.圖像基本信息

第五種類型是獲取圖像基本信息,主要有:

class CV_EXPORTS Mat
{
public:  

    int type() const; //類型

    int depth() const; //圖像深度

    int channels() const; //圖像通道數

    bool empty() const; //圖像是否爲空

};

當然Mat的函數不止有這些,其他的,如果大家深入學習,有可能會用到,在這裏就不講了。還是希望大家能夠多多練習,多多嘗試。

四、Mat使用

上面的主要是講解,這裏,我們通過幾個實際的例子來使用一下,加深我們的印象。

1、Mat創建對象

1.聲明對象

這個是最簡單的,也是最常用的,我們使用一個圖像類型,就要使用Mat進行聲明:

Mat src;

2.構造函數生成對象

我們可以使用Mat類型來生成對象,上面我們講了很多構造方法,大家可以逐一嘗試,在這裏,我們舉幾個例子:

        Mat src_rect, src_type, src_color;
        src_rect = Mat(src, Rect(100, 100, 300, 200));	
	src_type = Mat(10, 10, CV_8UC1);
	src_color = Mat(10, 10,CV_8UC3, Scalar(0, 0, 255));

3.拷貝生成對象

這個時候,我們就涉及到兩個概念:淺拷貝與深拷貝

淺拷貝只複製指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存。修改某一個對象,另一個對象就會一同被修改。

深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。

下面的方式是淺拷貝方式:

	Mat light_copy_1(src);
	Mat light_copy_2 = src;

 使用copyTo函數和clone函數可以實現深拷貝,下面的方式是深拷貝方式:

	Mat deep_copy_1 = src.clone();
	Mat deep_copy_2;
	src.copyTo(deep_copy_2);

 

2、Mat對象的簡單使用

簡單使用就是說我們只考慮其類體中的方法,因爲OpenCV的其他模塊,都可以用Mat創建的對象調用,在後續課程我們會繼續講解。

我們可以訪問常用成員獲得圖像的基本信息:

	cout << "src.flags = " << src.flags << endl;
	cout << "src.dims = " << src.dims << endl;
	cout << "src.rows = " << src.rows << endl;
	cout << "src.cols = " << src.cols << endl;
	cout << "src.data = " << endl;
	for (int i = 0; i < src.cols*src.rows; i++) {
		cout << (int)src.data[i] << " ";
		if (i%src.rows == src.rows - 1) cout << endl;
	}

我們可以使用Mat的方法獲得圖像的基本信息:

	Mat src;
	src = imread("./image/cat.jpg");
	if (src.empty())
	{
		cout << "ERROR : could not load image.\n";
		return -1;
	}
	cout << "src.channels() = " << src.channels() << endl;
	cout << "src.type() = " << src.type() << endl;
	cout << "src.depth() = " << src.depth() << endl;

執行結果如下:

我們可以生成全一全零或者單位矩陣:

	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;

執行結果如下:

3、Mat注意事項

根據我們上面講的,我們總結一下在使用過程中的注意事項:

1.OpenCV中的內存分配是自動完成的(不是特別指定的話)

2.使用OpenCV的C++ 接口時不需要考慮內存釋放問題

3.Mat的賦值運算和拷貝構造函數只會拷貝矩陣頭,仍然共同同一個矩陣

4.如果要複製矩陣數據,可以使用clone和copyTo函數

五、Mat與CVMat,IplImage

1、爲何引入Mat

講到這裏,Mat就基本講的差不多了,在最後,給大家來點科普,其實在最開始,OpenCV使用的圖像類型不是Mat,而是另外兩個,那爲什麼要引入Mat呢?

1.語言的變化,OpenCV1.x版本使用C語言編寫,從2.x版本引入C++之後,換用C++來寫。

2.單從上面一條肯定是不行,主要還是因爲這一條:C++提供了很多新特性,比如類可以在結束時自動調用析構函數,釋放內存,就不需要再自己寫代碼釋放了,但是C語言不行,申請的內存需要自己手動管理,特別是採用 lplImage 會直接暴露內存,如果忘記釋放內存,就會造成內存泄漏。而Mat的內存分配和釋放,由Mat類自己管理。

3.Mat相對於前兩個功能更加強大,Mat既可以深拷貝也可以淺拷貝,Mat在進行淺拷貝時,只複製矩陣頭,而不復制矩陣,提高效率。如果矩陣屬於多個Mat對象,則通過引用計數來判斷,當最後一個使用它的對象,則負責釋放矩陣。

所以上面的也是Mat的優勢。

2、類型轉換

但是在很多時候,很多人還是會使用另外兩個,那我們就要考慮他們的類型轉換了,這裏我將類型轉換方式分享給大家:

//1. IpIImage -> CvMat
/*cvGetMat*/
CvMat matheader;
CvMat * mat = cvGetMat(img, &matheader);
/*cvConvert*/
CvMat * mat = cvCreateMat(img->height, img->width, CV_64FC3);
cvConvert(img, mat);
 
//2. IplImage -> Mat
Mat::Mat(const IplImage* img, bool copyData=false);/*default copyData=false,與原來的IplImage共享數據,只是創建一個矩陣頭*/
//例子:
IplImage* iplImg = cvLoadImage("greatwave.jpg", 1);
Mat mtx(iplImg); /* IplImage * -> Mat,共享數據; or : Mat mtx = iplImg;*/
 
//3. Mat -> IplImage
Mat M
IplImage iplimage = M; /*只創建圖像頭,不復制數據*/
 
//4. CvMat -> Mat
Mat::Mat(const CvMat* m, bool copyData=false); /*類似IplImage -> Mat,可選擇是否複製數據*/
 
//5. Mat -> CvMat
//例子(假設Mat類型的imgMat圖像數據存在):
CvMat cvMat = imgMat;/*Mat -> CvMat, 類似轉換到IplImage,不復制數據只創建矩陣頭

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章