Create a rotating logo using ImageMagick and FFMPEG
The article may be of interest to those who want to slightly revive the design of their video channel, as well as those who have just started working with the tools specified in the title. Confident users, I hope, will complement my material.
I wondered how to revive the logo, which I impose on the videos of some sporting events. There was an idea to make a 20 logo turning around its vertical axis every seconds. As a result, I wanted to get something like this:
To complete the task, you need a system with imagemagick and ffmpeg installed. The bash will be used to form commands for imagemagick. What would be the use of the bc utility? The math in bash is pretty simple and it’s difficult to compute trigonometric functions. ')
Using python, for example, you can make the same thing even easier; in it, sine and cosine are calculated.
To create a video, create N frames for which the logo will take a full turn, then turn them into a video file.
In fact, rotation is the sequential compression and expansion of an image in one of the dimensions. If we want to get the effect of uniform rotation around the vertical axis, the image width will be changed multiple of the sine or cosine functions.
The initial state for us is a logo visible in full size, so to change the width of the image we will multiply its width by the cosine (its value at zero is just one).
Rotation of the image for these purposes can be divided into 4 stages: - the image is narrowed; - expands, but turned to the viewer the reverse side; - narrows downside to the viewer; - expands to its original size.
Let's form a basic set of frames, build for rotation by 0-90 degrees, and frames for the remaining 270 will get a little converting them. The cycle, accordingly, will not be from 0 to 360 degrees, but from 0 to 90.
We will prescribe the path to our logo, so that we can then access it with a short $ logo:
logo=../../logo/Moto_Gymkhana_transparent.png
For convenience, we introduce variables equal to the width and height of the logo, so it will be easier to correct the script for another image:
width=842 height=595
Let's get a variable equal to the number of frames for which the logo will make a quarter turn. It will be needed once - to calculate the step with which the rotation angle should be calculated.
Frames=15
In the loop you will need: - calculate what will be the width of the image when it is rotated at a given angle "n"; - create a blank canvas of size, say 850x600; - add a compressed image to its center.
for (( n=0; n<=90; n+=$(expr $(( 90/$Frames ))))) do oWidth=$(printf %.$2f $(echo"$width*(c($n*4*a(1)/180))+1" | bc -l)) convert -size 850x600 xc:transparent -background none \ \( -alpha set -channel A -evaluate add -60% $logo -geometry $oWidth\ x$height\! \)\ -gravity center -composite ./tmp/$n.png done
Let's sort the code in parts.
Calculate the width of the image at a given angle:
oWidth=$(printf %.$2f $(echo"$width*(c($n*4*a(1)/180))+1" | bc -l))
It will be more convenient to disassemble this piece from right to left: "| bc -l" indicates that this will be computed by the bc utility. Since cosine in the “bc” utility accepts values ​​in radians as input, and pi numbers in bash are not — use the following expression: c ($ n * 4 * a (1) / 180) - where “C” is the cosine, $ n - the angle in degrees is determined in a cycle; a (1) is the arctangent of a unit equal to pi / 4. Therefore, 4 * a (1) is a way to write pi. 180 divide to go from radians to degrees. Thus, "$ width * (c ($ n * 4 * a (1) / 180))" is the width multiplied by the cosine of the angle "n". "+1" after this expression to ensure that the image width does not take the value 0. Attempting to make an image of zero width will not understand ImageMagick. $ (printf%. $ 2f $ (echo "$ width * (c ($ n * 4 * a (1) / 180)) + 1" | bc -l)) - calculate the width for a given angle and output rounded to the whole.
convert -size 850x600 xc:transparent -background none \ \( -alpha set -channel A -evaluate add -60% $logo -geometry $oWidth\ x$height\! \)\ -gravity center -composite ./tmp/$n.png
convert -size 850x600 xc: transparent -background none - create a transparent canvas with the size of 640x360 without background.
The following part of the command is taken in escaped "\" brackets so that it runs in isolation. This is important, otherwise the resizing will touch the canvas. -alpha set - enable transparency channel. -channel A -evaluate add -60% - add "-60%" to the value of channel A of all image pixels. $ logo - the path to the image. -geometry $ oWidth \ x $ height \! - resize the image. "-geometry AxB" will resize the image but maintain the aspect ratio. Those. if we try to reduce the width, the image height will also decrease to maintain the “width x height” ratio, and empty areas will appear above and below. "-geometry AxB \!" They are not trying to keep the “width x height” ratio, but will change them independently of each other. -gravity center - place in the middle. -composite - combine images. ./tmp/$n.png - save the file to the “tmp” folder, calling the current value of the variable “n”.
View from an angle of 54 degrees:
From this reference image set, create 4 frames of future animation.
Let the logo rotate clockwise when viewed from above. To add volume to the image, add a “shadow” shifted to the right or left depending on where the image rotates on the frame. And we will reflect the image from left to right on the frames, where the logo is turned to the viewer with the back side. The “shadow” on the same frames will be added not under the original image, but on top of it to create the effect of the reverse side of the logo.
Consider in detail the expression for the first quarter of the turn. The rest will be clear by analogy. I only focus on the differences from the first:
convert tmp / $ n.png — take the source image as a base.
\ (+ clone -background '#cccf' -shadow 100x8 + $ shadowShift-3 \) - clone and create an image shadow. The color specified by the background option will set the color of the shadow. How much the shadow is opaque (in percent) and is blurred is set by the parameter “100x5”. The shadow shift relative to the original image is given as + x + y. In our case, the vertical shift is constant (3 pixels up), and the horizontal shift is set by the variable shadowShift. The value of this variable is calculated in the same way as the original frame is compressed vertically. Just take the sine of the angle, not the cosine, because the zero angle shift should be zero.
shadowShift = $ (printf%. $ 2f $ (echo "15 * (s ($ n * 4 * a (1) / 180)) + 1" | bc -l)) For the first and second quarters, the shadow should be shifted to the right, so the shadowShift value is taken with a plus sign, for the third and fourth - with a minus sign.
-background none -compose Dst_Over -layers merge -gravity center -depth 8 tmp / logo $ (expr $ ((1000 + n))). png
Here the important option is -compose. It sets which of the images will be located on top. For the first quarter of the turn, the logo itself is on top, therefore Dst_Over. For the second and third quarters, Src_Over will be spelled out (shadow above).
-layers merge -gravity center - combine layers, place in the middle.
tmp / logo $ (expr $ ((1000 + n))). png - save as logo + “1000 + value n” in the tmp folder.
The name is so that the frame names are immediately sorted alphabetically, and it was convenient to give them to ffmpeg for processing.
For frames of the second quarter of the turn there will be “1800-n”, for the third “2000 + n”, for the fourth “2800-n”.
For the second and third quarters, the -flop option will appear - flip the image from left to right. Because of this, by the way, you need to change the sign of the displacement of the shadow to the opposite.
-pattern_type glob - allows you to set the frame name mask in the usual console view -i 'tmp / logo * .png' - take as the initial data of the image from the tmp folder whose name begins with "logo" -pix_fmt argb - set image format with transparency. “A” in ARGB is just the alpha channel -vcodec qtrle - set the codec for video that supports transparency. From the known to me this “qtrle” and “png” -r 30 - set the final video frequency to 30 frames per second. rotating_logo.mov - save as “rotatingLogo.mov”
Now we can execute the command
ffplay rotatingLogo.mov
and look at the result:
The resulting video is only a couple of seconds long, t.ch. Let's combine a lot of these pieces together. For this we use the same ffmpeg.
So that the logo does not rotate permanently, but occasionally we will create a piece of video with a fixed logo. For an article, let it be a duration of 3 seconds so that you do not have to wait long for a rotation while viewing:
This command has the option "-loop 1" - indicating ffmpeg to repeat the input image sequence (in this case, the only one), and the option "-t 3" indicating that the duration of the final video is 3 seconds.
To glue the video with a fixed and rotating logo, create a file with a list of files that you want to glue. In this case, 20 copies of our files:
ffmpeg -i video.mov -i logoWithRotation.mov - take two videos as input.
-filter_complex - use filter_complex to which we specify the following: [1: 0] scale = iw / 3: ih / 3 [logo] - take stream 0 from the second video (1 sample is from zero) and reduce the width (iw - input width) and height (ih - input height) value . Pass on with the name "logo".
[0: 0] [logo] overlay = shortest = 1: x = 20: y = 0 — take stream 0 from the first video and stream “logo” and superimpose them on each other. “Shortest = 1” - says stop when the end of any stream is reached. x = 20: y = 500 - indicates where to place the left upper corner of the video overlay.
-vcodec libx264 -crf 18 - use H264 codec. The value after crf indicates how much to compress. 0 - no compression at all.
overlay.mov - save under this name.
View result:
ffplay overlay.mov
Drawings and animation for the article created using the same tools. In order of appearance in the article: