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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

OpenCV【零】—————cv::Mat——Mat对象创建方法

發布時間:2023/11/27 生活经验 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV【零】—————cv::Mat——Mat对象创建方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

OpenCV (一)——Mat對象創建方法

目錄

OpenCV (一)——Mat對象創建方法

1. cv::Mat優點及原理(本質類)

2. Mat類拷貝及對象的創建方法

3. Mat 對象元素的高效訪問

4. 存儲方法

5. 顯式創建Mat對象

6. 與其他語言對比的方式

7. Mat操作實例


1. cv::Mat優點及原理(本質類)

  • 不需要手動申請一塊內存;

  • 在不需要時不用再手動釋放內存;

  • 可以通過類的封裝,方便的獲取到數據的相關信息。

它利用了類的特性,將內存管理和數據信息封裝在類的內部,用戶只需要對Mat類對象進行數據或面向對象操作即可。 Mat類分為兩個部分:矩陣頭矩陣數據。如果我們在操作一副圖像的數據量時,矩陣數據量一般很大,那么針對拷貝和賦值函數的操作如果的深拷貝的話,效率會大大的降低。所以,Opencv的做法是只復制其矩陣頭信息,而矩陣數據采用引用的方式,即多個Mat對象共享同一個矩陣數據,這里使用的原理類似c++11中的共享指針(本質類)

    cv::Mat A = cv::imread("image.jpg");cv::Mat B(A);
//淺層拷貝:Mat B=A;B就是淺層拷貝A,B只拷貝了A的的頭部和地址,當B被操作后A也隨之改變。
?
memcpy(A.data, output.data(), rows * cols * sizeof(uint16_t));
?cv::Mat C = A;
//深層拷貝:Mat A=imread("x.jpg"); Mat B=A.clone();B是開辟了新的內存完全的復制了A的內容,操作B不會對A造成影響。printf("A.data = %p\nB.data = %p\nC.data = %p\n", A.data, B.data, C.data);
output:
A.data =  00240AA0ADHH00C0
B.data =  00240AA0ADHH00C0
C.data =  00240AA0ADHH00C0

釋放內存原則:由于內部使用了引用計數的方法,類似共享指針,當引用計數變為0的時候才會真正的釋放內存。

temp_thin.convertTo(temp_thin_image, CV_8UC1, 1, 0);
?
Mat F = A.clone();
Mat G;
A.copyTo(G);

  • 輸出圖像分配 OpenCV 功能是自動 (除非另行指定,否則)。

  • 用c + + OpenCV的接口就無需考慮內存釋放。

  • 賦值運算符和復制構造函數 (構造函數)只復制頭。

  • 使用clone () 或copyTo () 函數將復制的圖像的基礎矩陣。

//二值化,類型轉換,賦值memcpy(cv_thin_image_rgb_.data, output.data(), rows * cols * sizeof(uint16_t));temp_thin = cv_thin_image_rgb_;uint16_t* tdata = (uint16_t*) (temp_thin.data);uint8_t* bdata = cv_binary_image_rgb_.data;for (int i = 0; i < cols; i++) {for (int j = 0; j < rows; j++) {if (*tdata == 0) {*bdata = 0;} else {*bdata = 1;}tdata++;bdata++;}}temp_binary = cv_binary_image_rgb_;

Mat* 是一個類,由兩個數據部分組成:矩陣頭(包含矩陣尺寸,存儲方法,存儲地址等信息)和一個指向存儲所有像素值的矩陣(根據所選存儲方法的不同矩陣可以是不同的維數)的指針。矩陣頭的尺寸是常數值,但矩陣本身的尺寸會依圖像的不同而不同,通常比矩陣頭的尺寸大數個數量級。因此,當在程序中傳遞圖像并創建拷貝時,大的開銷是由矩陣造成的,而不是信息頭。

OpenCV是一個圖像處理庫,囊括了大量的圖像處理函數,為了解決問題通常要使用庫中的多個函數,因此在函數中傳遞圖像是家常便飯。同時不要忘了我們正在討論的是計算量很大的圖像處理算法,因此,除非萬不得已,我們不應該拷貝 的圖像,因為這會降低程序速度。

為了搞定這個問題,OpenCV使用引用計數機制。其思路是讓每個 Mat 對象有自己的信息頭,但共享同一個矩陣。這通過讓矩陣指針指向同一地址而實現。而拷貝構造函數則 只拷貝信息頭和矩陣指針 ,而不拷貝矩陣

Mat A, C; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 只創建信息頭部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 這里為矩陣開辟內存
?
Mat B(A); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 使用拷貝構造函數
?
C = A; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  // 賦值運算符

以上代碼中的所有Mat對象最終都指向同一個也是唯一一個數據矩陣。雖然它們的信息頭不同,但通過任何一個對象所做的改變也會影響其它對象。實際上,不同的對象只是訪問相同數據的不同途徑而已。這里還要提及一個比較棒的功能:你可以創建只引用部分數據的信息頭。比如想要創建一個感興趣區域( ROI ),你只需要創建包含邊界信息的信息頭:

Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries

