天天看點

【OpenCV】通路Mat圖像中每個像素的值圖像容器Mat通路圖像中的像素減小顔色空間 color space reduction實驗結果

今天百度搜資料還搜到了自己的。。。《通路圖像中每個像素的值》,這是之前寫的了,用的也是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>類型。 單通道灰階圖資料存放格式:

【OpenCV】通路Mat圖像中每個像素的值圖像容器Mat通路圖像中的像素減小顔色空間 color space reduction實驗結果

多通道的圖像中,每列并列存放通道數量的子列,如RGB三通道彩色圖:

【OpenCV】通路Mat圖像中每個像素的值圖像容器Mat通路圖像中的像素減小顔色空間 color space reduction實驗結果

注意通道的順序反轉了: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 種情況。這個操作可以用一個簡單的公式:

【OpenCV】通路Mat圖像中每個像素的值圖像容器Mat通路圖像中的像素減小顔色空間 color space reduction實驗結果

來實作,因為C++中int類型除法操作會自動截餘。 類如 Iold=14; Inew=(Iold/10)*10=(14/10)*10=1*10=10;

在處理圖像像素時,每個像素需要進行一遍上述計算也需要一定的時間花銷。但我們注意到其實隻有 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;
           

實驗結果

原圖:

【OpenCV】通路Mat圖像中每個像素的值圖像容器Mat通路圖像中的像素減小顔色空間 color space reduction實驗結果

降低顔色空間結果:

【OpenCV】通路Mat圖像中每個像素的值圖像容器Mat通路圖像中的像素減小顔色空間 color space reduction實驗結果

算法時間:

【OpenCV】通路Mat圖像中每個像素的值圖像容器Mat通路圖像中的像素減小顔色空間 color space reduction實驗結果

更清楚的時間對比表:

【OpenCV】通路Mat圖像中每個像素的值圖像容器Mat通路圖像中的像素減小顔色空間 color space reduction實驗結果

轉載請注明出處:http://blog.csdn.net/xiaowei_cqu/article/details/7771760

實驗代碼下載下傳:http://download.csdn.net/detail/xiaowei_cqu/4443761

繼續閱讀