本節主要介紹如何遍曆Mat數組中的元素,遍曆Mat中元素的相關函數,其中包括at族函數和ptr族函數,以及矩陣記憶體的布局,以及許多C++相關的知識點,這是整個Mat類的精髓,也註定了這將是華麗麗的一篇!
你如何定位矩陣中的元素?
OpenCV Tutorials中給出了三種方法遍曆數組中的元素:(一)高效的經典的C運運算元[ ]方法;(二)安全的C++中迭代器方法;(三)動態地址計算方法。下面簡要介紹下三種方法的主要用法以及優缺點
(一)無人能及的C []運運算元,基於ptr函數族
int i,j; uchar* p; for( i = 0; i < nRows; ++i) { p = I.ptr<uchar>(i);//擷取行指標 for ( j = 0; j < nCols; ++j) { p[j] = table[p[j]]; } }
最高效的方法
(二)C++迭代器方法
MatIterator_<uchar> it, end; for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it) *it = table[*it]; break;
最安全的方法
(三)動態地址計算方法:主要使用at函數族,千萬不要用!
for( int i = 0; i < I.rows; ++i) for( int j = 0; j < I.cols; ++j ) I.at<uchar>(i,j) = table[I.at<uchar>(i,j)];
此種方法適合隨機訪問操作,而並不適合遍曆操作。相較於最高效的C方法,這種方法遍曆每一個映像元素時都會重建一個行指標。稍後會分析方法(一)和方法(三)到底差在哪裡。
Tutotials中也給出了上述三種方法的時間對比
| Efficient Way |
Iterator |
On-The-Fly RA |
| 79.4717 milliseconds |
83.7201 milliseconds |
93.7878 milliseconds |
所以說,遍曆矩陣時,首選方法是方法一。
為什麼方法一和方法三會差那麼多呢?下面通過分析Mat的at函數族和ptr函數族回答這個問題。
at函數族和ptr函數族
at函數族:
template<typename _Tp> _Tp& at(int i0=0);template<typename _Tp> const _Tp& at(int i0=0) const; …...template<typename _Tp> _Tp& at(Point pt);template<typename _Tp> const _Tp& at(Point pt) const;
ptr函數族:
uchar* ptr(int i0=0);const uchar* ptr(int i0=0) const; …...template<typename _Tp, int n> _Tp* ptr(const Vec<int, n>& idx);template<typename _Tp, int n> const _Tp* ptr(const Vec<int, n>& idx) const;
ptr函數典型源碼:
inline uchar* Mat::ptr(int y){ return data + step.p[0]*y;}
at函數典型源碼:
template<typename _Tp> inline _Tp& Mat::at(int i0, int i1){ return ((_Tp*)(data + step.p[0]*i0))[i1];}
step.p[0]的解釋見:OpenCV學習:Mat類詳細解析+源碼剖析(四)MSize類和Mstep類
以二維情況為例,ptr返回的是指標(某維度某位移的起始地址),所示,at比ptr更進一步,獲得指標後又使用[ ]提取出了數值。上文中方法一和方法三最大差異所在,方法一中一個頭指標可以運用於整行資料,但是方法四中每遍曆一個元素就要算一次行指標,也就是說方法一中行指標計算了row次,而方法三中行指標計算了row×col次!