Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

BASH: Fotopułapka z Raspberry Pi

Fotopułpka to urządzenie pozwalające rejestrować obraz tylko w momencie wystąpienia zdarzenia. Może być używane np do obserwacji przyrody, ochrony obiektów (np domki jednorodzinne).

W tym wpisie przedstawię jak zbudować proste urządzenie tego typu w oparciu o Raspberry Pi wraz z modułem kamery rev 1.3, które będzie wykrywać zdarzenia i rejestrować kilkusekundowy film po ich wystąpieniu.

Algorytm całego procesu będzie wyglądać następująco:

  • pobierz nowe zdjęcie z kamery
  • jeśli istnieje poprzednie, porównaj
  • jeśli różnice na zdjęciu przekraczają zadany próg, rozpocznij nagrywanie
  • powrót do początku

Przygotowanie karty

Jako podstawy użyjemy dystrybucji Raspian w wersji 2014-01-07 lub nowszej. Na początku należy ją wyczyścić ze zbędnych elementów takich jak środowisko graficzne, przestrzeń wymiany i innych dodatków dostarczonych do dystrybucji. W sumie ponad 1GB, które użyjemy do przechowywania nagrań. Ta część bazuje na artykule Minimalist Raspberry Pi Server Image.

Usuwamy GUI - ok 700MB środowiska graficznego. Podczas tego procesu zostaną usunięte moduły, które doinstalujemy w następnych krokachsudo apt-get purge xserver.* x11.* xarchiver xauth xkb-data console-setup xinit lightdm lxde.* python-tk python3-tk scratch gtk.* libgtk.* openbox libxt.* lxpanel gnome.* libqt.* libxcb.* libxfont.* lxmenu.* gvfs.* xdg-.* desktop.* tcl.* shared-mime-info penguinspuzzle omxplayer gsfonts sudo apt-get --yes autoremove sudo apt-get upgrade

Usuwamy pakiety dla deweloperów ok 150MBsudo apt-get purge gcc-4\.[0-5].* sudo apt-get purge `sudo dpkg --get-selections | grep "\-dev" | sed s/install//` gcc-4\.[0-6].* python.* sudo apt-get --yes autoremove

Wyłączamy i usuwamy przestrzeni wymiany ok 100 MBsudo swapoff -a sudo apt-get purge dphys-swapfile sudo rm /var/swap

