边缘(edge):不同区域的分界线。图像局部灰度显著变化的部分。一阶导数的局部极大值,二阶导数的过零点。
轮廓(contour):物体在场景中的完整边界。边缘的连接构成轮廓。
边缘和轮廓关系,网上有一个解释的比较清楚:
opencv中轮廓检测主要由cv2.findContours函数实现的。
cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
代码实现,可以看到最基本的轮廓检测过程如下
# -*- coding: cp936 -*-
import cv2
from matplotlib import pyplot as plt
# Step1. 读入图像
image_tmp = cv2.imread('test.jpg',0)
plt.subplot(221), plt.imshow(image_tmp,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]),plt.yticks([])
image_canny = cv2.Canny(image_tmp, 200, 300) # 边缘检测
plt.subplot(224), plt.imshow(image_canny,cmap = 'gray')
plt.title('Edge Image'),plt.xticks([]),plt.yticks([])
# Step2. 二值化
ret, thresh = cv2.threshold(image_tmp, 127, 255, cv2.THRESH_BINARY)
# Step3. 轮廓提取
image, contour, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Step4. 轮廓绘制
color = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
result = cv2.drawContours(color, contour, -1, (0,255,0), 2) #轮廓用绿色绘制
plt.subplot(223),plt.imshow(result)
plt.title('Contour Image'),plt.xticks([]),plt.yticks([])
plt.show()
左下是轮廓检测效果,右下是边缘检测效果,可以对比一下。
关于drawContours()函数,是用于轮廓的绘制或填充,可以参见https://www.cnblogs.com/gengyi/p/10295407.html
cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]) -> image
以上获得的是图像的连续轮廓,还可以绘制图像的边界框,最小矩形框(车牌识别会用到),最小闭圆,最小外包三角形
代码如下
# -*- coding: cp936 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt
# Step1. 读入图像
image_tmp = cv2.imread('test2.jpg',0)
plt.subplot(231), plt.imshow(image_tmp,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]),plt.yticks([])
'''
image_canny = cv2.Canny(image_tmp, 200, 300) # 边缘检测
plt.subplot(232), plt.imshow(image_canny,cmap = 'gray')
plt.title('Edge Image'),plt.xticks([]),plt.yticks([])
'''
# Step2. 二值化
ret, thresh = cv2.threshold(image_tmp, 127, 255, cv2.THRESH_BINARY)
# Step3. 轮廓提取
image, contour, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Step4. 轮廓绘制
color= cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
result = cv2.drawContours(color, contour, -1, (0,255,0), 2)
plt.subplot(232),plt.imshow(result)
plt.title('Contour Image'),plt.xticks([]),plt.yticks([])
color_rectangle = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
color_rectangle_min = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
color_circle = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
color_triangle = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
print len(contour)
for c in contour:
# 绘制边界框
x,y,w,h = cv2.boundingRect(c) # 将轮廓信息转化为(x,y)坐标,并返回宽和高
# rectangle有四个参数:要绘制的图像,左上角坐标,右下角坐标,颜色,轮廓宽度(这里为2)
result_rectangle=cv2.rectangle(color_rectangle, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 绘制最小矩形区域
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect) # 获得矩形四个角的坐标, 返回浮点型
box = np.int0(box) # 把浮点型转化为整型
result_rectangle_min=cv2.drawContours(color_rectangle_min, [box], 0, (0, 255, 0), 3)
# 绘制最小闭圆
(x, y), radius = cv2.minEnclosingCircle(c)
center = (int(x), int(y))
radius = int(radius)
result_circle=cv2.circle(color_circle, center, radius, (255, 0 , 0), 3)
# 绘制最小外包三角形
retval, points = cv2.minEnclosingTriangle(c)
for i in range(0, 3):
# 这里使用了一个小技巧, 从i 绘制到 (i + 1) % 3
result_triangle=cv2.line(color_triangle, tuple(points[i][0]), tuple(points[(i + 1) % 3][0]), (0, 0, 255), 2)
plt.subplot(233),plt.imshow(color_rectangle)
plt.title('Rectangle'),plt.xticks([]),plt.yticks([])
plt.subplot(234),plt.imshow(color_rectangle_min)
plt.title('Rectangle-min'),plt.xticks([]),plt.yticks([])
plt.subplot(235),plt.imshow(result_circle)
plt.title('Circle'),plt.xticks([]),plt.yticks([])
plt.subplot(236),plt.imshow(result_triangle)
plt.title('Triangle'),plt.xticks([]),plt.yticks([])
plt.show()
原始图像里有2个7边形,1个比较正,1个是斜的。可以更好的体会一下边界框和最小矩形的区别(右上和左下)。顺便打印了一下识别出的轮廓(contours )数量,结果有5个。