Laboratorium 3 – Przetwarzanie obrazów 1
Zakres materiału
Tablice statyczne, tablice dwuwymiarowe. Odczyt z i zapis do pliku. Pętla for. Interfejs użytkownika programu.
Przygotowanie do zajęć
Przed przystąpieniem do zajęć przygotuj opisane poniżej trzy funkcje operujące na niepełnych, statycznych tablicach dwuwymiarowych (czyli tablicach o wielkości znanej i zadeklarowanej w chwili kompilacji, zaś wykorzystywanych w trakcie działania programu w miarę potrzeb, np. tablicy o wymiarach 10x10, z której w programie wykorzystane są jedynie pierwsze 3 wiersze i 7 kolumn). Dostosuj nagłówki opracowanych przez Ciebie funkcji tak, by mogły zostać użyte poprawnie w ich testowym wywołaniu opisanym w punkcie 4.
- Wypełnienie elementów tablicy liczbami będącymi iloczynem ich indeksów. Wymiar wykorzystanej części tablicy powinien być podany jako parametr funkcji.
- Wypisanie zawartości tablicy niepełnej (przy wymiarze podanym jak wyżej) na standardowe wyjście.
- Zmianę znaków wszystkich elementów tablicy niepełnej (przy wymiarze podanym jak powyżej) na przeciwny.
- Testowe wywołanie powyżej opisanych funkcji w rodzaju (wersja prosta):
int main(){
wypelnij(3,7);
printf("Zawartość oryginalna:\n");
wypisz(3,7);
zmien_znaki(3,7);
printf("Zawartość po zmianie znaków:\n");
wypisz(3,7);
return 0
}
przy tablicy, na której dokonywane są operacje zadeklarowanej jako zmienna globalna, powinno spowodować wypełnienie tej zmiennej, wypisanie jej zawartości na standardowe wyjście, zmianę znaków elementów tablicy i ponowne wypisanie zawartości na wyjście. W wersji bardziej rozbudowanej wywołanie przygotowanych funkcji powinno mieć postać wypelnij(obraz,3,7), gdzie obraz jest zmienną lokalną funkcji main.
Przykładowy efekt działania wywołania testowego:
[john_doe@diablo:~]$ ./a.out
Zawartość oryginalna:
0 0 0 0 0 0 0
0 1 2 3 4 5 6
0 2 4 6 8 10 12
Zawartość po zmianie znaków:
0 0 0 0 0 0 0
0 -1 -2 -3 -4 -5 -6
0 -2 -4 -6 -8 -10 -12
- Przygotuj także funkcję obsługi prostego menu (na bazie funkcji switch) złożonego z 4 pozycji (w tym pozycji Zakończ działanie), które po wybraniu liczby odpowiadającej danej pozycji poinformuje o tym fakcie i pozwoli na ponowne dokonanie wyboru (nie dotyczy pozycji Zakończ działanie). Przykładowy efekt działania funkcji:
[john_doe@diablo:~]$ ./a.out
Proste menu:
1 - Pozycja pierwsza
2 - Pozycja druga
3 - Pozycja trzecia
4 - Zakończ działanie
Twój wybór: 2
Wybrano pozycję drugą
Proste menu:
1 - Pozycja pierwsza
2 - Pozycja druga
3 - Pozycja trzecia
4 - Zakończ działanie
Twój wybór: 1
Wybrano pozycję pierwszą
Proste menu:
1 - Pozycja pierwsza
2 - Pozycja druga
3 - Pozycja trzecia
4 - Zakończ działanie
Twój wybór: 4
Koniec działania programu
Ponadto również:
- Zapoznaj się z tekstowym formatem zapisywania obrazów PGM, opisanym w dołączonych do zadania materiałach w pliku obrazy_format.pdf (strony 3-4).
- Przeanalizuj funkcję wczytywania obrazów PGM z pliku do pamięci, przedstawioną w dołączonych do zadania materiałach w programie odczyt.c, oraz wykorzystywane przez nią struktury danych. Zapoznaj się z obsługą plików i tablic dwuwymiarowych. Skompiluj znajdujący się w pliku odczyt.c progam. Uruchom go dla przykładowego obrazu PGM (takowe można znaleźć na diablo w katalogu /usr/local/air/info2/zadania/splot/obrazki (alternatywnie w /home/mucha/edu/info2/obrazki), np. kubus.pgm).
- Bazując na wymienionej powyżej funkcji odczytywania obrazów
zaproponuj i zapisz kompletne algorytmy pozwalające na realizację
funkcji zmiany poziomów i rozciągania histogramu (zobacz Zadanie do
wykonania) oraz zapisu obrazu do pliku. Przyjmujemy, że obrazy
reprezentowane są jako tablica dwuwymiarowa o zadanym rozmiarze.
- Zaprojektuj menu użytkownika oraz zaproponuj sposób jego
realizacji w programie.
Uwaga do programu odczyt.c
Program odczyt.c zawiera oprócz funkcji wczytującej do pamięci obraz z pliku w formacie PGM, funkcję wyświetlającą tenże obraz za pomocą popularnego programu display (program ten wchodzi w skład służącego do obróbki obrazów pakietu ImageMagick - w przypadku jego braku na lokalnym komputerze można go doinstalować lub skorzystać z usług innego programu przeznaczonego do wyświetlania obrazów). Przykład ten pokazuje jak w najprostszy sposób odwoływać się z poziomu programu w C do poleceń systemowych. Ponieważ program display jest programem graficznym, by zadziałał poprawnie musimy pracować w środowisku graficznym. W tym celu albo używamy konsoli graficznej w dowolnym systemie UNIX lub Linux (poprawnie skonfigurowanej), albo przy pracy z poziomu systemu MS Windows korzystamy z programu emulującego serwer X Window (przykładowe dostępne na stronie diablo) i uzyskujemy pożądany efekt łącząc się z diablo programem putty z ustawioną opcją ,,Enable X11 forwarding'' (w zakładce ,,Tunnels'' ). Przy pierwszym uruchamianiu programu odczyt.c z funkcji wyświetlającej można zrezygnować.
Wskazówka
Często dobrym pomysłem na realizację interfejsu użytkownika programu jest wyposażenie go w menu. Takie menu daje możliwość dokonania wyboru jednej z funkcji programu lub jego zakończenia. Może ono przyjmować formy od rozbudowanego menu graficznego po proste, wyświetlane sekwencyjnie menu tekstowe. Takie proste menu tekstowe w programie realizowane jest w pętli, która kończy swoje działania w chwili wybrania pozycji koniec działania programu. W każdym kroku tej pętli najpierw wysyłana jest na standardowe wyjście lista dostępnych pozycji menu, odczytywany jest wybór użytkownika i na jego podstawie wykonywana odpowiednia funkcja programu z ewentualnym zapytaniem o potrzebne argumenty.
Zadanie do wykonania
Napisać program do obsługi czarno-białych obrazów rastrowych (ze skalą
szarości). Takie obrazy mogą być przedstawione w postaci
dwuwymiarowej tablicy, której elementy są liczbami całkowitymi. Każda
z liczb reprezentuje kolor jednego punktu (piksela) obrazu, przy czym
przyjmuje się, że kolorowi czarnemu odpowiada wartość 0, a kolorowi
białemu wartość maksymalna, zależna od liczby poziomów szarości.
Program ma obsługiwać obrazy zapisane w plikach w formacie PGM. Przykładowe obrazy w formacie PGM znajdują się na diablo w katalogu /usr/local/air/info2/zadania/splot/obrazki (alternatywnie w /home/mucha/edu/info2/obrazki).
W programie należy zdefiniować:
- funkcje odczytu i zapisu pliku PGM (można wykorzystać funkcję
odczytu z pliku odczyt.c);
- wskazane przez prowadzącego funkcje przetwarzania obrazów (wybrane z zestawu funkcji opisanych w pliku obrazy_filtry.pdf);
- menu użytkownika, które pozwala na wybór odczytu oraz zapisu pliku
o podanej nazwie, realizację operacji przetworzenia obrazów oraz ich wyświetlanie za pomocą zewnętrznego programu display;
- wyjście z programu.
W pliku odczyt.c
przedstawiony został prosty program zawierający funkcje do odczytu
obrazu PGM z pliku oraz do jego wyświetlenia programem display.
Wskazówki
-
W ramach realizacji zadania należy poprawić funkcję wczytującą obrazy,
tak aby była odporna na nieprawidłowości w danych wejściowych oraz aby
mogła wczytywać wszystkie pliki w formacie PGM zgodne z ustaloną
specyfikacją.
-
Po wykonaniu operacji na obrazie, opracowywany program nie może
samoczynnie nadpisywać pliku z wczytanym obrazem. Zapis obrazu
wynikowego do pliku następuje po wybraniu odpowiedniej pozycji z menu,
która zapyta o nazwę pliku wynikowego.
-
Przyjmując, że zakres szarości obrazu wejściowego wynosi od 0 do
MAX_SZAROSC, zastanowić się nad rozpiętością zakresu szarości w
obrazie wynikowym, uzyskanym w efekcie konturowania obrazu według
formuły podanej w
pliku obrazy_filtry.pdf. Jak można zaradzić zaobserwowanemu zjawisku? Podaj 3 sposoby.
Zadania dodatkowe (dla chętnych)
-
Wyposażyć program w możliwość wykonywania filtracji obrazu przy użyciu operacji splotu, opisanej w pliku filtrsplot.pdf.
-
Zmodyfikować funkcje wczytywania i zapisywania obrazu, tak aby
zapisując przetworzony obraz, funkcja zapisu umieszczała w nim
oryginalne komentarze oraz dodała komentarze informujące o wykonanych
operacjach i datach ich wykonania.
Przebieg ćwiczenia
Kolejność realizacji zadań prowadząca w miarę bezboleśnie do celu:
- Na bazie programu odczyt.c napisać program, który dodatkowo będzie potrafił zapisać wczytany obraz do pliku. Sprawdzić, czy zapisany w ten sposób plik z obrazem jest nadal poprawny.
- Uzupełnić program o wskazaną przez prowadzącego punktową operację przetwarzania obrazu. Sprawdzić, czy uzyskiwany z programu plik wynikowy jest poprawny.
- Uzupełnić program o pozostałe funkcje przetwarzania obrazów wskazane przez prowadzącego. Wyposażyć go w menu użytkownika.
- Uzupełnić dokumentację programu i specyfikacje funkcji (które to powinny powstawać równocześnie z tworzeniem oprogramowania).
Alternatywnie można:
- Wykonać pierwsze 4 punkty z Przygotowanie do zajęć.
- Zastąpić funkcję wypełniającą tablicę funkcją wczytującą obraz czytaj z programu odczyt.c. Przetestować całość na pliku PGM zawierającym ,,nieduży'' obraz (np. 20 na 10 pikseli) - przykładowe takie obrazy znajdują się na diablo w katalogu /home/mucha/edu/info2/obrazki w plikach maly_*.pgm. Można też przygotować sobie takie pliki ,,ręcznie'', na przykład z wykorzystaniem edytora emacs, zachowując składnię formatu PGM opisaną w obrazy_format.pdf (Emacs w obecnej wersji częstokroć potrafi rozpoznać format wczytywanego pliku i wyświetlić go we właściwy sposób - oznacza to, że wczytując plik zawierający obraz (np. w formacie JPG czy PGM) nie zobaczymy jego ,,tekstowej'' zawartości, a jedynie właściwy obraz po interpretacji. By ,,zajrzeć do wnętrza'' tak zinterpretowanego pliku należy po jego wczytaniu wydać polecenie C-c C-c lub wybrać w standardowy sposób tryb tekstowy (M-x text-mode).).
- Zmodyfikować funkcję zmieniającą znaki na przeciwne tak, by wykonywała wskazaną przez prowadzącego punktową operację przetwarzania obrazów. Przetestować poprawność naniesionych modyfikacji.
- Na bazie funkcji czytaj napisać funkcję, która będzie potrafiła zapisać znajdujący się w pamięci obraz do pliku. Sprawdzić, czy uzyskiwane w ten sposób pliki z obrazami są poprawne (tu można już użyć ,,większych'' obrazów).
- Uzupełnić program o funkcje przetwarzania obrazów wskazane przez prowadzącego. Wyposażyć go w menu użytkownika.
- Uzupełnić dokumentację programu i specyfikacje funkcji (które to powinny powstawać równocześnie z tworzeniem oprogramowania).
Sprawozdanie
Sprawozdanie powinno zawierać:
- Imię i nazwisko autora, temat ćwiczenia, datę wykonania;
- Kod źródłowy programu;
- Testy programu (opis przebiegu z wynikami) w tym kolejno:
- Co dany test ma na celu;
- Metoda testowania - dane wejściowe;
- Dane wyjściowe zwrócone w teście przez program;
- Rezultat testu - ocena plus ewentualny komentarz;
- Wnioski końcowe.
Pytania kontrolne
-
Zapisz deklarację tablicy dwuwymiarowej o rozmiarze 100 na 30. Jak odwołać się
do poszczególnych elementów takiej tablicy?
-
Co spowoduje niezainicjowanie w programie odczyt.c zmiennej
odczytano?
-
Zapisz składnię pętli for. Podaj przykład wypisania za jej pomocą
zawartości tablicy dwuwymiarowej.
-
W jaki sposób można przekazać tablicę dwuwymiarową (MxN) do funkcji?
-
Jak jest zdefiniowany nagłówek pliku pgm?
-
Wymień funkcje pozwalające na zapis danych do pliku.
-
Na co pozwala funkcja fprintf(stderr,...)?
-
Jakie operacje trzeba wykonać, by wczytać z pliku daną typu int?
-
Czym się różnią funkcje fgetc od fgets?
-
Opisz przeznaczenie argumentów funkcji int czytaj(FILE *p,int
obraz_pgm[][MAX],int *wymx,int *wymy, int *szarosci) z
programu odczyt.c. Ustal jaką wartość ta funkcja zwraca.
-
W oparciu o jakie instrukcje można łatwo zbudować proste menu użytkownika? Uzasadnij.