2016-07-04 3 views
1

Ich schrieb eine Cython-Funktion, die ein Binärbild (numpy Array) um 1 Pixel erweitert. Deshalb möchte ich einfach den Bereich zu erweitern, wo Array-Werte sind 1. Hier ist mein naiver Ansatz:Leistung: binäre Bild erweitern (morphologische Dilatation)

def expand_1px (numpy.ndarray[numpy.uint8_t, ndim = 2] A): 
    cdef int h = A.shape[0] 
    cdef int w = A.shape[1] 
    cdef numpy.ndarray[numpy.uint8_t, ndim = 2] RES = numpy.zeros([h, w], dtype = numpy.uint8) 
    # These Two lines below were originally missing 
    cdef int y, x 
    cdef unsigned char prev, cur 

    for x in range (0, w): 
     for y in range (1, h): 
      prev = A[y-1,x] 
      cur = A[y,x] 
      if cur > prev: 
       RES[y-1, x] = 1 
      if cur < prev: 
       RES[y,x] = 1 
    for y in range (0, h): 
     for x in range (1, w): 
      prev = A[y,x-1] 
      cur = A[y,x] 
      if cur > prev: 
       RES[y, x-1] = 1 
      if cur < prev: 
       RES[y,x] = 1 
    return numpy.bitwise_or(A,RES) 

Dies funktioniert korrekt, aber ist kläglich langsam. Eine OpenCV-Funktion dilate() ist ~ 30 mal schneller als meine Cython-Variante und gibt das gleiche Ergebnis. Ich benutze es wie folgt aus:

kernel = numpy.ones((3,3), dtype="uint8") 
kernel[0,0] = 0 
kernel[2,2] = 0 
kernel[0,2] = 0 
kernel[2,0] = 0 
... 
IMG = cv2.dilate(IMG,kernel,iterations = 1) 

Q:

  • Wie kann so schnell OpenCV die Variante sein? Was macht es eigentlich?
  • Wie kann ich meine Cython-Funktion so schnell arbeiten lassen?

aktualisieren:

Eine solche schlechte Leistung war aufgrund fehlender "cdef Erklärungen, mein schlecht. Hinzufügen dieser Funktion macht den Unterschied:

cdef int y, x 
cdef unsigned char prev, cur 

Immer noch der Leistungsunterschied ist etwa 30 mal, was auch etwas enttäuschend ist. Irgendwelche Ratschläge für weitere Verbesserungen?

+0

* "Wie kann ich meine Cython-Funktion so schnell arbeiten lassen?" * Sie könnten damit beginnen, mehr (vielleicht alle) Ihrer lokalen Variablen zu deklarieren (.eg 'x',' y', 'prev',' cur')) mit dem entsprechenden C-Typ; siehe http://docs.cython.org/src/quickstart/cytholize.html –

+0

@WarrenWeckesser Danke. Du hast natürlich Recht, ich wusste, dass ich wahrscheinlich einen Noob-Fehler mache. Trotzdem ist es ~ 30 mal langsamer als die OpenCV-Variante, ich werde die Frage aktualisieren. –

+1

Haben Sie die Befehlszeile '-a' verwendet, damit der Cython-Befehl eine eingefärbte HTML-Version der Quelle erzeugt? Die dunkelgelben Linien sind Linien, die zu Python-Aufrufen führen, anstatt reines C zu erzeugen. Um die beste Leistung zu erzielen, möchten Sie den Cython-Code optimieren, sodass in den Schleifen kein Gelb angezeigt wird. (Diese Schleifen sehen ziemlich einfach aus. Wenn also alle Variablen C-Deklarationen haben, haben Sie wahrscheinlich keine Python-Aufrufe in den Schleifen.) –

Antwort

1

Es gibt ein großes Problem mit Ihrer Funktion: Sie machen mehr Schleifen, die Sie brauchen.

import cython 

@wraparound(false) 
def expand_1px(numpy.ndarray[numpy.uint8_t, ndim = 2] A): 
    cdef int h = A.shape[0] 
    cdef int w = A.shape[1] 
    cdef numpy.ndarray[numpy.uint8_t, ndim = 2] RES = numpy.zeros([h, w], dtype = numpy.uint8) 
    cdef int y, x 
    cdef unsigned char prev, cur 

    for x in range(1, w): 
     for y in range(1, h): 
      cur = A[y,x] 

      prev = A[y-1,x] 
      if prev < cur: 
       RES[y-1,x] = 1 
      elif cur < prev: 
       RES[y,x] = 1 

      prev = A[y, x-1] 
      if prev < cur: 
       RES[y,x-1] = 1 
      elif cur < prev: 
       RES[y,x] = 1 

    for x in range(1, w): 
     cur = A[0,x] 
     prev = A[0,x-1] 
     if prev < cur: 
      RES[0,x-1] = 1 
     elif cur < prev: 
      RES[0,x] = 1 

    for y in range(1, h): 
     cur = A[y,0] 
     prev = A[y-1,0] 
     if prev < cur: 
      RES[y-1,0] = 1 
     elif cur < prev: 
      RES[y,0] = 1 

    return numpy.bitwise_or(A,RES) 

Ursprünglich hast du 2wh Iterationen, mit dieser Änderung nur Sie wh + w + h Iterationen tun.

+0

Das ist nett.Aber das hat _exactly_ gleiche Leistung wie meine, auch mit größeren Arrays versucht. Weniger Iterationen, aber die gleiche Anzahl von Operationen, nehme ich an, also sammelst du beide Pässe in einer Schleife. –