2015-11-20 4 views
5

Ich habe folgendes Bild Maske:opencv euklidischen Clustering vs findContours

mask

Ich möchte etwas ähnliches wie cv::findContours bewerben, aber dieser Algorithmus schließt sich nur verbundenen Punkte in den gleichen Gruppen. Ich möchte dies mit einer gewissen Toleranz tun, d. H. Ich möchte die Pixel nahe beieinander innerhalb einer gegebenen Radiustoleranz hinzufügen: Dies ist ähnlich dem euklidischen hierarchischen Distanzclustering.

Ist dies in OpenCV implementiert? Oder gibt es einen schnellen Ansatz für die Umsetzung?

Was ich will, ist etwas ähnlich wie diese,

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

zu den weißen Pixeln dieser Maske aufgetragen.

Vielen Dank.

+0

Es ist nicht klar, was Sie der Algorithmus tun wollen . Können Sie ein anderes Bild mit dem erwarteten Ergebnis zeigen?Auf einer Ebene scheint es so, als würden morphologische Operatoren dir alles geben, was du brauchst, also bin ich mir sicher, dass das nicht der Fall sein kann. Wir müssen den Effekt sehen, den Sie erreichen wollen. –

+0

@RogerRowland Keine morphologischen Operatoren sind keine Option, da sie meine Kanten verzerren. Was ich will, ist die Kanten in meinem Maskenbild durch euklidischen Abstand zwischen ihnen zu gruppieren. Etwas ähnliches wie http://www.pointclouds.org/documentation/tutorials/cluster_extraction.php – manatttta

+1

Ich denke @ Humam Vorschlag ist ein guter, obwohl es keine OpenCV-Implementierung gibt. Für Clustering-Aufgaben in OpenCV erhalten Sie nicht viel mehr als k-Mittel oder mittlere Verschiebung. Da Sie jedoch bereits einen Beispielalgorithmus verknüpft haben, ist es möglicherweise einfacher, diesen in OpenCV zu portieren (und vermutlich benötigen Sie kein 3D). –

Antwort

10

Sie können partition (statisch keine Notwendigkeit abgestimmt werden) für dies:

partitionspaltet einen Elementsatz in Äquivalenzklassen. Sie können Ihre Äquivalenzklasse als alle Punkte innerhalb eines gegebenen euklidischen Abstand (Radius Toleranz)

definieren Wenn Sie C++ 11, Sie einfach eine Lambda-Funktion verwenden können:

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; 
}); 

sonst, Sie können einfach einen Funktor erstellen (siehe Details im folgenden Code).

Mit entsprechendem Radius Abstand (I 18 Arbeiten auf diesem Bild gut gefunden), ich habe:

enter image description here

Voll Code:

#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; 
} 
+0

danke @Miki du bist großartig! – sturkmen

+0

+1 für einen OpenCV-Ansatz. -1 für einen nicht dichtebasierten Ansatz. Die Schwelle der Entfernung wird ein Alptraum sein. Ich würde mich freuen, wenn Sie Ihre Gedanken über eine universelle Möglichkeit teilen, die Schwelle in diesem Problem einzustellen;) –

+0

@HumamHelfawi Nun, danke für +1; D. Zu -1 gebe ich zurück: 1) DBSCAN (zumindest in Originalformulierung) benötigt 2 Parameter, von denen einer genau die Entfernungsschwelle ist (wie hier). 2) Die Frage bezieht sich explizit auf das Clustering innerhalb einer Radiustoleranz, nicht auf einen dichtebasierten Ansatz. 3) Die Frage erwähnt nicht, gegenüber Ausreißern robust zu sein. Deshalb habe ich diese Lösung vorgeschlagen. – Miki

2

Ich schlage vor, DBSCAN Algorithmus zu verwenden. Es ist genau das, wonach Sie suchen. Verwenden Sie eine einfache euklidische Entfernung oder sogar Manhattan Distance kann besser funktionieren. Die Eingabe besteht aus allen weißen Punkten (Schwellenwert). Die Ausgabe ist eine Gruppe von Punkten (Ihre angeschlossene Komponente)

Hier ist ein DBSCAN C++ implenetation

EDIT: Ich habe versucht, mein Selbst DBSCAN und hier ist das Ergebnis: enter image description here

Wie Sie sehen, Nur die wirklich verbundenen Punkte werden als ein Cluster betrachtet.

Dieses Ergebnis wurde mit dem standerad DBSCAN Algorithmus mit EPS erhalten = 3MinPoints = 1 (statisch auch) und Manhattan Entfernung