Hrajeme sa s RAW zábermi z Androidu

10.10.2021 | 13:28 | Mirecove dristy | Miroslav Bendík

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).

Bayerov vzor
Obrázok 1: Bayerov vzor

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.

Vľavo JPEG, vpravo upravené RAW
Obrázok 2: Vľavo JPEG, vpravo upravené RAW
Porovnanie celých obrázkov
Obrázok 3: Porovnanie celých obrázkov

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.

Fotoaparát na Redmi 4A
Obrázok 4: Fotoaparát na Redmi 4A

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.

Nastavenie práv
Obrázok 5: Nastavenie práv

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ť:

Š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.

Hardvér
Obrázok 6: Hardvér

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.

Očakávaný výsledok merania
Obrázok 7

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).

Meranie odozvy na frekvenciu
Obrázok 8: Meranie odozvy na frekvenciu

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.

Odozva snímača na úroveň svetla
Obrázok 9: Odozva snímača na úroveň svetla

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é.

Odozva snímača na úroveň svetla a lineárna regresia
Obrázok 10: Odozva snímača na úroveň svetla a lineárna regresia

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ý.

Rovnomerne osvetlený záber
Obrázok 11: "Rovnomerne" osvetlený záber

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
Matica koeficientov, ktorými sa násobia hodnoty pixelov
Obrázok 12: Matica koeficientov, ktorými sa násobia hodnoty pixelov

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.

Hore JPEG, v strede RAW bez korekcií, dole RAW s korekciami
Obrázok 13: Hore JPEG z telefónu, v strede RAW bez korekcií, dole RAW s korekciami

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

  1. Pixel Grouping
    • RE: Hrajeme sa s RAW zábermi z Androidu 10.10.2021 | 14:30
      Avatar Pavel Q4OS KDE  Administrátor

      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 :-)

    • RE: Hrajeme sa s RAW zábermi z Androidu 10.10.2021 | 16:16
      Avatar mark   Používateľ

      Pekné čítanie! Už chýba len krôčik od spektrometra?

      • RE: Hrajeme sa s RAW zábermi z Androidu 10.10.2021 | 16:51
        Avatar Miroslav Bendík Gentoo  Administrátor

        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é.

    • RE: Hrajeme sa s RAW zábermi z Androidu 11.10.2021 | 09:49
      Avatar Branislav Poldauf Manjaro, Debian stable  Používateľ

      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)

      Linux: the operating system with a CLUE... Command Line User Environment
      • RE: Hrajeme sa s RAW zábermi z Androidu 11.10.2021 | 11:50
        Avatar Pavel Q4OS KDE  Administrátor

        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,,,

      • RE: Hrajeme sa s RAW zábermi z Androidu 11.10.2021 | 13:08
        Avatar Miroslav Bendík Gentoo  Administrátor

        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 ...

    • RE: Hrajeme sa s RAW zábermi z Androidu 12.10.2021 | 15:30
      Avatar Richard Antix  Používateľ

      Fantastický článok, ktorý by pokojne mohol byť aj ako seminárna či nebodaj bakalárska práca…