Skip to content

Instantly share code, notes, and snippets.

@Zulko
Created February 18, 2017 22:02
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Zulko/e072d78dd5dbd2458f34d2166265e081 to your computer and use it in GitHub Desktop.
Save Zulko/e072d78dd5dbd2458f34d2166265e081 to your computer and use it in GitHub Desktop.
"""
An alternative text clip for Moviepy, relying on Gizeh instead of ImageMagick
Advantages:
- Super fast (20x faster)
- no need to install imagemagick
- full-vector graphic, no aliasing problems
- Easier font names
Disadvantages:
- Requires Cairo installed
- Doesnt support kerning (=letters spacing)
"""
try:
import gizeh as gz
GIZEH_AVAILABLE = True
except ImportError:
GIZEH_AVAILABLE = False
import numpy as np
from moviepy.editor import ImageClip
def autocrop(np_img):
"""Return the numpy image without empty margins."""
if len(np_img.shape) == 3:
if np_img.shape[2] == 4:
thresholded_img = np_img[:,:,3] # use the mask
else:
thresholded_img = np_img.max(axis=2) # black margins
zone_x = thresholded_img.max(axis=0).nonzero()[0]
xmin, xmax = zone_x[0], zone_x[-1]
zone_y = thresholded_img.max(axis=1).nonzero()[0]
ymin, ymax = zone_y[0], zone_y[-1]
return np_img[ymin:ymax+1, xmin:xmax+1]
def text_clip(text, font_family, align='left',
font_weight='normal', font_slant='normal',
font_height = 70, font_width = None,
interline= None, fill_color=(0,0,0),
stroke_color=(0, 0, 0), stroke_width=2,
bg_color=None):
"""Return an ImageClip displaying a text.
Parameters
----------
text
Any text, possibly multiline
font_family
For instance 'Impact', 'Courier', whatever is installed
on your machine.
align
Text alignment, either 'left', 'center', or 'right'.
font_weight
Either 'normal' or 'bold'.
font_slant
Either 'normal' or 'oblique'.
font_height
Eight of the font in pixels.
font_width
Maximal width of a character. This is only used to
create a surface large enough for the text. By
default it is equal to font_height. Increase this value
if your text appears cropped horizontally.
interline
number of pixels between two lines. By default it will be
stroke_width
Width of the letters' stroke in pixels.
stroke_color
For instance (0,0,0) for black stroke or (255,255,255)
for white.
fill_color=(0,0,0),
For instance (0,0,0) for black letters or (255,255,255)
for white.
bg_color
The background color in RGB or RGBA, e.g. (255,100,230)
(255,100,230, 128) for semi-transparent. If left to none,
the background is fully transparent
"""
if not GIZEH_AVAILABLE:
raise ImportError("`text_clip` requires Gizeh installed.")
stroke_color = np.array(stroke_color)/255.0
fill_color = np.array(fill_color)/255.0
if bg_color is not None:
np.array(bg_color)/255.0
if font_width is None:
font_width = font_height
if interline is None:
interline = 0.3 * font_height
line_height = font_height + interline
lines = text.splitlines()
max_line = max(len(l) for l in lines)
W = int(max_line * font_width + 2 * stroke_width)
H = int(len(lines) * line_height + 2 * stroke_width)
surface = gz.Surface(width=W, height=H, bg_color=bg_color)
xpoint = {
'center': W/2,
'left': stroke_width + 1,
'right': W - stroke_width - 1
}[align]
for i, line in enumerate(lines):
ypoint = (i+1) * line_height
text_element = gz.text(line, fontfamily=font_family, fontsize=font_height,
h_align=align, v_align='top',
xy=[xpoint, ypoint], fontslant=font_slant,
stroke=stroke_color, stroke_width=stroke_width,
fill=fill_color)
text_element.draw(surface)
cropped_img = autocrop(surface.get_npimage(transparent=True))
return ImageClip(cropped_img)
# Example
clip = text_clip("This is\na multiline\ntext.", font_family="impact",
fill_color=(100,155,255))
clip.ipython_display()
@tamassengel
Copy link

Great stuff, works perfectly! Thanks!

@Zulko
Copy link
Author

Zulko commented Feb 16, 2021

Thanks, I believe there are plans to replace ImageMagick textclips by this new approach in MoviePy 2.0

@AhmedElwerdany
Copy link

thanks dude.
I have a small problem but i can't Identify what's wrong.
Screenshot from 2022-09-28 15-51-19
I Keep getting that extra space .

here's the code :

text_clip("Hello world",
        font_height=30,
        bg_color=(255,255,255),
        font_family="Ubuntu")

I tried to remove the autocrop function, and i got the same result.
tried to play with it before deleting it, but I got a random results ( I have no experience in numpy )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment