图像的直方图处理
1.直方图绘制
直方图显示图像数据时会以左暗又亮的分布曲线形式呈现出来,而不是显示原图像数据。利用opencv-python中的库函数绘制彩色图像直方图的自定义函数如下
# 直方图绘制函数 def draw_my_hist(image): color = ('b', 'g', 'r') for i, color in enumerate(color): # calcHist()函数有5个参数: # image输入图像,传入时应该用中括号[]括起来 # channels::传入图像的通道,如果是灰度图像,值为0,如果是彩色图像(有3个通道),那么值为0,1,2,中选择一个,对应着BGR各个通道。这个值也得用[]传入。 # mask:掩膜图像。如果统计整幅图,那么为none。主要是如果要统计部分图的直方图,就得构造相应的炎掩膜来计算。 # histSize:灰度级的个数,需要中括号,比如[256] hist = cv.calcHist([image], [i], None, [256], [0, 256]) # 计算直方图 plt.plot(hist, color) plt.xlim([0, 256])
不同图像有相差很大的直方图
2.整体直方图均衡
直方图均衡是通过调整图像的直方图来改变图像的对比度。用这种方法亮度可以更好地在直方图上分布,从而就可以增强局部对比度而对整体对比度没有太大影响。直方图均衡函数:
# 彩色图像直方图均衡函数 # equalizeHist()函数可以直接求解灰度图的直方图均衡结果,所以如果是灰度图像则不需要进行通道分解 def get_equalizeHist_image_color(image): (b, g, r) = cv.split(image) # 通道分解 bH = cv.equalizeHist(b) gH = cv.equalizeHist(g) rH = cv.equalizeHist(r) result = cv.merge((bH, gH, rH), ) # 通道合成 return result
图像均衡效果如下
3.局部直方图均衡
上述的直方图均衡化可以达到一种全局意义上的均衡化,但是有的时候这种操作并不是很好,会把某些不该调整的部分给调整了。Opencv中还有一种直方图均衡化方法,它是一种在图像局部进行均衡化的方法,也就是是说把整个图像分成许多小块(比如按7*7作为一个小块),那么对每个小块进行均衡化。这种方法主要对于图像直方图不是那么单一的(比如存在多峰情况)图像比较实用。Opencv中的局部均衡化实例如下:
# createCLAHE()函数中clipLimit参数为颜色对比度的阈值,titleGridSize参数为进行像素均衡化的网格大小 def get_part_equalizeHist(image): (image_b, image_r, image_g) = cv2.split(image) # 创建均衡化对象 clahe = cv2.createCLAHE(clipLimit=2, tileGridSize=(10, 10)) # 分别对三个通道进行局部均衡化,当然如果是灰度图则不需要通道分解操作 result_b = clahe.apply(image_b) result_r = clahe.apply(image_r) result_g = clahe.apply(image_g) result = cv2.merge((result_b, result_r, result_g)) return result
局部直方图均衡效果如下
4.直方图匹配
虽然直方图均衡在一定程度上能够增强图像的对比度,但是有的时候和我们理想的效果并不一致,如果我们能够规定变换后直方图的形状,进行直方图匹配,很多情况下要比直方图均衡中的常量直方图效果好。查了很多资料都表示Opencv-python中没有免费的直方图匹配的函数可以使用,所以这里借鉴博客上的思路自己编写了一个求解直方图匹配的函数
# 求累积直方图 def my_get_add_hist(original_hist): acc_hist = np.zeros([256, 1], np.float32) next_number = 0. for i in range(256): acc_hist[i, 0] = next_number + original_hist[i, 0] next_number = acc_hist[i, 0] acc_hist /= next_number return acc_hist def my_hist_match_gray(original_image, target_image): # 求原始图像直方图 original_hist = cv.calcHist([original_image], [0], None, [256], [0.0, 255.]) # 求目标图像直方图 target_hist = cv.calcHist([target_image], [0], None, [256], [0.0, 255.]) # 求原始图像累计直方图 original_acc_hist = my_get_add_hist(original_hist) # 求目标图像累计直方图 target_acc_hist = my_get_add_hist(target_hist) # 计算原始图像和目标图像在所有点概率密度的差值,并取绝对值 my_subtract = abs(np.tile(target_acc_hist.reshape((256, 1)), (1, 256)) - original_acc_hist.reshape(1, 256)) # 得到插值最小处的索引 my_index = np.argmin(my_subtract, axis=0) # 将索引转化为uint8,对于灰度图像cv2.LUT的table必须是uint8类型 my_index = my_index.astype(np.uint8) # 将原图像进行映射 result = cv.LUT(original_image, my_index) # 返回结果 return result def my_hist_match_RGB(original_image, target_image): # 将RGB图像进行分解 (original_image_b, original_image_r, original_image_g) = cv.split(original_image) (target_image_b, target_image_r, target_image_g) = cv.split(target_image) # 对每个图像进行灰度图像匹配 result_b = my_hist_match_gray(original_image_b, target_image_b) result_r = my_hist_match_gray(original_image_r, target_image_r) result_g = my_hist_match_gray(original_image_g, target_image_g) # 将所有通道匹配结果合并 result = cv.merge((result_b, result_r, result_g)) return result
图像进行直方图匹配后的效果如下