opencv-python 图像直方图和直方图均衡化
统计学中,直方图是一种对数据分布情况的图形表示,并且将统计数据组织到一系列定义好bin当中(bin是从数据中计算出的特征统计量,可以看作“直条”或“组距”)。
图像直方图是用来表示数字图像中亮度分布的直方图,统计的是图像中每个像素亮度值的个数。横坐标表示图像中各个像素点的灰度级,纵坐标表示该灰度级的像素数。
直方图相关的术语:
dims:需要统计的特征数目;
bins:每个特征空间子区段的数目;
range:需要统计特征的取值范围。
1 显示图像对应的直方图
opencv中提供了归一化直方图 纵坐标表示该灰度级的概率 calcHist(images,channels,mask,histSize,ranges)
ranges:像素值范围,[0,255];
channels:需要中括号,输入时灰度图像,值是[0],彩色图像BGR可以是[0],[1],[2];
mask默认None,统计图像某一部分时用;
histSize:bins数量,[256]。
import matplotlib.pyplot as plt import cv2 img = cv2.imread('./lena.jpg') img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) hist = cv2.calcHist([img_gray],[0],None,[256],[0,255]) #统计直方图数据 plt.figure(figsize=(8,6),dpi=100) plt.subplot(121),plt.imshow(img_gray,cmap = 'gray'),plt.axis('off'),plt.title('gray_img') plt.subplot(122),plt.plot(hist,label='gray'),plt.title('gray_hist') plt.show()
1)用opencv在窗口上把图像直方图绘制出来
import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('./lena.jpg') img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) hist = cv2.calcHist([img_gray],[0],None,[256],[0,255]) #统计直方图数据 返回值是256的灰度级的像素数量 hist_img = np.zeros([256,256],np.uint8) #创建纯白色的图像用于绘制直方图 hist_img[:] = 255 maxval = max(hist) #获取直方图统计数据的最大值 #print(maxval) for i in range(256): norm_val = int(hist[i]*256/maxval) #把直方图的像素统计数值归一化到[0,256]区间 cv2.line(hist_img,(i,256),(i,256-norm_val),[0,0,0]) #用黑色线绘制每个像素级的数量,图像的坐标是左上角[0,0] cv2.imshow('hist_img',hist_img) cv2.imshow('img_gray',img_gray) cv2.waitKey(0) cv2.destroyAllWindows()
2)用matplotlib来统计直方图
import cv2 import numpy as np import matplotlib.pyplot as plt %matplotlib inline img = cv2.imread('./lena.jpg') img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) plt.figure(figsize=(10,6),dpi=100) plt.subplot(121),plt.imshow(img_gray,cmap = 'gray'),plt.axis('off'),plt.title('gray_img') plt.subplot(122),plt.hist(img_gray.reshape(-1),bins=256,range=[0,255]) #img_gray.reshape(-1) 把灰度图像矩阵变为一维的,也可以用img.ravel(); plt.show()
3)统计图像rgb三通道的直方图
import cv2 #统计图像三通道的直方图 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('./lena.jpg') histB = cv2.calcHist([img],[0],None,[256],[0,255]) #统计B通道直方图数据 histG = cv2.calcHist([img],[1],None,[256],[0,255]) #统计G通道直方图数据 histR = cv2.calcHist([img],[2],None,[256],[0,255]) #统计R通道直方图数据 plt.figure(figsize=(10,6),dpi=100) plt.subplot(121),plt.imshow(img[:,:,::-1]),plt.axis('off'),plt.title('gray_img')
plt.subplot(122) plt.plot(histB,color='b',label='blue') # color指定绘制颜色,lable时标签,用legend显示在左上角 plt.plot(histG,color='g',label='green') plt.plot(histR,color='r',label='red') plt.legend() #显示label plt.show()
4)使用掩膜统计图像中感兴趣的部分的直方图
掩膜:通常使用二维矩阵来制作掩膜,掩膜是由0和1组成的二进制图像,利用掩膜对图像进行处理,其中1的区域被处理,0值区域被屏蔽,不会处理。
掩膜作用:提取感兴趣区域(与操作),屏蔽作用,结构特征提取。
import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('./lena.jpg') img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) mask = np.zeros(img_gray.shape,np.uint8) #制作掩膜 mask[100:200,110:180] = 255 mask_gray = cv2.bitwise_and(img_gray,mask) #图像与mask做与运算,获取图像的roi部分 cv2.imshow('img',img_gray) cv2.imshow('mask',mask) cv2.imshow('mask_gray',mask_gray) hist = cv2.calcHist([img_gray],[0],None,[256],[0,255]) #统计直方图数据 hist_mask = cv2.calcHist([img_gray],[0],mask,[256],[0,255]) #统计掩膜的直方图数据 plt.plot(hist,label='gray_img') plt.plot(hist_mask,label='mask_img') plt.legend() #显示标签
plt.grid() #显示网格 plt.show() cv2.waitKey(0) cv2.destroyAllWindows()
2 直方图均衡化
直方图均衡化是把原始图像的灰度直方图从比较集中的某个灰度区间变成在更广泛灰度范围内的分布。直方图均衡化是对图像进行非线性拉伸,重新分配图像像素值,使得一定范围内的像素数量大致相同。可以提高图像的整体对比度,在曝光过度或曝光不足的图像中可以更好的突出细节。
直方图均衡化流程大概是:对图像直方图做归一化处理,然后计算累计直方图,再进行灰度级区间转换(每个像素级对应的累计概率乘以总的像素级),最后统计新的灰度级的像素概率(累计直方图中,概率相近的原始值,会被处理为相同的值)。
opencv中直方图均衡化的函数时:equalizeHist(img)
import cv2 import numpy as np import matplotlib.pyplot as plt %matplotlib inline img = cv2.imread('./lena2.jpg') img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) img_black = cv2.subtract(img_gray,50) # 图片变暗 img_light = cv2.add(img_gray,50) #图片加亮度 equal_img = cv2.equalizeHist(img_gray) #直方图均衡化 #cv2.imshow('img_gray',img_gray) #cv2.imshow('img_black',img_black) #cv2.imshow('img_light',img_light) #cv2.imshow('img_equal',equal_img) hist_img = cv2.calcHist([img_gray],[0],None,[256],[0,255]) #统计灰度图像的直方图 hist_equal = cv2.calcHist([equal_img],[0],None,[256],[0,255]) #统计均衡化后的直方图 plt.figure(figsize=(10,8),dpi=100) plt.subplot(221),plt.imshow(img_gray,cmap='gray'),plt.title('img') plt.subplot(222),plt.plot(hist_img,color='r'),plt.title('hist_img') plt.subplot(223),plt.imshow(equal_img,cmap='gray'),plt.title('equal_img') plt.subplot(224),plt.plot(hist_equal,color='g'),plt.title('hist_equal') plt.show() cv2.waitKey(0) cv2.destroyAllWindows()
全局直方图均衡话后比较亮或暗的地方细节会丢失,可以用自适应直方图均衡化解决。首先,图像会分成很多小块,小块称为tiles(opencv中是8*8),然后对每个小块进行直方图均衡化。但是噪声会被放大,通常用对比度限制来避免这种情况。最后,为了去除每一个小块之间的边界,再用双线性插值进行拼接。
opencv中提供了自适应直方图均衡化函数:createCLAHE(clipLimit,tileGridSize)
clipLimit:对比度限制,默认40;
tileGridSize:分块的大小,默认8*8 。
import cv2 import numpy as np img = cv2.imread('./faces.jpg',0) #读取转换的灰度图像 equal_img = cv2.equalizeHist(img) #全局直方图均衡化 template = cv2.createCLAHE(10,(8,8)) #自适应直方图均衡化 img_cla = template.apply(img) cv2.imshow('img',img) cv2.imshow('equal_img',equal_img) cv2.imshow('img_cla',img_cla) cv2.waitKey(0) cv2.destroyAllWindows()