【從零學習OpenCV 4】兩影像間的像素操作

  • 2019 年 11 月 27 日
  • 筆記

經過幾個月的努力,小白終於完成了市面上第一本OpenCV 4入門書籍《從零學習OpenCV 4》。為了更讓小夥伴更早的了解最新版的OpenCV 4,小白與出版社溝通,提前在公眾號上連載部分內容,請持續關注小白。

前面介紹的計算最值、平均值等操作都是對一張影像進行處理,接下來將介紹兩張影像間像素的相關操作,包含兩張影像的比較運算、邏輯運算等。

1

01

兩張影像的比較運算

OpenCV 4中提供了求取兩張影像每一位像素較大或者較小灰度值的max()、min()函數,這兩個函數分別比較兩個影像中每一位元素灰度值的大小,保留較大(較小)的灰度值,這兩個函數的函數原型在程式碼清單3-13中給出。

程式碼清單3-13 max()和min()函數原型  1.  void cv::max(InputArray src1,  2.               InputArray src2,  3.               OutputArray dst  4.                  )  5.  void cv::min(InputArray src1,  6.               InputArray src2,  7.               OutputArray dst  8.                  )
  • src1:第一個影像矩陣,可以是任意通道數的矩陣。
  • src2:第二個影像矩陣,尺寸和通道數以及數據類型都需要與src1一致。
  • dst:保留對應位置較大(較小)灰度值後的影像矩陣,尺寸、通道數和數據類型與src1一致。

該函數的功能相對來說比較簡單,就是比較影像每個像素的大小,按要求保留較大值或者較小值,最後生成新的影像。例如,第一張影像

位置像素值為100,第二張影像

位置像素值為10,那麼輸出影像

位置像素值為100。在程式碼清單3-14中給出了這兩個函數的程式碼實現過程以及運算結果,運算結果在圖3-9、圖3-10和圖3-11中給出。這種比較運算主要用在對矩陣類型數據的處理,與掩模影像進行比較運算可以實現摳圖或者選擇通道的效果。

程式碼清單3-14 myMaxAndMin.cpp兩個矩陣或影像進行比較運算  1.  #include <opencv2opencv.hpp>  2.  #include <iostream>  3.  #include <vector>  4.  5.  using namespace std;  6.  using namespace cv;  7.  8.  int main()  9. {  10.    float a[12] = { 1, 2, 3.3, 4, 5, 9, 5, 7, 8.2, 9, 10, 2 };  11.    float b[12] = { 1, 2.2, 3, 1, 3, 10, 6, 7, 8, 9.3, 10, 1 };  12.    Mat imga = Mat(3, 4, CV_32FC1, a);  13.    Mat imgb = Mat(3, 4, CV_32FC1, b);  14.    Mat imgas = Mat(2, 3, CV_32FC2, a);  15.    Mat imgbs = Mat(2, 3, CV_32FC2, b);  16.  17.    //對兩個單通道矩陣進行比較運算  18.    Mat myMax, myMin;  19.    max(imga, imgb, myMax);  20.    min(imga, imgb, myMin);  21.  22.    //對兩個多通道矩陣進行比較運算  23.    Mat myMaxs, myMins;  24.    max(imgas, imgbs, myMaxs);  25.    min(imgas, imgbs, myMins);  26.  27.    //對兩張彩色影像進行比較運算  28.    Mat img0 = imread("lena.png");  29.    Mat img1 = imread("noobcv.jpg");  30.    if (img0.empty()|| img1.empty())  31.    {  32.      cout << "請確認影像文件名稱是否正確" << endl;  33.      return -1;  34.    }  35.    Mat comMin, comMax;  36.    max(img0, img1, comMax);  37.    min(img0, img1, comMin);  38.    imshow("comMin", comMin);  39.    imshow("comMax", comMax);  40.  41.    //與掩模進行比較運算  42.    Mat src1 = Mat::zeros(Size(512, 512), CV_8UC3);  43.    Rect rect(100, 100, 300, 300);  44.    src1(rect) = Scalar(255, 255, 255); //生成一個低通300*300的掩模  45.    Mat comsrc1, comsrc2;  46.    min(img0, src1, comsrc1);  47.    imshow("comsrc1", comsrc1);  48.  49.    Mat src2 = Mat(512, 512, CV_8UC3, Scalar(0,0, 255)); //生成一個顯示紅色通道的低通掩模  50.    min(img0, src2, comsrc2);  51.    imshow("comsrc2", comsrc2);  52.  53.    //對兩張灰度影像進行比較運算  54.    Mat img0G, img1G, comMinG, comMaxG;  55.    cvtColor(img0, img0G, COLOR_BGR2GRAY);  56.    cvtColor(img1, img1G, COLOR_BGR2GRAY);  57.    max(img0G, img1G, comMaxG);  58.    min(img0G, img1G, comMinG);  59.    imshow("comMinG", comMinG);  60.    imshow("comMaxG", comMaxG);  61.    waitKey(0);  62.    return 0;  63.  }

