Kuvankäsittely OpenCV

Kuvankasittely Opencv



Aiomme tutkia kuvankäsittelymenetelmiä tässä artikkelissa. Tarkastellaan joitain perustavanlaatuisia mutta kriittisiä aiheita tietokonenäön ja koneoppimisen alalla. Nämä peruskuvankäsittelytekniikat voivat ratkaista monimutkaisia ​​ongelmia, kuten tietojoukkoja. Tämän seurauksena kuvankäsittelyssä on kuusi perusvaihetta, jotka on lueteltu alla:
  1. Kuvan käännös
  2. Kuvan kierto
  3. Kuvaaritmetiikka
  4. Kuvan kääntäminen
  5. Kuvan rajaus
  6. Kuvan koon muuttaminen

Nyt selitämme kaikki edellä mainitut kuvankäsittelyn aiheet yksityiskohtaisesti.

1. Kuvan käännös

Kuvan käännös on kuvankäsittelymenetelmä, joka auttaa meitä liikuttamaan kuvaa x- ja y-akseleita pitkin. Voimme siirtää kuvaa ylös, alas, oikealle, vasemmalle tai mitä tahansa yhdistelmää.







Voimme määritellä käännösmatriisin symbolilla M, ja voimme esittää sen matemaattisessa muodossa, kuten alla on esitetty:





Voimme ymmärtää käännöskuvan käsitteen tämän ohjelman avulla.





Python-koodi: Säilytämme seuraavan ohjelman nimen muodossa translate.py .

# tuonti vaadittua pakettia

tuonti nuhjuinen kuten esimerkiksi.

tuonti argparse

tuonti imutil

tuonti cv2

# toteutamme argumentin jäsentimen

ap_obj = argparse. ArgumentParser ( )

ap_obj. add_argument ( '-k' , '--kuva' , edellytetään = Totta ,

auta = 'kuvatiedoston sijainti' )

args = jonka ( ap_obj. parse_args ( ) )

# lataa kuva ja näytä se näytöllä

kuva = cv2. lukematta ( args [ 'kuva' ] )

cv2. imshow ( 'Alkuperäinen_kuva' , kuva )

# Kuvan käännös on NumPy-matriisi, joka on annettu alla:

# [[1, 0, shiftX], [0, 1, shiftY]]

# Käytämme yllä olevaa NumPy-matriisia kuvien siirtämiseen pitkin

# x-akselin ja y-akselin suunnat. Tätä varten meidän on yksinkertaisesti välitettävä pikseliarvot.

# Tässä ohjelmassa siirrämme kuvaa 30 pikseliä oikealle

# ja 70 pikseliä alaspäin.

käännösmatto = esimerkiksi. kellua32 ( [ [ 1 , 0 , 30 ] , [ 0 , 1 , 70 ] ] )

image_translation = cv2. warpAffine ( kuva , käännösmatto ,

( kuva. muoto [ 1 ] , kuva. muoto [ 0 ] ) )

cv2. imshow ( 'Kuvan käännös alas ja oikealle' , image_translation )

# nyt aiomme käyttää yllä olevaa NumPy-matriisia kuvien siirtämiseen pitkin

# x-akselin (vasen) ja y-akselin (ylös) suunnat.

# Tässä aiomme siirtää kuvia 50 pikseliä vasemmalle

# ja 90 pikseliä ylöspäin.

käännösmatto = esimerkiksi. kellua32 ( [ [ 1 , 0 , - viisikymmentä ] , [ 0 , 1 , - 90 ] ] )

image_translation = cv2. warpAffine ( kuva , käännösmatto ,

( kuva. muoto [ 1 ] , kuva. muoto [ 0 ] ) )

cv2. imshow ( 'Kuvan käännös ylös ja vasemmalle' , image_translation )

cv2. odotaKey ( 0 )

Rivit 1-5: Tuomme kaikki tähän ohjelmaan tarvittavat paketit, kuten OpenCV, argparser ja NumPy. Huomaa, että on olemassa toinen kirjasto, joka on imutils. Tämä ei ole OpenCV-paketti. Tämä on vain kirjasto, joka näyttää helposti saman kuvankäsittelyn.



Kirjasto-imutileja ei sisällytetä automaattisesti, kun asennamme OpenCV:n. Joten imutilien asentamiseksi meidän on käytettävä seuraavaa menetelmää:

pip install imutils

Rivit 8-15: Loimme agrparserimme ja latasimme kuvamme.

Rivit 24-25: Tässä ohjelmaosassa käännös tapahtuu. Käännösmatriisi kertoo kuinka monta pikseliä kuvaa siirretään ylös tai alas tai vasemmalle tai oikealle. Koska OpenCV edellyttää, että matriisin arvo on liukulukutaulukossa, käännösmatriisi ottaa arvot liukulukutaulukoissa.

Käännösmatriisin ensimmäinen rivi näyttää tältä:

Tämä matriisin rivi on x-akselia varten. Arvo t x päättää, siirretäänkö kuva vasemmalle vai oikealle. Jos ohitamme negatiivisen arvon, se tarkoittaa, että kuva siirtyy vasemmalle puolelle, ja jos arvo on positiivinen, se tarkoittaa, että kuva siirtyy oikealle puolelle.

Määrittelemme nyt matriisin toisen rivin seuraavasti:

Tämä matriisin rivi on tarkoitettu y-akselille. Arvo t Y päättää, siirretäänkö kuva ylös vai alaspäin. Jos ohitamme negatiivisen arvon, se tarkoittaa, että kuva siirtyy ylösalaisin, ja jos arvo on positiivinen, se tarkoittaa, että kuva siirtyy huonompaan suuntaan.

Edellisen ohjelman rivillä 24 määritämme t x = 30 ja t Y = 70. Siirrämme siis kuvaa 30 pikseliä oikealle puolelle ja 70 pikseliä alaspäin.

Mutta pääkuvan käännösprosessi tapahtuu rivillä 25, jossa määritämme käännösmatriisin cv2.warpAffine . Tässä funktiossa välitämme kolme parametria: ensimmäinen parametri on kuva, toinen parametri on translaatiomatriisi ja kolmas parametri on kuvan ulottuvuus.

Rivi 27: Rivi 27 näyttää tuloksen lähdössä.

Nyt otamme käyttöön toisen käännösmatriisin vasemmalle ja yläpuolelle. Tätä varten meidän on määritettävä arvot negatiivisiksi.

Rivit 33-34: Edellisen ohjelman rivillä 33 määritämme t x = -50 ja t Y = -90. Joten siirrämme kuvaa 50 pikseliä vasenta puolta kohti ja 90 pikseliä ylöspäin. Mutta pääkuvan käännösprosessi tapahtuu rivillä 34, jossa määritämme käännösmatriisin cv2.warpAffine .

Rivi 36 : Rivi 36 näyttää tuloksen tulosteen mukaisesti.

Edellisen koodin suorittamiseksi meidän on annettava kuvan polku alla olevan mukaisesti.

Lähtö: python translate.py –image squirrel.jpg

Nyt toteutamme saman kuvan käännösohjelman käyttämällä imutil kirjasto. Tätä kirjastoa on erittäin helppo käyttää kuvankäsittelyyn. Tässä kirjastossa meidän ei tarvitse ajatella cv2.warpAffine koska tämä kirjasto huolehtii tästä. Toteutetaan siis tämä kuvan käännösohjelma imutils-kirjaston avulla.

Python-koodi: Säilytämme seuraavan ohjelman nimen muodossa translate_imutils.py .

# tuoda tarvittavat paketit

tuonti nuhjuinen kuten esimerkiksi.

tuonti argparse

tuonti imutil

tuonti cv2

# Tämä toiminto toteuttaa kuvan kääntämisen ja

# palauttaa käännetyn kuvan kutsuvaan funktioon.

def Kääntää ( kuva , x , Y ) :

käännösmatriisi = esimerkiksi. kellua32 ( [ [ 1 , 0 , x ] , [ 0 , 1 , Y ] ] )

image_translation = cv2. warpAffine ( kuva , käännösmatriisi ,

( kuva. muoto [ 1 ] , kuva. muoto [ 0 ] ) )

palata image_translation

# muodostaa argumentin jäsentimen ja jäsentää argumentit

ap = argparse. ArgumentParser ( )

ap. add_argument ( '-i' , '--kuva' , edellytetään = Totta , auta = 'Polku kuvaan' )

args = jonka ( ap. parse_args ( ) )

# lataa kuva ja näytä se näytöllä

kuva = cv2. lukematta ( args [ 'kuva' ] )

cv2. imshow ( 'Alkuperäinen_kuva' , kuva )

image_translation = imutil. Kääntää ( kuva , 10 , 70 )

cv2. imshow ( 'Kuvan käännös oikealle ja alapuolelle' ,

image_translation )

cv2. odotaKey ( 0 )

Rivit 9-13: Tässä ohjelman osassa käännös tapahtuu. Käännösmatriisi kertoo meille kuinka monta pikseliä kuvaa siirretään ylös tai alas tai vasemmalle tai oikealle.

Nämä rivit on jo selitetty, mutta nyt aiomme rakentaa funktion nimeltä translate () ja lähettää siihen kolme erillistä parametria. Itse kuva toimii ensimmäisenä parametrina. Käännösmatriisin x- ja y-arvot vastaavat toista ja kolmatta parametria.

Huomautus : Tätä käännöstoimintoa ei tarvitse määrittää ohjelman sisällä, koska se sisältyy jo imutils-kirjastopakettiin. Olen käyttänyt sitä ohjelmassa selkeän selityksen vuoksi. Voimme kutsua tätä funktiota suoraan imutileilla, kuten rivillä 24 näkyy.

Rivi 24: Edellinen ohjelma näyttää, että rivillä 24 määritellään tx = 10 ja ty = 70. Joten siirrämme kuvaa 10 pikseliä oikealle puolelle ja 70 pikseliä alaspäin.

Tässä ohjelmassa emme välitä mistään cv2.warpAffine-funktioista, koska ne ovat jo imutils-kirjastopaketin sisällä.

Edellisen koodin suorittamiseksi meidän on annettava kuvan polku, kuten alla:

Lähtö:

python imutils. py --kuva-orava. jpg

2. Kuvan kierto

Kävimme läpi kuinka kääntää (eli siirtää) kuva ylös, alas, vasemmalle ja oikealle edellisellä oppitunnilla (tai missä tahansa yhdistelmässä). Seuraavaksi keskustelemme pyörityksestä, koska se liittyy kuvankäsittelyyn.

Kuvaa käännetään kulmalla, theta, prosessissa, joka tunnetaan nimellä rotaatio. Kulmaa, jolla pyöritämme kuvaa, edustaa theta. Lisäksi annan myöhemmin käyttöön mukavuustoiminnon, joka tekee kuvien kääntämisestä yksinkertaisempaa.

Samoin kuin käännös, ja ehkä ei yllättävää, kulman verran kierto, theta määritetään rakentamalla matriisi M seuraavassa muodossa:

Tämä matriisi voi kiertää vektoria theta-astetta (vastapäivään) annetun (x, y) - suorakulmaisen tason origon ympäri. Tavallisesti tässä skenaariossa origo olisi kuvan keskipiste, mutta todellisuudessa voisimme nimetä minkä tahansa satunnaisen (x, y) pisteen kiertokeskipisteeksi.

Kierretty kuva R luodaan sitten alkuperäisestä kuvasta I käyttämällä suoraviivaista matriisikertoa: R = IM

Toisaalta OpenCV tarjoaa lisäksi mahdollisuuden (1) skaalata (eli muuttaa kuvan kokoa) ja (2) tarjota mielivaltaisen kiertokeskuksen kiertämisen suorittamiseksi.

Muokattu rotaatiomatriisi M näkyy alla:

Aloitetaan avaamalla ja luomalla uusi tiedosto nimeltä rotate.py :

# tuodaan tarvittavat paketit

tuonti nuhjuinen kuten esimerkiksi.

tuonti argparse

tuonti imutil

tuonti cv2

# luodaan argumentparser-objekti ja jäsennysargumentti

apobj = argparse. ArgumentParser ( )

apobj. add_argument ( '-k' , '--kuva' , edellytetään = Totta , auta = 'kuvapolku' )

argumentteja = jonka ( apobj. parse_args ( ) )

kuva = cv2. lukematta ( argumentteja [ 'kuva' ] )

cv2. imshow ( 'Alkuperäinen_kuva' , kuva )

# Laske kuvan keskipiste kuvan mittojen avulla.

( korkeus , leveys ) = kuva. muoto [ : 2 ]

( keskusX , keskusY ) = ( leveys / 2 , korkeus / 2 )

# Nyt käyttämällä cv2:ta käännämme kuvaa 55 astetta kohti

