圖像直方圖
圖像直方圖 是 圖像的 一種統計特征,可以圖像質量檢測,也可以用於 圖像相似度檢測;
圖像直方圖的形態 可以反應圖像的 質量,特別是 對比度,大致如下圖
通過 直方圖修整 可以提升圖像質量,直方圖修整 可以分為兩類,一是 直方圖均衡化,二是 直方圖規定化,本文主要講 均衡化
直方圖均衡化
直方圖均衡化 是 圖像增強 的一種重要方法,主要是提高圖像 質量、清晰度、對比度;
均衡化的知識和方法如下:
直方圖均衡化與對比度增強
直方圖均衡化(HE)
自適應直方圖均衡化(AHE)
限制對比度自適應直方圖均衡化(CLAHE)
自適應局部區域伸展(Local Region Stretch)直方圖均衡化
算法目標
直方圖趨勢集中 表示 圖像像素級大小相近,且像素級過於集中,對比度低;
直方圖均衡化 是 將原圖像通過某種變換,使得圖像直方圖為 近似均勻分布 的方法;
理想效果
這張更實際:原始圖像像素比較集中,較亮的圖像把所有像素限制在較高區間,但好的圖像會有來自所有像素級的像素;
直方圖均衡化 會使 像素級 跨度 更大,增加 像素級 的差別;像素權重分布更分散,增加圖像整體的飽和度、對比度; 【像素分布集中時,看起來就是 一片白,或者一片黑 之類】
直觀作用是 把像素權重大的灰度級進行 分割-拉伸-擴展,把像素權重小的灰度級進行 合並-壓縮,進而使得 不同像素權重大致相同;
最終實現提高 圖像對比度 的目的;
數學原理
專門做圖像的可以看看,我這里就只上一張圖吧
算法步驟
直接舉個例子吧,具體步驟說明見代碼
補充說明:
1. nk 第二列,紅色方框內 像素較多,保持不變,(再高就要分割了),綠色方框和黃色方框內 像素較低,分別進行合並
2. 新的像素級 為 0-n,把原像素權重 coumsum 后,也是 0-cumsum.max,兩者點乘,就得到了新的像素值,妙啊
手動實現
下面 用 numpy 實現 基本的直方圖均衡化
##### numpy 實現直方圖均衡 def hist_cdf(img): ### 畫像素直方圖,以及 直方圖的 帕累托圖,帕累托圖可反應 各個像素區間像素點 的變化 是否 均勻 # 帕累托圖 hist, bins = np.histogram(img.flatten(), 256, [0, 256]) cdf = hist.cumsum() cdf_normalized = cdf * float(hist.max()) / cdf.max() plt.plot(cdf_normalized, color='b') # 像素直方圖 plt.hist(img.flatten(), 256, [0, 256], color='r') plt.xlim([0, 256]) plt.legend(('cdf', 'histogram'), loc='upper left') plt.show() if 1: img = cv.imread('imgs/jh.png', 0) # img = cv.imread('imgs/image13.jpg', 0) hist_cdf(img) ##### 為了思路更清晰,我把代碼 拆開,方便描述 ## 實現代碼1 # 1.確定圖像灰度級,這里是 0-255,並計算權重 hist, bins = np.histogram(img.flatten(), 256, [0, 256]) # 2.累計權重 cdf = hist.cumsum() # len(cdf) = 256 cdf_m = np.ma.masked_equal(cdf, 0) # 掩蓋等於 0 的元素 # 3.權重占比 hist_zhanbi = (cdf_m - cdf_m.min()) / (cdf_m.max() - cdf_m.min()) # cdf_m.max()-cdf_m.min()=sum(hist) # 4.計算新的像素級 cdf_m = 255 * hist_zhanbi # 這里新的像素級還是 0-255 cdf = np.ma.filled(cdf_m, 0).astype('uint8') # 5.在圖像上進行新老像素的映射 # cdf:新的像素級,取值 0-255,長度 256, # 原來的 hist 下標是 0-255,其實就下標是每個老的像素級,原來 hist 的值 變成了新的像素值后,就是 cdf, # 也就是說 cdf 的下標 和 值的對應 就是 新老像素的對應 img2 = cdf[img] hist_cdf(img2) plt.subplot(211); plt.imshow(img) plt.subplot(212); plt.imshow(img2) plt.show() ## 實現代碼2 img = cv.imread('imgs/jh.png', 0) # 變換前的直方圖 plt.hist(img.flatten(), 256, [0, 256], color='r') plt.xlim([0, 256]) plt.title("before Histogram Equalization") plt.show() # 累積分布函數計算 hist, bins = np.histogram(img.flatten(), 256, [0, 256]) cdf = hist.cumsum() pixel_value = (cdf * 255.0 / cdf.max()).astype(np.uint8) # 變換后的像素值 img_conv = np.interp(img.flatten(), bins[:-1], pixel_value) # 將原始圖像的像素與變換后的圖像一一對應 img_conv = img_conv.reshape(img.shape) # 均衡后的直方圖 plt.hist(img_conv.flatten(), 256, [0, 256], color='r') plt.xlim([0, 256]) plt.title("after Histogram Equalization") plt.show()
效果圖
小結
意義:
1. 提高圖像質量、清晰度、對比度
2. 不管圖像是 過亮 還是 過暗,經過直方圖均衡后,其 亮度大致是相同的,也就是它可以使所有圖像具有相同的光照條件;
// 這在很多情況下都很有用。例如,在人臉識別中,在對人臉數據進行訓練之前,對人臉圖像進行直方圖均衡化處理,使其具有相同的光照條件。
優勢:不需要額外參數,整個過程是自動的;
缺點:拉伸后有些灰度級可能不被映射到,造成圖像觀感上的顆粒感;
opencv 用法
opencv 直方圖均衡化 只能 處理 單通道圖像
img = cv.imread('imgs/jh.png', 0) # 單通道 equ = cv.equalizeHist(img) print(equ.shape) res = np.hstack((img, equ)) # stacking images side-by-side cv.imshow('res', res) cv.waitKey(0) img = cv.imread('imgs/jh.png') b, g, r = cv.split(img) equ = cv.equalizeHist(b) # 單通道 print(equ.shape) res = np.hstack((b, equ)) # stacking images side-by-side cv.imshow('res2', res) cv.waitKey(0)
彩色圖像處理
1.分別對 R G B 進行 均衡,然后在合並,但這樣做 可能導致 圖像色彩失真
2.把 BGR 轉換成 HSV 色彩空間,然后對 V 通道進行 均衡,這樣可 保證圖像色彩不失真 【HSV 分別是 色調、飽和度、亮度】
img = cv.imread('imgs/jh.png') img2 = cv.cvtColor(img, code=cv.COLOR_BGR2HSV) print(img2.shape) # (201, 214, 3) 色調 飽和度 亮度 s, b, l = cv.split(img2) cv.imshow('hsvs', s) cv.imshow('hsvb', b) cv.imshow('hsvl', l) cv.waitKey(0) # 對 V 通道進行均衡 l_repair = cv.equalizeHist(l) res = np.hstack((l, l_repair)) cv.imshow('hsv', res) cv.waitKey(0)
限制對比度 自適應 直方圖均衡
直方圖均衡化考慮的是 圖像的整體對比度。在許多情況下,這不是一個好主意。例如下圖
直方圖均衡后,背景對比度確實得到了改善。但是圖像中雕像的臉。由於亮度過高,我們在那里丟失了大多數信息。
這是因為它的直方圖不像我們在前面的案例中所看到的那樣局限於特定區域。
因此,為了解決這個問題,使用了**自適應直方圖均衡**。
在這種情況下,圖像被分成稱為“tiles”的小塊(在OpenCV中,tileSize默認為8x8)。
然后,像往常一樣對這些塊中的每一個進行直方圖均衡。因此,在較小的區域中,直方圖將限制在一個較小的區域中(除非存在噪聲)。
如果有噪音,它將被放大。
為了避免這種情況,應用了對比度限制。如果任何直方圖bin超出指定的對比度限制(在OpenCV中默認為40),
則在應用直方圖均衡之前,將這些像素裁剪並均勻地分布到其他bin。均衡后,要消除圖塊邊界中的偽影,請應用雙線性插值。
opencv 用法
img = cv.imread('imgs/hist2.png', 0) plt.hist(img.flatten(), 256, (0, 256)) plt.show() ## 直方圖均衡化 img3 = cv.equalizeHist(img) print(img3.shape) cv.imshow('res', img3) cv.waitKey(0) ## 限制對比度 的 自適應 直方圖均衡 img4 = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) img4 = img4.apply(img) print(img4.shape) img5 = np.hstack([img3, img4]) cv.imshow('res', img5) cv.waitKey(0)
對比圖
參考資料:
https://zhuanlan.zhihu.com/p/54771264 直方圖均衡化原理 簡單直接
https://zhuanlan.zhihu.com/p/312220402 簡單看看
https://zhuanlan.zhihu.com/p/172049969 理論很詳細、很完整
https://zhuanlan.zhihu.com/p/32857009
https://zhuanlan.zhihu.com/p/382430357
https://zhuanlan.zhihu.com/p/409574671
https://zhuanlan.zhihu.com/p/82606523
https://zhuanlan.zhihu.com/p/44918476 多種 直方圖均衡化算法的 原理,
https://blog.csdn.net/dugudaibo/article/details/84450615 opencv-python 繪制圖像直方圖及直方圖均衡化