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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co

發布時間:2025/3/21 编程问答 34 豆豆

OpenCV學習筆記(四十一)——再看基礎數據結構core?

記得我在OpenCV學習筆記(四)——新版本的數據結構core里面講過新版本的數據結構了,可是我再看這部分的時候,我發現我當時實在是看得太馬虎了。對于新版本的數據結構,我再說說。

Point_類不用多言,里面兩個成員變量x,y。Point_<int>就是Point2i,也是Point,Point_<float>就是Point2f,Point_<double>就是Point2d

Point3_類不太常用,跟Point_類差不太多,成員變量x,y,z

Size_類成員變量width、height。Size_<int>就是Size2i,也是Size,Size_<float>就是Size2f大家就要不臆斷出來個Size2d啥的讓編譯器發懵了

Rect_類有些意思,成員變量x、y、width、height,分別為左上角點的坐標和矩形的寬和高。常用的成員函數有Size()返回值為一個Size,area()返回矩形的面積,contains(Point)用來判斷點是否在矩形內,inside(Rect)函數判斷矩形是否在該矩形內,tl()返回左上角點坐標,br()返回右下角點坐標。值得注意的是,如果想求兩個矩形的交集,并集,可以用如下格式

[cpp] view plain copy
  • Rect?rect?=?rect1?&?rect2;??
  • Rect?rect?=?rect1?|?rect2;??
  • 如果想讓矩形平移操作,縮放操作,甚至可以這樣寫 [cpp] view plain copy
  • Rect?rectShift?=?rect?+?point;??
  • Rect?rectScale?=?rect?+?size;??
  • 是不是很妙,可以大大減輕你的代碼量。

    Matx其實是個輕量級的Mat,必須在使用前規定好大小,比如一個2*3的float型的可以聲明為Matx23f。我想很容易理解的。

    Vec是Matx的一個派生類,就是一個1維的Matx,跟vector很相似。比如想聲明一個10個數據的float數組,可以寫成Vec2f。

    這樣就很容易引出一個大家經常使用的數據結構了,Scalar_類,這個類其實就是一個Vec4x的一個變種,大家常用的Scalar其實就是Scalar_<double>。這樣一說,大家是不是就很容易理解了,為啥很多函數的參數輸入可以是Mat,也可以是Scalar了。其實OpenCV定義的InputMat,outputMat參數格式,以上的這幾種數據結構都可以作為參數的

    接下來介紹一個有意思的類Range,大家可能用的不多,對它不熟悉,其實它就是為了使OpenCV的使用更像Matlab而產生的。比如Range::all()其實就是Matlab里的符號:或者...。而Range(a, b)其實就是Matlab中的a:b。有趣吧,注意a,b都需要是int型的哦,親。

    Ptr類我就不太敢介紹了,是智能指針,我也沒有用過,文檔里說是很類似大名鼎鼎的Boost庫里的shared_ptr。希望以后有機會用一下再拿出來分享心得。

    最后出場的還是最重量級的Mat,介紹幾個比較重要的成員變量flag(就是我之前說過的header里的結構信息,深度信息,通道數),dims是Mat的維數,要求大于等于2,rows和cols參數代表2維矩陣的行數列數(對于更高維的矩陣,這兩個參數都是-1),還有個比較常用的參數應該uchar*data,是Mat的數據指針(比較暴力的同學可以直接調用它好了,不推薦),還有個參數refconst,我理解應該就是我上一講提到的釋放內存的時候要判斷這個矩陣是否是最后一個被使用的,這個參數應該就是控制跟當前矩陣結構相關的個數的。



    OpenCV學習筆記(四十二)——Mat數據操作之普通青年、文藝青年、暴力青年?

    首先還是要感謝簫鳴朋友在我《OpenCV學習筆記(四十)——再談OpenCV數據結構Mat詳解》的留言,告訴我M.at<float>(3, 3)在Debug模式下運行緩慢,推薦我使用M.ptr<float>(i)此類方法。這不禁勾起了我測試一下的沖動。下面就為大家奉上我的測試結果。

    我這里測試了三種操作Mat數據的辦法,套用流行詞,普通青年,文藝青年,為啥第三種我不叫2b青年,大家慢慢往后看咯。

    普通青年的操作的辦法通常是M.at<float>(i, j)

    文藝青年一般會走路線M.ptr<float>( i )[ j ]

    暴力青年通常直接強制使用我第40講提到的M.data這個指針

    實驗代碼如下:

    [cpp] view plain copy
  • t?=?(double)getTickCount();??
  • Mat?img1(1000,?1000,?CV_32F);??
  • ??
  • for?(int?i=0;?i<1000;?i++)??
  • {??
  • ????for?(int?j=0;?j<1000;?j++)??
  • ????{??
  • ????????img1.at<float>(i,j)?=?3.2f;??
  • ????}??
  • }??
  • t?=?(double)getTickCount()?-?t;??
  • printf("in?%gms\n",?t*1000/getTickFrequency());??
  • //***************************************************************??
  • t?=?(double)getTickCount();??
  • Mat?img2(1000,?1000,?CV_32F);??
  • ??
  • for?(int?i=0;?i<1000;?i++)??
  • {??
  • ????for?(int?j=0;?j<1000;?j++)??
  • ????{??
  • ????????img2.ptr<float>(i)[j]?=?3.2f;??
  • ????}??
  • }??
  • t?=?(double)getTickCount()?-?t;??
  • printf("in?%gms\n",?t*1000/getTickFrequency());??
  • //***************************************************************??
  • t?=?(double)getTickCount();??
  • Mat?img3(1000,?1000,?CV_32F);??
  • float*?pData?=?(float*)img3.data;??
  • ??
  • for?(int?i=0;?i<1000;?i++)??
  • {??
  • ????for?(int?j=0;?j<1000;?j++)??
  • ????{??
  • ????????*(pData)?=?3.2f;??
  • ????????pData++;??
  • ????}??
  • }??
  • t?=?(double)getTickCount()?-?t;??
  • printf("in?%gms\n",?t*1000/getTickFrequency());??
  • //***************************************************************??
  • t?=?(double)getTickCount();??
  • Mat?img4(1000,?1000,?CV_32F);??
  • ??
  • for?(int?i=0;?i<1000;?i++)??
  • {??
  • ????for?(int?j=0;?j<1000;?j++)??
  • ????{??
  • ????????((float*)img3.data)[i*1000+j]?=?3.2f;??
  • ????}??
  • }??
  • t?=?(double)getTickCount()?-?t;??
  • printf("in?%gms\n",?t*1000/getTickFrequency());??

  • 最后兩招可以都看成是暴力青年的方法,因為反正都是指針的操作,局限了各暴力青年手段就不顯得暴力了。

    在Debug、Release模式下的測試結果分別為:

    測試結果
    ?DebugRelease
    普通青年139.06ms2.51ms
    文藝青年66.28ms2.50ms
    暴力青年14.95ms2.28ms
    暴力青年25.11ms1.37ms

    根據測試結果,我覺得簫銘說的是很可信的,普通青年的操作在Debug模式下果然緩慢,他推薦的文藝青年的路線確實有提高。值得注意的是本來后兩種辦法確實是一種比較2b青年的做法,因為at操作符或者ptr操作符,其實都是有內存檢查的,防止操作越界的,而直接使用data這個指針確實很危險。不過從速度上確實讓人眼前一亮,所以我不敢稱這樣的青年為2b,尊稱為暴力青年吧。

    不過在Release版本下,幾種辦法的速度差別就不明顯啦,都是很普通的青年。所以如果大家最后發行程序的時候,可以不在意這幾種操作辦法的,推薦前兩種哦,都是很好的寫法,操作指針的事還是留給大神們用吧。就到這里吧~~

    補充:簫銘又推薦了兩種文藝青年的處理方案,我也隨便測試了一下,先貼代碼,再貼測試結果:

    [cpp] view plain copy
  • /*********加強版********/??
  • t?=?(double)getTickCount();??
  • Mat?img5(1000,?1000,?CV_32F);??
  • float?*pData1;??
  • for?(int?i=0;?i<1000;?i++)???
  • {???
  • ????pData1=img5.ptr<float>(i);??
  • ????for?(int?j=0;?j<1000;?j++)???
  • ????{???
  • ????????pData1[j]?=?3.2f;???
  • ????}???
  • }???
  • t?=?(double)getTickCount()?-?t;??
  • printf("in?%gms\n",?t*1000/getTickFrequency());??
  • /*******終極版*****/??
  • t?=?(double)getTickCount();??
  • Mat?img6(1000,?1000,?CV_32F);??
  • float?*pData2;??
  • Size?size=img6.size();??
  • if(img2.isContinuous())??
  • {??
  • ????size.width?=?size.width*size.height;??
  • ????size.height?=?1;??
  • }??
  • size.width*=img2.channels();??
  • for(int?i=0;?i<size.height;?i++)??
  • {??
  • ????pData2?=?img6.ptr<float>(i);??
  • ????for(int?j=0;?j<size.width;?j++)??
  • ????{??
  • ????????pData2[j]?=?saturate_cast<float>(3.2f);??
  • ????}??
  • }??
  • t?=?(double)getTickCount()?-?t;??
  • printf("in?%gms\n",?t*1000/getTickFrequency());??

  • 測試結果:

    ?DebugRelease
    加強版文藝青年5.74ms2.43ms
    終極版文藝青年40.12ms2.34ms
    我的測試結果感覺這兩種方案只是錦上添花的效果,也使大家的操作有了更多的選擇,但感覺在速度上并沒有數量級的提升,再次感謝簫銘對我blog的支持。后來簫銘說saturate_cast才把速度降下來,我很同意,就不貼上去測試結果了。但我查看資料了解了一下 saturate_cast的作用。 可以看成是類型的強制轉換,比如對于saturate_cast<uchar>來說,就是把數據轉換成8bit的0~255區間,負值變成0,大于255的變成255。如果是浮點型的數據,變成round最近的整數,還是很有用處的函數,推薦大家在需要的時候嘗試。


    OpenCV學習筆記(四十三)——存取像素值操作匯總core

    在上一講OpenCV學習筆記(四十二)——Mat數據操作之普通青年、文藝青年、暴力青年里,對Mat內數據的各種讀寫操作進行了速度的比較,都是我自己想到的方法,感覺不夠系統,這次整理了下思路,參考了文獻,把能想到的方法進行了匯總,希望能對大家有所幫助。

    1.存取單個像素值

    最通常的方法就是

    [cpp] view plain copy
  • img.at<uchar>(i,j)?=?255;??
  • img.at<Vec3b>(i,j)[0]?=?255;??

  • 如果你覺得at操作顯得太笨重了,不想用Mat這個類,也可以考慮使用輕量級的Mat_類,使用重載操作符()實現取元素的操作。 [cpp] view plain copy
  • cv::Mat_<uchar>?im2=?img;?//?im2?refers?to?image??
  • ???im2(50,100)=?0;?//?access?to?row?50?and?column?100??

  • 2.用指針掃描一幅圖像

    對于一幅圖像的掃描,用at就顯得不太好了,還是是用指針的操作方法更加推薦。先介紹一種上一講提到過的

    [cpp] view plain copy
  • for?(int?j=0;?j<nl;?j++)??
  • {??
  • ????????uchar*?data=?image.ptr<uchar>(j);??
  • ????????for?(int?i=0;?i<nc;?i++)??
  • ???????{???????????????????
  • ??????????????????data[i]?=?255;??
  • ????????}??
  • }??

  • 更高效的掃描連續圖像的做法可能是把W*H的衣服圖像看成是一個1*(w*h)的一個一維數組,這個想法是不是有點奇葩,這里要利用 isContinuous這個函數判斷圖像內的像素是否填充滿,使用方法如下: [cpp] view plain copy
  • if?(img.isContinuous())??
  • {??
  • ????????nc?=?img.rows*img.cols*img.channels();??
  • }??
  • uchar*?data?=?img.ptr<uchar>(0);??
  • for?(int?i=0;?i<nc;?i++)??
  • {??
  • ????????data[i]?=?255;??
  • }??

  • 更低級的指針操作就是使用Mat里的data指針,之前我稱之為暴力青年,使用方法如下: [cpp] view plain copy
  • uchar*?data?=?img.data;??
  • //?img.at(i,?j)??
  • data?=?img.data?+?i?*?img.step?+?j?*?img.elemSize();??

  • 3.用迭代器iterator掃描圖像

    和C++STL里的迭代器類似,Mat的迭代器與之是兼容的。是MatIterator_。聲明方法如下:

    [cpp] view plain copy
  • cv::MatIterator_<Vec3b>?it;??

  • 或者是: [cpp] view plain copy
  • cv::Mat_<Vec3b>::iterator?it;??

  • 掃描圖像的方法如下: [cpp] view plain copy
  • Mat_<Vec3b>::iterator?it?=?img.begin<Vec3b>();??
  • Mat_<Vec3b>::iterator?itend?=?img.end<Vec3b>();??
  • for?(;?it!=itend;?it++)??
  • {??
  • ?????????(*it)[0]?=?255;??
  • }??

  • 4.高效的scan image方案總結

    還是用我們之前使用過的getTickCount、getTickFrequency函數測試速度。這里我就不一一列舉我測試的結果了,直接上結論。測試發現,好的編寫風格可以提高50%的速度!要想減少程序運行的時間,必要的優化包括如下幾個方面:

    (1)內存分配是個耗時的工作,優化之

    (2)在循環中重復計算已經得到的值,是個費時的工作,優化之;舉例:

    [cpp] view plain copy
  • int?nc?=?img.cols?*?img.channels();??
  • for?(int?i=0;?i<nc;?i++)??
  • {.......}??
  • //**************************??
  • for?(int?i=0;?i<img.cols?*?img.channels();?i++)??
  • {......}??

  • 后者的速度比前者要慢上好多。

    (3)使用迭代器也會是速度變慢,但迭代器的使用可以減少程序錯誤的發生幾率,考慮這個因素,可以酌情優化

    (4)at操作要比指針的操作慢很多,所以對于不連續數據或者單個點處理,可以考慮at操作,對于連續的大量數據,不要使用它

    (5)掃描連續圖像的做法可能是把W*H的衣服圖像看成是一個1*(w*h)的一個一維數組這種辦法也可以提高速度。短的循環比長循環更高效,即使他們的操作數是相同的

    以上的這些優化可能對于大家的程序運行速度提高并不明顯,但它們畢竟是個得到速度提升的好的編程策略,希望大家能多采納。

    還有就是利用多線程也可以高效提高運行速度。OpenMP和TBB是兩種流行的APT,不過對于多線程的東西,我是有些迷糊的,呵呵

    5.整行整列像素值的賦值

    對于整行或者整列的數據,可以考慮這種方式處理

    [cpp] view plain copy
  • img.row(i).setTo(Scalar(255));??
  • img.col(j).setTo(Scalar(255));??

  • 這節就先介紹這么多攻略吧~希望大家喜歡


    OpenCV學習筆記(四十四)——初探GPU

    好久沒有更新啦,感覺最近沒有什么特別的收獲值得和大家分享,還是有些懶,TLD結束了也沒有寫個blog做總結。還是和大家分享一下OpenCV的一個大家很少接觸的模塊吧——GPU。這個部分我接觸的也是很少,只是根據教程和大家簡單交流一下,如果有高手有使用心得,歡迎多多批評。

    OpenCV的GPU模塊只支持NVIDIA的顯卡,原因是該部分是基于NVIDIA的CUDA和NVIDIA的NPP模塊實現的。而該模塊的好處在于使用GPU模塊無需安裝CUDA工具,也無需學習GPU編程,因為不需要編寫GPU相關的代碼。但如果你想重新編譯OpenCV的GPU模塊的話,還是需要CUDA的toolkit。

    由于GPU模塊的發展,使大部分函數使用起來和之前在CPU下開發非常類似。首先,就是把GPU模塊鏈接到你的工程中,并包含必要的頭文件gpu.hpp。其次,就是GPU模塊下的數據結構,原本在cv名字空間中的現在都在gpu名字空間中,使用時可以gpu::和cv::來防止混淆。

    需要再說明的是,在GPU模塊中,矩陣的名字為GpuMat,而不是之前的Mat,其他的函數名字和CPU模塊中相同,不同的是,現在的參數輸入不再是Mat,而是GpuMat。

    還有一個問題就是,對于2.0的GPU模塊,多通道的函數支持的并不好,推薦使用GPU模塊處理灰度的圖像。有些情況下,使用GPU模塊的運行速度還不及CPU模塊下的性能,所以可以認為,GPU模塊相對而言還不夠成熟,需要進一步優化。很重要的一個原因就是內存管理部分和數據轉換部分對于GPU模塊而言消耗了大量的時間。

    需要注意的是,在所有使用GPU模塊的函數之前,最好需要調用函數gpu::getCudaEnabledDeviceCount,如果你在使用的OpenCV模塊編譯時不支持GPU,這個函數返回值為0;否則返回值為已安裝的CUDA設備的數量。

    還有一點就是使用GPU模塊,需要在用CMake編譯OpenCV時使其中的WITH_CUDAWITH_TBB的宏生效,為ON

    由于我對GPU部分的熟悉程度還不行,先拿來一段sample自帶的一段求矩陣轉置的程序來做例子,代碼如下:

    [cpp] view plain copy
  • #include?<iostream>??
  • #include?"cvconfig.h"??
  • #include?"opencv2/core/core.hpp"??
  • #include?"opencv2/gpu/gpu.hpp"??
  • #include?"opencv2/core/internal.hpp"?//?For?TBB?wrappers??
  • ??
  • using?namespace?std;??
  • using?namespace?cv;??
  • using?namespace?cv::gpu;??
  • ??
  • struct?Worker?{?void?operator()(int?device_id)?const;?};??
  • ??
  • int?main()??
  • {??
  • ????int?num_devices?=?getCudaEnabledDeviceCount();??
  • ????if?(num_devices?<?2)??
  • ????{??
  • ????????std::cout?<<?"Two?or?more?GPUs?are?required\n";??
  • ????????return?-1;??
  • ????}??
  • ????for?(int?i?=?0;?i?<?num_devices;?++i)??
  • ????{??
  • ????????DeviceInfo?dev_info(i);??
  • ????????if?(!dev_info.isCompatible())??
  • ????????{??
  • ????????????std::cout?<<?"GPU?module?isn't?built?for?GPU?#"?<<?i?<<?"?("??
  • ?????????????????<<?dev_info.name()?<<?",?CC?"?<<?dev_info.majorVersion()??
  • ?????????????????<<?dev_info.minorVersion()?<<?"\n";??
  • ????????????return?-1;??
  • ????????}??
  • ????}??
  • ??
  • ????//?Execute?calculation?in?two?threads?using?two?GPUs??
  • ????int?devices[]?=?{0,?1};??
  • ????parallel_do(devices,?devices?+?2,?Worker());??
  • ??
  • ????return?0;??
  • }??
  • ??
  • ??
  • void?Worker::operator()(int?device_id)?const??
  • {??
  • ????setDevice(device_id);??
  • ??
  • ????Mat?src(1000,?1000,?CV_32F);??
  • ????Mat?dst;??
  • ??
  • ????RNG?rng(0);??
  • ????rng.fill(src,?RNG::UNIFORM,?0,?1);??
  • ??
  • ????//?CPU?works??
  • ????transpose(src,?dst);??
  • ??
  • ????//?GPU?works??
  • ????GpuMat?d_src(src);??
  • ????GpuMat?d_dst;??
  • ????transpose(d_src,?d_dst);??
  • ??
  • ????//?Check?results??
  • ????bool?passed?=?norm(dst?-?Mat(d_dst),?NORM_INF)?<?1e-3;??
  • ????std::cout?<<?"GPU?#"?<<?device_id?<<?"?("?<<?DeviceInfo().name()?<<?"):?"??
  • ????????<<?(passed???"passed"?:?"FAILED")?<<?endl;??
  • ??
  • ????//?Deallocate?data?here,?otherwise?deallocation?will?be?performed??
  • ????//?after?context?is?extracted?from?the?stack??
  • ????d_src.release();??
  • ????d_dst.release();??
  • }??

  • 以上介紹的內容不但膚淺,而且顯得比較凌亂。希望高手看完后多多指正,跟我一樣不太明白的朋友僅供參考。


    OpenCV學習筆記(四十五)——小試隨機森林(random forest)算法ml

    對于隨機森林算法,原理我想大家都會去看論文,推薦兩個老外的網址http://www.stat.berkeley.edu/users/breiman/RandomForests/和https://cwiki.apache.org/MAHOUT/random-forests.html,第一個網址是提出隨機森林方法大牛寫的,很全面具體,第二個是我自己找的一個,算是一個簡化版的介紹吧。說白了,隨機森林分類的過程就是對于每個隨機產生的決策樹分類器,輸入特征向量,森林中每棵樹對樣本進行分類,根據每個樹的權重得到最后的分類結果。所有的樹訓練都是使用同樣的參數,但是訓練集是不同的,分類器的錯誤估計采用的是oob(out of bag)的辦法。如果大家看懂了,接下來就簡單咯

    還是先介紹一下對應opencv的類和函數吧,之前有籠統介紹過機器學習的類,對于隨機森林相關算法類有CvRTParams、CvRTrees。具體再講解一下:

    CvRTParams類包涵了隨機森林訓練過程中需要設置的全部參數,繼承自CvDTParams。其中

    max_depth表示單棵樹的最大深度

    min_sample_count閾值,當節點的樣本數比閾值小的時候,節點就不在進行分裂

    regression_accuracy回逆樹時候的閾值

    use_surrogates是否使用代理?(這是神馬功能。。。

    max_categories最大分類的閾值

    priors先驗分類可能性

    calc_var_importance變量重要性是否計算標志

    nactive_vars每棵樹選取特征子集的大小

    max_num_of_trees_in_the_forest森林內樹的數目上限

    forest_accuracy森林訓練OOB error的精度

    termcrit_type森林訓練閾值選取的類型

    以上參數的賦值都通過CvRTParams的構造函數實現。

    CvRTrees就是隨機森林的主體了。包涵了train訓練函數、predict預測函數、predict_prob返回預測分類標簽、getVarImportance返回變量權重矩陣、get_proximity返回兩訓練樣本之間的相似度、calc_error返回隨機森林的預測誤差、get_train_error返回訓練誤差、get_rng返回使用隨機數的當前值、get_tree_count返回構造隨機森林的樹的數目、get_tree返回構造隨機森林的其中一棵樹。

    接下來,結合sample自帶的letter_recog.cpp講解一下如何應用隨機森林算法做字母的識別,這里選擇的訓練樣本庫是http://archive.ics.uci.edu/ml/下的一個訓練集,這個網站還有很多其他的資源提供,非常好的機器學習的data庫。這個訓練文件letter-recognition.data有20000個訓練字母,每一字母用16維的特征表示。本程序使用前16000個進行訓練,后4000個進行測試。唯一讓我很不爽的是這個程序是用老版本的數據結構寫的,所以我決定用新結構再寫一下,我對原程序進行了簡化,刪除了很多不相關和不是必須的部分。

    其實隨機森林使用起來非常簡單,兩個最重要的步驟無非就是train()和predict()函數,其他的函數都是用來得到測試結果。

    這里把我簡化并用新結構改寫之后的代碼下載鏈接奉上http://download.csdn.net/detail/yang_xian521/4134557

    from: http://blog.csdn.net/yang_xian521/article/category/910716



    總結

    以上是生活随笔為你收集整理的OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。