2. Mat類拷貝及對象的創建方法

cv::Mat roi_image_color = temp_thin_image_color(cv::Rect(119, 69, 210, 260));//139,129,168,168
?
Mat E = A(Range:all(), Range(1,3)); // 用行和列來界定

上文中的所有對象,以相同的單個數據矩陣的結束點。他們頭不同,但是使用的其中任何一個對矩陣進行任何修改,也將影響所有其他的。在實踐中的不同對象只是提供相同的底層數據不同的訪問方法,然而,它們的頭部是不同的。真正有趣的部分是您可以創建僅指向完整數據的一小部分的頭。例如,要在圖像中創建興趣區域 ( ROI) 您只需創建一個新頭設置新邊界:

cv::Mat M4 = (cv::Mat_<double>(3, 3) << 0, -1, 0, -1, 0, 0, 0, 0, 1);
std::cout << "M4 = " << std::endl << M4 << std::endl;
?
Mat drawing_poly_color = Mat::zeros(roi_image_color.size(), CV_8UC1);
?
cv::Mat temp_thin_image_color = cv::imread("../example/mask.bmp", CV_LOAD_IMAGE_UNCHANGED);
?
?
//這里是將PCL的點云數據中的RGB信息提取出來進行賦值輸出圖片
cv::Mat gray(cloud->height, cloud->width, CV_8UC1);
//前兩個參數是矩陣的行數和列數,后一個矩陣類型8U 8位無符號整數,c1表示1個channel,rgb圖片這里就需要設置為CV_8UC3
?
for (int i = 0; i < cloud->points.size(); i++){uchar* grayrowptr = gray.ptr<uchar>(i / cloud->width);//提取行指針grayrowptr[i%cloud->width] = cloud->points[i].r;
}
?
cv::imwrite("gray_zxr.bmp", gray);

1. Mat創建

Mat A, C; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 只創建信息頭部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 這里為矩陣開辟內存
?
Mat B(A); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 使用拷貝構造函數
?
C = A; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  // 賦值運算符

以上代碼中的所有Mat對象最終都指向同一個也是唯一一個數據矩陣。雖然它們的信息頭不同,但通過任何一個對象所做的改變也會影響其它對象。實際上,不同的對象只是訪問相同數據的不同途徑而已。這里還要提及一個比較棒的功能:你可以創建只引用部分數據的信息頭。比如想要創建一個感興趣區域( ROI ),你只需要創建包含邊界信息的信息頭:

Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries

現在你也許會問,如果矩陣屬于多個 Mat 對象,那么當不再需要它時誰來負責清理?簡單的回答是:最后一個使用它的對象。通過引用計數機制來實現。無論什么時候有人拷貝了一個 Mat 對象的信息頭,都會增加矩陣的引用次數;反之當一個頭被釋放之后,這個計數被減一;當計數值為零,矩陣會被清理。但某些時候你仍會想拷貝矩陣本身(不只是信息頭和矩陣指針),這時可以使用函數 clone() 或者 copyTo() 。

Mat F = A.clone();
Mat G;
A.copyTo(G);

現在改變 F 或者 G 就不會影響 Mat 信息頭所指向的矩陣。總結一下,你需要記住的是

  • OpenCV函數中輸出圖像的內存分配是自動完成的(如果不特別指定的話)。

  • 使用OpenCV的C++接口時不需要考慮內存釋放問題。

  • 賦值運算符和拷貝構造函數( ctor )只拷貝信息頭。

  • 使用函數 clone() 或者 copyTo() 來拷貝一副圖像的矩陣。

3. Mat 對象元素的高效訪問

ptr訪問效率比較高,程序也比較安全,有越界判斷。

//方法1:Mat img(1000, 1000, CV_32F);for (int i=0; i<1000; i++){for (int j=0; j<1000; j++){img.at<float>(i,j) = 3.2f;//方法1:img.ptr<float>(i)[j] = 3.2f;//方法2:}}//***方法3********推薦使用。耗時最短******************************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++;}}//***方法4************************************************************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;}}

?

4. 存儲方法

這里講述如何存儲像素值。需要指定顏色空間和數據類型。顏色空間是指對一個給定的顏色,如何組合顏色元素以對其編碼。最簡單的顏色空間要屬灰度級空間,只處理黑色和白色,對它們進行組合可以產生不同程度的灰色。

對于 彩色 方式則有更多種類的顏色空間,但不論哪種方式都是把顏色分成三個或者四個基元素,通過組合基元素可以產生所有的顏色。RGB顏色空間是最常用的一種顏色空間,這歸功于它也是人眼內部構成顏色的方式。它的基色是紅色、綠色和藍色,有時為了表示透明顏色也會加入第四個元素 alpha (A)。

有很多的顏色系統,各有自身優勢:

  • RGB是最常見的,這是因為人眼采用相似的工作機制,它也被顯示設備所采用。

  • HSV和HLS把顏色分解成色調、飽和度和亮度/明度。這是描述顏色更自然的方式,比如可以通過拋棄最后一個元素,使算法對輸入圖像的光照條件不敏感。

  • YCrCb在JPEG圖像格式中廣泛使用。

  • CIE Lab*是一種在感知上均勻的顏色空間,它適合用來度量兩個顏色之間的 距離

