2012-10-09 6 views
6

Pracuję nad programem, który prześledzi kropki w centrum binarnych bloków przypominających zakrzywione elementy konfetti. Później dopasuję te punkty za pomocą splajnu sześciennego śledzącego krzywą.Jak wypróbować linię na kropelce pod kątem prostopadłym? (w Pythonie/OpenCV, chyba że sugerujesz przejście na coś innego)

W ramach programu, muszę:

- utworzyć 2D wektor próbkowania skośną linię na obrazie binarnym,

- obliczyć kąt używać przy każdym położeniu wzdłuż confetti plama.

Oto niektóre przykłady obrazów i szkice co punkty prześledzić może wyglądać następująco:

enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

Znalezienie centrum pionowego odcinka czarna konfetti jest prosta. Pod warunkiem, że znajdujesz się w czarnym pikselu, znajdź lewą i prawą białą krawędź, środek to połowa odległości między nimi. Jest to łatwe, ponieważ wektor 2D użyty do wykonania powyższych obliczeń jest tylko rzędem obrazu.

this is a 10 pixel height segment of one of the confetti pieces

enter image description here

Ale kawałki konfetti nie zawsze kolejce prosto i pionowo! Czasami są zakrzywione lub wyrównane poziomo. Tu potrzebny jest wektor 2D, który przecina przekrój przez konfetti pod kątem. Jaki jest najskuteczniejszy sposób pobrania tego zakrzywionego wektora z obrazu? Praktycznie, w bibliotece przetwarzania obrazów, takiej jak Python PIL lub OpenCV, istnieją operacje, które mogą uzyskać wektory linii pod kątami przez obraz? Jeśli ją stworzyłem, jak mogę się upewnić, że jest skuteczny?

Jaki jest najbardziej efektywny sposób obliczenia kąta wektora? Jednym ze sposobów uzyskania odpowiedniego kąta jest znalezienie kąta, który spowoduje minimalną szerokość czarnego segmentu w zwróconym wektorze 2D. Nie muszę tego robić w sposób wyczerpujący, tylko przechodzić przez 360 stopni z krokiem co 30 stopni Innym sposobem uzyskania odpowiedniego kąta może być znalezienie stycznej krzywej kształtu konfetti i użycie linii prostopadłej do tej - ale to może być bardziej skomplikowany.

Wszelkie przemyślenia na temat lepszego rozwiązania problemu będą bardzo pomocne. Dowolne konkretne sugestie dotyczące sposobu pobrania linii 2d na obrazie i skuteczny sposób uzyskania prostopadłego kąta również byłyby świetne.

enter image description here

Odpowiedz

1

To wygląda przerzedzenie konturu lub szkieletu. Spójrz na that answer. Istnieje nawet link do pakietu Pythona.

+0

Konturowe przerzedzenie może być przydatne - zajrzę do tego. Myślę, że szkieletyzacja będzie zbyt kosztowna pod względem obliczeniowym, ponieważ w końcu zrobię to na wideo, jeśli nie w czasie rzeczywistym. Jest to jednak ładna biblioteka przetwarzania obrazów Python! – user391339

4

Wygląda na to, że jesteś zainteresowany "przyśrodkowym dopasowaniem osi" - wraz z estymacją orientacji (jeśli masz oś, to zazwyczaj styczna osi w dowolnym momencie wystarcza).

Technicznie z OpenCV, możesz rozważyć użycie odległość transformacji (cv.PointPolygonTest), przy użyciu komórek Voronoi (cv.cvCalcSubdivVoronoi2D), lub - jak sugeruje @remi morfologicznej przerzedzenie ...

Ale, jeśli nie zrobił Zechcesz użyć pakietu scikits-image i po prostu musiałeś użyć OpenCV - tutaj jest próba uruchomienia przy użyciu jakiegoś kodu szkieletyzacji (based on a quick and easy technique).

simple first image skeletonenter image description hereenter image description here

Następnie można śledzić to przez jakiś splajnu łącznika wzdłuż odkrytych punktów do pracy swoje próbki i stycznych (ale to wymaga trochę więcej pracy, aby odkryć końce i usunąć wszelkie zabłąkane punkty/luki ...)

