OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
轮廓:更多函数

上一个教程: 轮廓属性
下一个教程: 轮廓层级

目标

在本章中,我们将学习

  • 凸缺陷以及如何找到它们。
  • 找到点到多边形的最短距离
  • 匹配不同的形状

理论和代码

1. 凸缺陷

在关于轮廓的第二章中,我们了解了什么是凸包。物体与该凸包的任何偏差都可以被认为是凸缺陷。

OpenCV 提供了一个现成的函数来查找它,cv.convexityDefects()。一个基本的函数调用如下所示

hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)
void convexHull(InputArray points, OutputArray hull, bool clockwise=false, bool returnPoints=true)
Finds the convex hull of a point set.
void convexityDefects(InputArray contour, InputArray convexhull, OutputArray convexityDefects)
Finds the convexity defects of a contour.
注意
请记住,为了找到凸缺陷,我们在查找凸包时必须传递 returnPoints = False。

它返回一个数组,其中每行包含以下值 - [ 起始点,结束点,最远点,到最远点的近似距离 ]。我们可以使用图像来可视化它。我们画一条连接起始点和结束点的线,然后在最远点画一个圆。请记住,返回的前三个值是 cnt 的索引。所以我们必须从 cnt 中取出这些值。

import cv2 as cv
import numpy as np
img = cv.imread('star.jpg')
assert img is not None, "无法读取文件,请检查 os.path.exists()"
img_gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,thresh = cv.threshold(img_gray, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt = contours[0]
hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)
for i in range(defects.shape[0])
s,e,f,d = defects[i,0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
cv.line(img,start,end,[0,255,0],2)
cv.circle(img,far,5,[0,0,255],-1)
cv.imshow('img',img)
void imshow(const String &winname, InputArray mat)
在指定窗口中显示图像。
int waitKey(int delay=0)
等待按键按下。
void destroyAllWindows()
销毁所有HighGUI窗口。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件加载图像。
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
将图像从一个颜色空间转换为另一个颜色空间。
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
绘制连接两点的线段。
void circle(InputOutputArray img, Point center, int radius, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
Draws a circle.
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
对每个数组元素应用固定级别的阈值。
void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
在二值图像中查找轮廓。

并查看结果

image

2. 点多边形测试

此函数查找图像中的点和轮廓之间的最短距离。它返回距离,当点在轮廓外部时为负,当点在轮廓内部时为正,如果点在轮廓上则为零。

例如,我们可以按如下方式检查点 (50,50)

dist = cv.pointPolygonTest(cnt,(50,50),True)
double pointPolygonTest(InputArray contour, Point2f pt, bool measureDist)
执行点在轮廓内的测试。

在该函数中,第三个参数是 measureDist。如果为 True,则查找带符号的距离。如果为 False,则查找点是在轮廓内部、外部还是在轮廓上(它分别返回 +1、-1、0)。

注意
如果您不想查找距离,请确保第三个参数为 False,因为这是一个耗时的过程。因此,将其设置为 False 可以提高大约 2-3 倍的速度。

3. 匹配形状

OpenCV 提供了一个函数 cv.matchShapes(),它使我们能够比较两个形状或两个轮廓,并返回一个显示相似性的度量。结果越低,匹配越好。它是基于 hu 矩值计算的。不同的测量方法在文档中进行了解释。

import cv2 as cv
import numpy as np
img1 = cv.imread('star.jpg', cv.IMREAD_GRAYSCALE)
img2 = cv.imread('star2.jpg', cv.IMREAD_GRAYSCALE)
assert img1 is not None, "无法读取文件,请检查 os.path.exists()"
assert img2 is not None, "无法读取文件,请检查 os.path.exists()"
ret, thresh = cv.threshold(img1, 127, 255,0)
ret, thresh2 = cv.threshold(img2, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt1 = contours[0]
contours,hierarchy = cv.findContours(thresh2,2,1)
cnt2 = contours[0]
ret = cv.matchShapes(cnt1,cnt2,1,0.0)
print( ret )
double matchShapes(InputArray contour1, InputArray contour2, int method, double parameter)
比较两个形状。

我尝试将形状与下面给出的不同形状进行匹配

image

我得到以下结果

  • 将图像 A 与自身匹配 = 0.0
  • 将图像 A 与图像 B 匹配 = 0.001946
  • 将图像 A 与图像 C 匹配 = 0.326911

看,即使图像旋转也不会对此比较产生太大影响。

注意
Hu-Moments 是七个对平移、旋转和缩放不变的矩。第七个是倾斜不变的。可以使用 cv.HuMoments() 函数找到这些值。

练习

  1. 查看 cv.pointPolygonTest() 的文档,您可以在红色和蓝色中找到一张不错的图像。它表示所有像素到其上的白色曲线的距离。曲线内的所有像素都根据距离显示为蓝色。类似地,外部点显示为红色。轮廓边缘用白色标记。所以问题很简单。编写代码来创建距离的这种表示形式。
  2. 使用 cv.matchShapes() 比较数字或字母的图像。(这将是朝着 OCR 迈出的简单一步)