Nieskończona głębia ostrości

Dzisiaj kontynuujemy temat przetwarzania obrazów. Jednak zamiast przekształcać zwykłe zdjęcia w artystyczne dzieła sztuki, zajmiemy się bardziej praktycznym algorytmem, który pozwoli uzyskać fotografie o nieskończonej głębi ostrości.

Dzisiaj kontynuujemy temat przetwarzania obrazów. Jednak zamiast przekształcać zwykłe zdjęcia w artystyczne dzieła sztuki, zajmiemy się bardziej praktycznym algorytmem, który pozwoli uzyskać fotografie o nieskończonej głębi ostrości.

Głębia ostrości

Aparat fotograficzny rysuje ostre obrazy tych obiektów, które znajdują się w optymalnym przedziale odległości, zwanym głębią ostrości. Fotografowany obiekt nie może być ani za blisko, ani za daleko od aparatu. Operowanie głębią ostrości jest jedną z najważniejszych umiejętności zawodowych fotografów i prawdziwym przekleństwem amatorów.

Rozpoczynając omówienie ostrości zdjęć, trochę na rozgrzewkę przyjrzyjmy się klasycznemu algorytmowi zwiększania ostrości. Jak się zresztą okaże, niewiele wartemu... Poprawianie nieostrej fotografii polega na wyszukiwaniu i uwypuklaniu wszelkich zmian barwy, napotykanych podczas przemierzania zdjęcia techniką piksel po pikselu.

Rysunek 1. Oto klasyczny interfejs programu do przekształcania obrazów. Dwa komponenty Image, umieszczone na powierzchniach z suwakami, reprezentują jego pola wejściowe i wyjściowe. Do tego dorzucamy kilka technicznych przycisków i tzw. dialogi do ustalenia nazw pliku wejściowego i wyjściowego.

Rysunek 1. Oto klasyczny interfejs programu do przekształcania obrazów. Dwa komponenty Image, umieszczone na powierzchniach z suwakami, reprezentują jego pola wejściowe i wyjściowe. Do tego dorzucamy kilka technicznych przycisków i tzw. dialogi do ustalenia nazw pliku wejściowego i wyjściowego.

Podstawowy algorytm wyostrzania porównuje każdy piksel z jego bliższym lub dalszym otoczeniem i wzmacnia jasność tym bardziej, im większą wykryje różnicę. Oznaczmy kolor badanego piksela symbolem k0, a kolory czterech otaczających go sąsiadów jako ka, kb, kc, kd. Rozłóżmy te kolory na amplitudy, które odpowiednio oznaczymy r0, g0, b0, ra, ga, ba,...rd, gd, bd. Wypadkowa amplituda, na przykład czerwieni, niech będzie dana następującym, dość sprytnym wyrażeniem:

r = 5 * r0 - ra - rb - rc - rd;

Amplitudy zieleni i błękitu wyznaczymy za pomocą takich samych wyrażeń, odpowiednio zbudowanych ze zmiennych g albo b.

Rysunek 2. Nieostry obraz to skutek błędu popełnionego podczas ustawiania soczewek projekcyjnych aparatu. Nie zawiera informacji, gdzie naprawdę powinien się znajdować wąs psa... Algorytmy wyostrzające musiałyby dokonać cudu, aby naprawdę to poprawić. Za to doskonale eksponują wszelkie artefakty, szumy, pyłki i zabrudzenia.

Rysunek 2. Nieostry obraz to skutek błędu popełnionego podczas ustawiania soczewek projekcyjnych aparatu. Nie zawiera informacji, gdzie naprawdę powinien się znajdować wąs psa... Algorytmy wyostrzające musiałyby dokonać cudu, aby naprawdę to poprawić. Za to doskonale eksponują wszelkie artefakty, szumy, pyłki i zabrudzenia.

Zauważmy, że wyrażenia takie pozostawiają niezmienioną wartość amplitudy - w tym wypadku czerwieni - gdy centralny piksel i jego otoczenie mają to samo nasycenie. Gdy nasycenie otoczenia jest mniejsze, czyli gdy centralny piksel wyróżnia się z niego, amplituda rośnie. Jeśli są różnice w amplitudach, to zostaną jeszcze bardziej podkreślone.

