1 - 直方圖處理
直方圖是多種空間域處理技術的基礎,可以用於圖像增強。
2 - 直方圖均衡
考慮連續灰度值,並用變量r表示待處理圖像的灰度。設r的取值區間爲[0,L-1],且r = 0表示黑色,r = L - 1表示白色。令和分別表示隨機變量r和s的概率密度函數,概率密度函數歸一化後可以畫出圖像的直方圖
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
image = cv.imread('images/test_1.jpg', 0)
hist = cv.calcHist([image],[0],None,[256],[0,255])
plt.plot(hist)
plt.show()
可以看到較暗的圖片灰度直方圖分佈的非常不均衡,那麼我們構造一個出變換
使得對應的輸出圖像概率密度函數變成均勻的
對於離散值,我們處理其概率(直方圖值)與求和來替代處理概率密度函數與積分。
一副數字圖像中灰度級出現的概率近似爲:
其中,MN是圖像中像素的綜述,是灰度爲像素的個數。
則離散形式的變化公式爲:
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
image = cv.imread('images/test_1.jpg', 0)
image2 = cv.equalizeHist(image)
cv.imshow("image_2",image2)
hist = cv.calcHist([image2],[0],None,[256],[0,255])
plt.plot(hist)
plt.show()
cv.waitKey(0)
可以看到直方圖的分佈更加均勻了,圖像的對比度和相關細節顯示的也更加明顯了。
3 - 直方圖匹配
直方圖均衡能自動地確定變換函數,當需要自動增強時,這是一種好方法,因爲由於這種技術得到的結果可以預知,並且這種方法實現也很簡單,但是對於某些應用,採用均勻直方圖的基本增強並不是最好的方法,有時我們希望處理後的圖像具有規定的直方圖形狀可能更有用。這種採用處理後有特殊直方圖的方法稱爲直方圖匹配或直方圖規定劃。
例:直方圖規定劃
設一副圖片的灰度概率密度函數(PDF)爲
我們想要尋找一個變換函數,使得產生的圖像的灰度PDF是
求出原圖像的直方圖均衡變換:
求出規定圖像的直方圖均衡變換:
最後使其相等
利用反變換得到
畫出函數的關係圖就很好理解了
在離散形式下
對於一個q值,有
再利用反變換,有
那麼我們總結直方圖規定劃過程如下:
- 計算給定圖像的直方圖,並用它尋找直方圖均衡變換,把四捨五入爲範圍[0,L-1]內的整數。
- 計算變換函數G的所有值,將G的值儲存在一個表中
- 對於每個值,使用儲存的G值尋找相應的值,使最接近。
- 首先對輸入圖像進行均衡,然後使用步驟3找到的映射把該圖像中的每個均衡後的像素值映射爲直方圖規定劃後的圖像相應值
具體python實現:
將一張較爲黑暗的原始圖像
與一張拍攝光線較好的匹配圖像
進行直方圖匹配,目的是爲了把原始圖像的直方圖匹配成匹配圖像的直方圖分佈,使得原始圖像能夠呈現出匹配圖像的光線效果。
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
import matplotlib
matplotlib.rcParams['font.sans-serif']=['SimHei'] # 用黑體顯示中文
#傳入的直方圖要求是個字典,每個灰度對應着概率
def drawHist(hist,name):
keys = hist.keys()
values = hist.values()
x_size = len(hist)-1#x軸長度,也就是灰度級別
axis_params = []
axis_params.append(0)
axis_params.append(x_size)
#plt.figure()
if name != None:
plt.title(name)
plt.bar(tuple(keys),tuple(values))#繪製直方圖
#plt.show()
#將灰度數組映射爲直方圖字典,nums表示灰度的數量級
def arrayToHist(grayArray,nums):
if(len(grayArray.shape) != 2):
print("length error")
return None
w,h = grayArray.shape
hist = {}
for k in range(nums):
hist[k] = 0
for i in range(w):
for j in range(h):
if(hist.get(grayArray[i][j]) is None):
hist[grayArray[i][j]] = 0
hist[grayArray[i][j]] += 1
#normalize
n = w*h
for key in hist.keys():
hist[key] = float(hist[key])/n
return hist
#計算累計直方圖計算出新的均衡化的圖片,nums爲灰度數,256
def equalization(grayArray,h_s,nums):
#計算累計直方圖
tmp = 0.0
h_acc = h_s.copy()
for i in range(256):
tmp += h_s[i]
h_acc[i] = tmp
if(len(grayArray.shape) != 2):
print("length error")
return None
w,h = grayArray.shape
des = np.zeros((w,h),dtype = np.uint8)
for i in range(w):
for j in range(h):
des[i][j] = int((nums - 1)* h_acc[grayArray[i][j] ] +0.5)
return des
def histMatch(grayArray,h_d):
#計算累計直方圖
tmp = 0.0
h_acc = h_d.copy()
for i in range(256):
tmp += h_d[i]
h_acc[i] = tmp
h1 = arrayToHist(grayArray,256)
tmp = 0.0
h1_acc = h1.copy()
for i in range(256):
tmp += h1[i]
h1_acc[i] = tmp
#計算映射
M = np.zeros(256)
for i in range(256):
idx = 0
minv = 1
for j in h_acc:
if (np.fabs(h_acc[j] - h1_acc[i]) < minv):
minv = np.fabs(h_acc[j] - h1_acc[i])
idx = int(j)
M[i] = idx
des = M[grayArray]
return des
imdir = "images/test_1.jpg"#原始圖片的路徑
imdir_match = "images/persian_cat.jpg"
#直方圖匹配
#打開文件並灰度化
im_s = Image.open(imdir).convert("L")
im_s = np.array(im_s)
print(np.shape(im_s))
#打開文件並灰度化
im_match = Image.open(imdir_match).convert("L")
im_match = np.array(im_match)
print(np.shape(im_match))
#開始繪圖
plt.figure()
#原始圖和直方圖
plt.subplot(2,3,1)
plt.title("原始圖片")
plt.imshow(im_s,cmap='gray')
plt.subplot(2,3,4)
hist_s = arrayToHist(im_s,256)
drawHist(hist_s,"原始直方圖")
#match圖和其直方圖
plt.subplot(2,3,2)
plt.title("match圖片")
plt.imshow(im_match,cmap='gray')
plt.subplot(2,3,5)
hist_m = arrayToHist(im_match,256)
drawHist(hist_m,"match直方圖")
#match後的圖片及其直方圖
im_d = histMatch(im_s,hist_m)#將目標圖的直方圖用於給原圖做均衡,也就實現了match
plt.subplot(2,3,3)
plt.title("match後的圖片")
plt.imshow(im_d,cmap='gray')
plt.subplot(2,3,6)
hist_d = arrayToHist(im_d,256)
drawHist(hist_d,"match後的直方圖")
plt.show()
實驗結果: