Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 1
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
Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 2
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)
Rys. 3. Modyfikacje w pliku KameraView.h
1
2
Rys. 4. Dodawanie symbolu ID_CAMWND
3
Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 3
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.
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.
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. 5. Dodawanie metody
OnInitialUpdate
Rys. 6. Kod w OnInitialUpdate()
Rys. 7. Spodziewany komunikat błędów
Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 4
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).
Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 5
5)
Rys. 9. Modyfikacje w KameraView.h – dodanie tablic łańcuchów z opisem urządzeń
Rys. 10. Kod w metodzie OnInitialUpdate
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
);
};
}
//-----------------------------------------------------------------------------
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];
…
Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 6
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.
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;
};
};
}
//-----------------------------------------------------------------------------
Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 7
Rys. 12. Kod w metodzie OnDraw – wyświetlanie tekstów
Rys. 13. Przykładowe okno dialogowe ustawiania parametrów.
void
CKameraView::OnInitialUpdate(){
…
bool
res = capDriverConnect( m_wndKamera, 0 );
capDlgVideoFormat( m_wndKamera );
}
//-----------------------------------------------------------------------------
void
CKameraView::OnKameraPobierz(){
capGrabFrame(
m_wndKamera,
true
);
}
//----------------------------------------
Rys. 15. Dodanie metody
obsługi komunikatu
ID KAMERA POBIERZ
Rys. 16. Kod OnKameraPobierz.
Rys. 14. Nowe wpisy w MENU.
Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 8
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).
//------------------------------------------------
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);
}
//-----------------------------------------------
//------------------------------------------------------------------------------
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 );
}
//-----------------------------------------------------------------------------
Rys. 17. Dodanie komunikatu
OnTimer.
Rys. 18. Kod metod OnKameraFilmTimerStart,
OnKameraFilmTimerStop oraz OnTimer
Architektura Systemów Komputerowych, Wydział Informatyki, ZUT
Opracował: Mariusz Kapruziak (
mkapruziak@wi.ps.pl
) wersja: 2009-11-11 9
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 (x
0
, y
0
) oraz (x
k
, y
k
).
Krok 11.
(praca samodzielna) Znajdowanie współrzędnej punktu ze wskaźnika
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