Są różne mutacje tego algorytmu, polegające na odmiennych technikach porównywania jasności piksela i jego otoczenia, ale efekty rzadko bywają godne uwagi. Nieostre zdjęcie jest zmarnowane, bo fotograf popełnił błąd podczas naświetlania, i kropka. Niemniej jednak algorytm wyostrzania za chwilę przyda nam się do nieco innego celu niż poprawianie obrazu. Treść tego programu znajduje się w załączonych materiałach źródłowych i zainteresowanych Czytelników zapraszam do jego analizy, a my zajmiemy się czymś zdecydowanie odmiennym i znacznie bardziej skomplikowanym.

Rysunek 3. Pierwszy plan jest ostry, ale dalszy już nie, bo wyszedł poza fizyczny zakres głębi ostrości. Może też być odwrotnie. Cała scena od początku do końca nie daje się uchwycić ostro, bo jest zbyt rozległa.

Rysunek 3. Pierwszy plan jest ostry, ale dalszy już nie, bo wyszedł poza fizyczny zakres głębi ostrości. Może też być odwrotnie. Cała scena od początku do końca nie daje się uchwycić ostro, bo jest zbyt rozległa.

Niekiedy - mimo najlepszej wiedzy i chęci - nie uda się zrobić dostatecznie ostrego zdjęcia (porównaj rysunek 3). Bywają obiekty tak rozległe, że po prostu nie mieszczą się w głębi ostrości. Jeśli ostra jest głowa jakiegoś hipotetycznego węża, to ogon już nie, bo się nie zmieścił w głębi ostrości... Co prawda, fotograf ma różne narzędzia do manewrowania nią, ale bardzo łatwo o scenę, której po prostu nie da się ostro sfotografować.

Postawmy zadanie następujące: jak zmieszać dwie fotografie, widoczne na rysunku 3, aby z każdej z nich wziąć tylko partie ostre i w efekcie otrzymać obraz, który będzie syntezą tych partii? Innymi słowy - jak połączyć serię obrazów tylko częściowo ostrych w jedno ostre zdjęcie?

Nasuwa się następujący schemat postępowania:

1. Z każdego zdjęcia należy wyodrębnić partie ostre i oznaczyć je.

2. Tak wyodrębnione obszary barwnych plam trzeba połączyć w ostateczne zdjęcie.

Zacznijmy od przygotowania niełatwego algorytmu, który każdy piksel zdjęcia porówna z otoczeniem i zawyrokuje, czy należy on do obszaru ostrego. Przyda się przed chwilą napisany program do wyostrzania, choć tym razem niczego nie będziemy wyostrzać, a jedynie oceniać istniejącą ostrość.

Rysunek 4. Program odczytuje dwa obrazki i sporządza dwie mapy ostrości (tutaj o niewielkich amplitudach, zatem prawie czarne). Docelowy obraz powstaje z tych pikseli, którym towarzyszą większe amplitudy na ich mapach ostrości, i jest subtelną mieszanką pikseli pochodzących z dwóch obrazów wejściowych.

Rysunek 4. Program odczytuje dwa obrazki i sporządza dwie mapy ostrości (tutaj o niewielkich amplitudach, zatem prawie czarne). Docelowy obraz powstaje z tych pikseli, którym towarzyszą większe amplitudy na ich mapach ostrości, i jest subtelną mieszanką pikseli pochodzących z dwóch obrazów wejściowych.

Program składa się z pięciu obrazów Image, standardowo umieszczonych na pudełkach z suwakami do przemieszczania dużych obiektów, zwanych ScrollBox. Dwa z tych obrazów przeznaczamy pod fotografie wejściowe, odczytywane z dysku i wyświetlane po zarejestrowaniu kliknięcia odpowiedniego przycisku:

void __fastcall

TForm1::Button1Click(TObject *Sender)

{

if( OpenPictureDialog1 -> Execute())

{

Image1 -> Picture -> LoadFromFile(

OpenPictureDialog1 -> FileName);

Image1->Picture->Bitmap->PixelFormat=pf32bit;

Image2->Picture->Bitmap->PixelFormat=pf32bit;

int szer = Image1->Picture->Bitmap->Width;

int wys = Image1->Picture->Bitmap->Height;

Image2 -> Picture -> Bitmap -> Width = szer;

Image2 -> Picture -> Bitmap -> Height = wys;

Image5 -> Picture -> Bitmap -> Width = szer;

Image5 -> Picture -> Bitmap -> Height = wys;

}

}