2021-07-25

直方图

直方图是在X轴上具有像素值(不总是从0到255的范围),在Y轴上具有图像中相应像素数的图。这是理解图像的另一种方式。通过查看图像的直方图,您可以直观地了解该图像的对比度,亮度,强度分布等。

一些概念

BINS:直方图显示每个像素值的像素数,即从0到255。即您需要256个值来显示上面的直方图。但是考虑一下,如果您不需要分别找到所有像素值的像素数,而是找到像素值间隔中的像素数怎么办? 例如,您需要找到介于0到15之间的像素数,然后找到16到31之间,…,240到255之间的像素数。只需要16个值即可表示直方图。因此,您要做的就是将整个直方图分成16个子部分,每个子部分的值就是其中所有像素数的总和。 每个子部分都称为“ BIN”。在第一种情况下,bin的数量为256个(每个像素一个),而在第二种情况下,bin的数量仅为16个。BINS由OpenCV文档中的histSize术语表示。

DIMS:这是我们为其收集数据的参数的数量。在这种情况下,我们仅收集关于强度值的一件事的数据。所以这里是1。

RANGE:这是您要测量的强度值的范围。通常,它是 [0,256] ,即所有强度值

直方图的查找、绘制和分析

直方图的计算

有两种方法,第一种采用cv.calcHist()函数绘制。

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]
返回值hist是256x1的数组,每个值对应于该图像中具有相应像素值的像素数。

img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\2d.png',0)
hist = cv.calcHist([img],[0],None,[256],[0,256])

第二个方法是np.histogram()

histogram(a,bins=10,range=None,weights=None,density=False);

a是待统计数据的图像;
bins指定统计的区间个数;
range是一个长度为2的元组,表示统计范围的最小值和最大值,默认值None,表示范围由数据的范围决定
可选参数:
weights为数组的每个元素指定了权值,histogram()会对区间中数组所对应的权值进行求和
density为True时,返回每个区间的概率密度;为False,返回每个区间中元素的个数
他的返回值是

img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\2d.png',0)
hist,bins = np.histogram(img.ravel(),256,[0,256])

注意cv比np快很多尽量使用cv

直方图的绘制

直方图的绘制同样有两种方法,第一种是matplotlib.pyplot.hist()它既可以计算也可以绘制直方图。无需采用前面的两种方法计算。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg',0)
plt.hist(img.ravel(),256,[0,256])
plt.show()

ravel()方法将数组维度拉成一维数组
必要参数:
第一个参数是 要求是一个一维数组
第二个参数是 bins统计的区间个数
第三个参数是 表示统计范围的最小值和最大值,默认值None,表示范围由数据的范围决定
可选参数:
weights为数组的每个元素指定了权值,histogram()会对区间中数组所对应的权值进行求和
density为True时,返回每个区间的概率密度;为False,返回每个区间中元素的个数

第二种方法是利用cv.calcHist()函数的返回值和matplotlib库绘制

import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\hws.jpg',0)
hist_full = cv.calcHist([img],[0],None,[256],[0,256])
plt.subplot(211), plt.imshow(img, 'gray')
plt.subplot(212), plt.plot(hist_full)
plt.xlim([0,256])
plt.show()

plt.xlim(xmin, xmax)可以设置x轴的数值显示范围。

一个可以绘制各个通道直方图的例子:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\hws.jpg')
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()

在这里插入图片描述

直方图均衡

均衡化原理如下:
1.计算直方图数据H(i),i为灰度值,范围是0-255
2.由H(i)计算H’(i)即H’(i)=sum(H(i)),加和的j从0到i,即对灰度值i的积累是把灰度值小于i的直方图数据全部加起来
3.对第二步中的累积直方图进行归一化,把值的范围回归到0-255
4. 以原图像的像素值为索引,逐一从第三步归一化的累积直方图中取值组成均衡化后的图像。

cv.normalize()介绍
normalize (src [, dst [, alpha [, beta [, norm_type [, dtype [, mask ] ] ] ] ] ] ) → dst

SRC -输入数组。
DST -与SRC大小相同的输出数组。
alpha要归一化的范数值或范围归一化的下限
beta 范围归一化时的上范围边界;它不用于规范规范化
normType—归一化的类型
Dtype -dtype为负数时,输出数组的type与输入数组的type相同;否则,输出数组与输入数组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype).
Mask—可选的操作掩码。
Dtype取值:

代码方法
NORM_MINMAX数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用
NORM_INF此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化数组的C-范数(绝对值的最大值)
NORM_L1归一化数组的L1-范数(绝对值的和)
NORM_L2归一化数组的(欧几里德)L2-范数
OpenCV中的直方图均衡

opencv具有执行此操作的功能cv.equalizeHist()。它的输入只是灰度图像,输出是我们的直方图均衡图像。

import cv2 as cv
import matplotlib.pyplot as plt
img=cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\hu.png',0)

plt.figure("hist",figsize=(8,8))

plt.subplot(221)
plt.imshow(img,plt.cm.gray)  #原始图像
plt.subplot(222)
plt.hist(img.ravel(),256,[0,256])

img1=cv.equalizeHist(img)
plt.subplot(223)
plt.imshow(img1,plt.cm.gray)  #均衡化图像
plt.subplot(224)
plt.hist(img1.ravel(),256,[0,256]) #均衡化直方图

