【從零學習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程式運行結果