OpenCV中的图像处理 [3]-OpenCV中的直方图

参考:

1、http://docs.opencv.org/3.3.0/  官方文档api

2、http://docs.opencv.org/3.3.0/d6/d00/tutorial_py_root.html 官方英文教程

3、https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html

4、https://github.com/makelove/OpenCV-Python-Tutorial# 进阶教程

5、https://docs.opencv.org/3.3.0/index.html  官方英文教程

6、https://github.com/abidrahmank/OpenCV2-Python-Tutorials

7、https://www.learnopencv.com/

8、http://answers.opencv.org/questions/ OpenCV论坛

9、https://github.com/opencv/opencv   官方github

10、https://github.com/abidrahmank/OpenCV2-Python-Tutorials


注:安装的版本 opencv_python-3.3.0-cp36-cp36m-win_amd64.whl



参考:https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html




直方图 - 1:查找,绘制,分析 !!!

目标

Learn to
  • Find histograms, using both OpenCV and Numpy functions
  • Plot histograms, using OpenCV and Matplotlib functions
  • You will see these functions : cv2.calcHist()np.histogram() etc.

理论


1. OpenCV中的直方图计算

所以现在我们使用cv2.calcHist() 函数来查找直方图。 让我们熟悉函数及其参数:

cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])


图像:它是uint8或float32类型的源图像。 应该在方括号中给出,即“[img]”。
频道:方括号也给出。 它是我们计算直方图的通道索引。 例如,如果输入是灰度图像,其值为[0]。 对于彩色图像,您可以通过[0],[1]或[2]分别计算蓝色,绿色或红色通道的直方图。
面具:面具图像。 要查找完整图像的直方图,它被赋予为“无”。 但是,如果要查找特定图像区域的直方图,则必须为其创建掩模图像并将其作为掩码。 (稍后我会举一个例子)
histSize:这表示我们的BIN计数。 需要在方括号中给出。 全尺寸,我们通过[256]。
范围:这是我们的范围。 通常,[0,256]。

所以让我们从一个示例图像开始。 只需在灰度模式下加载图像,并找到其完整的直方图。

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('Lenna.png',0)
hist = cv2.calcHist([img],[0],None,[256],[0,256])
plt.plot(hist)
plt.show()

hist是256x1数组,每个值对应于该图像中其像素数与其相应像素值的数量。


2. 直方图计算在Numpy

Numpy还提供了一个函数,np.histogram()。 所以,而不是calcHist()函数,你可以尝试下面的行:

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('Lenna.png',0)
# hist = cv2.calcHist([img],[0],None,[256],[0,256])
hist,bins = np.histogram(img.ravel(),256,[0,256])
plt.plot(hist)
plt.show()
hist与之前计算的相同。 但是bins将有257个元素,因为Numpy计算箱子为0-0.99,1.1.99,2.2.99等等,所以最终的范围是255-255.99。 为了表示这一点,他们还在bin的末尾增加了256个。 但是我们不需要256。


Numpy有另一个功能,np.bincount()比(大约10X)np.histogram()快得多。 所以对于一维直方图,你可以更好地尝试。 不要忘记在np.bincount中设置minlength = 256。 例如,hist = np.bincount(img.ravel(),minlength = 256)


OpenCV功能比np.histogram()快于(大约40X)。 所以坚持使用OpenCV功能。

绘制直方图

There are two ways for this,
  1. Short Way : use Matplotlib plotting functions
  2. Long Way : use OpenCV drawing functions

1. 使用Matplotlib

Matplotlib comes with a histogram plotting function :matplotlib.pyplot.hist()

