OpenCV-Python 笔记(五)直方图

一、直方图:查找、绘制和分析

在这里插入图片描述
直方图可以反映一张图片中,每个范围内像素值的多少。比如上图中中间调的像素比较集中,也就是说,(我们不看图片都知道)这张图片的像素,多数集中在这个值左右。在PS,LR等等修图软件里面,提供直方图,可以让创作者清楚地知道过曝面积和死黑面积大概有多少。同样在相机里,也有直方图,优秀的摄影师,可以根据直方图来调节相机参数,使得照片曝光处于最佳状态哈哈哈。(广告位留给自己:有兴趣的朋友,欢迎到我的pott:Mr_Zhou,ID:2100190切磋交流,嘻嘻)

寻找直方图

OpenCV:cv.calcHist(images,channels,mask,histSize,ranges [,hist [,accumulate]])

img=cv.imread("gh.jpg")
hist=cv.calcHist([img],[0],None,[256],[0,256])

numpy:

hist,bins = np.histogram(img.ravel(),256,[0,256])
hist = np.bincount(img.ravel(),minlength = 256)#比上面一条快10倍左右

注意: OpenCV函数比np.histogram()快大约40倍。因此,尽可能使用OpenCV函数

绘制直方图

**matplotlab:**使用matplotlib.pyplot.hist()

img=cv.imread("gh.jpg",0)
plt.subplot(122),plt.hist(img.ravel(),256,[0,256]),plt.title("hist")
plt.subplot(121),plt.imshow(img),plt.title("Original img")
plt.gray()
plt.show()

在这里插入图片描述
彩色图片,可以使用matplotlib的法线图,比较容易区分。(实际上用的是OpenCV的方法找到的直方图。)

img=cv.imread("gh.jpg")
color=("b","g","r")
plt.subplot(121)
for i,col in enumerate(color):
    histr=cv.calcHist([img],[i],None,[256],[0,256])
    plt.plot(histr,color=col)
    plt.xlim([0,256])
plt.title("color_hist")
plt.subplot(122),plt.imshow(img),plt.title("Original")
plt.show()

在这里插入图片描述
OpenCV:

img=cv.imread("gh.jpg",0)
hist=cv.calcHist([img],[0],None,[256],[0,256])
plt.subplot(121),plt.imshow(img),plt.title("Original")
plt.subplot(122),plt.plot(hist),plt.xlim([0,256]),plt.title("Hist")
plt.gray()
plt.show()

在这里插入图片描述

使用掩码

掩码可以使我们快速观察图片指定区域的直方图,而不需要裁切。

#创建掩码矩阵,要求和目标图片一致
mask=np.zeros(img.shape[:2],np.uint8)
#将不需要遮掩的区域,取值为255,即白色
mask[200:800,600:1500]=255
#掩码与图片加和,得到不需要掩码的区域
masked_img=cv.bitwise_and(img,img,mask=mask)
#用OpenCV方法获得直方图
hist_full=cv.calcHist([img],[0],None,[256],[0,256])
hist_mask=cv.calcHist([img],[0],mask,[256],[0,256])

plt.subplot(231),plt.imshow(img,"gray"),plt.title("Original")
plt.subplot(232),plt.imshow(mask,"gray"),plt.title("Mask")
plt.subplot(233),plt.imshow(masked_img,"gray"),plt.title("Masked image")
plt.subplot(234),plt.plot(hist_full),plt.xlim([0,256]),plt.title("Hist full")
plt.subplot(236),plt.plot(hist_mask),plt.xlim([0,256]),plt.title("Hist masked")
plt.show()

(果然,大部分好的图片在主视觉区域内都有优秀的直方图哈哈)
在这里插入图片描述

二、直方图均衡

理论

有时候,我们得到的照片,总会因为某些原因,比如说曝光过度,光源太强或者太弱,或者太过于集中在某个值范围内,那么我们可以重新布局每个像素值中像素的多少,来平衡这种情况,直方图均衡就是做这么一件事。在手机或者电脑上修过图片的人都知道,我们改变图片白平衡的时候,图片的直方图会跟着改变,这就是在做直方图的均衡操作。

要调整直方图,我们需要先知道像素分布的范围比例。

img=cv.imread("gh.jpg",0)
#直方图设置
hist,bins=np.histogram(img,256,[0,256])
#cdf作为像素的累计和
cdf=hist.cumsum()
#归一化cdf
cdf_normalized=cdf*float(hist.max())/cdf.max()

plt.subplot(121)
plt.plot(cdf_normalized,color="b")
plt.hist(img.flatten(),256,[0,256],color="r")
plt.xlim([0,256])
plt.legend(("cdf","histogram"),loc="best")
plt.subplot(122),plt.imshow(img),plt.title("Original")
plt.gray()
plt.show()

大概有一半的像素,在黑线左边。我们可以看到,曲线向上拱起。做均衡,也可以理解为,把这条曲线拉直(当然实际上大部分时候我们不会真的把它拉成直线,这样看起来照片会没有什么特色哈哈)。
在这里插入图片描述
使用掩码数组,对图像进行操作,所有操作都是在非掩码元素下执行的,所以掩码元素不会发生变化。比如说,某个地方特别亮,掩码就不覆盖那片区域的元素,我们在接下来均衡化的时候,就对这部分区域进行修改均衡。和前面 使用掩码 这个逻辑是一样的,只操作,非掩码区像素。

