OpenVINOTM,给你看得见的未来!>>
电子产品世界 » 论坛首页 » 企业专区 » OpenVINO生态社区 » 【原创】OpenCV-Python系列之轮廓特征中阶(二十九)

共3条 1/1 1 跳转至

【原创】OpenCV-Python系列之轮廓特征中阶(二十九)

高工
2020-07-11 21:06:51    评分

OpenCV-Python系列之轮廓特征中阶

本次我们将讨论OpenCV中图像轮廓的另一些特征,它们将非常有用。

边界矩形

有两类边界矩形

直边界矩形—boundingRect()

一个直矩形(就是没有旋转的矩形)。它不会考虑对象是否旋转。所以边界矩形的面积不是最小的。可以使用函数 cv2.boundingRect() 查找得到,我们来看函数原型:

x,y,w,h = cv2.boundingRect(cnt)

(xy)为矩形左上角的坐标,(wh)是矩形的宽和高,通常情况下,cnt代表识别的轮廓。

之后我们利用cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)画出矩形、

参数解释

第一个参数:img是原图

第二个参数:(xy)是矩阵的左上点坐标

第三个参数:(x+wy+h)是矩阵的右下点坐标

第四个参数:(0,255,0)是画线对应的rgb颜色

第五个参数:2是所画的线的宽度

我们来看代码:

import cv2

# 读取图片并转至灰度模式
img = cv2.imread("tubao.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化,取阈值为235
ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)

# 寻找图像中的轮廓
contours, hierarchy = cv2.findContours(thresh, 2, 1)

cnt = contours[0]
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)

cv2.imshow('finger', img)
cv2.waitKey()

                                               image.png

可以看到一个外接矩形,但是它不是最小的外接矩形,接下来我们讨论最小外接矩形。

旋转的边界矩形

这个边界矩形是面积最小的,因为它考虑了对象的旋转。

用到的函数为

cv2.minAreaRect(cnt)

在这里的cnt仍然跟上面的相同,该函数返回的是一个 Box2D 结构:

Box2D结构 rect:(最小外接矩形的中心(xy),(宽度,高度),旋转角度),但是要绘制此矩形,我们需要矩形的4个角。 它是通过函cv2.boxPoints()获得的。

我们来看代码:

import cv2
import numpy as np

# 读取图片并转至灰度模式
img = cv2.imread("tubao.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化,取阈值为235
ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)

# 寻找图像中的轮廓
contours, hierarchy = cv2.findContours(thresh, 2, 1)

cnt = contours[0]

rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img,[box],0,(0,0,255),2)

cv2.imshow('finger', img)
cv2.waitKey()

image.png

可以看到,这里得出的结果是最小外接矩形。

最小外接圆

跟之前一样,轮廓可以外接矩形,同时也可以外接圆形。接下来,我们使用函数cv.minEnclosingCircle()找到对象的外接圆。 它是一个以最小面积完全覆盖对象的圆圈。

函数原型:

      center, radius     =     cv.minEnclosingCircle(   points    )

image.png

直接来看代码:

import cv2
import numpy as np

# 读取图片并转至灰度模式
img = cv2.imread("tubao.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化,取阈值为235
ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)

# 寻找图像中的轮廓
contours, hierarchy = cv2.findContours(thresh, 2, 1)

cnt = contours[0]

(x, y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
cv2.circle(img, center, radius, (0, 255, 0), 2)

cv2.imshow('finger', img)
cv2.waitKey()

image.png

椭圆拟合

轮廓同样也可以进行椭圆拟合,函数原型:

retval=cv.fitEllipse(points)

输入参数跟之前的外界矩形是一样的,就不一一详述了。代码:

import cv2
import numpy as np

# 读取图片并转至灰度模式
img = cv2.imread("tubao.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化,取阈值为235
ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)

# 寻找图像中的轮廓
contours, hierarchy = cv2.findContours(thresh, 2, 1)

cnt = contours[0]

ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img,ellipse,(0,255,0),2)

