Every year myself and my wife go to Essen, Germany for the annual Spiel, this year it's 5th - 8th October. She runs her own board game YouTube channel as I Play Red. I help her film and stuff.
So, this year we decided to create a fun video before the Spiel, which coincidentally crosses over with Rails World which I am sad about not attending, maybe next year.
As a fun side-project I created MySpiel which gathers information about all of the new board game releases at Spiel including grabbing images of the board game box art. It's a Rails project with the images stored with ActiveStorage, there are about 1,000 images so what came we make with that.
We wanted to make a YouTube Short, the criteria for that is a video with a ratio of 16:9 that is 60 seconds long at the most. Ok, it actually got to be vertical, so the ratio is actually 9:16. So I think we're going to smash all of those images together into a video just to demonstrate how huge 1,000 new releases really is. So whatever images we've got they have to be resized to that ratio before we do anything else.
ImageMagick is a pretty standard tool for image manipulation and it's got a pretty powerful command line interface which honestly is often overwhelming but fortunately there are plenty of forums, stack overflow, etc to get good examples.
Firstly though I want to find all the images on my local disk I'm using local storage for ActiveStorage. We can grab the path to all of the files and whack that in an external file. You can extract the path name to the file using ActiveStorage::Blob.service#path_for
class Game < ApplicationRecord
has_one_attached :cover
def on_disk
ActiveStorage::Blob.service.path_for(self.cover.key)
end
end
Then we dump all the paths to a file.
File.open(Rails.root.join("images.txt"), "w") do |f|
f.puts Game.all.map(&:on_disk).join("\n")
end
Now that we have all the paths, I'm going to use ImageMagick to resize and crop all the images to 9:16 in bash. It will read in the files from images.txt
and output each of them to a .png
#!/bin/bash
for i in `cat images.txt`;
do
file $i;
o=`basename $i`
magick $i -gravity center -extent 9:16 -resize 1440x2560 out/$o.png;
done;
Celtae from Pythagoras Games is a "worker swapping" game powered by a rondel in which players choose actions to perform during their turn.
Celtae 1:1
Celtae 9:16
Ok, that took a while but now we have almost 1,100 images resized and cropped. We want to collect these altogether into a video. We can use FFMpeg for that, but pausing a bit. We want to fit 1,100 images into 60 seconds. That 18 images per second. I'm going to take a guess that smashing that many images together in close succession is going to be quite jarring. How can we make make it less jarring. Well for fun we could order the images by colour (or approximate colour).
This was a nice little snippet.
https://www.imagemagick.org/discourse-server/viewtopic.php?t=29789
Effectively it resizes the image in memory to 1px by 1px, then extracts the RGB value
convert celtae.png -scale 1x1\! txt:- | tail -n 1
0,0: (76,30,13) #4C1E0D srgb(29.6631%,11.7692%,4.93835%)
The full script here effectively renames each file to extracted RGB value.
#!/bin/bash
for file in *.png
do
filename=`convert $file -scale 1x1\! txt:- | tail -n 1 | awk -F\( '{print $2}'|cut -d\) -f1|awk -F\, '{print $1$2$3}'`
extension=".png"
while [ -f "$filename$extension" ]
do
random=`echo $RANDOM % 10 + 1 | bc`
filename=$filename$random
done
mv $file $filename$extension
done
So now I have a directory of images that are ordered by the filename (RGB value) by default. We can now use ffmpeg
to convert our images to into a video, each image represents a frame and we can set our framerate to 18 in order to fit everything into 60 seconds.
cat *.png | ffmpeg -framerate 18 -f image2pipe -i - -c:v libx264 -r 30 -pix_fmt yuv420p output.mp4
The result is our output.mp4
.
The final version needed a few tweaks, so I imported the final mp4 into DaVinci Resolve added a few overlay images but here it is.
That was a fun, can you spot any games you are looking forward to? I can pick a few of them out and recognise but 18 frames per second is a bit unsettling.
If you like boardgames this is I Play Red, if you're in Germany and not at Rails World then you could do worse than to check out Spiel. It's a little bit enormous, and a spectacle and with 1,100 releases this year you'll find something that appeals.
I'd love to see how other people are programmatically creating video content for some further inspiration.
Top comments (0)