OpenCV學習心得(11) -- Mat類型與Arraya

轉載請注明出處與作者

下面對Mat矩陣進行一些說明:

1. cv::Mat中的數據不需要釋放.cv::Mat會自動釋放.

例如,cv::Mat m=cv::imread("123.jpg"); 然後進行數據操作,最後根本不用對m進行釋放,也不需要調用其他的任何release函數.

       

2. cv::Mat的depth()函數可以得到圖像的深度,值類似于CV_8U.CV_16F等..可以理解為單個channel中的單個元素的大小(bit)與類型.8U表示8-bit unsigned char.

3. cv::Mat的cols表示圖像的width,rows表示圖像的height.

4. cv::Mat在取底層數據的時候使用at函數,類似于src.at<cv::Point3i>(i,j)[0]的方式,取出第i行第j列的像素的第0個channel的值.尖括號中的類型為Mat元素的類型.例如單channel的8U圖像可以使用unsigned char型,由於是單channel就不要後面的中括號了.3channel的BGR圖像可以使用cv::Point3i的格式.

5. cv::Mat的類型是由深度和channel數一起決定的,類似于CV_8UC3,表示深度為CV_8U,channel數為CV_C1.可以使用CV_MAKETYPE(depth,cn) 宏來合成類型值.

6. cv::Mat 默認情況下是不進行數據拷貝的,只是生成一個Mat頭信息,所以就導致如果兩個Mat指向同一個數據的時候,其中一個進行了變更,另一個也會變更.如果要進行實際數據的拷貝可以使用下面的2中方式:

cv::Mat m1=cv::imread("123.jpg");

cv::Mat m2=m1; //沒有數據拷貝

cv::Mat m3=m2.clone();//有數據拷貝

cv::Mat m4;

m2.copyTo(m4); //有數據拷貝

7. 當多個cv::Mat 指向同一組數據的時候,只有所有的Mat都釋放之後實際數據才會釋放,否則只是進行計數累加. 當其中一個Mat調用Relese函數后,只是釋放當前Mat的頭信息,如果此時還有其他Mat在使用這些實際數據,則這些實際數據并不釋放.也就是說cv::Mat.Relese()只是減少一個引用計數,并清空Mat頭,其他無影響.



下面說明一下cv::Mat的成員函數和成員變量:
Mat row(int y) const; 使用第y行的數據新建一個Mat,不進行數據拷貝.
Mat col(int x) const; 使用第x列的數據新建一個Mat,不進行數據拷貝.
Mat rowRange(int startrow, int endrow) const; 使用第startrow行到第endrow行的數據新建一個Mat,不進行數據拷貝.
Mat rowRange(const Range& r) const; 使用Range 中的行的數據新建一個Mat,不進行數據拷貝.
Mat colRange(int startcol, int endcol) const;使用第startcol列到第endcol列的數據新建一個Mat,不進行數據拷貝.
Mat colRange(const Range& r) const; 使用Range 中的列的數據新建一個Mat,不進行數據拷貝.
Mat diag(int d=0) const; 使用對角線(從左上到右下)中的數據新建一個單列的Mat,不進行數據拷貝.d=0時為主對角線,d>0表示主對角線下面的第d個對角線,d<0表示主對角線上面的第d個對角線.
Mat clone() const; 產生一個新的Mat,并複製實際數據.實際數據存儲在一個連續的total()*elemSize()大小的空間之中.
例如, cv::Mat m=src.diag(0);//m中存儲這一個對角線數據,實際上還是src中的數據,所以這些對角線數據是被其他值分開的.每行只有1個值有用.
          cv::Mat m2=m.clone(); //ma中的對角線數據是連續的,一個值接著一個值,中間沒有被其他值分開.