它直接找到直方图并绘制它。 您不需要使用calcHist()或np.histogram()函数来查找直方图。 请参阅以下代码:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('home.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.show()

或者您可以使用matplotlib的正常图,这对BGR图是有好处的。 为此,您需要首先找到直方图数据。 请尝试以下代码:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('messi5.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
    # histr = cv2.calcHist([img],[i],None,[256],[0,256])
    histr, bins = np.histogram(img[:,:,i].ravel(), 256, [0, 256])
    plt.plot(histr,color = col)
    plt.xlim([0,256])
plt.show()


2. 使用OpenCV

那么这里你可以调整直方图的值以及它的bin值,看起来像x,y坐标,以便您可以使用cv2.line()或cv2.polyline()函数绘制它,以生成与上述相同的图像。 OpenCV-Python2官方示例已经可以使用。 Check the Code


应用掩膜

我们使用cv2.calcHist()来查找完整图像的直方图。 如果要查找图像的某些区域的直方图怎么办? 只需在要查找直方图的区域上创建一个带有白色的mask图像,否则为黑色。 然后把它当作面具。

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('messi5.jpg',0)

# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img,img,mask = mask)

# Calculate histogram with mask and without mask
# Check third argument for 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()


Histogram Example

Additional Resources

  1. Cambridge in Color website



直方图 - 2:直方图均衡

目标

In this section,

  • We will learn the concepts of histogram equalization and use it to improve the contrast of our images.

理论

考虑图像像素值仅限于特定值范围。 例如,较亮的图像将所有像素都限制在高值。 但是,一个好的图像将会有来自图像所有区域的像素。 所以你需要将这个直方图拉伸到两端(如下图所示,从维基百科),这就是直方图均衡所做的(简单的话)。 这通常会 改善图像的对比度

Histograms Equalization

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('dave.jpg',0)

# x=np.max(img.ravel()-img.flatten())

hist,bins = np.histogram(img.flatten(),256,[0,256])

cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/ cdf.max()

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')
plt.show()
Histograms Equalization

您可以看到直方图位于较亮的区域。 我们需要全谱。 为此,我们需要一个转换函数,将更亮区域中的输入像素映射到全区域的输出像素。 那就是直方图均衡。

现在我们找到最小直方图值(不包括0),并应用维基页面中给出的直方图均衡方程。 但是我在这里使用了来自Numpy的掩码数组概念数组。 对于屏蔽阵列,所有操作都在非屏蔽元素上执行。 您可以从掩码数组的Numpy文档中阅读更多信息。

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('dave.jpg',0)

hist,bins = np.histogram(img.flatten(),256,[0,256])

cdf = hist.cumsum()

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]

hist_full = cv2.calcHist([img2],[0],None,[256],[0,256])

plt.subplot(221);plt.imshow(img,'gray')
plt.subplot(222);plt.plot(hist)

plt.subplot(223);plt.imshow(img2,'gray')
plt.subplot(224);plt.plot(hist_full)

plt.show()

另一个重要特点是,即使图像是较暗的图像(而不是我们使用的更亮的图像),均衡后,我们将获得几乎相同的图像。 因此,它被用作“参考工具”,以使所有具有相同光照条件的图像。 这在许多情况下很有用。 例如,在面部识别中,在训练面部数据之前,脸部的图像被直方图均衡以使它们 全部具有相同的照明条件

Histograms Equalization

OpenCV中的直方图均衡

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

下面是一个简单的代码片段,显示了我们使用的相同图像的用法:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('dave.jpg',0)

equ = cv2.equalizeHist(img) # 直方图均衡后的图像
'''
# 等价于
hist,bins = np.histogram(img.flatten(),256,[0,256])
cdf = hist.cumsum()
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')
equ = cdf[img]
'''
res = np.hstack((img,equ)) #stacking images side-by-side
cv2.imwrite('res.png',res)
cv2.imshow('img',img)
cv2.imshow('res',res)
cv2.waitKey(0)
cv2.destroyAllWindows()

所以现在你可以拍摄不同的光线条件不同的图像,使其均匀并检查结果。

当图像的直方图被限制在特定区域时,直方图均衡是很好的。 直方图覆盖较大的区域,即存在较大的强度变化的地方,即使存在明暗的像素也不会奏效。 请查看其他资源中的SOF链接。


CLAHE(对比度有限自适应直方图均衡)

我们刚刚看到的第一个直方图均衡,考虑了图像的全局对比度。 在很多情况下,这不是一个好主意。 例如,下图显示了输入图像及其全局直方图均衡后的结果。

Problem of Global HE


在直方图均衡后,背景对比度是改善的。 但是在两个图像中比较雕像的脸。 由于超亮度,我们失去了大部分信息。 这是因为它的直方图并不局限于特定区域,就像我们以前的情况一样(尝试绘制输入图像的直方图,你会得到更多的直觉)。

因此,为了解决这个问题,使用自适应直方图均衡。 在这种情况下,图像被划分为称为“瓦片”的小块(默认情况下,TileSize是OpenCV中的8x8)。 然后这些块中的每一个像平常一样直方图均衡。 所以在一个很小的区域,直方图将局限于一个小区域(除非有噪音)。 如果有噪音,那将会被放大。 为了避免这种情况,应用对比度限制。 如果任何直方图bin高于指定的对比度限制(OpenCV中默认为40),则在应用直方图均衡之前,这些像素将被裁剪并均匀分布到其他bin。 均衡后,为了去除瓦片边框中的伪像,应用双线性插值。

以下代码片段显示了如何在OpenCV中应用CLAHE:

import numpy as np
import cv2

img = cv2.imread('tsukuba_l.png',0)

# create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)

cv2.imwrite('clahe_2.jpg',cl1)


Additional Resources

  1. Wikipedia page on Histogram Equalization
  2. Masked Arrays in Numpy

Also check these SOF questions regarding contrast adjustment:

  1. How can I adjust contrast in OpenCV in C?
  2. How do I equalize contrast & brightness of images using opencv?


直方图 - 3:2D直方图

目标

In this chapter, we will learn to find and plot 2D histograms. It will be helpful in coming chapters.

介绍

在第一篇文章中,我们计算并绘制了一维直方图。 它被称为一维,因为我们仅考虑了一个特征,即像素的灰度强度值。 但是在二维直方图中,您可以考虑两个特征。 通常它用于查找颜色直方图,其中两个特征是每个像素的色相和饱和度值。

官方示例中有一个python样本已经用于查找颜色直方图。 我们将尝试了解如何创建这样的颜色直方图,并且它将有助于理解进一步的主题,如直方图反投影。


OpenCV中的2D直方图

它是相当简单和计算使用相同的函数, cv2.calcHist()。 对于颜色直方图,我们需要将图像从 BGR转换为HSV。 (记住,对于1D直方图,我们从 BGR转换为灰度)。 对于2D直方图,其参数将被修改如下:

  • channels = [0,1] because we need to process both H and S plane.
  • bins = [180,256] 180 for H plane and 256 for S plane.
  • range = [0,180,0,256] Hue value lies between 0 and 180 & Saturation lies between 0 and 256.
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('messi5.jpg')
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])

