如果一个图像轮廓数量比较多比较复杂,常常需要根据轮廓的面积以及相邻轮廓的距离进行过滤以及合并, 即将距离相对比较接近的轮廓进行合并,对于面积相对较小,周围又没有比较接近的轮廓将给予删除。
计算轮廓之间的距离,根据图像可选择以下方法:
1、如果轮廓各种形状都有可能存在,使用轮廓形心的距离作为两个轮廓之间距离进行合并;
2、如何轮廓均是凸包形状,则用两个轮廓最接近Point元素点的距离作为两个轮廓距离进行合并;
//根据轮廓形心之间距离是否小于阈值,进行图像轮廓合并
void mergeContoursByCentre(cv::Mat &grayImage) {
// 寻找轮廓
std::vector<std::vector<cv::Point>> contours;
cv::findContours(grayImage, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
// 合并距离非常接近的轮廓
double mergeDistanceThreshold = 10.0; // 距离阈值,根据需要调整
for (size_t i = 0; i < contours.size(); ++i) {
for (size_t j = i + 1; j < contours.size(); ++j) {
// 计算轮廓i和轮廓j的形心
cv::Moments moments_i = cv::moments(contours[i]);
cv::Moments moments_j = cv::moments(contours[j]);
cv::Point2f center_i(moments_i.m10 / moments_i.m00, moments_i.m01 / moments_i.m00);
cv::Point2f center_j(moments_j.m10 / moments_j.m00, moments_j.m01 / moments_j.m00);
// 计算形心之间的距离
double distance = cv::norm(center_i - center_j);
// 如果距离小于阈值,合并轮廓
if (distance < mergeDistanceThreshold) {
contours[i].insert(contours[i].end(), contours[j].begin(), contours[j].end());
contours.erase(contours.begin() + j);
--j; // 由于删除了一个轮廓,需要减少j的值
}
}
}
//绘制图像
int contours_area = g_inputdata.contours_area_min;
Mat convex_hull_img = Mat::zeros(grayImage.size(), CV_8U);
int contours_f_size = contours.size();
vector<vector<Point> > pointHull(contours.size());
for (size_t i = 0; i < contours.size(); i++)
{
// 输出结果为Point类型的凸包检测
convexHull(Mat(contours[i]), pointHull[i], false);
}
for (int i = 0; i < contours_f_size; i++)
{
cv::Scalar color(255); // 白色
drawContours(convex_hull_img, pointHull, i, color, -1, 8, vector<Vec4i>(), 0, Point());
}
imshow("", convex_hull_img);
waitKey();
}
//根据轮廓之间最近距离是否小于阈值,进行图像轮廓的合并
void mergeContoursByEdge(cv::Mat &grayImage) {
cv::Mat binaryImage = grayImage;
Mat convex_hull_img = Mat::zeros(grayImage.size(), CV_8U);
// 查找轮廓
std::vector<std::vector<cv::Point>> contours;
cv::findContours(binaryImage, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
// 设置距离阈值,用于决定合并哪些轮廓
double distanceThreshold = 10.0; // 距离阈值,根据需要调整
// 合并轮廓
std::vector<bool> merged(contours.size(), false);
int contours_size = contours.size();
for (int i = 0; i < contours_size; i++) {
if (!merged[i]) {
// 寻找距离足够近的轮廓并合并它们
std::vector<cv::Point> mergedContour = contours[i];
int contours_size = contours.size();
for (int j = i + 1; j < contours_size; j++) {
if (!merged[j]) {
double distance = cv::pointPolygonTest(contours[j], mergedContour[0], true);
if (std::abs(distance) < distanceThreshold) {
mergedContour.insert(mergedContour.end(), contours[j].begin(), contours[j].end());
merged[j] = true;
}
}
}
// 计算凸包
std::vector<cv::Point> convexHull;
cv::convexHull(mergedContour, convexHull);
// 在图像上绘制凸多边形
std::vector<std::vector<cv::Point>> convexHullContours{ convexHull };
cv::Scalar color(255); // 白色
cv::drawContours(convex_hull_img, convexHullContours, -1, color, -1);
}
}
imshow("", convex_hull_img);
waitKey();
}