直方图
直方图是图像像素的统计特性,其可以应用在图像处理中的均衡化,匹配,和图像的反向投影等
直方图计算和绘制
直方图是图像处理中非常重要的像素统计工具,利用的是图像的统计特性。由于一个图像无论旋转还是平移,其在图像中都有
相同的灰度值,因此直方图具有平移不变性和缩放不变性。
直方图计算:
简单的说,图像就是统计图像中每个灰度值的个数,将灰度值作为横轴,以个数或者所占比值为纵轴绘制直方图
可以看出图像中的哪些灰度值较多,通常情况下,灰度值代表亮暗。
hist = cv.calcHist(images,channels,mask,histSize,ranges,hist,accumulate)
mask:掩膜
channels: 需要统计的通道索引
ranges: 灰度图取值范围 一般[0,256]
hist: 输出直方图 大小一般为histSize*1的数组
accumulate: True 表示累积
传入值时,应该使用中括号括起来
[img]
对于灰度图,第二个参数 [0]
对于彩图,第二个参数[0]、[1]、[2] 为BGR
直方图绘制:
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
np.set_printoptions(suppress=True)
if __name__ == '__main__':
img = cv.imread('../preview.jpg')
img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
if img is None:
print('读取失败')
exit()
hist = cv.calcHist([img], [0], None, [256], [0, 256])
plt.hist(x=img.ravel(), bins=256, range=[0, 256])
plt.show()
plt.hist(x,bins,range=[0,256])
绘制直方图
x: 数据集
img.ravel() 将array转为一维数组
2D直方图
所谓一维直方图是因为仅仅考虑了灰度值,而对于彩色图像,通常要考虑色调和饱和度,并根据他们进行统计
对于2D直方图,我们同样使用 cv.calcHsit 函数,但是在计算前,需要将图像转为HSV格式,
其中参数channels 仅仅需要统计HS两个通道 [0,1]。histSize为[180,256] 其中180代表H
256代表S,ranges为[0,180,0,256] 分别代表了H和S取值范围
尽管对于2D直方图 Opencv没有现成的函数,但是仍有方法绘制,可以使用matplotlib
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.set_printoptions(suppress=True)
if __name__ == '__main__':
img = cv.imread('../preview.jpg')
img = cv.cvtColor(img, cv.COLOR_BGR2HSV)
img_hist = cv.calcHist([img], [0, 1], None, [180, 256], [0, 180, 0, 256])
print(img_hist)
print(len(img_hist))
plt.imshow(img_hist, interpolation='nearest')
plt.show()
使用plt.imshow() 第一个参数为数据集, 第二个参数为绘制方法 ,本例为插值法
直方图操作
直方图可以反应图像灰度值等统计特性,但是这个结果仅仅是统计了数值,可以通过对统计结果进一步操作来得到更多的有用的信息
比如求取统计结果的概率分布,通过直方图对两张图像中的内容进行比较。
直方图统一化
灰度值的数量和其尺寸有直接关系,所以不能将灰度值的数量来作为最终的统计结果。
两种归一化方式:
1.变为百分比,某灰度值所占有总像素的百分比
2.寻找统计中的最大结果,将所有的结果除以这个最大的结果,全部变为0-1间
针对上诉归一化,提供了函数:
dst = cv.normalize(src,dst,alpha,beta,norm_type,dtype,mask)
src: 原图像
dst: 归一化类型为float32
alpha: 范围归一化下限
beta: 范围归一化时上限,它不用于标准归一化
norm_type: 归一化过程中数据范数的种类标准
dtype: 数据类型
mask:掩膜
src : 直方图数据
norm_type: cv.NORM_INF 1 除以最大的灰度值
cv.NORM_L1 2 除以绝对值之和
cv.NORM_L2 3 除以平方和之根
cv.NORM_MINMAX 线性统一化
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.set_printoptions(suppress=True)
if __name__ == '__main__':
img = cv.imread('../preview.jpg')
img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
img_hist = cv.calcHist([img], [0], None, [256], [0, 256])
# img_hist = img_hist.reshape(256)
print(img_hist)
img_L1 = cv.normalize(img_hist, None, norm_type=cv.NORM_L1)
img_INF = cv.normalize(img_hist, None, norm_type=cv.NORM_INF)
fig, axes = plt.subplots(3)
axes[0].set_title('hist')
axes[0].plot(img_hist)
axes[1].set_title('L1')
axes[1].plot(img_L1)
axes[2].set_title('Inf')
axes[2].plot(img_INF)
fig.show()
直方图比较
图像的直方图表示了图像的灰度值统计,可以通过比较两种图像的直方图来看出两幅图像的相似程度,虽然两张图像的直方图分布并不代表
两种图像相似,但是若两幅图片相似,则其直方图一定相似。比如对图像进行缩放后,虽然其直方图不会和之前完全一致,但是,两者间还是有
很高的相似性。所以,可以通过直方图来对图像的相似来进程初步的筛选
函数:
retavl = cv.compareHist(H1,H2,method)
H1: 第一张图像的直方图
H2: 第二章图像的直方图
直方图需要时同一种方式进行归一化后的,否则因为图像尺寸不同,灰度值多少也不同
method: 比较方法
cv.HISTCMP_CORREL 0 相关法
cv.HISTCMP_CHISQR 1 卡方法
cv.HISTCMP_INTERSECT 2 直方图相交法
cv.HISTCMP_BHATTACHARYYA 3 巴氏距离法
retavl: 会返回相关性系数
(1).比较方法对比
相关法:
完全一致为1 完全不相关为0
卡方法:
完全一致为0 越不相似,越大
直方图相交法:
不会对直方图进行归一
值越大相似性越高, 值越小相似性越低
巴氏距离法:
完全一致为0 ,相似性越低值越大
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.set_printoptions(suppress=True)
if __name__ == '__main__':
img = cv.imread('../preview.jpg')
img2 = cv.pyrDown(img)
img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
img2 = cv.cvtColor(img2, cv.COLOR_BGR2GRAY)
img_hist = cv.calcHist([img], [0], None, [256], [0, 256])
img_hist2 = cv.calcHist([img2], [0], None, [256], [0, 256])
img_L1 = cv.normalize(img_hist, None, norm_type=cv.NORM_L1)
img_L1_2 = cv.normalize(img_hist2, None, norm_type=cv.NORM_L1)
fig, axes = plt.subplots(2)
axes[0].plot(img_L1)
axes[1].plot(img_L1_2)
print(cv.compareHist(img_L1, img_L1_2, 0))
直方图均衡化
如果一个图像的直方图都集中在一个区域,则整体图像的对比度较小。不便于图像中的纹理识别。例如两个像素的灰度值分别为
120,121,则差距较小,整体图像会给人一种模糊的感觉,可以通过映射关系将灰度值放大,增大灰度值的差值,提高图像的对比度
进而将图像问题凸显出来,这个过程就叫做图像直方图的均衡化
opencv中提供了函数
dst = cv.equalizeHist(src)
src: 需要进行均衡化的图像
注意: 该函数仅能对单通道的图像进行均衡化
直方图匹配
cv.equalizeHist() 函数可以自动改变直方图的分布方式,这种方式极大简化了直方图均衡化过程,但是该函数不可以指定均衡化后直方图
分布方式。在某些特定情况下,我们需要将直方图映射为指定的分布方式,这种将直方图映射为指定的形式的算法被叫做直方图匹配
或者直方图规定化。
直方图匹配可以有目的性的增强某一灰度区间
直方图反向投影
如果一张图的某个区域显示的是一种特殊的纹理,那么,直方图就可看为该图像的概率函数,在图像中寻找这种概论分布就是在图像
中寻找该结构纹理。反向投影就是一种记录给定的图像中的像素如何适应直方图模型中的像素分布的方式。
简单地说就是,反向投影就是先计算某一特征的直方图模型,然后使用模型判断图像中是否存在该特征的方式
函数:
dst = cv.calcBackProject(images,channels,hist,ranges,scale)
img: 待统计直方图的图像,所有图像应该有相同的尺寸,类型
channels: 需要统计的通道索引数组
hist: 输入的直方图
ranges: 每个图像中灰度值取值范围
scale: 输出的反向投影矩阵的比例因子
该函数用来在输入图像中寻找与特定图像最匹配的点或者区域,及对图像进行反向投影,并将结果返回
步骤:
1.加载模型和待检测图像
2.转换图像颜色空间,常用的为GRAY和HSV
3.计算模板直方图,灰度图像的直方图为一维,HSV为二维
4.带入函数,得到结果
现状写一个程序来寻找红色
import sys
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.set_printoptions(suppress=True)
if __name__ == '__main__':
img = cv.imread('../img.png')
be_pre = cv.imread('../img_1.png')
if img is None or be_pre is None:
print('读取失败')
sys.exit()
# 将待测图像转为灰度图像
img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
img = cv.pyrDown(img)
be_pre = cv.cvtColor(be_pre, cv.COLOR_BGR2GRAY)
# 将模板转为直方图,并进行归一化
pre_hist = cv.calcHist(be_pre, [0], None, [256], [0, 256])
pre_hist = cv.normalize(pre_hist, None, cv.NORM_MINMAX)
# 在图像中寻找模板
res = cv.calcBackProject([img], [0], pre_hist, [0, 256], 1)
# 展示寻找到的模板
cv.imshow('old', img)
cv.imshow('pre', be_pre)
cv.imshow('res', res * 100)
cv.waitKey(0)
cv.destroyAllWindows()
可以看到红色被选出来了,简单地说,就是在原图中寻找到了和红色模板相同的直方图分布
原理: 将模型的直方图作为一个查找表,将原图像的每个像素点对照其所在的bin(灰度值),并将该像素点的灰度值
依照直方图来改为直方图中的映射的值
最后,原图中,每个像素点在模板中的灰度值所对应的较大的部分将会更亮
---------体会一下--------
在本例中,红色的灰度值对应的是1,在原图中对每个像素点所对应灰度值为红色的变为1
直方图反向投影是一种匹配方式,简单地说就是在一张张图片中寻找模板图像
其有很明显的缺点,就是不同的图像可能有相似的直方图分布,这样进行反向投影就不能寻找到正确的纹理
图像模板匹配
通过图像直方图的反向投影方式可以在图像中寻找模板图像,但是由于直方图并不可以直接的反应图像得纹理,它有很明显得缺陷,并且得到得
结果也并不是很准确。所以,这里将介绍图像模板匹配
模板匹配是通过直接比较图像的像素的形式来搜索是否有相同的内容,通过比较相似灰度值来寻找相同内容的方式叫做图像模板匹配
模板匹配常用在一副图像中寻找特定的内容,由于模板图像尺寸小于待匹配图像尺寸,同时还需要比较两幅图像中每一个像素的灰度值
,因此常常在待匹配图像中选择与模板尺寸相同的滑动窗口,通过滑动窗口来比较与模板的相似程度
步骤:
1.在待匹配图像中选择与模板尺寸相同的滑动窗口
2.比较滑动窗口内每个像素的灰度值和模板中对应的像素灰度值的关系,计算相似性
3.将滑动窗口从左上开始向右滑动,滑动到最右边后向下滑动一行(一行像素点),开始向左滑动,记录每次移动后的相似性
4.比较所有位置的相似性,返还最大的
函数:
res = cv.matchTemplate(image,templ,method,mask)
mask: 掩膜
image: 待匹配图像
templ: 模板图像
method: 模板匹配方法的标准
res: 结果
method: 相似性计算方法
cv.TM_SQDIFF 0 平方差匹配法 完全匹配为0
cv.TM_SQDIFF_NORMED 1 归一化平方差匹配法 完全匹配为0
cv.TM_CCORR 2 相关匹配法 完全不匹配为0
cv.TM_CCORR_NORMED 3 归一化相关匹配法 完全不匹配为0
cv.TM_CCOEFF 4 系数匹配法 越大越匹配
cv.TM_CCOEFF_NORMED 5 归一化相关系数匹配法 越大越匹配
比较:
系数匹配法:很好的解决模板图像和原图像间因为亮度不同参数的影响
寻找最匹配的小红块
import sys
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
np.set_printoptions(suppress=True)
if __name__ == '__main__':
# 读取模板和待匹配图像
img = cv.imread('../img.png')
be_pre = cv.imread('../img_1.png')
if img is None or be_pre is None:
print('图像读取失败')
sys.exit()
cv.imshow('be_pre', be_pre)
cv.imshow('img',img)
# 拿到模板的长宽
h, w = be_pre.shape[:2]
# 对模板进行平方差匹配,值越低越匹配
res = cv.matchTemplate(img, be_pre, method=cv.TM_SQDIFF_NORMED)
# 返回了res res为一个数值,其存储了每一个匹配位置的相似度
print(img.shape)
print(be_pre.shape)
print(res.shape)
print(res)
# 拿到最匹配的坐标位置 近似是左上角的坐标
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
left_top = min_loc
print(min_loc)
rig_bottom = (left_top[0] + w, left_top[1] + h)
# 给这一部分圈起来
cv.rectangle(img, left_top, rig_bottom, 255, 2)
cv.imshow('res', img)
cv.waitKey(0)
cv.destroyAllWindows()