W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
我們會為以下問題尋求答案:
讓我們考慮一種簡單的減色方法。通過對矩陣項存儲使用unsigned char C和C ++類型,像素通道最多可以有256個不同的值。對于三通道圖像,這可以允許形成太多的顏色(1600萬精確)。使用如此多的色調(diào)可能會對我們的算法性能造成沉重打擊。然而,有時候,只要少一點工作能夠得到相同的最終結(jié)果就足夠了。
在這種情況下,我們通常會減少色彩空間。這意味著我們將顏色空間當前值與新的輸入值分開,以減少顏色。例如,零和九之間的每個值都將新的值為零,每個值在十到十十之間的值十等等。
當您使用int值將uchar(unsigned char-aka值在0和255之間)值分隔時,結(jié)果也將是char。這些值只能是char值。因此,任何分數(shù)將被向下舍入。利用這一事實,uchar域中的上層操作可以表示為:
簡單的顏色空間縮小算法將包括僅通過圖像矩陣的每個像素并應(yīng)用該公式。值得注意的是,我們做一個除法和乘法運算。這些操作對于系統(tǒng)來說是昂貴的。如果可能,通過使用更便宜的操作(如少量減法,添加或在最佳情況下是簡單的分配)來避免這種情況。此外,請注意,我們只有上限操作的輸入值有限。在uchar系統(tǒng)的情況下,這是256。
因此,對于較大的圖像,預(yù)先計算所有可能的值,并且在分配期間通過使用查找表來進行分配是明智的。查找表是簡單的數(shù)組(具有一個或多個維),對于給定的輸入值變量保存最終的輸出值。其實力在于我們不需要進行計算,只需要讀取結(jié)果。
我們的測試用例程序(以及此處提供的示例)將執(zhí)行以下操作:讀取控制臺線路參數(shù)圖像(可以是顏色或灰度級 - 控制臺線路參數(shù)),并使用給定的控制臺行參數(shù)整數(shù)值。在OpenCV中,目前有三種主要通過像素逐個通過圖像的方法。為了使事情更有趣,將使用所有這些方法對每個圖像進行掃描,并打印出花費多長時間。
您可以在這里下載完整的源代碼,或者在OpenCV的sample目錄中查看核心部分的cpp教程代碼。其基本用途是:
how_to_scan_images imageName.jpg intValueToReduce [G]
最后一個參數(shù)是可選的。如果給定圖像將以灰度格式加載,否則使用BGR顏色空間。首先是計算查找表。
int divideWith = 0; // convert our input string to number - C++ style
stringstream s;
s << argv[2];
s >> divideWith;
if (!s || !divideWith)
{
cout << "Invalid number entered for dividing. " << endl;
return -1;
}
uchar table[256];
for (int i = 0; i < 256; ++i)
table[i] = (uchar)(divideWith * (i/divideWith));
這里我們首先使用C ++ stringstream類將第三個命令行參數(shù)從文本轉(zhuǎn)換為整數(shù)格式。然后我們使用一個簡單的外觀和上面的公式來計算查找表。沒有OpenCV具體的東西在這里。
另一個問題是我們?nèi)绾魏饬繒r間?那么OpenCV提供了兩個簡單的函數(shù)來實現(xiàn)這個cv :: getTickCount()和cv :: getTickFrequency()。第一個從某個事件返回系統(tǒng)CPU的刻度數(shù)(就像您啟動系統(tǒng)一樣)。第二次返回您的CPU在一秒鐘內(nèi)發(fā)出多少次刻錄。所以為了測量秒數(shù),兩次操作之間的時間容易如下:
double t = (double)getTickCount();
// do something ...
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Times passed in seconds: " << t << endl;
正如您已經(jīng)閱讀我的Mat - 基本圖像容器教程中的矩陣大小取決于使用的顏色系統(tǒng)。更準確地說,它取決于所使用的通道數(shù)量。在灰度圖像的情況下,我們有一些像:
對于多通道圖像,列包含與通道數(shù)一樣多的子列。例如在BGR顏色系統(tǒng)的情況下:
注意,通道的順序是反向的:BGR而不是RGB。因為在許多情況下,內(nèi)存足夠大以便以連續(xù)的方式存儲行,所以這些行可以一個接一個地跟隨,創(chuàng)建一個長行。因為一切都在一個地方,這可能有助于加快掃描過程。我們可以使用cv :: Mat :: isContinuous()函數(shù)來詢問矩陣是否是這種情況。繼續(xù)下一節(jié)找一個例子。
當涉及到性能時,你無法擊敗經(jīng)典的C風格操作符[](指針)訪問。因此,我們可以推薦使用最有效的方法進行分配:
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
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;
}
在這里,我們基本上只是獲取一個指向每行開頭的指針,直到它結(jié)束。在特殊情況下,矩陣以連續(xù)的方式存儲,我們只需要單次請求指針,直到最后。我們需要尋找彩色圖像:我們有三個通道,所以我們需要通過每行三次以上的項目。
還有另一種方法。Mat對象的數(shù)據(jù)數(shù)據(jù)成員返回指向第一行第一列的指針。如果此指針為空,則該對象中沒有有效的輸入。檢查這是檢查您的圖像加載是否成功的最簡單的方法。如果存儲是連續(xù)的,我們可以使用它來遍歷整個數(shù)據(jù)指針。在灰度圖像的情況下,它將如下所示:
uchar * p = I.data;
for(unsigned int i = 0; i <ncol * nrows; ++ i)
* p ++ = table [* p];
你會得到相同的結(jié)果。但是,這段代碼稍后閱讀很難閱讀。如果你有更先進的技術(shù),那就更難了。此外,在實踐中,我觀察到您將獲得相同的性能結(jié)果(因為大多數(shù)現(xiàn)代編譯器可能會為您自動實現(xiàn)這種小型優(yōu)化技巧)。
如果有效的方式確保您通過適量的uchar字段,并跳過行之間可能發(fā)生的差距是您的責任。迭代程序方法被認為是更安全的方式,因為它從用戶接管這些任務(wù)。所有你需要做的是要求圖像矩陣的開始和結(jié)束,然后只是增加開始迭代程序,直到你到達結(jié)束。要獲取迭代程序指向的值,使用*運算符(在它之前添加)。
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
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;
}
在彩色圖像的情況下,我們每列有三個uchar項目。這可能被認為是一個簡短的uchar項目向量,已經(jīng)在OpenCV中使用Vec3b名稱進行了浸禮。要訪問第n個子列,我們使用簡單的operator []訪問。重要的是要記住,OpenCV迭代程序遍歷列,并自動跳到下一行。因此,如果使用簡單的uchar迭代程序,您將只能訪問藍色通道值。
最后的方法不推薦用于掃描。它是為了獲取或修改圖像中的某種方式的隨機元素。它的基本用法是指定要訪問的項目的行號和列號。在我們早期的掃描方法中,您可以通過我們正在查看的圖像來觀察這一點很重要。這在這里沒有什么不同,因為您需要手動指定在自動查找時要使用的類型。如果下列源代碼的灰度圖像(+ cv :: at()函數(shù)的用法),您可以觀察這一點:
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
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;
}
這些功能需要您的輸入類型和坐標,并即時計算查詢項目的地址。然后返回一個引用。當您設(shè)置值時,獲取值和非常數(shù)時,這可能是常數(shù)。作為調(diào)試模式的安全步驟*,執(zhí)行一個檢查,您的輸入坐標是有效的并且確實存在。如果不是這樣,您將在標準錯誤輸出流上獲得一個很好的輸出消息。與釋放模式中的有效方式相比,使用此方法的唯一區(qū)別是,對于圖像的每個元素,您將獲得一個新的行指針,以便我們使用C運算符[]獲取列元素。
如果您需要使用此方法對圖像執(zhí)行多次查找,則可能會麻煩和耗時地為每個訪問輸入類型和at關(guān)鍵字。為了解決這個問題OpenCV有一個cv :: Mat_數(shù)據(jù)類型。與Mat相同,在定義中需要通過查看數(shù)據(jù)矩陣來指定數(shù)據(jù)類型,但是您可以使用operator()快速訪問項目。為了使事情變得更好,這可以很容易地從和通常的cv :: Mat數(shù)據(jù)類型轉(zhuǎn)換。您可以在上方功能的彩色圖像的情況下看到此示例的用法。然而,重要的是要注意,cv :: at()可以完成相同的操作(具有相同的運行時速度)功能。對于懶惰的程序員的伎倆來說,這是一個更少的事情。
這是在圖像中實現(xiàn)查找表修改的一種獎勵方法。在圖像處理中,很常見的是要將所有給定的圖像值修改為其他值。OpenCV提供了修改圖像值的功能,無需編寫圖像的掃描邏輯。我們使用核心模塊的cv :: LUT()函數(shù)。首先我們構(gòu)建一個Mat類型的查找表:
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for( int i = 0; i < 256; ++i)
p[i] = table[i];
最后調(diào)用函數(shù)(我是我們的輸入圖像,J是輸出的一個):
LUT(I,lookUpTable,J);
為了最好的結(jié)果,編譯程序并以自己的速度運行它。為了使差異更加清晰,我使用了相當大的(2560 X 1600)圖像。這里呈現(xiàn)的性能是彩色圖像。為了獲得更準確的值,我將從函數(shù)調(diào)用得到的值平均為100次。
方法 | 時間 |
---|---|
高效的方式 | 79.4717毫秒 |
迭代程序 | 83.7201毫秒 |
在飛行RA | 93.7878毫秒 |
LUT功能 | 32.5759毫秒 |
我們可以總結(jié)一些事情。如果可能,請使用OpenCV已經(jīng)創(chuàng)建的功能(而不是重新創(chuàng)建它們)。最快的方法是LUT功能。這是因為OpenCV庫通過Intel Threaded Building Blocks啟用多線程。但是,如果你需要編寫一個簡單的圖像掃描,喜歡指針方法。迭代程序程序
是一個更安全的賭注,但是相當慢。使用即時參考訪問方法進行全圖像掃描是調(diào)試模式中最昂貴的。在釋放模式下,它可能會擊敗迭代程序方法,但是它肯定會犧牲迭代程序的安全性能。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: