Laskennallinen valokuvaus on valokuvausprosessin tehostamista laskennalla. Vaikka yleensä ajattelemme, että tämä koskee vain lopputuloksen jälkikäsittelyä (samanlainen kuin valokuvien muokkaus), mahdollisuudet ovat paljon laajemmat, koska laskenta voidaan ottaa käyttöön valokuvauksen jokaisessa vaiheessa - aloittaen kohtauksen valaistuksesta, jatkamalla objektiivissa ja lopulta jopa otetun kuvan näytöllä.
Tämä on tärkeää, koska sen avulla voidaan tehdä paljon enemmän ja eri tavoin kuin mitä normaalilla kameralla voidaan saavuttaa. Se on myös tärkeä, koska nykyisin yleisin kameratyyppi - mobiilikamera - ei ole erityisen tehokas verrattuna isompaan sisarukseensa (DSLR), mutta onnistuu kuitenkin tekemään hyvää työtä hyödyntämällä laitteessa olevaa laskentatehoa .
Tarkastelemme kahta esimerkkiä, joissa laskenta voi parantaa valokuvausta - tarkemmin sanottuna näemme, kuinka yksinkertaisesti useampien kuvien ottaminen ja vähän Pythonia käyttämällä niiden yhdistäminen voi luoda hienoja tuloksia kahdessa tilanteessa, jossa mobiilikameralaitteisto ei todella loistaa - heikko valo ja korkea dynaaminen alue.
Oletetaan, että haluamme ottaa kuvan heikossa valossa, mutta kameralla on pieni aukko (linssi) ja rajoitettu valotusaika. Tämä on tyypillinen tilanne matkapuhelinkameroille, jotka heikossa valaistuksessa voivat tuottaa tällaisen kuvan (otettu iPhone 6 -kameralla):
Jos yritämme parantaa kontrastia, tulos on seuraava, mikä on myös melko huono:
Mitä tapahtuu? Mistä kaikki tämä melu tulee?
Vastaus on, että melu tulee anturista - laitteesta, joka yrittää selvittää, milloin valo osuu siihen ja kuinka voimakas valo on. Heikossa valossa sen on kuitenkin lisättävä herkkyyttään paljon rekisteröidäkseen mitään, ja tämä korkea herkkyys tarkoittaa, että se alkaa myös havaita vääriä positiivisia - fotoneja, joita yksinkertaisesti ei ole. (Lisähuomautuksena tämä ongelma ei koske vain laitteita, vaan myös meitä ihmisiä: Kun seuraavan kerran olet pimeässä huoneessa, huomaa näkökentässäsi oleva ääni.)
Kuvantamislaitteessa on aina jonkin verran melua; kuitenkin, jos signaalilla (hyödyllinen tieto) on korkea intensiteetti, kohina on merkityksetön (korkea signaali-kohinasuhde). Kun signaali on heikko - kuten hämärässä - melu erottuu (matala signaali kohinaan).
Silti voimme voittaa meluongelman jopa kaikilla kamerarajoituksilla saadaksemme parempia kuvia kuin yllä oleva.
Tätä varten meidän on otettava huomioon, mitä tapahtuu ajan myötä: Signaali pysyy samana (sama kohtaus ja oletamme sen olevan staattinen), kun taas melu on täysin satunnainen. Tämä tarkoittaa, että jos otamme useita otoksia näkymästä, heillä on eri versiot melusta, mutta samat hyödylliset tiedot.
Joten, jos keskiarvo on useita ajan mittaan otettuja kuvia, kohina poistuu, kun signaali ei muutu.
Seuraava kuva esittää yksinkertaistetun esimerkin: Meillä on signaali (kolmio), johon kohina vaikuttaa, ja yritämme palauttaa signaalin keskiarvolla useita saman signaalin esiintymiä, joihin eri kohina vaikuttaa.
Näemme, että vaikka kohina on riittävän voimakas vääristämään signaalia kokonaan missä tahansa yksittäisessä tapauksessa, keskiarvo laskee asteittain ja palautamme alkuperäisen signaalin.
Katsotaanpa, miten tämä periaate soveltuu kuviin: Ensinnäkin meidän on otettava useita kuvia kohteesta mahdollisimman suurella valotuksella, jonka kamera sallii. Saat parhaat tulokset käyttämällä sovellusta, joka sallii manuaalisen kuvaamisen. On tärkeää, että kuvat otetaan samasta paikasta, joten (improvisoitu) jalusta auttaa.
Enemmän kuvia tarkoittaa yleensä parempaa laatua, mutta tarkka määrä riippuu tilanteesta: kuinka paljon valoa on, kuinka herkkä kamera on jne. Hyvä etäisyys voi olla missä tahansa välillä 10 ja 100.
Kun meillä on nämä kuvat (raakamuodossa, jos mahdollista), voimme lukea ja käsitellä niitä Pythonissa.
Niille, jotka eivät ole perehtyneet kuvankäsittelyyn Pythonissa, meidän on mainittava, että kuva on 2D-tavuarvojen taulukko (0-255) - siis yksivärinen tai harmaasävykuva. Värikuvan voidaan ajatella olevan kolmen tällaisen kuvan joukko, yksi kullekin värikanavalle (R, G, B), tai käytännössä 3D-ryhmä, joka on indeksoitu pystysuoran sijainnin, vaakasuoran sijainnin ja värikanavan (0, 1, 2) mukaan .
Käytämme kahta kirjastoa: NumPy ( http://www.numpy.org/ ) ja OpenCV ( https://opencv.org/ ). Ensimmäisen avulla voimme suorittaa laskutoimituksia taulukoista erittäin tehokkaasti (yllättävän lyhyellä koodilla), kun taas OpenCV käsittelee tässä tapauksessa kuvatiedostojen lukemista / kirjoittamista, mutta on paljon kykenevämpi tarjoamaan monia kehittyneitä grafiikkamenettelyjä - joista joitain aiomme käytä myöhemmin artikkelissa.
import os import numpy as np import cv2 folder = 'source_folder' # We get all the image files from the source folder files = list([os.path.join(folder, f) for f in os.listdir(folder)]) # We compute the average by adding up the images # Start from an explicitly set as floating point, in order to force the # conversion of the 8-bit values from the images, which would otherwise overflow average = cv2.imread(files[0]).astype(np.float) for file in files[1:]: image = cv2.imread(file) # NumPy adds two images element wise, so pixel by pixel / channel by channel average += image # Divide by count (again each pixel/channel is divided) average /= len(files) # Normalize the image, to spread the pixel intensities across 0..255 # This will brighten the image without losing information output = cv2.normalize(average, None, 0, 255, cv2.NORM_MINMAX) # Save the output cv2.imwrite('output.png', output)
Tulos (automaattikontrastilla) osoittaa, että kohina on kadonnut, mikä on erittäin suuri parannus alkuperäiseen kuvaan.
Huomaa kuitenkin edelleen joitain outoja esineitä, kuten vihertävä kehys ja ristikkomainen kuvio. Tällä kertaa se ei ole satunnainen, vaan kiinteä kuvamelu. Mitä tapahtui?
Jälleen voimme syyttää sitä anturista. Tässä tapauksessa havaitaan, että anturin eri osat reagoivat valoon eri tavoin, mikä johtaa näkyvään kuvioon. Jotkut näiden kuvioiden elementit ovat säännöllisiä ja liittyvät todennäköisesti anturisubstraattiin (metalli / pii) ja siihen, miten se heijastaa / absorboi saapuvia fotoneja. Muut elementit, kuten valkoinen pikseli, ovat yksinkertaisesti viallisia anturipikseleitä, jotka voivat olla liian herkkiä tai liian herkkiä valolle.
Onneksi on myös tapa päästä eroon tällaisesta melusta. Sitä kutsutaan tumman kehyksen vähennys .
Tätä varten tarvitsemme kuvan itse kuvakohinasta, ja tämä voidaan saada, jos valokuvaamme pimeyttä. Kyllä, se on oikein - peitä vain kameran reikä ja ota paljon kuvia (esimerkiksi 100), joissa on suurin valotusaika ja ISO-arvo, ja käsittele niitä yllä kuvatulla tavalla.
Kun keskiarvo mitataan useista mustista kehyksistä (jotka eivät itse asiassa ole mustia satunnaisen kohinan vuoksi), päädymme kiinteään kuvameluun. Voimme olettaa, että tämä kiinteä kohina pysyy vakiona, joten tätä vaihetta tarvitaan vain kerran: Tuloksena olevaa kuvaa voidaan käyttää uudelleen kaikissa tulevissa heikossa valossa otettavissa kuvissa.
Näin kuvion kohinan oikeassa yläkulmassa (kontrastisäätö) näyttää olevan iPhone 6: lle:
Jälleen huomaamme ruudukon kaltaisen tekstuurin ja jopa sen, joka näyttää olevan jumissa oleva valkoinen pikseli.
Kun meillä on tämän tumman kehyskohinan arvo (average_noise
-muuttujassa), voimme yksinkertaisesti vähentää sen tähänastisesta laukauksestamme ennen normalisointia:
average -= average_noise output = cv2.normalize(average, None, 0, 255, cv2.NORM_MINMAX) cv2.imwrite('output.png', output)
Tässä on viimeinen valokuvamme:
Toinen pienen (mobiilikameran) rajoitus on sen pieni dynaaminen alue, mikä tarkoittaa, että valon voimakkuuksien alue, jolla se voi siepata yksityiskohtia, on melko pieni.
Toisin sanoen kamera pystyy sieppaamaan vain kapean kaistan valon voimakkuuksista kohtauksesta; kyseisen kaistan alapuolella olevat voimakkuudet näyttävät puhtaalta mustalta, kun taas sen yläpuoliset voimakkuudet näyttävät puhtaalta valkoiselta, ja kaikki yksityiskohdat menetetään näiltä alueilta.
On kuitenkin temppu, jota kamera (tai valokuvaaja) voi käyttää - ja se on valotusajan (aika, jonka anturi altistuu valolle) säätäminen anturiin saapuvan valon kokonaismäärän hallitsemiseksi tehokkaasti vaihtamalla aluetta ylös tai alas tietyn kohtauksen sopivimman alueen kaappaamiseksi.
Mutta tämä on kompromissi. Monista yksityiskohdista ei pääse lopulliseen valokuvaan. Kahdessa alla olevassa kuvassa näkyy sama kohtaus, joka on kuvattu eri valotusajoilla: hyvin lyhyt valotus (1/1000 s), keskisuuri valotus (1/50 s) ja pitkä valotus (1/4 s).
Kuten näette, kumpikaan kolmesta kuvasta ei pysty sieppaamaan kaikkia käytettävissä olevia yksityiskohtia: Lampun hehkulanka näkyy vain ensimmäisessä otoksessa ja osa kukan yksityiskohdista näkyy joko keskellä tai viimeisessä otoksessa, mutta ei molemmat.
Hyvä uutinen on, että voimme tehdä asialle jotain, ja jälleen kerran siihen liittyy useiden kuvien rakentaminen pienellä Python-koodilla.
Lähestymistapa perustuu Paul Debevecin ym. Työhön, joka kuvaa menetelmää artikkelissaan tässä . Menetelmä toimii näin:
Ensinnäkin se vaatii useita otoksia samasta kohtauksesta (paikallaan), mutta eri valotusajoilla. Jälleen, kuten edellisessä tapauksessa, tarvitsemme jalustan tai tuen varmistaaksemme, että kamera ei liiku ollenkaan. Tarvitsemme myös manuaalisen kuvausohjelman (jos käytät puhelinta), jotta voimme hallita valotusaikaa ja estää kameran automaattiset säädöt. Vaadittujen kuvien määrä riippuu kuvassa olevien voimakkuuksien alueesta (kolmesta ylöspäin), ja valotusajat tulisi sijoittaa kyseisen alueen yli siten, että haluamamme yksityiskohdat näkyvät selvästi ainakin yhdessä kuvassa.
Seuraavaksi algoritmia käytetään rekonstruoimaan kameran vastekäyrä samojen pikselien värin perusteella eri valotusajoissa. Tämän avulla voimme periaatteessa luoda kartan pisteen todellisen kohtauksen kirkkauden, valotusajan ja arvon välillä, jonka vastaavalla pikselillä on otetussa kuvassa. Käytämme Debevecin menetelmän toteutusta OpenCV-kirjastosta.
# Read all the files with OpenCV files = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg'] images = list([cv2.imread(f) for f in files]) # Compute the exposure times in seconds exposures = np.float32([1. / t for t in [1000, 500, 100, 50, 10]]) # Compute the response curve calibration = cv2.createCalibrateDebevec() response = calibration.process(images, exposures)
Vastekäyrä näyttää tältä:
Pystyakselilla meillä on pisteen kohtauksen kirkkauden ja valotusajan kumulatiivinen vaikutus, kun taas vaaka-akselilla meillä on vastaavan pikselin arvo (0-255 kanavaa kohti).
Tämän käyrän avulla voimme suorittaa käänteisen toiminnan (joka on prosessin seuraava vaihe) - pikseliarvon ja valotusajan perusteella voimme laskea kunkin kohtauksen todellisen kirkkauden. Tätä kirkkausarvoa kutsutaan säteilyksi ja se mittaa valoenergian määrää, joka putoaa anturin pinta-alayksikköön. Toisin kuin kuvadata, se esitetään liukulukujen avulla, koska se heijastaa paljon laajempaa arvoja (siis korkea dynaaminen alue). Kun meillä on irradianssikuva (HDR-kuva), voimme yksinkertaisesti tallentaa sen:
# Compute the HDR image merge = cv2.createMergeDebevec() hdr = merge.process(images, exposures, response) # Save it to disk cv2.imwrite('hdr_image.hdr', hdr)
Niille meistä, jotka ovat onnekkaita saamaan HDR-näytön (joka on yhä yleisempää), voi olla mahdollista visualisoida tämä kuva suoraan kaikessa loistossaan. Valitettavasti HDR-standardit ovat vielä lapsenkengissään, joten prosessi siihen voi olla hieman erilainen eri näytöissä.
Meille muille hyvä uutinen on, että voimme silti hyödyntää näitä tietoja, vaikka normaali näyttö edellyttää, että kuvalla on tavuarvokanavia (0–255). Vaikka meidän on luovuttava joistakin säteilykartan rikkauksista, meillä on ainakin mahdollisuus hallita, miten se tehdään.
Tätä prosessia kutsutaan sävykartoitus ja siihen kuuluu kelluvan pisteen säteilykartan muuntaminen (suurella arvoalueella) vakiotavuarvokuvaksi. On tekniikoita tehdä niin, että monet ylimääräiset yksityiskohdat säilyvät. Kuvittele vain, että annat sinulle esimerkin siitä, miten tämä voi toimia, ennen kuin puristamme liukuluku-alueen tavuarvoiksi, parannamme (terävöitämme) HDR-kuvassa olevia reunoja. Näiden reunojen parantaminen auttaa säilyttämään ne (ja implisiittisesti niiden tarjoaman yksityiskohdan) myös matalan dynaamisen alueen kuvassa.
OpenCV tarjoaa joukon näistä sävykartoitusoperaattoreista, kuten Drago, Durand, Mantiuk tai Reinhardt. Tässä on esimerkki siitä, miten yhtä näistä operaattoreista (Durand) voidaan käyttää ja mitä se tuottaa.
durand = cv2.createTonemapDurand(gamma=2.5) ldr = durand.process(hdr) # Tonemap operators create floating point images with values in the 0..1 range # This is why we multiply the image with 255 before saving cv2.imwrite('durand_image.png', ldr * 255)
Pythonin avulla voit myös luoda omia operaattoreita, jos tarvitset enemmän prosessin hallintaa. Tämä on esimerkiksi tulos, joka on saatu mukautetulla operaattorilla, joka poistaa hyvin muutamissa pikseleissä esitetyt intensiteetit ennen kuin kutistuu arvoalue 8 bittiin (jota seuraa automaattisen kontrastin vaihe):
Ja tässä on yllä olevan operaattorin koodi:
def countTonemap(hdr, min_fraction=0.0005): counts, ranges = np.histogram(hdr, 256) min_count = min_fraction * hdr.size delta_range = ranges[1] - ranges[0] image = hdr.copy() for i in range(len(counts)): if counts[i] = ranges[i + 1]] -= delta_range ranges -= delta_range return cv2.normalize(image, None, 0, 1, cv2.NORM_MINMAX)
Olemme nähneet, kuinka pienellä Pythonilla ja parilla tukevalla kirjastolla voimme siirtää fyysisen kameran rajoja lopputuloksen parantamiseksi. Molemmat keskustelemamme esimerkit käyttävät useita huonolaatuisia otoksia luomaan jotain parempaa, mutta on olemassa monia muita lähestymistapoja erilaisiin ongelmiin ja rajoituksiin.
Vaikka monissa kamerapuhelimissa on tallennettuja tai sisäänrakennettuja sovelluksia, jotka vastaavat näihin esimerkkeihin, ei ole selvästikään lainkaan vaikeaa ohjelmoida niitä käsin ja nauttia korkeammasta hallinnasta ja ymmärryksestä, joka voidaan saada.
Jos olet kiinnostunut kuvan laskemisesta mobiililaitteella, tarkista OpenCV-opetusohjelma: Reaaliaikainen objektitunnistus MSER: n avulla iOS: ssä ApeeScapeer ja eliitti OpenCV-kehittäjä Altaibayar Tseveenbayar.
Anturi muuntaa tulevan valon sähköisiksi signaaleiksi, jotka sitten vahvistetaan ja digitoidaan. Sen pinta on jaettu pikseleihin, jotka jaetaan edelleen kanaviin. Siksi anturi tuottaa kolme numeroa, jotka vastaavat punaista, vihreää ja sinistä valovoimaa jokaiselle kohtauksen kohdalle.
Kuvankäsittely viittaa kaikkiin laskennan vaiheisiin, jotka suoritetaan sen jälkeen, kun anturi on kaapannut raakakuvan sen parantamiseksi tai muokkaamiseksi.
Kuvankäsittely tapahtuu ohjelmistossa soveltamalla numeerisia toimintoja kuvadataan. Yleisimmillä kuvankäsittelytekniikoilla on vankka matemaattinen tausta.
Python on ohjelmointikieli, joka soveltuu hyvin tieteelliseen laskentaan. NumPy on Python-kirjasto, joka yksinkertaistaa matriisien numeeristen operaatioiden tekemistä. OpenCV on erikoistunut kirjasto, joka keskittyy kuvankäsittelyyn ja tietokonenäköön.
Hämärässä ja rajoitetulla valotuksella anturiin putoaa hyvin pieni määrä valoenergiaa. Anturi yrittää kompensoida vahvistamalla signaalia, mutta päätyy myös vahvistamaan omaa sähköistä kohinaa.
Suuren dynaamisen alueen (HDR) valokuvaus viittaa fyysisen valon voimakkuuden sieppaamiseen näkymässä. Perinteinen digitaalinen valokuvaus käyttää vain vähän intensiteettitasoja (tyypillisesti 256).
Sävykartoitus on tekniikka, joka muuntaa HDR-kuvan tavanomaiseksi kuvaksi säilyttäen suurimman osan yksityiskohdista, jotta kuva voidaan näyttää muulla kuin HDR-näytöllä.