Architektura Systemów Komputerowych, Wydział Informatyki, ZUT Laboratorium: MS Windows obsługa kamery Krok 1. Uruchamiamy Visual Studio C++. Otwieramy nowy projekt (menu-File- New-Project). Konfigurujemy tak jak na rysunku poniżej. Projekt umieszczamy w katalogu C:\users\inazwisko gdzie inazwisko to pierwsza litera swojego imienia i nazwisko (bez spacji). Rys. 1. Okno zakładania nowego projektu Krok 2. Konfigurujemy wzorzec projektu tak jak na rysunku poniżej (typu Single Document). Rys. 2. Okno konfiguracji projektu 1 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11 Architektura Systemów Komputerowych, Wydział Informatyki, ZUT Krok 3. Otwieramy plik KameraView.h i wprowadzamy modyfikacje: 1) dołączamy plik Vfw.h (rys.3) 2) dodajemy pole HWND m_wndKamera (rys.3) 3) Dodajemy symbol ID_CAMWND (rys.4) 1 2 Rys. 3. Modyfikacje w pliku KameraView.h 3 Rys. 4. Dodawanie symbolu ID_CAMWND 2 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11 Architektura Systemów Komputerowych, Wydział Informatyki, ZUT Krok 4. Do klasy CKameraView dodajemy metodę OnInitialUpdate (rys.5). Wpisujemy kod do utworzonej metody (rys.6). Następnie uruchamiamy program. Powinniśmy otrzymać komunikat błędu taki jak na rys.7. void CKameraView::OnInitialUpdate(){ CView::OnInitialUpdate(); // TODO: Add your specialized code here HWND hwndParent = this->GetSafeHwnd(); m_wndKamera = capCreateCaptureWindow( L"Nazwa", WS_CHILD | WS_VISIBLE, 10, 10, 640, 480, hwndParent, ID_CAMWND ); }//--------------------------------------------------- Rys. 6. Kod w OnInitialUpdate() Rys. 7. Spodziewany komunikat błędów Rys. 5. Dodawanie metody OnInitialUpdate komentarz: Funkcja capCreateCaptureWindow tworzy okno przechwytywania strumienia wideo. Wykorzystując technologię Vfw (Video for Windows) trzeba takie okno utworzyć, nawet jeśli nie chcemy aby było widoczne. Zaleca się spojrzenie do dokumentacji MSDN w celu przeanalizowania argumentów przekazywanych do funkcji. Ciekawszą obserwacją może być sposób otrzymania uchwytu do naszego okna (CKameraView), które ma się stać oknem nadrzędnym względem tworzonego. Wykorzystana jest do tego metoda GetSafeHwnd. 3 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11 Architektura Systemów Komputerowych, Wydział Informatyki, ZUT Krok 5. Dodajemy do linkera powiązanie z biblioteką vfw32.lib (rys.8). Następnie uruchamiamy program ponownie. Rys. 8. Dodanie powiązania do vfw32.lib komentarz: Tym razem kompilacja i łączenie powinno przejść pomyślnie. W wyniku powinniśmy otrzymać okno z czarnym kwadratem w środku o rozmiarach 640 na 480. W tym kwadracie w dalszych etapach będzie pokazywany obraz z kamery po jego przetworzeniu wykonanym przez nas. Krok 6. Enumeracja urządzeń. komentarz: Wykorzystując Vfw można obsługiwać do 10 urządzeń wideo. Każde dostaje swój numer. Które urządzenie jest gotowe do obsługi i które ma jaki numer można określić wykorzystując funkcje capGetDriverDescription, capDriverGetName oraz capDriverGetCaps. Do połączenia się ze sterownikiem służy funkcja capDriverConnect a do rozłączenia capDriverDisconnect. Zadanie: Wypisać listę wszystkich urządzeń ich statusu oraz stanu ich gotowości: 1) Dodać tablicę łańcuchów (CString) do klasy CKameraView (rys.9). 2) Dodać kod do enumeracji w OnInitialUpdate (rys.10). 3) Przeanalizować dodany kod, w sytuacji jakby coś nie było wystarczająco jasne KONIECZNIE PYTAĆ (ważne aby rozumieć) 4) Dodać kod do OnDraw wypisujący listę urządzeń na ekranie (rys.11). 4 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11 Architektura Systemów Komputerowych, Wydział Informatyki, ZUT 5) class CKameraView : public CView { protected: // create from serialization only CKameraView(); DECLARE_DYNCREATE(CKameraView) protected: HWND m_wndKamera; CString m_strDriversEnum[10]; CString m_strDriversParam[10]; bool m_bIsDrivers[10]; & Rys. 9. Modyfikacje w KameraView.h dodanie tablic łańcuchów z opisem urządzeń void CKameraView::OnInitialUpdate(){ CView::OnInitialUpdate(); HWND hwndParent = this->GetSafeHwnd(); m_wndKamera = capCreateCaptureWindow( L"Nazwa", WS_CHILD | WS_VISIBLE, 10, 10, 640, 480, hwndParent, ID_CAMWND ); for( int i =0; i<10; i++ ){ bool res = capDriverConnect( m_wndKamera, i ); m_bIsDrivers[i] = res; m_strDriversParam[i] = L" "; if( res ){ char mstring[80]; capDriverGetName( m_wndKamera, mstring, 80 ); CAPDRIVERCAPS drvCaps; capDriverGetCaps( m_wndKamera, &drvCaps, sizeof( drvCaps ) ); m_strDriversEnum[i].Format( L"%d. %s", i, mstring ); if( drvCaps.fHasDlgVideoSource ){ m_strDriversParam[i] += L"SourceDLG; "; }; if( drvCaps.fHasDlgVideoFormat ){ m_strDriversParam[i] += L"FormatDLG; "; }; if( drvCaps.fCaptureInitialized ){ m_strDriversParam[i] += L"INIT; "; }; }else{ m_strDriversEnum[i].Format( L"%d. ----------", i ); }; capDriverDisconnect( m_wndKamera ); }; }//----------------------------------------------------------------------------- Rys. 10. Kod w metodzie OnInitialUpdate 5 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11 Architektura Systemów Komputerowych, Wydział Informatyki, ZUT void CKameraView::OnDraw(CDC* pDC){ int y = 20; for( int i =0; i<10; i++ ){ pDC->TextOut( 700, y, m_strDriversEnum[i] ); y += 20; if( m_bIsDrivers[i] ){ pDC->TextOut( 700, y, m_strDriversParam[i] ); y += 20; }; }; }//----------------------------------------------------------------------------- Rys. 11. Kod w metodzie OnDraw wyświetlanie tekstów komentarz: Pomysł algorytmu jest dość prosty. Na etapie inicjalizacji w pętli pytane jest wszystkie 10 możliwych urządzeń (pętla for) o parametry. W tym celu należy połączyć się ze sterownikiem urządzenia (capDriverConnect) i jeśli połączenie się udało (sterownik właściwy) to zapytać go o nazwę (capDriverGetName) oraz zapewniane przez sterownik możliwości (capDriverGetCaps, końcówka -Caps od Capabilities). Procedura wypełnia właściwe łańcuchy (odpowiednio m_strDriversEnum do numeru oraz nazwy oraz m_strDriversParam do zapewnianych możliwości) oraz wypełnia informacje, czy sterownik był dostępny(m_bIsDrivers). Obie tablice łańcuchów jak również informacja o dostępności sterownika wykorzysytwana jest w procedurze obsługi komunikatu WM_PAINT (rysowanie ekranu, metoda OnDraw w tym przypadku). Krok 7. Połączenie ze sterownikiem kamery, ustawienie parametrów kamery i pobieranie ramki obrazu. Zadanie: Pokazać w oknie właściwy obraz z kamery i pobierać nową ramkę na żądanie ręcznie oraz automatycznie w procedurze obsługi WM_TIMER: 1) Połączyć się ze sterownikiem oraz uruchomić okno ustalania parametrów (capDlgVideoFormat) (rys.12). 2) Uruchomić aplikację, powinno na starcie uruchomić się okno dialogowe z możliwością ustawiania parametrów kamery. Ustawić rozdzielczość 640x480 oraz kompresje na RGB24 (rys.13). 3) Dodać do menu aplikacji nowe wpisy (rys.14). 4) Dodać metodę na obsługę zdarzenia wybrania komendy Pobierz ramke z menu (rys.15). 5) Dodać obsługę komunikatów OnKameraFilmTimerStart oraz OnKameraFilmTimerStop analogicznie do punktu 4. 6) Dodać metodę OnTimer obsługujące komunikat WM_TIMER (rys.17). W celu obsługi komunikatu od Timera należy dodać symbol ID_TIMER, podobnie jak to robione było z symbolem ID_CAMWND (rys.4). 7) Wypisać kod do nowo utworzonych metod zgodnie z rys.18. 6 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11 Architektura Systemów Komputerowych, Wydział Informatyki, ZUT void CKameraView::OnInitialUpdate(){ & bool res = capDriverConnect( m_wndKamera, 0 ); capDlgVideoFormat( m_wndKamera ); }//----------------------------------------------------------------------------- Rys. 12. Kod w metodzie OnDraw wyświetlanie tekstów Rys. 13. Przykładowe okno dialogowe ustawiania parametrów. Rys. 14. Nowe wpisy w MENU. void CKameraView::OnKameraPobierz(){ capGrabFrame( m_wndKamera, true ); }//---------------------------------------- Rys. 15. Dodanie metody Rys. 16. Kod OnKameraPobierz. obsługi komunikatu ID KAMERA POBIERZ 7 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11 Architektura Systemów Komputerowych, Wydział Informatyki, ZUT //------------------------------------------------ void CKameraView::OnKameraFilmTimerStart(){ SetTimer( ID_TIMER, 50, NULL ); }//----------------------------------------------- //------------------------------------------------ void CKameraView::OnKameraFilmTimerStop(){ KillTimer( ID_TIMER ); }//----------------------------------------------- //------------------------------------------------ void CKameraView::OnTimer(UINT_PTR nIDEvent){ capGrabFrame( m_wndKamera, true ); CView::OnTimer(nIDEvent); }//----------------------------------------------- Rys. 17. Dodanie komunikatu Rys. 18. Kod metod OnKameraFilmTimerStart, OnTimer. OnKameraFilmTimerStop oraz OnTimer Krok 8. Modyfikacje na obrazie odwracanie obrazu. Zadanie: Pokazać w oknie zanegowany obraz z kamery (tam gdzie był kolor biały ma być czarny itd.): 1) Dodać funkcję cbFunct. Ma znaczenie w którym miejscu w pliku CKameraView.cpp umieścimy tą funkcję. Powinna być umieszczona przed odwołaniem się do niej, czyli przed metodą OnInitialUpdate. Proponuje się umieszczenie jej kodu bezpośrednio za konstruktorem klasy. 2) Zmodyfikować metodę OnInitialUpdate dodając na końcu wywołanie funkcji capSetCallbackOnFrame (rys.20). //------------------------------------------------------------------------------ LRESULT CALLBACK cbFunct(HWND hWnd, LPVIDEOHDR lpVHdr){ long size = lpVHdr->dwBufferLength; for( int x=0; x<640; x++ ) for( int y = 0; y<480; y++ ){ int i0 = x*3 + y * 3 * 640; lpVHdr->lpData[i0] = 255 - lpVHdr->lpData[i0]; // BLUE lpVHdr->lpData[i0+1] = 255 - lpVHdr->lpData[i0+1]; // GREEN lpVHdr->lpData[i0+2] = 255 - lpVHdr->lpData[i0+2]; // RED }; return (LRESULT) TRUE ; }//----------------------------------------------------------------------------- void CKameraView::OnInitialUpdate(){ & capSetCallbackOnFrame( m_wndKamera, cbFunct ); }//----------------------------------------------------------------------------- 8 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11 Architektura Systemów Komputerowych, Wydział Informatyki, ZUT komentarz: Jest to typowe wykorzystanie mechanizmu CallBack. Przypominam w skrócie, że polega on na tym, że do urządzenia przekazujemy adres funkcji którą chcemy aby on wykonał na skutek wybranego zdarzenia. Alternatywą do tego mechanizmu jest na przykład mechanizm wykorzystujący pętle komunikatów, na przykład ten znany z systemu MS Windows. W tym konkretnym przypadku zdarzeniem jest odebranie nowej ramki obrazu z kamery. Gdy nowa ramka nadejdzie sterownik urządzenia po jej sformatowaniu wywoła funkcję cbFunct. Pytanie do zastanowienia brzmi, jakie mogą być potencjalne wady wykorzystania mechanizmu CallBack, tak że nie jest on dominującym mechanizmem. Krok 9. (praca samodzielna) (1) Rysować tylko składową zieloną, (2) rysować obraz w odcieniach szarości (współczynniki uśredniania R:0.2125, G:0.7154, B:0.0721) (3) rysować obraz w odcieniach szarości ze współczynnikami uśredniania 1/3 i porównać z pp.2, (3) Wykonać progowanie (thresholding) dla progu równego 50 i eksperymentować z innymi wartościami progu. Krok 10. (praca samodzielna) Rysowanie na obrazie funkcja rysująca zielony kwadrat pomiędzy zadanymi współrzędnymi (x0, y0) oraz (xk, yk). Krok 11. (praca samodzielna) Znajdowanie współrzędnej punktu ze wskaznika laserowego (najbardziej czerwonego punktu na obrazie) i oznaczanie jej (śledzenie obiektu). Wypisać pod obrazem współrzędne tego obiektu. Krok 12. (praca samodzielna) Konkurs na najlepszy projekt na roku dotyczący rozpoznawania obrazów z kamery. Najlepszy projekt będzie nagrodzony oceną bardzo dobrą z całości kursu (laboratorium oraz wykładu). Linki do ciekawych pomysłów tego typu aplikacji (proszę nie ograniczać pomysłów do tego typu aplikacji, czym ciekawszy pomysł tym lepiej): http://www.youtube.com/watch?v=fGFypLpxD7M&feature=related http://www.youtube.com/watch?v=ml4_MIM52g0&feature=related http://www.youtube.com/watch?v=YBorbRaFrn8 http://www.youtube.com/watch?v=i_bZNVmhJ2o http://www.youtube.com/watch?v=exbGdHpFiW0 9 Opracował: Mariusz Kapruziak (mkapruziak@wi.ps.pl) wersja: 2009-11-11