天天看點

OpenCV開發筆記(三):OpenCV圖像的概念和基本操作

 上一篇:《​​OpenCV開發筆記(二):cvui互動界面​​》

下一篇:《​​OpenCV開發筆記(四):OpenCV圖檔和視訊資料的讀取與存儲​​》

前言

       OpenCV最主要的功能是用于操作圖像,是以圖像的概念貫穿整個OpenCV,與其相關的核心類就是cv::Mat。

圖像與矩陣

 圖像指數字圖像。

OpenCV開發筆記(三):OpenCV圖像的概念和基本操作

       以上看到的是一張可視化的圖檔,但是在計算機中這副圖像知識一系列亮度各異的店,該圖檔的像素為300 x 200像素的圖,可以用一個300*200的矩陣來表示,舉證元素的值表示這個位置上的像素的亮度,一般來說像素值越大表示該店越亮。

       放大“C”後如下圖所示:

OpenCV開發筆記(三):OpenCV圖像的概念和基本操作

       每個點對應的亮度可以了解為rgb的值,無符号8位數3維,則一個像素點為3維數組,分别對應RGB的值,在OpenCV中資料類型為:CV_8U3C。

       假設M x N,Iij表示第j行j列,對應上圖就是M = 300, N = 200。

OpenCV開發筆記(三):OpenCV圖像的概念和基本操作

       注意:在Opencv中三維數組存儲RGB值,存儲顔色通道的順序不是RGB,而是BGR,如下圖:

OpenCV開發筆記(三):OpenCV圖像的概念和基本操作

OpenCV中的矩陣類cv::Mat

定義

       Mat貫穿了整個OpenCV。

       Mat類的關鍵定義如下:

class CV_EXPORTS Mat
{
    public:
    // 一系列函數
    ...
    /* flag 參數中包含許多關于矩陣的資訊,如:
    -Mat 的辨別
    -資料是否連續
    -深度
    -通道數目
    */
    int flags;
    // 矩陣的維數,取值應該大于或等于 2
    int dims;
    // 矩陣的行數和列數,如果矩陣超過 2 維,這兩個變量的值都為-1
    int rows, cols;
    // 指向資料的指針
    uchar* data;
    // 指向引用計數的指針
    // 如果資料是由使用者配置設定的,則為 NULL
    int* refcount;
    // 其他成員變量和成員函數
    ...
}      

初始化方式

       Mat是一個非常優秀的圖像類,它同時也是一個通用的矩陣類,可以用來建立和操作多元矩陣。有多種方法建立一個Mat對象。

       首先了解下資料的類型(舉例:CV_8UC3):

OpenCV開發筆記(三):OpenCV圖像的概念和基本操作

初始化函數

// 預設形式
cv::Mat mat;
// 拷貝構造形式
cv::Mat mat(const cv::Mat& mat);
// 指定行列範圍的拷貝構造
cv::Mat mat(const cv::Mat& mat, const cv::Range& rows, const cv::Range& cols);
// 指定ROI的拷貝構造
cv::Mat mat(const cv::Mat& mat, const cv::Rect& roi);
// 使用多元數組中指定範圍内的資料的拷貝構造
cv::Mat mat(const cv::Mat& mat, const cv::Range* ranges);
// 指定類型和大小(行列)的二維數組(注意:是行在前,列在後)
cv::Mat mat(int rows, int cols, int type);
// 有初始化值的置頂類型和大小(行列)的二維資料
cv::Mat mat(int rows, int cols, int type, const Scalar& s);
// 使用預先存在資料定義的指定類型和大小(行列)的二維數組
cv::Mat mat(int rows, int cols, int type, void *data, size_t step = AUTO_STEP);
// 指定大小和類型的二維數組
cv::Mat mat(cv::Size sz, int type, const Scalar& s);
// 使用預先存在的資料定義的指定大小和類型的二維數組
cv::Mat mat(cv::Size sz, int type, void *data, size_t step = AUTO_STEP);
// 指定類型的多元資料
cv::Mat mat(int ndims, const int *sizes, int type);
// 使用預先存在的資料定義的指定類型的多元數組
cv::Mat mat(int ndims, const int* sizes, int type, void* data, size_t step = AUTO_STEP);
// 使用cv::Vec定義相同類型、大小為n的一維數組
cv::Mat mat(const cv::Vec<T, n>& vec, bool = copyData = true);
// 使用cv::Matx定義相同類型、大小為mxn的二維數組
cv::Mat mat(const cv::Matx<T, m, n>& vec, bool copyData = true);
// 使用STL vector定義相同類型的一維數組
cv::Mat mat(const std::vector<T>& vec, bool copyData = true);
// 使用zeros()函數定義指定大小和類型的cv::Mat(全為0)
cv::Mat mat = cv::Mat::zeros(int rows, int cols, int type);
// 使用ones()函數定義指定大小和類型的cv::Mat(全為0)
cv::Mat ma = cv::Mat::ones(int rows, int cols, int type);
// 使用eye()函數定義指定大小和類型的cv::Mat(恒等矩陣)
cv::Mat mat = cv::Mat::eye(int rows, int cols, int type);      

像素值的讀寫

       很多時候,我們需要讀取某個像素值,或者設定某個像素值;在更多的時候,

我們需要對整個圖像裡的所有像素進行周遊。OpenCV提供了多種方法來實作圖像的周遊。

方法一:at()函數

cv::Mat grayim(600, 800, CV_8UC1);
cv::Mat colorim(600, 800, CV_8UC3);
// 周遊所有像素,并設定像素值
for( int i = 0; i < grayim.rows; ++i)
{
    for( int j = 0; j < grayim.cols; ++j )
}
    grayim.at<uchar>(i,j) = (i+j)%255;
