Filtracja obrazu CPU (aktualizacja 1.IV.2014).
Transkrypt
Filtracja obrazu CPU (aktualizacja 1.IV.2014).
SMOR - LABORATORIUM 1 FILTRACJA OBRAZU CPU CEL I ZADANIA Celem laboratorium jest zapoznanie studentów z problemami programowania współbieżnego. Zadanie: rozszerz załączoną aplikację "FiltracjaObrazu" o: 1. 2. 3. Synchronizację zapisywania i wyświetlania obrazu przefiltrowanego. Filtrowanie (filtr Sobela) obrazu w jednym wątku. Filtrowanie (filtr Sobela) obrazu wielowątkowe (równoległe). Założenia: Możliwe jest wielokrotne, jednoczesne wywołanie funkcji filtracji. Liczba wątków w przetwarzaniu równoległym może być z góry ustalona (ale >= 4). INFORMACJE SZCZEGÓŁOWE Załączona aplikacja wymaga Visual Studio w wersji co najmniej 2010. Wymaga zainstalowanej biblioteki MFC, a więc nie można jej uruchomić w wersjach Visual Studio Express Edition. Do wykonania laboratorium wystarczy ImageProcessing.cpp. Pliki te zawierają: modyfikacja jedynie dwóch plików: ImageProcessing.h i Klasę ImageProcessing, która zawiera funkcję rysowania obrazu: drawImage (przefiltrowanego lub nie - w zależności od wartości checkboxa "Pokaż wynik filtracji"). Treść funkcji wątku filteringThreadFunction, który jest uruchamiany w momencie naciśnięcia przycisku "Uruchom filtrację". Po zakończeniu wątek ten wysyła wiadomość do głównego okna WMU_PROCESSING_COMPLETE, informującą, że proces został zakończony. Główny wątek programu w obsłudze tej wiadomości sprawdza, czy ustawiony jest checkbox "Filtracja ciągła". Jeśli tak, to po zakończeniu ponownie tworzony jest wątek wywołujący funkcję filtracji. Dzieje się to do momentu odznaczenia tej flagi. Funkcję processImage, która jest odpowiedzialna za przetwarzanie obrazu. Zaimplementowana w obecnej postaci funkcja processImage pokazuje w jaki sposób uzyskać dostęp do elementów obrazu z klasy SimpleImage oraz wykonuje prostą operację zwiększenia wartości wszystkich pikseli. Zadaniem studenta jest zmiana tej funkcji na funkcję filtracji krawędziowej za pomocą filtru Sobela (opisany w kolejnym rozdziale). Należy także zmodyfikować funkcję OnInitDialog w pliku FiltracjaObrazuDlg.cpp w polu „TODO: Add extra initialization here”. Funkcja ta jest uruchamiana jeden raz w czasie tworzenia okienka dialogowego. Można w niej wpisać stworzenie obiektu synchronizacyjnego, np. typu Mutex. W innych funkcjach będzie można uzyskać dostęp do tego obiektu poprzez funkcję OpenMutex, gdzie jako parametr podana będzie jego nazwa. Rysunek 1 przedstawia główne okno aplikacji. Interfejs pozwala na wczytanie obrazu (format JPG) lub wygenerowanie obrazu testowego. Po wciśnięciu przycisku "Rozpocznij filtrację" następuje jednokrotna filtracja za pomocą napisanej funkcji wątka, o ile nie została zaznaczona flaga "Filtracja ciągła", która powoduje, że filtracja jest wykonywana ciągle jedna po drugiej. Zaznaczenie flagi "Pokaż wynik filtracji" powoduje wyświetlanie obrazu przefiltrowanego zamiast oryginalnego. Rysunek 1: Główne okno aplikacji. FILTR SOBELA Filtr Sobela jest jednym z podstawowych filtrów krawędziowych. Opiera się on na dwóch maskach 3x3: Gx 1 0 -1 2 0 -2 1 0 -1 0 0 0 -1 -2 -1 Gy 1 2 1 Pierwsza z nich służy do wyznaczania wartości krawędzi poziomych, druga do wartości krawędzi pionowych. Filtracji dokonuje się poprzez splot wartości w maskach z obrazem. Splot ten można określić w następujący sposób: Dla każdego piksela (x, y) obrazu I: a. Wyznacz Gx jako Gx = 1 * I(x-1,y-1) + 2 * I(x, y-1) + 1 * I(x+1, y-1) + -1 * I(x-1,y+1) – 2 * I(x, y+1) – 1 * I(x+1, y+1). b. Wyznacz Gy jako Gy = 1 * I(x-1,y-1) + 2 * I(x-1, y) + 1 * I(x-1, y+1) + -1 * I(x+1,y-1) – 2 * I(x+1, y) – 1 * I(x+1, y+1). Proszę pamiętać, że zarówno wartości Gx jak i Gy mogą przyjmować wartości dodatnie i ujemne, poza zakresem typu unsigned char. Następnie dla każdego piksela należy wyznaczyć całkowitą siłę krawędzi jako: G Gx G y 2 2 Całkowita siła krawędzi obcięta do zakresu [0..255] będzie naszym ostatecznym wynikiem filtracji. Proszę pamiętać, aby pośrednie wyniki filtracji zapisywać w zmiennych pomocniczych, a nie w obrazie oryginalnym! Proszę także pamiętać, że indeksy analizowanych pikseli nie mogą wyjść poza zakres obrazu. SYNCHRONIZACJA Należy również pamiętać o wyznaczeniu sekcji krytycznej (poprzez obiekt sekcji krytycznej lub mutex) przy zapisie obrazu przefiltrowanego do tablicy znajdującej się pod wskaźnikiem będącym parametrem wywołania wątku, czyli funkcji: if (params.filtered_image!=NULL) *(params.filtered_image) = local_filtered_image; Również przy rysowaniu obrazu dostęp do zmiennej powinien być chroniony aby zapobiec sytuacji, w której wyświetlamy częściowo zmieniony obraz: StretchDIBits(pDC->m_hDC, 1, 1, dest_w - 2, dest_h - 2, 0, 0, image.width, image.height, image.data_ptr, image.getBip(), DIB_RGB_COLORS, SRCCOPY); KLASA SIMPLEIMAGE Klasa SimpleImage zaimplementowana w aplikacji służy przechowywaniu, wczytywaniu i zapisywaniu obrazów typu JPG i PGM. Do jej podstawowych zmiennych należą: width – szerokość obrazu height – wysokość obrazu data_ptr – wskaźnik na dane obrazu ułożone wierszami. Każdy piksel to jedna wartość jasności w zakresie 0-255 typu unsigned char. W aplikacji do funkcji ImageProcessing podawane są zawsze obrazy w skali szarości, tak więc dostęp do pojedynczego elementu (x, y) obrazu mamy poprzez operację: image.data_ptr[x + y*image.width] Powodzenia! W razie pytań I wątpliwości proszę pytać w czasie laboratorium, czy mailowo.