Prosta aplikacja do „kolażu” zdjęć
16.11.2015 | aktual.: 18.11.2015 11:26
Jako iż leniwość matką wynalazku, to zamiast szukać programu, który pasowałby gustom mego znajomego, wolałem sam taki program napisać. Do pracy zaprzągłem OpenCV i gdyby nie to, że brakowało mi jednej rzeczy, to program do kolażu zdjęć napisałbym w miarę szybko. Zacząłem niestety chyba od podsumowania... Czas prześledzić od początku.
Kolaż zdjęć
Kolaż zdjęć, to umieszczenie zdjęć każdego z wybranych obok innego wybranego. Z tego powstaje duży obraz.
Właściwy wstęp
OpenCV dostarcza bardzo wiele możliwości. posłużę się możliwością odczytu obrazów, a także ich skalowania. Wykorzystam także bibliotekę Libgreattao, jednak w tym wpisie pominę rzeczy związane z tą biblioteką. Jutro wrzucę też gdzie źródła mojego programu, zwanego kola.
[h2']Założenia[/h2]
W moim wypadku postanowiłem, by zdjęcia miały identyczne wymiary, więc muszą zostać przeskalowane. Jednak, by nie tracić za bardzo na jakości, to obliczymy średnią szerokość zdjęcia i średni stosunek wysokości i szerokości. Zdjęcia zostaną przeskalowane do średnia szerokość w ramach szerokości i średnia szerokość * stosunek w ramach wysokości. Wyciągniemy także pierwiastek drugiego stopnia z liczby zdjęć, by znać ilość zdjęć w wierszu. Podłoga tego pierwiastka, to będzie ilość zdjęć w wierszu, więc dla trzech zdjęć otrzymamy 1, a dla czterech 2.
Należy zwrócić uwagę na jeden aspekt. Jeżeli podzielimy ilość zdjęć przez otrzymaną podłogę, by uzyskać liczbę wierszy, to możemy otrzymać błędny wynik. Z tego względu należy wykonać pewne sprawdzenie
[code=C] floor((float) count / in_row) == (float)count / in_row [/code]
Sprawdzamy czy zostaje reszta z dzielenia, a jeżeli tak, to do ilości wierszy dodajemy jeden. By korzystać z możliwości poszerzania obrazka, to musimy zdefiniować obrazek z docelowymi wymiarami, a następnie wywołać odpowiednio funkcję cvResize. Na początku wpisu wspomniałem, że była pewna niedogodność. Chodziło o to, że nie potrafiłem znaleźć funkcji, która kopiowałaby dane z przesunięciem. Z tego względu musiałem sam zaimplementować tę cechę programu.
Kawałki kodu
Obrazek wczytujemy w następujący sposób:
[code=C] obrazek = cvLoadImage(ścieżka, CV_LOAD_IMAGE_COLOR); [/code]
Za ścieżka należy podstawić ścieżkę do obrazka. Oczywiście, że funkcja zwraca wskaźnik do odpowiedniej struktury.
Ważnym etapem jest zbieranie statystyk co do naszych obrazków. Oto kod:
++count; img_width = image->width; img_height= fimage->height; avg_proportion += (float) img_width / (float) img_height; avg_width += img_width;
Powyższy kod należy umieścić w pętli wczytującej obrazki. Myślę, że nie trzeba go tłumaczyć.
Poniżej pętli należy umieścić poniższy kod:
[code=C] avg_proportion /= count; avg_width /= count;
in_row = (int) sqrt(count); if (floor((float) count / in_row) == (float)count / in_row) { resultImage = cvCreateImage(cvSize((int)(in_row * avg_width), (int)(avg_width * avg_proportion * count / in_row)), f_dialog.files->image->depth, f_dialog.files->image->nChannels); } else { resultImage = cvCreateImage(cvSize((int)(in_row * avg_width), (int)(avg_width * avg_proportion * (count / in_row + 1))), f_dialog.files->image->depth, f_dialog.files->image->nChannels); } resizeImage = cvCreateImage(cvSize((int)(avg_width), (int)(avg_width * avg_proportion)), f_dialog.files->image->depth, f_dialog.files->image->nChannels); cvSetZero(resultImage); cvSetZero(resizeImage);
[/code]
Ważna jest funkcja cvCreateImage. Pierwszym argumentem jest struktura zawierająca wymiary naszego obrazka. Szerokością będzie ilość obrazków w wierszu pomnożona przez średnią szerokość obrazka. Wysokością będzie średnia szerokość pomnożona przez stosunek i dodatkowo pomnożone przez iloraz ilości obrazków i ilości obrazków w wierszu. Tutaj stosujemy sztuczkę z dodaniem dodatkowo wiersza,, zwiększając wspomniany iloraz o jeden. cvSetZero wypełnia obrazki czernią, ale w drugim wypadku jest to raczej zbędne.
Następnie należy łączyć obrazki. Musimy pamiętać zmienne i (ilość obrazków położonych w bieżącym wierszu), x (przesunięcie względem lewej krawędzi), y(przesunięcie względem górnej krawędzi). Jeżeli i osiągnie in_row, to musimy wyzerować x, wyzerować i, a także do y dodać średnią wysokość obrazka.
W pętli łączącej obrazki należy kopiować piksele z obrazka rozszerzonego do ostatecznego. Należy pamiętać, że:
- Obrazek ma image->width szerokości i image->height wysokości
- Jednak to nie wystarcza, bo szerokość należy mnożyć przez liczbę kanałów, czyli bps
Ostatecznie zapisujemy obrazek funkcją cvSaveImage, jak poniżej:
[code=C] cvSaveImage(ścieżka, obrazek_do_zapisu, 0); [/code]
To już wszystko na dzisiaj!