绘制轮廓
函数 cv2.findContours() 有三个参数,第一个是输入图像,第二个是 轮廓检索模式,第三个是轮廓近似方法。
im = cv2.imread('img/chess.jpg')
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,30,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
#绘制所有轮廓,第一个参数是原始图像,第二个参数是轮廓,
#第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设 置为 -1 时绘制所有轮廓)。
#接下来的参数是轮廓的颜色和厚度。
img = cv2.drawContours(im, contours, -1, (0,255,0), 1)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
直方图
通过直方图你可以对整幅图像的灰度分布有一个整体的了解。直方图的 x 轴是灰度值(0 到 255),y轴是图片中具有同一个灰度值的点的数目。 直方图其实就是对图像的另一种解释。以下图为例,通过直方图我们可以对图像的对比度,亮度,灰度分布等有一个直观的认识。
首先,了解一下直方图的相关术语
BINS:上面的直方图显示了每个灰度值对应的像素数。如果像素值为 0 到 255,你就需要 256 个数来显示上面的直方图。但是,如果你不需要知道 每一个像素值的像素点数目的,而只希望知道两个像素值之间的像素点数目怎 么办呢?举例来说,我们想知道像素值在 0 到 15 之间的像素点的数目,接着 是 16 到 31,....,240 到 255。我们只需要 16 个值来绘制直方图。你只需要把原来的 256 个值等分成 16 小组,取每组的总和。而这里的每一个小组就被成为 BIN。在 OpenCV 的文档中用 histSize 表示 BINS。
DIMS:表示我们收集数据的参数数目。在本例中,我们对收集到的数据只考虑一件事:灰度值。所以这里就是 1。
RANGE:就是要统计的灰度值范围,一般来说为 [0,255],也就是说所有的灰度值
函数 cv2.calcHist 可以帮助我们统计一幅图像的直方图。
img = cv2.imread('img/cat1.jpg',0)
hist = cv2.calcHist([img],[0],None,[256],[0,256])
'''
1. images: 原图像(图像格式为 uint8 或 float32)。当传入函数时应该 用中括号 [] 括起来,例如:[img]。
2. channels: 同样需要用中括号括起来,它会告诉函数我们要统计那幅图 像的直方图。如果输入图像是灰度图,
它的值就是 [0];如果是彩色图像 的话,传入的参数可以是 [0],[1],[2] 它们分别对应着通道 B,G,R。
3. mask: 掩模图像。要统计整幅图像的直方图就把它设为 None。但是如 果你想统计图像某一部分的直方图的话,
你就需要制作一个掩模图像,并 使用它。(后边有例子)
4. histSize:BIN 的数目。也应该用中括号括起来,例如:[256]。
5. ranges: 像素值范围,通常为 [0,256]
'''
img = cv2.imread('img/cat1.jpg',0)
plt.hist(img.ravel(),256,[0,256]);
plt.show()
或者你可以使用 matplotlib 的绘图功能,这在同时绘制多通道(BGR)的直方图
img = cv2.imread('img/cat1.jpg')
color = ('b','g','r')
#对一个列表或数组既要遍历索引又要遍历元素时使用内置enumerrate函数会有更加直接,
#enumerate会将数组或列表组成一个索引序列。使我们再获取索引和索引内容的时候更加方便
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.show()
直方图均衡化
想象一下如果一副图像中的大多是像素点的像素值都集中在一个像素值范 围之内会怎样呢?例如,如果一幅图片整体很亮,那所有的像素值应该都会很高。但是一副高质量的图像的像素值分布应该很广泛。所以你应该把它的直方图做一个横向拉伸(如下图),这就是直方图均衡化要做的事情。通常情况下这 种操作会改善图像的对比度。
OpenCV 中的直方图均衡化函数为 cv2.equalizeHist()。这个函数的输入图片是一副灰度图像,输出结果是直方图均衡化之后的图像。
img = cv2.imread('img/dog.JPEG',0)
equ = cv2.equalizeHist(img)
res = np.hstack((img,equ))
cv2.imwrite('res.png',res)
我们在上边做的直方图均衡化会改变整个图像的对比度,但是在很多情况下,这样做的效果并不好,因为会导致部分区域亮度过高而丢失信息。造成这种结果的根本原因在于这幅图像的直方图并不是集中在某一个区域(试着画出它的直方图,你就明白了)。
为了解决这个问题,我们需要使用自适应的直方图均衡化。这种情况下,整幅图像会被分成很多小块,这些小块被称为“tiles”(在 OpenCV 中 tiles 的 大小默认是 8x8),然后再对每一个小块分别进行直方图均衡化(跟前面类似)。 所以在每一个的区域中,直方图会集中在某一个小的区域中(除非有噪声干 扰)。如果有噪声的话,噪声会被放大。为了避免这种情况的出现要使用对比度限制。对于每个小块来说,如果直方图中的 bin 超过对比度的上限的话,就把 其中的像素点均匀分散到其他 bins 中,然后在进行直方图均衡化。最后,为了去除每一个小块之间“人造的”(由于算法造成)边界,再使用双线性差值,对小块进行缝合。
img = cv2.imread('img/dog.JPEG',0)
clahe = cv2.createCLAHE(clipLimit=4.0, tileGridSize=(8,8)) #clipLimit颜色对比度的阈值
cl1 = clahe.apply(img)
cv2.imshow("img", cl1)
cv2.waitKey(0)
cv2.destroyAllWindows()
模板匹配
模板匹配是用来在一副大图中搜寻查找模版图像位置的方法。OpenCV 为 我们提供了函数:cv2.matchTemplate()。和 2D 卷积一样,它也是用模 板图像在输入图像(大图)上滑动,并在每一个位置对模板图像和与其对应的 输入图像的子区域进行比较。OpenCV 提供了几种不同的比较方法(细节请看 文档)。返回的结果是一个灰度图像,每一个像素值表示了此区域与模板的匹配 程度。
如果输入图像的大小是(WxH),模板的大小是(wxh),输出的结果 的大小就是(W-w+1,H-h+1)。当你得到这幅图之后,就可以使用函数 cv2.minMaxLoc() 来找到其中的最小值和最大值的位置了。第一个值为矩形左上角的点(位置),(w,h)为 moban 模板矩形的宽和高。这个矩形就是找到的模板区域了。注意:如果你使用的比较方法是 cv2.TM_SQDIFF,最小值对应的位置才是匹 配的区域。
img = cv2.imread('img/cat1.jpg',0)
img2 = img.copy()
template = cv2.imread('img/cathead.png',0)
w, h = template.shape[::-1]
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR', 'cv2.TM_CCORR_NORMED',
'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
for meth in methods:
img = img2.copy()
method = eval(meth)
res = cv2.matchTemplate(img,template,method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img,top_left, bottom_right, 255, 2)
plt.subplot(121),plt.imshow(res,cmap = 'gray')
plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img,cmap = 'gray')
plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()