Aritmetika boja i režimi preklapanja u programima za obradu fotografija

Vereovatno ste se do sada zapitali kakvi proračuni stoje iza različitih režima preklapanja u programima za obradu fotografija. Kako da postignemo da slika koju postavljamo iznad druge slike deluje poluprovidno? Kako da pri preklapanju dve slike odstranimo belu pozadinu i ostavimo samo "šarene" piksele.

Ovo su samo neke od stvari koje su vam mogle pasti na pamet, a odgovor na pitanje iz prethodnog pasusa, o tome kakva to matematika stoji iza preklapanja fotografija u racunarskim programima je (srećom): veoma jednostavna - onakakva kakva se uci u nižim razredima osnovne škole.

Konvencije pri predstavljanju boja

Boje se u računarskim sistemima tipično zapisuju preko takozvanog aditivnog RGBA modela. Termin adidtivni, označava model u kome se konačna boja nekog piksela formira dodavanjem (adicija - dodavanje) crvene (R), zelene(G) i plave(B) boje na crnu.

Najtipičniji slučaj je onaj u kome za zapisivanje jedne od tri boje koje cine konacnu boju nekog piksela imamo 8 bita (to jest, celobrojne vrednosti u rasponu od 0 do 255). 8-bitni model tipičan je za BMP i JPEG fajlove.

Medutim, postoje i drugi modeli, sa više (ili, u današnje vreme ređe), manje, bitova. Recimo, 16-bitni model kakav se koristi u TIFF i PNG fajlovima.

Nama je u interesu da pišemo programe koji se lako mogu prilagoditi različitim zahtevima, pa ćemo ovde sagledati način predstavljanja boja koji će (uz odgovarajuće mere predostrožnosti) važiti i u slučaju da koristimo 8-bitne fajlove, i u slucaju da koristimo 16-bitne.

Opšti model (za potrebe ovog teksta zvacemo ga apsolutnim modelom) podrazumeva da se bilo koja od tri komponente (RGB) nekog piksela zapisuje kao decimalni broj izmedu 0 i 1, a ne kao celobrojna vrednost u rasponu od 0 do 255 (u slučaju 8-bitnog RGB modela).

Za primer, uzećemo da je zelena komponenta nekog piksela u 8-bitnom modelu zapisana kao 217.

Na ekranu će ta vrednost biti 217, dok ce u proračunima u programu ta vrednost biti 217 / 255, odnosno 0.84765625. Kada bude potrebno da obavimo prikaz datog piksela na ekranu, dati decimalni broj ćemo pretvoriti ponovo u odgvoarajuću celobrojnu vrednost.

Takode, ako želimo da sloj slike koji sadrži ovaj piksel bude poluprovidan, njegova alfa vrednost (sećate se da smo zapravo rekli da se model zove RGBA - A, kao Alpha, označava transparentnost, ali tako da sa porastom alfa vrednosti providnost opada), u apsolutnom modelu će biti 0.5, dok će u 8-bitnom RGBA modelu ta vrednost biti 0.5 * 217 = 108 (podsetimo se, RGB vrednosti su celobrojne).

Dakle, možemo reci da se RGB vrednost 217 može predstaviti kao:

									
		
		217 = 255 * 217 / 255
		
		// Ili, malo opštije
		
		217 = MAX_RGB * 217 / MAX_RGB
		
		// .... gde je MAX_RGB 255 (što je najveća vrednost za 8-bitne kanale)
									
								
Slika 1. - Prebacivanje jedne od RGB vrednosti nekog piksela iz RGB modela u apsolutni.

RGB vrednost u apsolutnom modelu za određeni piksel, za datu RGB vrednost, dobijamo kao:

									
		Double Pixel.ABS = (Double) Pixel.RGB / MAX_RGB;
									
								
Slika 2. - Formula za pretvaranje RGB vrednosti u apsolutnu vrednost.

RGB vrednost u RGB modelu, za datu vrednost iz apsolutnog modela, dobijamo kao:

									
		Int32 Pixel.RGB = (Int32) (Pixel.ABS * MAX_RGB);
									
								
Slika 3. - Formula za pretvaranje apsolutne vrednosti piksela u RGB vrednost.

Osnovno preklapanje

