本文转载http://blog.csdn.net/wc781708249/article/details/78317061,感谢博主的译文!
参考:
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,
-
- Short Way : use Matplotlib plotting functions
- Long Way : use OpenCV drawing functions
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()
Additional Resources
直方图 - 2:直方图均衡
目标
In this section,
- We will learn the concepts of histogram equalization and use it to improve the contrast of our images.
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](http://opencv-python-tutroals.readthedocs.io/en/latest/_images/histeq_numpy1.jpg)
您可以看到直方图位于较亮的区域。 我们需要全谱。 为此,我们需要一个转换函数,将更亮区域中的输入像素映射到全区域的输出像素。 那就是直方图均衡。
现在我们找到最小直方图值(不包括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()另一个重要特点是,即使图像是较暗的图像(而不是我们使用的更亮的图像),均衡后,我们将获得几乎相同的图像。 因此,它被用作“参考工具”,以使所有具有相同光照条件的图像。 这在许多情况下很有用。 例如,在面部识别中,在训练面部数据之前,脸部的图像被直方图均衡以使它们 全部具有相同的照明条件 。
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(对比度有限自适应直方图均衡)
我们刚刚看到的第一个直方图均衡,考虑了图像的全局对比度。 在很多情况下,这不是一个好主意。 例如,下图显示了输入图像及其全局直方图均衡后的结果。在直方图均衡后,背景对比度是改善的。 但是在两个图像中比较雕像的脸。 由于超亮度,我们失去了大部分信息。 这是因为它的直方图并不局限于特定区域,就像我们以前的情况一样(尝试绘制输入图像的直方图,你会得到更多的直觉)。
因此,为了解决这个问题,使用自适应直方图均衡。 在这种情况下,图像被划分为称为“瓦片”的小块(默认情况下,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
- Wikipedia page on Histogram Equalization
- Masked Arrays in Numpy
Also check these SOF questions regarding contrast adjustment:
- How can I adjust contrast in OpenCV in C?
- 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.calcHis t()。 对于颜色直方图,我们需要将图像从 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直方图
我们得到的结果是尺寸为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值。
在直方图中,您可以看到H = 100和S = 200附近的一些高值。它对应于天空的蓝色。 类似地,在H = 25和S = 100附近可以看到另一个峰值。它对应于宫殿的黄色。 您可以使用任何图像编辑工具(如GIMP)进行验证。
方法3:OpenCV样本样式 !!
OpenCV-Python2样本中有一个颜色直方图的示例代码 。 如果你运行代码,你可以看到直方图显示相应的颜色。 或者只是输出一个颜色编码的直方图。 它的结果是非常好的(虽然你需要添加额外的一些行)。在该代码中,作者在HSV中创建了一个颜色映射。 然后将其转换为BGR。 所得到的直方图图像与该颜色图相乘。 他还使用一些预处理步骤来删除小的孤立像素,导致良好的直方图。
直方图 - 4:直方图反投影
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、找到比率 。 然后是backproject R,即使用R作为调色板,并创建一个新的图像与每个像素作为其相应的目标概率。 即B(x,y) = R[h(x,y),s(x,y)]其中h是色调,s是像素在(x,y)时的饱和度。 之后应用条件
.
h,s,v = cv2.split(hsvt) B = R[h.ravel(),s.ravel()] B = np.minimum(B,1) B = B.reshape(hsvt.shape[:2])
3.现在用圆盘卷积,其中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()