home     zurück
letzte Änderung am 20.02.2016
Erkennung von Bewegungen



Mangels besserer Ideen, fange ich mal mit Bewegungserkennung an.
Ausgangspunkt waren drei Bilder von der Kamera:
Bereits als ich mir die quasi gleichen Bilder mit gThumb angesehen habe, war zu erkennen, dass sie nicht wirklich gleich waren bzw. die Kamera offenbar ein deutliches Rauschen hat.

Nach etwas Internet-Recherche habe ich mir zuerst Pillow
    python3-Pillow-3.1.1-33.1.x86_64
und dann auch bald NumPy
    python3-numpy-1.10.4-41.4.x86_64
installiert.

Pillow brauche ich, um die JPG-Bilder zu laden, in Graustufen umzuwandeln und als Bitmap im Speicher zu halten.
NumPy wird gebraucht, um zwei Bitmaps voneinander subtrahieren zu können.

Die ersten Differenz-Bilder sahen nicht ganz so aus, wie ich das für quasi gleiche Bilder erwartet hätte.
Das Differenz-Bild von zwei unterschiedlichen Bildern hatte lediglich mehr graue Flächen.

Daraufhin wollte ich mehr Bilder miteinander vergleichen - bzw. voneinander subtrahieren.
Also soll sich das Script doch die Live-Bilder einfach direkt von der Kamera holen.
Auch hierfür musste ich erst mal wieder was nachinstallieren.
Und zwar urllib3
    python3-urllib3-1.14-16.2.noarch
Wie schon bei NumPy hatte ich dieses Paket zwar schon - aber nur für Python (v2.7).

Dann habe ich mir die Werte des Differenz-Bildes angesehen und dabei festgestellt, dass die Graustufen-Werte zu einem sehr großen Anteil entweder fast 0 oder fast 255 sind, wenn die Differenz von zwei quasi gleichen Bildern gebildet wurde. Bei unterschiedlichen Bildern hat das Differenz-Bild mehr Werte im mittleren Bereich.
Damit lässt sich ja vielleicht was anfangen.

Und nach drei bis vier Stunden Spielerei, hatte ich das folgende Script fertig:
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

import os, sys
import time
import io
import urllib3
from PIL import Image, ImageFilter, ImageMath
import numpy as np

CAM_URL="192.168.178.36/snapshot.cgi"
CAM_AUTH="camuser:pwd4Cam"

class IP_Cam:
  def __init__(self):
    self.http=urllib3.PoolManager()
    self.headers=urllib3.util.make_headers(basic_auth=CAM_AUTH)
 
  def getImage(self):
    r=self.http.request('GET', CAM_URL, headers=self.headers)
    image=Image.open(io.BytesIO(r.data))
    return(image)

# ###########################################################
# Bild nach Graustufen
def transformImage(img):
  im=img.convert('L') #.filter(ImageFilter.EMBOSS) #.filter(ImageFilter.FIND_EDGES)
  return(im)

# ###########################################################
# Ersetzt in dem NumPy-Array "arr" alle Werte mit 0, die
# entweder kleiner als "val" oder größer als 256-"val" sind.
def cutValues(arr, val):
  arr2=1*arr    # deep-copy erzeugen
  arr2[arr2>(256-val)]=0
  arr2[arr2<val]=0
  return(arr2)
  for y in range(arr.shape[0]): # zu langsame Version
    for x in range(arr.shape[1]):
      if arr[y][x]<val or arr[y][x]>(256-val):
        arr[y][x]=0
  return(arr)


# ###########################################################
# Main
if __name__=="__main__":
  cam=IP_Cam()

  img1=cam.getImage()       # Bild holen
  img1=transformImage(img1) # nach Graustufen
  img1.save("live1.jpg")
  imga1=np.asarray(img1)    # in array wandeln

  while True:
    time.sleep(0.5)

    img2=cam.getImage()
    img2=transformImage(img2)
    img2.save("live2.jpg")
    imga2=np.asarray(img2)

    imga3=imga1-imga2             # Bild_alt - Bild_neu
    s3=np.sum(imga3)
    img3=Image.fromarray(imga3)   # Differenz-Bild bauen
    img3.save("live3.jpg")

    imga4=cutValues(imga3, 32)    # Differenz-Bild "glätten"
    s4=np.sum(imga4)
    img4=Image.fromarray(imga4)
    img4.save("live4.jpg")

    print(s3)
    print(s4)

    if s4>500000:
      fn=time.strftime("%Y_%m_%d_%H_%M_%S")
      img1.save(fn+"-1.jpg")
      img2.save(fn+"-2.jpg")
      img3.save(fn+"-3.jpg")
      img4.save(fn+"-4.jpg")
      print("saved")

    imga1=imga2
    img1=img2
    print()