// 周遊所有像素,并設定像素值
for( int i = 0; i < colorim.rows; ++i)
{
    for( int j = 0; j < colorim.cols; ++j )
    {
        cv::Vec3b pixel;
        // 注意:opencv通道順序,BGR,非RGB
        pixel[0] = i%255;  // Blue
        pixel[1] = j%255;  // Green
        pixel[2] = 0;      // Red
        colorim.at<Vec3b>(i,j) = pixel;
    }
}      

方法二:使用疊代器

// 周遊所有像素,并設定像素值

// 周遊所有像素,并設定像素值
cv::MatIterator_<uchar> grayit, grayend;
for(grayit = grayim.begin<uchar>(), grayend = grayim.end<uchar>();
    grayit != grayend; ++grayit)
{
    *grayit = rand()%255;
}
// 周遊所有像素,并設定像素值
cv::MatIterator_<cv::Vec3b> colorit, colorend;
for(colorit = colorim.begin<cv::Vec3b>(), colorend = colorim.end<cv::Vec3b>();
    colorit != colorend; ++colorit)
{
    (*colorit)[0] = rand()%255; // Blue
    (*colorit)[1] = rand()%255; // Green
    (*colorit)[2] = rand()%255; // Red
}      

方法三:通過資料指針

cv::Mat grayim(600, 800, CV_8UC1);
cv::Mat colorim(600, 800, CV_8UC3);
//周遊所有像素,并設定像素值
for( int i = 0; i < grayim.rows; ++i)
{
    //擷取第 i 行首像素指針
    uchar * p = grayim.ptr<uchar>(i);
    //對第 i 行的每個像素(byte)操作
    for( int j = 0; j < grayim.cols; ++j )
    p[j] = (i+j)%255;
}
//周遊所有像素,并設定像素值
for( int i = 0; i < colorim.rows; ++i)
{
    //擷取第 i 行首像素指針
    cv::Vec3b * p = colorim.ptr<cv::Vec3b>(i);
    for( int j = 0; j < colorim.cols; ++j )
    {
        p[j][0] = i%255;    //Blue
        p[j][1] = j%255;    //Green
        p[j][2] = 0;        //Red
    }
}      

圖像局部操作

選擇單行/單列

Mat Mat::row(int i) const
Mat Mat::col(int j) const      

示例: A 矩陣的第i行,将這一行的所有元素都乘以2,然後指派給第j行

A.row(j) = A.row(i)*2;      

選擇多行/多列

       Range是OpenCV 中新增的類,該類有兩個關鍵變量star和end。Range 對

象可以用來表示矩陣的多個連續的行或者多個連續的列。其表示的範圍為從 start到end,包含start。

示例

// 建立一個機關陣
Mat A = Mat::eye(10, 10, CV_32S);
// 提取第 1 到 3 列(不包括 3)
Mat B = A(Range::all(), Range(1, 3));
// 提取B的第 5 至 9 行(不包括 9)
// 其實等價于C = A(Range(5, 9), Range(1, 3))
Mat C = B(Range(5, 9), Range::all());      

選擇指定區域

    圖像中提取感興趣區域(Region of interest)有兩種方法:

  • 方法一:使用構造函數
//建立寬度為 320,高度為 240 的 3 通道圖像
Mat img(Size(320, 240), CV_8UC3);
//roi 是表示 img 中 Rect(10, 10, 100, 100)區域的對象
Mat roi(img, Rect(10, 10, 100, 100));      
  • 方法二:使用括号運算符
Mat roi2 = img(Rect(10, 10, 100, 100));
當然也可以使用Range對象來定義感興趣區域,如下:
// 用括号運算符
Mat roi3 = img(Range(10, 100), Range(10, 100)); 
// 用構造函數
Mat roi4(img, Range(10, 100), Range(10, 100));      

取對角線元素

       矩陣的對角線元素可以使用cv::Mat類的diag()函數擷取:

Mat Mat::diag(int d) const      
  • 參數d=0時,表示取主對角線;
  • 當參數d>0是,表示取主對角線下方的次對線,如當d=1 時,表示取主對角線下方,且緊貼主多角線的元素;
  • 當參數d<0時,示取主對角線上方的次對角線。如同 row()和 col()函數, diag()函數也不進行記憶體複制操作,其複雜度也是O(1)。

cv::Mat矩陣支援的運算操作

       cv::Mat表達式所支援的運算。

       下面的清單中使用 A 和 B 表示 Mat 類型的對象,使用s表示Scalar對象,alpha 表示double值。

  • 加法,減法,取負:A+B, A-B, A+s, A-s, s+A, s-A, -A
  • 縮放取值範圍:A * alpha
  • 矩陣對應元素的乘法和除法:A.mul(B),A/B,alpha/A
  • 矩陣乘法:A*B(注意此處是矩陣乘法,而不是矩陣對應元素相乘)
  • 矩陣轉置:A.t()
  • 矩陣求逆和求僞逆:A.inv()
  • 矩陣比較運算:A cmpop B,A cmpop alpha,alpha cmpop A。此處cmpop
  • 可以是>,>=,==,!=,<=,<。如果條件成立,則結果矩陣(8U類型矩陣)的對應元素被置為255;否則置0
  • 矩陣位邏輯運算: A logicop B, A logicop s, s logicop A, ~A,此處 logicop
  • 可以是&,|和^
  • 矩陣對應元素的最大值和最小值:min(A, B),min(A, alpha),max(A, B),max(A, alpha)
  • 矩陣中元素的絕對值:abs(A)
  • 叉積和點積:A.cross(B),A.dot(B)

對矩陣不了解的可以閱讀《​​線性代數矩陣以及Eigen庫的介紹、編譯和使用​​》中的矩陣操作解析。

繼續閱讀