2013-07-19 3 views
7

Mon travail est basé sur des images avec un tableau de points (Fig. 1), et le résultat final est montré sur la Fig. 4. Je vais expliquer mon travail pas à pas.Opencv: Détection de contours, Dilatation et Dessin de compteur de masse

Figure 1 Image originale

enter image description here

Étape 1:. détecter le bord de chaque objet, y compris les points et un "anneau" que je veux supprimer pour une meilleure performance. Et le résultat de la détection de bord est représenté sur la Fig.2. J'ai utilisé le détecteur de bord Canny mais il ne marchait pas bien avec quelques points gris clair. Ma première question est de savoir comment fermer les contours des points et réduire autant que possible les autres bruits?

figure 2 Détection des bords

enter image description here

Etape 2:. de Dilate chaque objet. Je n'ai pas trouvé un bon moyen de remplir les trous, donc je les dilate directement. Comme le montre la figure 3, les trous semblent être trop élargis, de même que les autres bruits. Ma deuxième question est comment remplir ou dilater les trous afin de les faire remplir des cercles de la même taille?

figure 3 Dilatation

enter image description here

Étape 3:. Trouver et établir le centre de masse de chaque point. Comme le montre la figure 4, en raison du traitement d'image grossier, il existe une marque de «l'anneau» et certains points sont représentés en deux pixels blancs. Le résultat recherché ne doit montrer que les points et un pixel blanc pour un point.

Fig. 4: centres de masse

enter image description here

Voici mon code pour ces 3 étapes. Quelqu'un peut-il aider à améliorer mon travail?

#include "opencv2/imgproc/imgproc.hpp" 
#include "opencv2/highgui/highgui.hpp" 
#include <stdlib.h> 
#include <stdio.h> 
#include <cv.h> 
#include <highgui.h> 
using namespace std; 
using namespace cv; 

// Global variables 
Mat src, edge, dilation; 
int dilation_size = 2; 

// Function header 
void thresh_callback(int, void*); 

int main(int argc, char* argv) 
{ 
    IplImage* img = cvLoadImage("c:\\dot1.bmp", 0);   // dot1.bmp = Fig. 1 

    // Perform canny edge detection 
    cvCanny(img, img, 33, 100, 3); 

    // IplImage to Mat 
    Mat imgMat(img); 
    src = img; 

    namedWindow("Step 1: Edge", CV_WINDOW_AUTOSIZE); 
    imshow("Step 1: Edge", src); 

    // Apply the dilation operation 
    Mat element = getStructuringElement(2, Size(2 * dilation_size + 1, 2 * dilation_size + 1), 
        Point(dilation_size, dilation_size));  // dilation_type = MORPH_ELLIPSE 
    dilate(src, dilation, element); 
    // imwrite("c:\\dot1_dilate.bmp", dilation);    

    namedWindow("Step 2: Dilation", CV_WINDOW_AUTOSIZE); 
    imshow("Step 2: Dilation", dilation); 

    thresh_callback(0, 0); 

    waitKey(0); 
    return 0; 
} 

/* function thresh_callback */ 
void thresh_callback(int, void*) 
{ 
    vector<vector<Point>> contours; 
    vector<Vec4i> hierarchy; 

    // Find contours 
    findContours(dilation, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); 

    // Get the moments 
    vector<Moments> mu(contours.size()); 
    for(int i = 0; i < contours.size(); i++) { 
     mu[i] = moments(contours[i], false); 
    } 

    // Get the mass centers 
    vector<Point2f> mc(contours.size()); 
    for(int i = 0; i < contours.size(); i++) { 
     mc[i] = Point2f(mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00); 
    } 

    // Draw mass centers 
    Mat drawing = Mat::zeros(dilation.size(), CV_8UC1); 
    for(int i = 0; i< contours.size(); i++) { 
     Scalar color = Scalar(255, 255, 255); 
     line(drawing, mc[i], mc[i], color, 1, 8, 0); 
    } 

    namedWindow("Step 3: Mass Centers", CV_WINDOW_AUTOSIZE); 
    imshow("Step 3: Mass Centers", drawing); 
} 
+0

Avez-vous déjà essayé quelque chose de [ici] (http://stackoverflow.com/questions/1716274/fill-the-holes-in-opencv)? – William

Répondre

9

Il y a plusieurs choses que vous pouvez faire pour améliorer vos résultats. Pour réduire le bruit dans l'image, vous pouvez appliquer un flou médian avant d'appliquer l'opérateur Canny. C'est une technique de débruitage commune. Essayez également d'éviter d'utiliser l'API C et IplImage.

cv::Mat img = cv::imread("c:\\dot1.bmp", 0);   // dot1.bmp = Fig. 1 

    cv::medianBlur(img, img, 7); 

    // Perform canny edge detection 
    cv::Canny(img, img, 33, 100); 

Cela réduit considérablement la quantité de bruit dans l'image de votre bord: Canny result

Pour mieux conserver les dimensions d'origine de vos points, vous pouvez effectuer quelques itérations de fermeture morphologique avec un plus petit noyau plutôt que dilatation. Cela permettra également de réduire la jonction des points avec le cercle:

// This replaces the call to dilate() 
cv::morphologyEx(src, dilation, MORPH_CLOSE, cv::noArray(),cv::Point(-1,-1),2); 

Ceci effectuera deux itérations avec un noyau 3x3, a indiqué à l'aide cv::noArray().

Le résultat est plus propre, et les points sont complètement remplis:

Closing result

laissant le reste de votre pipeline non modifié donne le résultat final. Il y a encore quelques centres de masse parasites du cercle, mais beaucoup moins que la méthode originale:

Mass centers

Si vous vouliez tenter de retirer le cercle des résultats tout à fait, vous pouvez essayer d'utiliser cv::HoughCircles() et le réglage de la paramètres jusqu'à ce que vous obteniez un bon résultat. Cela pourrait avoir quelques difficultés parce que le cercle entier n'est pas visible dans l'image, seulement les segments, mais je vous recommande de l'expérimenter. Si vous avez détecté le cercle le plus interne, vous pouvez l'utiliser comme masque pour filtrer les centres de masse externes.

+0

Merci, vous avez amélioré mon travail. Une autre question: j'ai remarqué une petite différence entre cv :: Canny et cvCanny. Pourquoi ont-ils créé deux fonctions similaires et lequel (fonctions avec les préfixes 'cv ::' et 'cv') dois-je utiliser plus souvent? – WangYudong

+0

Il ne devrait pas y avoir de différence entre les deux - ils utilisent la même implémentation en interne. 'cv :: Canny()' est utilisé avec 'cv :: Mat' dans l'API C++, que vous devriez préférer. 'cvCanny()' provient de l'API C, qui utilise 'IplImage' et est obsolète. – Aurelius

5

Comment fermer les contours de points? utilisation de la méthode drawContours avec option de dessin rempli (CV_FILLED ou épaisseur = -1)

réduire le bruit? utilisez l'une des méthodes blurring (filtrage passe-bas).

taille similaire? utiliser l'érosion après dilatation = morphological closing.

un point pour un cercle, sortie sans bague extérieure? trouver la moyenne de tous contour areas. erase contours ayant une grosse différence avec cette valeur. sortir les centres restants.

Aurelius a déjà mentionné la plupart d'entre eux, mais comme ce problème est assez intéressant, j'essaierai probablement de poster une solution complète quand j'en aurai le temps. Bonne chance.