<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何用OpenCV掃描圖像、查找表和測(cè)量時(shí)間(附鏈接)

          共 4008字,需瀏覽 9分鐘

           ·

          2021-08-05 02:31


          大數(shù)據(jù)文摘授權(quán)轉(zhuǎn)載自數(shù)據(jù)派THU
          翻譯:陳之炎
          校對(duì):張一然、林夕

          在這里將尋求以下問題的答案
          • 如何遍歷圖像的各個(gè)像素?
          • OpenCV的矩陣值是如何存儲(chǔ)的?
          • 如何衡量算法的性能?
          • 什么是查找表,為什么要使用查找表?

          測(cè)試案例

          首先來考慮一個(gè)簡(jiǎn)單的減色方法。利用C和C ++的無符號(hào)字符(unsigned char)數(shù)據(jù)類型來存儲(chǔ)矩陣項(xiàng),像素的一個(gè)通道可以具備256個(gè)不同的值。對(duì)于一幅三通道的圖像來說,可以構(gòu)造出多種色彩(色彩數(shù)量可達(dá)16,000,000種)。數(shù)量眾多的顏色會(huì)給算法的性能帶來沉重的負(fù)擔(dān)。然而,有些時(shí)候,往往利用較少的色彩數(shù)便能夠獲得同樣的結(jié)果。



          在這種情況下,常見的做法是減少色彩空間(color space reduction)。這意味著,將色彩空間的當(dāng)前值除以一個(gè)新的輸入值,從而減少顏色的數(shù)量。例如:將零和九之間的每一個(gè)值設(shè)置為零,十和十九之間的值設(shè)置為十等等。

          當(dāng)一個(gè)UCHAR 值(無符號(hào)字符unsigned char -0到255之間的值)除以INT值之后,其結(jié)果也將是一個(gè)數(shù)據(jù)類型為char的值,并且相除之后的結(jié)果值只能為char數(shù)據(jù)類型的值,小數(shù)部分將被舍去。利用C和C ++的這一優(yōu)勢(shì),對(duì) UCHAR域的操作可以表示為:

          簡(jiǎn)單的減色算法將該公式應(yīng)用于圖像矩陣中的每個(gè)像素,值得一提的是:我們進(jìn)行了一次除法和一次乘法運(yùn)算,這兩種運(yùn)算會(huì)耗費(fèi)昂貴的系統(tǒng)開銷。如果可能的話,可以用一些開銷相對(duì)來說比較小的操作來取代它們,如一些減法, 加法或者一些簡(jiǎn)單的賦值運(yùn)算操作。此外,需要注意的是,上述操作的輸入值的數(shù)量是有限的,對(duì)于UCHAR數(shù)據(jù)類型,準(zhǔn)確地來講,輸入值的數(shù)量為256。

          對(duì)于較大的圖像,則是通過使用查找表,將事先計(jì)算好所有可能的值在賦值階段直接進(jìn)行賦值操作。查找表是具有一個(gè)或多個(gè)維度的簡(jiǎn)單數(shù)組,對(duì)于給定的輸入值對(duì)應(yīng)一個(gè)確定的輸出值。它的優(yōu)勢(shì)在于:無需進(jìn)行計(jì)算,便能讀取到結(jié)果。

          測(cè)試?yán)蹋ê拖率龃a示例)將執(zhí)行以下操作:利用命令行參數(shù)傳遞讀取圖像(可以是彩色圖像或灰度圖像),對(duì)給定命令行參數(shù)的整數(shù)值進(jìn)行減色。在OpenCV中,主要有三種方式遍歷圖像的每個(gè)像素。為了增加實(shí)驗(yàn)的趣味性,會(huì)利用這三種方式掃描圖像,并打印出各自花費(fèi)的時(shí)長。

          可以在這里下載完整的源代碼,或者查看OpenCV的cpp教程示例代碼的核心部分。其基本用法是:


          最后一個(gè)參數(shù)是可選項(xiàng),除非加載的是給定的圖像的灰度格式,否則默認(rèn)使用BGR色彩空間。首先,需要做的第一件事是計(jì)算查找表。


          首先,利用C ++的stringstream類將第三個(gè)命令行參數(shù)由文本格式轉(zhuǎn)換為整數(shù)格式。然后,利用一個(gè)看似簡(jiǎn)單的公式計(jì)算查找表。此時(shí),沒有涉及到OpenCV的具體內(nèi)容。

          接下來的問題是如何測(cè)量時(shí)間?OpenCV提供了cv::getTickCount()和cv::getTickFrequency() 這兩個(gè)簡(jiǎn)單的函數(shù)來實(shí)現(xiàn)時(shí)間的測(cè)量。第一個(gè)函數(shù)cv::getTickCount()返回返回某個(gè)事件(如啟動(dòng)系統(tǒng))之后系統(tǒng)CPU 的嘀嗒(Tick)數(shù)量。第二個(gè)函數(shù)cv::getTickFrequency() 返回CPU每秒鐘發(fā)出多少次嘀嗒聲。有了這兩個(gè)函數(shù)之后,便很容易測(cè)量出兩個(gè)操作之間的時(shí)間間隔:

          https://docs.opencv.org/4.5.2/db/de0/group__core__utils.html


          如何在內(nèi)存中存儲(chǔ)圖像矩陣?

          在上一節(jié)Mat-基本圖像容器教程中,講到像素矩陣的大小取決于所使用的色彩系統(tǒng)。更準(zhǔn)確地說,取決于所使用的色彩通道數(shù)?;叶葓D像的情況是這樣的:
          多通道圖像的列包含許多子列,子列的數(shù)目即通道的數(shù)量。例如:BGR色彩系統(tǒng)圖像的情況是這樣的:
          注意,在這里通道的順序是相反的:在這里是BGR ,而不是RGB。因?yàn)樵诖蠖鄶?shù)情況下,內(nèi)存足夠大,可以一行接一行順序存儲(chǔ),形成一個(gè)單一的長行,有助于加快掃描的速度??梢允褂?cv::Mat::isContinuous()函數(shù)查詢矩陣是否以這種方式存儲(chǔ)。請(qǐng)繼續(xù)閱讀下一節(jié)中的示例。

          cv::Mat::isContinuous()
          https://docs.opencv.org/4.5.2/d3/d63/classcv_1_1Mat.html

          最為有效的方法


          通過經(jīng)典C風(fēng)格操作符(指針)的方式來獲取數(shù)據(jù)是性能最好的方法,因此對(duì)于賦值我們推薦的最高效的方法是:


          在這里,只需要獲取每一行起始的指針,然后遍歷到最后一行。在某些特殊情況下,像素矩陣以連續(xù)的方式存儲(chǔ),只需要一次“請(qǐng)求指針”的操作,便能一路到底遍歷所有的像素。對(duì)于彩色圖像有三個(gè)色彩通道,每一行需要遍歷三次。

          還有另一種方式:Mat 對(duì)象的數(shù)據(jù)成員data 會(huì)返回指向第一行、第一列的指針。如果這個(gè)指針為空,則這一對(duì)象中不存在有效的輸入。利用這種簡(jiǎn)單的方法,可以檢查圖像是否成功加載。如果像素存儲(chǔ)是連續(xù)的,我們可以用它來遍歷所有的數(shù)據(jù)指針。如果是灰度圖像, 代碼應(yīng)該是這樣的:


          上述兩種方法會(huì)得出相同的結(jié)果。然而,這段代碼閱讀起來會(huì)困難得多。如果你有更高級(jí)的技術(shù),它閱讀起來會(huì)變得更加困難。此外,在實(shí)踐中,得到的性能結(jié)果卻是相同的(因?yàn)榇蠖鄶?shù)現(xiàn)代編譯器會(huì)自動(dòng)對(duì)代碼進(jìn)行優(yōu)化)。

          迭代器(安全的)方法


          在上述所講的方法中,你要確保傳入正確數(shù)量的uchar數(shù)據(jù)類型值,并跳過行與行之間的間隙,對(duì)于用戶來說,迭代器方法(iterator method)被視為是一種更安全的方式, 因?yàn)樗鼜挠脩裟抢锝庸芰诉@些任務(wù)。利用迭代器方法,只需要找出圖像矩陣的起始行和結(jié)尾行,從起始行開始迭代,直到到達(dá)結(jié)尾行。使用*運(yùn)算符獲取迭代器指向的值(在迭代器前添加該符號(hào))。



          對(duì)于彩色圖像來說,每一列包含三個(gè)UCHAR數(shù)據(jù)項(xiàng),可以將這三個(gè)數(shù)據(jù)項(xiàng)視為一個(gè) UCHAR數(shù)據(jù)類型的短向量,在 OpenCV中,稱之為 Vec3b。用簡(jiǎn)單的操作符[]訪問第n個(gè)子列。需要記住的重點(diǎn)是:OpenCV的迭代器遍歷這些列,并會(huì)自動(dòng)跳到下一行。因此,在彩色圖像的情況下,如果采用一個(gè)簡(jiǎn)單的UCHAR迭代器,只能訪問到藍(lán)色通道的值。

          利用引用返回值計(jì)算即時(shí)地址


          不推薦采用最后一種方法掃描圖像。利用這種方法可以訪問或修改圖像中的隨機(jī)像素,基本的用法是:指定需要訪問元素所在的行數(shù)和列數(shù)。在前面所述的掃描方法中,需要指定數(shù)據(jù)類型,在這里同樣如此,在自動(dòng)查找之前,需要手動(dòng)指定使用什么數(shù)據(jù)類型。你可以在以下源代碼的灰度圖像的情況下觀察這一點(diǎn)(用到了+ cv::Mat::at() 函數(shù))


          該函數(shù)根據(jù)輸入的數(shù)據(jù)類型和坐標(biāo),計(jì)算出查詢項(xiàng)的地址,然后返回這個(gè)地址的引用值。當(dāng)get 這個(gè)引用值時(shí),會(huì)獲得一個(gè)常量,當(dāng)set 這個(gè)引用值,它是一個(gè)非常量。為了安全起見,僅在調(diào)試模式*,可以檢查輸入坐標(biāo)是否有效,是否確實(shí)存在。如果不是在調(diào)試模式下,會(huì)有標(biāo)準(zhǔn)錯(cuò)誤輸出流的錯(cuò)誤提示。相比于正式發(fā)布模式,二者唯一的區(qū)別是:對(duì)于圖像的每一個(gè)元素,你將獲得一個(gè)新的行指針,用于我們使用 C 運(yùn)算符 [] 獲取列元素的內(nèi)容。

          如果需要使用該方法對(duì)圖像做多次查找時(shí),輸入數(shù)據(jù)類型和坐標(biāo)的操作會(huì)相當(dāng)麻煩和費(fèi)時(shí)。為解決這一問題,OpenCV添加了 cv::Mat_ 數(shù)據(jù)類型,它與Mat類似,但額外需要在定義時(shí)通過要查看的數(shù)據(jù)矩陣的內(nèi)容來指定數(shù)據(jù)類型,但好處是你可以使用()操作符快速訪問矩陣值。更好的是,Mat和cv::Mat數(shù)據(jù)類型之間的可以很方便的進(jìn)行轉(zhuǎn)換。在上述示例中,可以看到這個(gè)函數(shù)在彩色圖像中的應(yīng)用。然而,需要注意的是:cv::Mat::at函數(shù)中已經(jīng)包含了相同的操作(具有相同的運(yùn)行速度)。它只是一個(gè)偷懶的編程技巧。

          cv::Mat_ 
          https://docs.opencv.org/4.5.2/df/dfc/classcv_1_1Mat__.html

          cv::Mat::at
          https://docs.opencv.org/4.5.2/d3/d63/classcv_1_1Mat.html

          核心功能


          這是在圖像中修改查找表的一個(gè)額外獎(jiǎng)勵(lì)的方法。在圖像處理中, 用戶常常會(huì)希望將給定的圖像值修改為其他值。OpenCV提供一個(gè)函數(shù),利用這個(gè)函數(shù),無需寫入圖像的掃描邏輯,便可修改圖像的像素值。在這里,用到核心模塊的cv::LUT() 函數(shù)。首先,創(chuàng)建一個(gè)Mat類型的查找表:

          cv::LUT() 
          https://docs.opencv.org/4.5.2/d2/de8/group__core__array.html


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


          性能差異對(duì)比

          編譯并運(yùn)行程序以獲得最佳結(jié)果。為使差別更加明晰,我用了一個(gè)相當(dāng)大(2560 X 1600)的彩色圖像。此處介紹的性能適用于彩色圖像. 為了得到更準(zhǔn)確的結(jié)果,我對(duì)上百次函數(shù)調(diào)用的結(jié)果做了平均。



          可以得出以下結(jié)論:盡可能使用(而不是徹底改造已有函數(shù))OpenCV已有的函數(shù)。LUT函數(shù)是最快的方法,因?yàn)镺penCV庫可以通過英特爾線程構(gòu)建模塊啟用多線程。然而,如果需要編寫一個(gè)簡(jiǎn)單的圖像掃描方法可選擇指針方法,迭代器是一個(gè)更加安全的選擇,但是速度相對(duì)來說要慢一些。在調(diào)試模式下,使用引用返回值訪問方法掃描全圖的代價(jià)最高;在正式發(fā)布模式下,可能會(huì)優(yōu)于迭代方法,但它以犧牲迭代器的安全特性為代價(jià)。

          最后,可以觀看YouTube頻道上發(fā)布的程序運(yùn)行視頻。
          https://www.youtube.com/watch?v=fB3AN5fjgwc


          點(diǎn)「在看」的人都變好看了哦!
          瀏覽 40
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码操 | 三区在线视频观看 | 伊人大香蕉网 | 鸡巴视频久久 | 激情深爱五月 |