0

I've been wanting to have a count-up timer overlaying a video but all I could find online for doing so was using Adobe After Effects which isn't really an option as I'm on Linux and am unwilling to pay for it.

So, if I could get a series of images or a video, then it could be imported into Openshot to overlay a video quite nicely, but it's been a struggle getting said series of images or video.

I've started work on a script to make the series of images but have no idea how to place variables into the command. Here's the script in its current form:

#!/bin/bash
for i in {0..18000000}
do
    echo "$i"
    convert -size 780x100 xc:#d3fff5ff -font Courier-New -pointsize 100 -fill black -draw "text 20,80 '000:00:00.00'" 0.png
done

What I would like is for 0.png to essentially be $i.png, so it increments up to 18,000,000. The time (shown as 000:00:00.00) I could probably work out for myself after knowing how to insert variables into commands. The time format is hours, minutes, seconds and frames.

Here's an example for if I've poorly explained things (sorry for the spacing - only double new lines only work on here for some reason):

0.png = 000:00:00.00

1.png = 000:00:00.01

49.png = 000:00:00.49

50.png = 000:00:01.00

51.png = 000:00:01.01

18000000.png = 100:00:00.00

3
  • Why doesn't 50.png become 000:00:00.50? Only 50 frames per second? Commented Feb 16, 2016 at 19:14
  • Yeah, 50fps. Deinterlaced PAL footage using Bob. Commented Feb 16, 2016 at 19:33
  • Btw: 18000000.png = 99:59:59.49 Commented Feb 16, 2016 at 20:07

3 Answers 3

2

The math to compute hours, minutes, seconds, and frames is fairly simple:

f=$(( i % 50 ))
s=$(( (i/50) % 60 ))
m=$(( (i/(50*60)) % 60 ))
h=$(( (i/(50*60*60)) ))

Then you can simply embed the values in the string like so:

... -draw "text 20,80 '$(printf "%03d:%02d:%02d.%02d" $h $m $s $f)'" "$i.png"

The bigger problem is how long it's going to take to run convert 18 million times.

Sign up to request clarification or add additional context in comments.

Comments

1

This kind of builds on chepner's and choroba's answers but also addresses how you get 18,000,000 images generated using all your CPU's cores. I benchmarked 3 different methods on a decent spec iMac and achieved the following results - starting with the simplest and slowest and going from there to faster methods...

Method 1 - Plain Sequential bash script

#!/bin/bash
for ((i=0; i<18000000; i++ )); do
   f=$(( i % 50 ))
   s=$(( (i/50) % 60 ))
   m=$(( (i/(50*60)) % 60 ))
   h=$(( (i/(50*60*60)) ))
   convert -size 780x100 xc:#d3fff5ff -font Courier -pointsize 100 -fill black -draw "text 20,80 '$(printf "%03d:%02d:%02d.%02d" $h $m $s $f)'" $i.png
done

This takes around 4.6 days to generate 18 million images.

Method 2 - GNU Parallel script

#!/bin/bash

# Define a function for GNU Parallel to run
doit() {
   i=$1
   f=$(( i % 50 ))
   s=$(( (i/50) % 60 ))
   m=$(( (i/(50*60)) % 60 ))
   h=$(( (i/(50*60*60)) ))
   convert -size 780x100 xc:#d3fff5ff -font Courier -pointsize 100 -fill black -draw "text 20,80 '$(printf "%03d:%02d:%02d.%02d" $h $m $s $f)'" $i.png
}

# Export doit() function so GNU Parallel knows about it
export -f doit

ulimit -S -n 2048

for ((i=0;i<=18000000;i++)); do
   echo $i
done | parallel -k doit {1} 

This keeps all your CPU cores busy and takes around 32 hours to generate 18 million files. I think it's a pretty good compromise compared to having to write code and compile and build it as in the next answer.

Method 3 - Magick++ program running 8 copies in parallel

#include <Magick++.h>

using namespace std;
using namespace Magick;

int main(int argc, char* argv[]){

   // Pick up parameters
   if(argc!=3){
      printf("ERROR: Please supply 2 arguments - start and end number\n");
      exit(EXIT_FAILURE);
   }
   int start=atoi(argv[1]);
   int end  =atoi(argv[2]);

   InitializeMagick(*argv);

   // Create a basic template
   Image img(Geometry(780,100),Color("#d3fff5ff"));
   img.font("Courier");
   img.fillColor(Color("black"));
   img.fontPointsize(100);

   // Get cycling through the images
   for(int i=start;i<=end;i++){
      // Work out timestamp for this frame
      int f=i % 50;
      int s=(i/50) % 60;
      int m=(i/(50*60)) % 60;
      int h=(i/(50*60*60));
      char timestamp[100];
      sprintf(timestamp,"%03d:%02d:%02d.%02d",h,m,s,f);

      // Copy the basic template image
      Image thisframe=img;
      thisframe.draw(Magick::DrawableText(20,80,timestamp));
      char filename[100];
      sprintf(filename,"%d.gif",i);
      thisframe.write(filename);
   }
}

Here is how you run 8 copies in parallel:

#!/bin/bash
./main 1 2250000 &
./main 2250001 4500000 &
./main 4500001 6750000 &
./main 6750001 9000000 &
./main 9000001 11250000 &
./main 11250001 13500000 &
./main 13500001 15750000 &
./main 15750001 18000000 &
wait

This takes just under 9 hours to generate the 18 million files.

1 Comment

My word, you did all the work for me! Will just use your second method due to the balance of efficiency and easiness. NOTES FOR OTHERS: sudo apt-get install parallel must be run to install the Parallel software and Courier-New must be used instead of Courier for the monospacing.
1

Bash expands variables in commands if they aren't in single quotes. Here, they're in double quotes:

convert -size 780x100 xc:#d3fff5ff -font Courier-New -pointsize 100 -fill black -draw "text 20,80 '$hours:$minutes:$seconds.$frames'" $i.png

Note that {0..18000000} expands to a list of 18M strings first. It might be better to use a C-style loop:

for (( i=0 ; i<=18000000 ; i++ )) ; do

Another issue might be having a directory with 18M files. Maybe store them into folders like hours/minutes/seconds?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.