plt.plot(hist);plt.show()


二维直方图在Numpy

Numpy还提供了一个特定的功能:np.histogram2d()。 (记住,对于1D直方图,我们使用np.histogram())。

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('home.jpg')
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])

第一个参数是H平面,第二个是S平面,第三个是四和四的数量是它们的范围。

现在我们可以检查如何绘制这个颜色直方图。


绘制2D直方图

方法 - 1:使用cv2.imshow()

我们得到的结果是尺寸为180x256的二维数组。 所以我们可以像正常使用cv2.imshow()函数一样显示它们。 这将是一个灰度图像,除非你知道不同颜色的色相值,否则它不会给出很多的颜色。

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('messi5.jpg')
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
h,s,v=cv2.split(hsv)
hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])
cv2.imshow('his',hist)
cv2.waitKey(0)
cv2.destroyAllWindows()


方法 - 2:使用Matplotlib

我们可以使用matplotlib.pyplot.imshow()函数来绘制具有不同颜色图的2D直方图。 它使我们更好地了解不同的像素密度。 但是,这也不是让我们了解第一眼看什么颜色,除非你知道不同颜色的色相值。 仍然我喜欢这种方法。 这是简单和更好。

使用此功能时,请记住,插补标志应该是nearest以获得更好的结果。

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('home.jpg')
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()

