边缘与轮廓¶
- 基于图像边缘提取或二值化的基础寻找对象轮廓
- 边缘提取的阈值会最终影响轮廓发现的结果
- 主要API有以下两个
findContours
发现轮廓drawContours
绘制轮廓
查找轮廓¶
处理的图像,轮廓列表,继承关系 = 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链逼近算法 |
绘制轮廓¶
cv.drawContours(图像,轮廓列表,轮廓索引-1则绘制所有,轮廓颜色,轮廓的宽度)
绘制外切圆¶
((x,y),radius) = cv.minEnclosingCircle(contour)
实现步骤:
- 读取图片
- 将图片转成一张灰色图片
- 对图片进行二值化处理
- 使用findContours查找轮廓
- 对轮廓进行处理
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()
网球案例¶
实现步骤:
- 读取图片
- 过滤出球的颜色
- 使用轮廓检测
- 找到球的中心点
- 展示信息
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()