每個組成元素都有其自己的定義域,取決于其數據類型。如何存儲一個元素決定了我們在其定義域上能夠控制的精度。最小的數據類型是 char ,占一個字節或者8位,可以是有符號型(0到255之間)或無符號型(-127到+127之間)。盡管使用三個 char 型元素已經可以表示1600萬種可能的顏色(使用RGB顏色空間),但若使用float(4字節,32位)或double(8字節,64位)則能給出更加精細的顏色分辨能力。但同時也要切記增加元素的尺寸也會增加了圖像所占的內存空間。

5. 顯式創建Mat對象

Mat 不但是一個很贊的圖像容器類,它同時也是一個通用的矩陣類,所以可以用來創建和操作多維矩陣。創建一個Mat對象有多種方法:

 Mat M(2,2, CV_8UC3, Scalar(0,0,255)); cout << "M = " << endl << " " << M << endl << endl; ?

對于二維多通道圖像,首先要定義其尺寸,即行數和列數。

然后,需要指定存儲元素的數據類型以及每個矩陣點的通道數。為此,依據下面的規則有多種定義

CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]

比如 CV_8UC3 表示使用8位的 unsigned char 型,每個像素由三個元素組成三通道。預先定義的通道數可以多達四個。 Scalar 是個short型vector。指定這個能夠使用指定的定制化值來初始化矩陣。當然,如果你需要更多通道數,你可以使用大寫的宏并把通道數放在小括號中,如下所示

在 C\C++ 中通過構造函數進行初始化

 int sz[3] = {2,2,2}; Mat L(3,sz, CV_8UC(1), Scalar::all(0));
  • 上面的例子演示了如何創建一個超過兩維的矩陣:指定維數,然后傳遞一個指向一個數組的指針,這個數組包含每個維度的尺寸;其余的相同

  • 為已存在IplImage指針創建信息頭:

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

    Create() function: 函數

     ?  M.create(4,4, CV_8UC(2));cout << "M = "<< endl << " "  << M << endl << endl;

    這個創建方法不能為矩陣設初值,它只是在改變尺寸時重新為矩陣數據開辟內存。

6. 與其他語言對比的方式

MATLAB形式的初始化方式: zeros(), ones(), :eyes() 。使用以下方式指定尺寸和數據類型:

    Mat E = Mat::eye(4, 4, CV_64F); ? ?cout << "E = " << endl << " " << E << endl << endl;Mat O = Mat::ones(2, 2, CV_32F); ? ?cout << "O = " << endl << " " << O << endl << endl;
?Mat Z = Mat::zeros(3,3, CV_8UC1);cout << "Z = " << endl << " " << Z << endl << endl;

對于小矩陣你可以用逗號分隔的初始化函數:

Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); 
cout << "C = " << endl << " " << C << endl << endl;

7. Mat操作實例

int main(int argc, char** argv) {
?//help();// create by using the constructorMat M(2, 2, CV_8UC3, Scalar(0, 0, 255));cout << "M = " << endl << " " << M << endl << endl;
?// create by using the create function()M.create(4, 4, CV_8UC(2));cout << "M = " << endl << " " << M << endl << endl;
?// create multidimensional matricesint sz[3] = { 2,2,2 };Mat L(3, sz, CV_8UC(1), Scalar::all(0));// Cannot print via operator <<
?// Create using MATLAB style eye, ones or zero matrixMat E = Mat::eye(4, 4, CV_64F);cout << "E = " << endl << " " << E << endl << endl;
?Mat O = Mat::ones(2, 2, CV_32F);cout << "O = " << endl << " " << O << endl << endl;
?Mat Z = Mat::zeros(3, 3, CV_8UC1);cout << "Z = " << endl << " " << Z << endl << endl;
?// create a 3x3 double-precision identity matrixMat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);cout << "C = " << endl << " " << C << endl << endl;
?Mat RowClone = C.row(1).clone();cout << "RowClone = " << endl << " " << RowClone << endl << endl;
?// Fill a matrix with random valuesMat R = Mat(3, 2, CV_8UC3);randu(R, Scalar::all(0), Scalar::all(255));
?Point2f P(5, 1);cout << "Point (2D) = " << P << endl << endl;
?Point3f P3f(2, 6, 7);cout << "Point (3D) = " << P3f << endl << endl;
?
?vector<float> v;v.push_back((float)CV_PI); ? v.push_back(2); ? ?v.push_back(3.01f);
?cout << "Vector of floats via Mat = " << Mat(v) << endl << endl;
?vector<Point2f> vPoints(20);for (size_t E = 0; E < vPoints.size(); ++E)vPoints[E] = Point2f((float)(E * 5), (float)(E % 7));
?cout << "A vector of 2D Points = " << vPoints << endl << endl;getchar();return 0;
}

總結

以上是生活随笔為你收集整理的OpenCV【零】—————cv::Mat——Mat对象创建方法的全部內容,希望文章能夠幫你解決所遇到的問題。

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