Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextClip produces low quality text #1472

Closed
mondeja opened this issue Jan 20, 2021 · 14 comments
Closed

TextClip produces low quality text #1472

mondeja opened this issue Jan 20, 2021 · 14 comments
Labels
bug Issues that report (apparent) bugs. images Related to ImageClip, or handling of images in general. lib-ImageMagick Issues pertaining to dependency ImageMagick.

Comments

@mondeja
Copy link
Collaborator

mondeja commented Jan 20, 2021

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)

cairo_pil_imagemagick

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
@mondeja mondeja added the bug Issues that report (apparent) bugs. label Jan 20, 2021
@mondeja mondeja added lib-ImageMagick Issues pertaining to dependency ImageMagick. images Related to ImageClip, or handling of images in general. labels Jan 20, 2021
@mondeja
Copy link
Collaborator Author

mondeja commented Jan 20, 2021

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?

@tburrows13
Copy link
Collaborator

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

textdemo.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.

@tburrows13
Copy link
Collaborator

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.

@mondeja
Copy link
Collaborator Author

mondeja commented Jan 20, 2021

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 👍 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.

@Zulko
Copy link
Owner

Zulko commented Jan 20, 2021

+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.

@tburrows13
Copy link
Collaborator

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.

@mondeja
Copy link
Collaborator Author

mondeja commented Jan 20, 2021

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.

@mondeja
Copy link
Collaborator Author

mondeja commented Jan 21, 2021

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.

@Zulko
Copy link
Owner

Zulko commented Jan 21, 2021

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.

@tburrows13
Copy link
Collaborator

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.

@michaelosthege
Copy link

michaelosthege commented Aug 12, 2021

@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 👍

@KehaoWu
Copy link

KehaoWu commented Jun 16, 2022

good solutions.

@claell
Copy link

claell commented Dec 9, 2024

I think, v2 now uses PIL? Or am I wrong? Does that mean, this issue can be closed? However, I still seem to get problems in certain circumstances (also see #1395, #195).

@OsaAjani
Copy link
Collaborator

Thank you for your contributions and for reporting issues in this repository. With the release of v2, which introduces significant changes to the codebase and API, we’ve reviewed the backlog of open PRs and issues. Due to the length of the backlog and the likelihood that many of these are either fixed or no longer applicable, we’ve made the decision to close all previous PRs and issues.

If you believe that any of these are still relevant to the current version or if you'd like to reopen a related discussion, please feel free to create a new issue or pull request, referencing the old one.

Thank you for your understanding and continued support!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issues that report (apparent) bugs. images Related to ImageClip, or handling of images in general. lib-ImageMagick Issues pertaining to dependency ImageMagick.
Projects
None yet
Development

No branches or pull requests

7 participants