# määrittää kiertomatriisin käyttämällä getRotationMatrix2D()

rotationMatrix = cv2. getRotationMatrix2D ( ( keskusX , keskusY ) , 55 , 1.0 )

kierretty kuva = cv2. warpAffine ( kuva , rotationMatrix , ( leveys , korkeus ) )

cv2. imshow ( 'Kierretty kuvaa 55 astetta' , kierretty kuva )

cv2. odotaKey ( 0 )

# Kuvaa käännetään nyt -85 astetta.

rotationMatrix = cv2. getRotationMatrix2D ( ( keskusX , keskusY ) , - 85 , 1.0 )

kierretty kuva = cv2. warpAffine ( kuva , rotationMatrix , ( leveys , korkeus ) )

cv2. imshow ( 'Kierretty kuvaa -85 astetta' , kierretty kuva )

cv2. odotaKey ( 0 )

Rivit 1-5: Tuomme kaikki tähän ohjelmaan tarvittavat paketit, kuten OpenCV, argparser ja NumPy. Huomaa, että on olemassa toinen kirjasto, joka on imutils. Tämä ei ole OpenCV-paketti. Tämä on vain kirjasto, jota käytetään näyttämään saman kuvankäsittelyn helposti.

Kirjasto-imutileja ei sisällytetä automaattisesti, kun asennamme OpenCV:n. OpenCV asentaa imutilit. Meidän on käytettävä seuraavaa menetelmää:

pip install imutils

Rivit 8-14: Loimme agrparserimme ja latasimme kuvamme. Tässä argparserissa käytämme vain yhtä kuva-argumenttia, joka kertoo meille kuvan polun, jota käytämme tässä ohjelmassa osoittamaan kiertoa.

Kun kuvaa pyöritetään, meidän on määriteltävä kiertopiste. Suurimman osan ajasta haluat kiertää kuvaa sen keskustan ympäri, mutta OpenCV antaa sinun valita minkä tahansa satunnaisen pisteen sijaan. Kierretään vain kuvaa sen keskustan ympäri.

Rivit 17-18 ota kuvan leveys ja korkeus ja jaa sitten jokainen mitta kahdella määrittääksesi kuvan keskipisteen.

Rakennamme matriisin kuvan kiertämiseksi samalla tavalla kuin määritimme matriisin kuvan kääntämiseksi. Soitamme vain cv2.getRotationMatrix2D toiminto rivillä 22 sen sijaan, että luot matriisin manuaalisesti NumPyllä (mikä voi olla hieman hankalaa).

The cv2.getRotationMatrix2D toiminto vaatii kolme parametria. Ensimmäinen syöte on haluttu kiertokulma (tässä tapauksessa kuvan keskikohta). Thetaa käytetään sitten määrittämään kuinka monta (vastapäivään) astetta kuvaa käännetään. Tässä käännetään kuvaa 45 astetta. Viimeinen vaihtoehto liittyy kuvan kokoon.

Huolimatta siitä, että emme ole vielä keskustelleet kuvan skaalauksesta, voit antaa tähän liukulukuluvun 1,0:lla, mikä tarkoittaa, että kuvaa tulee käyttää sen alkuperäisissä mittasuhteissa. Jos kuitenkin kirjoitit arvon 2,0, kuva kaksinkertaistuu. Luku 0,5 pienentää kuvan kokoa tällä tavalla.

Rivit 22-23: Saatuaan rotaatiomatriisimme M cv2.getRotationMatrix2D -toimintoa, kierrämme kuvaamme käyttämällä cv2.warpAffine tekniikka rivillä 23. Toiminnon ensimmäinen syöte on kuva, jota haluamme kiertää. Tuloskuvamme leveys ja korkeus määritetään sitten yhdessä kiertomatriisimme M kanssa. Rivillä 23 kuvaa käännetään sitten 55 astetta.

Voit huomata, että kuvaamme on käännetty.

Rivit 28-30 muodostavat toisen kierroksen. Koodin rivit 22–23 ovat identtisiä, paitsi että tällä kertaa käännetään -85 astetta 55 asteen sijaan.

Olemme yksinkertaisesti kiertäneet kuvaa sen keskustan ympäri tähän pisteeseen asti. Mitä jos haluaisimme kiertää kuvaa satunnaisen pisteen ympäri?

Aloitetaan avaamalla ja luomalla uusi tiedosto nimeltä rotate.py:

# tuodaan tarvittavat paketit

tuonti nuhjuinen kuten esimerkiksi.

tuonti argparse

tuonti imutil

tuonti cv2

# luodaan argumentparser-objekti ja jäsennysargumentti

ap_obj = argparse. ArgumentParser ( )

ap_obj. add_argument ( '-k' , '--kuva' , edellytetään = Totta , auta = 'kuvapolku' )

Perustelu = jonka ( ap_obj. parse_args ( ) )

# lataa kuva ja näytä se näytöllä

kuva = cv2. lukematta ( Perustelu [ 'kuva' ] )

cv2. imshow ( 'Alkuperäinen_kuva' , kuva )

# Laske kuvan keskipiste kuvan mittojen avulla.

( korkeus , leveys ) = kuva. muoto [ : 2 ]

( keskusX , keskusY ) = ( leveys / 2 , korkeus / 2 )

# Nyt käyttämällä cv2:ta käännämme kuvaa 55 astetta kohti

# määrittää kiertomatriisin käyttämällä getRotationMatrix2D()

rotationMatrix = cv2. getRotationMatrix2D ( ( keskusX , keskusY ) , 55 , 1.0 )

kierretty kuva = cv2. warpAffine ( kuva , rotationMatrix , ( leveys , korkeus ) )

cv2. imshow ( 'Kierretty kuvaa 55 astetta' , kierretty kuva )

cv2. odotaKey ( 0 )

# Kuvaa käännetään nyt -85 astetta.

rotationMatrix = cv2. getRotationMatrix2D ( ( keskusX , keskusY ) , - 85 , 1.0 )

kierretty kuva = cv2. warpAffine ( kuva , rotationMatrix , ( leveys , korkeus ) )

cv2. imshow ( 'Kierretty kuvaa -85 astetta' , kierretty kuva )

cv2. odotaKey ( 0 )

# kuvan kierto jostain mielivaltaisesta pisteestä, ei keskustasta

rotationMatrix = cv2. getRotationMatrix2D ( ( centerX - 40 , centerY - 40 ) , 55 , 1.0 )

