Wykrywanie ruchu kamerą internetową i Raspberry Pi

Zagadnienia zdjęć i wideo w kontekście Raspberry Pi kojarzą się zwykle z modułem wideo dostępnym dla Malinki. Jest to kawałek płytki z optyką, którą wpina się taśmą bezpośrednio w płytę komputera. Moduł kamery nie zawsze jest pod ręką, a poza tym bywa drogi.

Dziś pokażę jak zdziałać małe co nieco używając taniej kamerki USB i Raspberry Pi.

Postanowiłem nie skupiać się jedynie na podłączeniu kamery i udowodnieniu, że działa. Spróbowałem użyć ją do czegoś konkretniejszego. A poniżej wynik prac :).

Czego będziemy potrzebować

Oto zestaw sprzętu:

Software

Łączymy wszystko razem, uruchamiamy i instalujemy odpowiedni soft:

sudo apt-get install fswebcam

Przy okazji sprawdzamy, jaką nazwę dostała nasza kamera w systemie:

ls /dev

U mnie kamera po podłączeniu pokazała się jako video0.

Pierwsze zdjęcia

Mamy już wszystko na miejscu, więc możemy przystąpić do testów.

Wykonanie pierwszego zdjęcia może wyglądać tak:

fswebcam --device /dev/video0 --resolution 1280x800 --no-banner capture.jpg

Opcje według kolejności to: nazwa urządzenia, rozdzielczość, wyłączenie winiety z podpisem i nazwa pliku, do którego będziemy zapisywać wykonane zdjęcie.

Ot i cała filozofia - zdjęcie może wyglądać tak:

Krzywuśne selfie - może to i lepiej ;)

Krok dalej

Skoro działa, to możemy zająć się czymś zdecydowanie ciekawszym :).

Napiszmy program, który będzie zapisywał zdjęcie wtedy, kiedy kamera wykryje ruch. Ciekawe zagadnienie. Oto program, a poniżej omówienie:

# -*- coding: utf-8 -*-
import StringIO
from datetime import datetime
import subprocess
from PIL import Image


DEVICE = "/dev/video0"
THRESHOLD = 30
SENSITIVITY = 320
MIN_WIDTH, MIN_HEIGHT = 160, 120
WIDTH, HEIGHT = 1280, 720


def get_test_image():
    command = "fswebcam --quiet --device %s --no-banner --resolution %sx%s -" % (
        DEVICE, MIN_WIDTH, MIN_HEIGHT)
    img_data = StringIO.StringIO()
    img_data.write(subprocess.check_output(command, shell=True))
    img_data.seek(0)
    img = Image.open(img_data)
    buffer = img.load()
    img_data.close()
    return buffer


def save_image():
    time = datetime.now()
    filename = "capture-%04d%02d%02d-%02d:%02d:%02d:%02d.jpg" % (
        time.year, time.month, time.day, time.hour, time.minute, time.second, time.microsecond)
    subprocess.call(
        "fswebcam --quiet --device %s --no-banner --resolution %sx%s  %s" % (DEVICE, WIDTH, HEIGHT, filename), shell=True)
    print("Captured %s" % filename)


def check_distance(x, y):
    pixdiff = abs(x - y)
    if pixdiff > THRESHOLD:
        return True
    return False


class ImageCaptured(Exception):
    pass

buffer_prev = get_test_image()

while(True):
    buffer_current = get_test_image()

    try:
        changed_pixels = 0
        for x in xrange(0, MIN_WIDTH):
            for y in xrange(0, MIN_HEIGHT):
                if check_distance(buffer_prev[x, y][1], buffer_current[x, y][1]):
                    changed_pixels += 1
                    if changed_pixels > SENSITIVITY:
                        save_image()
                        raise ImageCaptured
    except ImageCaptured:
        pass

    buffer_prev = buffer_current

Kod można podzielić na trzy fazy.

Pierwsza faza, to pobieranie małego zdjęcia i porównanie go z poprzednio pobranym małym zdjęciem. W moim przypadku najmniejszy rozmiar jaki udało mi się wymusić to 160 na 120 pikseli, ale możecie operować na jeszcze mniejszych..

Druga faza, to sprawdzenie na ile obecne zdjęcie różni się od poprzedniego. Robimy to licząc różnicę nasycenia koloru zielonego, odpowiadających sobie punktów w obu zdjęciach (można użyć innych kolorów lub wykonać obliczenia dla wszystkich składowych). Jeśli różnica wynosi więcej niż THRESHOLD (w moim wypadku 30), to inkrementujemy licznik zmienionych pikseli changed_pixels. Jeśli przekroczony został poziom SENSITIVITY (w moim przypadku 320 różnych pikseli), to przechodzimy do fazy trzeciej.

Faza trzecia, to zrobienie dużego zdjęcia, zapisanie go i rzucenie wyjątkiem (żeby nie kontynuować dalszego liczenia).

Po wszystkim następuje powrót do fazy pierwszej.

Takim to w miarę prostym sposobem możemy zyskać fajny mechanizm rejestrujący reagujący na ruch.

W niektórych wypadkach potrzebne będzie zwiększenie lub zmniejszenie wartości THRESHOLD i/lub SENSITIVITY, żeby kamera łapała zdjęcia dokładnie wtedy kiedy potrzebujemy.

Wartości ustalone przeze mnie pozwoliły rejestrować ruch na chodniku i parkingu przed moim domem.

Zakończenie

Nie zawsze potrzeba dedykowanych modułów do zadań które chcemy osiągnąć. Kamerka HP kosztowała około 40zł i w zupełności wystarcza do zadania, które przed nią stawiam :).

comments powered by Disqus