sobota, 29 grudnia 2012

Do biegu gotowi? LavaVision!

0 komentarze
Kończy się rok, a ja już myślę o następnym. To nie był rok końca świata, tylko rok narodzin. W tym roku powołałem do życia moje nowe przedsięwzięcie - firmę LavaVision. Doświadczenia zdobyte w ostatnich latach w różnych projektach badawczo-rozwojowych będą teraz miały szanse wykorzystania w zastosowaniach praktycznych.

Z grupą współpracujących osób planujemy przedstawić kilka innowacyjnych rozwiązań z zakresu wizji komputerowej, naturalnych interfejsów użytkownika oraz systemów komputerowego wsparcia diagnostyki obrazowej. Lubimy eksperymentować i dlatego jesteśmy otwarci na nowe pomysły. Pierwszym wyzwaniem było dla nas ożywienie miedziorytów krakowskiego artysty Krzysztofa Skórczewskiego. Efektu nie da się opisać słowami. Trzeba to było zobaczyć (i poczuć) na żywo.

Jeśli chcesz być na bieżąco z naszymi działaniami zaglądaj koniecznie na nasz Fan Page na Facebooku. W 2013 będzie się działo :-) Trzymajcie za nas kciuki!



poniedziałek, 5 marca 2012

Algorytm Perona-Malik

7 komentarze
Pełny kod źródłowy
Jak usunąć szum z obrazu nie tracąc przy okazji cennej informacji o lokalizacji krawędzi? Odpowiedź na to pytanie można znaleźć w mojej Rozprawie doktorskiej w rozdziale 3.2 - Nieliniowe wygładzenie obrazu bazujące na funkcji Gaussa. Rozpoczyna się on tak:

Pomimo tego, że rozmycie funkcją Gaussa lepiej sobie radzi z zachowaniem granic między obiektami niż zastosowanie średniej arytmetycznej, to w rzeczywistości każdy punkt jest tak samo przetwarzany, niezależnie od tego czy leży wewnątrz obiektu, czy też w pobliżu jego granicy. Jest to znana cecha filtrów liniowych. Skuteczniejszym rozwiązaniem mogłoby się okazać indywidualne traktowanie każdego punktu w zależności od jego lokalizacji, tak aby proces filtracji był wzmocniony na obszarach jednolitych, a zahamowany w pobliżu krawędzi. Jedną z pierwszych prac w tym zakresie był artykuł Perona i Malika[53].

[53] P. Perona, J. Malik. Scale-space and edge detection using anisotropic diffusion. IEEE Transactions on Pattern Analysis and Machine Intelligence, 12(7):629–639,July 1990.
[Rozprawa doktorska, str. 28]

Nie będę tutaj powtarzał treści, którą można doczytać we właściwym tekście rozprawy. Chciałbym jedynie podzielić się dodatkowymi materiałami, które opracowałem przy okazji jej pisania.

Najpierw chciałbym polecić rozprawę doktorską Bartosza Jabłońskiego "Równania różniczkowe cząstkowe w problemach filtracji obrazów i trajektorii przestrzennych" jako świetne wprowadzenie do tematyki nieliniowej filtracji obrazów. Jest ona w całości dostępna w sieci jako plik PDF. Drugi rozdział tej pracy "Porównanie metod filtracji dla obrazów dwuwymiarowych" to w mojej opinii najlepszy polskojęzyczny materiał na ten temat.

Ja osobiście poza krótkim opisem w rozprawie jak działa algorytm Perona-Malik i porównaniem tego algorytmu do klasycznej filtracji Gaussa mogę podzielić się jeszcze jego implementacją. Opracowałem ją na podstawie książki "Mathematical Problems in Image Processing" (autorzy: Gilles Aubert i Pierre Kornprobst) z wykorzystaniem biblioteki CImg. Kod jest dostępny w moim publicznym repozytorium GitHub.

I jeszcze na "deser" prezentacja działania algorytmu Perona-Malik dla kilku wybranych obrazów.

wtorek, 21 lutego 2012

Rozprawa w sieci

0 komentarze
Przyszedł w końcu ten czas, aby podzielić się w sieci pełnym tekstem rozprawy doktorskiej, nad którą pracowałem przez ostatnie 3 lata. Rozprawa sama w sobie jeszcze nie jest "obroniona", dlatego tym bardziej zależy mi na tym, aby była ona dostępna dla każdego, kto chce przyjść na jej publiczną prezentację, która została zaplanowana na 1 marca na AGH (więcej szczegółów).