kierretty kuva = cv2. warpAffine ( kuva , rotationMatrix , ( leveys , korkeus ) )

cv2. imshow ( 'Kuvan kierto mielivaltaisista pisteistä' , kierretty kuva )

cv2. odotaKey ( 0 )

Rivit 34-35: Nyt tämän koodin pitäisi näyttää melko yleiseltä objektin pyörittämiseen. Jos haluat kiertää kuvaa pisteen ympäri, joka on 40 pikseliä vasemmalla ja 40 pikseliä sen keskustan yläpuolella, ohjeistamme cv2.getRotationMatrix2D toiminto kiinnittää huomiota sen ensimmäiseen parametriin.

Kuva, joka syntyy, kun käytämme tätä kiertoa, näkyy alla:

Näemme selvästi, että kierron keskipiste on nyt (x, y)-koordinaatti, joka on 40 pikseliä vasemmalla ja 40 pikseliä kuvan lasketun keskikohdan yläpuolella.

3. Kuvaaritmetiikka

Itse asiassa kuvan aritmetiikka on vain matriisilisäystä, johon liittyy muutamia lisärajoituksia tietotyypeille, joita käsittelemme myöhemmin.

Käydään hetki läpi joitakin lineaarisen algebran perusperiaatteita.

Harkitse kahden seuraavan matriisin yhdistämistä:

Millaisen tuloksen matriisilisäys antaisi? Yksinkertainen vastaus on matriisimerkintöjen summa elementti kerrallaan:

Tarpeeksi yksinkertainen, eikö?

Ymmärrämme kaikki yhteen- ja vähennyslaskutoiminnot tällä hetkellä. Meidän on kuitenkin otettava huomioon väriavaruuden ja tietotyyppien asettamat rajoitukset, kun työskentelemme kuvien kanssa.

Esimerkiksi RGB-kuvien pikselit ovat välillä [0, 255]. Mitä tapahtuu, jos yritämme lisätä 10 pikseliin, jonka intensiteetti on 250, kun katsomme sitä?

Saisimme arvoon 260, jos sovellettaisiin standardiaritmeettisia periaatteita. 260 ei ole kelvollinen arvo, koska RGB-kuvat esitetään 8-bittisinä etumerkittämättöminä kokonaislukuina.

Mitä siis pitäisi tapahtua? Pitäisikö meidän suorittaa tarkistus varmistaaksemme, että yksikään pikseli ei ole alueen [0, 255] ulkopuolella, leikkaamalla jokaisen pikselin arvoksi 0-255?

Vai 'kääritäänkö' ja suoritetaan moduulioperaatio? Moduulisääntöjen mukaisesti 10:n lisääminen 255:een johtaisi vain arvoon 9.

Miten arvon [0, 255] ulkopuolella olevien kuvien lisäyksiä ja vähennyksiä tulee käsitellä?

Totuus on, ettei ole olemassa oikeaa tai väärää tekniikkaa; kaikki riippuu siitä, kuinka työskentelet pikseleidesi kanssa ja mitä toivot saavuttavasi.

Muista kuitenkin, että OpenCV:n ja NumPyn lisäämisen välillä on eroja. Modulusaritmeettinen ja 'kääritty' tekee NumPy. Sitä vastoin OpenCV suorittaa leikkaamisen ja varmistaa, että pikseliarvot eivät koskaan jätä aluetta [0, 255].

Aloitetaan luomalla uusi tiedosto nimeltä aritmetiikka.py ja avaa se:

# python arithmetic.py --image squirrel.jpg

# tuodaan tarvittavat paketit

tuonti nuhjuinen kuten esimerkiksi.

tuonti argparse

tuonti imutil

tuonti cv2

# luodaan argumentparser-objekti ja jäsennysargumentti

apObj = argparse. ArgumentParser ( )

apObj. add_argument ( '-k' , '--kuva' , edellytetään = Totta , auta = 'kuvapolku' )

argumentteja = jonka ( apObj. parse_args ( ) )

kuva = cv2. lukematta ( argumentteja [ 'kuva' ] )

cv2. imshow ( 'Alkuperäinen_kuva' , kuva )

'''

Pikseliemme arvot ovat alueella [0, 255]

koska kuvat ovat NumPy-taulukoita, jotka tallennetaan etumerkittöminä 8-bittisinä kokonaislukuina.

Kun käytetään funktioita, kuten cv2.add ja cv2.subtract, arvot leikataan

tähän alueeseen, vaikka ne lisätään tai vähennetään ulkopuolelta

[0, 255]. Tässä on esimerkki:

'''


Tulosta ( 'enintään 255: {}' . muoto ( str ( cv2. lisätä ( esimerkiksi. uint8 ( [ 201 ] ) ,

esimerkiksi. uint8 ( [ 100 ] ) ) ) ) )

Tulosta ( 'vähintään 0: {}' . muoto ( str ( cv2. vähentää ( esimerkiksi. uint8 ( [ 60 ] ) ,

esimerkiksi. uint8 ( [ 100 ] ) ) ) ) )

'''

Kun teet aritmeettisia operaatioita näillä taulukoilla NumPy:n avulla,

arvo kiertää sen sijaan, että se leikattaisiin arvoon

[0, 255]alue. Kuvia käytettäessä on tärkeää säilyttää tämä

mielessä.

'''


Tulosta ( 'kietoa: {}' . muoto ( str ( esimerkiksi. uint8 ( [ 201 ] ) + esim. uint8 ( [ 100 ] ) ) ) )

Tulosta ( 'kietoa: {}' . muoto ( str ( esimerkiksi. uint8 ( [ 60 ] ) - esimerkiksi. uint8 ( [ 100 ] ) ) ) )

'''

Kerrotaan jokaisen kuvamme pikselin kirkkaus 101:lla.

Tätä varten luomme NumPy-taulukon, joka on samankokoinen kuin matriisimme,

täytetty ykkösillä ja kerro se 101:llä saadaksesi täytetyn matriisin

101:n kanssa. Lopuksi yhdistämme nämä kaksi kuvaa.

Huomaat, että kuva on nyt 'kirkkaampi'.

'''


Matriisi = esimerkiksi. yhdet ( kuva. muoto , dtype = 'uint8' ) * 101

kuva_lisätty = cv2. lisätä ( kuva , Matriisi )

cv2. imshow ( 'Lisätty kuvatulos' , kuva_lisätty )