Funkciju koja vraća jednu od RGB vrednosti nekog piksela, tako da ta vrednost odgovara pikselu iz gornjeg sloja slike, mogli bismo napisati na sledeći način:

									
		Int32 preklapanjeGornji(Int32 donji, Int32 gornji)
		{
			return gornji;
		}
									
								
Slika 4. - Primitivna funkcija za preklapanje - domišljato .... ali, u praksi ipak ne možemo da prođemo ovako "jeftino".

I funkcija bi radila svoj posao adekvatno.

Medutim, šta ukoliko želimo da pri preklapanju zadamo transparentnost (prozirnost) gornjeg sloja? Tu stvari postaju komplikovanije, ali i zanimljivije!

Preklapanje sa zadavanjem transparentnosti

Osnovna funkcija, koja bi važila za apsolutni model, bi mogla izgledati ovako:

									
		Double preklapanjeAlfa(Double donji, Double gornji, Double alfa)
		{
			return (1.0 - alfa) * donji + alfa * gornji;
		}
									
								
Slika 5. - Funkcija za preklapanje dva sloja. Parametar alfa možemo shvatiti kao procenat vidljivosti, pa stoga, ako je gornji sloj 80% vidljiv, to znači da će donji sloj biti 20% vidljiv (100% - 80%).

Promenljive gornji i donji se zadaju kao decimalni brojevi u rasponu od 0 do 1, a alfa (transparentnost) je takode decimalni broj u rasponu od 0 do 1.

Dakle, ukoliko je alfa 1 (100%), formula postaje: 1 * gornji, dok u slucaju kada je alfa gornjeg kanala 0, formula postaje: 1 * donji.

Preklapanje slojeva slike - donji sloj
Slika 6. - Donji sloj slike u primerima koje ćemo koristiti u ovom članku, predstavlja standardna fotografija (pejzaž).
Preklapanje slojeva slike - gornji sloj
Slika 7. - Gornji kanal za prvi primer sastoji se iz bele pozadine i crnog natpisa.
Preklapanje slojeva slike - alfa=50%
Slika 8. - Preklapanje slojeva (alfa gornjeg sloja je 50%)

Ostaje samo da prepravimo formulu (funkciju), tako da se vraća RGB vrednost iz konkretnog RGB modela.

Ovoga puta ćemo kao parametre postaviti RGB vrednost za gornji i donji sloj, dok ce Alfa i dalje biti decimalna vrednost između 0 i 1. Ovoga puta ćemo kao parametar takođe predati i maksimalnu RGB vrednost iz datog modela.

									
		Int32 preklapanjeAlfa(Int32 donji, Int32 gornji, Double alfa, Int32 MAX_RGB)
		{
			return (Int32)(MAX_RGB * ((1.0 - alfa) * donji / MAX_RGB + alfa * gornji / MAX_RGB));
		}
									
								
Slika 9. - Funkcija za preklapanje dva sloja koja uzima u obzir i RGB vrednosti.

Deluje malo komplikovano, pa cemo analizirati delove koda:

Podsetimo se da sada (za razliku od prethodne funkcije), vrednost za donji i gornji kanal unosimo kao celobrojnu vrednost u rasponu od 0 do MAX_RGB - 1, pa cemo prvo te dve vrednosti svesti na vrednosti iz apsolutnog modela:

									
		Donji: (1.0 - alfa) * donji / MAX_RGB; 
		Gornji: alfa * gornji / MAX_RGB; 
									
								
Slika 10. - Kao što smo već spomenuli, alfa donjeg kanala računa se tako što se alfa donjeg kanala oduzme od jedinice (jedinicu možemo shvatiti i kao 100% vidljivosti).

Pošto se ove dve vrednosti saberu, dobićemo vrednost u apsolutnom modelu, koju je potrebno prebaciti u RGB model. Ovo ćemo postići prostim množenjem sa MAX_RGB.

Pošto je data vrednost i dalje realan broj, potrebno je još samo da formulu dopunimo operatorom kastovanja koji stavljamo na početak formule.

Preklapanje množenjem

Funkcija za množenje neke od tri RGB vrednosti određenog piksela na dva sloja slike izgleda ovako:

									
		Double preklapanjeMnozenjem(Double donji, Double gornji)
		{
			return donji * gornji;
		}
									
								
