OpenCV图像处理(四、直方图)

直方图

原理:通过直方图可以对整幅图像的灰度分布有一个整体的了解。直方图的x轴灰度值是(0~255),y轴是图片中具有同一个灰度值的点的数目。
直方图左侧是暗一点的像素数量,右侧是亮一点的像素的数量。

一、直方图的计算,绘制与分析

1.统计直方图

直方图的相关术语:

  • BINS:将256个值分成多少小组即有多少BINS。在openCV理用histSize表示BINS;
  • DIMS:表示收集数据的参数数目;
  • RANGE:要统计的灰度值范围,一般来说是[0, 256],也就是说所有的灰度值
1.1 用openCV统计直方图

用cv2.calcHist统计一副图像的直方图:

cv2.calcHist(images, channels, mask, histSize, ranges[,hist[,accumulate]])
  • images:原图像(图像格式为unit8或float32)。当传入函数时应该用中括号括起来,例如:[img];
  • channels:同样需要中括号括起来,它会告诉函数我们要统计哪幅图像的直方图。如果输入图像是灰度图,它的值是[0];如果是彩色图,传入图像可以是[0],[1],[2],分别对应B,G,R;
  • mask:掩模图像。要统计整幅图像的直方图就设置None。但是如果要统计图像的某一部分,需要制作一个掩模图像,并使用它;
  • histSize:BIN的数目;
  • ranges:像素值范围,通常为[0, 256]。

代码如下:

img = cv2.imread()
hist = cv2.calcHist([],[],None,[],[])
1.2 用Numpy统计直方图

用np.histogram()统计直方图

#img.ravel():将图像转换成一维数组,没有中括号
hist, bins = np.histgram(img.ravel(), 256, [0, 256])
#hist = np.bincount(img.ravel(),minlength=256)

Numpy还有一个函数是np.bincount(),它的运行速度是np.histgram的10倍。对一一维直方图,最好使用np.bincount(),但是使用时需设置minlength=256。
另外,OpenCV的函数要比np.histgram()快40倍,所以坚持用OpenCV的函数

2. 绘制直方图

灰度图:

plt.hist(img.ravel(), 256, [0, 256])
plt.show()

灰度图
多通道的直方图

color = ('b', 'g', 'r')
#对一个列表或数组既要遍历索引又要遍历元素时使用enumerate函数会更加直接,优美。enumerate会将数组或列表组成一个索引序列,使用时更加方便
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()

多通道直方图

3.使用掩模

    要统计某个局部区域的直方图需构建一副掩模图像:将要统计的部分设置成白色,其余部分为黑色,就构成了一副掩模图像,然后把这个图像传给函数。

#创建掩模
mask = np.zeros(img.shape[:2], np.uint8)# img1.shape[:2]:取彩色图片的长、宽
mask[150:500, 200:900] = 255
masked_img = cv2.bitwise_and(img, img, mask=mask)
#统计原图和有掩模的图的直方图
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
#画图
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()

结果图如下:其中蓝色是原图的直方图,橙色是进行掩模之后的直方图
使用掩模

二、直方图均衡化

  • 直方图均衡化是图像处理领域中利用图像直方图对对比度进行调整的方法;
  • 直方图均衡化对亮度分布针对图像的直方图不是集中在某一个区域需要采用自适应的直方图均衡化;
  • 人脸识别中,训练集样本均需要进行直方图均衡化。

1.用Numpy进行直方图均衡化

# flatten()将数组变成一维
hist, bins = np.histogram(img.flatten(), 256, [0, 256])
#计算累积分布图
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/cdf.max()
# 构建Numpy掩模数组,cdf为原数组,当前组元素为0时,掩盖
cdf_m = np.ma.masked_equal(cdf, 0)
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max() - cdf_m.min())
# 对被掩盖的元素赋值,赋值为0
cdf = np.ma.filled(cdf_m, 0).astype('uint8')
img2 = cdf[img]
#画出直方图
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.plot(cdf_normalized, color='b'), plt.hist(img.flatten(), 256, [0, 256], color='r')
plt.subplot(223), plt.imshow(img2, 'gray')
plt.subplot(224), plt.plot(cdf_normalized, color='b'), plt.hist(img2.flatten(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'histogram'), loc='upper left')
plt.show()