void copyTo( OutputArray m ) const;拷貝實際數據到m中,m會根據源Mat的大小重建.(如果m中有數據則會先釋放).
void copyTo( OutputArray m, InputArray mask ) const; 功能同 copyTo( OutputArray m ) ,只是只會拷貝mask中對應不是0的值.mask與源Mat要有相同的size. m在被拷貝之前會用0填充.
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const; 將源Mat的格式轉換為另一種數據格式存儲到m中,如果m的格式或大小不正確則會重新創建.rtype為轉換后的類型,如果為負數,則與源Mat相同.這個函數可以對其中的值進行縮放和偏移,公式為 m(x,y)=newtype((alpha*src(x,y)+beta));
void assignTo( Mat& m, int type=-1 ) const; 內部函數,功能同convertTo.
Mat=Scalar; 重載等號,將Mat的中每一個元素都填充為一個Scalar.
Mat& setTo(InputArray value, InputArray mask=noArray());根據mask的將M中的元素設置為value.
Mat reshape(int _cn, int _rows=0) const; //將源Mat的改成cn個channel和rows行數據,不進行數據拷貝.使用reshape函數有一個要點就是rows*cols*channels的值必須等於源Mat.也就是實際數據保持不變._rows=0表示rows與源Mat相同,_cn=0表示channel數量與源Mat相同,
例如:
cv::Mat src(200,400,CV_8UC3);
cv::Mat m1=src.reshape(2,0); //將圖像變成2個channel的圖像.由於rows不變,所以cols變為src.rows*src.cols*src.channels()/src.rows/2;圖像顯示為顏色失真(少了一個channel嘛)圖像寬度變大.這是由於原來的第3個channel的值被當做下一個元素的第一個值來使用了,所以圖像看起來并不是從圖像中截取一截放到右邊,而是像是使用差值的方式進行擴展了(實際上再轉換成4channel的時候就是差值的方式,只是在沒兩個元素後面插入2個零).
cv::Mat m1=src.reshape(0,src.rows/2);這是在保持channel不變的情況下改變rows的值.有一rows變為一半,所以cols則增加一倍,圖像顯示的效果就是原Mat第2行的像素被挪到了第1行的後面,也就是新Mat的第一行是由原Mat的第1,2行組成的,第3行變成了新Mat第2行,第4行被挪到新Mat第2行后半部份.以此類推,最終的結構就是原圖像的奇數行成為新圖像的左半部份,原mat的偶數行變成新Mat的右半部份.
cv::Mat m1=src.reshape(2,src.rows/2);變成1個channel,行數減半的新矩陣,顯示情況就是中和上面連個例子,分為左右2個圖像,每個圖像都變寬并失真.
Mat reshape(int _cn, int _newndims, const int* _newsz) const;將源Mat的改成cn個channel和_newndims個維度的數據._newsz為int型數組,存儲每個維度的Size. 也是要保持實際數據不變.
MatExpr t() const; 對矩陣進行轉置,並沒有改變實際數據,但是會返回一個臨時的數據供其他操作使用.(實際上還是進行了數據拷貝,只是沒有改變源數據,而是拷貝到其他地方去了).
MatExpr inv(int method=DECOMP_LU) const;反轉一個矩陣(必須是方陣,再根據方陣是否為奇異矩陣來選擇反轉模式),method為反轉 模式,DECOMP_LU適用於非奇異矩陣(可逆的方陣)一般圖像都不是非奇異矩陣.DECOMP_CHOLESKY:適用於對稱正矩陣的分解,大矩陣的情況下此法比LU法快2倍,DECOMP_SVD如果矩陣是奇異矩陣或者不是2維的方形矩陣.則計算偽反轉矩陣.這個函數不變更源矩陣,而是生成一個新的臨時矩陣(也就是有數據拷貝)
MatExpr mul(InputArray m, double scale=1) const;//兩個矩陣的乘法/除法.m為另一個矩陣,必須與源矩陣有相同的類型和大小,可是使用1/src的方式實現除法功能.scale為對每個相乘結果的係數變換.最終結果為相乘結果*scale.生成一個新的臨時矩陣(也就是有數據拷貝)
Mat cross(InputArray m) const;計算兩個3D矩陣的叉積.必須是浮點數據.生成一個新的臨時矩陣(也就是有數據拷貝)
double dot(InputArray m) const; 兩個矩陣的點乘,將矩陣按照從上到下,從左到右的順序當做1維數組與另一個矩陣(也被當做1維的)進行點積.如果是多channel矩陣,則所有channel得到的點積被加在一起.
MatExpr zeros() 返回指定大小和類型的內部數據全部填充為零的矩陣.
MatExpr ones() 返回指定大小和類型的內部數據全部填充為1的矩陣,如果想填充別的數據可以這樣使用:
cv::Mat m=cv::Mat.ones(8,8,CV_8UC3)*4; 這樣就填充了一個全是4的矩陣(需要注意的是在初始化的時候就直接填進去了,而不是先填充1然後再乘以4).
MatExpr eye() 返回指定大小和類型的單位矩陣.如果想填充別的數據可以這樣使用:
cv::Mat m=cv::Mat.eye(8,8,CV_8UC3)*4; 這樣就填充了一個單位為4的單位矩陣.
void create(int _rows, int _cols, int _type); 創建一個指定大小和類型的矩陣(包括申請實際數據的空間),如果新類型或大小與源矩陣的不匹配則會先施放原來的實際數據(實際上是引用計數減1)并申請新的空間.
void release(); 減少一個引用計數,如果引用計數為0則施放實際數據.
void reserve(size_t sz); 預留足夠的空間.
void resize(size_t sz, const Scalar& s);重置矩陣大小,新Size與就Size重疊的部份的數據將被保留,新增加的數據用s填充.
push_back(const _Tp& elem);在底部添加元素,如果添加的是一個元素的則添加在底部,如果添加的是一個矩陣,則矩陣的類型和列數必須與源矩陣相同.
void pop_back(size_t nelems=1); 刪除底部的n個數據.
void locateROI( Size& wholeSize, Point& ofs ) const;獲取一個子矩陣在其父矩陣中的位置.wholeSize返回父矩陣的大小,ofs返回子矩陣左上角在父矩陣中的座標.
子矩陣就是父矩陣使用col(),row(),rowRange(),colRange(),Mat(Rect)等函數生成的沒有數據拷貝的cv::Mat.
Mat& adjustROI( int dtop, int dbottom, int dleft, int dright );調整子矩陣的大小和位置.需要注意的是此函數的4個參數都是邊界座標的offset值,左邊界和上邊界採用src-offset的公式確定座標,右邊界和下邊界採用src+offset的方式確定座標:計算之後的Rect不能超過源矩陣的邊界,否則出錯.舉個例子:
cv::Mat m=m_src(cv::Rect(100,150,200,250)); //設置一個ROI.
cv::Mat m2=m.adjustROI(20,21,22,23); 調整ROI.第一個參數為上邊界,即150-20=130,所以新ROI的上邊界座標為130,第二個參數為下邊界,即150+250+21=421,第三個參數是左邊界,即100-22=78,第四個參數為右邊界,即100+200+23=323.
cv::Size s;
cv::Point p;
m2.locateROI(s,p); p為ROI左上角座標即(x=78,y=130)
cv::Size s2=m2.size(); s2為ROI大小,即width=323-78=245,height=421-130=291
bool isContinuous() const; 判斷矩陣的行末尾是否有缺失(即數據是否是連續的),當你使用col(),diag(),或者外部數據的時候,可能就不是連續的了.
bool isSubmatrix() const; 判斷矩陣是否是另外一個矩陣的子矩陣(ROI).
size_t elemSize() const; 返回矩陣中元素的大小(單位為byte),計算方式:通道數*單個通道中單個元素的字節數,例如CV_16SC3類型的矩陣則返回3(通道)*(16/8)=6,CV_32FC2返回2*32/8=8
size_t elemSize1() const; 返回單個chennel的元素的大小(單位為byte),例如CV_16SC3類型的矩陣則返回(16/8)=6,CV_32FC2返回32/8=8
int type() const; 返回矩陣類型,可以使用類似于CV_8UC3的值來判讀類型.
int depth() const; 返回矩陣的深度,這個深度值必須使用類似于CV_8U的值來判斷深度類型.而不能把這個值直接用於其他的API(例如BITMAP的bpp值).
int channels() const; 返回channel的個數.
size_t step1(int i=0) const; 返回cv::Mat::step/elemSize1(),這個值對任意存取矩陣中的元素很有作用.
bool empty() const; 判斷矩陣是否為空(是否有元素).需要注意的是cv::Mat::data不為NULL的時候,不一定有元素,也就是判斷一個矩陣是否為空的時候不能使用cv::Mat::data是否為NULL來判斷.而應該使用這個函數來判斷.
size_t total() const;返回矩陣中有多少個元素.
uchar* ptr(int i0=0); 得到執行實際數據的一個指針.一個參數的是ptr是指向1維數據的,兩個參數的ptr是執行2維數據的(也就是行和列).一次類推..
ptr模板函數:他可以得到某一個特定類型的特定位置的值的指針.
at模板函數:可以得到某一個特定類型的特定位置的值的引用.一般我們都使用at來取實際數據.下面詳細舉例:
src.at<uchar>(i,j)=5; 將源數據中的數據類型當做uchar類型來使用,將其中第(i,j)個元素設置為5; 這裡取值時每次步進sizeof(uchar)個byte.也就是1個元素值.
cv::Point &p=src.at<cv::Point>(i,j);p[0]=2; 將第(i,j)個像素點的第1個值設置為2,可以合併為下面一條語句: src.at<cv::Point>(i,j)[0]=2;這裡取值時每次步進一個cv::Point 大小的byte.也就是2個元素值.

