Kamera śledząca ruch.

0

Witam wszystkich :)
Mam do wykonania pewien projekt a mianowicie kamerę, która będzie śledziła ruch. Z założenia kamera będzie umieszczona na silniku krokowym i będzie obracała się w zależności od ruchu przedmiotu śledzonego. Wszystkim ma zarządzać aplikacja napisana w Delphi 7.
Śledziłem to forum od jakiegoś czasu ale dopiero dziś się zarejestrowałem, wiem że jest tu sporo osób z ogromną wiedzą o programowaniu.
Mam do Was pytanie a raczej prośbę o jakieś wskazówki i porady jak wykonać ten projekt. W moim zamyśle ma to wyglądać tak:
Kamera obserwuje jakiś obiekt, co jakiś czas jest robiony screen (myślę że jeden na sekundę by wystarczył ponieważ obiekt nie będzie się poruszał z dużą prędkością). Screeny zapisywane są do BMP (ze względu na kompresje bezstratną) i porównywane. Tutaj zastanawiam się nad sposobem porównania tych screenów. Następnie program poda 4 bity w odpowiedniej kolejności i odpowiednim interwałem do silnika krokowego, który obróci kamerę ze względu na ruch przedmiotu.

Dziś wziąłem się do wykonania tego projektu. Na razie mam program, który pobiera obraz z kamery i zapisuje screeny co 1 sekundę. Na razie zapisywane są 3 screeny a każde następne nadpisują już istniejące w pętli tak aby nie zapychać dysku. Możliwe że trzeba będzie zwiększyć ich liczbę w zależności od ilości czasu jaką będzie potrzebował program do obróbki i porównania.

Sterowaniem silnika się nie martwię bo wiem jak to ma wyglądać. Już pisałem program do takiego układu tylko że w pascalu.

Co do porównania grafiki to tu mam największy problem. Jeszcze nigdy się tym nie bawiłem. Pisałem bazy danych i drobne programy sieciowe ale nigdy nie zajmowałem się grafiką. Zastanawiam się jaki algorytm użyć aby to miało ręce i nogi. Mam kilka pomysłów:

  1. bazowanie na konturach czyli przepuszczenie obrazka przez filtr i porównanie zmiany konturów: sposób prosty ale podatny na zakłócenia.
    2.Odejmowanie klatek czyli szukanie różnic na kolorowych obrazkach.
    3.Użycie filtrów cząstkowych: "particle filtering" czyli "nauczenie" programu jakiejś tekstury i wyszukiwanie jej na rysunku i sprawdzanie jej ruchu.
    Doszedłem do wniosku że chyba NIE MA sensu przeszukiwać całego obrazka tylko trzeba stworzyć "celownik" czyli mniejszy prostokąt, w którym będzie się działa cała akcja:) i program będzie sprawdzał czy dany element(tekstura) nie wysunęła się poza ten prostokąt i nastawiał kamerę tak aby tekstura wróciła do tego prostokąta. Na razie kamera ma się poruszać w poziomie ale jeżeli program będzie działał dobrze to to może dojedzie jeszcze ruch pionowy.

Proszę Was o sugestie, porady, wskazówki co najlepiej użyć, jak by wyście widzieli ten projekt, co w nim zmienić co dodać co usunąć.
Pozdrawiam.

0

Sposob drugi bedzie chyba najdokladniejszy (i relatywnie szybki w dzialaniu). Dodatkowo, o ile nie musisz, nie przechowuj klatek, ktore juz przetworzyles. Proponuje 3 osobne watki: glowny, zarzadzajacy programem, 1 dodatkowy do analizy bitmap, 2 dodatkowy do sterowania silnikiem. Problem wlasciwie dosyc prosty i nie wymagajacy duzo roboty.

0

Nie mam zamiaru przechowywać klatek przetworzonych. Myślę że nawet dwie wystarczą żeby je ze sobą porównywać. A każda następna będzie zastępować tą starszą. A jak pogodzić porównywanie klatek ze stroną w którą następuje zmiana pozycji przedmiotu śledzonego? A co jeśli zmiany będą z dwóch stron obrazka to program nie będzie wiedział co zrobić.

0

Zacząłem się bawić porównywaniem dwóch obrazków. Na razie pisze małe programiki, które z dwóch podanych "podobnych" obrazków tworzą trzeci, który ma pokazać gdzie są różnice. Przykładowy kod:
var i,j:integer;
begin
for i:=1 to 321 do
begin
for j:=1 to 241 do
begin
image3.Canvas.Pixels[i,j]:=image1.Canvas.Pixels[i,j]-image2.Canvas.Pixels[i,j];
end;
end;
Wiem że to amatorszczyzna na razie ale tak jak już pisałem nigdy się grafiką nie bawiłem. Niby w teorii kod jest dobry ale utworzony obrazek jest strasznie kolorowy a w zamyśle ma być biały/czarny tam gdzie jest obrazek identyczny a tam gdzie są zmiany ma pojawić się jakiś kolor. No niestety tak się nie dzieje. Jako że kamera jest nie najlepszej jakości to pewnie pojawiają się szumy. Tu mam problem jak zrobić to aby było dobrze. Czy da się jakoś inaczej niż przez canvas-bo zabiera on trochę czasu? Może przepuścić obrazki przez jakiś filtr przed porównaniem?

0

Napiszesz mi maila to moge ci pomoc :)

0

To juz zalezy do tego, co filmujesz. Zalozmy prosty przypadek, obrazek w skali szarosci 4x2 piksele o takich wartosciach:

Klatka starsza:

FF 80 FF 20
C0 99 F0 50

Klatka nowsza:

30 60 FF 00
D0 10 77 95

Roznica:

+CF +20 +00 +20
-10 -89 -79 -45

Wartosci bezwzgledne:

CF 20 00 20
10 89 79 45

Piksel FF nie zmienil koloru, wiec roznica wynosi 00 (kolor czarny), w przeciwnym wypadku wartosc bezwzgledna bedzie wieksza od zera (im wyzsza tym jasniejsza skladowa piksela).

Zalozmy, ze interesuje nas podzial na dwie czesci: lewo i prawo. Suma wartosci bezwzglednych z lewej wynosi CF+20+10+89=188, z prawej 00+20+79+45=DE. 188 > DE, zatem obraz z lewej zmienil sie bardziej.

Szybsze od Bitmap.Canvas.Pixels jest Bitmap.ScanLine, ale do niego najpierw musisz znac rozmiar piksela w bitach, czyli Bitmap.PixelFormat.

0

Wynik:
user image
Kod:
begin
for i:=1 to 320 do
begin
for j:=1 to 240 do
begin
image3.Canvas.Pixels[i,j]:=abs(image1.Canvas.Pixels[i,j]-image2.Canvas.Pixels[i,j]);
end;
end;

I czemu jest tyle kolorów? Mazak został przesunięty lekko w prawo

0

Nie mozesz odejmowac calych pikseli od siebie, bo pozyczenia/przeniesienia wystepuja z jednych skladowych kolorow na drugie. Kazda ze skladowych RGB musisz odjac osobno i osobno wyliczyc jej wartosc bezwzgledna.

Przyklad (RGB):
Twoja metoda: (00 01 00) czarny - (00 00 01) czarny = (00 00 FF) cyan
Prawidlowo: (00 01 00) czarny - (00 00 01) czarny = (00 01 01) czarny

Dlatego w opisie dla uproszczenia zalozylem obraz monochromatyczny, by miec tylko jedna skladowa koloru.

0

No fakt nie pomyślałem o tym.
Czyli kod powinien wyglądać np tak?:
begin
for wi:=0 to image1.picture.width do
begin
for he:=0 to image1.picture.height do
begin
R:=abs(getRvalue(image1.canvas.pixels[wi,he])-getRvalue(image2.canvas.pixels[wi,he]);
g:=abs(getgvalue(image1.canvas.pixels[wi,he])-getgvalue(image2.canvas.pixels[wi,he]));
b:=abs(getbvalue(image1.canvas.pixels[wi,he])-getbvalue(image2.canvas.pixels[wi,he]));
image3.canvas.pixels[wi,he]:=rgb(R,G,B);
end;
end;
Efekt jest już lepszy :)
user image

0

Jeszcze wartosci bezwzgledne...

Bezposrednio metoda okreslenia jeszcze kierunku zmian wymaga troche poglowkowania, ale juz wiesz w ktorej czesci obrazu w ogole zaszly zmiany. Tu widac naocznie, ale numerycznie obszar zmian mozesz wykryc, jak pokazalem. Podziel obraz na okreslona liczbe pol (powiedzmy 10x10) i zsumuj w kazdym nich wartosci odchylen od zera. Jesli szukasz najwiekszych zmian, znajdz obszar z wieksza suma, jesli szukasz mozliwie duzego obrysu, szukaj powyzej pewnego ustalonego progu (w takim przypadku dla wyostrzenia roznic znormalizuj zakres kolorow obrazu).

0

A jak by to wyglądało na szybszym Scanline? Obrazki są 24bitowe.
Czyli jak się nie mylę:
image1.Picture.Bitmap.PixelFormat:=pf24bit;
image2.Picture.Bitmap.PixelFormat:=pf24bit;

0

Pisane z palca:

type TLine = array[word] of TRGBTriple;
     PLine = ^TLine;
var Line1: PLine;
    Line2: PLine;
    Line3: PLine;
    x, y: Integer;
begin
y := Bitmap1.Height;
while (y > 0) do begin
  dec(y);
  Line1 := Bitmap1.ScanLine[y];
  Line2 := Bitmap2.ScanLine[y];
  Line3 := Bitmap3.ScanLine[y];
  for x:=0 to Bitmap.Width - 1 do begin
    Line3[x].R = abs(Line1[x].R - Line2[x].R);
    Line3[x].G = abs(Line1[x].G - Line2[x].G);
    Line3[x].B = abs(Line1[x].B - Line2[x].B);
    inc(Line1);
    inc(Line2);
    inc(Line3);
  end;
end;
0

Dzięki wielkie za pomoc :)
Zmieniłem trochę kod na:

var x,y:integer;
    line1, line2, line3: Pline;
begin
for y:=1 to image1.Picture.Bitmap.Height-1 do
  begin
  line1:=image1.Picture.Bitmap.scanline[y];
  line2:=image2.Picture.Bitmap.scanline[y];
  line3:=image3.Picture.Bitmap.ScanLine[y];
  for x:=1 to image1.Picture.Bitmap.Width-1 do
  begin
    Line3[x].rgbtRed := abs(Line1[x].rgbtRed - Line2[x].rgbtRed);
    Line3[x].rgbtGreen := abs(Line1[x].rgbtGreen - Line2[x].rgbtGreen);
    Line3[x].rgbtBlue := abs(Line1[x].rgbtBlue - Line2[x].rgbtBlue);

    end;
    end;

Ale wyskakuje "Scan line index out of range"

Ktoś wie jak naprawić ten błąd?

1 użytkowników online, w tym zalogowanych: 0, gości: 1