前言
很多情况需要对物体进行检测,常规的方法也有很多种。但是检测出来的边缘一般都是非常多,结果也是非常杂乱的,显然这种结果不是我们想要的。
如果颜色相较于背景非常鲜艳的可以调节hsv阈值再进行检测,如果是一直在运动的物体可以通过帧差法进行物体检测,还有很多高深的算法也可以进行物体检测。
但在这里我介绍一种最简单,也是最实用的方法——轮廓检测法。
轮廓检测法
轮廓检测也是图像处理中经常用到的。OpenCV-Python接口中使用cv2.findContours()函数来查找检测物体的轮廓。
contours, hierarchy = cv2.findContours(image,mode,method)
- image:输入图像
- mode:轮廓的模式。cv2.RETR_EXTERNAL只检测外轮廓;cv2.RETR_LIST检测的轮廓不建立等级关系;cv2.RETR_CCOMP建立两个等级的轮廓,上一层为外边界,内层为内孔的边界。如果内孔内还有连通物体,则这个物体的边界也在顶层;cv2.RETR_TREE建立一个等级树结构的轮廓。
- method:轮廓的近似方法。cv2.CHAIN_APPROX_NOME存储所有的轮廓点,相邻的两个点的像素位置差不超过1;cv2.CHAIN_APPROX_SIMPLE压缩水平方向、垂直方向、对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需要4个点来保存轮廓信息;cv2.CHAIN_APPROX_TC89_L1,cv2.CV_CHAIN_APPROX_TC89_KCOS
- contours:返回的轮廓
- hierarchy:每条轮廓对应的属性
注意:cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),所以读取的图像要先转成灰度的,再转成二值图。
轮廓的绘制
cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
-
第一个参数是指明在哪幅图像上绘制轮廓;
-
第二个参数是轮廓本身,在Python中是一个list。
-
第三个参数指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。后面的参数很简单。其中color:线的颜色(0,0,255)表示红色;(255,0,0)表示蓝色;thickness表明轮廓线的宽度,如果是-1(cv2.FILLED),则为填充模式。
最小外接矩形
x, y, w, h = cv2.boundingRect(i)
x, y 分别是轮廓的左上点坐标,w, h分别是轮廓的宽和高。
我们获取了物体的轮廓可以利用轮廓的特性进行筛选轮廓,例如我的识别目标是一个圆形,那么它的外接矩形一定是个近似正方形的矩形。
所以可以如下述代码进行筛选:
if w - 25 < h < w + 25:print("轮廓面积:", area)cv2.rectangle(img, (x, y + h), (x + w, y), (0, 0, 255))break
获取轮廓的面积
area = cv2.contourArea(i)
area则为轮廓的面积
测试效果
最终代码
对下述代码修改筛选条件达到效果最佳。
# 轮廓提取
import cv2
# 转二进制图像
def ToBinray():global imgray, binary# 1、灰度图imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# cv2.imshow('imgray', imgray)# 2、二进制图像ret, binary = cv2.threshold(imgray, 70, 255, 0)# cv2.imshow('binary', binary)# 提取轮廓
def GetGontours():contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)# 获取轮廓面积for i in contours:area = cv2.contourArea(i)# 筛选轮廓面积if area < 800 or area > 2000: continuex, y, w, h = cv2.boundingRect(i)if w - 25 < h < w + 25:print("轮廓面积:", area)cv2.rectangle(img, (x, y + h), (x + w, y), (0, 0, 255))break# 打开摄像头
cap = cv2.VideoCapture(0)
# 检查摄像头是否成功打开
if not cap.isOpened():print("Error: Could not open camera.")exit()while True:# 读取视频帧ret, img = cap.read()# 检查是否成功读取帧if not ret:print("Error: Could not read frame.")breakToBinray()GetGontours()# 显示当前帧cv2.imshow('Camera', img)# 按下Esc键退出循环if cv2.waitKey(1) == 27:break
# 释放摄像头并关闭窗口
cap.release()
cv2.destroyAllWindows()