2013-07-19 15 views
7

Moja praca oparta jest na obrazach z tablicą kropek (ryc. 1), a efekt końcowy pokazano na ryc. 4. Wyjaśnię moją pracę krok po kroku.Opencv: wykrywanie krawędzi, dylatacja i rysunek cala

Rys 1 oryginalny obraz

enter image description here

Krok 1:. Wykrywanie krawędzi każdego obiektu, łącznie z kropek i "pierścień", który chcę usunąć dla lepszej wydajności. Wynik wykrycia krawędzi pokazano na rys. 2. Użyłem detektora krawędzi Canny'ego, ale nie działało dobrze z niektórymi jasnoszarymi kropkami. Moje pierwsze pytanie brzmi: jak zamknąć kontury kropek i jak najdokładniej zredukować hałas?

fig wykrywania dwóch krawędzi

enter image description here

Etap 2. Jednokładność każdy obiekt. Nie znalazłem dobrego sposobu na wypełnienie dziur, więc rozszerzam je bezpośrednio. Jak pokazano na rys. 3, dziury wydają się zbyt duże, podobnie jak inne szumy. Moje drugie pytanie brzmi: jak wypełnić lub rozszerzyć otwory, aby były wypełnione kółkami w tym samym/podobnym rozmiarze?

Fig.3 Rozszerzenie

enter image description here

Etap 3. Znajdź i wyciągnąć środka ciężkości każdego punktu. Jak pokazano na rys. 4, ze względu na przetwarzanie zgrubnego obrazu, istnieje znaczek "pierścienia", a niektóre kropki są pokazane w dwóch białych pikselach. Poszukiwany wynik powinien pokazywać tylko kropki i jeden biały piksel dla jednej kropki.

Rys. 4: Masowe centra

enter image description here

Oto mój kod dla tych 3 etapów. Czy ktoś może pomóc w poprawie mojej pracy?

#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

Czy próbowałeś już czegoś z [tutaj] (http://stackoverflow.com/questions/1716274/fill-the-holes-in-opencv) jeszcze? – William

Odpowiedz

9

Jest kilka rzeczy, które możesz zrobić, aby poprawić swoje wyniki. Aby zmniejszyć szum na obrazie, możesz zastosować medianę rozmycia przed zastosowaniem operatora Canny. Jest to powszechna technika usuwania hałasu. Należy również unikać używania interfejsu API C i 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); 

To znacznie zmniejsza ilość hałasu w swojej krawędzi obrazu: Canny result

Aby lepiej zachować oryginalne rozmiary swoich punktów, można wykonać kilka iteracji morfologicznej zamknięcia z mniejszym jądra zamiast rozszerzanie się. Ograniczy to również łączenie punktów z okręgu:

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

to wystąpi dwóch powtórzeń z jądrem 3x3, oznaczony za pomocą cv::noArray().

Wynik jest czystsze, a punkty są całkowicie wypełnione:

Closing result

pozostawiając resztę swojego rurociągu niemodyfikowana daje wynik końcowy. Istnieje jeszcze kilka fałszywych centra masowe z kręgu, ale znacznie mniej niż oryginalny sposób:

Mass centers

Jeśli chciał próbować zdejmowania koła z wynikami całkowicie, można spróbować użyć cv::HoughCircles() i regulując parametry, dopóki nie uzyskasz dobrego wyniku. Może to mieć pewne trudności, ponieważ cały krąg nie jest widoczny na obrazie, tylko segmenty, ale polecam eksperymentować z nim. Jeśli wykryłeś najgłębszy krąg, możesz użyć go jako maski do odfiltrowania zewnętrznych ośrodków masowych.

+0

Dzięki, ulepszyłeś moją pracę. Kolejne pytanie: zauważyłem niewielką różnicę między cv :: Canny i cvCanny. Dlaczego stworzyłem dwie podobne funkcje, a które z nich (fukcje z prefiksem 'cv ::' i 'cv') powinny być częściej używane? – WangYudong

+0

Nie powinno być różnicy między tymi dwoma - używają one tej samej implementacji wewnętrznie. 'cv :: Canny()' jest używany z 'cv :: Mat' w C++ API, który powinieneś preferować. 'cvCanny()' pochodzi z C API, które używa 'IplImage' i jest nieaktualne. – Aurelius

5

jak zamknąć kontury kropek? zastosowanie drawContours sposób z opcją rysunku wypełniony (CV_FILLED lub grubości = -1)

zredukować hałas? użyj jednej z metod blurring (filtrowanie dolnoprzepustowe).

Podobny rozmiar? użyj erozji po dylatacji = morphological closing.

jedna kropka na jedno koło, wyjście bez pierścienia zewnętrznego? znaleźć średnią wszystkich contour areas. erase contours ma dużą różnicę w stosunku do tej wartości. wyprowadzić pozostałe centra.

Aurelius już wspomniał o większości z nich, ale ponieważ ten problem jest dość interesujący, prawdopodobnie będę starał się opublikować kompletne rozwiązanie, gdy mam wystarczająco dużo czasu. Powodzenia.

Powiązane problemy