赞
踩
在图像中发现和分析形式是解决大多数计算机视觉问题的技巧之一,获取轮廓是其中之一。对于新手来说,我会将轮廓描述为“仅仅是一条连接所有位于形状边缘上的点的曲线。”
假设我有下面这张手的图像,手的轮廓由绿线表示。红点代表我们将连接起来形成轮廓曲线的点。
我对轮廓的高级数学课程记忆犹新。然而,由于老师从未强调过轮廓在现实世界中的应用,所以很难理解这个主题的重要性。今天,我发现它在计算机视觉中的重要性。
什么是凸包?
一个没有大于180度的内角的物品被称为凸形的。非凸形或凹形是指不是凸形的形状。一个物体的外部或形状被称为外壳。
因此,一个形状或一组点的凸包是紧密围绕这些点或形状的凸形边界。
用外行话来说,一个物体的凸包是能够完全环绕或包裹该物体(或该物体的轮廓)的最小边界。
可以使用多种方法找到凸包。以下是一些最常见的算法及其相关的时间复杂度。输入点的数量为n,而外壳上的点的数量为h。
使用OpenCV实现凸包
src = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
将图像转换为灰度(在读取图像时已经完成)。
通过应用任何模糊算法从图像中去除噪声(这里我使用了高斯模糊)。
然后将图像阈值化,使其成为二进制形式。
cv::GaussianBlur(src, src, cv::Size(3,3), 0); // 应用3x3核的高斯模糊
ShowImg("Image After Applying Blur", src);
const int max_thresh = 255;
const std::string source_window = "Canny ";
cv::createTrackbar("Canny thresh:", source_window, &thresh, max_thresh, thresh_callback);
thresh_callback(0, 0);
cv::waitKey();
return 0;
}
void thresh_callback(int, void*) {
cv::Mat canny_output;
cv::Canny(src, canny_output, thresh, thresh*2);
....
接下来,我们使用OpenCV的findContour
函数找到每个图像周围的轮廓。
如果你是新手,你可能会想知道为什么我们不只是使用边缘检测。边缘检测只会提供边缘的位置。
然而,我们对边缘是如何相互连接的感到好奇。findContour
找到连接并返回构成轮廓的点列表。
std::vector<std::vector<cv::Point>> contours;
cv::findContours(canny_output, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
....
使用convexHull
函数找到凸包
现在我们已经得到了轮廓,我们可以为每个轮廓找到凸包了。可以使用convexHull
函数来实现。
std::vector<std::vector<cv::Point>> hull(contours.size());
for (size_t i = 0; i < contours.size(); i++) {
cv::convexHull(contours[i], hull[i]);
}
....
绘制凸包
最后一步是可视化我们到目前为止发现的凸包。因为凸包本质上是一个轮廓,我们可以使用OpenCV的drawContours
函数来创建一个。
cv::Scalar contours_color = cv::Scalar(255,0,0); // 蓝色
cv::Scalar hull_color = cv::Scalar(0,0,255); // 红色
for (size_t i = 0; i < contours.size(); i++) {
cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256));
cv::drawContours(drawing, contours, (int)i, contours_color);
cv::drawContours(drawing, hull, (int)i, hull_color);
}
ShowImg("Hull: ", drawing);
输出
应用
参考文献
代码
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
cv::Mat src;
int thresh = 100;
void thresh_callback(int, void);
void ErrorMsg(std::string msg) {
std::cout << "!! Error !! n";
std::cout << msg << std::endl;
}
void ShowImg(const std::string windowName, cv::Mat& img) {
cv::namedWindow(windowName);
cv::imshow(windowName, img);
}
int main(int argc, char argv[]) {
if(argc < 1) {
ErrorMsg("Please Provide Input Imagen");
}
src = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
if (src.empty()) {
ErrorMsg("Could not open or find the image!n");
return -1;
}
cv::GaussianBlur(src, src, cv::Size(3,3), 0); // 应用3x3核的高斯模糊
ShowImg("Image After Applying Blur", src);
const int max_thresh = 255;
const std::string source_window = "Canny ";
cv::createTrackbar("Canny thresh:", source_window, &thresh, max_thresh, thresh_callback);
thresh_callback(0, 0);
cv::waitKey();
return 0;
}
void thresh_callback(int, void) {
cv::Mat canny_output;
cv::Canny(src, canny_output, thresh, thresh*2);
std::vector<std::vector<cv::Point>> contours;
cv::findContours(canny_output, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
std::vector<std::vector<cv::Point>> hull(contours.size());
for (size_t i = 0; i < contours.size(); i++) {
cv::convexHull(contours[i], hull[i]);
}
cv::Mat drawing = cv::Mat::zeros(canny_output.size(), CV_8UC3);
cv::Scalar contours_color = cv::Scalar(255,0,0); // 蓝色
cv::Scalar hull_color = cv::Scalar(0,0,255); // 红色
for (size_t i = 0; i < contours.size(); i++) {
cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256));
cv::drawContours(drawing, contours, (int)i, contours_color);
cv::drawContours(drawing, hull, (int)i, hull_color);
}
ShowImg("Hull: ", drawing);
}
这里详细介绍了凸包的概念、实现方法以及在计算机视觉中的应用,并提供了C++代码示例。凸包是计算机视觉中一个重要的概念,用于确定一组点或形状的最小凸形边界。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。