2016年11月14日 星期一

OPENCV(8)--Histogram & Histograms Equalization(長條圖與長條圖均衡化)


        這篇文章介紹長條圖原理及OPENCV對長條圖的處裡,包含如何均等化長條圖。

長條圖原理:
        什麼是長條圖呢?通過長條圖你可以對整幅圖像素的分佈有一個整體的瞭解,特別是對灰階圖。
        長條圖的 x 軸是灰度值0(黑) 到 255(白),y 軸是圖片中相同灰度值數量。
透過長條圖我們可以 對圖像的對比度,亮度,灰度分佈等有一個直觀的認識。

   如下<圖一> 左上圖為一對比不強烈的原圖,其對應的右上角長條圖可以看出像素值大多集中在中間像素,通常一張亮度,對比均衡的圖片應當像左下角,而可以看出其對應的右下角長條圖可以看出像素值分布較為均衡。而實際上在圖中下方圖片是經過長條圖均衡化後的結果,底下即來介紹如何用OPENCV,Numpy,MatplotLib來製作長條圖。

<圖一>







        使用 OpenCV 統計長條圖函數 cv2.calcHist() 可以用來統計一幅圖像的長條圖。
        我們一起來熟悉一下這個函數和它的參數:

                cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])

        1.images:  原圖像(圖像格式為 uint8  或 float32)。當傳入函數時應該 用中括弧 []括起
                         來,例如:[img]。
        2.channels: 同樣需要用中括弧括起來,它會告訴函數我們要統計那幅圖像的長條圖。如果
                         輸入圖像是灰度圖,它的值就是 [0];如果是彩色圖像 的話,傳入的參數可以
                         是 [0],[1],[2]   它們分別對應著通道  B,G,R。
        3.mask: 遮罩圖像。要統計整幅圖像的長條圖就把它設為 None。
                         但是如果你想統計圖像某一部分的長條圖的話,你就需要製作一個遮罩圖像,
                         並使用它。
        4.histSize:BIN的數目。也應該用中括弧括起來,例如:[256]。
    上面的長條圖顯示了每個灰度值對應的像素數。如果圖元值為0 到 255,你就需
                         要256個數來顯示上面的長條圖。那bin 就等於256,如果我們只想知道像素值在
                         0 到 15之間的素值點的數目,把它做一個群組,接著 是 16 到 31為一群組,....
                          ,240 到 255。那麼我們只需要 16個值來繪製長條圖。Bin就等於16
         5.ranges: 圖元值範圍,通常為0~256 [0,256]

         底下我們使用OPENCV  cv2.calcHist()函數分別畫出R.G.B的像素的長條圖分布。

<圖二>Home.jpg 圖片為OPENCV source code裡的範例圖片



< 圖二的完整範例程式>

import cv2
import numpy as np
from matplotlib import pyplot as plt


img = cv2.imread('home.jpg',1)
img =cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

hist_full_r = cv2.calcHist([img],[0],None,[256],[0,256])
hist_full_g = cv2.calcHist([img],[1],None,[256],[0,256])
hist_full_b = cv2.calcHist([img],[2],None,[256],[0,256])

plt.subplot(211), plt.imshow(img)
plt.subplot(212),
plt.plot(hist_full_r),
plt.plot(hist_full_g),
plt.plot(hist_full_b),
plt.xlim([0,256]),
plt.show()





長條圖均衡化原理
          想像一下如果一副圖像中的大多是像素點的像素值都集中在一個像素值範圍之內會怎樣呢?例如,如果一幅圖片整體很亮,那所有的圖元值應該都會很 高。但是一副高品質的圖像
的圖元值分佈應該很廣泛。
         所以你應該把它的直方圖做一個橫向拉伸,這就是長條圖均衡化要做的事情,通常情況下這 種操作會改善圖像的對比度。如上<圖一>所示

OpenCV 提供長條圖均衡化函數為 cv2.equalizeHist()。
這個函數的輸 入圖片僅僅是一副灰度圖像,輸出結果是長條圖均衡化之後的圖像。

