一、获取直方图
def hist_cal(img):
img = np.array(img)
heigh,width=img.shape
histogram=np.zeros(256)
for i in range(heigh):
for j in range(width):
gray = img[i][j]
histogram[gray] += 1#出现某个像素值则加1,最终统计出各个像素值的频数
return histogram
二、累积分布函数
def EqualizeHistogram(img,histogram,LIMIT = 200):
img = np.array(img)
heigh, width = img.shape
pixcount=heigh * width
dataOfSrc = img
dataOfDst=np.zeros([heigh, width])
steal = 0
for k in range(256):
if histogram[k]>LIMIT:
histogram[k]=LIMIT
steal+= histogram[k]-LIMIT
bonus=math.floor(steal/256)
for l in range(256):
histogram[l]+=bonus
pro_den=histogram/pixcount
sum=0
LUT=np.zeros(256)
for i in range(256):
sum+=pro_den[i]
LUT[i]=sum * 255
for m in range(heigh):
for n in range(width):
dataOfDst[m][n]=LUT[dataOfSrc[m][n]]
Equalize_img=dataOfDst.reshape([heigh, width])
Equalize_img=Equalize_img.astype(np.uint8)
return LUT,Equalize_img
#返回两个值,前者用于AHE、CLAHE的插值,后者用于直方图均衡化直接使用
三、双线性插值
def CLAHE(block_number,img_gray):
heigh, width = img_gray.shape
img_ahe_liner = np.zeros([heigh, width])
block_height = heigh // block_number
block_width = width // block_number
all_LUT = np.zeros([block_number, block_number, 256]) # 存储累积分布函数
# 求得各个块的累积分布函数
for i in range(block_number):
for j in range(block_number):
block = img_gray[i * block_height:(i + 1) * block_height, j * block_width:(j + 1) * block_width]
hist_block = hist_cal(block)
all_LUT[i, j], Equalize_hist = EqualizeHistogram(block, hist_block)
for m in range(heigh):
for n in range(width):
# four coners
# 在角上的像素点为累积分布函数得到的值
if m <= block_height / 2 and n <= block_width / 2:
block_m = 0
block_n = 0
img_ahe_liner[m, n] = all_LUT[block_m, block_n][img_gray[m, n]]
elif m <= block_height / 2 and n >= width - block_width / 2:
block_m = 0
block_n = block_number - 1
img_ahe_liner[m, n] = all_LUT[block_m, block_n][img_gray[m, n]]
elif n <= block_width / 2 and m >= heigh - block_height / 2:
block_m = block_number - 1
block_n = 0
img_ahe_liner[m, n] = all_LUT[block_m, block_n][img_gray[m, n]]
elif m >= heigh - block_height / 2 and n >= width - block_width / 2:
block_m = block_number - 1
block_n = block_number - 1
img_ahe_liner[m, n] = all_LUT[block_m, block_n][img_gray[m, n]]
# four edges except coners
# 四个边采取线性插值
elif n <= block_width / 2:
block_m_1 = math.floor((m - block_height / 2) / block_height)
block_n_1 = 0
block_m_2 = block_m_1 + 1
block_n_2 = block_n_1
u = np.float((m - (block_m_1 * block_height + block_height / 2)) / (block_height))
v = 1 - u
img_ahe_liner[m, n] = v * all_LUT[block_m_1, block_n_1][img_gray[m, n]] + \
u * all_LUT[block_m_2, block_n_2][img_gray[m, n]]
elif m <= block_height / 2:
block_m_1 = 0
block_n_1 = math.floor((n - block_width / 2) / block_width)
block_m_2 = block_m_1
block_n_2 = block_n_1 + 1
u = np.float((n - (block_n_1 * block_width + block_width / 2)) / (block_width))
v = 1 - u
img_ahe_liner[m, n] = v * all_LUT[block_m_1, block_n_1][img_gray[m, n]] + \
u * all_LUT[block_m_2, block_n_2][img_gray[m, n]]
elif m >= heigh - block_height / 2:
block_m_1 = block_number - 1
block_n_1 = math.floor((n - block_width / 2) / block_width)
block_m_2 = block_m_1
block_n_2 = block_n_1 + 1
u = np.float((n - (block_n_1 * block_width + block_width / 2)) / (block_width))
v = 1 - u
img_ahe_liner[m, n] = v * all_LUT[block_m_1, block_n_1][img_gray[m, n]] + \
u * all_LUT[block_m_2, block_n_2][img_gray[m, n]]
elif n >= width - block_width / 2:
block_m_1 = math.floor((m - block_height / 2) / block_height)
block_n_1 = block_number - 1
block_m_2 = block_m_1 + 1
block_n_2 = block_n_1
u = np.float((m - (block_m_1 * block_height + block_height / 2)) / (block_height))
v = 1 - u
img_ahe_liner[m, n] = v * all_LUT[block_m_1, block_n_1][img_gray[m, n]] + \
u * all_LUT[block_m_2, block_n_2][img_gray[m, n]]
# 中间区域采取双线性插值
else:
block_m_1 = math.floor((m - block_height / 2) / block_height)
block_n_1 = math.floor((n - block_width / 2) / block_width)
block_m_2 = block_m_1 + 1
block_n_2 = block_n_1 + 1
u = np.float((m - (block_m_1 * block_height + block_height / 2)) / (block_height))
v = np.float((n - (block_n_1 * block_width + block_width / 2)) / (block_width))
img_ahe_liner[m, n] = (1 - u) * (1 - v) * all_LUT[block_m_1, block_n_1][img_gray[m, n]] + \
v * (1 - u) * all_LUT[block_m_1, block_n_2][img_gray[m, n]] + \
u * (1 - v) * all_LUT[block_m_2, block_n_1][img_gray[m, n]] + \
u * v * all_LUT[block_m_2, block_n_2][img_gray[m, n]]
return img_ahe_liner.astype(np.uint8)
如果不采用插值,最终的结果将会有很明显边界。
四、主函数
im=cv2.imread('040a07b4c00a844_size196_w640_h480.jpg')
img_LAB = cv2.cvtColor(im,cv2.COLOR_BGR2LAB)
lab_planes = cv2.split(img_LAB)#转化为LAB颜色域,对light分量进行CLAHE,可以得到彩色图,如果转化为灰度图,则是对灰度颜色域的CLAHE
lab_planes[0]=CLAHE(8,lab_planes[0])
lab = cv2.merge(lab_planes)
bgr = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
cv2.namedWindow("img_gray",0)
cv2.imshow("img_gray",img_LAB)
cv2.resizeWindow("img_gray",640,480)
cv2.namedWindow("Equalize_img",0)
cv2.resizeWindow("Equalize_img",640,480)
cv2.imshow("Equalize_img",bgr)
cv2.waitKey()
五、使用的库
import numpy as np
import cv2
import math
六、与官方函数的对比
使用CV2中的函数:
import cv2
mri_img = cv2.imread('040a07b4c00a844_size196_w640_h480.jpg')
lab = cv2.cvtColor(mri_img, cv2.COLOR_BGR2LAB)
lab_planes = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=10,tileGridSize=(8,8))
lab_planes[0] = clahe.apply(lab_planes[0])
lab = cv2.merge(lab_planes)
bgr = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
cv2.namedWindow("p",0)
cv2.resizeWindow("p",640,480)
cv2.imshow('p',bgr)
cv2.waitKey(0)
图1为原图,图2为cv2库得到,图三为自己代码实现:
图1 原图
图2 cv2库得到
图三 自己代码实现
官方库运行速度快,自己代码写的for循环太多,耗时较长,采取矩阵运算的方式应该会快很多。