Mat-基本圖像容器

2018-08-29 10:58 更新

目標(biāo)

我們有多種方式從現(xiàn)實(shí)世界中獲取數(shù)字圖像:數(shù)碼相機(jī),掃描儀,計(jì)算機(jī)斷層掃描和磁共振成像等等。在任何情況下,我們(人類(lèi))看到的都是圖像。然而,當(dāng)將其轉(zhuǎn)換為數(shù)字設(shè)備時(shí),我們記錄的是圖像中每個(gè)點(diǎn)的數(shù)值。

Mat-基本圖像容器

例如在上述圖像中,您可以看到汽車(chē)的鏡像只不過(guò)是一個(gè)包含像素點(diǎn)所有強(qiáng)度值的矩陣。我們?nèi)绾潍@取和存儲(chǔ)像素值可能會(huì)根據(jù)我們的需要而有所不同,但最終,計(jì)算機(jī)世界內(nèi)的所有圖像可能會(huì)被減少到描述矩陣本身的數(shù)字矩陣和其他信息。OpenCV是一個(gè)計(jì)算機(jī)視覺(jué)庫(kù),其主要重點(diǎn)是處理和操縱這些信息。因此,您需要熟悉的第一件事是OpenCV如何存儲(chǔ)和處理圖像。

Mat

OpenCV自2001年以來(lái)一直存在。在那些日子里,庫(kù)是圍繞C接口構(gòu)建的,并將圖像存儲(chǔ)在內(nèi)存中,它們使用了一個(gè)稱(chēng)為IplImage的C結(jié)構(gòu)。這是大部分老版本的教程和教材。這樣做的問(wèn)題是它帶來(lái)了C語(yǔ)言的所有缺點(diǎn)。最大的問(wèn)題是手動(dòng)內(nèi)存管理。它建立在用戶(hù)負(fù)責(zé)處理內(nèi)存分配和釋放的假設(shè)的基礎(chǔ)上。雖然這不是一個(gè)較小的程序的問(wèn)題,但一旦你的代碼基礎(chǔ)的增長(zhǎng),處理這些代碼就更難,而不是專(zhuān)注于解決你的發(fā)展目標(biāo)。

幸運(yùn)的是C ++來(lái)了,并介紹了類(lèi)的概念,使用戶(hù)更容易通過(guò)自動(dòng)內(nèi)存管理(或多或少)。好消息是,C ++與C完全兼容,所以在進(jìn)行更改時(shí)不會(huì)出現(xiàn)任何兼容性問(wèn)題。因此,OpenCV 2.0引入了一個(gè)新的C ++界面,它提供了一種新的處理方式,這意味著您不需要調(diào)節(jié)內(nèi)存管理,使您的代碼簡(jiǎn)潔(少寫(xiě),實(shí)現(xiàn)更多)。C ++界面的主要缺點(diǎn)是,目前許多嵌入式開(kāi)發(fā)系統(tǒng)只支持C.因此,除非您定位嵌入式平臺(tái),否則無(wú)需使用舊方法(除非您是一個(gè)受虐狂程序員,而且您在問(wèn)為了麻煩)。

您需要了解Mat的第一件事是,您不再需要手動(dòng)分配其內(nèi)存,并在不需要它時(shí)立即發(fā)布它。在執(zhí)行此操作仍然是可能的情況下,大多數(shù)OpenCV功能將自動(dòng)分配其輸出數(shù)據(jù)。如果您傳遞已經(jīng)存在的Mat對(duì)象(已經(jīng)為矩陣分配了所需的空間),那么這是一個(gè)很好的獎(jiǎng)勵(lì),這將被重用。換句話說(shuō),我們?cè)谌魏螘r(shí)候都使用與我們需要執(zhí)行任務(wù)一樣多的內(nèi)存。

Mat基本上是一個(gè)具有兩個(gè)數(shù)據(jù)部分的類(lèi):矩陣頭(包含矩陣的大小,用于存儲(chǔ)的方法,存儲(chǔ)在哪個(gè)地址的信息等等)和指向包含像素值(取決于所選存儲(chǔ)方法的任何維度)。矩陣頭大小是恒定的,然而矩陣本身的大小可以隨著圖像的不同而變化,通常會(huì)大一個(gè)數(shù)量級(jí)。