Slika 11. - Funkcija za množenje dva sloja - U slučaju apsolutnog modela, funkcija doslovno množi dve vrednosti.

U pitanju je apsolutni model i najveca vrednost koju možemo dobiti je 1.

Ukoliko pravimo funkkciju koja uzima u obzir RGB vrednosti, to će izgledati ovako:

									
		Int32 preklapanjeMnozenjem(Int32 donji, Int32 gornji, Int32 MAX_RGB)
		{
			return (Int32)(MAX_RGB * ((Double) donji / MAX_RGB) * ((Double) gornji / MAX_RGB));
		}
									
								
Slika 12. - Funkcija za množenje dva sloja koja uzima u obzir RGB vrednosti i vraća RGB vrednost.

Pitate se verovatno, šta je to zapravo korisno što možemo dobiti množenjem dva sloja? Ako se setite priče sa početka teksta (da znamo, davno je to bilo) kada smo spomenuli skidanje bele pozadine sa gornjeg sloja. Zapravo je moguće ovo izvesti na besprekoran način, pod uslovom da su pikseli iz gornjeg sloja koje želimo da uklonimo skroz beli.

Preklapanje slojeva slike - množenje (crno-belo)
Slika 13. - Množenjem gornjeg sloja (bela pozadina, crn natpis) dobijamo isti efekat kao da smo na sliku "nalepili" tekst.

Kada pomnožimo donji piksel sa gornjim pikselom koji je beo, rezultat ce biti donji piksel! Setimo se, u apsolutnom modelu, bela boja ima vrednost 1, a šta je rezultat množenja jedinicom - svi znamo. Ukoliko je gornji piksel skroz crn, rezultat ce biti gornji piksel (rezultat ce biti 0, ali to je zapravo i vrednost gornjeg piksela).

Ukoliko gornji piksel nije crn, onda cemo zapravo dobiti piksel čija je vrednost proizvod dve vrednosti, što ce dodatno "ušareniti" sliku. Da bismo mogli da skidamo bele pozadine na gornjem sloju i za "šarene" (ne-crne) piksele, napravićemo našu specijalizovanu funkciju.

Preklapanje slojeva slike - množenje (raznobojno) - gornji sloj
Slika 14. - Ako natpis u gornjem sloju nije crn ....
Preklapanje slojeva slike - množenje (raznobojno)
Slika 15. - .... rezultat je nepredvidiv i "šaren"!

Jednostavno: preko ternarnog operatora pitacemo da li je piksel beo, ako jeste, vraticemo donji piksel, a ako nije, vraticemo gornji (ne moramo pozivati funkciju za množenje), ali, budući da ovoga puta moramo istovremeno voditi racuna o sve tri RGB komponente istovremeno, spremičemo strukturu koja predstavlja piksel i time olakšava obaljanje ovog zadatka.

									
		struct Piksel
		{
			Int32 R, G, B;
		}
									
								
Slika 16. - Struktura koja opisuje piksel, sa poljima za beleženje R, G i B vrednosti.

Funkcija bi onda mogla izgledati ovako:

									
		Int32 skidanjePozadine(Piksel donji, Piksel gornji, Double MAX_RGB)
		{
			Boolean uslov = gornji.R == MAX_RGB &&
							 gornji.G == MAX_RGB &&
							 gornji.B == MAX_RGB;
			
			return uslov? donji : gornji;
		}
									
								
Slika 17. - Specijalizovana funkcija koja (praktično) uklanja belu pozadinu gornjeg sloja!

.... dok bi rezultat izvršavanja bio kao na donjoj slici.

Preklapanje slojeva slike - množenje - preko specijalizovane funkcije
Slika 18. - Korišćenjem specijalizovane funkcije koju smo malopre opisali, lako dolazimo do pravog rezultata.

Nadamo se da smo vas ovim člankom zainteresovali da dodatno istražujute ovu zanimljivu tematiku. :)

Napomena: Tekstovi i slike na sajtu www.skola-programiranja.rs (osim u slučajevima pojedinih fotografija, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta www.skola-programiranja.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora i Računarskog centra SystemPro. ©SystemPro d.o.o. novembar 2019.
Autor članka Nikola Vukićević Za web portal www.skola-programiranja.rs Preuzeto sa sajta www.codeblog.rs uz odobrenje autora
Podelite sa prijateljima: