Proste przetwarzanie obrazów

23.12.2009 - Rafał Kozik
TrudnośćTrudność

2. Operacje per-pixel

Zabawę z przetwarzaniem obrazów zaczniemy od filtrów, które będą wykonywały operacje dla każdego piksela z osobna. Dla uproszczenia będziemy zajmować się kolorami w postaci RGB, mimo, że w niektórych przypadkach wygodniej byłoby użyć HSV. Będziemy operować na wartościach r, g i b (oznaczających odpowiednie składowe). Zakładamy też, że po wykonaniu każdej operacji wartości te zostaną przycięte do przedziału [0; 255] (wartości ujemne zastąpimy przez 0, a dodatnie wartości spoza zakresu zamienimy na 255).

Wybór składowej

Jeżeli chcemy zobaczyć jak wygląda któraś składowa obrazu (czerwona, zielona, niebieska), możemy po prostu ustawić pozostałe wartości na 0. Dla naszego obrazu testowego wygląda to tak:

red filter

Nie jest to nic nadzwyczajnego, ale pozwala nam lepiej zrozumieć w jaki sposób został zbudowany obraz, który widzimy.

1
2
3
// wybór czerwonego
g = 0;
b = 0;
1
2
3
// wybór zielonego
r = 0;
b = 0;
1
2
3
// wybór niebieskiego
r = 0;
g = 0;

Inwersja

Jednym z typowych efektów jest negatyw. Myślę, że z aktualną wiedzą nie stanowiłoby już problemu napisanie odpowiedniej operacji, która pozwoliłaby na wygenerowanie takiego obrazu:

negatyw

Barwę każdego piksela musimy zamienić na barwę dopełniającą. Gdyby 'dodać' do siebie obraz i jego negatyw, otrzymalibyśmy w wyniku obraz całkowicie biały.

1
2
3
r = 255 - r;
g = 255 - g;
b = 255 - b;

Odcienie szarości

Kiedyś, dawno temu, królowały zdjęcia czarno białe. Teraz, poprzewracało nam się w głowach od ogromu dostępnych kolorów i czasami chcemy, żeby powróciły tamte prostsze czasy. A poważniej, niektóre zdjęcia po prostu wyglądają lepiej w odcieniach szarości, pozwala to na dodanie im odpowiedniego nastroju:

odcienie szarości

Zmiana na odcienie szarości jest trochę bardziej skomplikowana niż poprzednie filtry i aby ją wykonać, musimy zorientować się w jaki sposób działa ludzie oko. Ponieważ nie rozpoznajemy każdej składowej z taką samą intensywnością, powinny one mieć różny wpływ na obraz wynikowy (różne ilości czopków rozpoznających odpowiednie barwy). Na szczęście takie dane łatwo dziś w Internecie znaleźć. Zatem po chwilowych poszukiwaniach wiemy już, że 29.9% będzie stanowić składowa czerwona, 58.7% zielona i 11.4% niebieska. Gotowy filtr ma następującą postać:

1
2
3
gray = 0.299 * r + 0.587 * g + 0.114 * b;
 
r = g = b = gray;

Jasność

Kolejnym prostym filtrem, który chcę pokazać jest zmiana jasności. Zastanówmy się przez chwilę co to znaczy, że chcemy rozjaśnić jakiś obraz. Chcemy, żeby każdy z pikseli stał się bliższy bieli. A co to znaczy przyciemnić obraz? Każdy piksel powinien stać się bliższy czerni. Idąc dalej tym rozumowaniem dochodzimy do wniosku, że wystarczy składowe każdego piksela zmodyfikować o pewną wartość. Przykładowo, gdy zwiększymy je o 100, otrzymamy taki rezultat:

jasność
1
2
3
4
5
6
// o ile zmienić jasność
change = 100;
 
r = r + change;
g = g + change;
b = b + change;

Kontrast

Pomysł na zmianę kontrast jest dość ciekawy. Zwiększając kontrast chcemy, żeby ciemne piksele stały się jeszcze ciemniejsze, a jasne jaśniejsze. Jeżeli od każdej składowej odejmiemy 128, to dostaniemy wtedy wartość z zakresu [-128; 127]. Ciemne kolory będą miały ujemne wartości, a jasne dodatnie. Jeżeli przemnożymy to przez jakąś liczbę większą od 1 to wartości składowych ciemnych kolorów zmaleją, a jasnych urosną. Na koniec wystarczy dodać 128, żeby wrócić do odpowiedniego zakresu. Gdy pomnożymy przez 8, otrzymamy taki wynik:

kontrast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// wartość kontrastu
contrast = 8;
 
r = r - 128;
g = g - 128;
b = b - 128;
 
r = r * contrast;
g = g * contrast;
b = b * contrast;
 
r = r + 128;
g = g + 128;
b = b + 128;

Gamma

Ostatnim filtrem, o którym tutaj wspomnę, jest filtr gamma. Korekcja gamma jest nieliniową operacją przekształcającą dane, która opisana jest wzorem (gamma jest grecką literą uzytą w wykładniku):

gamma

Niektóre urządzenia, jak np. monitory, skanery wprowadzają pewne zakłócenia przez to, że ich sposób podświetlania nie jest idealny. Chodzi o to, że jeżeli moc światła będzie ustawiona na 50%, to wcale nie oznacza, że będzie świecić w połowie tego co gdy moc ustawiona jest na 100%. Bardzo dobrze obrazuje to ten wykres z wikipedii:

gamma

Zatem moc przekłada się na jasność zgodnie z takim wzorem (powyżej gamma była równa 2.2):

gamma

Dolna krzywa pokazuje jak przykładowo wzrasta jasność wraz z mocą dla monitora kineskopowego. Ponieważ chcemy zobaczyć obraz taki jaki jest naprawdę, robimy prostą operację matematyczną, która sprawi, że obraz na monitorze będzie wyglądał tak jak powinien. A dokładniej chcemy zrobić coś takiego:

gamma

Gdy teraz będziemy chcieli wyświetlić tak przetworzony obraz, na wyjściu dostaniemy:

gamma

Dla gamma równego 2.2 dostaniemy taki, jaśniejszy obraz:

gamma
1
2
3
4
5
6
7
// wartość gamma
 
gamma = 2.2;
 
r = 255 * Math.pow(r / 255.0, 1.0 / gamma);
g = 255 * Math.pow(g / 255.0, 1.0 / gamma);
b = 255 * Math.pow(b / 255.0, 1.0 / gamma);

3. Operacje per-pixel -- aplet

Skoro już wiemy w jaki sposób wykonywane są operacje per-pixel, możemy z tym chwilę poeksperymentować. Poniższy aplet ma wbudowane wszystkie filtry, które zostały pokazane powyżej, pozwala też na pisanie własnych. Operacje są pisane w języku JavaScript (można używać biblioteki matematycznej).

Your browser is not Java enabled.
Zgłoś problem z apletem
4.333335
Twoja ocena: Brak Ocena: 4.3 (3 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com