OpenCV是一個(gè)圖像處理庫(kù)。它包含大量的圖像處理功能。為了解決計(jì)算挑戰(zhàn),大多數(shù)時(shí)候你最終會(huì)使用庫(kù)的多個(gè)功能。因此,將圖像傳遞給功能是常見(jiàn)的做法。我們不應(yīng)該忘記,我們正在談?wù)摰膱D像處理算法,這往往是相當(dāng)計(jì)算重。我們想要做的最后一件事是通過(guò)制作不必要的可能的大圖像副本進(jìn)一步降低程序的速度。

為解決這個(gè)問(wèn)題,OpenCV使用引用計(jì)數(shù)系統(tǒng)。這個(gè)想法是每個(gè)Mat對(duì)象都有自己的頭,但是通過(guò)使它們的矩陣指針指向相同的地址,矩陣可以在它們的兩個(gè)實(shí)例之間共享。此外,復(fù)制操作符只會(huì)將頭和指針復(fù)制到大矩陣,而不是數(shù)據(jù)本身。

Mat A, C;                          // creates just the header parts
A = imread(argv[1], IMREAD_COLOR); // here we'll know the method used (allocate matrix)
Mat B(A);                                 // Use the copy constructor
C = A;                                    // Assignment operator

所有上述對(duì)象,最后指向相同的單個(gè)數(shù)據(jù)矩陣。然而,它們的頭部是不同的,并且使用它們中的任何一個(gè)進(jìn)行修改也會(huì)影響所有其他的。在實(shí)踐中,不同的對(duì)象只是向相同的底層數(shù)據(jù)提供不同的訪問(wèn)方法。然而,他們的頭部不一樣。真正有趣的部分是您可以創(chuàng)建僅引用完整數(shù)據(jù)的小節(jié)的標(biāo)題。例如,要在圖像中創(chuàng)建感興趣區(qū)域(ROI),您只需創(chuàng)建一個(gè)新邊界的新標(biāo)題:

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)在您可以詢(xún)問(wèn)矩陣本身是否屬于多個(gè)Mat對(duì)象,它們?cè)诓辉傩枰獣r(shí)負(fù)責(zé)清理它。簡(jiǎn)短的答案是:使用它的最后一個(gè)對(duì)象。這是通過(guò)使用引用計(jì)數(shù)機(jī)制來(lái)處理的。每當(dāng)有人復(fù)制Mat對(duì)象的標(biāo)題時(shí),矩陣的計(jì)數(shù)器就會(huì)增加。每當(dāng)頭部被清潔時(shí),這個(gè)計(jì)數(shù)器就會(huì)減少。當(dāng)計(jì)數(shù)器達(dá)到零時(shí),矩陣也被釋放。有時(shí)你也想復(fù)制矩陣本身,所以O(shè)penCV提供了cv :: Mat :: clone()cv :: Mat :: copyTo()函數(shù)。

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

現(xiàn)在修改F或G不會(huì)影響Mat頭指向的矩陣。所有這一切你需要記住的是:

  • OpenCV功能的輸出圖像分配是自動(dòng)的(除非另有說(shuō)明)。
  • 您不需要考慮OpenCVs C ++界面的內(nèi)存管理。
  • 賦值運(yùn)算符和復(fù)制構(gòu)造函數(shù)只復(fù)制標(biāo)題。
  • 可以使用cv :: Mat :: clone()cv :: Mat :: copyTo()函數(shù)復(fù)制圖像的基礎(chǔ)矩陣。

存儲(chǔ)方法

這是關(guān)于如何存儲(chǔ)像素值。您可以選擇使用的顏色空間和數(shù)據(jù)類(lèi)型。顏色空間是指我們?nèi)绾谓M合顏色分量以編碼給定的顏色。最簡(jiǎn)單的一個(gè)是灰色,我們可以使用的顏色是黑色和白色。這些組合使我們能夠創(chuàng)建許多灰色陰影。

