文章目录
- 轮廓查找
- cv2.findContours
- cv2.drawContours
- 示例
- 对象测量
- cv2.contourArea
- cv2.arcLength
- cv2.boundingRect
- cv2.moments
- 示例
轮廓查找
cv2.findContours
在二值图像中查找轮廓
findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> contours, hierarchy
- image:寻找轮廓的图像;
- mode:表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口):
- cv2.RETR_EXTERNAL:表示只检测外轮廓
- cv2.RETR_LIST:检测的轮廓不建立等级关系
- cv2.RETR_CCOMP :建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
- cv2.RETR_TREE:建立一个等级树结构的轮廓。
- method:为轮廓的近似办法
- cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
- cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
- cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法
- CV_LINK_RUNS :通过连接为 1 的水平碎片使用完全不同的轮廓提取算法。仅有 CV_RETR_LIST 提取模式可以在本方法中应用
- offset:每一个轮廓点的偏移量. 当轮廓是从图像 ROI 中提取出来的时候,使用偏移量有用,因为可以从整个图像上下文来对轮廓做分析
- 返回值:
- contours:一个列表,每一项都是一个轮廓, 不会存储轮廓所有的点,只存储能描述轮廓的点
- hierarchy:一个ndarray, 元素数量和轮廓数量一样,每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数
cv2.drawContours
绘制轮廓轮廓或填充轮廓
drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]) -> image
- image:指明在哪幅图像上绘制轮廓;image为三通道才能显示轮廓
- contours:是轮廓本身,在Python中是一个list;
- contourIdx:指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。后面的参数很简单。
- color:线的颜色
- thickness表明轮廓线的宽度,如果是-1(cv2.FILLED),则为填充模式
示例
def canny(image):
"""canny边缘提取"""
blurred = cv.GaussianBlur(image, (3, 3), 0)
gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)
grad_x = cv.Sobel(gray, cv.CV_16SC1, 1, 0)
grad_y = cv.Sobel(gray, cv.CV_16SC1, 0, 1)
# image:要检测的图像,threshold1:阈值1(最小值),threshold2:阈值2(最大值),使用此参数进行明显的边缘检测,
# canny_output2 = cv.Canny(grad_x, grad_y, 30, 150)
canny_output1 = cv.Canny(gray, 50, 150) # 也可以直接传入gray
return canny_output1
def contours(image):
"""轮廓查找"""
binary = canny(image)
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for i, contour in enumerate(contours):
# 函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。
# 它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。
# 第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设 置为 -1 时绘制所有轮廓)。
# 接下来的参数是轮廓的颜色和厚度等。
cv.drawContours(image, contours, i, (0, 0, 255), 2) # 2为像素大小,-1时填充轮廓
print(i)
cv.imshow("detect contours", image)
def image_contour(image):
"""轮廓查找并描点"""
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU) # 图像二值化
print("threshold value: %s" % ret) # 输出阈值
# cv.imshow("binary image", binary)
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for i, contour in enumerate(contours):
cv.drawContours(image, contours, i, (0, 0, 255), 2) # 用红色线条画出轮廓
epsilon = 0.01 * cv.arcLength(contour, True)
approx = cv.approxPolyDP(contour, epsilon, True)
cv.drawContours(image, approx, -1, (255, 0, 0), 10)
cv.imshow("contour_approx", image)
结果:
注意:
- 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或者 Canny 边界检测
- 在Opencv4.0中 cv.findContours()的返回值以从三个变为二个
参考链接:
对象测量
cv2.contourArea
此函数利用格林公式计算轮廓的面积。对于具有自交点的轮廓,该函数几乎肯定会给出错误的结果。
contourArea(contour[, oriented]) -> retval
- contour:输入二维的向量。
- oriented:有方向的区域标志。
- true:此函数依赖轮廓的方向(顺时针或逆时针)返回一个已标记区域的值。
- false:默认值。意味着返回不带方向的绝对值
cv2.arcLength
计算轮廓周长或曲线长度
arcLength(curve, closed) -> retval
- curve:二维点的输入向量
- closed:指示曲线是否闭合的标志(闭合的(True))
cv2.boundingRect
用一个最小的矩形,把找到的形状包起来
boundingRect(array) -> retval
返回四个值,分别是x,y,w,h;
- x,y是矩阵左上点的坐标
- w,h是矩阵的宽和高
cv2.moments
计算多边形或栅格化形状的所有三阶矩
moments(array[, binaryImage]) -> retval
- array:输入数组,可以是光栅图像(单通道,8-bit或浮点型二维数组),或者是一个二维数组(1 X N或N X 1),二维数组类型为Point或Point2f
- binaryImage:默认值是false,如果为true,则所有非零的像素都会按值1对待,也就是说相当于对图像进行了二值化处理,阈值为1,此参数仅对图像有效
示例
def image_measure(image):
"""对象测量"""
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU) # 图像二值化
# print("threshold value: %s" % ret)
# cv.imshow("binary image", binary)
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for i, contour in enumerate(contours):
cv.drawContours(image, contours, i, (0, 255, 255), 1) # 用黄色线条画出轮廓
area = cv.contourArea(contour) # 计算轮廓面积
print("contour area:", area)
# 轮廓周长,第二参数可以用来指定对象的形状是闭合的(True),还是打开的(一条曲线)。
perimeter = cv.arcLength(contour, True)
print("contour perimeter:", perimeter)
x, y, w, h = cv.boundingRect(contour) # 用矩形框出轮廓
cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2) # 画出矩形
rate = min(w, h) / max(w, h) # 计算矩阵宽高比
print("rectangle rate", rate)
mm = cv.moments(contour) # 函数 cv2.moments() 会将计算得到的矩以一个字典的形式返回
print(mm)
# 计算出对象的重心
cx = mm['m10'] / mm['m00']
cy = mm['m01'] / mm['m00']
cv.circle(image, (np.int(cx), np.int(cy)), 2, (0, 255, 255), -1) # 用实心圆画出重心
cv.imshow("measure_object", image)
结果:
参考链接:
- Python OpenCV contourArea()函数
- opencv-python中 boundingRect(cnt)以及cv2.rectangle用法
- opencv学习(四十三)之图像的矩moments()