#Samalla tavalla voimme tummentaa kuvaamme ottamalla

# 60 päässä kaikista pikseleistä.

Matriisi = esimerkiksi. yhdet ( kuva. muoto , dtype = 'uint8' ) * 60

image_subtracted = cv2. vähentää ( kuva , Matriisi )

cv2. imshow ( 'Vähennetty kuvatulos' , image_subtracted )

cv2. odotaKey ( 0 )

Rivit 1-16 käytetään suorittamaan normaali prosessimme, joka sisältää pakettiemme tuomisen, argumenttijäsentimme konfiguroinnin ja kuvan lataamisen.

Muistatko kuinka aiemmin keskustelin erosta OpenCV:n ja NumPy-lisäyksen välillä? Nyt kun olemme käsitelleet sen perusteellisesti, tarkastellaan tiettyä tapausta varmistaaksemme, että ymmärrämme sen.

Kaksi 8-bittinen etumerkitön kokonaisluku NumPy-taulukko on määritetty rivi 26 . Arvo 201 on ainoa elementti ensimmäisessä taulukossa. Vaikka toisessa taulukossa on vain yksi jäsen, sen arvo on 100. Arvot lisätään sitten OpenCV:n cv2.add-funktiolla.

Millaisen tuloksen odotat?

Perinteisten aritmeettisten periaatteiden mukaan vastauksen tulisi olla 301. Muista kuitenkin, että kyseessä ovat 8-bittiset etumerkittömät kokonaisluvut, jotka voivat olla vain alueella [0, 255]. Koska käytämme cv2.add-menetelmää, OpenCV käsittelee leikkaamisen ja varmistaa, että lisäys palauttaa vain maksimituloksen 255.

Alla olevan luettelon ensimmäinen rivi näyttää tämän koodin suorittamisen tuloksen:

aritmeettinen. py

enintään 255 : [ [ 255 ] ]

Summa tuotti todellakin luvun 255.

Sitä seuraten, rivi 26 käyttää cv2.subtract suorittaakseen vähennyslaskua. Jälleen kerran määrittelemme kaksi 8-bittistä etumerkitöntä NumPy-kokonaislukua, joissa kummassakin on yksi elementti. Ensimmäisen taulukon arvo on 60, kun taas toisen taulukon arvo on 100.

Aritmetiikkamme sanelee, että vähennyslasku antaa tulokseksi arvon -40, mutta OpenCV hoitaa leikkaamisen puolestamme vielä kerran. Havaitsemme, että arvo on leikattu nollaan. Alla oleva tuloksemme osoittaa tämän:

aritmeettinen. py

vähintään 0 : [ [ 0 ] ]

Käyttämällä cv2:ta vähennä 100 60:n vähennyksestä, jolloin saadaan arvo 0.

Mutta mitä tapahtuu, jos käytämme NumPyä OpenCV:n sijaan laskelmien suorittamiseen?

Rivit 38 ja 39 käsitellä tätä asiaa.

Ensin määritellään kaksi 8-bittistä etumerkitöntä kokonaisluku NumPy-taulukkoa, joissa kummassakin on yksi elementti. Ensimmäisen taulukon arvo on 201, kun taas toisen taulukon arvo on 100. Lisäyksemme leikattaisiin ja palautettaisiin arvo 255, jos käyttäisimme cv2.add-funktiota.

NumPy puolestaan ​​'kiertyy' ja tekee moduloaritmetiikkaa leikkaamisen sijaan. NumPy kiertää nollaan, kun arvo 255 on saavutettu, ja jatkaa sitten laskemista, kunnes 100 askelta on saavutettu. Tämän vahvistaa ensimmäinen tulosrivi, joka näkyy alla:

aritmeettinen. py
kietoa: [ Neljä viisi ]

Sitten määritetään vielä kaksi NumPy-taulukkoa, joista toisen arvo on 50 ja toisen arvolla 100. Tämä vähennyslasku leikattaisiin cv2.subtract-menetelmällä, jolloin tulokseksi saadaan 0. Mutta olemme tietoisia siitä, että leikkaamisen sijaan NumPy suorittaa suorituksen. moduloaritmetiikka. Sen sijaan modulo-proseduurit kiertyvät ja alkavat laskea taaksepäin 255:stä, kun 0 on saavutettu vähennyksen aikana. Näemme tämän seuraavasta lähdöstä:

aritmeettinen. py

kietoa: [ 207 ]

Jälleen kerran, päätetulomme osoittaa eron leikkaamisen ja käärimisen välillä:

On erittäin tärkeää pitää mielessäsi haluamasi tulos, kun suoritat kokonaislukuaritmetiikkaa. Haluatko, että alueen [0, 255] ulkopuolella olevat arvot leikataan? Käytä sen jälkeen OpenCV:n sisäänrakennettua kuvan aritmeettista tekniikkaa.

Haluatko, että arvot kiertävät, jos ne ovat alueen [0, 255] ja moduuliaritmeettisten operaatioiden ulkopuolella? NumPy-taulukot yksinkertaisesti lisätään ja vähennetään tavalliseen tapaan.

Rivi 48 määrittää yksiulotteisen NumPy-taulukon, jolla on samat mitat kuin kuvassamme. Taas kerran varmistamme, että tietotyyppimme on 8-bittisiä etumerkittömiä kokonaislukuja. Kerromme vain yksinumeroisten arvojen matriisimme 101:llä täyttääksemme sen arvoilla 101 1:n sijasta. Lopuksi käytämme cv2.add-funktiota lisätäksemme 100 s matriisin alkuperäiseen kuvaan. Tämä lisää kunkin pikselin intensiteettiä 101:llä ja varmistaa samalla, että kaikki arvot, jotka yrittävät ylittää 255, leikataan alueelle [0, 255].

Tarkkaile, kuinka kuva on huomattavasti kirkkaampi ja näyttää 'pestyneemmältä' kuin alkuperäinen. Tämä johtuu siitä, että ohjaamme pikseleitä kohti kirkkaampia värejä nostamalla niiden pikseliintensiteettiä 101:llä.

Vähentääksemme 60 kuvan jokaisesta pikselin intensiteetistä, luomme ensin toisen NumPy-taulukon riville 54, joka on täytetty 60-luvulla.

Tämän vähennyksen tulokset on kuvattu seuraavassa kuvassa:

Ympärillämme olevat esineet näyttävät huomattavasti tummemmilta kuin aiemmin. Tämä johtuu siitä, että vähentämällä 60 jokaisesta pikselistä siirrämme RGB-väriavaruuden pikseleitä tummemmille alueille.