對(duì)于豐富多彩的方式,我們有更多的選擇方法。他們每個(gè)人都將它們分解成三到四個(gè)基本組件,我們可以使用這些組合來(lái)創(chuàng)建其他組件。最流行的是RGB,主要是因?yàn)檫@也是我們的眼睛如何建立顏色。其基色為紅,綠,藍(lán)。為了編碼顏色的透明度有時(shí)是第四個(gè)元素:添加了α(A)。

然而,還有許多其他顏色系統(tǒng)都有自己的優(yōu)勢(shì):

  • RGB是最常見(jiàn)的,因?yàn)槲覀兊难劬κ褂妙?lèi)似的東西,但請(qǐng)記住,OpenCV標(biāo)準(zhǔn)顯示系統(tǒng)使用BGR顏色空間(紅色和藍(lán)色通道的開(kāi)關(guān))組成顏色。
  • HSV和HLS將顏色分解為色調(diào),飽和度和值/亮度分量,這是我們描述顏色的更自然的方式。例如,您可能會(huì)忽略最后一個(gè)組件,使您的算法對(duì)輸入圖像的光線條件不太敏感。
  • YCrCb被流行的JPEG圖像格式使用。
  • CIE L * a * b *是感知統(tǒng)一的顏色空間,如果您需要測(cè)量給定顏色與其他顏色的距離,則可方便使用。

每個(gè)建筑組件都有自己的有效域。這導(dǎo)致使用的數(shù)據(jù)類(lèi)型。我們?nèi)绾未鎯?chǔ)組件定義了我們?cè)谄溆蛑械目丶???赡艿淖钚?shù)據(jù)類(lèi)型是char,這意味著一個(gè)字節(jié)或8位。這可能是無(wú)符號(hào)的(因此可以存儲(chǔ)從0到255的值)或帶符號(hào)(從-127到+127的值)。盡管在三個(gè)組件的情況下,這已經(jīng)提供了1600萬(wàn)個(gè)可能的顏色來(lái)表示(像在RGB情況下),我們可以通過(guò)使用浮點(diǎn)數(shù)(4字節(jié)= 32位)或雙(8字節(jié)= 64位)數(shù)據(jù)來(lái)獲得更精細(xì)的控制每個(gè)組件的類(lèi)型。然而,請(qǐng)記住,增加組件的大小也會(huì)增加內(nèi)存中整個(gè)畫(huà)面的大小。

明確創(chuàng)建一個(gè)Mat對(duì)象

加載,修改和保存圖像教程中,您已經(jīng)學(xué)習(xí)了如何使用cv :: imwrite()函數(shù)將矩陣寫(xiě)入圖像文件。但是,為了調(diào)試目的,查看實(shí)際值更為方便。您可以使用Mat的“操作符”來(lái)執(zhí)行此操作。請(qǐng)注意,這僅適用于二維矩陣。

雖然Mat作為一個(gè)圖像容器非常好,但它也是一個(gè)通用的矩陣類(lèi)。因此,可以創(chuàng)建和操縱多維矩陣。您可以通過(guò)多種方式創(chuàng)建Mat對(duì)象:

    Mat M(2,2,CV_8UC3,Scalar(0,0,255));
    cout << “M =” << endl << “” <<“M << endl << endl;

MatBasicContainerOut1

對(duì)于二維和多通道圖像,我們首先定義它們的大小:行和列數(shù)明智。

然后,我們需要指定用于存儲(chǔ)元素的數(shù)據(jù)類(lèi)型和每個(gè)矩陣點(diǎn)的通道數(shù)。為此,我們根據(jù)以下約定構(gòu)造了多個(gè)定義:

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

例如,CV_8UC3意味著我們使用8位長(zhǎng)的無(wú)符號(hào)字符類(lèi)型,每個(gè)像素有三個(gè)形成三個(gè)通道。這是最多四個(gè)通道號(hào)預(yù)定義的。該CV ::標(biāo)量是四個(gè)元件短矢量。指定這一點(diǎn),您可以使用自定義值初始化所有矩陣點(diǎn)。如果您需要更多功能,您可以使用上部宏來(lái)創(chuàng)建類(lèi)型,在括號(hào)中設(shè)置通道號(hào),如下所示。

  • 使用C / C ++數(shù)組并通過(guò)構(gòu)造函數(shù)進(jìn)行初始化
    int sz[3] = {2,2,2};
    Mat L(3,sz, CV_8UC(1), Scalar::all(0));