plt.show()
Numpy中的直方图均衡
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\hu.png',0)
hist,bins = np.histogram(img.flatten(),256,[0,256])#步骤1
cdf = hist.cumsum()#步骤2
cdf_normalized = cdf * float(hist.max()) / cdf.max()步骤3
img1=cdf[img]步骤4

plt.figure("hist",figsize=(8,8))
plt.subplot(321)
plt.imshow(img,plt.cm.gray)  #原始图像
plt.subplot(322)
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.subplot(323)
plt.imshow(img1,plt.cm.gray)  #均衡化图像
plt.subplot(324)
plt.hist(img1.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc = 'upper left')
plt.subplot(325)
plt.plot(cdf_normalized, color = 'b')
plt.show()
自适应直方图均衡

在这种情况下,图像被分成称为“tiles”的小块(在OpenCV中,tileSize默认为 8x8 )。然后,像往常一样对这些块中的每一个进行直方图均衡。应用了对比度限制。如果任何直方图bin超出指定的对比度限制(在OpenCV中默认为40),则在应用直方图均衡之前,将这些像素裁剪并均匀地分布到其他bin。均衡后,要消除图块边界中的伪影,请应用双线性插值。
这里需要用到cv2.createCLAHA用于生成自适应均衡化图像
他的输入是clipLimit和 titleGridSize,clipLimit是颜色对比度的阈值它的默认值是8.0, titleGridSize是进行像素均衡化的网格大小,即在多少网格下进行直方图的均衡化操作它的默认值是(8,8)。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\rentou.png',0)

clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
plt.figure("hist",figsize=(8,8))
plt.subplot(121)
plt.imshow(img,plt.cm.gray)
plt.subplot(122)
plt.imshow(cl1,plt.cm.gray)
plt.show()
cv.waitKey(100000)

二维直方图

OpenCV中的二维直方图

它使用和一维直方图相同的函数cv.calcHist()进行计算。相对于一维直方图将颜色转换为灰度,对于颜色直方图,我们需要将图像从BGR转换为HSV。对于二维直方图,其参数将进行如下修改:
channel = [0,1],因为我们需要同时处理H和S平面。
bins = [180,256] 对于H平面为180,对于S平面为256。
range = [0,180,0,256] 色相值介于0和180之间,饱和度介于0和256之间。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\build.png')
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
plt.subplot(121)
hist = cv.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
plt.imshow(hist,interpolation = 'nearest')
plt.subplot(122)
plt.imshow(img)
plt.show()

直方图反投影

原理:
反投影原理

OpenCV的反投影

OpenCV提供了一个内建的函数cv.calcBackProject()。它的参数几乎cv.calchist()函数相同。它的一个参数是直方图,也就是物体的直方图,我们必须找到它。另外,在传递给backproject函数之前,应该对对象直方图进行归一化。它返回概率图像。然后我们用圆盘内核对图像进行卷积并应用阈值。
参数详解
images,输入图像
channels,//要进行投影的通道数
hist,/样本得直方图ranges, //输入直方图得特征空间的取值范围
scale = 1,输出反投影的可选比例因子。
flag 指示直方图是否均匀的标志
返回值是反转后的直方图

例子

import numpy as np
import cv2 as cv
#读取图像
roi = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\cao.png')#局部
target = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\mei.png')#整体
#转为HSV格式因为HSV表达颜色更为方便区分
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
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))
dst=cv.filter2D(dst,-1,disc)

# 应用阈值作与操作
ret,thresh = cv.threshold(dst,50,255,0)
#thresh就是识别出来相同物体的掩码
thresh = cv.merge((thresh,thresh,thresh))
#从原图像中取出这一部分
res = cv.bitwise_and(target,thresh)
res = np.vstack((target,thresh,res))
cv.namedWindow('a',cv.WINDOW_NORMAL)
cv.resizeWindow('a',640,480)
cv.moveWindow('a',0,0)
cv.imshow('a',res)
cv.waitKey(100000)

Numpy的反投影

求出比值R=M/II。然后反向投影R,即使用R作为调色板,并以每个像素作为其对应的目标概率创建一个新图像。即B(x,y) = R[h(x,y),s(x,y)] 其中h是色调,s是像素在(x,y)的饱和度。之后,应用条件B(x,y)=min[B(x,y),1]B(x,y)=min[B(x,y),1]。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
#roi是我们需要找到的对象或对象区域
roi = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\shui.png')#局部
target = cv.imread('C:\\Users\\Administrator\\Desktop\\pic\\zhuo.png')#整体
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV)
# 使用calcHist查找直方图。也可以使用np.histogram2d完成
M = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
I = cv.calcHist([hsvt],[0, 1], None, [180, 256], [0, 180, 0, 256] )

h,s,v = cv.split(hsvt)
R=M/I
B = R[h.ravel(),s.ravel()]
B = np.minimum(B,1)
B = B.reshape(hsvt.shape[:2])

disc = cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
cv.filter2D(B,-1,disc,B)
B = np.uint8(B)
cv.normalize(B,B,0,255,cv.NORM_MINMAX)
ret,thresh = cv.threshold(B,50,255,0)
thresh = cv.merge((thresh,thresh,thresh))
res = cv.bitwise_and(target,thresh)
cv.namedWindow('a',cv.WINDOW_NORMAL)
cv.resizeWindow('a',640,480)
cv.moveWindow('a',0,0)
cv.imshow('a',res)
cv.waitKey(100000)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值