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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

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

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

目錄

OpenCV如何掃描圖像、利用查找表和計(jì)時(shí)

1.函數(shù)計(jì)算時(shí)間測(cè)試case

2. Mat圖像的存儲(chǔ)機(jī)理

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

4. 實(shí)例


OpenCV如何掃描圖像、利用查找表和計(jì)時(shí)

  • 如何計(jì)算函數(shù)運(yùn)行時(shí)間?

  • Mat圖像如何存儲(chǔ)?

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

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

    ?

?

1.函數(shù)計(jì)算時(shí)間測(cè)試case

OpenCV提供了兩個(gè)簡(jiǎn)便的可用于計(jì)時(shí)的函數(shù) getTickCount() 和 getTickFrequency() 。

第一個(gè)函數(shù)返回你的CPU自某個(gè)事件(如啟動(dòng)電腦)以來(lái)走過(guò)的時(shí)鐘周期數(shù).

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

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

?

2. Mat圖像的存儲(chǔ)機(jī)理

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

而對(duì)多通道圖像來(lái)說(shuō),矩陣中的列會(huì)包含多個(gè)子列,其子列個(gè)數(shù)與通道數(shù)相等。例如,RGB顏色模型的矩陣:

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

?

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

3.1高效的方法 Efficient Way

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

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

這里,我們獲取了每一行開(kāi)始處的指針,然后遍歷至該行末尾。如果矩陣是以連續(xù)方式存儲(chǔ)的,我們只需請(qǐng)求一次指針、然后一路遍歷下去就行。彩色圖像的情況有必要加以注意:因?yàn)槿齻€(gè)通道的原因,我們需要遍歷的元素?cái)?shù)目也是3倍。

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

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

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

因此推薦采用該方法遍歷

    int i,j;uchar* p; for( i = 0; i < nRows; ++i){p = I.ptr<uchar>(i); ? ? ?  //行指針,拿到一行的地址開(kāi)端for ( j = 0; j < nCols; ++j){p[j] = table[p[j]]; ? ? ? ? ? ? }}

?

3.2迭代法 The iterator (safe) method

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

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

3.3通過(guò)相關(guān)返回值的On-the-fly地址計(jì)算

事實(shí)上這個(gè)方法并不推薦被用來(lái)進(jìn)行圖像掃描,它本來(lái)是被用于獲取或更改圖像中的隨機(jī)元素。它的基本用途是要確定你試圖訪問(wèn)的元素的所在行數(shù)與列數(shù)。在前面的掃描方法中,我們觀察到知道所查詢的圖像數(shù)據(jù)類型是很重要的。這里同樣的你得手動(dòng)指定好你要查找的數(shù)據(jù)類型。下面的代碼中是一個(gè)關(guān)于灰度圖像的示例(運(yùn)用 + 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ù)類型及需求元素的坐標(biāo),返回的是一個(gè)對(duì)應(yīng)的值-如果用 get 則是constant,如果是用 set 、則為non-constant. 處于程序安全,當(dāng)且僅當(dāng)在 debug 模式下 它會(huì)檢查你的輸入坐標(biāo)是否有效或者超出范圍. 如果坐標(biāo)有誤,則會(huì)輸出一個(gè)標(biāo)準(zhǔn)的錯(cuò)誤信息. 和高性能法(the efficient way)相比, 在 release模式下,它們之間的區(qū)別僅僅是On-the-fly方法對(duì)于圖像矩陣的每個(gè)元素,都會(huì)獲取一個(gè)新的行指針,通過(guò)該指針和[]操作來(lái)獲取列元素.

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

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

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

#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如何扫描图像、利用查找表和计时的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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