Paste an image onto another image?

What is Julia’s equivalent of python PIL Image.paste? And how would I paste a rotated image onto a background image?

(everything below is just context, that’s the question)

I am trying to use Julia to make a dataset of images for a classifier.
I want to put the objects I’m trying to classify randomly into a scene. I have a folder of small images that represent each class I want to detect, and a folder of larger images that represent scenes (a bunch of backgrounds, wallpapers, and pictures of scenes that I might want to classify in)

I want to write a method that takes a scene and randomly adds some of the small images to it. How would I do this? Here’s the kind of thing I have in mind

  1. generate a vector of random length where each element is the name of a class
  2. for each class, choose a random picture from it’s corresponding folder
    a) also run some kind of augmentation pipeline, but that looks easy to add later
  3. glue the images together in a “tableau” (strip or square) (involves resizing them without losing aspect ratio, which I also cannot figure out)
  4. choose a random background file
  5. pick a random size & orientation for the tableau
  6. place the tableau into the scene.

(all the random choices will be within some predefined range)


function make_random_scene() 
  tableau = make_rand_tableau( choose(classes, rand(2:5) ) )
  background = get_random_background( ... )
  # ??? how do i place one image into another at a specific orientation + size? 
end 

#---------------- extra stuff in case that stub doesn't make sense ----

classes = readdir(class_folder)
get_random_image(class) = load( ... ) # i have written this already
get_random_background(background_folder) = load( ... ) # this too

function make_rand_tableau(class_list :: Vector{String}; white = RGB(0.5, 0.5, 0.5) )
	make_tableau(map(class -> clean_image(get_random_class(class)), class_list)...)
end

function make_tableau(images...; kind=:row, white = RGB(150/255,140/255,135/255), height=100)
	strip = rowcat(images...; height=height)
	newim = copy(strip)
	newim[strip .== RGB(1,1,1)] .= white
	newim
end

# this doesn't work correctly - images are squished or stretched. 
# I didn't see a way to resize to a fixed dimension + maintain aspect ratio
rowcat(images...; height=100) = mosaicview(
  map(im -> imresize(im, (height, height/size(im)[1] * size(im)[2] )), images); 
  fillvalue=RGB(1,1,1), nrow=1, npad=15)


I am able to compose images directly onto each other by just copying one matrix on top of another.
Unclear how to do this with rotated images. Here’s what I’ve tried so far:

function compose(bg, fg, pos, angle)
	mask = Gray.(fg)
	mask[:] .= Gray(1)
	img = warp(fg, LinearMap(RotMatrix(deg2rad(angle))) ∘ Translation(pos...))
	mask = warp(mask, LinearMap(RotMatrix( deg2rad(angle) )) ∘ Translation(pos...))
	
	scene = copy(bg)
	scene[mask .== Gray(1)] = img[mask .== Gray(1)]
	img, mask
end

This gives me a boundserror which I don’t understand. The bg is definitely much bigger than the fg, and I’m placing the foreground in the center of the image.

Alright, I’m answering my own question:

function compose(bg, fg, pos, angle)
	mask = Gray.(fg)
	mask[:] .= Gray(1)
	img = warp(fg, LinearMap(RotMatrix(deg2rad(angle))) ∘ Translation(pos...))
	mask = warp(mask, LinearMap(RotMatrix( deg2rad(angle) )) ∘ Translation(pos...))
	
	scene = copy(bg)
	scene[findall(mask .== Gray(1))] = img[mask .== Gray(1)]
	scene
end