Pozostałe dodatkisudo rm -rf /usr/share/doc/* /opt/vc/src/hello_pi/hello_video/test.h264 /home/pi/python_games find /usr/share/locale/* -maxdepth 0 -type d |grep -v en |xargs sudo rm -rf find /usr/share/man/* -maxdepth 0 -type d |grep -Pv 'man\d' |xargs sudo rm -rf

Instalacja programów

Na początku powinniśmy zaktualizować repozytoria używane przez apt-get, w przeciwnym razie system pakietów nadal będzie korzystać z wcześniejszych ustawień.sudo apt-get update

Do porównania obrazu użyjemy programu compare dostępnego w pakiecie ImageMagick. sudo apt-get install imagemagick

Aby ograniczyć liczbę zapisanych danych na karcie SD, poszczególne klatki zostaną zmniejszone do rozmiaru 64x64 pikseli i będą zapisane w RAM dysku. Funkcja ta jest dostępna w naszej dystrybucji, dodamy jednie obsługę systemu plików FAT, który umożliwi nam swobodne zaalokowanie dysku o rozmiarze 8MB lub mniejszym.sudo apt-get install dosfstoolsW katalogu /home/pi tworzymy plik ramdisk.sh o następującej zawartości#!/bin/bash mkfs.vfat /dev/ram1 8192 mkdir -p /ramcache mount /dev/ram1 /ramcachektóremu nadajemy uprawnienia uruchamiania chmod +x ramdisk.shDodatkowo należy dodać ten plik podczas uruchamiania systemu. Można to wykonać na wiele sposobów, najprościej jednak będzie dopisać go do cron'a:sudo su echo "@reboot root /home/pi/ramdisk.sh" >> /etc/crontab exit

Wynik, czyli kilkusekundowe nagrania wideo, zostaną umieszczone na serwerze www. Do tego użyjemy programu lighttpd.sudo apt-get install lighttpdW podstawowej konfiguracji zawiera stronę powitalną, którą usuniemysudo rm /var/www/*.htmla następnie aktywujemy listowanie katalogów, w tym celu edytujemy plik lighttpd.confsudo nano /etc/lighttpd/lighttpd.confna końcu pliku należy dopisać dir-listing.activate = "enable"następnie zamykamy edytor i wykonujemy restart serwera www sudo /etc/init.d/lighttpd restart

Ponieważ BASH nie pozwala na porównywanie zmiennoprzecinkowe, a takie liczby uzyskamy używając programu compare, musimy doinstalować odpowiednią aplikację:sudo apt-get install bc

Rejestracja obrazu

Aby pobrać obraz z kamery należy zastosować program raspistill. Z odpowiednimi parametrami pozwoli zmniejszyć plik, a tym samym przyspieszyć operację porównywania. W poniższym przykładzie jest to 64x64 pikseli. Dodatkowo wprowadzone zostało niewielkie opóźnienie 10ms (-t 10), ponieważ przy niższych parametrach program może ulec zawieszeniu.raspistill -t 10 -e bmp -o nowyplik.bmp -w 64 -h 64

Porównanie obrazu następuje przez wykonanie programu compare. Pełna specyfikacja tego modułu znajduje się na stronie programu. Użyjemy tylko jednego z podanych tam przykładów:compare -metric PSNR rose.jpg reconstruct.jpg difference.pngPrzy wykonaniu porównania w trybie pomiaru PSNR uzyskamy podobieństwo obrazów wyrażone w dB - im wyższa wartość tym bardziej podobne są do siebie zdjęcia, wynik inf będzie oznaczać nieskończoność (zdjęcia takie same). Jeżeli użyjemy większego obrazka, otrzymamy dokładniejszą wartość, ale spowoduje to, że powstaną większe opóźnienia pomiędzy kolejnymi klatkami. W gotowym programie jako próg ustawiona została wartość 27dB

Jeżeli wartość PSNR będzie mniejsza niż 27dB rozpoczynamy nagrywanie przez program raspividraspivid -t 30000 -w 1280 -h 720 -b 1500000 -fps 15 -o /var/www/plik.h264parametr -t określa czas, po którym program automatycznie się zakończy, w tym przypadku jest to 30 sekund. Parametry -w -h to odpowiednio szerokość i wysokość. Parametr -b to bitrate, w zależności od szybkości karty możemy zmieniać parametry przepływności wideo. Wartość 1500000 odpowiada 1.5Mbps. FPS to liczba klatek na sekundę, a -o plik wynikowy, który w tym przypadku jest umieszczony bezpośrednio w katalogu serwera www. Warto tutaj zaznaczyć, że plik wynikowy to strumień h264 (nie kontener multimedialny). Pliki w tym formacie bez problemu odtworzymy w popularnym VLC.

Po zapętleniu w/w kroków otrzymamy prosty system rejestracji obrazów, wyzwalany w momencie wystąpienia zmiany w obrazie.

Całość w formie programu, wraz z funkcją okresowego czyszczenia karty, wygląda następująco: #!/bin/bash #wartości początkowe tmpfolder="/ramcache" tmpnewfile="$tmpfolder/newframe.bmp" tmpoldfile="$tmpfolder/oldframe.bmp" tmpdiffile="$tmpfolder/difframe.bmp" outdir="/var/www" # długość materiału wideo, zmienna tt tt="30" ttt="$tt"000 # czyszczenie dysku po przekroczeniu parametru usedspace="95" # funkcje pozyskiwania zdjęć i wideo execphoto="raspistill -t 10 -e bmp -o $tmpnewfile -w 64 -h 64" execvideo="raspivid -t $ttt -w 1280 -h 720 -b 1500000 -fps 15 -o " #usuń stare pliki rm $tmpnewfile rm $tmpoldfile rm $tmpdiffile #zamknij wszystkie procesy odpowiadające za kamerę killall -9 raspivid killall -9 raspistill clean_disk() { # zajętość procentowa dysku used=`df -h | grep rootfs | while read a b c d e f; do echo $e | sed "s/%//"; done` # jeśli większa od zadeklarowanej stałej check=`echo "$used>$usedspace" | bc` if [ "$check" == "1" ]; then # usuń najstarsze ls -1 -tr $outdir | head -10 | while read fn; do rm $outdir/$fn done fi } # pętla nieskończona while true; do eval $execphoto # jeśli nie było wcześniej obrazu, to skopiuj nowy jako stary # a wynikiem porównania będzie "inf" if [ ! -f $tmpoldfile ]; then cp $tmpnewfile $tmpoldfile fi img=`compare -metric PSNR $tmpnewfile $tmpoldfile $tmpdiffile 2>&1` # jeśli te same obrazy if [ "$img" == "inf" ]; then img='100' fi # w bashu nie ma możliwości sprawdzenia liczb zmiennoprzecinkowych img=`echo "$img<27" | bc` if [ "$img" == "1" ]; then dt=`date +%Y-%m-%d-%H-%M-%S` cp $tmpnewfile $outdir/$dt.bmp eval "$execvideo $outdir/$dt.h264" # pobierz nowa klatkę, ponieważ w czasie tt mogło coś się zmienić eval $execphoto clean_disk fi mv $tmpnewfile $tmpoldfile done

Powyższy kod możemy umieścić w pliku, który dodajemy do uruchamiania przy starcie.
Przy każdym uruchomieniu naszego Raspberry Pi rozpocznie się nagrywanie. 

linux programowanie hobby

Komentarze

0 nowych
dragonn   11 #1 11.05.2014 17:18

Mała sugestia - zamiast while [ 1 -eq 1 ] lepiej dać while true, wygląda z punktu widzenie programistycznego po prostu lepiej.

  #2 11.05.2014 17:58

Nie wytrzymam... ktoś używa linuxa?

  #3 11.05.2014 18:04

Cóż więcej dodać.

wojtekadams   18 #4 11.05.2014 19:15

Rozwiązanie dobre i warte zainteresowania. Możesz jeszcze sobie zerknąć na małe toole typu grabby i montion ;)

Zawsze jeszcze możesz dodać twój skrypt do startu systemu, żeby w razie przerwy w dostawie prądu monitornig nadal działał (bo o ramdisku pamiętałeś a o skrypcie już nie :) ).

cyryllo   17 #5 11.05.2014 19:18

A nie łatwiej użyć motion?

maciejkaczkowski   6 #6 11.05.2014 19:32

@dragonn: poprawione, przyzwyczajenia z x86 (cmp eax,eax)

@wojtekadams: na samym końcu napisałem o autostarcie, jednak lepiej jest dodać wywołanie przez ramdisk.sh, np dopisując na końcu linię /home/pi/plik.sh . Przy stosowaniu crona nie zawsze zdąży się zamontować ramdysk

@cyryllo: nie znam dobrze motion, nie udało mi się zmusić do pracy z kamerą Raspberry Pi. Dlatego stosuje te narzędzia, które dostarczyli (raspivid i raspistill).

  #7 11.05.2014 19:55
hind   2 #8 12.05.2014 09:01

Mnie dziwi tylko dla czego ramdysk a nie /dev/shm

  #9 12.05.2014 09:56

@Anonim: #2 news-flash zdziwisz się w ilu urządzeniach zastosowany został Linux ... poza tym masz słaby hejt....

  #10 12.05.2014 10:03

A nie lepiej skorzystać z Zonemindera? Jeżeli chodzi o ochronę obiektów na pewno będzie lepszym rozwiązaniem ale i nie do końca.
BMP? really? już nawet w podstawowych rejestratorach wizyjnych nie używa się tego formatu.

alucosoftware   7 #11 12.05.2014 12:24

@maciejkaczkowski
Gratulacje, bardzo ciekawy wpis.

Nie sprawdzę poprawności działania, ale wierzę, że gdy wpadnie mi do ręki R-Pi, będę mógł wykorzystać kod w niezmienionej formie. Zastanawia mnie zasadność porównywania obrazów z użyciem PSNR, nie takie jest jego przeznaczenie, choć każdy sposób na początek jest dobry :]

maciejkaczkowski   6 #12 12.05.2014 14:50

@no4b: dzięki na przyszłość będę wiedzieć

@hind: dzięki, nie znałem, ale sprawdzę

@alucosoftware: dzięki! mam nadzieję, że następne będą równie ciekawe ;) compare oferuje wiele możliwości porównywania, domyślnie jest obrazek z różnicami. Przy załączeniu parametru -metric można uzyskać wartości numeryczne na wiele sposobów, ale praktycznie każda zależy od ilości pikseli tutaj max 64*64. Zmniejszenie obrazka dodatkowo eliminuje szumy własne matrycy. Z opcji, które są dostępne w compare PSNR był najbardziej przewidywalny i nie zależy od wielkości obrazu.

maciejkaczkowski   6 #13 12.05.2014 15:18

@Anonim: pewnie, że można skorzystać z wielu gotowych rozwiązań. Co do BMP, spróbuj zapisać obraz z kamery do JPG 64x64 i następnie go porównaj. Zniekształcenia pochodzące z algorytmu kompresji spowodują zmiany w obrazie. Owszem można używać PNG tylko po co? PNG spowoduje zwiększenie zajętości CPU ponieważ jest to obraz kompresowany, aby porównać sąsiednie klatki należy oba obrazy zdekompresować. Kolejna sprawa "ZoneMinder also requires MySQL and PHP", spróbuj uruchomić to wszystko na Raspberry Pi. Można, tylko parsowanie dokumentu PHP i odczyt do bazy zwiększy zajętość CPU i liczbę odczytów z karty, kosztem zapisu klatek z kamery. Ponadto jest to w dziale hobby / programowanie - miało zawierać trochę elementów "edukacyjnych". Dzięki komentarzom ja również zyskuję cenną wiedzę.

alucosoftware   7 #14 12.05.2014 20:20

@maciejkaczkowski
Zgadza się, pominąłem link do dokumentacji technicznej ImageMagick ;-)

@a nie lepiej...
Tak... stwórzcie własny wpis. Łatwo jest oceniać. Czytanie takich komentarzy pod naprawdę sensownymi wpisami jest mocno deprymujące.

cyryllo   17 #15 12.05.2014 21:21

@alucosoftware pijesz do mnie? :P Moim zdaniem bawienie się w jakieś skrypty, które robią to samo co inne nie ma sensu (to jest moje zdanie). To jest wymyślanie koła na nowo. Autor wpisu wykonał dobrą robotę ale zapytałem czy nie lepiej użyć znanego i sprawdzonego narzędzia :P

Co do wpisu to właśnie się tym tematem zajmuje (wykrywanie ruchu w danym rejonie obrazu) i zapewne nie zapomnę o tym napisać.

alucosoftware   7 #16 12.05.2014 22:05

@cyryllo
Skądże, raczej do Anonimów, którym nie przychodzi na myśl pochwalenie autora, a jedynie wytykanie błędów i dodawanie bzdurnych komentarzy. Dobrze wiesz jak wiele pracy trzeba włożyć w utworzenie wpisu na poziomie i jak szybko można się zniechęcić, gdy jedynymi "odpowiedziami" są te wytykające nieistotne potknięcia.

cyryllo   17 #17 12.05.2014 22:18

@alucosoftware co do niektórych anonimowych wpisów masz rację ale mówię tu o ogóle całego DP :P

Liczę że @maciejkaczkowski nie podda się i podzieli się dalszymi wpisami z terenów walki z RPi. Ja na to czekam ;)

hind   2 #18 13.05.2014 08:31

@maciejkaczkowski: To jeszcze dodam że istnieje coś takiego jak tmpfs który można normalnie dodać do fstaba (najnowsza fedora montuje /tmp jako tmpfs).

  #19 14.05.2014 15:25

Znalazłem coś takiego
http://simpledev.webs.com/apps/blog/

  #20 14.05.2014 21:16

> used=`df -h | grep rootfs | while read a b c d e f; do echo $e | sed "s/%//";
Po co przełącznik -h? Poza tym przepisałbym na awka dla czytelności:
used=$(df | awk '/rootfs/ {print $5}' | tr -d '%')
i obciąć % można też bashem: used=${used%\%} albo nawet awkiem

> check=`echo "$used>$usedspace" | bc`
> if [ "$check" == "1" ]; then
Porównujesz liczby całkowite, lżej chyba korzystać z leta:
if (( used > usedspace )); then

> img=`echo "$img if [ "$img" == "1" ]; then
To jest ok, mnie bardziej przekonuje użycie " ls -1 -tr $outdir | head -10 | while read fn; do
> rm $outdir/$fn
Nie podoba mi się to, ale nie wiem co z tym zrobić ;-) normalnie bym użył xargs (albo gnu parallel), ale tutaj nie masz zbudowanego argumentu... może tak:
for x in $(ls -1 -tr "$outdir" | head -n 10); do
rm "$outdir/$fn"

Dwie ogólne uwagi:
1. nie łapanie $zmiennych w "$cudzysłów" jest złe jeśli ktoś może wpisać tam "/home/ścieżkę/do/ulubionego katalogu" ze spacją w środku. Zemści się to na tobie jeśli w $outdir ktoś stworzy plik ze spacją, np. "nice passwd". Dlaczego? Bo jeśli wykonasz to z katalogu /etc, to dostaniesz:
rm /var/www/nice passwd
Co się zakończy przez "rm: nie można usunąć „/var/www/nice”: Nie ma takiego pliku ani katalogu" oraz...
Z mniej drastycznych rzeczy - 10 plikóo ze spacją w środku w $outdir i masz przepełniony dysk...
2. branie czegoś w `apostrof` jest kiepskie, bo to się kiepsko zagnieżdża. polecam $(ten sposób).

  #21 26.09.2014 11:00

Świetny pomysł, ale początek nagrywania zdarzenia następuje z opóźnieniem, którego raczej nie da się wyeliminować :(
JB

  #22 19.12.2014 14:32

tutaj mozna znalezc duzo informacji na temat fatopulapek http://www.tvprzemyslowa.pl/kamery-gsm/ informacje o kamerach gsm oraz zobaczyc jakosc

  #23 14.02.2016 21:56

Dzięki za bardzo ciekawy dokument z kolejnymi możliwościami wspaniałego systemu, na takim "klocku lego" jak pi.