#创建掩码,分布和cdf相同
cdf_m=np.ma.masked_equal(cdf,0)
cdf_m=(cdf_m-cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
cdf=np.ma.filled(cdf_m,0).astype("uint8")
img2=cdf[img]
hist1,bins1=np.histogram(img2,256,[0,256])
cdf_normalized1=cdf*float(hist1.max())/cdf.max()

plt.subplot(223),plt.plot(cdf_normalized1,color="y"),plt.hist(img2.flatten(),256,[0,256],color="b"),plt.xlim([0,256]),plt.legend(("cdf","histogram"),loc="best")
plt.subplot(224),plt.imshow(img2),plt.title("Original"),plt.gray()
plt.show()

和原图对比:相对于原图,均衡化之后的图片明暗更统一。
在这里插入图片描述
放大后,曲线明显被“拉直”,图片黑暗的地方被提亮。
在这里插入图片描述

OpenCV中的均衡

OpenCV中使用cv.equalizeHist()语句进行直方图均衡。

img=cv.imread("gh.jpg",0)
equ=cv.equalizeHist(img)
#并排两个图像
res=np.hstack((img,equ))
plt.subplot(),plt.imshow(res,"gray"),plt.title("Res")
plt.show()

左边使原图,右边是均衡化以后的图像,明显看见,暗部被提亮,但是在明暗对比较强的地方,变化不大。甚至在某些亮部,某些细节被抹去,导致重要的信息丢失。
在这里插入图片描述

自适应直方图均衡 CLAHE

为了解决亮部再次被提亮,导致很多的细节信息丢失,我们需要在均衡图片直方图的时候,希望它能自适应地去操作。

我们使用一个滤波器,对图片进行过滤,在次滤波器包含的元素中绘制直方图。但是图片中总会出现噪声,所以我们设定一个对比度阈值,超过这个阈值,就裁剪掉这个地方的像素并向其它地方均匀分配。

在OpenCV中,默认使用的titleSize是8X8的过滤器,阈值为40。

img=cv.imread("gh.jpg",0)
#自适应均衡,(对比度,过滤器)
clache=cv.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
#用到图片中
cll=clache.apply(img)

equ=cv.equalizeHist(img)
res=np.hstack((img,equ,cll))
plt.subplot(),plt.imshow(res,"gray"),plt.title("Original-Normal-CLACHE")
plt.show()

从左往右,分别是原图-普通均衡-自适应均衡,可以看出,自适应均衡不会再将亮部细节抹去,反而加强了效果,同时暗部细节也突出,明暗对比强烈的同时,保证整体的亮度均衡。
在这里插入图片描述

三、二维直方图

一维直方图,检测的是图片像素的灰度值,所以它的图片输出只能是黑白色。
二维直方图,检测的是图像每个像素的饱和度和色相,通常,它是用于检测有颜色的图片。

OpenCV中的二维直方图

二维直方图,也是采用cv.equalizeHist()这个函数。在开始之前,我们在前面已经知道,OpenCV采用的颜色是BRG,我们需要将其转换为HSV颜色通道。其中,H的取值范围是0-180,S和V的取值范围是0-256。

在这里,色相和饱和度,就是H和S,所以,
channel=[0,1]两个通道
bins=[180,256]对应两个通道最大值
range=[0,180,0,156]对应两个通道的取值范围

img=cv.imread("gh.jpg")
#颜色转换
hsv=cv.cvtColor(img,cv.COLOR_BGR2HSV)
#获得二维直方图
hist=cv.calcHist([hsv],[0,1],None,[180,256],[0,180,0,256])
#matplotlab显示方式,插值法使用邻近值可以获得更好的效果
plt.subplot(),plt.imshow(hist,interpolation="nearest")
plt.show()
#OpenCV的显示方式
cv.imshow("hist",hist)
cv.waitKey(0)

matplotlab的显示方式,横轴表示饱和度S,纵轴表示色相H。蓝色的像素点表示图片中有蓝色。
在这里插入图片描述
OpenCV显示方式,主要存在三种颜色,黑色占多,然后是蓝色,最后是白色。如果不是对色相值有所了解的话,这张图上并不能获取任何信息,因为它是黑白的。
在这里插入图片描述

Numpy中的二维直方图

img=cv.imread("gh.jpg")
hsv=cv.cvtColor(img,cv.COLOR_BGR2HSV)
h=hsv[0:1]
s=hsv[1:2]
hist,xbins,ybins=np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])
plt.subplot(),plt.imshow(hist,interpolation="nearest")
plt.show()
cv.imshow("hist",hist)
cv.waitKey(0)

效果和上面的不太相同,它的分布让我比较困惑。存疑。
在这里插入图片描述

四、直方图反投影

反投影可以获得我们感兴趣的区域(像素),类似于前面的颜色查找,但是我们在这里输入的不再是颜色,而是一张图片,在另一张图片里查找这张图片相关的像素区域,然后进行掩码操作。

roi=cv.imread("gh1.jpg")
hsv=cv.cvtColor(roi,cv.COLOR_BGR2HSV)
target=cv.imread("gh.jpg")
hsvt=cv.cvtColor(target,cv.COLOR_BGR2HSV)
#获取直方图
roihist=cv.calcHist([hsv],[0,1],None,[180,256],[0,180,0,256])
#直方图的归一化
cv.normalize(roihist,roihist,0,255,cv.NORM_MINMAX)
#使用反传算法
dst=cv.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)
#卷积操作
disc=cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
cv.filter2D(dst,-1,disc,dst)
#用阈值加和
ret,thresh=cv.threshold(dst,50,255,0)
thresh=cv.merge((thresh,thresh,thresh))
res=cv.bitwise_and(target,thresh)
res=np.vstack((target,thresh,res))
plt.subplot(121),plt.imshow(roi),plt.title("Select area")
plt.subplot(122),plt.imshow(res),plt.title("Result")
plt.show()

在这里插入图片描述

可以和前面颜色寻找作比较。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值