4. Kuvan kääntäminen

Samoin kuin kierto, kuvan kääntäminen sen x- tai y-akselin poikki on toinen OpenCV:n tarjoama vaihtoehto. Vaikka kääntötoimintoja ei käytetäkään yhtä usein, niiden tunteminen on uskomattoman hyödyllistä useista syistä, joita et välttämättä heti huomaa.

Kehitämme koneoppimisluokittajaa pienelle startup-yritykselle, joka pyrkii tunnistamaan kasvot kuvista. Jotta järjestelmämme voisi 'oppia', mitä kasvot ovat, tarvitsemme jonkinlaisen tietojoukon, jossa on näytekasvot. Valitettavasti yritys on antanut meille vain pienen 40 kasvojen tietojoukon, emmekä pysty keräämään lisätietoja.

Mitä sitten tehdään?

Koska kasvot pysyvät kasvoina riippumatta siitä, onko ne peilattu vai ei, voimme kääntää jokaisen kasvojen kuvan vaakasuunnassa ja käyttää peilattuja versioita ylimääräisenä harjoitustietona.

Tämä esimerkki saattaa tuntua typerältä ja keinotekoiselta, mutta se ei ole sitä. Flipping on tietoinen strategia, jota vahvat syväoppimisalgoritmit käyttävät tuottamaan enemmän dataa harjoitusvaiheen aikana.

Edellisestä on selvää, että tässä moduulissa opitut kuvankäsittelymenetelmät toimivat perustana isommille tietokonenäköjärjestelmille.

Tavoitteet:

Käyttämällä cv2.flip -toiminnolla opit kääntämään kuvan sekä vaaka- että pystysuunnassa tässä istunnossa.

Kääntäminen on seuraava kuvankäsittely, jota tutkimme. Kuvan x- ja y-akselit voidaan kääntää tai jopa molemmat. Ennen kuin sukeltaamme koodaukseen, on parasta tarkastella kuvan kääntämisen tuloksia. Katso vaakasuunnassa käännetty kuva seuraavassa kuvassa:


Huomaa, kuinka alkuperäinen kuvamme on vasemmalla ja kuinka kuva on peilattu vaakasuunnassa oikealla.

Aloitetaan luomalla uusi tiedosto nimeltä flipping.py .

Olet nähnyt esimerkin kuvan käännöstä, joten tutkitaan koodia:

# python flipping.py --image quirrel.jpg

# tuodaan tarvittavat paketit

tuonti argparse

tuonti cv2

# luodaan argumentin kohde ja jäsennetään argumentti

apObj = argparse. ArgumentParser ( )

apObj. add_argument ( '-i' , '--kuva' , edellytetään = Totta , auta = 'kuvapolku' )

Perustelu = jonka ( apObj. parse_args ( ) )

kuva = cv2. lukematta ( Perustelu [ 'kuva' ] )

cv2. imshow ( 'Alkuperäinen' , kuva )

# käännä kuva vaakasuunnassa

kuva käännetty = cv2. voltti ( kuva , 1 )

cv2. imshow ( 'Käännetty kuva vaakasuunnassa' , kuva käännetty )

# käännä kuva pystysuunnassa

kuva käännetty = cv2. voltti ( kuva , 0 )

cv2. imshow ( 'Käännetty kuva pystysuunnassa' , kuva käännetty )

# kuva käännetään molempia akseleita pitkin

kuva käännetty = cv2. voltti ( kuva , - 1 )

cv2. imshow ( 'Käännetty vaaka- ja pystysuunnassa' , kuva käännetty )

cv2. odotaKey ( 0 )

Vaiheet, jotka otamme tuodaksemme pakettimme, jäsentääksemme syötteitämme ja ladataksemme kuvan levyltä, käsitellään l ines 1-12 .

Kutsumalla cv2.flip-toiminto päälle Rivi 15 , on helppo kääntää kuva vaakasuunnassa. Kuva, jota pyrimme kääntämään, ja erityinen koodi tai lippu, joka määrittää, kuinka kuva käännetään, ovat kaksi argumenttia, joita tarvitaan cv2.flip-menetelmässä.

Kääntökoodin arvo 1 tarkoittaa, että käännämme kuvaa y-akselin ympäri ja käännämme sen vaakasuoraan ( Rivi 15 ). Jos annamme kääntökoodiksi 0, haluamme kiertää kuvaa x-akselin ympäri ( Rivi 19 ). Negatiivinen kääntökoodi ( Rivi 23 ) kiertää kuvaa molemmilla akseleilla.

Yksi helpoimmista esimerkeistä tässä aiheessa on kuvan kääntäminen, mikä on perusasia.

Seuraavaksi keskustelemme kuvien rajaamisesta ja käytämme NumPy-taulukon viipaleita tiettyjen kuvaosien poimimiseen.

5. Kuvan rajaus

Rajaus, kuten nimestä voi päätellä, on prosessi, jossa valitaan ja poistetaan kiinnostava alue (tai yksinkertaisesti ROI), joka on meitä kiinnostava kuvan alue.

Kasvot on rajattava kuvasta kasvojentunnistussovellusta varten. Lisäksi, jos loimme Python-skriptin koirien löytämiseksi kuvista, saatamme haluta rajata koiran pois kuvasta, kun löydämme sen.

Tavoitteet: Päätavoitteemme on tutustua NumPy-taulukon viipalointiin ja käyttää sitä helposti alueiden rajaamiseen kuvasta.

Rajaus : Kun rajaamme kuvaa, tavoitteenamme on poistaa ulkoiset elementit, jotka eivät kiinnosta meitä. ROI:n valintaprosessia kutsutaan usein kiinnostavan alueen valitsemiseksi.

Luo uusi tiedosto nimeltä crop.py , avaa se ja lisää seuraava koodi:

# python crop.py

# tuodaan tarvittavat paketit

tuonti cv2

# kuvan lataus ja näyttö näytöllä

kuva = cv2. lukematta ( 'orava.jpg' )

Tulosta ( kuva. muoto )

cv2. imshow ( 'Alkuperäinen' , kuva )

# NumPy-taulukon viipaleita käytetään kuvan nopeaan leikkaamiseen

# aiomme rajata oravan kasvot kuvasta

oravanaama = kuva [ 35 : 90 , 35 : 100 ]