rows變量:得到矩陣的行數.也就是圖像的高度.
cols變量:得到矩陣的列數,也就是圖像的寬度.
uchar* data;得到圖像的實際數據的指針.一般只有使用外部數據的時候才用到這個變量.
MStep step; 矩陣步進大小.MStep 是內嵌類,可以直接準換為size_t類型.
MSize size; 獲得矩陣的大小.MSize 是一個內嵌類,可以直接轉換為cv::Size類型.


介紹完了cv::Mat,那麼再看一下cv::MatExpr
cv::MatExpr是什麽類型呢?
cv.::MatExpr實際上也是一個類,這個類封裝了cv::Mat的一些操作,實際上我們在進行兩個cv::Mat的加減乘除運算的時候返回的是cv::MatExpr類型,然後再自動調用cv::MatExpr類中的操作符Mat將其轉換為cv::Mat類型.所以我們就會看到下面的使用方法:
cv::Mat m=src1+src2; src1和src2都是cv::Mat類型.
所以我們以後看到參數或返回值為cv::MatExpr類型的時候,完全可以當做cv::Mat來使用.

再來看看cv::Matx,
cv::Matx實際上也是一個類,實際上也是一個矩陣,一般用於小型的數據矩陣,這個矩陣的一個特點就是必須在編譯的時候就能確定矩陣的大小.也就是說它的大小在一開始就確定了,不能在運行的時候根據變量進行設定大小.其他功能與Mat差不多.cv::Matx與cv::Mat之間可以進行轉換.
如果你需要靈活的申請矩陣的大小,可以使用cv::Mat. cv::Matx的功能cv::Mat都能實現.

