重磅干貨,,第一時間送達(dá) 經(jīng)過幾個月的努力,,小白終于完成了市面上第一本OpenCV 4入門書籍《從零學(xué)習(xí)OpenCV 4》。為了更讓小伙伴更早的了解最新版的OpenCV 4,,小白與出版社溝通,,提前在公眾號上連載部分內(nèi)容,請持續(xù)關(guān)注小白,。 直方圖均衡化函數(shù)可以自動的改變圖像直方圖的分布形式,,這種方式極大的簡化了直方圖均衡化過程中需要的操作步驟,但是該函數(shù)不能指定均衡化后的直方圖分布形式,。在某些特定的條件下需要將直方圖映射成指定的分布形式,,這種將直方圖映射成指定分布形式的算法稱為直方圖匹配或者直方圖規(guī)定化。直方圖匹配與直方圖均衡化相似,,都是對圖像的直方圖分布形式進(jìn)行改變,,只是直方圖均衡化后的圖像直方圖是均勻分布的,,而直方圖匹配后的直方圖可以隨意指定,即在執(zhí)行直方圖匹配操作時,,首先要知道變換后的灰度直方圖分布形式,,進(jìn)而確定變換函數(shù)。直方圖匹配操作能夠有目的的增強(qiáng)某個灰度區(qū)間,,相比于直方圖均衡化操作,,該算法雖然多了一個輸入,但是其變換后的結(jié)果也更靈活,。 由于不同圖像間像素數(shù)目可能不同,,為了使兩個圖像直方圖能夠匹配,需要使用概率形式去表示每個灰度值在圖像像素中所占的比例,。理想狀態(tài)下,,經(jīng)過圖像直方圖匹配操作后圖像直方圖分布形式應(yīng)與目標(biāo)分布一致,因此兩者之間的累積概率分布也一致,。累積概率為小于等于某一灰度值的像素數(shù)目占所有像素中的比例,。我們用Vs表示原圖像直方圖的各個灰度級的累積概率,用Vz表示匹配后直方圖的各個灰度級累積概率,。那么確定由原圖像中灰度值n映射成r的條件如式(6.8)所示,。
(6.8)
為了更清楚的說明直方圖匹配過程,在圖4-7中給出了一個直方圖匹配示例,。示例中目標(biāo)直方圖灰度值2以下的概率都為0,,灰度值3的累積概率為0.16,灰度值4的累積概率為0.35,,原圖像直方圖灰度值為0時累積概率為0.19,。0.19距離0.16的距離小于距離0.35的距離,因此需要將原圖像中灰度值0匹配成灰度值3,。同樣,,原圖像灰度值1的累積概率為0.43,其距離目標(biāo)直方圖灰度值4的累積概率0.35的距離為0.08,,而距離目標(biāo)直方圖灰度值5的累積概率0.64的距離為0.21,,因此需要將原圖像中灰度值1匹配成灰度值4。 圖4-7 直方圖匹配示例
這個尋找灰度值匹配的過程是直方圖匹配算法的關(guān)鍵,,在代碼實現(xiàn)中我們可以通過構(gòu)建原直方圖累積概率與目標(biāo)直方圖累積概率之間的差值表,,尋找原直方圖中灰度值n的累積概率與目標(biāo)直方圖中所有灰度值累積概率差值的最小值,這個最小值對應(yīng)的灰度值r就是n匹配后的灰度值,。 在OpenCV 4中并沒有提供直方圖匹配的函數(shù),,需要自己根據(jù)算法實現(xiàn)圖像直方圖匹配。在代碼清單4-9中給出了實現(xiàn)直方圖匹配的示例程序。程序中待匹配的原圖是一個圖像整體偏暗的圖像,,目標(biāo)直方圖分配形式來自于一張較為明亮的圖像,,經(jīng)過圖像直方圖匹配操作之后,提高了圖像的整體亮度,,圖像直方圖分布也更加均勻,,程序中所有的結(jié)果在圖4-8、圖4-9給出,。 代碼清單4-9 myHistMatch.cpp圖像直方圖匹配 1. #include <opencv2\opencv.hpp> 2. #include <iostream> 3. 4. using namespace cv; 5. using namespace std; 6. 7. void drawHist(Mat &hist, int type, string name) //歸一化并繪制直方圖函數(shù) 8. { 9. int hist_w = 512; 10. int hist_h = 400; 11. int width = 2; 12. Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3); 13. normalize(hist, hist, 1, 0, type, -1, Mat()); 14. for (int i = 1; i <= hist.rows; i++) 15. { 16. rectangle(histImage, Point(width*(i - 1), hist_h - 1), 17. Point(width*i - 1,hist_h - cvRound(20 * hist_h*hist.at<float>(i-1)) - 1), 18. Scalar(255, 255, 255), -1); 19. } 20. imshow(name, histImage); 21. } 22. //主函數(shù) 23. int main() 24. { 25. Mat img1 = imread("histMatch.png"); 26. Mat img2 = imread("equalLena.png"); 27. if (img1.empty()||img2.empty()) 28. { 29. cout << "請確認(rèn)圖像文件名稱是否正確" << endl; 30. return -1; 31. } 32. Mat hist1, hist2; 33. //計算兩張圖像直方圖 34. const int channels[1] = { 0 }; 35. float inRanges[2] = { 0,255 }; 36. const float* ranges[1] = { inRanges }; 37. const int bins[1] = { 256 }; 38. calcHist(&img1, 1, channels, Mat(), hist1, 1, bins, ranges); 39. calcHist(&img2, 1, channels, Mat(), hist2, 1, bins, ranges); 40. //歸一化兩張圖像的直方圖 41. drawHist(hist1, NORM_L1, "hist1"); 42. drawHist(hist2, NORM_L1, "hist2"); 43. //計算兩張圖像直方圖的累積概率 44. float hist1_cdf[256] = { hist1.at<float>(0) }; 45. float hist2_cdf[256] = { hist2.at<float>(0) }; 46. for (int i = 1; i < 256; i++) 47. { 48. hist1_cdf[i] = hist1_cdf[i - 1] + hist1.at<float>(i); 49. hist2_cdf[i] = hist2_cdf[i - 1] + hist2.at<float>(i); 50. 51. } 52. //構(gòu)建累積概率誤差矩陣 53. float diff_cdf[256][256]; 54. for (int i = 0; i < 256; i++) 55. { 56. for (int j = 0; j < 256; j++) 57. { 58. diff_cdf[i][j] = fabs(hist1_cdf[i] - hist2_cdf[j]); 59. } 60. } 61. 62. //生成LUT映射表 63. Mat lut(1, 256, CV_8U); 64. for (int i = 0; i < 256; i++) 65. { 66. // 查找源灰度級為i的映射灰度 67. // 和i的累積概率差值最小的規(guī)定化灰度 68. float min = diff_cdf[i][0]; 69. int index = 0; 70. //尋找累積概率誤差矩陣中每一行中的最小值 71. for (int j = 1; j < 256; j++) 72. { 73. if (min > diff_cdf[i][j]) 74. { 75. min = diff_cdf[i][j]; 76. index = j; 77. } 78. } 79. lut.at<uchar>(i) = (uchar)index; 80. } 81. Mat result, hist3; 82. LUT(img1, lut, result); 83. imshow("待匹配圖像", img1); 84. imshow("匹配的模板圖像", img2); 85. imshow("直方圖匹配結(jié)果", result); 86. calcHist(&result, 1, channels, Mat(), hist3, 1, bins, ranges); 87. drawHist(hist3, NORM_L1, "hist3"); //繪制匹配后的圖像直方圖 88. waitKey(0); 89. return 0; 90. } 圖4-8 myHistMatch.cpp程序中匹配圖像原圖,、模板以及匹配后圖像 圖4-9 myHistMatch.cpp程序中給圖像的直方圖
|