cv2. imshow ( 'oravan kasvot' , oravanaama )

cv2. odotaKey ( 0 )

# Ja nyt, tässä aiomme rajata koko kehon

#oravan

oravanvartalo = kuva [ 35 : 148 , 23 : 143 ]

cv2. imshow ( 'Oravan ruumis' , oravanvartalo )

cv2. odotaKey ( 0 )

Näytämme rajauksen Pythonissa ja OpenCV:ssä käyttämällä kuvaa, jonka lataamme levyltä Rivit 5 ja 6 .

Alkuperäinen kuva, jonka aiomme rajata

Käyttämällä vain perusrajaustekniikoita pyrimme erottamaan oravan kasvot ja oravan vartalon ympäröivästä alueesta.

Käytämme aiempaa tietoamme kuvasta ja toimitamme manuaalisesti NumPy-taulukon siivut siitä, missä keho ja kasvot ovat. Normaaliolosuhteissa käyttäisimme yleensä koneoppimis- ja tietokonenäköalgoritmeja kasvojen ja vartalon tunnistamiseen kuvassa. Mutta pidetään asiat toistaiseksi suoraviivaisina ja vältetään havaitsemismallien käyttöä.

Voimme tunnistaa kuvan kasvot vain yhdestä koodirivistä. Rivi 13 , Jos haluat poimia kuvasta suorakaiteen muotoisen osan, alkaen kohdasta (35, 35), tarjoamme NumPy-taulukon viipaleita (90, 100). Saattaa tuntua hämmentävältä, että syötämme rajauksen indekseillä korkeus-ensimmäisessä ja leveys-sekunnissa, mutta muista, että OpenCV tallentaa kuvat NumPy-taulukoina. Tämän seurauksena meidän on annettava arvot y-akselille ennen x-akselia.

NumPy vaatii seuraavat neljä indeksiä rajauksen suorittamiseen:

Aloita y: Y-koordinaatti alussa. Tässä tapauksessa aloitamme kohdasta y=35.

Loppu y: Y-koordinaatti lopussa. Ratomme pysähtyy, kun y = 90.

Aloita x: Viipaleen alku x koordinaatti. Rajaus aloitetaan kohdasta x=35.

Loppu x: Viipaleen pään x-akselin koordinaatti. Kun x = 100, siivumme on valmis.

Samoin rajaamme alueet (23, 35) ja (143, 148) alkuperäisestä kuvasta poimimaan koko rungon kuvasta Rivi 19 .

Voit havaita, että kuvaa on rajattu näyttämään vain vartalo ja kasvot.

6. Kuvan koon muuttaminen

Kuvan leveyden ja korkeuden lisääminen tai pienentäminen tunnetaan nimellä skaalaus tai yksinkertaisesti koon muuttaminen. Kuvasuhde, joka on kuvan leveyden suhde sen korkeuteen, tulee ottaa huomioon kuvan kokoa muuttaessa. Kuvasuhteen laiminlyönti voi aiheuttaa skaalattuja kuvia, jotka näyttävät pakattuilta ja vääristyneiltä:

Alkukuvamme on vasemmalla. Oikealla näet kaksi kuvaa, jotka on skaalattu ilman kuvasuhdetta, mikä vääristää kuvan leveyden suhdetta sen korkeuteen. Kun muutat kuvien kokoa, sinun tulee yleensä ottaa huomioon kuvasuhde.

Koonmuutosalgoritmimme käyttämän interpolointitekniikan on myös otettava huomioon interpolointifunktion tavoite käyttää näitä pikselialueita kuvan koon suurentamiseen tai pienentämiseen.

Yleensä kuvan koon pienentäminen on paljon tehokkaampaa. Tämä johtuu siitä, että kuvapisteiden poistaminen kuvasta on kaikki mitä interpolointitoiminnon tarvitsee tehdä. Toisaalta interpolointimenetelmän pitäisi 'täyttää aukot' pikselien välillä, joita ei aiemmin ollut, jos kuvan kokoa haluttaisiin suurentaa.

Meillä on alkuperäinen kuvamme vasemmalla. Kuva on pienennetty keskeltä puoleen alkuperäisestä koostaan, mutta muuten kuvan 'laatu' ei ole heikentynyt. Siitä huolimatta kuvan kokoa on parannettu huomattavasti oikealla. Se näyttää nyt 'räjäytyneeltä' ja 'pikseloidulta'.

Kuten aiemmin totesin, haluat yleensä pienentää kuvan kokoa sen kasvattamisen sijaan. Pienentämällä kuvan kokoa analysoimme vähemmän pikseleitä ja joudumme käsittelemään vähemmän 'kohinaa', mikä tekee kuvankäsittelyalgoritmeista nopeampia ja tarkempia.

Käännös ja kierto ovat kaksi tähän mennessä käsiteltyä kuvan muuntamista. Tutkimme nyt, kuinka kuvan kokoa muutetaan.

Ei ole yllättävää, että muutamme kuviemme kokoa cv2.resize-menetelmällä. Kuten aiemmin totesin, meidän on otettava huomioon kuvan kuvasuhde, kun käytämme tätä menetelmää. Mutta ennen kuin menemme liian syvälle yksityiskohtiin, sallikaa minun antaa sinulle esimerkki:

# python resize.py --image squirrel.jpg

# tuodaan tarvittavat paketit

tuonti argparse

tuonti cv2

# luodaan argumentin kohde ja jäsennetään argumentti

apObj = argparse. ArgumentParser ( )

apObj. add_argument ( '-k' , '--kuva' , edellytetään = Totta , auta = 'kuvapolku' )

argumentteja = jonka ( apObj. parse_args ( ) )

# lataa kuva ja näytä se näytöllä

kuva = cv2. lukematta ( argumentteja [ 'kuva' ] )

cv2. imshow ( 'Alkuperäinen' , kuva )

# Jotta kuva ei näy vinossa, käytä kuvasuhdetta

# täytyy harkita tai muuttaa muotoaan; siksi selvitämme mitä

# uuden kuvan suhde nykyiseen kuvaan.

# Tehdään uuden kuvamme leveydestä 160 pikseliä.

näkökohta = 160,0 / kuva. muoto [ 1 ]

ulottuvuus = ( 160 , int ( kuva. muoto [ 0 ] * aspekti ) )

# tämä rivi näyttää todelliset koonmuutostoiminnot

kuvan kokoa muutettu = cv2. muuttaa kokoa ( kuva , ulottuvuus , interpolointi = cv2. INTER_AREA )

