边缘与轮廓

  • 基于图像边缘提取或二值化的基础寻找对象轮廓
  • 边缘提取的阈值会最终影响轮廓发现的结果
  • 主要API有以下两个
  • findContours发现轮廓
  • drawContours绘制轮廓

查找轮廓

1
2
3
处理的图像,轮廓列表,继承关系 = cv.findContours(图像,轮廓检索模式,检索的方法)

# hierarchy[i][3],分别表示第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号
  • 轮廓检索模式
RETR_EXTERNAL 只检测最外层轮廓
RETR_LIST 提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
RETR_CCOMP 提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
RETR_TREE 提取所有轮廓并重新建立网状轮廓结构
  • 轮廓检索算法
CHAIN_APPROX_NONE 获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
CHAIN_APPROX_TC89_L1 Teh-Chinl链逼近算法
CHAIN_APPROX_TC89_KCOS Teh-Chinl链逼近算法

绘制轮廓

1
cv.drawContours(图像,轮廓列表,轮廓索引-1则绘制所有,轮廓颜色,轮廓的宽度)

绘制外切圆

1
((x,y),radius) = cv.minEnclosingCircle(contour)

实现步骤:

  1. 读取图片
  2. 将图片转成一张灰色图片
  3. 对图片进行二值化处理
  4. 使用findContours查找轮廓
  5. 对轮廓进行处理
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import cv2 as cv

def read_rgb_img(img_name):
    rgb_img = cv.imread(img_name,cv.IMREAD_COLOR)
    cv.imshow("rgb img",rgb_img)
    return rgb_img

def convert_rgb2gray(img):
    gray_img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)

    cv.imshow("gray img", gray_img)
    return gray_img

def convert_gray2binary(img):
    binary_img = cv.adaptiveThreshold(img,
                                      255,
                                      cv.ADAPTIVE_THRESH_GAUSSIAN_C,
                                      cv.THRESH_BINARY,5,2)
    # _,binary_img = cv.threshold(img,50,255,cv.THRESH_BINARY_INV)
    cv.imshow("binary img", binary_img)
    return binary_img

def getContours(img):
    _,contours,hierarchy = cv.findContours(img,cv.RETR_TREE,cv.CHAIN_APPROX_SIMPLE)
    print(contours,hierarchy)
    return contours


def draw_contours(img,contours):
    index = -1 # 所有的轮廓
    thickness = 2 # 轮廓的宽度
    color = (255,125,125) # 轮廓的颜色
    cv.drawContours(img,contours,index,color,thickness)
    cv.imshow('draw contours',img)


if __name__ == '__main__':
    img_name = "assets/shape0.jpg"

    rgb_img = read_rgb_img(img_name)
    gray_img = convert_rgb2gray(rgb_img)
    binary_imgage = convert_gray2binary(gray_img)
    contours = getContours(binary_imgage)
    draw_contours(rgb_img,contours)

    cv.waitKey(0)
    cv.destroyAllWindows()

网球案例

实现步骤:

  1. 读取图片
  2. 过滤出球的颜色
  3. 使用轮廓检测
  4. 找到球的中心点
  5. 展示信息
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import cv2 as cv
import numpy as np

def read_rgb_img(img_name):
    rgb_img = cv.imread(img_name,cv.IMREAD_COLOR)
    cv.imshow("rgb img",rgb_img)
    return rgb_img


def convert_rgb2gray(img):
    gray_img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    # 采用高斯滤波去掉噪点
    gray_img = cv.GaussianBlur(gray_img,(5,5),0)
    cv.imshow("gray img", gray_img)

    return gray_img

def convert_gray2binary(img):
    binary_img = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY_INV,5,2)
    cv.imshow("binary img", binary_img)
    return binary_img

def filter_tenis(img,lower_color,upper_color):
    hsv_img = cv.cvtColor(img,cv.COLOR_BGR2HSV)
    # 查找颜色
    mask_img = cv.inRange(hsv_img, lower_color, upper_color)
    cv.imshow("mask img",mask_img)
    return mask_img

def getContours(img):
    _,contours,hierarchy = cv.findContours(img,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
    print(contours,hierarchy)
    return contours


def process_tenis_contours(rgb_img,contours):
    black_img = np.zeros([rgb_img.shape[0],rgb_img.shape[1],3],np.uint8)

    for c in contours:
        # 计算面积
        area = cv.contourArea(c)
        # 该函数计算曲线长度或闭合轮廓周长。
        perimeter = cv.arcLength(c,True)
        # 获取最小的外切圆
        ((x,y),radius) = cv.minEnclosingCircle(c)

        # 绘制轮廓
        cv.drawContours(rgb_img,[c],-1,(150,250,150),2)
        cv.drawContours(black_img,[c],-1,(150,250,150),2)
        # 获取轮廓中心点
        # cx,cy = get_contour_center(c)
        # print(cx,cy)
        x = int(x)
        y = int(y)
        cv.circle(rgb_img,(x,y),int(radius),(0,0,255),2)
        cv.circle(black_img,(x,y),int(radius),(0,0,255),2)

        print("Area:{},primeter:{}".format(area,perimeter))

    print("number of contours:{}".format(len(contours)))
    cv.imshow("rgb img contours",rgb_img)
    cv.imshow("black img contours",black_img)

if __name__ == '__main__':
    img_name = "assets/tenis1.jpg"
    # 定义范围
    lower_color = (30, 120, 130)
    upper_color = (60, 255, 255)

    rgb_img = read_rgb_img(img_name)
    binary_imgage = filter_tenis(rgb_img,lower_color,upper_color)

    contours = getContours(binary_imgage)
    process_tenis_contours(rgb_img,contours)

    cv.waitKey(0)
    cv.destroyAllWindows()