乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      視覺SLAM——OpenCV之Mat結(jié)構(gòu)詳解 數(shù)據(jù)成員和構(gòu)造函數(shù) 創(chuàng)建Mat方法 遍歷Mat方法

       印度阿三17 2019-03-25

      前言

      OpenCV1時(shí)代采用基于C語言接口構(gòu)建函數(shù)庫(kù),使用名為IplImage的結(jié)構(gòu)體在內(nèi)存中存儲(chǔ)圖像,其問題在于需要用戶手動(dòng)管理內(nèi)存,如果不手動(dòng)釋放內(nèi)存會(huì)造成內(nèi)存泄漏。
      OpenCV2引入面向?qū)ο缶幊趟枷?,加入了一個(gè)c 接口,使用Mat類數(shù)據(jù)結(jié)構(gòu)作為主打,可以實(shí)現(xiàn)自動(dòng)內(nèi)存管理,且擴(kuò)展性大大提高。

      Mat概述

      對(duì)于Mat類,首先要知道的是
      1)不必手動(dòng)為其開辟空間;
      2)不必再在不需要時(shí)將空間釋放。
      但手動(dòng)做還也是可以的:大多數(shù)OpenCV函數(shù)仍會(huì)手動(dòng)地為輸出數(shù)據(jù)開辟空間。當(dāng)傳遞一個(gè)已經(jīng)存在的 Mat 對(duì)象時(shí),開辟好的矩陣空間會(huì)被重用。

      Mat是一個(gè)類,由兩個(gè)數(shù)據(jù)部分組成:矩陣頭(包含矩陣尺寸、存儲(chǔ)方法、存儲(chǔ)地址等)和一個(gè)指向存儲(chǔ)所有像素值矩陣的指針。

      Mat類最重要的一點(diǎn)是淺拷貝和深拷貝問題。由于OpenCV處理圖像時(shí)很多時(shí)候沒有必要重新復(fù)制一份圖像矩陣,因而采用了引用計(jì)數(shù)機(jī)制。其思路是讓每個(gè)Mat對(duì)象有自己的信息頭,但共享一個(gè)圖像矩陣(矩陣指針指向同一地址)。賦值運(yùn)算符和拷貝構(gòu)造函數(shù)只復(fù)制矩陣頭和矩陣指針,而不復(fù)制矩陣。

      Mat A , C;
      A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 為矩陣開辟內(nèi)存
      Mat B(A);                                 // 拷貝構(gòu)造函數(shù)
      C = A;                                    // 賦值
      

      這里A、B、C矩陣頭不同,但指向了相同的圖像矩陣,其中一個(gè)對(duì)象對(duì)矩陣數(shù)據(jù)進(jìn)行改變也會(huì)影響其他對(duì)象。如果矩陣屬于多個(gè)Mat對(duì)象,由最后一個(gè)使用它的對(duì)象進(jìn)行內(nèi)存清理。這通過引用計(jì)數(shù)來判斷,每復(fù)制一個(gè)Mat對(duì)象,計(jì)數(shù)器加一,每釋放一個(gè)計(jì)數(shù)器減一,當(dāng)計(jì)數(shù)器值為0時(shí)矩陣就會(huì)被清理。

      如果需要進(jìn)行對(duì)象的深拷貝可以采用clone()函數(shù)或者copyTo()。

      Mat A;
      A = imread(argv[1], CV_LOAD_IMAGE_COLOR); 
      Mat B = A.clone();
      Mat C;
      A.copyTo(C);
      

      Mat類的數(shù)據(jù)成員

      	/*
      	flag的詳細(xì)解釋可以看 https://blog.csdn.net/yiyuehuan/article/details/43701797
      	0-2位 depth:每一個(gè)像素的位數(shù),也就是每個(gè)通道的位數(shù),即數(shù)據(jù)類型(如CV_8U)
      		enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }
      		8U 表示 8 位無符號(hào)整數(shù),16S 表示 16 位有符號(hào)整數(shù),64F表示 64 位浮點(diǎn)數(shù)
      	3-11位 number of channels:代表通道數(shù)channels,最高512位
      	0-11位共同代表type:矩陣元素的類型,即通道數(shù)和數(shù)據(jù)類型(如CV_8UC3、CV_16UC2)
      	14位 continuity flag:代表Mat的內(nèi)存是否連續(xù)
      	15位 submat flag:代表該Mat是否為某一個(gè)Mat的submatrix
      	16-31位 the magic signature:用來區(qū)分Mat的類型,如果Mat和SparseMat
      	*/
      	int flags;
      	
          //矩陣的維數(shù),一般大于2
          int dims;
          
          //矩陣的行數(shù)與列數(shù),超過2維矩陣時(shí)(-1,-1)
          int rows, cols;
          
          //指向存放矩陣數(shù)據(jù)的內(nèi)存
          uchar* data;
          
          //用來控制ROI區(qū)域,來獲取一些圖像的局部切片,減少計(jì)算量或者特殊需求的。
          const uchar* datastart;
          const uchar* dataend;
          const uchar* datalimit;
          
      	//如果需要?jiǎng)?chuàng)建一個(gè)新矩陣的內(nèi)存空間,會(huì)調(diào)用MatAllocator類作為分配符進(jìn)行內(nèi)存的分配。
          MatAllocator* allocator;
          
          //interaction with UMat
          //UMatData結(jié)構(gòu)體總有一個(gè)成員refcount:記錄了矩陣的數(shù)據(jù)被其他變量引用了多少次
          UMatData* u;
          
          //返回矩陣大小
          MatSize size;
          
          //矩陣元素尋址,step[i]表示第i維的總大小,單位字節(jié)
          //對(duì)于2維矩陣:step[0]是矩陣中一行元素的字節(jié)數(shù),step[1]是矩陣中一個(gè)元素的字節(jié)數(shù)
          //以下公式可以得到Mat中任意元素地址
          //addr(M{i,j})=M.data M.step[0]?i M.step[1]?j;
          MatStep step;
      

      此外其他版本還存在以下成員:
      elemSize :矩陣一個(gè)元素占用的字節(jié)數(shù),例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes。
      elemSize1 :矩陣元素一個(gè)通道占用的字節(jié)數(shù),例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2 bytes = elemSize / channels。

      Mat類的構(gòu)造函數(shù)

      //1、默認(rèn)構(gòu)造函數(shù),無參數(shù)
      Mat::Mat();
      	
      //2、行數(shù)為rows,列數(shù)為cols,類型為type(如CV_8UC1、CV_16UC2)
      Mat(int rows, int cols, int type);
      
      //3、矩陣大小為size,類型為type
      //注意size的構(gòu)造函數(shù)是Size_(_Tp _width,_Tp _height) 先列后行
      Mat(Size size, int type);
      
      //4、行數(shù)為rows,列數(shù)為cols(或矩陣大小為size),類型為type,所有元素初始化為s
      //Scalar表示具有4個(gè)元素的數(shù)組,如Scalar(a,b,b),其原型為Scalar_<double>
      Mat(int rows, int cols, int type, const Scalar& s);
      Mat(Size size, int type, const Scalar& s);
      
      //5、矩陣維數(shù)為ndims,sizes為指定ndims維數(shù)組形狀的整數(shù)數(shù)組,所有元素初始化為s
      Mat(int ndims, const int* sizes, int type);
      Mat(const std::vector<int>& sizes, int type);
      Mat(int ndims, const int* sizes, int type, const Scalar& s);
      Mat(const std::vector<int>& sizes, int type, const Scalar& s);
      
      //6、拷貝構(gòu)造函數(shù),將m賦值給新創(chuàng)建的對(duì)象,淺拷貝
      Mat(const Mat& m);
      
      //7、行數(shù)為rows,列數(shù)為cols,類型為type,矩陣數(shù)據(jù)為data,直接使用data所指內(nèi)存,淺拷貝
      Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);
      Mat(Size size, int type, void* data, size_t step=AUTO_STEP);
      Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0);
      Mat(const std::vector<int>& sizes, int type, void* data, const size_t* steps=0);
      
      //8、創(chuàng)建的新圖像為m的一部分,范圍由rowRange和colRange指定,新圖像與m共用圖像數(shù)據(jù),淺拷貝
      Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all());
      Mat(const Mat& m, const Range* ranges);
      Mat(const Mat& m, const std::vector<Range>& ranges);
      
      //創(chuàng)建的新圖像為m的一部分,具體的范圍由矩陣對(duì)象roi指定
      //Rect的成員函數(shù)有x,y,width,height,分別為左上角點(diǎn)的坐標(biāo)好矩陣寬和高
      Mat(const Mat& m, const Rect& roi);
      

      創(chuàng)建Mat對(duì)象

      1、使用Mat()構(gòu)造函數(shù)

      Mat M(2,2, CV_8UC3, Scalar(0,0,255)); 
      

      需要指定行數(shù)、列數(shù)、存儲(chǔ)元素的數(shù)據(jù)類型以及每個(gè)矩陣點(diǎn)的通道數(shù)。

      2、使用Mat()構(gòu)造函數(shù)2

      int sz[3] = {2,2,2}; 
      Mat L(3,sz, CV_8UC(1), Scalar::all(0));
      

      常用于創(chuàng)建一個(gè)超過兩維的矩陣:指定維數(shù),然后傳遞一個(gè)指向一個(gè)數(shù)組的指針,這個(gè)數(shù)組包含每個(gè)維度的尺寸,其余的相同

      3、為已存在IplImage指針創(chuàng)建信息頭(一般不用)

      IplImage* img = cvLoadImage("greatwave.png", 1);
      Mat mtx(img); // convert IplImage* -> Mat
      

      4、利用create()函數(shù)
      這個(gè)創(chuàng)建方法不能為矩陣設(shè)初值,它只是在改變尺寸時(shí)重新為矩陣數(shù)據(jù)開辟內(nèi)存

       M.create(4,4, CV_8UC(2));//4X4的圖像矩陣,通道數(shù)為2,沒有初值
      

      5、MATLAB形式的初始化方式: zeros(), ones(), eyes()

       Mat E = Mat::eye(4, 4, CV_64F);//4X4的單位矩陣   
        
       Mat O = Mat::ones(2, 2, CV_32F);//2X2的全為1矩陣
      
       Mat Z = Mat::zeros(3,3, CV_8UC1);//3X3的零矩陣
      

      6、小矩陣使用逗號(hào)分隔式初始化

       Mat C = (Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1); //3X3的單位矩陣
      

      7、使用clone()或copyto()為已存在對(duì)象創(chuàng)建新信息頭

      Mat RowClone = C.row(1).clone();//復(fù)制 C中的第2行[0,1,0]作為新矩陣(深拷貝)
      

      遍歷Mat對(duì)象

      首先假設(shè)需要對(duì)圖像將那些顏色空間縮減,如imgnew=imgold/10?10img_{new} = img_{old} / 10 * 10imgnew?=imgold?/10?10。對(duì)于較大的圖像,一般不是對(duì)每個(gè)像素點(diǎn)每個(gè)通道的值進(jìn)行如上計(jì)算,而是預(yù)先計(jì)算所有可能的值,構(gòu)建查找表,然后利用查找表對(duì)其直接復(fù)制。

      	//構(gòu)建查找表table
      	int divideWith; 
          cin >> divideWith;
          uchar table[256]; 
          for (int i = 0; i < 256;   i)
             table[i] = divideWith* (i/divideWith);
      

      1、ptr指針單像素單通道值訪問
      即通過uchar* ptr =img.ptr<char>(i); 得到第i行首地址,然后采用指針運(yùn)算( )或者操作符[]遍歷。

      Mat& ScanImage(Mat& img,const uchar* const table)
      {
      	CV_Assert(img.depth() != sizeof(uchar))//只接收uchar類型矩陣
      	int channels = img.channel();
      	int nRows = img.rows * channels;//注意行數(shù)乘以通道數(shù)
      	int nCols = img.cols;
      	
      	if(img.isContinuous())//如果存儲(chǔ)連續(xù)則可以使用一次循環(huán)
      	{
      		nCols *= nRows;
      		nRows = 1; 
      	}
      	uchar* ptr;
      	for(int i=0; i<nRows;   i)
      	{
      		ptr = img.ptr<char>(i);
      		for( j=0; j<nCols;   j)
      		{
      			ptr[j] = table[ptr[j]]
      			//*ptr   =  table[*ptr]
      		}
      	}
      	return I;
      }
      

      2、ptr指針單像素訪問
      利用cv::Vec3b *ptr =img.ptr<cv::Vec3b>(i);得到第i行第一個(gè)像素的3個(gè)通道地址。

      Mat& ScanImage(Mat& img,const uchar* const table)
      {
      	CV_Assert(img.depth() != sizeof(uchar))//只接收uchar類型矩陣
      	int channels = img.channel();
      	int nRows = img.rows;//每一行
      	int nCols = img.cols;
      	Vec3b *ptr;
      	for(int i=0; i<nRows;   i)
      	{
      		ptr = img.ptr<Vec3b>(i);
      		for( j=0; j<nCols;   j)
      		{
      			ptr[j][0] = table[ptr[j][0]];
      			ptr[j][1] = table[ptr[j][1]];
      			ptr[j][2] = table[ptr[j][2]];
      		}
      	}
      	return I;
      }
      

      3、對(duì)data操作
      data會(huì)從Mat中返回指向矩陣第一行第一列的指針。常用來檢查圖像是否被成功讀入(如果指針為NULL則表示無輸入)。當(dāng)矩陣式連續(xù)存儲(chǔ)時(shí),可以通過data遍歷矩陣。
      也可以采用addr(M{i,j})=M.data M.step[0]?i M.step[1]?j;得到每個(gè)元素的地址,但是不常用。

      if(I.isContinuous())
      {
      	uchar* p = img.data;
      	for( unsigned int i =0; i < ncol * nrows;   i)
          	*p   = table[*p];
      }
      

      4、迭代器iterator訪問
      使用迭代器操作像素,與STL庫(kù)用法相似,只需要獲得圖像矩陣的begin和end,然后增加迭代直至從begin到end。將*操作符添加在迭代指針前,即可訪問當(dāng)前指向的內(nèi)容。
      迭代器可以采用MatIterator_以及Mat的模板子類Mat_,它重載了operator()。

      MatIterator_<uchar> it = img.begin<uchar>();
      Mat_<uchar>::iterator it = img.begin<uchar>();
      
      Mat& ScanImage(Mat& I,const uchar* const table)
      {
      	CV_Assert(img.depth() != sizeof(uchar));
      	const int channels = img.channels();
      	switch(channels)
      	{
      		case 1:
      			{
      				Mat_<uchar>::iterator it = img.begin<uchar>();
      				Mat_<uchar>::iterator itend = img.end<uchar>();
      				for(;it != itend;   it)
      					*it = table[*it];
      				break;
      			}
      		case 3:
      			{
      				Mat_<Vec3b>::iterator it = img.begin<uchar>();
      				Mat_<Vec3b>::iterator itend = img.end<uchar>();
      				for(; it != itend ;   it)
      				{
      					(*it)[0] = table[(*it)[0]];
      					(*it)[1] = table[(*it)[1]];
      					(*it)[2] = table[(*it)[2]];
      				}
      			}
      	}
      	return img;
      }
      
      

      5、動(dòng)態(tài)地址計(jì)算(at)
      該方法一般用于獲取或更改圖像中的某個(gè)元素(或隨機(jī)元素),而不用于進(jìn)行全圖掃描。它的基本用途是要確定你試圖訪問的元素的所在行數(shù)與列數(shù)。
      在debug模式下該方法會(huì)檢查你的輸入坐標(biāo)是否有效或者超出范圍,如果坐標(biāo)有誤則會(huì)輸出一個(gè)標(biāo)準(zhǔn)的錯(cuò)誤信息;在release模式下,它和其他的區(qū)別僅僅是對(duì)于矩陣的每個(gè)元素,都會(huì)獲取一個(gè)新的行指針,然后通過該指針和[]操作獲取元素。

      Mat& ScanImage(Mat& img,const uchar* const table)
      {
      	CV_Assert(img.depth() != sizeof(uchar));
      	const int channels = img.channels();
      	switch(channels)
      	{
      		case 1:
      			{
      				for(int i=0; i<img.rows;   i)
      					for(int j=0; j<img.cols;   i)
      						img.at<uchar>(i,j) = table[img.at<uchar>(i,j)];
      				break;
      			}
      		case 3:
      			{
      				for(int i=0; i<img.rows;   i)
      				{
      					for(int j=0; j<img.cols;   i)
      					{
      						img.at<vec3b>(i,j)[0] = table[img.at<vec3b>(i,j)[0]];
      						img.at<vec3b>(i,j)[1] = table[img.at<vec3b>(i,j)[1]];
      						img.at<vec3b>(i,j)[2] = table[img.at<vec3b>(i,j)[2]];
      					}
      				}
      				break;
      			}
      	}
      	return img;
      }
      

      6、LUT函數(shù)
      LUT(Look up table)被官方推薦用于實(shí)現(xiàn)批量圖像元素查找和更改。對(duì)于修改像素值,OpenCV提供函數(shù)operationsOnArray:LUT()<lut>,直接實(shí)現(xiàn)該操作,而不需要掃描圖像。

      //建立mat類對(duì)象用于查表
      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時(shí)輸入,J是輸出)
      LUT(I, LookUpTable, J);
      

      一般認(rèn)為,采用ptr指針訪問圖像像素的效率更高;而迭代器則被認(rèn)為是更安全的方式,僅需要獲得圖像矩陣的begin和end;at方法一般不用于對(duì)圖像進(jìn)行遍歷;而調(diào)用LUT函數(shù)可以獲得最快的速度,因?yàn)镺penCV庫(kù)可以通過英特爾線程架構(gòu)啟用多線程。其他也存在一些比較偏的遍歷方法,不過這幾種最為常見,效率和安全性也相對(duì)較好。

      來源:http://www./content-4-148551.html

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多