import cv 

# get images 
orig = cv.LoadImage('o1Mlp.png') 

# create storage images 
grey = cv.CreateImage(cv.GetSize(orig), 8, 1) 
skel = cv.CreateImage(cv.GetSize(orig),8, 1) 
temp = cv.CreateImage(cv.GetSize(orig),8,1) 

# convert image to pure binary B&W based on threshold 
cv.CvtColor(orig, grey, cv.CV_RGB2GRAY) 
cv.Threshold(grey,grey,200,255,cv.CV_THRESH_BINARY_INV) 

# Create cross operator - good for skeletonization 
elem = cv.CreateStructuringElementEx(3,3,1,1,cv.CV_SHAPE_CROSS) 

# Loop until we have consumed all the values in the original image 
while True: 
    cv.MorphologyEx(grey,temp,None,elem,cv.CV_MOP_OPEN) # Shrink.. 
    cv.Not(temp,temp) # ...invert... 
    cv.And(grey,temp,temp) # ...intersect with original... 
    cv.Or(skel,temp,skel) # ... add to current skeleton... 
    cv.Erode(grey,grey,elem) # and reduce original ready for next. 

    (minVal,maxVal,minLoc,maxLoc)= cv.MinMaxLoc(grey) 
    if (maxVal==0): # Any pixels left? 
    break 

# show result 
cv.ShowImage("orig", orig) 
cv.ShowImage("skel", skel) 
cv.WaitKey(-1) 
+0

Wielkie dzięki. Zbadam to i zobaczę, czy to działa. Będę musiał usunąć małe gałęzie z głównego szkieletu, ale z pewnością wykonuje dobrą robotę przy śledzeniu. – user391339

2

Odnośnie ostatniej części problemu, znalezienie normaliów: Używam własnego algorytmu. Wydaje się działać. Jeśli znajdziesz standardowe rozwiązanie lub ulepszysz moje, daj nam znać!

/// <summary> 
    /// Returns absolute angle between points at offset length, or double.MinValue when not found. 
    /// </summary> 
    /// <param name="sequence">Ordered array of points (e.g., from OpenCV Contour)</param> 
    /// <param name="length">Number of points used to calculate angle</param> 
    /// /// <param name="increment">number of points between each angle calculation (e.g., 1 to attempt to determine angles for all points)</param> 
    /// <returns></returns> 
    public static double[] FindAbsoluteAngles(Point[] sequence, int length, int increment) 
    { 
     double[] angles = new double[sequence.Length]; 
     for (int i = 0; i < sequence.Length; i++) 
      angles[i] = double.MinValue; 

     double last = double.MinValue; 
     for (int i = length; i < sequence.Length; i += increment) 
     { 
      int i1 = i - length; 
      int i2 = i - ((int)length/2); 
      int i3 = i; 

      Point p1 = sequence[i1]; 
      Point p2 = sequence[i2]; 
      Point p3 = sequence[i3]; 

      if (p1.X != p3.X & p1.Y != p3.Y)//Is a diagonal 
      { 
       angles[i2] = 180 - Math.Atan(1.0 * (p1.X - p3.X)/(p1.Y - p3.Y)) * 180/Math.PI; 
      } 
      else if (last != double.MinValue) 
      { 
       //USe previous angle to determine non-diagonals (which can be: 0 or 180; 90 or 270) 
       double error; 
       if (p1.X == p3.X)//Is a vertical 
       { 
        error = Math.Abs(last - 180); 
        if (Math.Min(error, 180 - error) == error) 
         angles[i2] = 180; 
        else 
         angles[i2] = 0; 
       } 
       else if (p1.Y == p3.Y)//Is a horizontal 
       { 
        error = Math.Abs(last - 270); 
        if (Math.Min(error, 180 - error) == error) 
         angles[i2] = 270; 
        else 
         angles[i2] = 90; 
       } 
      } 

      last = angles[i2]; 
     } 

     return angles; 
    } 
Powiązane problemy