【OpenCV】訪問Mat影象中每個畫素的值
今天百度搜資料還搜到了自己的。。。《訪問影象中每個畫素的值》,這是之前寫的了,用的也是2.0的風格IplImage*格式,不太適用後來Mat的格式,特此重寫一篇。
以下例子源自《The OpenCV Tutorials --Release 2.4.2》2.2 How to scan images, lookup tables and time measurement with OpenCV
影象容器Mat
還是先看Mat的儲存形式。Mat和Matlab裡的陣列格式有點像,但一般是二維向量,如果是灰度圖,一般存放<uchar>型別;如果是RGB彩色圖,存放<Vec3b>型別。
單通道灰度圖資料存放格式:
多通道的影象中,每列並列存放通道數量的子列,如RGB三通道彩色圖:
注意通道的順序反轉了:BGR。通常情況記憶體足夠大的話影象的每一行是連續存放的,也就是在記憶體上影象的所有資料存放成一行,這中情況在訪問時可以提供很大方便。可以用 isContinuous()函式來判斷影象陣列是否為連續的。
訪問影象中的畫素
高效的方法:C操作符[ ]
最快的是直接用C風格的記憶體訪問操作符[]來訪問:
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
int channels = I.channels();
int nRows = I.rows ;
int nCols = I.cols* channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
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]];
}
}
return I;
}
注意:書中這段程式碼是有問題的,前面寫成了
int nRows = I.rows * channels;
int nCols = I.cols;
一般情況 isContinous為true,執行不會出錯,但你可以註釋掉那個if,會有訪問越界的問題。
這種訪問形式就是在每行定義一個指標,然後在記憶體上直接連續訪問。如果整個陣列在記憶體上都是連續存放的,那麼只需要定義一個指標就可以訪問所有的資料!如單通道的灰度圖訪問方式如下:
uchar* p = I.data;
for( unsigned int i =0; i < ncol*nrows; ++i)
*p++ = table[*p];
安全的方法:迭代器iterator
相比用指標直接訪問可能出現越界問題,迭代器絕對是非常安全的方法:Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
const int channels = I.channels();
switch(channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
*it = table[*it];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = table[(*it)[0]];
(*it)[1] = table[(*it)[1]];
(*it)[2] = table[(*it)[2]];
}
}
}
return I;
}
這裡我們只定義了一個迭代器,用了一個for迴圈,這是因為在OpenCV裡迭代器會訪問每一列然後自動跳到下一行,不用管在記憶體上是否isContinous。另外要注意的是在三通道影象中我們定義的是 <Vec3b>格式的迭代器,如果定義成uchar,則只能訪問到B即藍色通道的值。這種方式雖然安全,但是挺慢的,一會兒就知道了。
更慢的方法:動態地址計算
這種方法在需要連續掃描所有點的應用時並不推薦,因為它更實用與隨機訪問。這種方法最基本的用途是訪問任意的某一行某一列:
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));
const int channels = I.channels();
switch(channels)
{
case 1:
{
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)];
break;
}
case 3:
{
Mat_<Vec3b> _I = I;
for( int i = 0; i < I.rows; ++i)
for( int j = 0; j < I.cols; ++j )
{
_I(i,j)[0] = table[_I(i,j)[0]];
_I(i,j)[1] = table[_I(i,j)[1]];
_I(i,j)[2] = table[_I(i,j)[2]];
}
I = _I;
break;
}
}
return I;
}
因為這種方法是為隨機訪問設計的,所以真的是奇慢無比。。。減小顏色空間 color space reduction
現在來介紹下上述函式對每個元素的操作,也就是用table更改畫素值。這裡其實是做了個減小顏色空間的操作,這在一些識別之類的應用中會大大降低運算複雜度。類如uchar型別的三通道影象,每個通道取值可以是0~255,於是就有 256*256個不同的值。我們可以通過定義:
0~9 範圍的畫素值為 0
10~19 範圍的畫素值 為 10
20~29 範圍的畫素值為 20
。。。。。。
著這樣的操作將顏色取值降低為 26*26*26 種情況。這個操作可以用一個簡單的公式:
0~9 範圍的畫素值為 0
10~19 範圍的畫素值 為 10
20~29 範圍的畫素值為 20
。。。。。。
著這樣的操作將顏色取值降低為 26*26*26 種情況。這個操作可以用一個簡單的公式:
來實現,因為C++中int型別除法操作會自動截餘。 類如 Iold=14; Inew=(Iold/10)*10=(14/10)*10=1*10=10;
在處理影象畫素時,每個畫素需要進行一遍上述計算也需要一定的時間花銷。但我們注意到其實只有 0~255 種畫素,即只有256種情況。進一步可以把256種計算好的結果提前存在表中 table 中,這樣每種情況不需計算直接從 table 中取結果即可。
在處理影象畫素時,每個畫素需要進行一遍上述計算也需要一定的時間花銷。但我們注意到其實只有 0~255 種畫素,即只有256種情況。進一步可以把256種計算好的結果提前存在表中 table 中,這樣每種情況不需計算直接從 table 中取結果即可。
int divideWith=10;
uchar table[256];
for (int i = 0; i < 256; ++i)
table[i] = divideWith* (i/divideWith);
於是table[i]存放的是值為i的畫素減小顏色空間的結果,這樣也就可以理解上述方法中的操作:p[j] = table[p[j]];
LUT : Look up table
OpenCV 很聰明的有個 LUT 函式就是針對這種 Look up talbe 的操作:Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.data;
for( int i = 0; i < 256; ++i)
p[i] = table[i];
for (int i = 0; i < times; ++i)
LUT(I, lookUpTable, J);
演算法計時
為了驗證幾種方法的效率,可以用一個簡單的計時和輸出:
double t;
t = (double)getTickCount();
t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;
實驗結果
原圖:
降低顏色空間結果:
演算法時間:
更清楚的時間對比表:
轉載請註明出處:http://blog.csdn.net/xiaowei_cqu/article/details/7771760
實驗程式碼下載:http://download.csdn.net/detail/xiaowei_cqu/4443761
相關文章
- 多維 opencv Mat訪問OpenCV
- 影象中的畫素處理
- Python-OpenCV 處理影象(三):影象畫素點操作PythonOpenCV
- 常用的畫素操作演算法:影象加法、畫素混合、提取影象中的ROI演算法
- opencv 修改畫素OpenCV
- 讀取BMP影象每一畫素點RGB資料
- 計算機視覺 OpenCV Android | Mat畫素操作(影象畫素的讀寫、均值方差、算術、邏輯等運算、權重疊加、歸一化等操作)...計算機視覺OpenCVAndroid
- c#影象處理入門(-bitmap類和影象畫素值獲取方法)C#
- [Python影象處理] 二.OpenCV+Numpy庫讀取與修改畫素PythonOpenCV
- #解決opencv與matlab對於BMP格式讀出的畫素值不同的問題OpenCVMatlab
- 影象二值化(python+opencv)PythonOpenCV
- 影象縮放--插值法(opencv,原理)OpenCV
- opencv mat轉IplImage*OpenCV
- OpenCV(cv::Mat::setTo())OpenCV
- OpenGL中的畫素操作
- Python-OpenCV 處理影象(八):影象二值化處理PythonOpenCV
- Python+OpenCV 影象風格遷移(模仿名畫)PythonOpenCV
- 裝置畫素、獨立畫素和css畫素CSS
- OpenCV(cv::Point、cv::Rect、cv::Mat)OpenCV
- 常用的畫素操作演算法:影像加法、畫素混合、提取影像中的ROI演算法
- opencv中的影象形態學——腐蝕膨脹OpenCV
- python改變單通道圖片的畫素值Python
- opencv的Mat類詳解以及mannal翻譯OpenCV
- SDL程式設計入門(28)每畫素碰撞檢測程式設計
- 亞畫素數值極值檢測演算法總結演算法
- 利用opencv合併影象OpenCV
- canvas畫素畫板Canvas
- 掌握web開發基礎系列--物理畫素、邏輯畫素、css畫素WebCSS
- OpenCV(cv::Mat 類的成員函式 ptr<T>())OpenCV函式
- 什麼是物理畫素、虛擬畫素、邏輯畫素、裝置畫素,什麼又是 PPI, DPI, DPR 和 DIP
- OpenCV之Python學習筆記(1)(2): 影象的載入、顯示和儲存 影象元素的訪問、通道分離與合併OpenCVPython筆記
- 基於 OpenCV 的影象匹配( Java 版)OpenCVJava
- 用 CSS 做畫素畫CSS
- 畫素畫——明暗基礎
- Python-OpenCV 處理影象(五):影象中邊界和輪廓檢測PythonOpenCV
- 裝置畫素和css畫素簡單介紹CSS
- lcd螢幕顯示jpeg圖片,其中lcd每個畫素佔據4位元組
- 吐嘈OpenCV的影象旋轉功能 >_<7OpenCV