直方图
何为直方图?没那么高大上,其实就是二维统计图。每个照片都是有像素点所组成,当然也是[0,255],直方图就是统计每个值所对应的像素点有几个。
直方图横坐标表示0-255这些像素点值;纵坐标表示对应像素点值的个数有多少个,例如:像素为55的像素点有多少个
cv2.calcHist(images,channels,mask,histSize,ranges)
cv2.calcHist([img],[0],None,[256],[0,256])
参数一:images:原图像格式为uint8或float32;当传入函数时应该用中括号括住,通常情况下都是输入的是灰度图
参数二:同样用中括号括起来,它会告诉函数我们图像的直方图;如果输入图像时灰度图它的值就是[0];如果时彩色图像传入的参数可以是[0][1][2],分别对应BGR
参数三:掩模图像,说白了就是取部分图像而已;统计整个图像的直方图时就把它设置为None;当然也可以通过掩模来统计图像的某部分的直方图
参数四:BIN的数目,也就是横坐标的总量程而已,一般都是256,也就是0-255这256个像素点值,也需要用中括号括起来
参数五:像素值的范围,一般设置为[0,256]
import cv2
import numpy as np
from matplotlib import pyplot as pltdef show_photo(name,picture):#图像显示函数cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()img = cv2.imread('E:\Jupyter_workspace\study\data/cat.png',0)#这里的参数0表示以灰度图进行读取
hist = cv2.calcHist([img],[0],None,[256],[0,256])#当然由于是灰度图通道数也只有一个即参数二[0];参数三None表示没有使用掩模直接输出整体图像的直方图;参数四[256]直方图的横坐标量程;参数五[0,256]像素值的范围
hist.shape#结果为:(256, 1) 其中256表示这个图像中有0-255这256个取值,1表示得到的直方图是二维的,即每个像素出现多少个plt.hist(img.ravel(),256)
plt.show()img = cv2.imread('E:\Jupyter_workspace\study\data/cat.png')#第二个参数不填表示原图输入,这个图像为彩色图也就是彩色图输入
color = ('b','g','r')
for i,col in enumerate(color):#枚举格式遍历每个颜色通道histr = cv2.calcHist([img],[i],None,[256],[0,256])plt.plot(histr,color = col)plt.xlim([0,256])
plt.show()
原图:
原图对应的灰度图的直方图:
原图的BGR直方图:
掩模mask
np.zeros(img.shape[:2],np.uint8)
上面的函数中有掩模的操作,接下来介绍一下掩模mask的定义的操作
import cv2
import numpy as np
from matplotlib import pyplot as pltdef show_photo(name,picture):#图像显示函数cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()#创建mast,掩模是由黑白两部分组成的,然后与原图重叠,掩模白色区域对应原图区域不变,黑色区域对应原图区域变黑
mask = np.zeros(img.shape[:2],np.uint8)#这里面的掩码实际上就是边缘黑中间白,此时的mask就和原图片大小相同,uint8表示无符号八位整数0-255之间
print(mask.shape)#结果为:(321, 287)
mask[50:200,50:200] = 255#要保存的东西是白色的,白色区域为要保存的地方,所以将选取地方置为255
show_photo('mask',mask)img = cv2.imread('E:\Jupyter_workspace\study\data/cat.png',0)#灰度图读取照片
print(img.shape)#结果为:(321, 287)
show_photo('img',img)masked_img = cv2.bitwise_and(img,img,mask=mask)#与操作,也就是有0则0,黑色为0所以说掩码黑色地区都为黑;参数一表示原图像,参数三表示掩模图像
show_photo('masked_img',masked_img)hist_full = cv2.calcHist([img],[0],None,[256],[0,256])#不带掩模的图像进行统计直方图
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256])#带掩模mask的时候统计图像的部分掩模区域的直方图plt.subplot(221),plt.imshow(img,'gray')
plt.subplot(222),plt.imshow(mask,'gray')
plt.subplot(223),plt.imshow(masked_img,'gray')
plt.subplot(224),plt.plot(hist_full),plt.plot(hist_mask)
plt.xlim([0,256])
plt.show()
掩模图像:
原图:
掩模操作后的图像:
上一:原图
上二:自定义的掩模
下一:掩模对应的原图部分
下二:蓝线对应原图的直方图,橙线对应掩模处理的部分原图的直方图
直方图均衡化
假设某图片部分像素值为:
255 | 128 | 200 | 50 |
50 | 200 | 255 | 50 |
255 | 200 | 128 | 128 |
200 | 200 | 255 | 50 |
下面表格中的函数映射中(255-0)表示设置的横轴的量程这里设置的是0-255
灰度值 | 像素个数 | 概率 | 累积概率 | 根据函数映射后灰度值 | 取整 |
---|---|---|---|---|---|
50 | 4 | 4/16 = 0.25 | 0.25 | 0.25*(255-0)=63.75 | 64 |
128 | 3 | 3/16 = 0.1875 | 0.25+0.1875=0.4375 | 0.4375*(255-0)=111.5625 | 112 |
200 | 5 | 5/16 = 0.3125 | 0.25+0.1875+0.3125=0.75 | 0.75*(255-0)=191.25 | 191 |
255 | 4 | 4/16 = 0.25 | 0.25+0.1875+0.3125+0.25=1 | 1*(255-0)=255 | 255 |
均衡化后的像素值为:
255 | 112 | 191 | 64 |
64 | 191 | 255 | 64 |
255 | 191 | 112 | 112 |
192 | 191 | 255 | 255 |
均衡化之后发现了这16个数相差的并不是特别大了
cv2.equalizeHist(img)
传入图像对象名称即可进行整体均衡化
import cv2
import numpy as np
from matplotlib import pyplot as pltdef show_photo(name,picture):#图像显示函数cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()img = cv2.imread('E:\Jupyter_workspace\study\data/people.jpg',0)
plt.hist(img.ravel(),256)#原图像的直方图
plt.show()equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256)#均衡化后的直方图
plt.show()res = np.hstack((img,equ))
show_photo('img_equ',res)#显示原图和均衡化后的图片
原图直方图:
均衡化后的直方图:
原图和均衡化后的图片对比:
均衡化后的图像脸上的细节变得更加模糊了,尴尬不???
这时候就需要自定义均衡化
自定义均衡化
直方图的均衡化也就是整体的均衡化,其他像素点值大的地方给平均给其他地方了导致一下细节会丢失
若将原图分成块进行均衡化,每块进行自己块的均衡化效果会比全局整体均衡化更好些
当然,若图像里面噪音太大,局部反而没有整体均衡化好,需要自己事先去衡量一下
cv2.createCLAHE(clipLimit = 2.0,tileGridSize = (8,8))
(8,8)表示分块均衡化中块的大小
import cv2
import numpy as np
from matplotlib import pyplot as pltdef show_photo(name,picture):#图像显示函数cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()img = cv2.imread('E:\Jupyter_workspace\study\data/people.jpg',0)
plt.hist(img.ravel(),256)#原图像的直方图equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256)#均衡化后的直方图clahe = cv2.createCLAHE(clipLimit = 2.0,tileGridSize = (8,8))#自定义均衡化,每8*8分成块,按块进行均衡化
res_clahe = clahe.apply(img)res = np.hstack((img,equ,res_clahe))
show_photo('img_equ_clahe',res)
原图-整体均衡化-自定义均衡化