Opencv 直方圖均衡化(Histogram Equalization)

 
 留言

直方圖均衡化是圖像處理領域中利用圖像直方圖對比度進行調整的方法。

簡單來說,就是當一張圖片是明亮對比對較為強烈、整體偏暗或亮 (也就是太暗或曝光) 、又或者是彩色圖片中 RGB 的某個 channel 分布較為極端時,就適合是用該方法來讓圖片看起來舒適一些。

再來,我們先來釐清直方圖均衡化的公式,我們以照灰階的圖片來舉例。

  1. px(i)=p(x=i)=nin,0≤i<L。
    • px(i):0~255的灰階值出現的機率值。
    • ni: 圖片中灰階值 i 出現的總次數。
    • n : 像素的總數,也就是圖片的寬乘高。
    • L : 灰階像素值的總數也就是 256

簡單描述上面的公式,我們可以知道 i 的範圍是 0~255。然後使用ni除上 n
我們就可以算出像素值 i 在圖片中出現的機率值。
這一個步驟主要在做歸一化,簡單點來說就是將像素出現的機率值計算出來。

  1. cdfx(i)=∑j=0ipx(j)

接下來我們可以使用累積分布函數(Cumulative Distribution Function)
將原本0~255像素值的累積機率算出來,這一步是將所有像素出現的機率值進行線性化

  1. cdfx(i)∗(255−0)

最後我們再把累積分布函數(cdfx)乘上255,這一步的動作主要是將cdfx(i)實現均衡化到所有像素中。也就是將 0~1 的累積機率放大到0~255。
原本的pixel值為i,會變為 pixel[i]=cdfx(i)∗(255−0);


我們舉個例子假設今天的圖片灰階值i的範圍是0~10, 正常的灰階圖是 0~255。

我們可以發現,原本的機率值是px(i),依序0~10的數值是非常離散的。
我們經過直方圖均衡化後的結果會變成cdfx(i)∗(10)
我們拿來與最初的機率值做相比,我們可以發現像素值變得線性許多。


我們這邊舉實際的範例,灰階圖片的像素值是0~255的範圍。
先來說說灰階。Pixel的數值255是白色、0是黑色。
藉由0~255的像素值進行排列組合到圖片中,就會形成我們看到的灰階的圖片。

我們以圖片進行舉例。
這是原本的圖片:

這是經過變暗的圖片(所有的Pixel數值減少):

當我們經過了直方圖均衡化後,可以發現整張圖片看起來對比度均勻很多:


我們用直方圖來看看經過直方圖均衡化前後的差別。
(以下為示意圖,圖畫的可能不是那麼精準)
我們先來查看這張未經過均衡化的直線圖,我們可以發現整張圖的像素值會著重在大約110~200像素值左右的區間,這會導致照片的黑白不平均。
假設說有一張照片拍攝時,在很暗地方,像是上面的圖片二。
我們可以看到圖片整體是灰暗的,看起來很不協調。

經過了直方圖均衡化後,我們可以看到直方圖的像素值都均勻了許多,像素間不會有明顯的落差。就像是我們看到的圖片三。我們可以發現圖片整體曝光相較於圖片二明亮了許多。改善了圖片二過於昏暗的情境。


下方為完整的程式碼:

#include<opencv2/opencv.hpp>
#include<stdio.h>
using namespace std;
using namespace cv;

int main()
{
	string fileName = "dog_gray_dark";
	Mat img = imread(fileName + ".jpg", 0);
	Mat dst = Mat::zeros(img.size(), CV_8UC1);

	double pixelValue[256] = { 0 }; // Init array
	int pixelSum = 0;

	// 計算所有0~255的灰階中pixel有幾個
	for (int i = 0; i < 256; i++) {
		for (int j = 0; j < img.rows; j++) {
			for (int k = 0; k < img.cols; k++) {
				if (img.at<uchar>(j, k) == i) {
					pixelValue[i]++;

				}
			}
		}
	}

	// 計算所有pixel的總數等於圖片的寬*高
	pixelSum = img.rows * img.cols;

	double addCDFX = 0;
	for (int i = 0; i < 256; i++) {
		addCDFX += double(pixelValue[i] / pixelSum);
		for (int j = 0; j < dst.rows; j++) {
			for (int k = 0; k < dst.cols; k++) {
				if (img.at<uchar>(j, k) == i) {
					dst.at<uchar>(j, k) = 0 + int(addCDFX * 255);
				}
			}
		}
	}
	imwrite(fileName + "_result.jpg", dst);
	imshow("src image", img);
	imshow("dst image", dst);

	waitKey();
}

如需更多客製化服務:


另外有安裝或者其他使用上的問題,皆可參考下列篇章:

如有任何問題歡迎下方留言討論!!!

arrow
arrow

    微笑創客 發表在 痞客邦 留言(0) 人氣()