cv::Scalar又是什麽?原來它就是一個4元素的vector.可以實現與cv::Mat的相互轉換.

我們在讀使用說明的時候經常會碰到OutputArray /InputArray /OutputArrayOfArrays /InputArrayOfArrays等類型,那麼這些類型到底是什麽類型呢?
下面對這些類型進行說明一下:
InputArray 和InputArrayOfArrays 就是_InputArray類.也就是說這2個類型是一樣.
OutputArray,OutputArrayOfArrays,InputOutputArray,InputOutputArrayOfArrays就是_OutputArray類,換句話說這4個類型實際上是一樣的.
那麼爲什麽要叫不同的名字呢?實際上是爲了表示在函數中實現的具體功能不同.下面詳細說明一下:
InputArray  表示是一個輸入參數,可以使cv::Mat,也可以是std::vector<XXX> 類型的(注意XXX不能是另一層vector,如果是另一層vector就用InputArrayOfArrays 表示了),有些時候也可以使cv::Scalar或則cv::Vec.這個參數可以使const型的引用.
InputArrayOfArrays  表示是一個InputArray 型的數組,也就是可以是std::vector<std::vector<XXX>>, 也可以使std::vector<cv::Mat>型的,或者std::vector<cv::Max>,.這個參數可以使const型的引用.
OutputArray 表示是一個輸出參數,可以使用cv::Mat,也可以是std::vector<XXX> 類型的(注意XXX不能是另一層vector,如果是另一層vector就用OutputArrayOfArrays表示了).有些時候也可以使cv::Scalar或則cv::Vec,最好不要使用const型引用.
OutputArrayOfArrays表示是一個OutputArray 型的數組,也就是可以是std::vector<std::vector<XXX>>, 也可以使std::vector<cv::Mat>型的.最好不要使用const型引用.
InputOutputArray和InputOutputArrayOfArrays表示此參數既要輸入數據也要輸出數據與其中.可以像OutputArray /OutputArrayOfArrays一樣使用.




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值