Hrajeme sa s RAW zábermi z Androidu
V tomto článku sa pozrieme na niektoré nedostatky, ktoré majú fotoaparáty v telefóne a pokúsime sa aplikovať korekcie. Moje metódy merania nie sú úplne bežné, ale snažil som sa využiť zariadenia, ktoré mám doma bez nákupu drahých spektrometrov, či osvetlenia.
Trocha teórie na začiatok
Hoc je fotografovanie z pohľadu používateľa jednoduchý proces v skutočnosti ide o pekne zložitý proces ako po hardvérovej, tak aj po softvérovej stránke.
V momente, keď stlačíme spúšť sa dá do pohybu šošovka fotoaparátu, aby správne zaostrila objekt pred kamerou. Nasleduje zber fotónov, ktorý trvá určitú dobu (táto doba sa nazýva doba expozície) a digitalizácia s príslušným zosilnením. Úroveň zosilnenia poznáme pod skratkou ISO. Surové dáta zo snímača sú následne spracované softvérom a uložené napríklad ako jpeg obrázok.
Optika
Výrobca telefónu sa nás samozrejme bude snažiť presvedčiť, že optika telefónu je dokonalá. Pravdou je, že optika je vždy určitým kompromisom. Pre miniatúrnu optiku telefónu to platí obzvlášť. Na rozdiel od zrkadlovky má telefón pevne danú optiku, takže nie je problém pridať korekciu chýb optiky priamo do softvéru.
Najčastejšími chybami optiky sú vinetácia (tmavšie rohy) a chromatická aberácia (spôsobená rozdielnym indexom lomu pre rôzne farby, na fotografii sa prejavuje farebnými okrajmi objektu). Obe sa dajú odstrániť pomocou softvéru bez viditeľnej degradácie obrazu.
Snímač
Obraz je zvyčajne snímaný CMOS, alebo CCD snímačom. Oba typy využívajú fotoelektrický jav, pri ktorom fotón pri náraze do atómu vyrazí elektrón. Na vyrazenie elektrónu stačí jediný fotón. Snímače sú prekvapivo citlivé a pri dobrom chladení sú schopné registrovať aj jednotlivé fotóny. Čím viacej fotónov dopadne na snímač, tým vznikne väčší elektrický náboj. Kapacita snímača je obmedzená, takže náboj pri expozícii najskôr rastie lineárne, ale na hornej hranici sa rast spomaľuje, až dosiahne saturáciu.
Aby bol obraz farebný býva pred snímačom umiestnený farebný filter. Rozmiestnenie farebných plôch nie je typická obdĺžniková RGB mriežka, ako ju poznáme z obrazoviek. Namiesto nej sa používa Bayerov vzor. Štvorica pixelov má vždy zelené pixely usporiadané diagonálne, čo nám dáva 2 možnosti usporiadania. Zvyšné 2 pozície majú červené a zelené pixely, čo nám dokopy dáva 4 možnosti usporiadania (GRBG
, GBRG
, RGGB
a BGGR
).
Nazbieraný náboj sa následne prevedie z analógovej hodnoty na digitálnu. Signál sa najskôr zosilní (čím vyššie ISO, tým vyššie zosilnenie a tým vyšší šum) a následne sa preženie A/D prevodníkom. Výsledné hodnoty sú v určitom rozsahu, napríklad 0-1023. Snímač môže mať nulovú hodnotu vychýlenú (napr. 16), aby sa orezala začiatočná nelineárna časť. Tak isto môže byť hodnota blížiaca sa k hornej hranici nepoužiteľná v dôsledku nelineárneho priebehu.
Od RAW k JPEG
Spracovanie RAW obvykle v telefóne obvykle začína prevodom hodnôt pixelov na lineárne, začínajúce v nule. Ak je prevodník lineárny, postačuje odrátať hodnotu čiernej (pre vychýlený snímač).
Následne sa aplikuje korekcia vinetácie. Tu sa zvyčajne hodnoty pixelov vynásobia faktorom, ktorý závisí od pozície pixelu. Čím je pixel ďalej od stredu, tým býva faktor vyšší.
Úroveň červených, zelených a modrých pixelov sa násobí ďalším faktorom, pretože farebné spektrum sa zásadne mení podľa druhu osvetlenia. Svetlo pri východe / západe slnka obsahuje napríklad omnoho menej modrej zložky než cez poludnie. Táto transformácia sa volá vyváženie bielej.
Nasleduje prevod z Bayerovho vzoru na RGB
hodnoty napríklad pomocou algoritmu PPG[1]. Z obrázku vzoru si môžme všimnúť, že na plnú farebnú informáciu potrebujeme hodnoty zo 4 susedných pixelov. V skutočnosti sa všetky 4 pixely používajú na výpočet jasu, ale farebný odtieň má nižšie rozlíšenie. Na prvý pohľad to vyzerá ako limitácia, alebo klamanie zákazníka, pretože odtieň má nižšie rozlíšenie, ale ľudský zrak má tak isto nižšie rozlíšenie pre hodnotu farby. Výstup do JPEGu je často tiež vo farebnom priestore YUV420
, ktorý má štvrtinové rozlíšenie farby.
Spracovanie pokračuje voliteľnými krokmi, ako je doostrenie, odstránenie šumu, zvýšenie kontrastu farieb, odstránenie chromatickej aberácie atď. Posledným krokom je aplikácia gama krivky a vygenerovanie výstupu (napríklad JPEG). Tieto kroky už bude robiť softvér na vyvolávanie RAW, takže sa nimi v tomto článku nebudem zaoberať.
Prečo fotografovať do RAW
Fotografovanie do JPEGu je často postačujúce. Sú však okamihy, ktoré sa neopakujú a je veľmi nepríjemné, keď práve v tom okamihu automatika pokazí záber.
Formát RAW má zvyčajne vyššiu bitovú hĺbku než JPEG. Kým JPEG má 256 úrovní jasu, RAW z môjho mobilu má 1024 úrovní. To umožňuje zo záberu vytiahnuť detaily, ktoré by v JPEGu ani neboli. V nasledujúcom obrázku je ukážka, ako sa zmení atmosféra, ak sa upraví kontrast v určitých oblastiach.
Pri RAWe sa niekedy dajú zachrániť zdanlivé prepaly. Kým JPEG prešiel celým procesom násobenia pri korekcii vyváženia bielej, v RAWe kľudne môže byť zelená plne saturovaná, kým červená / modrá je na 80% maxima. Z RAWu je v takom prípade možné bez problémov korigovať prepal.
Častým neduhom automatiky je zle odhadnuté vyváženie bielej. Je drastický rozdiel vo farebnom spektre poludňajšieho slnka a napríklad žiarovky. V bežnom živote si to moc neuvedomujeme, pretože takéto veci vie mozog automaticky vykompenzovať. Ten, kto niekedy filmoval v manuálnom režime s uzamknutým vyvážením bielej bude vedieť, aký brutálny rozdiel v podaní farieb majú rôzne zdroje svetla. Fotografie urobené vo formáte RAW majú uložené nespracované hodnoty zo snímača, takže úprava výváženia bielej sa dá urobiť jednoducho v softvéri pre úpravu RAW ukázaním na bielu plochu.
Čo k tomu potrebujeme
Keďže sa bavíme o Androide, budeme potrebovať Android telefón s podporou Camera 2 API a RAW formátom. To by pre väčšinu mobilov nemal byť problém, aj keď na niektorých je nutné povoliť persist.camera.HAL3.enabled = 1
v build.prop
. Ja používam napríklad low end Redmi 4A z roku 2016.
Okrem hardvéru budeme potrebovať aj softvér, pretože vstavaný fotoaparát nevie fotografovať do RAW. Ja som si celkom obľúbil Open Camera. Pre spracovanie RAWu osobne používam Darktble, skôr zo zvyku, nemám žiaden konkrétny dôvod, prečo som vybral práve Darktable.
Parametre optiky / senzoru
Softvér pre vyvolávanie RAW snímkov bežne obsahuje databázu objektívov a snímačov pre bežne používané fotoaparáty. To isté neplatí pre mobilné telefóny. Mňa k hľadaniu parametrov priviedol fakt, že vyvolané snímky mali hroznú vinetáciu, ružový nádych na okrajoch, zelený v strede a prepaly na oblakoch boli ružové.
Skôr než budeme zisťovať parametre potrebujeme vedieť, aký senzor máme v mobile. Ja mám Xiaomi Redmi 4A. Jednoduché hľadanie na googli mi nedalo odpoveď, takže som siahol po aplikácii na zobrazenie hardvéru. Podľa Device Info HW mám ov13850_q13v06k
. Konfiguráciu kamery, ktorá obsahuje len odkazy na binárky som našiel v device tree. Dekompilovať binárku tu vzhľadom na obsah nepomôže, keďže skoro celý súbor sa skladá z parametrov. Zdrojový kód je možné nájsť napríklad tu.
Podľa toho, čo som našiel na internete tadiaľ cesta nevedie. Rozhodol som sa teda parametre, ako čierny bod a vinetáciu zmerať sám pomocou nástrojov, ktoré som našiel v zásuvke.
Meranie parametrov
Zmerať čierny bod je triviálna úloha. Stačí zakryť objektív, cvaknúť RAW a pomocou softvéru sa pozrieť na priemernú hodnotu.
Vinetácia vzniká kvôli nedostatku optiky. Na okraj snímača tak dopadá menej svetla než na stred. Pozrime sa na modelovú situáciu, v ktorej budeme mať snímač s rozlíšením neuveriteľné 3x3 pixely. Fotografovať budeme jednoliatu bielu plochu osvetlenú rovnomerne intenzitou svetla 100 lx. Napriek jednoliatej bielej ploche dostaneme zo snímača nasledujúcu úroveň osvetlenia:
50 80 50 80 100 80 50 80 50
Ak vieme, že sme fotografovali jednoliatu plochu, potom môžme ľahko vyrátať útlm pre každý pixel. Stačí hodnoty pixelov vydeliť maximom (100) a máme pole:
0.5 0.8 0.5 0.8 1.0 0.8 0.5 0.8 0.5
Keď vieme, že nameraná hodnota vznikla ako hodnota * útlm, potom vieme, že vynásobením nameranej hodnoty úrovne osvetlenia prevrátenou hodnotou útlmu dostaneme pôvodnú úroveň osvetlenia - pôvodná hodnota = nameraná hodnota * (1 / útlm). Pole prevrátených hodnôt vyzerá nasledovne:
2.00 1.25 2.00 1.25 1.00 1.25 2.00 1.25 2.00
Keď každý prvok poľa nameraných hodnôt vynásobíme týmto poľom dostaneme pôvodné neskreslené hodnoty:
100 100 100 100 100 100 100 100 100
Ako to už býva zvykom, ten najväčší problém býva ukrytý v detailoch. V posledných odstavcoch som písal o úrovni osvetlenia. Na jednotkách až tak nezáleží, dôležité je, aby som vedel, že hodnota napríklad 100 je dvojnásobkom 50. To platí len, keď je nameraná hodnota zo snímača lineárne závislá od úrovne osvetlenia. Ideálne by mala byť lineárna, ale istí si nemôžme byť. Takže ako zmerať túto krivku?
V prvom rade budeme potrebovať aplikáciu pre Android, ktorá bude ovládať kameru a prenášať obrázky do počítača. Ďalej budeme potrebovať hardvér, ktorý umožní plynulé lineárne stmievanie a softvér pre kalibráciu (bude riadiť kameru a stmievanie).
Prenášame fotografie z Androidu
Nenašiel som žiaden pekný softvér, pre uskutočnenie toho, čo som zamýšľal. Ak poznáte nejakú peknú aplikáciu, tak do komentárov s ňou.
Bez vhodnej aplikácie som sa ocitol pred dilemou, ako a v čom naprogramovať jednoduché ovládanie kamery a prenos. Ovládam dokopy tak 10-15 jazykov, ale obľubujem python, pretože mi umožňuje narýchlo pozliepať jednoduché nástroje, má skvelý shell (ipython), takže môžem aj bez dokumentácie jednoducho systémom pokus-omyl zostaviť čo potrebujem a hlavne nemusím čakať na kompiláciu.
Asi bolo naivné myslieť si, že nainštalujem balík, otvorím adb shell
a spustím python. Jednoducho Android má tak sprznený userspace, ako je to len možné. Ani termux ani QPython neboli schodné. Ako jediné schodné riešenie som našiel kivy-remote-shell. Lenže link na binárku nefunguje a ja som aj tak musel nainštalovať celý ten bordel okolo Androidu. No aj tak si hovorím, že za tých 6 hodín sťahovania a hučania chladiča v noci to bude stáť, keď potom budem programovať bez kompilácie. Keby niekto chcel binárku, tak tu je môj build.
Takže mám python shell a podľa dokumentácie môžem cez PyJNIus volať všetky API Androidu. Uznávam, že možné to je. Dokonca je možné implementovať java interface v pythone. Čo však nie je možné - implementovať abstraktnú triedu, ktorá je nevyhnutná na získanie záberu z kamery. Oficiálne odporúčanie je implementovať si abstraktné triedy v jave, čo zrušilo celý môj zámer vyhnúť sa kompilácii. Tak som sa nakoniec po dni zabitom slepou uličkou rozhodol použiť Javu.
Kód som vtesnal do jediného súboru. Viem, že je to proti filozofii Javy, ale mne sa nechcelo babrať s viacerými súbormi pre totálne jednoduchý nástroj. Na githube mám zverejnené skompilované apk.
Pár slov ku kompiácii
Javisti majú v obľube rôzne šialené XML a IDE, ktoré nie sú schopné ani hodinu po štarte schopné skompilovať jednoduchý Hello world. Mňa jednoducho prestalo baviť čakanie a pokúsil som sa všetko skompilovať ručne z konzoly. Tento postup, ktorý tu popisujem nie je oficiálne podporovaný a to, že funguje v čase písania článku neznamená, že bude fungovať o niekoľko dní neskôr.
Na začiatok budeme potrebovať Java JDK. Odporúčam nainštalovať z distribučných zdrojov.
Zo stránky developer.android.com stiahneme "Command line tools only". Archív som u seba rozbalil do adresára /opt/android-sdk/cmdline-tools/latest
a celému adresáru som zmenil vlastníka na užívateľa, pod ktorým budem vyvíjať. Pridal som cestu k binárkam do PATH
.
export ANDROID_HOME=/opt/android-sdk export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
Ďalej je potrebné nainštalovať príslušné SDK a nástroje pre zostavovanie balíkov:
sdkmanager 'platforms;android-31' 'build-tools;31.0.0' platform-tools
Doplníme cesty k ďalším nástrojom:
export PATH=$PATH:$ANDROID_HOME/build-tools/31.0.0:$ANDROID_HOME/platform-tools::$ANDROID_HOME/tools export ANDROID_PLATFORM=$ANDROID_HOME/platforms/android-31
Skompilovať a nainštalovať aplikáciu je teraz otázkou pár príkazov:
# Kompilácia java kódu javac -d build -bootclasspath $ANDROID_PLATFORM/android.jar -classpath src -source 1.7 -target 1.7 src/sk/linuxos/*.java # Prevod java bytekódu na DEX bytekód d8 build/sk/linuxos/*.class --release --output build/dex --no-desugaring # Zabalenie resorce súborov aapt package -f -F build/CalibrateCamera.apkPart -I $ANDROID_PLATFORM/android.jar -M AndroidManifest.xml -S res -v # Vytvorenie apk súboru CLASSPATH=$ANDROID_HOME/tools/lib/* java com.android.sdklib.build.ApkBuilderMain build/CalibrateCamera.apkUnalign -d -f build/dex/classes.dex -v -z build/CalibrateCamera.apkPart # Zarovnanie nekomprimovaných súborov voči začiatku súboru zipalign -f -v 4 build/CalibrateCamera.apkUnalign build/CalibrateCamera.apk # Inštalácia do zariadenia adb install -r build/CalibrateCamera.apk
Pre inštaláciu je potrebné mať zapnutý režim vývojára v Androide, povolenú inštaláciu cez USB a pravdepodobne bude potrebné prvú inštaláciu potvrdiť na mobile. Pri aktualizácii už potvrdenie nie je nutné.
Použitie aplikácie
Keďže som lenivý implementovať kontrolu práv, bude aplikácia automaticky predpokladať, že má prístup ku kamere. Pred spustením je nutné zobraziť informácie o aplikácii, prekliknúť sa na povolenia a povoliť prístup ku kamere, inak havaruje.
Pred spustením odporúčam zapnúť aj režim vývojára a spustiť si logovanie:
adb logcat sk.linuxos.CameraCalibrate:V *:S AndroidRuntime:E
Aplikácia prijíma príkazy cez TCP na porte 8421. Aby bol prenos dát čo najrýchlejší odporúčam presmerovať tento port cez USB príkazom:
adb forward tcp:8421 tcp:8421
Po spustení aplikácie buď cez ikonku, alebo v konzole príkazom adb shell am start -n sk.linuxos/.CameraCalibrate
by sa mal v logu objaviť výpis podobný tomuto:
10-09 17:13:38.656 24197 24197 V sk.linuxos.CameraCalibrate: RAW size: 4208x3120 10-09 17:13:38.942 24197 24197 V sk.linuxos.CameraCalibrate: Supported ISO: 100-1550 10-09 17:13:38.942 24197 24197 V sk.linuxos.CameraCalibrate: Supported Exposure: 12367-328094112 10-09 17:13:39.151 24197 24197 V sk.linuxos.CameraCalibrate: Camera opened
Aj všetko funguje správne, je možné komunikovať s telefónom cez príkazy posielané na port 8421. Takto vyzerá napríklad prečítanie rozsahu hodnoty ISO:
echo "getIsoRange\nquit"|socat -,ignoreeof TCP4:127.0.0.1:8421 > 100 1550
Podrobnejšie informácie o fungovaní
K tejto časti odporúčam otvoriť si kompletný zdrojový kód.
Mobilné telefóny majú zvyčajne viacej kamier. Ja vyberám konkrétne zadnú kameru, poslednú v zozname kamier (keďže mám len jednu neimplementoval som lepší výber).
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); String[] cameraIdList = cameraManager.getCameraIdList(); String mainCamera = null; CameraCharacteristics mainCameraCharacteristics = null; for (String cameraId: cameraIdList) { CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) { mainCamera = cameraId; mainCameraCharacteristics = characteristics; } }
Sieťová vrstva je totálne odfláknutá a zvládne maximálne 1 klienta. Na to, čo potrebujeme postačujúce.
Implementované sú nasledujúce príkazy:
- quit
- Ukončenie spojenia
- getResolution
- Vráti rozlíšenie ako 2 čísla oddelené medzerou ukončené novým riadkom
- getBytesPerPixel
- Vráti počet bytov na pixel ukončený novým riadkom
- getIsoRange
- Vráti minimálna a maximálne ISO oddelené medzerou ukončené novým riadkom
- getExposureRange
- Vráti minimálnu a maximálnu dobu expozície oddelenú medzerou ukončené novým riadkom
- getPixelPattern
- Vráti rozloženie pixelov, napr. BGGR ukončené novým riadkom
- setIso hodnota
- Nastavenie ISO
- setExposure hodnota
- Nastavenie expozície
- getRaw [x y w h [počet]]
- Vráti zachytené dáta obrázka, alebo výrezu. Voliteľný parameter počet umožňuje zachytiť sériu (ide o maximálnu rýchlosť snímania, snímanie je omnoho rýchlejšie než postupné volanie getRaw). Vrátené dáta obsahujú najskôr hlavičku s hodnotami v nových riadkoch a nakoniec
data počet_bytov
nasledované surovými dátami.
echo "setIso 100\nsetExposure 32809411\ngetRaw 0 0 0 0\nquit"|socat -,ignoreeof TCP4:127.0.0.1:8421 > color_correction_gains: 1.826987 1.0 1.0 1.555144 > black_level: 16 16 16 16 > white_level: 1023 > data: 0
Stmievateľný zdroj svetla
Lineárne stmievanie zdroja svetla nie je tak jednoduché, ako na prvý pohľad vyzerá. Či už chceme stmievať žiarovku, LED, či iný zdroj svetla, v žiadnom prípade zdroj nereaguje na zmenu prúdu, alebo napätia lineárne. Okrem toho ani samotné zdroje, ako napríklad žiarovka nemusia mať stabilné farebné spektrum.
Na začiatku som popisoval optický snímač ako zariadenie, ktoré zbiera elektrický náboj. Principiálne je jedno, či snímač osvetlíme celú dobu slabším svetlom, alebo ho osvetlíme blikajúcim zdrojom svetla s nastaviteľnou dĺžkou doby svietenia.
Ideálnym zdrojom svetla je monochomatická LED dióda. Pozor na biele LED, v drvivej väčšine sú to modré / ultrafialové LED s vrstvou luminoforu. Pre luminofor je typické, že žiari chvíľu po vypnutí zdroja svetla. Odporúčam použiť 525nm zelenú LED (pretože zelené pixely majú väčší zisk, niekedy sú dokonca fyzicky väčšie).
Budeme teda potrebovať:
- kus papiera
- lepiacu pásku
- zelenú LED
- mikrokontrolér s PWM
- štipec
Šošovku prelepíme kusom papiera. LED pripojíme k mikrokontroléru a štipcom pripevníme k šošovke.
Ja som použil ako mikrokontrolér ESP32, ktoré sa mi povaľovalo v zásuvke. Samozrejme nebol by problém použiť napríklad STM32, či AVR.
Zdrojové kódy pre ESP32 mám zverejnené v repozitári. Hardvér prijíma cez UART nasledujúce príkazy:
- f hodnota
- Nastavenie frekvencie v HZ
- r hodnota
- Nastavenie rozlíšenia (počtu krokov) PWM v bitoch (počet krokov = 2 hodnota)
- v hodnota
- Nastavenie dĺžky intervalu zapnutia (ak je rozlíšenie 8, potom minimum je 0 a maximum 255)
- a
- Aplikovať nastavenia
Kalibrácia
Pre kalibráciu som napísal sadu nástrojov calibrate_tools.py. Pre spustenie budeme potrebovať numpy, matplotlib a opencv.
Pomoc sa zobrazí volaním python calibrate_tools.py --help
.
Calibrate tools positional arguments: {get_resolution,get_bytes_per_pixel,get_iso_range,get_exposure_range,get_pixel_pattern,show,measure,measure_frequency_response,show_frequency_response,measure_gamma,show_gamma_response,create_gamma_curve,write_camera_info,generate_dark_frame,save_raw,write_vignette} Command get_resolution Get resolution get_bytes_per_pixel Get bytes per pixel get_iso_range Get iso range get_exposure_range Get exposure range get_pixel_pattern Get pixel pattern show Show image measure Measure single frame measure_frequency_response Measure frequency response show_frequency_response Show frequency response measure_gamma Measure gamma show_gamma_response Show frequency response create_gamma_curve Create gamma curve write_camera_info Camera info generate_dark_frame Generate dark frame save_raw Save raw write_vignette Write vignette map optional arguments: -h, --help show this help message and exit --port PORT Port --host HOST Host --tty TTY TTY
Jednotlivé podprogramy sa spúšťajú príkazom python calibrate_tools.py prikaz parametre
. Dodatočné parametre sa dajú zobraziť príkazom python calibrate_tools.py prikaz --help
.
python calibrate_tools.py measure --help usage: calibreate_tools.py measure [-h] [--iso ISO] [--exposure EXPOSURE] [--size SIZE] [--color {R,G,B}] [--show] optional arguments: -h, --help show this help message and exit --iso ISO ISO --exposure EXPOSURE Exposure time --size SIZE Capture area size --color {R,G,B} Color component --show Show image
Najskôr uložíme informácie o kamere príkazom python calibrate_tools.py write_camera_info camera.json
. Výstup camera.json
z môjho telefónu vyzerá nasledovne:
{ "resolution": [ 4208, 3120 ], "iso_range": [ 100, 1550 ], "exposure_range": [ 1.2367e-05, 0.328094112 ], "bytes_per_pixel": 2, "pixel_pattern": "BGGR", "black_level": [ 16, 16, 16, 16 ], "white_level": 1023 }
Väčšina meraní má voliteľný parameter --size
, ktorý určuje, aká veľká plocha senzoru sa použije na meranie. Štandardne sa používa oblasť o veľkosti 100x100 pixelov. Mala by byť dosť malá, aby sa na nej ešte neprejavovala vinetácia, ale dosť veľká, aby sa dalo spriemerovať väčšie množstvo pixelov.
Určenie doby expozície
Teraz je pre nás zaujímavá hlavne hodnota bielej (1023) a rozsah doby expozície. Teraz nastavíme PWM na maximálny jas zápisom nasledujúcich príkazov na UART mikrokontroléru:
r 8 f 10000 v 255 a
Počas kalibrácie sa doba expozície nastavuje ručne. Nastavená by mala byť tak, aby pri plnom jase dochádzalo k úplnému prepalu (všetky nemŕtve pixely by mali v tomto prípade hodnotu 1023). Na nájdenie vhodnej hodnoty použijeme nástroj measure.
python calibrate_tools.py measure --exposure 0.01 Mean: 270.0773966578716, stdev: 2.4867616235027503 python calibrate_tools.py measure --exposure 0.02 Mean: 508.9207836456559, stdev: 3.728845256883643 python calibrate_tools.py measure --exposure 0.04 Mean: 975.359126984127, stdev: 4.529225645971837 python calibrate_tools.py measure --exposure 0.05 Mean: 1022.6521264994548, stdev: 0.4762956310566431 python calibrate_tools.py measure --exposure 0.06 Mean: 1022.6497597437267, stdev: 0.4770450913267325
Nad hodnotou 0.05 už nestúpa nameraná hodnota. V tomto prípade vyzerá ako ideálna hodnota 0.05 s. Od teraz budeme pri kalibračných utilitách používať vybranú expozičnú dobu (--exposure 0.05
).
Určenie frekvencie PWM modulácie
Frekvencia PWM modulácie by mala byť dostatočne vysoká, aby sa nedochádzalo k nerovnomernému osvetleniu po sebe idúcich snímkov, ale zároveň dostatočne nízka na to, aby sa neprejavovalo skreslenie v dôsledku kapacity / indukcie prívodných vodičov.
Vytvoril som jednoduchý nástroj measure_frequency_response
, ktorý nastaví jas na 50% a postupne meria výstup snímača pre rôzne frekvencie PWM modulácie.
V nasledujúcom grafe je modrá čiara priemerná hodnota. Tmavo-modrá oblasť tesne okolo čiary je štandardná odchýlka v rámci snímkov (vyráta sa pre každý snímok zvlášť a potom sa z nej vypočíta priemer). Svetlo-modrá oblasť je štandardná odchýlka priemerných hodnôt medzi snímkami na danej frekvencii. Z grafu môžme vidieť, že stabilná hodnota je v oblasti 1000 - 100000 Hz. Volím teda niekde v strede - 39367 Hz (nechcem nikde chytať interferenciu, preto volím prvočíslo).
Určenie odozvy snímača na úroveň osvetlenia
Meranie odozvy snímača na zmenu osvetlenia spustíme príkazom:
python calibrate_tools.py measure_gamma --exposure=0.05 --points=2048 --frequency=39367
Po 20480 snímkoch nám v adresári ../output
vznikne súbor gamma_iso100_exp5000000.csv
s konkrétnou odozvou snímača. Zobraziť výsledok je možné príkazom python calibrate_tools.py show_frequency_response ../output/gamma_iso100_exp5000000.csv
.
Prekvapujúco lineárne až na hodnoty presahujúce 1010. Podľa merania by sa môj problém s ružovými prepalmi dal vyriešiť posunutím bieleho bodu v RAW na 1010. Ešte pre istotu som si cez graf nakreslil lineárnu regresnú krivku (oranžová). Prekrytie s modrou je nečakane presné.
Korekčnú tabuľku (v tomto prípade nie je ani potrebná) môžme vytvoriť príkazom python calibrate_tools.py create_gamma_curve ../output/gamma_iso100_exp5000000.csv --black_level=16 --white_level=1010
. Výsledný súbor ../output/gamma_iso100_exp5000000.json
stačí potom pridať do camera.json
, aby sa použil pri korekcii.
{ "resolution": [ 4208, 3120 ], "iso_range": [ 100, 1550 ], "exposure_range": [ 1.2367e-05, 0.328094112 ], "bytes_per_pixel": 2, "pixel_pattern": "BGGR", "black_level": [ 16, 16, 16, 16 ], "white_level": 1023, "gamma": "../output/gamma_iso100_exp5000000.json", "black_frame": "../output/black_frame.npy", "vignette_map": "../output/vignette_map.npy" }
Korekcia čierneho snímku
Aj keď na snímač nedopadá svetlo na výstupe nameriame určitý šum. Časť šumu je spôsobená teplom a časť je statický šum v dôsledku nedokonalej výroby zosilňovačov. Pri korekcii čierneho snímku vytvoríme veľké množstvo záberov so zatieneným objektívom. Zábery sa spriemerujú, čím sa zbavíme náhodného tepelného šumu a zostane len dynamický.
Pre vygenerovanie čierneho snímku stačí zakryť objektív a spustiť príkaz:
python calibrate_tools.py generate_dark_frame --count=100 --exposure=0.05 ../output/black_frame.npy
Korekcia vinetácie
Asi najväčší problém, ktorý mám pri fotografiách z mobilu sú extrémne tmavé rohy. Aby som presne zmeral, aký zlý je tento problém, prelepil som objektív tenkým papierom (ak by niekoho zaujímalo, použil som vyblednutý cestovný lístok, pretože sa mi štruktúra tohto papiera zdala najhomogénnejšia). Objektív som rovnomerne osvetlil a nastavil expozíciu tak, aby maximum bolo tesne pred hodnotou, kde je snímač už nelineárny. V nasledujúcom snímku dávam do pozornosti rozsah hodnôt vpravo. Pritom snímač by mal byť takmer dokonale rovnomerne osvetlený.
Korekčná matica je invertovaný normalizovaný biely snímok. Rozlíšenie korekčnej matice je znížené, aby sa redukoval šum. Vytvoriť ho môžme nasledujúcim príkazom:
python calibrate_tools.py write_vignette ../output/gamma_iso100_exp5000000.json ../output/vignette_map.npy --exposure 0.1
Aplikovanie korekcií
Nenašiel som žiadnu knižnicu na modifikáciu RAW. Našťastie z Androidu lezú .dng súbory bez kompresie. Pri písaní fix_raw.py
som sa preto spoliehal na to, že na konci súboru sú surové dáta a ja ich môžem beztrestne nahradiť. Korekcie sa aplikujú príkazom python fix_raw.py camera.json zle.dng opravene.dng
.
Záver
Zábery s aplikovanou korekciou vyzerajú výrazne lepšie než neupravené, či pôvodné JPEGy. Či to stojí za celú námahu? Neviem. Mojim hlavným cieľom bolo naučiť sa niečo nové a to som si splnil.
Korekcia, ktorú som robil je len taký základ. Pokračovať by sme mohli napríklad opravou geometrie šošovky, chromatickou aberáciou, miernou stratou saturácie v rohoch … Nemyslím si, že by tieto zostávajúce nedostatky boli nejak závažné.
Poznámky
Pre pridávanie komentárov sa musíte prihlásiť.
Za mňa určite veľmi kvalitný článok, niekedy až zbytočne do hĺbky :-) Keď budem mať viac času prejdem to podrobnejšie, raw formát má samozrejme svoje výhody :-)
Pekné čítanie! Už chýba len krôčik od spektrometra?
Vyrobiť použiteľný spektrometer je šialene ťažké. Ale vyrobiť taký hračkársky z CD alebo v lepšom prípade pomocou difrakčnej mriežky nie je až tak ťažké.
neuverejníš aj svoj proces pri upravovaní rawiek v darktable ?
viem že je na to kopa navodov na nete a YT ale mňa by celkom zaujímalo aj ako to robíš ty, kedže výsledky máš pekné (na rozdiel od mojich pokusov)
Tak toto by aj mňa zaujímalo, najlepšie výsledky mi vždy vyšli v Lightroome alebo keď som bol lenivý tak s presetmi Luminaru. Nejako ten Darktable tiež nemám v ruke,,,
Nemám žiaden ustálený postup. Vždy záleží od toho, aký mám vstup a aký výstup chcem dostať. Na svojom youtube mám jeden príklad. Pre farebné typicky upravujem expozíciu, zapnem lokálny kontrast, zapnem odstránenie šumu (len chromatický), pridám kontrast farieb a zvyšok totálne podľa potreby. Niekde použijem parametrickú + kreslenú masku na stiahnutie jasu oblohy, niekde použijem clarity ...
Fantastický článok, ktorý by pokojne mohol byť aj ako seminárna či nebodaj bakalárska práca…