Verfolgen Sie grüne Objekte mit Python + Numpy (Partikelfilter)

zunaechst

Zuvor habe ich einen Artikel wie diesen geschrieben (lassen Sie uns rote Objekte mit Python erkennen) (http://qiita.com/odaman68000/items/ae28cf7bdaf4fa13a65b). Es ist eine Methode, um einfach ein Bild in HSV (Farbton, Sättigung, Wert) umzuwandeln und eine Region mit einer starken roten Komponente zu finden.

Betrachten wir dieses Mal ein "grünes" Objekt als Anwendung. Und dann werde ich über das "Verfolgen" von Objekten in einem kontinuierlichen Videostream sprechen. Wenn Sie sich den kontinuierlichen Videostream genau ansehen, handelt es sich um ein kontinuierliches Standbild. Wenn Sie das grüne Objekt anhand dieses Standbilds weiterhin erkennen, können Sie es auf natürliche Weise verfolgen. Es ist kein großer Fehler, das zu denken. Es gibt jedoch eine Kamera, die sich vom menschlichen Auge unterscheidet. Wenn der Winkel des Zielobjekts ein wenig abweicht, leuchtet es je nach Lichtmenge weiß und weicht vom Urteil ab (das Urteil schlägt fehl und Sie verlieren es aus den Augen), der Beurteilungspunkt fliegt herum oder Sie verlieren es aus den Augen, selbst wenn sich das Umgebungslicht ein wenig ändert. Es ist. Was passiert, wenn sich schlechte Bedingungen überschneiden und Sie dies aus den Augen verlieren? Der Computer stellt fest, dass das Objekt "nicht vorhanden" ist. Aber im nächsten Moment können sich die Bedingungen verbessern und wieder auftreten. Es erscheint und verschwindet, es erscheint wieder und es gibt überhaupt kein Gefühl von Stabilität, und eine 100% ige Erkennungsgenauigkeit kann nicht immer erwartet werden.

Daher ist es notwendig, die Idee aufzugeben, dass ein Standbild jedes Mal analysiert werden sollte, selbst wenn es sich um einen Videostream handelt, und den Videostream als "kontinuierliches Standbild" zu überdenken. Mit anderen Worten, es sollte vorhergesagt und durchsucht werden, indem die Idee der Wahrscheinlichkeit und Statistik voll genutzt wird, dass "es hier sein sollte, weil es das letzte Mal hier war".

Partikelfilter

Es gibt gute Möglichkeiten, diese Probleme zu lösen. Es wird "Partikelfilter" genannt. Ich weiß nicht genug, um die akademische Definition und den Inhalt des Partikelfilters zu erklären, deshalb möchte ich es vielen anderen Kommentarseiten überlassen. Konzeptionell jedoch Bildanalyse mit einem Partikelfilter

  1. Streuen Sie Partikel (Punkte) auf das Bild
  2. Berechnen Sie für jedes Partikel die Wahrscheinlichkeit der Umgebung (Yudo: Gewicht, z. B. rötlich oder bläulich).
  3. Verwenden Sie Partikel mit hoher Wahrscheinlichkeit und entfernen Sie Partikel mit geringer Wahrscheinlichkeit
  4. Finden Sie den Punkt, an dem die wahrscheinlichsten Partikel am konzentriertesten sind (dies ist die Position des Objekts).
  5. Bewegen Sie alle Partikel nach und nach nach dem Zufallsprinzip, um sich auf das nächste Bild vorzubereiten (→ fahren Sie mit Schritt 2 fort).

Wenn Sie diese Verfahren 1 bis 5 fortsetzen, können Sie natürlich das oben genannte erreichen. Der Zustand "Ich war das letzte Mal hier" wird von den verstreuten Partikeln in Erinnerung gerufen, und für jedes Partikel: "Ist es diese Seite als nächstes?" Und wiederholt das zufällige Bewegen der Partikel. Schlechte Partikel, die die Vorhersage weiterhin beeinträchtigen, werden eliminiert. Partikel sagen verzweifelt den nächsten Frame voraus und überleben w Es fühlt sich unerwartet grausam an und es scheint, dass selbst Anhaftungen, dass Partikel schwierig sind (lügen).

Es ist ziemlich schwierig, diese Reihe von Prozessen in C / C ++ zu beschreiben, aber hauptsächlich aufgrund der Kraft von Numpy ist es ein großartiges Tokoro von Python + Numpy, dass eine kleine Menge Code erforderlich ist.

Implementierung eines Partikelfilters (Basic)

Zunächst zum Import. Es wird davon ausgegangen, dass Sie wie folgt importiert haben.

import cv2
import numpy as np

Definition von Partikeln

Definieren Sie zunächst die Struktur der Partikel. Lassen Sie uns wie folgt vorgehen. "x" und "y" sind die Positionen der Teilchen und "Gewicht" ist die Wahrscheinlichkeit (Gewicht).

particle = [x, y, weight]

Wahrscheinlichkeitsberechnungsfunktion

Erstellen Sie als Nächstes eine Funktion zur Berechnung der Wahrscheinlichkeit (des Gewichts) der Partikel. Diese Funktion scannt einen Bereich von 30 x 30 Pixel um die angegebenen Koordinaten und gibt den Prozentsatz von 900 Pixel zurück, der die Kriterien erfüllt. Wenn beispielsweise der Bereich von 30 x 30 Pixel alle NG ist, ein Wert von 0,0, wenn alle in Ordnung sind, ein Wert von 1,0, und wenn ungefähr die Hälfte in Ordnung ist, wird ein Wert von ungefähr 0,5 zurückgegeben. Die Entscheidungsfunktion func () ist out und ermöglicht es dem Aufrufer, die Entscheidungsfunktion anzugeben.

def likelihood(x, y, func, image, w=30, h=30): 
  x1 = max(0, x - w / 2)
  y1 = max(0, y - h / 2)
  x2 = min(image.shape[1], x + w / 2)
  y2 = min(image.shape[0], y + h / 2)
  region = image[y1:y2, x1:x2]
  count = region[func(region)].size
  return (float(count) / image.size) if count > 0 else 0.0001

Partikelinitialisierungsfunktion

Als nächstes folgt die Partikelinitialisierungsfunktion. Der Parameter func soll die Entscheidungsfunktion angeben, die wie zuvor ausgehen soll. Diese Funktion erzeugt viele (500) dieser Partikel. Als Anfangswert werden die Partikel in der Nähe des größten Bereichs in dem durch die Funktion "func ()" bestimmten Bereich positioniert. Die "500" von "(500, 3)", die im Aufruf von "np.ndarray ()" angegeben ist, ist die Anzahl der Partikel, und "3" ist die Anzahl der Elemente der obigen "x", "y", "weight". ist.

def init_particles(func, image): 
  mask = image.copy()
  mask[func(mask) == False] = 0
  contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
  if len(contours) <= 0:
    return None
  max_contour = max(contours, key=cv2.contourArea)
  max_rect = np.array(cv2.boundingRect(max_contour))
  max_rect = max_rect[:2] + max_rect[2:] / 2
  weight = likelihood(max_rect[0], max_rect[1], func, image)
  particles = np.ndarray((500, 3), dtype=np.float32)
  particles[:] = [max_rect[0], max_rect[1], weight]
  return particles

Implementierung eines Partikelfilters (Algorithmus)

Dies ist der Teil der Bewegung des Partikelfilters im eigentlichen Sinne. Der Partikelfilter durchläuft die folgenden vier Verfahren.

  1. Partikel-Resampling
  2. Prognose
  3. Wahrscheinlichkeits- (Gewichts-) Beurteilung
  4. Messung

Resampling von Partikeln

Beim Resampling unter Verwendung von Zufallszahlen werden Partikel mit schlechten Qualitäten eliminiert und durch Partikel mit guten Qualitäten ersetzt. Die Methode cumsum (), mit der das Array weight erstellt wird, berechnet die kumulative Summe. Wenn Sie "(weight> weight) .argmax ()" einstellen, wird das Array "weight" gescannt und der Array-Index wird zurückgegeben, wenn zum ersten Mal ein Wert angezeigt wird, der größer als "weight" ist.

def resample(particles): 
  tmp_particles = particles.copy()
  weights = particles[:, 2].cumsum()
  last_weight = weights[weights.shape[0] - 1]
  for i in xrange(particles.shape[0]):
    weight = np.random.rand() * last_weight
    particles[i] = tmp_particles[(weights > weight).argmax()]
    particles[i][2] = 1.0

Prognose

Bewegen Sie die Partikel tatsächlich zum nächsten Frame. Addiere den Koeffizienten, der durch "Varianz" multipliziert mit dem Ergebnis von "numpy.random.randn ()" angegeben wird. Dieser "Varianz" -Koeffizient ist ein numerischer Wert, der entsprechend der Intensität der Bewegung des Ziels eingestellt werden sollte. Die zufällige Bewegung von Partikeln wird als "Vorhersage" bezeichnet. Teilchen, die sich zufällig in die gute Richtung bewegen, überleben, und Teilchen, die sich zufällig in die falsche Richtung bewegen, sind dazu bestimmt, beseitigt (überschrieben) zu werden.

def predict(particles, variance=13.0): 
  particles[:, 0] += np.random.randn((particles.shape[0])) * variance
  particles[:, 1] += np.random.randn((particles.shape[0])) * variance

Wahrscheinlichkeits- (Gewichts-) Beurteilung

Bestimmen Sie die Wahrscheinlichkeit (Gewicht) für jedes Partikel. Es wird das Material sein, um das Ergebnis der vorherigen Vorhersage zu beurteilen. Diese Wahrscheinlichkeit (Gewichtung) wird berechnet, indem die zuvor erstellte Funktion "Wahrscheinlichkeit ()" aufgerufen wird.

def weight(particles, func, image): 
  for i in xrange(particles.shape[0]): 
    particles[i][2] = likelihood(particles[i][0], particles[i][1], func, image)
  sum_weight = particles[:, 2].sum()
  particles[:, 2] *= (particles.shape[0] / sum_weight)

Messung

Messen Sie die Partikel, um den Ort in der Nähe der Konzentration der guten Partikel zu bestimmen.

def measure(particles): 
  x = (particles[:, 0] * particles[:, 2]).sum()
  y = (particles[:, 1] * particles[:, 2]).sum()
  weight = particles[:, 2].sum()
  return x / weight, y / weight

Fertig

Schließlich wird die bisher implementierte Verarbeitung wie eine Utility-Funktion zusammengefasst. Wenn während des durch das Argument angegebenen "max_frame" keine grüne Komponente gefunden wird, werden die Partikel neu initialisiert.

particle_filter_cur_frame = 0

def particle_filter(particles, func, image, max_frame=10):
  global particle_filter_cur_frame
  if image[func(image)].size <= 0:
    if particle_filter_cur_frame >= max_frame:
      return None, -1, -1
    particle_filter_cur_frame = min(particle_filter_cur_frame + 1, max_frame)
  else:
    particle_filter_cur_frame = 0
    if particles is None:
      particles = init_particles(func, image)

  if particles is None:
    return None, -1, -1

  resample(particles)
  predict(particles)
  weight(particles, func, image)
  x, y = measure(particles)
  return particles, x, y

Lass es uns benutzen!

Erstellen wir also ein Programm, das grüne Objekte mithilfe der bisher implementierten Partikelfilter verfolgt. Hier werden die von "cv2.VideoCapture ()" erfassten Rahmendaten (BGR-Bild) in HSV konvertiert, und S (Sättigung) und V (Wert) werden mit dem Schwellenwert von OTSU multipliziert, und die Farbtiefe und Helligkeit werden multipliziert. In beiden Fällen wird der Prozess ausgeführt, bei dem nur genügend Pixel verwendet werden (die H-Komponente wird mit durchlaufenden Pixeln von S und V maskiert und mit 0 gefüllt). Ich möchte den Bereich von Grün (50-85) finden, daher ist es sinnvoll, 0 zu löschen. (Wenn Sie dagegen Rot erkennen, ist es seltsam, wenn Sie es nicht mit einem anderen Wert als 0 füllen.)

Was tatsächlich an die Funktion "Particle_Filter ()" übergeben wird, ist die oben gefilterte H-Komponente. Danach denke ich, dass die Funktion Partikel_Filter () die Partikel verwaltet und den grünen Teil gut verfolgt.

import cv2
import numpy as np

if __name__ == "__main__": 
  def is_green(region): 
    return (region >= 50) | (region < 85)

  cap = cv2.VideoCapture(0)
  particles = None

  while cv2.waitKey(30) < 0:
    _, frame = cap.read()
	frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV_FULL)
	frame_h = frame_hsv[:, :, 0]
	_, frame_s = cv2.threshold(frame_hsv[:, :, 1], 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
	_, frame_v = cv2.threshold(frame_hsv[:, :, 2], 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
    frame_h[(frame_s == 0) | (frame_v == 0)] = 0
	
    particles, x, y = particle_filter(particles, is_green, frame_h)
	
    if particles is not None:
      valid_particles = particles[(particles[:, 0] >= 0) & (particles[:, 0] < frame.shape[1]) &
                                  (particles[:, 1] >= 0) & (particles[:, 1] < frame.shape[0])]
      for i in xrange(valid_particles.shape[0]): 
        frame[valid_particles[i][1], valid_particles[i][0]] = [255, 0, 0]
	  p = np.array([x, y], dtype=np.int32)
	  cv2.rectangle(frame, tuple(p - 15), tuple(p + 15), (0, 0, 255), thickness=2)
	
	cv2.imshow('green', frame)

  cap.release()
  cv2.destroyAllWindows()

Dieser Partikelfilter ist extrem widerstandsfähig gegen Geräusche und Bewegungen und verfolgt ein Objekt ziemlich hartnäckig. Darüber hinaus ist auch die dadurch erhaltene Erfassungsposition sehr stabil. Sobald Sie den Partikelfilter implementiert haben, ist der Schlüssel meiner Meinung nach die Implementierung der Funktion "Wahrscheinlichkeit ()". Das Leben und der Tod der Partikel hängen von dieser "Wahrscheinlichkeit ()" ab.

Beobachten Sie die verzweifelte Bewegung der Partikel, um zu überleben. Allmählich wird die Verzweiflung jedes Teilchens interessant, die rücksichtslos durch "Wahrscheinlichkeit ()" bewertet wird. Es ist ein Partikelfilter, der genau folgt, aber im Schatten davon werden unzählige Partikel geboren und sterben ab, und Sie können solch harte Trauer spüren.

Recommended Posts

Verfolgen Sie grüne Objekte mit Python + Numpy (Partikelfilter)
[Python] Berechnungsmethode mit numpy
Lassen Sie uns rote Objekte mit Python erkennen
Verfolgen Sie Baseballbälle mit Python + OpenCV
Bildverarbeitung mit Python 100 Knock # 10 Medianfilter
Bildverarbeitung mit Python 100 Knock # 12 Bewegungsfilter
Bildverarbeitung mit Python 100 Knock # 9 Gauß-Filter
Kantenextraktion mit Python + OpenCV (Sobel-Filter, Laplace-Filter)
Debuggen mit VS-Code mit Boost Python Numpy
1. Mit Python 1-2 gelernte Statistiken. Berechnung verschiedener Statistiken (Numpy)
[Rust / Python] Behandle Numpy mit PyO3 (Version August 2020)
[Fenwick_Tree] AtCoder Library liest mit einem grünen Codierer ~ Implementierung in Python ~
Mein Numpy (Python)
FizzBuzz in Python3
Scraping mit Python
Statistik mit Python
Scraping mit Python
Python mit Go
Twilio mit Python
In Python integrieren
Spielen Sie mit 2016-Python
AES256 mit Python
Getestet mit Python
Python beginnt mit ()
# Python-Grundlagen (#Numpy 1/2)
mit Syntax (Python)
# Python-Grundlagen (#Numpy 2/2)
Bingo mit Python
Zundokokiyoshi mit Python
Verfolgen Sie Python-Programme
Python #Numpy Basics
Excel mit Python
[Python] Numpy Memo
Mikrocomputer mit Python
Mit Python besetzen
[Python] Strukturiertes Array erstellen (heterogene Daten mit NumPy speichern)
FFT-Verarbeitung mit numpy und scipy bis Tiefpassfilter