wyświetlanie obrazu
Transkrypt
wyświetlanie obrazu
Ćwiczenie 8 FILTRACJA W DZIEDZINIE CZĘSTOTLIWOŚCI Zakres pracy W ramach ćwiczenia należy do przygotowanego wcześniej programu dodać możliwość przeprowadzania następujących operacji: 1. wyświetlanie obrazu po przemnożeniu go przez tzw. okno Hamminga, 2. wyświetlanie wykresu amplitudy i fazy po przeprowadzeniu transformacji Fouriera obrazu wejściowego, 3. zaznaczanie (za pomocą myszy) obszarów usuwanych z obrazu w dziedzinie częstotliwości (a więc z wyniku transformacji Fouriera), 4. filtracja, czyli odtworzenie obrazu wejściowego ze zmodyfikowanej (w punkcie 3) transformaty Fouriera. Zadania te dotyczą wyłącznie obrazów 8-bitowych. Realizując punkt 3 należy umożliwić rysowanie na obrazie wynikowym obszarów w kształcie koła (lewy przycisk myszy) lub prostokąta (prawy przycisk). W normalnym trybie obszary te powinny mieć kolor zielony o przeźroczystości 70 - będą wówczas reprezentować regiony uwzględnione w filtracji. Rysowanie z wciśniętym przyciskiem SHIFT powinno skutkować powstawaniem obszarów w kolorze czerwonym, a więc wykluczonych. Podwójne kliknięcie prawym przyciskiem ma wykluczać cały obszar obrazu, podwójne kliknięcie lewym - usuwać wszystkie obszary. Informacje pomocnicze Jednowymiarowe okno Hamminga to funkcja opisana następującym wzorem 2n w(n) cos N 1 gdzie = 0,53836, = 1 - , natomiast N oznacza szerokość okna. W naszym (dwuwymiarowym) przypadku N będzie równe przekątnej obrazu, natomiast n zostanie określone wzorem n N 2r, gdzie r oznacza odległość od środka obrazu. Nałożenie na obraz okna Hamminga zapobiega niepożądanym zjawiskom, jakie do transformaty Fouriera mogłyby wprowadzać granice obrazu. Transformacja Fouriera obrazu o wymiarach M×N odbywa się wg wzoru: M 1 N 1 F (u, v) I ( x, y) exp i 2 ux / M vy / N x 0 y 0 gdzie I(x,y) oznacza jasność piksela w punkcie (x,y). Należy pamiętać o następujących zależnościach e ix cosx i sin x e ix cosx i sin x W wyniku transformacji otrzymujemy liczbę zespoloną (złożoną z części rzeczywistej Re i urojonej Im), którą możemy reprezentować w postaci modułu (amplitudy) i argumentu (fazy). Aby prawidłowo wykreślić amplitudę i fazę transformaty Fouriera należy ćwiartki obrazu przemieścić w sposób pokazany poniżej (wewnątrz poszczególnych obszarów nie ma żadnych przemieszczeń). Transformacja odwrotna odbywa się wg wzoru I ( x, y ) 1 MN M 1 N 1 F (u, v) expi 2 ux / M vy / N u 0 v 0 Powinniśmy z niej otrzymać liczby zespolone o zerowej części urojonej. Mnożenie liczb zespolonych opisuje wzór: (a bi) (c di) ac bd i(ad bc) Przed wykonaniem transformacji odwrotnej należy przywrócić właściwą kolejność ćwiartek, a po jej przeprowadzeniu przemnożyć wynik przez odwrotność okna Hamminga. Wskazówki implementacyjne 1. Aby uzyskać prawidłowy wykres amplitudy (modułu), należy zastosować skalę logarytmiczną: I log(1 I ) oraz przeskalować jasności do zakresu 0..255. 2. Przy wykreślaniu fazy (argumentu) w celu zmniejszenia niestabilności wyniku należy przyjąć, że gdy wartość części rzeczywistej jest mniejsza od 10-3, faza wynosi /2 lub - /2 (w zależności od znaku części urojonej). 3. Przed wyświetleniem obrazu przefiltrowanego należy przeskalować uzyskane wartości jasności do przedziału 0..255. 4. Aby umożliwić rysowanie figur o barwach przeźroczystych, trzeba skorzystać z GDI+. W tym celu w pliku PO1.h należy umieścić następujące polecenia: #include <gdiplus.h> using namespace Gdiplus; Poza tym w klasie CPOApp należy dodać następujące pola: GdiplusStartupInput gdiplusParametry; ULONG_PTR gdiplusToken; W metodzie CPOApp::InitInstance() inicjujemy GDI+: GdiplusStartup(&gdiplusToken, &gdiplusParametry, NULL); W metodzie CPOApp::ExitInstance() wyłączamy GDI+: GdiplusShutdown(gdiplusToken); 5. Aby rysowanie przebiegało płynnie (bez migotania), należy wykorzystać kontekst pamięciowy. Do metody CImgWnd::Create(...) dodajemy następujący kod: CDC* pDC = GetDC(); dcMemory.CreateCompatibleDC(pDC); //dcMemory to pole typu CDC CBitmap memBM; memBM.CreateCompatibleBitmap(pDC, szer_okna, wys_okna); dcMemory.SelectObject(memBM); ReleaseDC(pDC); Teraz możemy wewnątrz CImgWnd::OnPaint() rysować na kontekście pamięciowym używając GDI+: Graphics gr(dcMemory); SolidBrush pedzelCzerwony(Color(70, 255, 0, 0)); Rect r = Rect(10,10,100,100); gr.FillRectangle(&pedzelCzerwony,r); Na koniec gotowy obraz kopiujemy do kontekstu ekranowego: dc.BitBlt(0, 0, szer_okna, wys_okna, &dcMemory, 0, 0, SRCCOPY); 6. Obszary rysowane na ekranie mają stanowić maskę filtra (wyłączać określone częstotliwości). Dlatego równolegle warto tworzyć złożoną z tych samych obszarów binarną mapę, która zostanie nałożona na część rzeczywistą i urojoną transformaty Fouriera. Trzeba jednak pamiętać, że obraz w oknie jest przeskalowany, a transformata ma rozmiary obrazu rzeczywistego. Dlatego najprościej jest potraktować maskę filtra jako bitmapę i - korzystając z kolejnego kontekstu pamięciowego - przeskalować ją przy użyciu funkcji StretchBlt(): StretchBlt(dcDest, 0, 0, DestWidth, DestHeight, dcSrc, 0, 0, SrcWidth, SrcHeight, SRCCOPY);