OpenCv学习笔记(二)—cv Mat学习
生活随笔
收集整理的這篇文章主要介紹了
OpenCv学习笔记(二)—cv Mat学习
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
由于在寫上一篇圖像的數(shù)據(jù)結(jié)構(gòu)時(shí),發(fā)現(xiàn)自己只知道CvMat,竟然還有Mat數(shù)據(jù)結(jié)構(gòu),真是無知了,看了這么多程序,貌似沒有看到這個(gè)結(jié)構(gòu)。有可能那些程序都是些老版本的例子,這是在2.0以后加上的,所以我也得緊跟呀!以下是自己的學(xué)習(xí)心得。。。。
一、Mat簡(jiǎn)介
??? 在2001年剛剛出現(xiàn)的時(shí)候,OpenCV基于 C 語(yǔ)言接口而建。為了在內(nèi)存(memory)中存放圖像,當(dāng)時(shí)采用名為 IplImage 的C語(yǔ)言結(jié)構(gòu)體,時(shí)至今日這仍出現(xiàn)在大多數(shù)的舊版教程和教學(xué)材料。但這種方法必須接受C語(yǔ)言所有的不足,這其中最大的不足要數(shù)手動(dòng)內(nèi)存管理,其依據(jù)是用戶要為開辟和銷毀內(nèi)存負(fù)責(zé)。雖然對(duì)于小型的程序來說手動(dòng)管理內(nèi)
一、Mat簡(jiǎn)介
??? 在2001年剛剛出現(xiàn)的時(shí)候,OpenCV基于 C 語(yǔ)言接口而建。為了在內(nèi)存(memory)中存放圖像,當(dāng)時(shí)采用名為 IplImage 的C語(yǔ)言結(jié)構(gòu)體,時(shí)至今日這仍出現(xiàn)在大多數(shù)的舊版教程和教學(xué)材料。但這種方法必須接受C語(yǔ)言所有的不足,這其中最大的不足要數(shù)手動(dòng)內(nèi)存管理,其依據(jù)是用戶要為開辟和銷毀內(nèi)存負(fù)責(zé)。雖然對(duì)于小型的程序來說手動(dòng)管理內(nèi)存不是問題,但一旦代碼開始變得越來越龐大,你需要越來越多地糾纏于這個(gè)問題,而不是著力解決你的開發(fā)目標(biāo)。
???幸運(yùn)的是,C++出現(xiàn)了,并且?guī)眍惖母拍?#xff0c;這給用戶帶來另外一個(gè)選擇:自動(dòng)的內(nèi)存管理(不嚴(yán)謹(jǐn)?shù)卣f)。這是一個(gè)好消息,如果C++完全兼容C的話,這個(gè)變化不會(huì)帶來兼容性問題。為此,OpenCV在2.0版本中引入了一個(gè)新的C++接口,利用自動(dòng)內(nèi)存管理給出了解決問題的新方法。使用這個(gè)方法,你不需要糾結(jié)在管理內(nèi)存上,而且你的代碼會(huì)變得簡(jiǎn)潔(少寫多得)。但C++接口唯一的不足是當(dāng)前許多嵌入式開發(fā)系統(tǒng)只支持C語(yǔ)言。所以,當(dāng)目標(biāo)不是這種開發(fā)平臺(tái)時(shí),沒有必要使用舊 方法(除非你是自找麻煩的受虐狂碼農(nóng))。
? ?關(guān)于 Mat ,首先要知道的是你不必再手動(dòng)地(1)為其開辟空間(2)在不需要時(shí)立即將空間釋放。但手動(dòng)地做還是可以的:大多數(shù)OpenCV函數(shù)仍會(huì)手動(dòng)地為輸出數(shù)據(jù)開辟空間。當(dāng)傳遞一個(gè)已經(jīng)存在的Mat 對(duì)象時(shí),開辟好的矩陣空間會(huì)被重用。也就是說,我們每次都使用大小正好的內(nèi)存來完成任務(wù)。
? ?基本上講 Mat 是一個(gè)類,由兩個(gè)數(shù)據(jù)部分組成:矩陣頭(包含矩陣尺寸,存儲(chǔ)方法,存儲(chǔ)地址等信息)和一個(gè)指向存儲(chǔ)所有像素值的矩陣(根據(jù)所選存儲(chǔ)方法的不同矩陣可以是不同的維數(shù))的指針。矩陣頭的尺寸是常數(shù)值,但矩陣本身的尺寸會(huì)依圖像的不同而不同,通常比矩陣頭的尺寸大數(shù)個(gè)數(shù)量級(jí)。因此,當(dāng)在程序中傳遞圖像并創(chuàng)建拷貝時(shí),大的開銷是由矩陣造成的,而不是信息頭。OpenCV是一個(gè)圖像處理庫(kù),囊括了大量的圖像處理函數(shù),為了解決問題通常要使用庫(kù)中的多個(gè)函數(shù),因此在函數(shù)中傳遞圖像是家常便飯。同時(shí)不要忘了我們正在討論的是計(jì)算量很大的圖像處理算法,因此,除非萬不得已,我們不應(yīng)該拷貝大 的圖像,因?yàn)檫@會(huì)降低程序速度。
二、Mat的基本操作
? ?這里展示一個(gè)例子解釋一下Mat的基本操作
using namespace cv;using namespace std;int main(){/*********************************Mat基本操作-矩陣*******************************************/??? //二維三通道矩陣建立??? Mat M(2,2, CV_8UC3, Scalar(0,0,255)); //使用構(gòu)造函數(shù)創(chuàng)建矩陣/*CV_8UC3 表示使用8位的 unsigned char 型,每個(gè)像素由三個(gè)元素組成三通道,初始化為(0,0,255)*/??? cout << "M = " << endl << " " << M << endl << endl; //格式化輸出??? //三維??? int sz[3] = {3,3,3}; ??? Mat L(3,sz, CV_8UC(1), Scalar::all(0));/*超過兩維的矩陣:指定維數(shù),然后傳遞一個(gè)指向一個(gè)數(shù)組的指針,這個(gè)數(shù)組包含每個(gè)維度的尺寸;其余的相同*/??? cout << "L = " << endl << " " << M << endl << endl; //格式化輸出/********************************************Mat基本操作-圖像*******************************/?? Mat A, C;????? // 只創(chuàng)建信息頭部分??? A=imread("D:\\openCV\\openCVProject\\openCv筆記\\openCv筆記\\test.jpg", CV_LOAD_IMAGE_COLOR); // 這里為矩陣開辟內(nèi)存??? Mat B(A);??????????????? // 使用拷貝構(gòu)造函數(shù)??? C = A;????????????????? // 賦值運(yùn)算符??? /*?拷貝構(gòu)造函數(shù)和賦值函數(shù) 只拷貝信息頭和矩陣指針?*/??? Mat D (A, Rect(10, 10, 100, 100) ); //選取A中一個(gè)矩形區(qū)域,即只訪問其矩形區(qū)域的信息頭,只是創(chuàng)建信息頭?Mat E = A(cv::Range::all(), Range(1,3)); // 創(chuàng)建訪問邊界的信息頭。??? /*??? 要?jiǎng)?chuàng)建一個(gè)感興趣區(qū)域( ROI ),你只需要?jiǎng)?chuàng)建包含邊界信息的信息頭?*/??? Mat F = A.clone();//復(fù)制圖像,包括數(shù)據(jù)??? Mat G;??? A.copyTo(G);??? /*?拷貝矩陣本身(不只是信息頭和矩陣指針),?*/?//測(cè)試?namedWindow( "a", CV_WINDOW_AUTOSIZE );?namedWindow( "c", CV_WINDOW_AUTOSIZE );??imshow( "a", D);?imshow( "c", E );??/****************************************圖像的讀取、處理和保存**************************************/?? Mat image;? image = imread( "D:\\openCV\\openCVProject\\openCv筆記\\openCv筆記\\test.jpg", CV_LOAD_IMAGE_COLOR);//導(dǎo)入圖像? if( !image.data )? {???? cout<< " No image data \n " ;???? return -1;? }? Mat gray_image;? cvtColor( image, gray_image, CV_BGR2GRAY );//轉(zhuǎn)化為灰度圖? imwrite( "../../images/Gray_Image.jpg", gray_image );//寫入圖像? namedWindow( "source", CV_WINDOW_AUTOSIZE );? namedWindow( "Gray image", CV_WINDOW_AUTOSIZE );? imshow( "source", image );? imshow( "Gray image", gray_image );???? /*******************************************************************************************/? waitKey(0);? return 0;}
?對(duì)于Mat數(shù)據(jù)結(jié)構(gòu),在對(duì)圖像進(jìn)行處理時(shí)要注意:
OpenCV函數(shù)中輸出圖像的內(nèi)存分配是自動(dòng)完成的(如果不特別指定的話)。
使用OpenCV的C++接口時(shí)不需要考慮內(nèi)存釋放問題。
賦值運(yùn)算符和拷貝構(gòu)造函數(shù)( ctor )只拷貝信息頭。
使用函數(shù) clone() 或者copyTo() 來拷貝一副圖像的矩陣?
三、掃描圖像的方法
using namespace cv;using namespace std;int main(){??? //Mat img(10,10,CV_8UC3,Scalar(0,0,255));?Mat img,img_gray,img_gray2;?img=imread("D:\\openCV\\openCVProject\\openCv筆記\\openCv筆記\\test.jpg", CV_LOAD_IMAGE_COLOR);??? cvtColor( img, img_gray, CV_BGR2GRAY );//轉(zhuǎn)化為灰度圖?img_gray.copyTo(img_gray2);??? //方式一?for( int i=0;i<img_gray.rows;i++)?{??uchar* data = img_gray.ptr<uchar>(i);??for(int j=0;j<img_gray.cols;j++)??{???data[j] = 255; ??}?}?//img.create(10,10,CV_8UC3,Scalar(0,0,255));?//cout << "img = " << endl << " " << img_gray << endl << endl; //格式化輸出??? //方式二 W*H的一幅圖像看成是一個(gè)1*(w*h)的一個(gè)一維數(shù)組?int nc;?if(img_gray.isContinuous())//判斷是否被所有的像素填滿?{??nc = img_gray.rows*img_gray.cols*img_gray.channels();??}?else?{??cout<<"像素未填滿,不可用第二種方式"<<endl;??return -1;?}?uchar* data_2 = img_gray.ptr<uchar>(0);//提取第一個(gè)像素點(diǎn)指針?for(int i=0;i<nc;i++)//遍歷所有的元素?{??data_2[i] = 255;?}??? //方式三指針掃描?uchar* data_3 = img.data;//單個(gè)元素?img.at<uchar>(0,0)=0;?for(int i=0;i<img.rows;i++)//遍歷所有的元素?{??for(int j=0;j<img.cols;j++)??{???????????? data_3 = img.data + i*img.step + j * img.elemSize(); ??? //對(duì)各個(gè)通道賦值??? data_3[0]=100;???????????? data_3[1]=100;??? data_3[2]=100;??}?}?/*時(shí)間函數(shù)??? double start = getTickCount();?finish = clock(); ??? duration = (double)(finish - start) / CLOCKS_PER_SEC; ??? */?//方式四 迭代器iterator掃描圖像?Mat_<Vec3b>::iterator it = img.begin<Vec3b>();? ?Mat_<Vec3b>::iterator itend = img.end<Vec3b>();? ?for (; it!=itend; it++)? ?{? ?????? //對(duì)各個(gè)通道賦值??? (*it)[0] = 200;? ??? (*it)[1] = 200; ??? (*it)[2] = 200; ?}? ??? ?//測(cè)試,根據(jù)自己的選擇查看結(jié)果?namedWindow("sorce",WINDOW_AUTOSIZE);?namedWindow("result",WINDOW_AUTOSIZE);?cv::imshow("sorce",img);?cv::imshow("result",img_gray);?waitKey(0);?return 0;}
以上是對(duì)http://blog.csdn.net/yang_xian521/article/details/7182185#的綜合,以下是其博文,正如博主所說的, data_3 = img.data + i*img.step + j * img.elemSize();,int i=0;i<img_gray.rows;i++。。。這種在循環(huán)中出現(xiàn)的語(yǔ)句識(shí)別比較耗時(shí)的,注意避免。以下是其博文
1.存取單個(gè)像素值
最通常的方法就是
img.at<uchar>(i,j) = 255;img.at<Vec3b>(i,j)[0] = 255;
如果你覺得at操作顯得太笨重了,不想用Mat這個(gè)類,也可以考慮使用輕量級(jí)的Mat_類,使用重載操作符()實(shí)現(xiàn)取元素的操作。
cv::Mat_<uchar> im2= img; // im2 refers to image?? im2(50,100)= 0; // access to row 50 and column 100
2.用指針掃描一幅圖像
對(duì)于一幅圖像的掃描,用at就顯得不太好了,還是是用指針的操作方法更加推薦。先介紹一種上一講提到過的
for (int j=0; j<nl; j++){??????? uchar* data= image.ptr<uchar>(j);??????? for (int i=0; i<nc; i++)?????? {???????????????? ????????????????? data[i] = 255;??????? }}
更高效的掃描連續(xù)圖像的做法可能是把W*H的衣服圖像看成是一個(gè)1*(w*h)的一個(gè)一維數(shù)組,這個(gè)想法是不是有點(diǎn)奇葩,這里要利用isContinuous這個(gè)函數(shù)判斷圖像內(nèi)的像素是否填充滿,使用方法如下:
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;}
更低級(jí)的指針操作就是使用Mat里的data指針,之前我稱之為暴力青年,使用方法如下:
uchar* data = img.data;// img.at(i, j)data = img.data + i * img.step + j * img.elemSize();
3.用迭代器iterator掃描圖像
和C++STL里的迭代器類似,Mat的迭代器與之是兼容的。是MatIterator_。聲明方法如下:
cv::MatIterator_<Vec3b> it;?
或者是:
cv::Mat_<Vec3b>::iterator it;掃描圖像的方法如下:
Mat_<Vec3b>::iterator it = img.begin<Vec3b>();Mat_<Vec3b>::iterator itend = img.end<Vec3b>();for (; it!=itend; it++){???????? (*it)[0] = 255;}
4.高效的scan image方案總結(jié)
還是用我們之前使用過的getTickCount、getTickFrequency函數(shù)測(cè)試速度。這里我就不一一列舉我測(cè)試的結(jié)果了,直接上結(jié)論。測(cè)試發(fā)現(xiàn),好的編寫風(fēng)格可以提高50%的速度!要想減少程序運(yùn)行的時(shí)間,必要的優(yōu)化包括如下幾個(gè)方面:
(1)內(nèi)存分配是個(gè)耗時(shí)的工作,優(yōu)化之;
(2)在循環(huán)中重復(fù)計(jì)算已經(jīng)得到的值,是個(gè)費(fèi)時(shí)的工作,優(yōu)化之;舉例:
int nc = img.cols * img.channels();for (int i=0; i<nc; i++){.......}//**************************for (int i=0; i<img.cols * img.channels(); i++){......}
后者的速度比前者要慢上好多。
(3)使用迭代器也會(huì)是速度變慢,但迭代器的使用可以減少程序錯(cuò)誤的發(fā)生幾率,考慮這個(gè)因素,可以酌情優(yōu)化
(4)at操作要比指針的操作慢很多,所以對(duì)于不連續(xù)數(shù)據(jù)或者單個(gè)點(diǎn)處理,可以考慮at操作,對(duì)于連續(xù)的大量數(shù)據(jù),不要使用它
(5)掃描連續(xù)圖像的做法可能是把W*H的衣服圖像看成是一個(gè)1*(w*h)的一個(gè)一維數(shù)組這種辦法也可以提高速度。短的循環(huán)比長(zhǎng)循環(huán)更高效,即使他們的操作數(shù)是相同的
以上的這些優(yōu)化可能對(duì)于大家的程序運(yùn)行速度提高并不明顯,但它們畢竟是個(gè)得到速度提升的好的編程策略,希望大家能多采納。
還有就是利用多線程也可以高效提高運(yùn)行速度。OpenMP和TBB是兩種流行的APT,不過對(duì)于多線程的東西,我是有些迷糊的,呵呵
5.整行整列像素值的賦值
對(duì)于整行或者整列的數(shù)據(jù),可以考慮這種方式處理
img.row(i).setTo(Scalar(255));img.col(j).setTo(Scalar(255));這節(jié)就先介紹這么多攻略吧~希望大家喜歡
參考資料
? ? ? ? ? 1.http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/mat%20-%20the%20basic%20image%20container/mat%20-%20the%20basic%20image%20container.html
? ? ? ? ? 2http://blog.sina.com.cn/s/blog_73ee929c01010yor.html
? ? ? ? ? ????????????
一、Mat簡(jiǎn)介
??? 在2001年剛剛出現(xiàn)的時(shí)候,OpenCV基于 C 語(yǔ)言接口而建。為了在內(nèi)存(memory)中存放圖像,當(dāng)時(shí)采用名為 IplImage 的C語(yǔ)言結(jié)構(gòu)體,時(shí)至今日這仍出現(xiàn)在大多數(shù)的舊版教程和教學(xué)材料。但這種方法必須接受C語(yǔ)言所有的不足,這其中最大的不足要數(shù)手動(dòng)內(nèi)存管理,其依據(jù)是用戶要為開辟和銷毀內(nèi)存負(fù)責(zé)。雖然對(duì)于小型的程序來說手動(dòng)管理內(nèi)
一、Mat簡(jiǎn)介
??? 在2001年剛剛出現(xiàn)的時(shí)候,OpenCV基于 C 語(yǔ)言接口而建。為了在內(nèi)存(memory)中存放圖像,當(dāng)時(shí)采用名為 IplImage 的C語(yǔ)言結(jié)構(gòu)體,時(shí)至今日這仍出現(xiàn)在大多數(shù)的舊版教程和教學(xué)材料。但這種方法必須接受C語(yǔ)言所有的不足,這其中最大的不足要數(shù)手動(dòng)內(nèi)存管理,其依據(jù)是用戶要為開辟和銷毀內(nèi)存負(fù)責(zé)。雖然對(duì)于小型的程序來說手動(dòng)管理內(nèi)存不是問題,但一旦代碼開始變得越來越龐大,你需要越來越多地糾纏于這個(gè)問題,而不是著力解決你的開發(fā)目標(biāo)。
???幸運(yùn)的是,C++出現(xiàn)了,并且?guī)眍惖母拍?#xff0c;這給用戶帶來另外一個(gè)選擇:自動(dòng)的內(nèi)存管理(不嚴(yán)謹(jǐn)?shù)卣f)。這是一個(gè)好消息,如果C++完全兼容C的話,這個(gè)變化不會(huì)帶來兼容性問題。為此,OpenCV在2.0版本中引入了一個(gè)新的C++接口,利用自動(dòng)內(nèi)存管理給出了解決問題的新方法。使用這個(gè)方法,你不需要糾結(jié)在管理內(nèi)存上,而且你的代碼會(huì)變得簡(jiǎn)潔(少寫多得)。但C++接口唯一的不足是當(dāng)前許多嵌入式開發(fā)系統(tǒng)只支持C語(yǔ)言。所以,當(dāng)目標(biāo)不是這種開發(fā)平臺(tái)時(shí),沒有必要使用舊 方法(除非你是自找麻煩的受虐狂碼農(nóng))。
? ?關(guān)于 Mat ,首先要知道的是你不必再手動(dòng)地(1)為其開辟空間(2)在不需要時(shí)立即將空間釋放。但手動(dòng)地做還是可以的:大多數(shù)OpenCV函數(shù)仍會(huì)手動(dòng)地為輸出數(shù)據(jù)開辟空間。當(dāng)傳遞一個(gè)已經(jīng)存在的Mat 對(duì)象時(shí),開辟好的矩陣空間會(huì)被重用。也就是說,我們每次都使用大小正好的內(nèi)存來完成任務(wù)。
? ?基本上講 Mat 是一個(gè)類,由兩個(gè)數(shù)據(jù)部分組成:矩陣頭(包含矩陣尺寸,存儲(chǔ)方法,存儲(chǔ)地址等信息)和一個(gè)指向存儲(chǔ)所有像素值的矩陣(根據(jù)所選存儲(chǔ)方法的不同矩陣可以是不同的維數(shù))的指針。矩陣頭的尺寸是常數(shù)值,但矩陣本身的尺寸會(huì)依圖像的不同而不同,通常比矩陣頭的尺寸大數(shù)個(gè)數(shù)量級(jí)。因此,當(dāng)在程序中傳遞圖像并創(chuàng)建拷貝時(shí),大的開銷是由矩陣造成的,而不是信息頭。OpenCV是一個(gè)圖像處理庫(kù),囊括了大量的圖像處理函數(shù),為了解決問題通常要使用庫(kù)中的多個(gè)函數(shù),因此在函數(shù)中傳遞圖像是家常便飯。同時(shí)不要忘了我們正在討論的是計(jì)算量很大的圖像處理算法,因此,除非萬不得已,我們不應(yīng)該拷貝大 的圖像,因?yàn)檫@會(huì)降低程序速度。
二、Mat的基本操作
? ?這里展示一個(gè)例子解釋一下Mat的基本操作
using namespace cv;using namespace std;int main(){/*********************************Mat基本操作-矩陣*******************************************/??? //二維三通道矩陣建立??? Mat M(2,2, CV_8UC3, Scalar(0,0,255)); //使用構(gòu)造函數(shù)創(chuàng)建矩陣/*CV_8UC3 表示使用8位的 unsigned char 型,每個(gè)像素由三個(gè)元素組成三通道,初始化為(0,0,255)*/??? cout << "M = " << endl << " " << M << endl << endl; //格式化輸出??? //三維??? int sz[3] = {3,3,3}; ??? Mat L(3,sz, CV_8UC(1), Scalar::all(0));/*超過兩維的矩陣:指定維數(shù),然后傳遞一個(gè)指向一個(gè)數(shù)組的指針,這個(gè)數(shù)組包含每個(gè)維度的尺寸;其余的相同*/??? cout << "L = " << endl << " " << M << endl << endl; //格式化輸出/********************************************Mat基本操作-圖像*******************************/?? Mat A, C;????? // 只創(chuàng)建信息頭部分??? A=imread("D:\\openCV\\openCVProject\\openCv筆記\\openCv筆記\\test.jpg", CV_LOAD_IMAGE_COLOR); // 這里為矩陣開辟內(nèi)存??? Mat B(A);??????????????? // 使用拷貝構(gòu)造函數(shù)??? C = A;????????????????? // 賦值運(yùn)算符??? /*?拷貝構(gòu)造函數(shù)和賦值函數(shù) 只拷貝信息頭和矩陣指針?*/??? Mat D (A, Rect(10, 10, 100, 100) ); //選取A中一個(gè)矩形區(qū)域,即只訪問其矩形區(qū)域的信息頭,只是創(chuàng)建信息頭?Mat E = A(cv::Range::all(), Range(1,3)); // 創(chuàng)建訪問邊界的信息頭。??? /*??? 要?jiǎng)?chuàng)建一個(gè)感興趣區(qū)域( ROI ),你只需要?jiǎng)?chuàng)建包含邊界信息的信息頭?*/??? Mat F = A.clone();//復(fù)制圖像,包括數(shù)據(jù)??? Mat G;??? A.copyTo(G);??? /*?拷貝矩陣本身(不只是信息頭和矩陣指針),?*/?//測(cè)試?namedWindow( "a", CV_WINDOW_AUTOSIZE );?namedWindow( "c", CV_WINDOW_AUTOSIZE );??imshow( "a", D);?imshow( "c", E );??/****************************************圖像的讀取、處理和保存**************************************/?? Mat image;? image = imread( "D:\\openCV\\openCVProject\\openCv筆記\\openCv筆記\\test.jpg", CV_LOAD_IMAGE_COLOR);//導(dǎo)入圖像? if( !image.data )? {???? cout<< " No image data \n " ;???? return -1;? }? Mat gray_image;? cvtColor( image, gray_image, CV_BGR2GRAY );//轉(zhuǎn)化為灰度圖? imwrite( "../../images/Gray_Image.jpg", gray_image );//寫入圖像? namedWindow( "source", CV_WINDOW_AUTOSIZE );? namedWindow( "Gray image", CV_WINDOW_AUTOSIZE );? imshow( "source", image );? imshow( "Gray image", gray_image );???? /*******************************************************************************************/? waitKey(0);? return 0;}
?對(duì)于Mat數(shù)據(jù)結(jié)構(gòu),在對(duì)圖像進(jìn)行處理時(shí)要注意:
OpenCV函數(shù)中輸出圖像的內(nèi)存分配是自動(dòng)完成的(如果不特別指定的話)。
使用OpenCV的C++接口時(shí)不需要考慮內(nèi)存釋放問題。
賦值運(yùn)算符和拷貝構(gòu)造函數(shù)( ctor )只拷貝信息頭。
使用函數(shù) clone() 或者copyTo() 來拷貝一副圖像的矩陣?
三、掃描圖像的方法
using namespace cv;using namespace std;int main(){??? //Mat img(10,10,CV_8UC3,Scalar(0,0,255));?Mat img,img_gray,img_gray2;?img=imread("D:\\openCV\\openCVProject\\openCv筆記\\openCv筆記\\test.jpg", CV_LOAD_IMAGE_COLOR);??? cvtColor( img, img_gray, CV_BGR2GRAY );//轉(zhuǎn)化為灰度圖?img_gray.copyTo(img_gray2);??? //方式一?for( int i=0;i<img_gray.rows;i++)?{??uchar* data = img_gray.ptr<uchar>(i);??for(int j=0;j<img_gray.cols;j++)??{???data[j] = 255; ??}?}?//img.create(10,10,CV_8UC3,Scalar(0,0,255));?//cout << "img = " << endl << " " << img_gray << endl << endl; //格式化輸出??? //方式二 W*H的一幅圖像看成是一個(gè)1*(w*h)的一個(gè)一維數(shù)組?int nc;?if(img_gray.isContinuous())//判斷是否被所有的像素填滿?{??nc = img_gray.rows*img_gray.cols*img_gray.channels();??}?else?{??cout<<"像素未填滿,不可用第二種方式"<<endl;??return -1;?}?uchar* data_2 = img_gray.ptr<uchar>(0);//提取第一個(gè)像素點(diǎn)指針?for(int i=0;i<nc;i++)//遍歷所有的元素?{??data_2[i] = 255;?}??? //方式三指針掃描?uchar* data_3 = img.data;//單個(gè)元素?img.at<uchar>(0,0)=0;?for(int i=0;i<img.rows;i++)//遍歷所有的元素?{??for(int j=0;j<img.cols;j++)??{???????????? data_3 = img.data + i*img.step + j * img.elemSize(); ??? //對(duì)各個(gè)通道賦值??? data_3[0]=100;???????????? data_3[1]=100;??? data_3[2]=100;??}?}?/*時(shí)間函數(shù)??? double start = getTickCount();?finish = clock(); ??? duration = (double)(finish - start) / CLOCKS_PER_SEC; ??? */?//方式四 迭代器iterator掃描圖像?Mat_<Vec3b>::iterator it = img.begin<Vec3b>();? ?Mat_<Vec3b>::iterator itend = img.end<Vec3b>();? ?for (; it!=itend; it++)? ?{? ?????? //對(duì)各個(gè)通道賦值??? (*it)[0] = 200;? ??? (*it)[1] = 200; ??? (*it)[2] = 200; ?}? ??? ?//測(cè)試,根據(jù)自己的選擇查看結(jié)果?namedWindow("sorce",WINDOW_AUTOSIZE);?namedWindow("result",WINDOW_AUTOSIZE);?cv::imshow("sorce",img);?cv::imshow("result",img_gray);?waitKey(0);?return 0;}
以上是對(duì)http://blog.csdn.net/yang_xian521/article/details/7182185#的綜合,以下是其博文,正如博主所說的, data_3 = img.data + i*img.step + j * img.elemSize();,int i=0;i<img_gray.rows;i++。。。這種在循環(huán)中出現(xiàn)的語(yǔ)句識(shí)別比較耗時(shí)的,注意避免。以下是其博文
1.存取單個(gè)像素值
最通常的方法就是
img.at<uchar>(i,j) = 255;img.at<Vec3b>(i,j)[0] = 255;
如果你覺得at操作顯得太笨重了,不想用Mat這個(gè)類,也可以考慮使用輕量級(jí)的Mat_類,使用重載操作符()實(shí)現(xiàn)取元素的操作。
cv::Mat_<uchar> im2= img; // im2 refers to image?? im2(50,100)= 0; // access to row 50 and column 100
2.用指針掃描一幅圖像
對(duì)于一幅圖像的掃描,用at就顯得不太好了,還是是用指針的操作方法更加推薦。先介紹一種上一講提到過的
for (int j=0; j<nl; j++){??????? uchar* data= image.ptr<uchar>(j);??????? for (int i=0; i<nc; i++)?????? {???????????????? ????????????????? data[i] = 255;??????? }}
更高效的掃描連續(xù)圖像的做法可能是把W*H的衣服圖像看成是一個(gè)1*(w*h)的一個(gè)一維數(shù)組,這個(gè)想法是不是有點(diǎn)奇葩,這里要利用isContinuous這個(gè)函數(shù)判斷圖像內(nèi)的像素是否填充滿,使用方法如下:
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;}
更低級(jí)的指針操作就是使用Mat里的data指針,之前我稱之為暴力青年,使用方法如下:
uchar* data = img.data;// img.at(i, j)data = img.data + i * img.step + j * img.elemSize();
3.用迭代器iterator掃描圖像
和C++STL里的迭代器類似,Mat的迭代器與之是兼容的。是MatIterator_。聲明方法如下:
cv::MatIterator_<Vec3b> it;?
或者是:
cv::Mat_<Vec3b>::iterator it;掃描圖像的方法如下:
Mat_<Vec3b>::iterator it = img.begin<Vec3b>();Mat_<Vec3b>::iterator itend = img.end<Vec3b>();for (; it!=itend; it++){???????? (*it)[0] = 255;}
4.高效的scan image方案總結(jié)
還是用我們之前使用過的getTickCount、getTickFrequency函數(shù)測(cè)試速度。這里我就不一一列舉我測(cè)試的結(jié)果了,直接上結(jié)論。測(cè)試發(fā)現(xiàn),好的編寫風(fēng)格可以提高50%的速度!要想減少程序運(yùn)行的時(shí)間,必要的優(yōu)化包括如下幾個(gè)方面:
(1)內(nèi)存分配是個(gè)耗時(shí)的工作,優(yōu)化之;
(2)在循環(huán)中重復(fù)計(jì)算已經(jīng)得到的值,是個(gè)費(fèi)時(shí)的工作,優(yōu)化之;舉例:
int nc = img.cols * img.channels();for (int i=0; i<nc; i++){.......}//**************************for (int i=0; i<img.cols * img.channels(); i++){......}
后者的速度比前者要慢上好多。
(3)使用迭代器也會(huì)是速度變慢,但迭代器的使用可以減少程序錯(cuò)誤的發(fā)生幾率,考慮這個(gè)因素,可以酌情優(yōu)化
(4)at操作要比指針的操作慢很多,所以對(duì)于不連續(xù)數(shù)據(jù)或者單個(gè)點(diǎn)處理,可以考慮at操作,對(duì)于連續(xù)的大量數(shù)據(jù),不要使用它
(5)掃描連續(xù)圖像的做法可能是把W*H的衣服圖像看成是一個(gè)1*(w*h)的一個(gè)一維數(shù)組這種辦法也可以提高速度。短的循環(huán)比長(zhǎng)循環(huán)更高效,即使他們的操作數(shù)是相同的
以上的這些優(yōu)化可能對(duì)于大家的程序運(yùn)行速度提高并不明顯,但它們畢竟是個(gè)得到速度提升的好的編程策略,希望大家能多采納。
還有就是利用多線程也可以高效提高運(yùn)行速度。OpenMP和TBB是兩種流行的APT,不過對(duì)于多線程的東西,我是有些迷糊的,呵呵
5.整行整列像素值的賦值
對(duì)于整行或者整列的數(shù)據(jù),可以考慮這種方式處理
img.row(i).setTo(Scalar(255));img.col(j).setTo(Scalar(255));這節(jié)就先介紹這么多攻略吧~希望大家喜歡
參考資料
? ? ? ? ? 1.http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/mat%20-%20the%20basic%20image%20container/mat%20-%20the%20basic%20image%20container.html
? ? ? ? ? 2http://blog.sina.com.cn/s/blog_73ee929c01010yor.html
? ? ? ? ? ????????????
再分享一下我老師大神的人工智能教程吧。零基礎(chǔ)!通俗易懂!風(fēng)趣幽默!還帶黃段子!希望你也加入到我們?nèi)斯ぶ悄艿年?duì)伍中來!https://blog.csdn.net/jiangjunshow
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的OpenCv学习笔记(二)—cv Mat学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: favicon.ico--网站标题小图片
- 下一篇: Nginx系列二:(Nginx Rewr