实验结果:均衡化之前的图如左上,左下是均衡化之后的图,对比度上升。
Numpy直方图均衡化效果图

2.用OpenCV进行直方图均衡化

    OpenCV中直方图均衡化的函数为cv2.equalizeHist():这个函数的输入图片是一副灰度图像,输出结果是直方图均衡化之后的图像。代码如下:

equ = cv2.equalizeHist(img)
res = np.hstack((img, equ))
cv2.imwrite('res.png', res)
cv2.imshow('cv2', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果图:
OpenCV直方图均衡化

3. CLAHE有限对比适应性直方图均衡化

    针对图像的直方图不是集中在某一个区域需要采用自适应的直方图均衡化。原理是:将图像分成很多小块,这些小块称为‘tiles’,然后对每一个小块进行直方图均衡化,代码如下:

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))# tileGridSize默认是8*8
cl1 = clahe.apply(img)
res = np.hstack((img, cl1))
cv2.imwrite('res.png', res)
cv2.imshow('clahe', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

三、2D直方图

    一维直方图只考虑了图像的一个特征:灰度值;2D直方图需要考虑两个特征:颜色和饱和度。
    使用cv2.calcHist()来计算直方图,需要将图像的颜色空间从BGR转换到HSV。函数的参数变动如下:

  • channels = [0,1]:需要同时处理H和S两个通道;
  • bins = [180, 256]:H通道为180,S通道外256;
  • range = [0, 180, 0, 256]:H的取值范围在0至180,S的取值范围在0到256

代码如下:

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
plt.imshow(hist, interpolation='nearest')
plt.show()

在这里插入图片描述

四、直方图反向投影

    用来做图像分割,或者在图像中寻找我们感兴趣的部分

1.Numpy中的算法

# 要查找的图
roi = cv2.imread("/~/logo_roi.jpg")
hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
# 目标区域
target = cv2.imread("/~/logo.jpg")
hsvt = cv2.cvtColor(target, cv2.COLOR_BGR2HSV)
# 统计直方图
M = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
I = cv2.calcHist([hsvt], [0, 1], None, [180, 256], [0, 180, 0, 256])
#计算反向投影R
R = M/I
h, s, v = cv2.split(hsvt)
B = R[h.ravel(), s.ravel()]
B = np.minimum(B, 1)
B = B.reshape(hsvt.shape[:2])
# 卷积
disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
B = cv2.filter2D(B, -1, disc)
B = np.uint8(B)
cv2.normalize(B, B, 0, 255, cv2.NORM_MINMAX)
ret, thresh = cv2.threshold(B, 50, 255, 0)
cv2.imshow('thresh', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

2. OpenCV中的算法

    OpenCV 提供的函数cv2.calcBackProject() 可以用来做直方图反向投影

# 要查找的图
r = cv2.imread("/~/logo_roi.jpg")
h0 = cv2.cvtColor(r, cv2.COLOR_BGR2HSV)
# 目标区域
t = cv2.imread("/~/logo.jpg")
h = cv2.cvtColor(t, cv2.COLOR_BGR2HSV)
# 统计直方图
rh = cv2.calcHist([h0], [0, 1], None, [180, 256], [0, 180, 0, 256])
# 归一化
cv2.normalize(rh, rh, 0, 255, cv2.NORM_MINMAX)
# 把H0的直方图反向投影到h上面
dst = cv2.calcBackProject([h], [0, 1], rh, [0, 180, 0, 256], 1)#相当于找到所有红色区域
#把零散区域的点连成一片:
d = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
dst = cv2.filter2D(dst, -1, d)
#把dst转变成二值图片tr:
ret, tr = cv2.threshold(dst, 50, 255, 0)
#把tr转变成三通道图片:
tr = cv2.merge((tr, tr, tr))
# 把tr作为蒙版,在原图上作用:
res = cv2.bitwise_and(t, tr)
# 把三个过程的结果上下排列
s = np.row_stack((t, tr, res))
plt.imshow(s, interpolation='nearest')
plt.show()

结果图如下:
反向直方图结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值