上面的例子顯示了如何創(chuàng)建一個(gè)具有兩維以上的矩陣。指定其維度,然后傳遞一個(gè)包含每個(gè)維的大小的指針,其余的保持不變。

    M.create(4,4,CV_8UC(2));
    cout << “M =” << endl << “”   <<“M << endl << endl;

MatBasicContainerOut2

您無(wú)法使用此結(jié)構(gòu)初始化矩陣值。如果新的大小不適合舊的,它將僅重新分配其矩陣數(shù)據(jù)存儲(chǔ)器。

    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;

MatBasicContainerOut3

  • 對(duì)于小矩陣,您可以使用逗號(hào)分隔的初始化器或初始化器列表(在最后一種情況下需要C ++ 11支持):
    Mat C =(Mat_ <double>(3,3)<< 0,-1,0,-1,5,-1,0,-1,0);
    cout << “C =” << endl << “”“ << C << endl << endl;
    C =(Mat_ <double>({0,-1,0,-1,5,-1,0,-1,0}))。reshape(3);
    cout << “C =” << endl << “”“ << C << endl << endl;

MatBasicContainerOut6

為現(xiàn)有的Mat對(duì)象和cv :: Mat :: clonecv :: Mat :: copyTo創(chuàng)建一個(gè)新標(biāo)題。

    Mat RowClone = C.row(1).clone();
    cout << “RowClone =” << endl << “” << RowClone << endl << endl;

MatBasicContainerOut7

注意
您可以使用cv :: randu()函數(shù)填入隨機(jī)值的矩陣。您需要給出隨機(jī)值的較低和較高值:
    Mat R = Mat(3,2,CV_8UC3);
    randu(R,Scalar :: all(0),Scalar :: all(255));

輸出格式

在上述示例中,您可以看到默認(rèn)的格式化選項(xiàng)。然而,OpenCV允許您格式化矩陣輸出:

  • Default
    cout << “R(default)=” << endl << R << endl << endl;

MatBasicContainerOut8

  • Python

    cout << "R (python)  = " << endl << format(R, Formatter::FMT_PYTHON) << endl << endl;

MatBasicContainerOut16

  • Comma separated values (CSV)

    cout << “R(csv)=” << endl << format(R,F(xiàn)ormatter :: FMT_CSV)<< endl << endl;

MatBasicContainerOut10

  • NumPy

    cout << "R (numpy)   = " << endl << format(R, Formatter::FMT_NUMPY ) << endl << endl;

MatBasicContainerOut9

  • C

    cout << “R(c)=” << endl << format(R,F(xiàn)ormatter :: FMT_C)<< endl << endl;

MatBasicContainerOut11

其他常用項(xiàng)??目的輸出

OpenCV還通過(guò)<<運(yùn)算符來(lái)支持其他常見(jiàn)OpenCV數(shù)據(jù)結(jié)構(gòu)的輸出:

  • 2D Point
    Point2f P(5,1);
    cout << “Point(2D)=” << P << endl << endl;

MatBasicContainerOut12

  • 3D Point

    Point3f P3f(2, 6, 7);
    cout << "Point (3D) = " << P3f << endl << endl;

MatBasicContainerOut13

    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;

MatBasicContainerOut14

  • std::vector of points

    vector<Point2f> vPoints(20);
    for (size_t i = 0; i < vPoints.size(); ++i)
        vPoints[i] = Point2f((float)(i * 5), (float)(i % 7));
    cout << "A vector of 2D Points = " << vPoints << endl << endl;

MatBasicContainerOut15

這里的大多數(shù)樣品已被包含在一個(gè)小的控制臺(tái)應(yīng)用程序中。您可以從這里或cpp示例的核心部分下載。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)