<圖一>

< 圖一的完整範例程式>

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('h1.jpg',0)
equ = cv2.equalizeHist(img)
#res = np.hstack((img,equ)) #stacking images side-by-side
#
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/ cdf.max()
#
hist2,bins = np.histogram(equ.flatten(),256,[0,256])
cdf2 = hist2.cumsum()
cdf_normalized2 = cdf2 * hist2.max()/ cdf.max()


plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222)
plt.plot(cdf_normalized, color = 'b')
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])

plt.subplot(223), plt.imshow(equ, 'gray')
plt.subplot(224)
plt.plot(cdf_normalized2, color = 'b')
plt.hist(equ.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.show()



CLAHE 有限對比適應性長條圖均衡化

        我們在上邊做的長條圖均衡化會改變整個圖像的對比度,但是在很多情況 下,
這樣做的效果並不好。例如<圖三>右上及中分別是輸入圖像和進行長條圖均衡化之後的輸出圖像,的確在進行完長條圖均衡化之後,圖片背景的對比度被改變了。但是你再對比一下兩幅圖像中雕像的面圖,由於太亮我們丟失了很多資訊。造成這種結果的根本原因在於這幅圖像的長條圖並不是集中在某一個區域。可以觀察右方的長條圖。

        為了解決這個問題,需要使用自我調整的長條圖均衡化(CLAHE)。這種情況下, 整幅圖像會被分成很多小塊,這些小塊被稱為<tiles>(在 OpenCV 中 tiles 的 大小默認是 8x8),然後再對每一個小塊分別進行長條圖均衡化(跟前面類似)。
         所以在每一個的區域中,長條圖會集中在某一個小的區域中(除非有雜訊幹擾。 如果有雜訊的話,雜訊會被放大。為了避免這種情況的出現要使用對比度限制。 對於每個小塊來說,如果長條圖中的 bin 超過對比度的上限的話, 就把 其中的圖元點均勻分散到其他 bins 中,然後在進行長條圖均衡化。
         最後,為了 去除每一個小塊之間“人造的”(由於演算法造成)邊界, 再使用雙線性差值,對 小塊進行縫合。


<圖三>CLAHE 有限對比適應性長條圖均衡化





< 圖三的完整範例程式>

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('statue.jpg',0)

equ = cv2.equalizeHist(img)
#res = np.hstack((img,equ)) #stacking images side-by-side

# create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
#
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/ cdf.max()
#
hist2,bins = np.histogram(equ.flatten(),256,[0,256])
cdf2 = hist2.cumsum()
cdf_normalized2 = cdf2 * hist2.max()/ cdf.max()
hist3,bins = np.histogram(cl1.flatten(),256,[0,256])
cdf2 = hist2.cumsum()
cdf_normalized2 = cdf2 * hist2.max()/ cdf.max()

plt.subplot(321), plt.imshow(img, 'gray')
plt.subplot(322)
plt.plot(cdf_normalized, color = 'b')
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])

plt.subplot(323), plt.imshow(equ, 'gray')
plt.subplot(324)
plt.plot(cdf_normalized2, color = 'b')
plt.hist(equ.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])

plt.subplot(325), plt.imshow(cl1, 'gray')
plt.subplot(326)
plt.plot(cdf_normalized2, color = 'b')
plt.hist(cl1.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])

plt.show()





加入阿布拉機的3D列印與機器人的FB專頁
https://www.facebook.com/arbu00/


<其他有關OPENCV 文章>
OPENCV(7)--2D Convolution ,Image Filtering and Blurring (旋積,濾波與模糊)
OPENCV(6)--Trackbar(軌道桿)
OPENCV(5)--Drawing
OPENCV(4)--Grayscale,Binarization,Threshole(灰階化,二值化,閥值)
OPENCV(3)--Matplotlib pyplot bassic function
OPENCV(2)--Capture Video from Camera
OPENCV(1 )--How to install OPENCV in Python

<參考資料:>OPENCV官網
http://docs.opencv.org/3.1.0/index.html