下面是输入图像及其颜色直方图。 X轴显示S值,Y轴显示Hue值。

2D Histograms

在直方图中,您可以看到H = 100和S = 200附近的一些高值。它对应于天空的蓝色。 类似地,在H = 25和S = 100附近可以看到另一个峰值。它对应于宫殿的黄色。 您可以使用任何图像编辑工具(如GIMP)进行验证。


方法3:OpenCV样本样式 !!

OpenCV-Python2样本中有一个颜色直方图的示例代码。 如果你运行代码,你可以看到直方图显示相应的颜色。 或者只是输出一个颜色编码的直方图。 它的结果是非常好的(虽然你需要添加额外的一些行)。

在该代码中,作者在HSV中创建了一个颜色映射。 然后将其转换为BGR。 所得到的直方图图像与该颜色图相乘。 他还使用一些预处理步骤来删除小的孤立像素,导致良好的直方图。



直方图 - 4:直方图反投影

目标

In this chapter, we will learn about histogram backprojection.

理论

Numpy中的算法

1、首先,我们需要计算我们需要找到的对象(让它成为'M')和我们要搜索的图像(让它是'I')的颜色直方图。

import cv2
import numpy as np
from matplotlib import pyplot as plt

#roi is the object or region of object we need to find
roi = cv2.imread('rose_red.png')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)

#target is the image we search in
target = cv2.imread('rose.png')
hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)

# Find the histograms using calcHist. Can be done with np.histogram2d also
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] )

2、找到比率 R = \frac{M}{I}。 然后是backproject R,即使用R作为调色板,并创建一个新的图像与每个像素作为其相应的目标概率。 即B(x,y) = R[h(x,y),s(x,y)]其中h是色调,s是像素在(x,y)时的饱和度。 之后应用条件 B(x,y) = min[B(x,y), 1].

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

3.现在用圆盘卷积B = D \ast B,其中D是光盘内核。

disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
cv2.filter2D(B,-1,disc,B)
B = np.uint8(B)
cv2.normalize(B,B,0,255,cv2.NORM_MINMAX)

4、现在最大强度的位置给我们物体的位置。 如果我们期待图像中的一个区域,则为适当的值进行阈值提供了不错的结果。

ret,thresh = cv2.threshold(B,50,255,0)

OpenCV中的反投影

OpenCV提供内置函数cv2.calcBackProject()。 它的参数与cv2.calcHist()函数几乎相同。 其参数之一是直方图,它是对象的直方图,我们必须找到它。 此外,在传递给backproject函数之前, 对象直方图应该被归一化。 它返回概率图像。 然后我们使用光盘内核卷积图像并应用阈值。 下面是我的代码和输出:

import cv2
import numpy as np

'''
roi = cv2.imread('rose_red.png')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)

target = cv2.imread('rose.png')
hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)
'''

roi=cv2.imread('messi5.jpg')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
target=roi[300:340,10:100,:]
hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)

# calculating object histogram
roihist = cv2.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )

# normalize histogram and apply backprojection
cv2.normalize(roihist,roihist,0,255,cv2.NORM_MINMAX) # 目标对象归一化
dst = cv2.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)

# Now convolute with circular disc
disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
cv2.filter2D(dst,-1,disc,dst)

# threshold and binary AND
ret,thresh = cv2.threshold(dst,50,255,0)
thresh = cv2.merge((thresh,thresh,thresh))
res = cv2.bitwise_and(target,thresh)

res = np.vstack((target,thresh,res))
# cv2.imwrite('res.jpg',res)
cv2.imshow('res',res)
cv2.waitKey(0)
cv2.destroyAllWindows()


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值