moviepy
moviepy copied to clipboard
TextClip produces low quality text
I think that the usage of ImageMagick to render texts is a bad option, but maybe I'm wrong and someone could explain me how to achieve good quality texts using TextClip
. To illustrate this, I've done a comparison between gizeh
vs PIL
vs TextClip
:
import math
import gizeh as gz # pip install gizeh
import numpy as np
from moviepy import *
from PIL import Image, ImageFont, ImageDraw
SIZE = (300, 200)
BG_COLOR = (255, 90, 0)
DURATION = 2
FPS = 1
TEXT = "Text"
TEXT_SIZE = (200, 100)
TEXT_POSITION = (0, 0)
TEXT_ROTATION = 45
FONT_SIZE = 50
TEXT_COLOR_RGB = (74, 74, 74)
TEXT_COLOR_HEX = "#4a4a4a"
FONT = "Amiri-Bold"
def create_gizeh_demo():
def create_gizeh_surface():
surface = gz.Surface(
width=SIZE[0],
height=SIZE[1],
bg_color=(BG_COLOR[0] / 255, BG_COLOR[1] / 255, BG_COLOR[2] / 255),
)
text = gz.text(
TEXT,
xy=(
TEXT_SIZE[0] / 2 + TEXT_POSITION[0],
TEXT_SIZE[1] / 2 + TEXT_POSITION[1],
),
fill=(
TEXT_COLOR_RGB[0] / 255,
TEXT_COLOR_RGB[1] / 255,
TEXT_COLOR_RGB[2] / 255,
),
fontfamily=FONT.split("-")[0], # only family
fontsize=FONT_SIZE,
)
text = text.rotate(
-math.radians(TEXT_ROTATION), # Gizeh accepts radians and inversed
center=(
(TEXT_POSITION[0] + SIZE[0]) / 2,
(TEXT_POSITION[1] + SIZE[1]) / 2,
),
)
text.draw(surface)
return surface.get_npimage()
surface = create_gizeh_surface()
text_clip = VideoClip(lambda t: surface, duration=DURATION).with_fps(FPS)
text_clip.size = SIZE
return CompositeVideoClip([text_clip], size=SIZE).with_duration(DURATION)
def create_PIL_demo():
img = Image.new("RGB", size=SIZE, color=BG_COLOR)
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(f"~/.local/share/fonts/{FONT}.ttf", size=FONT_SIZE)
draw.text((50, 50), TEXT, font=font, fill=TEXT_COLOR_RGB)
img = img.rotate(
TEXT_ROTATION,
expand=False,
fillcolor=BG_COLOR,
)
img = np.array(img)
return VideoClip(lambda t: img, duration=DURATION).with_fps(FPS)
def create_TextClip_demo():
bg_clip = ColorClip(size=SIZE, color=BG_COLOR, duration=DURATION).with_position(
TEXT_POSITION
)
text_clip = (
TextClip(
TEXT,
color=TEXT_COLOR_HEX,
size=TEXT_SIZE,
font=FONT,
font_size=FONT_SIZE,
stroke_color=TEXT_COLOR_HEX,
)
.with_position(TEXT_POSITION)
.with_duration(DURATION)
)
text_clip = text_clip.rotate(TEXT_ROTATION, unit="deg", expand=True)
return CompositeVideoClip([bg_clip, text_clip]).with_duration(DURATION)
final = concatenate_videoclips(
[create_gizeh_demo(), create_PIL_demo(), create_TextClip_demo()]
)
final.preview()
Rendered result (MP4)
The result is that Gizeh creates a text with good quality using Cairo (first scene), followed by PIL (second scene) and moviepy using ImageMagick creates a low quality text (third scene).
Specifications
- Python Version: 3.8.5
- Moviepy Version: master
- Platform Name: Ubuntu
- Platform Version: 20.04
- ImageMagick Version: 6.9.10-23
- PIL version: 8.1.0
- cairocffi version: 1.2.0
- gizeh version: 0.1.11
I'm seeing now your implementation of TextClip
using PIL @tburrows13. What do you think that is worth to make?
- Implementation for PIL.
- Implementation for Cairo.
- Remove support for ImageMagick or not?
- Support all 3 providers and create a
provider
argument to select between them?
Indeed. The other alternative to Gizeh is just to use PIL directly. I was working on this before: https://github.com/tburrows13/moviepy/blob/redo-textclip/moviepy/video/NewTextClip.py.
With the following added to your demo (and adjusting the rest of the code to be compatible with ~v2.0.0.dev1 which this fork is currently based off):
def create_PILTextClip_demo():
bg_clip = ColorClip(size=SIZE, color=BG_COLOR, duration=DURATION).set_position(
TEXT_POSITION
)
text_clip = (
NewTextClip(TEXT, color=TEXT_COLOR_HEX, size=TEXT_SIZE, fontsize=FONT_SIZE, bg_color=(0, 0, 0, 0))
.set_position(TEXT_POSITION)
.set_duration(DURATION)
)
text_clip = text_clip.rotate(TEXT_ROTATION, unit="deg", expand=True)
return CompositeVideoClip([bg_clip, text_clip]).set_duration(DURATION)
I get
https://user-images.githubusercontent.com/22625968/105173962-b2fa7300-5b19-11eb-8412-ca5cefc346b1.mp4
Which admittedly looks bad.
Maybe that's just a bug in my implementation though? I've not worked on it for a while. Feel free to take over my effort, or start again from scratch with Gizeh.
The main advantage of getting rid of ImageMagick, besides the poor quality that you've demonstrated, is that it is tricky for users to get setup. Pillow is great because it is already a dependency. As long as Gizeh is pip-installable it shouldn't matter too much adding it as a dependency.
I'm seeing now your implementation of
TextClip
using PIL @tburrows13. What do you think that is worth to make?
- Implementation for PIL.
- Implementation for Cairo.
- Remove support for ImageMagick or not?
- Support all 3 providers and create a
provider
argument to select between them?
Yep, as I noted, I'd like to completely remove ImageMagick (it is only used here and as one of 3 optional gif renderers - I don't think it will be missed). This allows us to remove a lot of the complexity of installation.
Between PIL and Cairo/Gizeh, if PIL can get equivalent results, then it is preferable. If Gizeh is as easy to install as PIL is, then that would be fine as well.
Between PIL and Cairo/Gizeh, if PIL can get equivalent results, then it is preferable. If Gizeh is as easy to install as PIL is, then that would be fine as well.
Perfect :+1: I'm going to try to create the same results with PIL.
Yep, as I noted, I'd like to completely remove ImageMagick (it is only used here and as one of 3 optional gif renderers - I don't think it will be missed). This allows us to remove a lot of the complexity of installation.
I agree completely, that would be the most reasonable approach.
+1 to remove ImageMagick (which was a 10-minute decision I took at the very beginning of the project and am not proud of).
I made a "Gizeh text clip for moviepy" some time ago here. It is much better than ImageMagick, but Cairo can be difficult to install on some machines.
PIL would be the best in terms of user installation experience. But can PIL access any user-installed font easily? Last time I checked (years ago...) it required the complete path to a font file.
You can see that I changed some of the more niche parameters in the PIL TextClip to align with what got passed to PIL. I think that that is fine. It makes sense to align the TextClip parameters with whatever is being used to underly it.
Maybe that's just a bug in my implementation though? I've not worked on it for a while. Feel free to take over my effort, or start again from scratch with Gizeh.
I've updated the issue description with the complete example: Gizeh vs PIL vs TextClip. It seems that, as @Zulko said, Cairo render with best quality.
I made a "Gizeh text clip for moviepy" some time ago here. It is much better than ImageMagick, but Cairo can be difficult to install on some machines.
It seems that Pycairo provides a wheel with cairo 1.17.2 included for Windows, so I think that we can go with it. In other systems the installation is easy.
PIL would be the best in terms of user installation experience. But can PIL access any user-installed font easily? Last time I checked (years ago...) it required the complete path to a font file.
Matplotlib has a multiplatform system fonts discovering feature that we could reuse in the case of providing a PIL-based TextClip.
I made a "Gizeh text clip for moviepy" some time ago here. It is much better than ImageMagick, but Cairo can be difficult to install on some machines.
I've integrated your example inside a TextClip
version removing all the gizeh stuff and using pycairo directly (is the same API as cairocffi used by gizeh), and works like a charm but results in crazy unmaintanable code. Would you consider a pull to replace cairocffi dependency by pycairo in gizeh @Zulko? Keep in mind that the benefits are:
- Removing the need of install CFFI (difficult in Windows platforms).
- Adding standalone support for Windows thanks to prebuilt dll included in the pycairo Windows wheel.
With that I could create a TextClip
Cairo based in your version using Gizeh maintaining a reasonable code base, adding gizeh as a direct dependency of moviepy.
If you manage that I would vote for it (@tburrows13 what do you think?). Note that this would require some work to update the scripts that use the current ImageMagick-based text API, but that's allowed by the major version bump in v2.0.
Yep. As long as it is pip-installable (which the current ImageMagick solution isn't), then I'm very happy. Breaking changes are fine, so presumably make the TextClip parameters fit as neatly around the underlying library possible.
@tburrows13 While trying your PIL-based TextClip you linked here, I found that the default bg_color="transparent"
causes an error with PIL:
ValueError: unknown color specifier: 'transparent'
Otherwise it works fine 👍
good solutions.