凸缺陷/凸包检测
当前内容其实和毛星云的凸包检测内容有点重复了,只是叫法不一样,我这边也蛮整理一下,当前文章的代码会比毛星云的代码简洁一点。
PDF原文下载地址如下:
OpenCV-Python-Tutorial-中文版.pdf(P116 轮廓的性质)
PDF在线预览
当前系列所有demo下载地址:
https://github.com/GaoRenBao/OpenCv4-Demo
不同编程语言对应的OpenCv版本以及开发环境信息如下:
语言 | OpenCv版本 | IDE |
C# | OpenCvSharp4.4.8.0.20230708 | Visual Studio 2022 |
C++ | OpenCv-4.5.5-vc14_vc15 | Visual Studio 2022 |
Python | OpenCv-Python (4.6.0.66) | PyCharm Community Edition 2022.1.3 |
前面我们已经学习了轮廊的凸包,对象上的任何凹陷都被成为凸缺陷。
OpenCV中有一个函数cv.convexityDefect()可以帮助我们找到凸缺陷。
函数调用如下:
hull=cv2.convexHull(cnt,returnPoints=False)
defects=cv2.convexityDefects(cnt,hull)
注意:如果要查找凸缺陷,在使用函数cv2.convexHull找凸包时,参数returnPoints一定要是False。
它会返回一个数组,其中每一行包含的值是[起点,终点,最远的点,到最远点的近似距离1。我们可以在一张图上显示它。我们将起点和终点用一条绿线连接,在最远点画一个圆圈,要记住的是返回结果的前三个值是轮廓点的索引所以我们还要到轮廓点中去找它们。
测试原图:

运行效果:

C#版本运行代码如下:
using OpenCvSharp;
using System;
namespace ConsoleApp
{
internal class Program
{
static void Main(string[] args)
{
demo1();
demo2();
Cv2.WaitKey(0);
}
static void demo1()
{
Mat img = Cv2.ImRead("../../../images/star3.jpg");
Mat img_gray = new Mat();
Cv2.CvtColor(img, img_gray, ColorConversionCodes.BGR2GRAY);
Mat thresh = new Mat();
Cv2.Threshold(img_gray, thresh, 127, 255, ThresholdTypes.Binary);
Mat[] contours = new Mat[] { };
Cv2.FindContours(thresh, out contours, new Mat(), RetrievalModes.CComp, ContourApproximationModes.ApproxNone, null);
Mat cnt = contours[0];
// 函数 cv2.isContourConvex() 可以可以用来检测一个曲线是不是凸的。它只能返回 True 或 False。
bool k = Cv2.IsContourConvex(cnt);
Console.WriteLine($"K={k}");
// 函数 cv2.convexHull() 可以用来检测一个曲线的凸包
Mat hull = new Mat();
Cv2.ConvexHull(cnt, hull, false);
// 创建结果图像并绘制轮廓和凸包
Cv2.DrawContours(img, contours, 0, new Scalar(0, 0, 255), 2); // 绘制原始轮廓
Cv2.DrawContours(img, new Mat[] { hull }, 0, new Scalar(0, 255, 0), 2); // 绘制凸包
Cv2.ImShow("img1", img);
}
static void demo2()
{
Mat img = Cv2.ImRead("../../../images/star3.jpg");
Mat img_gray = new Mat();
Cv2.CvtColor(img, img_gray, ColorConversionCodes.BGR2GRAY);
Mat thresh = new Mat();
Cv2.Threshold(img_gray, thresh, 127, 255, ThresholdTypes.Binary);
Point[][] contours = new Point[][] { };
HierarchyIndex[] hierarcy;
Cv2.FindContours(thresh, out contours, out hierarcy, RetrievalModes.CComp, ContourApproximationModes.ApproxNone, null);
Point[] cnt = contours[0];
// 函数 cv2.isContourConvex() 可以可以用来检测一个曲线是不是凸的。它只能返回 True 或 False。
bool k = Cv2.IsContourConvex(cnt);
Console.WriteLine($"K={k}");
// 函数 cv2.convexHull() 可以用来检测一个曲线的凸包
int[] hull = Cv2.ConvexHullIndices(cnt, false);
// 将所有凸包提取出来
//前三个值得含义分别为:凸缺陷的起始点,凸缺陷的终点,凸缺陷的最深点(即边缘点到凸包距离最大点)。
var defects = Cv2.ConvexityDefects(cnt, hull);
for (int j = 0; j < defects.Length; j++)
{
Point start = cnt[defects[j].Item0];
Point end = cnt[defects[j].Item1];
Point far = cnt[defects[j].Item2];
//Point fart = cnt[defects[j].Item3];
Cv2.Line(img, start, end, new Scalar(0, 255, 0), 2);
Cv2.Circle(img, far, 5, new Scalar(0, 0, 255), -1);
}
Cv2.ImShow("img2", img);
}
}
}
C++版本运行代码如下:
#include <opencv2/opencv.hpp>
#include <opencv2/cvconfig.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <string>
#include <cmath>
#include <iostream>
using namespace cv;
using namespace std;
void demo1()
{
Mat img = cv::imread("../images/star3.jpg");
Mat img_gray;
cv::cvtColor(img, img_gray, COLOR_BGR2GRAY);
Mat thresh;
cv::threshold(img_gray, thresh, 127, 255, THRESH_BINARY);
vector<Mat> contours;
Mat hierarcy;
cv::findContours(thresh, contours, hierarcy, RETR_CCOMP, CHAIN_APPROX_NONE, Point(0, 0));
Mat cnt = contours[0];
// 函数 cv::isContourConvex() 可以可以用来检测一个曲线是不是凸的。它只能返回 True 或 False。
bool k = cv::isContourConvex(cnt);
cout << "K=" << k << endl;
// 函数 cv::convexHull() 可以用来检测一个曲线的凸包
Mat hull;
cv::convexHull(cnt, hull, false);
// 创建结果图像并绘制轮廓和凸包
cv::drawContours(img, contours, 0, Scalar(0, 0, 255), 2); // 绘制原始轮廓
vector<Mat> hulls;
hulls.push_back(hull);
cv::drawContours(img, hulls, 0, Scalar(0, 255, 0), 2); // 绘制凸包
cv::imshow("img1", img);
}
void demo2()
{
Mat img = cv::imread("../images/star3.jpg");
Mat img_gray;
cv::cvtColor(img, img_gray, COLOR_BGR2GRAY);
Mat thresh;
cv::threshold(img_gray, thresh, 127, 255, THRESH_BINARY);
vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
cv::findContours(thresh, contours, hierarcy, RETR_CCOMP, CHAIN_APPROX_NONE, Point(0, 0));
vector<Point> cnt = contours[0];
// 函数 cv::isContourConvex() 可以可以用来检测一个曲线是不是凸的。它只能返回 True 或 False。
bool k = cv::isContourConvex(cnt);
cout << "K=" << k << endl;
// 函数 cv::convexHull() 可以用来检测一个曲线的凸包
vector<int> hull;
convexHull(Mat(cnt), hull, false);
// 创建结果图像并绘制轮廓和凸包
vector<Vec4i> defects;
cv::convexityDefects(cnt, hull, defects);
for (int j = 0; j < defects.size(); j++)
{
Point start = cnt[defects[j][0]];
Point end = cnt[defects[j][1]];
Point far = cnt[defects[j][2]];
//Point fart = cnt[defects[j][3]];
cv::line(img, start, end, Scalar(0, 255, 0), 2);
cv::circle(img, far, 5, Scalar(0, 0, 255), -1);
}
cv::imshow("img2", img);
}
int main()
{
demo1();
demo2();
cv::waitKey(0);
}
Python版本运行代码如下:
import cv2
img = cv2.imread('../images/star3.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]
# 函数 cv2.isContourConvex() 可以可以用来检测一个曲线是不是凸的。它只能返回 True 或 False。
k = cv2.isContourConvex(cnt)
print("K=", k)
# 函数 cv2.convexHull() 可以用来检测一个曲线的凸包
hull = cv2.convexHull(cnt, returnPoints=False)
# 将所有凸包提取出来
defects = cv2.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])
cv2.line(img, start, end, [0, 255, 0], 2)
cv2.circle(img, far, 5, [0, 0, 255], -1)
cv2.imshow('img', img)
cv2.imwrite('out.jpg', img)
cv2.waitKey(0)
cv2.destroyAllWindows()