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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

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

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

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

目錄

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

1. cv::Mat優(yōu)點(diǎn)及原理(本質(zhì)類)

2. Mat類拷貝及對象的創(chuàng)建方法

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

4. 存儲(chǔ)方法

5. 顯式創(chuàng)建Mat對象

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

7. Mat操作實(shí)例


1. cv::Mat優(yōu)點(diǎn)及原理(本質(zhì)類)

  • 不需要手動(dòng)申請一塊內(nèi)存;

  • 在不需要時(shí)不用再手動(dòng)釋放內(nèi)存;

  • 可以通過類的封裝,方便的獲取到數(shù)據(jù)的相關(guān)信息。

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

    cv::Mat A = cv::imread("image.jpg");cv::Mat B(A);
//淺層拷貝:Mat B=A;B就是淺層拷貝A,B只拷貝了A的的頭部和地址,當(dāng)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是開辟了新的內(nèi)存完全的復(fù)制了A的內(nèi)容,操作B不會(huì)對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

釋放內(nèi)存原則:由于內(nèi)部使用了引用計(jì)數(shù)的方法,類似共享指針,當(dāng)引用計(jì)數(shù)變?yōu)?的時(shí)候才會(huì)真正的釋放內(nèi)存。

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

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

  • 用c + + OpenCV的接口就無需考慮內(nèi)存釋放。

  • 賦值運(yùn)算符和復(fù)制構(gòu)造函數(shù) (構(gòu)造函數(shù))只復(fù)制頭。

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

//二值化,類型轉(zhuǎn)換,賦值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* 是一個(gè)類,由兩個(gè)數(shù)據(jù)部分組成:矩陣頭(包含矩陣尺寸,存儲(chǔ)方法,存儲(chǔ)地址等信息)和一個(gè)指向存儲(chǔ)所有像素值的矩陣(根據(jù)所選存儲(chǔ)方法的不同矩陣可以是不同的維數(shù))的指針。矩陣頭的尺寸是常數(shù)值,但矩陣本身的尺寸會(huì)依圖像的不同而不同,通常比矩陣頭的尺寸大數(shù)個(gè)數(shù)量級。因此,當(dāng)在程序中傳遞圖像并創(chuàng)建拷貝時(shí),大的開銷是由矩陣造成的,而不是信息頭。

OpenCV是一個(gè)圖像處理庫,囊括了大量的圖像處理函數(shù),為了解決問題通常要使用庫中的多個(gè)函數(shù),因此在函數(shù)中傳遞圖像是家常便飯。同時(shí)不要忘了我們正在討論的是計(jì)算量很大的圖像處理算法,因此,除非萬不得已,我們不應(yīng)該拷貝 的圖像,因?yàn)檫@會(huì)降低程序速度。

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

Mat A, C; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 只創(chuàng)建信息頭部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 這里為矩陣開辟內(nèi)存
?
Mat B(A); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 使用拷貝構(gòu)造函數(shù)
?
C = A; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  // 賦值運(yùn)算符

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

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類拷貝及對象的創(chuàng)建方法

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)); // 用行和列來界定

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

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的點(diǎn)云數(shù)據(jù)中的RGB信息提取出來進(jìn)行賦值輸出圖片
cv::Mat gray(cloud->height, cloud->width, CV_8UC1);
//前兩個(gè)參數(shù)是矩陣的行數(shù)和列數(shù),后一個(gè)矩陣類型8U 8位無符號(hào)整數(shù),c1表示1個(gè)channel,rgb圖片這里就需要設(shè)置為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創(chuàng)建

Mat A, C; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 只創(chuàng)建信息頭部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 這里為矩陣開辟內(nèi)存
?
Mat B(A); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 使用拷貝構(gòu)造函數(shù)
?
C = A; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  // 賦值運(yùn)算符

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

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

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

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

現(xiàn)在改變 F 或者 G 就不會(huì)影響 Mat 信息頭所指向的矩陣。總結(jié)一下,你需要記住的是

  • OpenCV函數(shù)中輸出圖像的內(nèi)存分配是自動(dòng)完成的(如果不特別指定的話)。

  • 使用OpenCV的C++接口時(shí)不需要考慮內(nèi)存釋放問題。

  • 賦值運(yùn)算符和拷貝構(gòu)造函數(shù)( ctor )只拷貝信息頭。

  • 使用函數(shù) 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********推薦使用。耗時(shí)最短******************************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. 存儲(chǔ)方法

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

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

有很多的顏色系統(tǒng),各有自身優(yōu)勢:

  • RGB是最常見的,這是因?yàn)槿搜鄄捎孟嗨频墓ぷ鳈C(jī)制,它也被顯示設(shè)備所采用。

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

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

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

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

5. 顯式創(chuàng)建Mat對象

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

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

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

然后,需要指定存儲(chǔ)元素的數(shù)據(jù)類型以及每個(gè)矩陣點(diǎn)的通道數(shù)。為此,依據(jù)下面的規(guī)則有多種定義

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

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

在 C\C++ 中通過構(gòu)造函數(shù)進(jìn)行初始化

 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è)維度的尺寸;其余的相同

  • 為已存在IplImage指針創(chuàng)建信息頭:

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

    Create() function: 函數(shù)

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

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

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

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

    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;

對于小矩陣你可以用逗號(hào)分隔的初始化函數(shù):

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

7. Mat操作實(shí)例

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;
}

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。