Nie będę teraz szczegółowo rozpisywał się czym zajmowałem się w trakcie prowadzonych badań i jakie wyniki udało mi się uzyskać. Zapraszam po prostu do przekartkowania tekstu, który zamieściłem poniżej.

Postaram się w najbliższym czasie podzielić dodatkowymi przykładami, algorytmami i narzędziami, które zostały opracowane na potrzeby pracy, a z powodu przyjętych ograniczeń (np. nie opisywałem kodu źródłowego i skrótowo przedstawiałem powszechnie znane algorytmy) nie zostały one umieszczone w tekście właściwym.


Rafał Petryniak (wersja elektroniczna w serwisie Scribd)

Aktualizacja: 2 marca 2012

Poniżej zamieszczam prezentację z obrony. Pozostałe dodatkowe materiały powiązane z doktoratem znajdują się na stronie:


poniedziałek, 9 stycznia 2012

Aplikacja interaktywna w OpenCV reagującą na ruch

5 komentarze
Pełny kod źródłowy
Kurs programowania systemów interaktywnych z wykorzystaniem OpenCV, który jakiś czas temu rozpocząłem na łamach tego bloga (patrz: etykieta OpenCV), byłby niekompletny, gdyby zabrakło opisu interakcji ruchowej. Obsługa klawiatury i myszki są oczywiście ważne, ale już na nikim nie robią szczególnego wrażenia. OpenCV jak wiadomo jest przeznaczony do śledzenia i analizy ruchu i to jest jego główna siła.

W dzisiejszym wpisie zostanie pokazane jak dodać kolejną warstwę interakcji poprzez analizę i detekcję ruchu. Wpis ten będzie naturalnym rozszerzeniem przykładów omawianych wcześniej, dlatego zachęcam do ich wcześniejszej analizy. Szczególnie istotne są dwa z nich:
Dobra wiadomość jest taka, że dzięki wprowadzeniu obiektowego szkieletu aplikacji, rozszerzenie aplikacji o interakcję ruchową będzie niezwykle proste i nie będzie wymagało wielu zmian w kodzie. Dla przykładu kod kulki praktycznie pozostaje bez zmian. Logika aplikacji jest również taka sama jak dla poprzedniego przykładu.

Pierwszą rzeczą, którą należy zrobić jest dodanie kilku pomocniczych zmiennych, które będą zawierać pośrednie etapu przetwarzania obrazu.

// plik: interactive_app_tutorial-MainApp.h
class MainApp
{
private:
  Mat cam_frame, img_gray, img_first, img_prev, img_diff, img_bin;

Następnie należy rozszerzyć funkcję update klasy MainApp o sprawdzanie na ile każda z kulek pokrywa się z fragmentami obrazu binarnego, gdzie został wykryty ruch.

// plik: interactive_app_tutorial-MainApp.cpp
for (int k = 0; k < myBall.size(); k++) {
  movementAmount = 0;
  ball_dim = myBall[k].getDim();
  ball_x   = myBall[k].getX();
  ball_y   = myBall[k].getY();
  
  for (int i=(-1*ball_dim); i<ball_dim; i++)
    for (int j=(-1*ball_dim); j<ball_dim; j++)
      if ( dist(0,0,i,j) < ball_dim ) {          
        canvas_ind_x = ball_x + i;
        canvas_ind_y = ball_y + j;
        
        if ( canvas_ind_x<=0 || canvas_ind_y<=0 || 
             canvas_ind_x>CANVAS_WIDTH || canvas_ind_y>CANVAS_HEIGHT) 
          continue; 
        
        if ( img_bin.at<uchar>(canvas_ind_y,canvas_ind_x) > 30) {
          movementAmount++;
        }
      }
      
      if (movementAmount > max_motion_points)
        myBall[k].decrementLives();
      else
        if (!pauseBallsMove) myBall[k].update();
}

W powyższym przykładzie widać, że jeśli więcej niż 30 pikseli leży na masce (zmienna img_bin) wskazującej miejsce detekcji ruchu to możemy uznać, że kulka została "dotknięta". Oczywiście można zmienić tą wartość na inną, lub nawet ustalać ją w zależności od wielkości elementu zbijanego. To prawdopodobnie byłoby lepsze rozwiązanie od obecnego, dlatego zachęcam czytelników do samodzielnych eksperymentów.