pobierz 234KB

Transkrypt

pobierz 234KB
Tomasz Władziński
Michał Leśniewski
Układy Cyfrowe – projekt
Korekcja jasności obrazów w 24-bitowym formacie BMP z użyciem funkcji gamma
Cel projektu
Celem jest zaprojektowanie układu cyfrowego, który po podaniu parametru γ i wysłaniu przez port
USB do układu pliku BMP o głębi kolorów 24BPP, skorygował jasność jego kolorów z
wykorzystaniem funkcji gamma zgodnie z parametrem, a następnie odesłał do komputera.
Wstęp teoretyczny
Obrazy i zdjęcia cyfrowe pochodzące z różnych źródeł często posiadają wiele niedoskonałości
wynikających z różnych przyczyn – zdjęcia z aparatów wbudowanych w telefony komórkowe i
obrazy zeskanowane są często za ciemne lub za jasne, zdjęcia prześwietlone lub zrobione przy
nieodpowiednim świetle. W związku z tym wykorzystuje się rozmaite filtry, by skorygować te
niedoskonałości.
Korekcja jasności obrazów
Przykładem typowej operacji na obrazie jest rozjaśnienie lub przyciemnienie kolorów.
Najprostszym sposobem jest oczywiście operacja liniowa, czyli każdy kolor rozjaśniony lub
przyciemniony w tym samym stopniu. Jeżeli więc jasność koloru określona jest przez liczbę z
przedziału <0; 1>, gdzie 0 odpowiada głębokiej czerni, a 1 najjaśniejszej bieli, możemy każdy kolor
przesunąć w kierunku bieli lub czerni o pewną stałą wartość. Zasadniczą wadą tej metody jest fakt,
że przesuwając o stałą możemy wyjść poza zakres <0; 1>, przez co część kolorów rozróżnionych w
obrazie pierwotnym zostanie w obrazie wynikowym utracona – zostanie im przypisana biel lub
czerń (czyli odpowiedni kraniec zakresu). Ponadto część wartości z przedziału (na jego początku
lub końcu) w wyniku przesunięcia nie zostanie wykorzystana wcale.
Lepszym rozwiązaniem jest wykorzystanie funkcji gamma. Zakładając tak, jak wcześniej, że
wartość jasności leży w przedziale <0; 1>, możemy zależność nowego stopnia jasności od starego
wyrazić wzorem:
ly = lx1/γ
gdzie:
lx – jasność koloru w obrazie pierwotnym,
ly – jasność koloru w obrazie po korekcji,
γ – parametr funkcji.
Metoda ta jest lepsza, gdyż jej zbiór wartości to <0; 1>, więc obraz wynikowy wykorzysta
wszystkie dostępne stopnie jasności, a kolory rozróżnialne na obrazie pierwotnym, będą dalej
rozróżnione. Mamy więc do czynienia z korekcją jasności oraz dodatkowo kontrastu.
Parametr γ występujący we wzorze odpowiada
stopniowi rozjaśnienia lub przyciemnienia.
Wartości γ < 1 odpowiadają przyciemnianiu, γ >
1 rozjaśnianiu, zaś γ = 1 powoduje uzyskanie na
wyjściu obrazu identycznego jak na wejściu.
Teoretycznie γ może przyjmować wartości z
zakresu (0; +∞), ale w praktyce zazwyczaj
sensowne są tylko wartości z zakresu około
<0,7; 1,3>.
Na wykresie 1 przedstawiono funkcję gamma
dla trzech różnych parametrów. Wartości
parametrów są celowo dosyć nietypowe (za
bardzo odbiegają od 1), aby uwidocznić zmianę
kształtu krzywej.
Format graficzny BMP
Wykres 1: Funkcja gamma dla różnych
parametrów.
Format pliku, z jakim będzie współpracować program to Windows Bitmap (skrót i rozszerzenie
plików to BMP). Jest to jeden z najprostszych formatów grafiki rastrowej, praktycznie nie
wykorzystujący kompresji (teoretycznie format umożliwia kompresję run length encoding, ale nie
jest ona zazwyczaj wykorzystywana). Format umożliwia zapis z różnymi liczbami bitów na pixel,
co wiąże się z liczbą (głębią) kolorów. Dostępne liczby bitów to:
— 32 (po 8 bitów na składową czerwoną, błękitną i zieloną, czwarty bajt ignorowany lub
interpretowany jako kanał przeźroczystości),
— 24 (jak przy 32 bitach, lecz bez ostatniego bajtu),
— 16 (6 bitów na czerwień i po 5 bitów na zieleń i błękit),
— 8, 4 i 1 (formaty starsze, obecnie praktycznie nieużywane, wykorzystywały paletę kolorów
dołączonych do pliku)
Układ obsługiwać będzie jedynie pliki 24 bitowe, gdyż są one dziś obok 32 bitowych
najpowszechniej używane, pozwalają na wystarczająco dokładne określenie intensywności
składowych, intensywności mają te same zakresy (po 8 bitów na każdą składową).
Format BMP a jasność
W poprzednich częściach była mowa o stopniu jasności, jednak nie została podana metoda
obliczenia takiego stopnia. Jasność łatwo obliczyć i podstawić do wzoru dla obrazu
monochromatycznego. Dla obrazu kolorowego natomiast, stopień jasności można określić
przeliczając składowe modelu RGB na HSL – składowa L odpowiada wówczas jasności. W
przypadku korekcji gamma nie musimy jednak tego jednak robić – okazuje się, że wystarczy
potraktować każdy z kanałów R, G i B jako pojedynczy obraz monochromatyczny. Należy jednak
zwrócić uwagę, że w związku z tym, że format BMP zapisuje składowe R, G i B każdego piksela
osobno, każdy na 8 bitach, ich wartości leżą w zakresie <0; 255>, a nie jak wcześniej przyjęliśmy
<0; 1>. Musimy więc wziąć to pod uwagę i zmodyfikować wzór. Otrzymamy:
ly = 255 (lx/255)1/γ
Dzięki temu dziedzina i zbiór wartości funkcji będą równe zbiorowi <0; 255>.
Rozwiązanie praktyczne
Niestety podane wzory na funkcję są bardzo skomplikowane. W związku z tym obliczanie wartości
funkcji przy pomocy płytki byłoby bardzo skomplikowane i prawdopodobnie działałoby dość
wolno. W związku z tym musimy ograniczyć się do aproksymowania funkcji gamma.
Stablicowanie funkcji gamma
Najprostszym sposobem realizacji funkcji jest oczywiście jej stablicowanie. Wówczas otrzymaną
tablicę należałoby wgrać do pamięci płytki i zaprogramować układ tak, by odczytywał odpowiednią
wartość zgodnie z zadanym parametrem γ i jasnością piksela. Nie jest to jednak możliwe, mamy
bowiem do czynienia ze zbyt dużą liczbą punktów dla każdego parametru funkcji gamma (256
wartości po 8 bitów). Ponadto należy pamiętać, że parametr γ może teoretycznie przyjmować
wartości ciągłe. W sprzęcie mamy jednak do dyspozycji pamięć o wielkości 2048 bitów.
Ograniczenie zbioru wartości parametru γ
Rozpatrzmy najpierw parametr γ. Jak wcześniej było wspomniane, parametr ten jest ustawiany na
ogół na wartości z zakresu <0,7; 1,30>. Czasem jednak użytkownicy chcą uzyskać nietypowy obraz
ustawiając bardzo wysokie lub bardzo małe wartości γ. Należy więc przyjąć pewien kompromisowy
zakres. Założony w projekcie zakres to <0,25; 4>. Krańce zbioru zostały dobrane tak, by końcowy
był odwrotnością początkowego. Dzięki temu pierwsza i ostatnia krzywa są symetryczne względem
prostej y = x. Na rysunku przedstawiono wykres obu krańcowych krzywych:
280
y
γ = 0,25
γ=4
y=x
260
240
220
200
180
160
140
120
100
80
60
40
20
x
20
40
60
80
100
120
140
160
180
200
220
240
260
Wykres 2: Pierwsza i ostatnia krzywa możliwa do otrzymania
przy założonych parametrach
Spośród wszystkich wartości parametru γ leżących w zakresie należy wybrać pewien dyskretny
zbiór wartości. Należy podzielić zakres tak, by wartości odpowiadały krzywym równoodległym od
siebie. Taki podział uzyskamy wprowadzając:
γ[n] = a(n – k/2)
gdzie:
n – numer parametru, n = 0, 1, 2...k
k – liczba krzywych dostępnych w pamięci (parzysta liczba całkowita)
a – pewien współczynnik
Współczynnik a można obliczyć znając wartość minimalną parametru γ (początek zakresu):
a = γmin2/k
gdzie:
γmin – początek zakresu
260
y
Funkcje gamma po kwantyzacji wartości gamma
240
220
200
180
160
140
120
100
80
60
40
20
x
20
40
60
80
100
120
140
160
180
200
220
240
260
Wykres 3: Rozkład krzywych względem siebie dla γmin = 0.25
oraz k = 10
Dowolna ciągła wartość parametru γ podana przez użytkownika może być wówczas przypisana do
pewnej wartości n odpowiadającej najbliższemu parametrowi γ z tablicy. Następujący wzór
pozwala wyznaczyć odpowiednią wartość n dla danego parametru γ:
n = [logaγ + k/2]
Należy zaznaczyć, że wszystkie przedstawione obliczenia wykonane zostaną albo przez program
komunikujący się z płytką, albo jeszcze w czasie projektowania układu. Układ więc nie będzie
musiał wykonać żadnego z podanych obliczeń, otrzyma jedynie liczbę gamma, której
przyporządkuje pewną liczbę n (w zależności od zakresu, w którym znajduje się gamma). Będzie
ona wykorzystana tylko do obliczenia z którego pola tablicy będą czytane parametry.
Ograniczenie zbioru punktów
Ograniczając liczbę wartości parametru γ nie rozwiązaliśmy jeszcze problemu z brakiem pamięci.
Nawet przy małych wartościach liczby parametrów otrzymujemy zbyt dużo danych do zapisania.
Przykładowo dla k = 8, ilość potrzebnych bitów wynosi 8∙256∙8 = 16384, a więc stanowczo za
dużo.
Okazuje się jednak, że wystarczy znać współrzędne tylko kilku punktów należących do krzywej, by
móc dość dobrze ją odwzorować w całości. Wystarczy pomiędzy punktami liniowo przybliżać
wartość funkcji. Zwłaszcza dla wartości parametru γ bliskich jedności metoda ta dobrze się
sprawdza.
y
Funkcja
Aproksymacja
260
240
220
200
180
160
140
120
100
80
60
40
20
x
-20
20
40
60
80
100
120
140
160
180
200
220
240
260
-20
Wykres 4: Aproksymacja liniowa dla γ = 1.32 i liczbie punktów
i = 11
Jak widać wykresy praktycznie się nie różnią. Niewielki błąd pojawia się na tylko na początku, dla
małych wartości x. Maksymalne odchylenie aproksymacji od funkcji występuje dla x = 5 i wynosi
około 3.
Następujące wzory pozwalają określić numery punktów, pomiędzy którymi będzie obliczana
aproksymacja:
p(x) = [x/∆]↓ (zaokrąglenie w dół)
n(x) = p(x) + 1
Funkcja
Stablicowane punkty
Aktualny punkt
∆
gdzie:
∆ – odstęp pomiędzy punktami,
∆ = 256 / i
i – liczba stablicowanych punktów
5
n(x) = 4
Kolejny wzór pozwala wyznaczyć jaką część
drogi między punktami p(x) i n(x) między
punktem odpowiada liczbie x:
i(x) = x/∆ - p(x)
p(x) = 3
∆ i(x)
x
2
Aproksymowana wartość funkcji będzie mieć wartość następującą:
v(x) = tab[n(x)] ∙ (1 - i(x)) + tab[n(x)] ∙ i(x)
gdzie:
tab[k] – wartość odpowiadająca k-temu punktowi odczytanemu z tablicy
Wzór jest rodzajem średniej ważonej wartości funkcji w 2 stablicowanych punktach, przy czym
wagom odpowiadają odległości od nich.
Warto zauważyć, że tablica w pamięci nie musi mieć wprowadzonych wartości dla pierwszego i
ostatniego punktu, ponieważ dla każdej krzywej wartość ta jest taka sama – pierwszy punkt jest w
zerze, ostatni w 255
Ostatecznie założone parametry
Po wprowadzeniu powyższych uproszczeń możemy zdecydować się na następujące parametry:
● Zakres regulacji parametru γ: <0,25; 4>
● Liczba krzywych stablicowanych: 16
● Liczba punktów stablicowanych dla każdej krzywej: 17
Takiemu dobór parametrów okazuje się optymalny. Dzięki 17 punktom dla każdej krzywej
otrzymamy 16 przedziałów, wówczas ∆ = 16, co jest bardzo korzystne przy obliczeniach w
systemie binarnym. Ponadto dostępna pamięć zostanie wykorzystana optymalnie. Biorąc pod
uwagę, że dla każdej krzywej wystarczy zapisać jedynie 15 punktów (bo wartość dla pierwszego i
ostatniego nie zmienia się), wykorzystane zostanie dokładnie 16∙15∙8 = 1920 bitów, a więc prawie
całość.
y
Funkcje gamma po kwantyzacji wartości gamma i aproksymacji liniowej
260
240
220
200
180
160
140
120
100
80
60
40
20
x
-20
20
40
60
80
100
120
140
160
180
200
220
240
260
-20
Wykres 5: Wykres przedstawia wszystkie możliwe do otrzymania
krzywe przy założonych parametrach.
Algorytm
Dzięki przyjęciu konkretnych założeń, algorytm wyznaczania wartości funkcji można bardzo
znacznie uprościć. Liczby i funkcje zdefiniowane wcześniej, zawierające liczne mnożenia i
dzielenia, są w większości przypadków równoznaczne z przesunięciami bitowymi bądź iloczynom
logicznym. W dalszej części przedstawiono dokładniejszy opis działań koniecznych do wykonania
po uproszczeniu.
Użyte zmienne i uproszczone funkcje
Jak wcześniej wspomniano w praktyce liczba x na wejściu będzie reprezentować jasność jednej z
trzech składowych koloru. Dla standardu 24-bitowego każdą składową reprezentuje liczba
całkowita z przedziału <0, 255>. Zakładamy więc, że x jest 8-bitową liczbą całkowitą bez znaku.
W związku z tym, że przyjęto liczbę punktów w tablicy równą 17, będziemy mieć do czynienia z 16
przedziałami, każdy z nich długości ∆ = 16. W związku z tym funkcja p(x) zwraca po prostu część
całkowitą wyniku dzielenia przez 16, co jest bardzo korzystne w systemie binarnym, gdyż
odpowiada to przesunięciu bitowemu liczby x w prawo o 4 bity. Można również przyjąć że p(x)
reprezentują pierwsze cztery bity liczby x. Liczba p będzie więc reprezentowana przez 4 bity i
interpretowana jako liczba całkowita bez znaku.
Funkcję i(x) możemy przy natomiast potraktować jak iloczyn logiczny z liczbą 15 (binarnie
00001111). Analogicznie jak przy funkcji p(x), możemy uznać wartość funkcji i(x) jako równą 4
ostatnim bitom liczby x. Należy jednak zaznaczyć, że jest to ułamek. Zmienną i będziemy zatem
traktować jako 4-bitową liczbę stałoprzecinkową bez znaku o pozycji przecinka przed jej
pierwszym bitem.
Tablica wartości funkcji gamma powinna być tablicą bajtów. Indeks tablicy mówi o numerze
odczytywanego bajtu. Wartości powinny być zapisane w takiej kolejności, że aby odczytać k-ty
punkt przy parametrze n należy odczytać dane z komórki numer 16n + k – 1. Należy pamiętać, że
wartości punktu pierwszego (właściwie zerowego) jak i ostatniego nie są czytane z tablicy.
Algorytm obliczając adres korzystać może wówczas wykorzystać prostszy wzór:
a = n << 4 + k - 1
Ponieważ najwyższy adres jest równy 255, zmienne przechowujące adresy możemy traktować jako
8-bitowe liczby całkowite bez znaku.
Wynik interpolacji jest liczbą stałoprzecinkową o przecinku przed 4 bitem od końca. Obliczony
zostaje ze wzoru
v = w2 ∙ i + w1 ∙ (1 - i)
gdzie:
w1, w2 – odczytane z tablicy wartości funkcji w najbliższych punktach
Aby obciąć to, co po przecinku wystarczy odczytać pierwsze 8 bitów liczby v. Jest to wynik
końcowy działania.
Podsumowanie zmiennych
Nazwa zmiennej
Ilość bitów
Rodzaj
Pozycja przecinka
x
8
Bez znaku
(liczba całkowita)
n
4
Bez znaku
(liczba całkowita)
p
4
Bez znaku
(liczba całkowita)
i
5
Bez znaku
Po 1. bicie
i2
5
Bez znaku
Po 1. bicie
w1
8
Bez znaku
(liczba całkowita)
w2
8
Bez znaku
(liczba całkowita)
v
13
Bez znaku
Po 9. bicie
Schemat algorytmu
Bibliografia
http://en.wikipedia.org/wiki/RGB_color_model
– Informacje o modelu barw RGB
http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
– Specyfikacja formatu BMP
http://www.algorytm.org/index.php?option=com_content&task=view&id=136&Itemid=28
– Opis funkcji gamma
Inne informacje
Do sporządzenia większości wykresów wykorzystano program Graph (http://www.padowan.dk/).
Schemat blokowy algorytmu został utworzony programem Structured Flow Chart Editor
(http://www.cs.sonoma.edu/~tiawatts/SFC/index.html).