Das Script holt zwei Bilder von der Kamera (bei der offenbar gerade Spiegeln eingestellt war) und wandelt diese in Graustufen.

Bild 1
Bild 1

Bild 2
Bild 2

Von diesen zwei Bildern wird dann die Differenz gebildet.

Bild 3 - Differenz von Bild_1 -
        Bild_2
Differenz-Bild von Bild1 und Bild2

Ein verdammt gruseliges Rauschen. Warum aber eigentlich soviel Weiß, wo das doch die Differenz von zwei Bildern ist...?
Etwas nachgedacht...und schon wirds logisch. Die Werte nahe 255 entstehen natürlich bei einem Null-Durchgang.
Will heißen: ein Pixel im Bild_1 hat z.B. den Wert 100 und im Bild_2 hat er den Wert 110.
100 - 110 = -10   ->   -10 & 0xFF = 246
Wenn man nun aus dem Bild alle Werte, die fast Schwarz oder fast Weiß sind, durch maximal Schwarz ersetzt, ergibt sich das folgende Bild.

Bild 4 - Bild 3 ohne Extremwerte
Differenz-Bild, dessen fast ganz weißen Bereiche in Schwarz gewandelt wurden

Das Script gibt die Summe der Graustufen-Werte der beiden Differenz-Bilder aus.
Ein komplett weißes Bild würde den Wert 640*480*255=78.336.000 liefern, ein komplett schwarzes Bild 640*480*0=0.
dede@i5:~/bastelein/ipcam> ./tst4.py
30976511
296802

31350889
286517

31838555
289726

31357339
292987

32864985
298362

Wie im Script zu erkennen ist, habe ich den Wert 500.000 verwendet, um eine Bewegung im Bild detektieren zu lassen.

Greife ich nun testhalber ins Bild zu meiner Bierflasche (natürlich nur zu rein akademischen Zwecken), ändert sich die Summe des modifizierten Differenz-Bildes. Das Script meldet:
31697963
639452
saved

Und speichert alle vier Bilder mit einem Namen gemäß Datum+Uhrzeit.

vor dem "ins Bild
        greifen"
vor dem "ins Bild greifen"

nach dem "ins Bild
        greifen"
nach dem "ins Bild greifen"

Differenz-Bild
Differenz-Bild

Differenz-Bild modifiziert
Differenz-Bild modifiziert

Die Hand ist vage zu erkennen ... und reicht als Indikator für Bewegung. Der Wert 639.452 ist schließlich größer als 500.000.
Eine halbe Sekunde später wird noch deutlich mehr Bewegung erkannt.
34085813
2104499
saved

Das Vorher-Bild spare ich hier mal ein, denn es ist ja praktisch das Nachher-Bild von eben.

etwas tiefer ins Bild gegriffen
etwas tiefer ins Bild gegriffen

das Differenz-Bild von dem etwas
        tieferen Griff
das Differenz-Bild von dem etwas tieferen Griff

und das modifizierte Differenz-Bild
        dafür
und das modifizierte Differenz-Bild dafür

So weit, so schön. Das reicht erst mal für den Anfang.

Der nächste Schritt könnte sein, die Ecke der Bewegung zu detektieren und die Kamera entsprechend neu auszurichten.
Wobei sich ein Problem dadurch ergeben könnte, dass die Kamera keine Koordinaten annimmt - sondern man lediglich eine definierte Zeit lang in eine Richtung schwenken kann.
Und während des Schwenkens bewegt sich natürlich das komplette Bild.
Da werde ich nicht mehr viel mit dem Differenz-Bild anfangen können.
Schnell noch ein Test.
Jupp....sieht dann etwa so aus:

ein modifiziertes Differenz-Bild
        während eines Kamera-Schwenks
ein modifiziertes Differenz-Bild während eines Kamera-Schwenks

Wie erwartet: weitestgehend wertlos.
Und vorher die Schwenk-Dauer zu berechnen, dürfte ebenfalls schwierig werden, wenn man die Entfernung des bewegten Bereiches zur Kamera nicht kennt.
Also bräuchte es so eine Art Start-Stopp-Betrieb. Aber das Ding schwenkt ohnehin schon so träge - zumindest seitlich.

Bewegunglokalisation