R-ohjelmointi.org

Tilastotieteellistä ohjelmointia R-kielellä

Kuvakollaasi magick-paketilla

Jarno kirjoitti aiemmin kuvakollaasin tekemisestä EBImage-paketilla. EBImagen kuvankäsittelyominaisuudet eivät valitettavasti tyydytä kaikilta osin omia vaatimuksiani. Esimerkiksi kuvan pienentäminen paketin resize-funktiolla heikentää usein kuvan laatua merkittävästi.

Onneksi kuvankäsittely R:ssä on ottanut hiljattain aimo askelen eteenpäin. Uusi magick-paketti tuo tunnetun ImageMagick-kirjaston laajat kuvankäsittelyominaisuudet R:n käyttöön ja tarjoaa mahdollisuuden tehdä kaiken vektoroidusti, eli vaikkapa koko hakemiston kuville kerralla. Paketin dokumentaatio on toistaiseksi niukanpuoleista, mutta pakettiin liittyy kätevä vinjetti, joka auttaa hyvin alkuun. Kokeillaanpa, miten kollaasin tekeminen luonnistuu magickilla.

Yhden kuvan käsittely

Tavoitteena on rajata, pienentää ja kehystää jpg-kuva, monistaa muunnettu kuva kollaasiksi sekä tallentaa lopputulos levylle png-muodossa.

Aluksi asennetaan ja ladataan magick-paketti CRANista. Tätä ennen koneella on kuitenkin oltava asennettuna ImageMagick, jonka saa ladattua ohjelman kotisivuilta.

install.packages("magick")
library(magick)

Luetaan kuva R:n muistiin objektiin img.

img <- image_read("C:/MyDir/IMG_1060.JPG")

Ainakin RStudiossa kuvan saa näkymään plottausikkunassa kirjoittamalla komentoriville kuvaobjektin nimen, tässä tapauksessa siis img.

Tarkastellaan kuvan ominaisuuksia image_info-funktiolla.

image_info(img)
# format width height colorspace filesize
# 1 JPEG 4000 2248 sRGB 3289227

Kuvan rajaamista varten tarvitaan kuvan nykyiset leveys- ja korkeustiedot. Lasketaan näiden perusteella uudet reunakoordinaatit.

width <- image_info(img)[2] # leveys
height <- image_info(img)[3] # korkeus
min <- min(width, height) # pienemmän ulottuvuuden arvo (tässä korkeus)
left <- floor((width - min) / 2) # rajauksen vasen reuna
top <- floor((height - min) / 2) # rajauksen yläreuna

Kuvaa voidaan rajata image_crop-funktiolla. Rajausfunktio tarvitsee useiden muiden magickin kuvamuunnosfunktioiden tavoin syötteekseen määrämuotoisen geometry-parametrin, jolla määritetään muunnoksen koordinaatit. Parametrin syntaksi on muotoa ”leveys x korkeus + vasen reuna + yläreuna”, jossa jokainen elementti on valinnainen. Jos kuvasta haluttaisiin vaikkapa rajata ylhäältä alkaen 500 pikselin siivu, voitaisiin antaa geometry-parametri ”x 500”. Saman siivun poistaminen onnistuu parametrin arvolla ”+ 0 + 500”, ja 500 x 600 pikselin suuruinen rajaus alkaen pisteestä (200, 300) onnistuu arvolla ”500 x 600 + 200 + 300”.

Esimerkkikuva rajataan keskeltä neliön muotoiseksi alkuperäisen kuvan pienemmän ulottuvuuden (tässä tapauksessa korkeuden) mukaan. Geometria-parametrin lausekkeessa left ja top ovat aiemmin lasketut reunakoordinaatit ja min on alkuperäisen kuvan korkeus.

img2 <- image_crop(img, paste0(min, "x", min, "+", left, "+", top))

Pienennetään kuva 400 x 400 pikselin kokoon ja lisätään ohut musta ja paksumpi valkoinen kehys.

img3 <- image_scale(img2, "400x400")
img4 <- image_border(img3, "black", "2x2")
img5 <- image_border(img4, "white", "5x5")

Muodostetaan 3 x 2 kollaasi kokoamalla ensin kolmen kuvan rivi ja asettamalla sitten kaksi kuvariviä päällekkäin.

img6 <- c(img5, img5, img5) # kolmen kuvan vektori
img7 <- image_append(img6) # muodostetaan vektorin sisällöstä sarjakuva
img8 <- c(img7, img7) # kahden sarjakuvan vektori
img9 <- image_append(img8, stack=TRUE) # muodostetaan vektorin sisällöstä ruudukko

Tallennetaan lopputulos hävikittömässä png-muodossa.

image_write(img9, path = "C:/MyDir/kollaasi1.png", format = "png")

