日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

OpenCV 【十二】OpenCV如何扫描图像、利用查找表和计时

發(fā)布時間:2023/11/27 生活经验 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV 【十二】OpenCV如何扫描图像、利用查找表和计时 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

OpenCV如何掃描圖像、利用查找表和計時

1.函數(shù)計算時間測試case

2. Mat圖像的存儲機理

3. 像素遍歷的3--4種方式

4. 實例


OpenCV如何掃描圖像、利用查找表和計時

  • 如何計算函數(shù)運行時間?

  • Mat圖像如何存儲?

  • 如何高效遍歷圖像像素?

  • 查找表是什么?為什么要用它?

    ?

?

1.函數(shù)計算時間測試case

OpenCV提供了兩個簡便的可用于計時的函數(shù) getTickCount() 和 getTickFrequency() 。

第一個函數(shù)返回你的CPU自某個事件(如啟動電腦)以來走過的時鐘周期數(shù).

第二個函數(shù)返回你的CPU一秒鐘所走的時鐘周期數(shù)。這樣,我們就能輕松地以秒為單位對某運算計時。

double t = (double)getTickCount();
// 做點什么 ...
t = ((double)getTickCount() - t)/getTickFrequency();
cout << "Times passed in seconds: " << t << endl;

?

2. Mat圖像的存儲機理

圖像矩陣的大小取決于我們所用的顏色模型,確切地說,取決于所用通道數(shù)。如果是灰度圖像,矩陣就會像這樣:

而對多通道圖像來說,矩陣中的列會包含多個子列,其子列個數(shù)與通道數(shù)相等。例如,RGB顏色模型的矩陣:

注意到,子列的通道順序是反過來的:BGR而不是RGB。很多情況下,因為內(nèi)存足夠大,可實現(xiàn)連續(xù)存儲,因此,圖像中的各行就能一行一行地連接起來,形成一個長行。連續(xù)存儲有助于提升圖像掃描速度,我們可以使用 isContinuous() 來去判斷矩陣是否是連續(xù)存儲的. 實例見4。

?

3. 像素遍歷的3--4種方式

3.1高效的方法 Efficient Way

說到性能,經(jīng)典的C風格運算符[](指針)訪問要更勝一籌. 因此,我們推薦的效率最高的查找表賦值方法,還是下面的這種:

Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{// accept only char type matricesCV_Assert(I.depth() != sizeof(uchar)); ? ? 
?int channels = I.channels();
?int nRows = I.rows * channels; int nCols = I.cols;
?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; 
}

這里,我們獲取了每一行開始處的指針,然后遍歷至該行末尾。如果矩陣是以連續(xù)方式存儲的,我們只需請求一次指針、然后一路遍歷下去就行。彩色圖像的情況有必要加以注意:因為三個通道的原因,我們需要遍歷的元素數(shù)目也是3倍。

這里有另外一種方法來實現(xiàn)遍歷功能,就是使用 data , data會從 Mat 中返回指向矩陣第一行第一列的指針。注意如果該指針為NULL則表明對象里面無輸入,所以這是一種簡單的檢查圖像是否被成功讀入的方法。當矩陣是連續(xù)存儲時,我們就可以通過遍歷 data 來掃描整個圖像。例如,一個灰度圖像,其操作如下:

uchar* p = I.data;
?
for( unsigned int i =0; i < ncol*nrows; ++i)*p++ = table[*p];

這回得出和前面相同的結(jié)果。但是這種方法編寫的代碼可讀性方面差,并且進一步操作困難。同時,在實際應用中,該方法的性能表現(xiàn)上并不明顯優(yōu)于前一種(因為現(xiàn)在大多數(shù)編譯器都會對這類操作做出優(yōu)化)。

因此推薦采用該方法遍歷

    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]]; ? ? ? ? ? ? }}

?

3.2迭代法 The iterator (safe) method

在高性能法(the efficient way)中,我們可以通過遍歷正確的 uchar 域并跳過行與行之間可能的空缺-你必須自己來確認是否有空缺,來實現(xiàn)圖像掃描,迭代法則被認為是一種以更安全的方式來實現(xiàn)這一功能。在迭代法中,你所需要做的僅僅是獲得圖像矩陣的begin和end,然后增加迭代直至從begin到end。將*操作符添加在迭代指針前,即可訪問當前指向的內(nèi)容。

Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{// accept only char type matricesCV_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; 
}

對于彩色圖像中的一行,每列中有3個uchar元素,這可以被認為是一個小的包含uchar元素的vector,OpenCV中用 Vec3b 來命名。如果要訪問第n個子列,我們只需要簡單的利用[]來操作就可以。需要指出的是,OpenCV的迭代在掃描過一行中所有列后會自動跳至下一行,所以說如果在彩色圖像中如果只使用一個簡單的 uchar 而不是 Vec3b 迭代的話就只能獲得藍色通道(B)里的值。

3.3通過相關返回值的On-the-fly地址計算

事實上這個方法并不推薦被用來進行圖像掃描,它本來是被用于獲取或更改圖像中的隨機元素。它的基本用途是要確定你試圖訪問的元素的所在行數(shù)與列數(shù)。在前面的掃描方法中,我們觀察到知道所查詢的圖像數(shù)據(jù)類型是很重要的。這里同樣的你得手動指定好你要查找的數(shù)據(jù)類型。下面的代碼中是一個關于灰度圖像的示例(運用 + at() 函數(shù)):

Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{// accept only char type matricesCV_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;
}