cv2. imshow ( 'Kuvan leveys muutettu' , kuvan kokoa muutettu )

# Mitä jos haluaisimme muuttaa kuvan korkeutta? - käyttämällä

# Sama periaate, voimme laskea kuvasuhteen perusteella

# korkeudesta leveyden sijaan. Tehdään skaalaus

# kuvan korkeus 70 pikseliä.

näkökohta = 70,0 / kuva. muoto [ 0 ]

ulottuvuus = ( int ( kuva. muoto [ 1 ] * aspekti ) , 70 )

# suorita koon muuttaminen

kuvan kokoa muutettu = cv2. muuttaa kokoa ( kuva , ulottuvuus , interpolointi = cv2. INTER_AREA )

cv2. imshow ( 'Kuvan korkeutta muutettu' , kuvan kokoa muutettu )

cv2. odotaKey ( 0 )

Rivit 1-14 , Kun paketit on tuotu ja argumenttijäsennin on määritetty, lataamme ja näytämme kuvamme.

Rivit 20 ja 21: Näiltä riveiltä alkaa vastaava koodaus . Kuvan kuvasuhde on otettava huomioon sen kokoa muuttaessa. Kuvan leveyden ja korkeuden välinen suhde tunnetaan kuvasuhteena.

Korkeus leveys on kuvasuhde.

Jos emme ota kuvasuhdetta huomioon, koon muuttamisen tulokset vääristyvät.

Päällä Rivi 20 , muutetun suhteen laskenta on tehty. Tarjoamme uuden kuvamme leveyden tällä koodirivillä 160 pikselinä. Määrittelemme yksinkertaisesti suhteemme (aspectratio) uudeksi leveydeksi (160 pikseliä) jaettuna vanhalla leveydellä, johon pääsemme kuvan avulla laskeaksemme uuden korkeuden suhteen vanhaan korkeuteen. muoto[1].

Kuvan uudet mitat päällä Rivi 21 voidaan laskea nyt, kun tiedämme suhteemme. Uuden kuvan leveys on jälleen 160 pikseliä. Kun vanha korkeus on kerrottu suhteellamme ja tulos muutettu kokonaisluvuksi, korkeus lasketaan. Voimme säilyttää kuvan alkuperäisen kuvasuhteen suorittamalla tämän toimenpiteen.

Rivi 24 on paikka, jossa kuvan kokoa todella muutetaan. Kuva, jonka kokoa haluamme muuttaa, on ensimmäinen argumentti, ja toinen on mitat, jotka laskemme uudelle kuvalle. Interpolointimenetelmämme, joka on algoritmi todellisen kuvan koon muuttamiseen, on viimeinen parametri.

Lopuksi päälle Rivi 25 , näytämme skaalatun kuvan.

Määrittelemme suhteemme (aspectratio) uudelleen Rivi 31 . Uuden kuvamme korkeus on 70 pikseliä. Jaamme 70 alkuperäisellä korkeudella saadaksemme uuden korkeuden ja alkuperäisen korkeuden suhteen.

Seuraavaksi määritämme uuden kuvan mitat. Uuden kuvan korkeus on 70 pikseliä, mikä on jo tiedossa. Voimme jälleen säilyttää kuvan alkuperäisen kuvasuhteen kertomalla vanhan leveyden suhteella, jolloin saadaan uusi leveys.

Kuvan kokoa muutetaan sitten Rivi 35 , ja se näkyy Rivi 36.

Tässä voimme nähdä, että olemme pienentäneet alkuperäisen kuvamme leveyttä ja korkeutta säilyttäen samalla kuvasuhteen. Kuvamme näyttäisi vääristyneeltä, jos kuvasuhdetta ei säilytetä.

Johtopäätös

Tässä blogissa olemme tutkineet eri kuvankäsittelyn peruskäsitteitä. Olemme nähneet kuvien kääntämisen OpenCV-paketin avulla. Olemme nähneet menetelmiä kuvan siirtämiseksi ylös, alas, oikealle ja vasemmalle. Nämä menetelmät ovat erittäin hyödyllisiä, kun luomme tietojoukon samankaltaisista kuvista annettavaksi harjoitustietojoukoksi, joten kone näkee erilaisia ​​kuvia, vaikka ne olisivat samat. Tämä artikkeli opetti myös, kuinka voit kiertää kuvaa minkä tahansa pisteen ympäri suorakulmaisessa avaruudessa käyttämällä rotaatiomatriisia. Sitten huomasit kuinka OpenCV kiertää kuvia tämän matriisin avulla ja näit pari kuvaa pyörivistä kuvista.

Tässä osiossa tarkasteltiin kahta peruskuvaaritmeettista (mutta merkittävää) yhteen- ja vähennysoperaatiota. Kuten näet, perusmatriisien lisääminen ja vähentäminen on kaikki kuvan aritmeettiset toiminnot.

Lisäksi käytimme OpenCV:tä ja NumPyä kuvaaritmeettisten erityispiirteiden tutkimiseen. Nämä rajoitukset on pidettävä mielessä, tai saatat saada odottamattomia tuloksia suorittaessasi kuvillesi aritmeettisia operaatioita.

On tärkeää muistaa, että vaikka NumPy suorittaa moduulioperaation ja 'kiertyy', OpenCV:n yhteen- ja vähennysleikkausarvot ylittävät alueen [0, 255], jotta ne mahtuvat alueen sisälle. Kun kehität omia tietokonenäkösovelluksiasi, tämän muistaminen auttaa sinua välttämään hankalia bugeja.

Kuvan kääntäminen on epäilemättä yksi yksinkertaisimmista ideoista, joita tutkimme tällä kurssilla. Kääntöä käytetään usein koneoppimisessa lisäämään harjoitustietonäytteitä, mikä johtaa tehokkaampiin ja luotettavampiin kuvan luokittelijoihin.

Opimme myös käyttämään OpenCV:tä kuvan koon muuttamiseen. On erittäin tärkeää ottaa huomioon sekä käyttämäsi interpolointimenetelmä että alkuperäisen kuvan kuvasuhde, kun muutat kuvan kokoa, jotta tulos ei näytä vääristyneeltä.

Lopuksi on tärkeää muistaa, että jos kuvan laatu on ongelma, on aina parasta vaihtaa suuremmasta pienempään kuvaan. Useimmissa tapauksissa kuvan suurentaminen luo artefakteja ja heikentää sen laatua.