kollaasi1
Toistokuva

Kaikki kansion kuvat kollaasiksi

Hyvinhän se sujui yhdelläkin kuvalla, mutta kollaasi olisi kenties mielenkiintoisempi, jos kuva-aihe ei olisi niin monotoninen. Kokeillaan toistaa samat muunnokset kerralla kansion kaikille kuville, ja kootaan niistä lopuksi hiukan monipuolisempi kollaasi.

Luetaan jälleen kuvat R:n muistiin objektiin img.

setwd("C:/MyDir/")
pics <- dir(pattern=".JPG")
# pics
# [1] "IMG_1060.JPG" "IMG_1198.JPG" "IMG_1753.JPG" "IMG_1765.JPG" "IMG_1840.JPG" "IMG_2863.JPG" "IMG_2887.JPG"
# [8] "IMG_3061.JPG" "IMG_4136.JPG"
img <- image_read(pics)
image_info(img)
 
#   format width height colorspace filesize
# 1   JPEG  4000   2248       sRGB  3289227
# 2   JPEG  4000   2248       sRGB  2454799
# 3   JPEG  1235   1870       sRGB   375517
# 4   JPEG  2154   1488       sRGB   392205
# 5   JPEG  4000   2248       sRGB  2034988
# 6   JPEG  4000   2248       sRGB  2614357
# 7   JPEG  4000   2248       sRGB  2572706
# 8   JPEG  4000   2248       sRGB  2483413
# 9   JPEG  2367   2007       sRGB   528066

Objekti img sisältää siis nyt kaikki kansion yhdeksän jpg-kuvaa. Objektin tiedoista huomataan, että kuvista kahdeksan on vaakasuuntaisia mutta kuva numero 3 on pystysuuntainen. Tämä ei haittaa, sillä edellä määritelty kuvanrajausalgoritmi osaa rajata kuvan sen suunnasta riippumatta. Ainoa (sisällöllinen) vaatimus on, että kohde on suunnilleen keskellä kuvaa ja mahtuu neliön muotoiselle alueelle.

Kaikki kuvanmuunnokset, jotka tehtiin edellä yhdelle kuvalle, voidaan tehdä vektoroidusti myös kaikille img-objektin sisältämille kuville kerralla. Kuvien rajausta en tosin saanut jostain syystä toimimaan vektoroidusti, mutta ongelma ratkesi suhteellisen nätisti pienellä silmukalla.

# Kuvien nykyiset mitat ja näiden perusteella lasketut uudet reunakoordinaatit
width <- image_info(img)[,2]
height <- image_info(img)[,3]
mitat <- image_info(img)[,c(2,3)]
min <- apply(mitat, 1, min)
left <- floor((width - min) / 2)
top = floor((height - min) / 2)
 
# Kuvien rajaus
img2 <- img
for (i in 1:length(pics)) {
  img2[i] <- image_crop(img[i], paste0(min[i], "x", min[i], "+", left[i], "+", top[i]))
}
 
# Kuvien pienentäminen
img3 <- image_scale(img2, "400x400")
 
# Ohut musta ja paksumpi valkoinen kehys
img4 <- image_border(image_border(img3, "black", "2x2"), "white", "5 x 5")

Kollaasia varten kuvat pitää ensin jakaa ja yhdistää riveiksi, jotka lopuksi pinotaan päällekkäin ruudukoksi. Luonnollisesti tämän voi tehdä myös toisin päin, eli koota ensin sarakkeet ja asetella ne sen jälkeen vierekkäin. 3 x 3:n kuvan kollaasin kokoaminen onnistuu esimerkiksi rekursiivisella silmukalla seuraavasti.

r <- 3                            # rivien määrä
c <- 3                            # sarakkeiden määrä
img5 <- image_append(img4[1:c])
for (i in 1:(r-1)) {
  img5 <- image_append(c(img5, image_append(img4[(c*i+1):(c*i+c)])),
  stack=TRUE)
}

Viimeistellään lopuksi kuvasarja paksuntamalla valkoista ulkokehystä ja tallennetaan tuotos levylle.

img6 <- image_border(img5, "white", "5x5")
image_write(img6, path = "C:/MyDir/kollaasi2.png", format = "png")

Magickilla pienennetyn kuvan laatu on kautta linjan selvästi parempi kuin EBImagella pienennetyn. Lisäksi magick tarjoaa laajan valikoiman muitakin kuvanmuunnoksia ja -suodattimia, jotka saa listattua komennolla ?transformations. Magick siis vaikuttaa näin pikaisen tutustumisen perusteella oikein pätevältä kuvankäsittelytyökalulta, mikäli haluaa muokata kuvia nimenomaan R-ympäristössä.

kollaasi2
Lopputulos