Vaikka käsittelemme kuvantekoa ja käsittelyä neuroverkoilla, suuri osa kehittelystä ja koodaustyöstä liittyy muuhun kuin itse algoritmiin. Edellisessä artikkelissa jo törmäsimme mm. siihen, miten joudutaan muuntamaan kuvia eri esitystapojen välillä (esim. 0..1 pikselikuvista -1..1 tensoreiksi), kun haluamme ja on järkevääkin käyttää valmiita kirjastoja.
Tähänastisten skriptien varmaankin suurin käytännön ongelma on se että kaikki asetukset ovat koodissa. Esimerkiksi tavoitekuvan nimen on oltava test.png ja sen muuttaminen vaatii koodiin koskemista. Tässä artikkelissa muutammekin koodia niin että asetukset voidaan antaa komennon yhteydessä tyyliin:
python harj2ii.py --image summer2019/image0028.jpg --lr 0.002 --imageSize 512 --b 0.3
Tällä komennolla otamme tavoitteeksi tietyn kuvan koneeltamme, mistä hyvänsä kansiosta, asetamme koulutustahdin, teemme kuvaa koossa 512x512px ja painotamme vihreää neliötä painolla 0.3.
Tätä varten joudumme ensinnäkin luomaan ns. argumenttiparserin, jossa määrittelemme mitä asetuksia voidaan tai on pakko antaa, minkä tyyppisiä ne ovat (kokonaisluku, liukuluku, tosi/epätosi, merkkijono eli raaka teksti) ja annamme niille oletusarvon, ts. mitä arvoa käytetään jos asetusta ei ole annettu.
import argparse # use command line parameters parser = argparse.ArgumentParser() # define params and their types with defaults if needed parser.add_argument('--image', type=str, default="test.png", help='path to image') parser.add_argument('--image2', type=str, default="", help='path to optional second image') parser.add_argument('--b', type=float, default=0, help='blend factor, 0 for none') parser.add_argument('--lr', type=float, default=0.05, help='learning rate') parser.add_argument('--niter', type=int, default=150, help='number of iterations') parser.add_argument('--name', type=str, default="harj2i", help='basename for storing images') parser.add_argument('--show', action="store_true", help='show image in a window') # get params into opt object, so we can access them like opt.image opt = parser.parse_args()
Nyt kaikki em. asetukset ovat ohjelmassa käytettävissä, esim. kuvatiedoston nimi löytyy opt.image:sta. Niinpä koodia on nyt muutettava vastaavasti, esimerkkejä alla.
filename = opt.image #"test.png" imgT = preprocess(Image.open(filename).convert("RGB"))
Tai vaihtoehtoisesti ihan vain…
imgT = preprocess(Image.open(opt.image).convert("RGB")) imgT2 = preprocess(Image.open(opt.image2).convert("RGB")) optimizer = torch.optim.Adam([imgG], lr)
Ja optimointisilmukan sisällä
# calculate loss loss1 = torch.abs(imgT - imgG).mean() # an alternative loss: green target loss2 = torch.abs(imgT2 - imgG).mean() # use opt.b to adjust the effect of the second target image loss2 = opt.b * loss2 loss = loss1 + loss2 #... # save image with a name given as a parameter if (i < 10) or (i % 10 == 0): save_image(imgG, opt.name+"-"+str(i)+".jpg")
Olemme tähän asti seuranneet kuvan kehittymistä tallennettuja kuvatiedostoja seuraamalla. Usein on kätevää jos voi seurata kuvan kehitystä suoraan näytöllä olevasta ikkunasta. Tämä on täysin mahdollista, vaikka ajammekin ohjelmaa komentorivillä, kunhan vain olemme itse koneen ääressä emmekä pelkän pääteyhteyden takana. Tehdään nyt sitä varten rutiini. Siihen tarvitaan kirjastot opencv ja numpy, jotka muutenkin kuuluvat keskeiseen työkalupakkiin näissä hommissa.
Luomme tässä show_on_screen -rutiinin, jolle annetaan kuvan sisältävä tensori, joka muunnetaan pytorch-maailmasta pythonin kuvankäsittelymaailmaan ja näytetään ruudulla omassa ikkunassaan. Ikkunan oletusnimi on out, ja niin kauan kuin nimeä ei muuteta, kuva päivittyy samassa ikkunassa. Haluttaessa voidaan luoda useampia ikkunoita ja seurata usean kuvan muuttumista.
# import opencv image processing library and numpy import cv2 import numpy # define display routine def show_on_screen(image_tensor, window="out"): im = image_tensor.detach().numpy() # convert from pytorch tensor to numpy array # pytorch tensors are (C, H, W), rearrange to (H, W, C) im = im.transpose(1, 2, 0) # adjust range to 0 .. 1 im -= im.min() im /= im.max() # show it in a window (this will not work on a remote session) cv2.imshow(window, im) cv2.waitKey(100) # display for 100 ms and wait for a keypress (which we ignore here)
Vielä ei ole ihan valmista. Teimme vasta rutiinin jolla halutessamme voimme näyttää kuvia ruudulla. Vielä pitää muuttaa koodia niin että myös käytämme sitä. Sopiva paikka on optimointisilmukan lopussa, jossa myös talletamme kuvan levylle.
# save image if (i < 10) or (i % 10 == 0): save_image(imgG, opt.name+"-"+str(i)+".jpg") # show on screen if opt.show: show_on_screen(imgG, opt.name)
Huomaa myös että kuvaa näytetään ruudulla vain jos komennossa on asetus –show mukana. Tämä asetus on sellaista tyyppiä, johon ei anneta luku- tai muuta arvoa, vaan sen läsnäololla tehdään eräänlainen on/off-ohjaus. Joko näytämme kuvaa (–show) tai emme näytä.