該函數(shù)輸入為數(shù)據(jù)類型及需求元素的坐標,返回的是一個對應的值-如果用 get 則是constant,如果是用 set 、則為non-constant. 處于程序安全,當且僅當在 debug 模式下 它會檢查你的輸入坐標是否有效或者超出范圍. 如果坐標有誤,則會輸出一個標準的錯誤信息. 和高性能法(the efficient way)相比, 在 release模式下,它們之間的區(qū)別僅僅是On-the-fly方法對于圖像矩陣的每個元素,都會獲取一個新的行指針,通過該指針和[]操作來獲取列元素.

當你對一張圖片進行多次查詢操作時,為避免反復輸入數(shù)據(jù)類型和at帶來的麻煩和浪費的時間,OpenCV 提供了:basicstructures:Mat_ <id3> data type. 它同樣可以被用于獲知矩陣的數(shù)據(jù)類型,你可以簡單利用()操作返回值來快速獲取查詢結(jié)果。

3.4 核心函數(shù)LUT(The Core Function)

這是最被推薦的用于實現(xiàn)批量圖像元素查找和更該操作圖像方法。在圖像處理中,對于一個給定的值,將其替換成其他的值是一個很常見的操作,OpenCV 提供里一個函數(shù)直接實現(xiàn)該操作,并不需要你自己掃描圖像,就是:operationsOnArrays:LUT() <lut> ,一個包含于core module的函數(shù). 首先我們建立一個mat型用于查表:

    Mat lookUpTable(1, 256, CV_8U);uchar* p = lookUpTable.data; for( int i = 0; i < 256; ++i)p[i] = table[i];

然后我們調(diào)用函數(shù) (I 是輸入 J 是輸出):

LUT(I, lookUpTable, J);

?

4. 實例

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <sstream>
?
using namespace std; 
using namespace cv;
?
void help()
{cout<< "\n--------------------------------------------------------------------------" << endl<< "This program shows how to scan image objects in OpenCV (cv::Mat). As use case"<< " we take an input image and divide the native color palette (255) with the "  << endl<< "input. Shows C operator[] method, iterators and at function for on-the-fly item address calculation."<< endl<< "Usage:" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? << endl<< "./howToScanImages imageNameToUse divideWith [G]" ? ? ? ? ? ? ? ? ? ? ? ? ? ?  << endl<< "if you add a G parameter the image is processed in gray scale" ? ? ? ? ? ? ?  << endl<< "--------------------------------------------------------------------------" ? << endl << endl;
}
?
Mat& ScanImageAndReduceC(Mat& I, const uchar* table);
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* table);
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar * table);
?
int main( int argc, char* argv[])
{help(); if (argc < 3){cout << "Not enough parameters" << endl;return -1; }
?Mat I, J;if( argc == 4 && !strcmp(argv[3],"G") )I = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);elseI = imread(argv[1], CV_LOAD_IMAGE_COLOR);
?if (!I.data){cout << "The image" << argv[1] << " could not be loaded." << endl;return -1;}
?int divideWith; // convert our input string to number - C++ stylestringstream s;s << argv[2];s >> divideWith;if (!s){cout << "Invalid number entered for dividing. " << endl; return -1;}uchar table[256]; for (int i = 0; i < 256; ++i)table[i] = divideWith* (i/divideWith);
?const int times = 100; double t;
?t = (double)getTickCount(); ? ?for (int i = 0; i < times; ++i)J = ScanImageAndReduceC(I.clone(), table);
?t = 1000*((double)getTickCount() - t)/getTickFrequency();t /= times;
?cout << "Time of reducing with the C operator [] (averaged for " << times << " runs): " << t << " milliseconds."<< endl; ?
?t = (double)getTickCount(); ? ?
?for (int i = 0; i < times; ++i)J = ScanImageAndReduceIterator(I.clone(), table);
?t = 1000*((double)getTickCount() - t)/getTickFrequency();t /= times;
?cout << "Time of reducing with the iterator (averaged for " << times << " runs): " << t << " milliseconds."<< endl; ?
?t = (double)getTickCount(); ? ?
?for (int i = 0; i < times; ++i)ScanImageAndReduceRandomAccess(I.clone(), table);
?t = 1000*((double)getTickCount() - t)/getTickFrequency();t /= times;
?cout << "Time of reducing with the on-the-fly address generation - at function (averaged for " << times << " runs): " << t << " milliseconds."<< endl; ?
?Mat lookUpTable(1, 256, CV_8U);uchar* p = lookUpTable.data; for( int i = 0; i < 256; ++i)p[i] = table[i];
?t = (double)getTickCount(); ? ?for (int i = 0; i < times; ++i)LUT(I, lookUpTable, J);
?t = 1000*((double)getTickCount() - t)/getTickFrequency();t /= times;
?cout << "Time of reducing with the LUT function (averaged for " << times << " runs): " << t << " milliseconds."<< endl; ?return 0; 
}
?
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{// accept only char type matricesCV_Assert(I.depth() != sizeof(uchar)); ? ? 
?int channels = I.channels();
?int nRows = I.rows * channels; int nCols = I.cols;
?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; 
}
?
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{// accept only char type matricesCV_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; 
}
?
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{// accept only char type matricesCV_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;
}

總結(jié)

以上是生活随笔為你收集整理的OpenCV 【十二】OpenCV如何扫描图像、利用查找表和计时的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。