使用OpenCV和Matplotlib函数绘制直方图 :cv.calcHist(),np.histogram()
直方图可以总体了解图像的强度分布。通过查看图像的直方图,您可以直观地了解该图像的对比度,亮度,强度分布等。直方图的左侧区域显示图像中较暗像素的数量,而右侧区域则显示明亮像素的数量。
一、寻找直方图
BINS:上面的直方图显示每个像素值的像素数,即从0到255。如果找介于0到15之间的像素数,然后找到16到31之间,…,240到255之间的像素数。只需要16个值即可表示直方图。因此,您要做的就是将整个直方图分成16个子部分,每个子部分的值就是其中所有像素数的总和。每个子部分都称为“ BIN”。在第一种情况下,bin的数量为256个(每个像素一个),而在第二种情况下,bin的数量仅为16个。BINS由OpenCV文档中的
histSize
术语表示。
DIMS:这是我们为其收集数据的参数的数量。在这种情况下,我们仅收集关于强度值的一件事的数据。所以这里是1。
RANGE:这是您要测量的强度值的范围。通常,它是[0,256],即所有强度值。
查找直方图的函数及其参数:
cv.calcHist(images,channels,mask,histSize,ranges[,hist [,accumulate]])
images:它是uint8或float32类型的源图像。它应该放在方括号中,即“ [img]”。channels:也以方括号给出。它是我们计算直方图的通道的索引。例如,如果输入为灰度图像,则其值为[0]。对于彩色图像,您可以传递[0],[1]或[2]分别计算蓝色,绿色或红色通道的直方图。
mask:图像掩码。为了找到完整图像的直方图,将其指定为“无”。但是,如果要查找图像特定区域的直方图,则必须为此创建一个掩码图像并将其作为掩码。
histSize:这表示BIN计数。需要放在方括号中。对于全尺寸,我们通过[256]。ranges:这是我们的RANGE。通常为[0,256]。
如:img=cv.imread(‘home.jpg’,0)
hist=cv.calcHist([img],[0],None,[256],[0,256])
还有hist,bins=np.histogram(img.ravel(),256,[0,256])也用来求直方图,对于一维直方图,hist = np.bincount(img.ravel(),minlength = 256)速度更快。
matplotlib.pyplot.hist()它直接找到直方图并将其绘制。无需使用
calcHist
()或np.histogram()函数来查找直方图。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('E:\\python opencv\\demo2\\a.jpg', 0)
cv.imshow('img', img)
plt.hist(img.ravel(), 256, [0, 256])
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
对于BGR图片:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('E:\\python opencv\\demo2\\a.jpg')
cv.imshow('img', img)
color = ('b', 'g', 'r')
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.show()
cv.waitKey(0)
cv.destroyAllWindows()
掩码的应用
:查找需要的区域,需创建一个掩码图像,在你要找到直方图为白色,否则黑色。然后把这个作为掩码传递。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('E:\\python opencv\\demo2\\a.jpg', 0)
#创建掩码
mask = np.zeros(img.shape[0:2], np.uint8)
mask[0:250, 150:350] = 255
mask_img = cv.bitwise_and(img, img, mask=mask)
# 计算掩码区域和非掩码区域的直方图
# 检查作为掩码的第三个参数
hist_full = cv.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv.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(mask_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0, 256])
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
均衡
调节亮度
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('E:\\python opencv\\demo2\\wiki.png', 0)
cv.imshow('img', img)
hist, bins = np.histogram(img.flatten(), 256, [0, 256])
cdf = hist.cumsum()
cdf_normalized = cdf*float(hist.max())/cdf.max()
plt.subplot(2, 1, 1), plt.plot(cdf_normalized, color='b')
plt.hist(img.flatten(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'histogram'), loc='upper left')
#均衡
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]
cv.imshow('img2', img2)
hist1, bins1 = np.histogram(img2.flatten(), 256, [0, 256])
cdf1 = hist1.cumsum()
cdf_normalized1 = cdf1*float(hist1.max())/cdf1.max()
plt.subplot(2, 1, 2), plt.plot(cdf_normalized1, color='b')
plt.hist(img2.flatten(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'histogram'), loc='upper left')
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
或者用cv.equalizeHist() 函数直接均衡
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('E:\\python opencv\\demo2\\wiki.png', 0)
cv.imshow('img', img)
res = cv.equalizeHist(img)
#res = np.hstack((img, equ))
cv.imshow('res', res)
cv.waitKey(0)
cv.destroyAllWindows()
自适应直方图均衡
有时候直接全部均衡会使图片失去一些信息,在这种情况下,图像被分成称为“tiles”的小块(在OpenCV中,tileSize默认为8×8)。然后,像往常一样对这些块中的每一个进行直方图均衡。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('E:\\python opencv\\demo2\\wiki1.jpg', 0)
cv.imshow('img', img)
res = cv.equalizeHist(img)
#res = np.hstack((img, equ))
cv.imshow('res', res)
#自适应均衡
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)
cv.imshow('cl1', cl1)
cv.waitKey(0)
cv.destroyAllWindows()
二、二维直方图
一维直方图,之所以称为一维,是因为我们仅考虑一个特征,即像素的灰度强度值。但是在二维直方图中,您要考虑两个特征。通常,它用于查找颜色直方图,其中两个特征是每个像素的色相和饱和度值。
二维直方图使用相同的函数
cv.calcHist
()进行计算。对于颜色直方图,我们需要将图像从BGR转换为HSV。(请记住,对于一维直方图,我们从BGR转换为灰度)。
对于二维直方图,其参数将进行如下修改:
channel = [0,1],因为我们需要同时处理H和S平面。
bins = [180,256]对于H平面为180,对于S平面为256。
range = [0,180,0,256]色相值介于0和180之间,饱和度介于0和256之间。
也可以用
hist,xbins,ybins=np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])
来完成找直方图
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('E:\\python opencv\\demo2\\wiki2.jpg')
cv.imshow('img', img)
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)
hist = cv.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
plt.imshow(hist, interpolation='nearest')
plt.show()
cv.waitKey(0)
cv.destroyAllWindows()
三、直方图反投影
它用于图像分割或在图像中查找感兴趣的对象。简而言之,它创建的图像大小与输入图像相同(但只有一个通道),其中每个像素对应于该像素属于我们物体的概率。
OpenCV提供了一个内建的函数
cv.calcBackProject
()。它的参数几乎与
cv.calchist
()函数相同。它的一个参数是直方图,也就是物体的直方图,我们必须找到它。另外,在传递给backproject函数之前,应该对对象直方图进行归一化。它返回概率图像。然后我们用圆盘内核对图像进行卷积并应用阈值。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
#感兴趣的部分
roi= cv.imread('E:\\python opencv\\demo2\\wiki6.jpg')
hsv = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
#整张图
target = cv.imread('E:\\python opencv\\demo2\\wiki4.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))
cv.imshow('res', res)
cv.waitKey(0)
cv.destroyAllWindows()