cv2.imshow('finger', img)
cv2.waitKey()

image.png

直线拟合

同样,我们可以将一条直线拟合到一组点。下图包含一组白点,我们可以近似一条直线。函数原型:

output = cv2.fitLine(InputArray  points, distType, param, reps, aeps)

InputArray Points: 待拟合的直线的集合,必须是矩阵形式;

distType: 距离类型。fitline为距离最小化函数,拟合直线时,要使输入点到拟合直线的距离和最小化。这里的距离的类型有以下几种:

cv2.DIST_USER : User defined distance

cv2.DIST_L1: distance = |x1-x2| + |y1-y2|

cv2.DIST_L2: 欧式距离,此时与最小二乘法相同

cv2.DIST_C:distance = max(|x1-x2|,|y1-y2|)

cv2.DIST_L12:L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1))

cv2.DIST_FAIR:distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998

cv2.DIST_WELSCH: distance = c2/2(1-exp(-(x/c)2)), c = 2.9846

cv2.DIST_HUBER:distance = |x|<c |x^2/2 : c(|x|-c/2), c=1.345

param 距离参数,跟所选的距离类型有关,值可以设置为0

reps, aeps 5/6个参数用于表示拟合直线所需要的径向和角度精度,通常情况下两个值均被设定为1e-2

output 对于二维直线,输出output4维,前两维代表拟合出的直线的方向,后两位代表直线上的一点。(即通常说的点斜式直线)。

代码:

import cv2
import numpy as np

# 读取图片并转至灰度模式
img = cv2.imread("tubao.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化,取阈值为235
ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)

# 寻找图像中的轮廓
contours, hierarchy = cv2.findContours(thresh, 2, 1)

cnt = contours[0]
rows,cols = img.shape[:2]

[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)

cv2.imshow('finger', img)
cv2.waitKey()

image.png

现在我们可以来一个实战部分,用椭圆拟合来进行人眼识别之后标注出来,首先我们需要安装dlib库,这种一个人脸特征检测库,接下来我们设置特征检测器,dlib有已经训练的好的需要下载,也可以自己根据需要训练:

下载链接:http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2

下载完之后解压,将路径放到根目录里面,当然了,下载是需要科学上网的,否则下载速度非常慢。

我们使用图片:

image.png

代码:

import cv2
 import dlib
 import numpy as np
 
 detector = dlib.get_frontal_face_detector()
 landmark_predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
 
 img = cv2.imread("min.jpg")
 faces = detector(img,1)
 left_eye = []
 right_eye = []
 if ( len(faces) > 0):
     for k,d in enumerate(faces):
         shape = landmark_predictor(img,d)
         for i in range(36,42):
             right_eye.append([shape.part(i).x,shape.part(i).y])
         for i in range(42,48):
             left_eye.append([shape.part(i).x,shape.part(i).y])
 
 
 
 
 
 ellipse_left = cv2.fitEllipse(np.array(left_eye))
 ellipse_right = cv2.fitEllipse(np.array(right_eye))
 cv2.ellipse(img, ellipse_left, (0,255,0), 1)
 cv2.ellipse(img, ellipse_right, (0,255,0), 1)
 
 ellipse_left
 ((275.1310119628906, 197.24081420898438),
  (13.491097450256348, 47.203433990478516),
  84.19256591796875)
 
 
 center = ellipse_left[0]
 size = ellipse_left[1]
 angle = ellipse_left[2]
 
 
 
 cv2.imshow('PIC',img)
 cv2.waitKey(0)

image.png

可以看到,人眼已经识别出来,并用椭圆进行了拟合,是不是很有趣。

本次我们主要讨论轮廓的外界图形,事实上它们都是非常有用的,下次我们将讨论轮廓特征的一些高阶方法。




工程师
2020-07-11 21:11:44    评分
2楼

楼主这个系列非常棒


助工
2020-07-11 21:16:08    评分
3楼

学到了


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]