圖3-9 maxAndMin.cpp程式中兩個矩陣進行比較運算結果

圖3-10 axAndMin.cpp程式中兩個彩色影像和灰度影像進行比較運算結果

圖3-11 與掩模影像進行比較運算結果

1

02

兩張影像的邏輯運算

OpenCV 4針對兩個影像像素之間的與、或、異或以及非運算提供了bitwise_and()、bitwise_or()、bitwise_xor()和bitwise_not()四個函數,在程式碼清單3-15中給出了這四個函數的函數原型。在了解函數用法之前,我們先了解一下影像像素邏輯運算的規則。影像像素間的邏輯運算與數字間的邏輯運算相同,具體規則在圖3-12中給出。像素的非運算只能針對一個數值進行,因此在圖3-12中對像素求非運算時對影像1的像素值進行非運算。如果像素取值只有0和1的話,那麼圖中的前4行數據正好對應了所有的運算規則,但是CV_8U類型的影像像素值從0取到255,此時的邏輯運算就需要將像素值轉成二進位數後再進行,因為CV_8U類型是8位數據,因此對0求非是11111111,也就是255。在圖3-12中最後一行數據中,像素值5對應的二進位為101,像素值6對應的二進位是110,因此與運算得100(4),或運算得111(7),異或運算得011(3),對像素值5進行非運算得11111010(250)。了解了像素的輯運算原理之後,我們再來看OpenCV 4中提供的輯運算函數的使用方法。

圖3-12 影像邏輯運算規則

程式碼清單3-15 OpenCV 4中像素邏輯運算函數原型  1.  //像素求與運算  2.  void cv::bitwise_and(InputArray src1,  3.                           InputArray src2,  4.                           OutputArray dst,  5.                           InputArray mask = noArray()  6.                           )  7.  //像素求或運算  8.  void cv::bitwise_or(InputArray src1,  9.                     InputArray src2,  10.                     OutputArray dst,  11.                     InputArray mask = noArray()  12.                       )  13.  //像素求異或運算  14.  void cv::bitwise_xor(InputArray src1,  15.                      InputArray src2,  16.                      OutputArray dst,  17.                      InputArray mask = noArray()  18.                        )  19.  //像素求非運算  20.  void cv::bitwise_not(InputArray src,  21.                      OutputArray dst,  22.                      InputArray mask = noArray()  23.                        )
  • src1:第一個影像矩陣,可以是多通道影像數據。
  • src2:第二個影像矩陣,尺寸和通道數以及數據類型都需要與src1一致。
  • dst:邏輯運算輸出結果,尺寸和通道數和數據類型與src1一致。
  • mask:掩模,用於設置影像或矩陣中邏輯運算的範圍。

這幾個函數都執行相應的邏輯運算,在進行邏輯計算時,一定要保證兩個影像矩陣之間的尺寸、數據類型和通道數相同,多個通道進行邏輯運算時不同通道之間是獨立進行的。為了更加直觀的理解兩個影像像素間的邏輯運算,在程式碼清單3-16中給出兩個黑白影像像素邏輯運算的示常式序,最後運行結果在圖3-13中給出。

程式碼清單3-16 myLogicOperation.cpp兩個影像像素邏輯運算  1.  #include <opencv2opencv.hpp>  2.  #include <iostream>  3.  #include <vector>  4.  5.  using namespace std;  6.  using namespace cv;  7.  8.  int main()  9. {  10.    Mat img = imread("lena.png");  11.    if (img.empty())  12.    {  13.      cout << "請確認影像文件名稱是否正確" << endl;  14.      return -1;  15.    }  16.    //創建兩個黑白影像  17.    Mat img0 = Mat::zeros(200, 200, CV_8UC1);  18.    Mat img1 = Mat::zeros(200, 200, CV_8UC1);  19.    Rect rect0(50, 50, 100, 100);  20.    img0(rect0) = Scalar(255);  21.    Rect rect1(100, 100, 100, 100);  22.    img1(rect1) = Scalar(255);  23.    imshow("img0", img0);  24.    imshow("img1", img1);  25.  26.    //進行邏輯運算  27.    Mat myAnd, myOr, myXor, myNot, imgNot;  28.    bitwise_not(img0, myNot);  29.    bitwise_and(img0, img1, myAnd);  30.    bitwise_or(img0, img1, myOr);  31.    bitwise_xor(img0, img1, myXor);  32.    bitwise_not(img, imgNot);  33.    imshow("myAnd", myAnd);  34.    imshow("myOr", myOr);  35.    imshow("myXor", myXor);  36.    imshow("myNot", myNot);  37.    imshow("img", img);  38.    imshow("imgNot", imgNot);  39.    waitKey(0);  40.    return 0;  41.  }

圖3-13 logicOperation.cpp程式運行結果