tutorial findcontours español c++ opencv cluster-analysis

c++ - español - opencv euclidean clustering vs findContours



findcontours opencv c++ (2)

Puede usar una partición para esto:

partition divide un elemento establecido en clases de equivalencia. Puede definir su clase de equivalencia como todos los puntos dentro de una distancia euclídea dada (tolerancia de radio)

Si tiene C ++ 11, puede simplemente usar una función lambda:

int th_distance = 18; // radius tolerance int th2 = th_distance * th_distance; // squared radius tolerance vector<int> labels; int n_labels = partition(pts, labels, [th2](const Point& lhs, const Point& rhs) { return ((lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y)) < th2; });

de lo contrario, puedes construir un functor (ver detalles en el código a continuación).

Con la distancia de radio adecuada (encontré 18 obras buenas en esta imagen), obtuve:

Código completo:

#include <opencv2/opencv.hpp> #include <vector> #include <algorithm> using namespace std; using namespace cv; struct EuclideanDistanceFunctor { int _dist2; EuclideanDistanceFunctor(int dist) : _dist2(dist*dist) {} bool operator()(const Point& lhs, const Point& rhs) const { return ((lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y)) < _dist2; } }; int main() { // Load the image (grayscale) Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE); // Get all non black points vector<Point> pts; findNonZero(img, pts); // Define the radius tolerance int th_distance = 18; // radius tolerance // Apply partition // All pixels within the radius tolerance distance will belong to the same class (same label) vector<int> labels; // With functor //int n_labels = partition(pts, labels, EuclideanDistanceFunctor(th_distance)); // With lambda function (require C++11) int th2 = th_distance * th_distance; int n_labels = partition(pts, labels, [th2](const Point& lhs, const Point& rhs) { return ((lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y)) < th2; }); // You can save all points in the same class in a vector (one for each class), just like findContours vector<vector<Point>> contours(n_labels); for (int i = 0; i < pts.size(); ++i) { contours[labels[i]].push_back(pts[i]); } // Draw results // Build a vector of random color, one for each class (label) vector<Vec3b> colors; for (int i = 0; i < n_labels; ++i) { colors.push_back(Vec3b(rand() & 255, rand() & 255, rand() & 255)); } // Draw the labels Mat3b lbl(img.rows, img.cols, Vec3b(0, 0, 0)); for (int i = 0; i < pts.size(); ++i) { lbl(pts[i]) = colors[labels[i]]; } imshow("Labels", lbl); waitKey(); return 0; }

Tengo la siguiente máscara de imagen:

Quiero aplicar algo similar a cv::findContours , pero ese algoritmo solo une los puntos conectados en los mismos grupos. Quiero hacer esto con cierta tolerancia, es decir, quiero agregar los píxeles uno cerca del otro dentro de una tolerancia de radio dada: esto es similar a la agrupación jerárquica de distancia euclidiana.

¿Esto se implementa en OpenCV? ¿O hay algún enfoque rápido para implementar esto?

Lo que quiero es algo similar a esto,

http://www.pointclouds.org/documentation/tutorials/cluster_extraction.php

aplicado a los píxeles blancos de esta máscara.

Gracias.


Sugiero usar el algoritmo DBSCAN . Es exactamente lo que estás buscando. Use una distancia euclidiana simple o incluso la distancia de Manhattan puede funcionar mejor. La entrada es todos los puntos blancos (umbral ed). La salida es un grupo de puntos (su componente conectado)

Aquí hay una implementación de DBSCAN C ++

EDITAR: Probé DBSCAN yo y aquí está el resultado:

Como puede ver, solo los puntos realmente conectados se consideran como un solo grupo.

Este resultado se obtuvo utilizando el algoritmo standerad DBSCAN con EPS = 3 (no es necesario ajustar la estática ) MinPoints = 1 (estático también) y Manhattan Distancia