¿Cómo puedo dibujar texto con diferentes trazos y colores de relleno en imágenes con Python?

¿Cómo puedo dibujar texto con diferentes trazos y colores de relleno en imágenes con Python?

Aquí hay un texto con trazo rojo y relleno gris.

Ejemplo

Intenté hacer esto con PIL pero no había ninguna opción para configurar el color del trazo.

preguntado el 08 de noviembre de 11 a las 11:11

6 Respuestas

Usar El Cairo (con mucho código tomado de aquí):

import cairo

def text_extent(font, font_size, text, *args, **kwargs):
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
    ctx = cairo.Context(surface)
    ctx.select_font_face(font, *args, **kwargs)
    ctx.set_font_size(font_size)
    return ctx.text_extents(text)

text='Example'
font="Sans"
font_size=55.0
font_args=[cairo.FONT_SLANT_NORMAL]
(x_bearing, y_bearing, text_width, text_height,
 x_advance, y_advance) = text_extent(font, font_size, text, *font_args)
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(text_width), int(text_height))
ctx = cairo.Context(surface)
ctx.select_font_face(font, *font_args)
ctx.set_font_size(font_size)
ctx.move_to(-x_bearing, -y_bearing)
ctx.text_path(text)
ctx.set_source_rgb(0.47, 0.47, 0.47)
ctx.fill_preserve()
ctx.set_source_rgb(1, 0, 0)
ctx.set_line_width(1.5)
ctx.stroke()

surface.write_to_png("/tmp/out.png")

enter image description here

respondido 08 nov., 11:22

¿Necesito conocer las dimensiones del texto para usar esto? - Seppo Erviälä

Puedo obtener la información necesaria con Context.text_extents ("Ejemplo") pero después de eso necesito crear una nueva superficie con el ancho y alto correctos. - Seppo Erviälä

Sí, lamentablemente, no conozco una mejor manera. - unutbu

@unutbu, porté su código a C ++ con éxito, pero tuve un problema con la transparencia. puedes comprobarlo aquí Gracias - Prakash M

PIL no admite esto, pero puede fingirlo: renderice el texto cuatro u ocho veces con el color del contorno usando compensaciones de un píxel:

x+1,y
x-1,y
x  ,y+1
x  ,y-1 

(versión cuatro veces)

x+1,y+1
x  ,y+1
x-1,y+1

x+1,y
x-1,y

x+1,y-1
x  ,y-1 
x-1,y-1

(versión ocho veces)

y luego una vez en x,y con el color de relleno.

Respondido el 29 de diciembre de 14 a las 12:12

La versión 8x es básicamente equivalente a renderizar 4 veces solo en las diagonales. - Luke Taylor

@HassanBaig No lo notarás. - Aaron Digulla

Solo funciona si el borde es delgado, por ejemplo, 1 píxel. Si usa un borde más ancho, el texto producido no es tan satisfactorio. - jdhao

Usar ImageMagick:

import subprocess

args = {
    'bgColor': 'transparent',
    'fgColor': 'light slate grey',
    'fgOutlineColor': 'red',
    'text': 'Example',
    'size': 72,
    'geometry': '350x100!',
    'output': '/tmp/out.png',
    'font': 'helvetica'
}

cmd = ['convert', 'xc:{bgColor}', '-resize', '{geometry}', '-gravity', 'Center', 
       '-font', '{font}', '-pointsize', '{size}', '-fill', '{fgColor}', 
       '-stroke', '{fgOutlineColor}', '-draw', "text 0,0 '{text}'", '-trim', '{output}']
cmd = [item.format(**args) for item in cmd]

proc = subprocess.Popen(cmd)
proc.communicate()

enter image description here

contestado el 30 de mayo de 17 a las 23:05

¿Necesito conocer las dimensiones del texto de salida antes de poder crearlo? - Seppo Erviälä

Puede utilizar cualquier geometry que desee, imagemagick creará una imagen de exactamente esa geometría siempre que use el signo de exclamación, pero si la fuente size es demasiado grande, es posible que parte del texto quede recortado. - unutbu

Me gustaría obtener una imagen del tamaño exacto del texto. Sin texto recortado y sin espacio adicional en algún lado. - Seppo Erviälä

Puede recortar al tamaño del texto utilizando el -trim opción. - unutbu

Sería mejor poner el comando y sus opciones en una lista y luego reemplazar las variables en cada elemento. Eso resolvería el problema de los espacios y / o citar caracteres en los argumentos. - Aaron Digulla

Agregaron una solución nativa dentro de Pillow desde 6.2.0: https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.PIL.ImageDraw.ImageDraw.text

Ahora especifica el stroke_fill cuál es el color del contorno y stroke_width que es el grosor del contorno.

respondido 15 mar '20, 19:03

Para actualizar la almohada, pip install -U pillow. Esto funciona para Pillow 7.2. - jdhao

Puede utilizar Inkscape:

import subprocess
subprocess.call("inkscape in.svg --export-text-to-path --export-plain-svg out.svg", shell = True)

nota: tienes que descargar Inkscape para usar esto, por lo que no es práctico para uso permanente

Respondido 03 Oct 15, 20:10

Complementando la respuesta de @Aaron Digulla, así es como falsificas el color del contorno en PIL:

from PIL import Image, ImageDraw, ImageFont

def main():
    text_color = (255, 0, 0)
    outline_color = (0, 0, 255)
    size = (512, 256)
    img = Image.new(mode='RGB', size=size, color=(255, 255, 255))
    font = ImageFont.truetype(font="C:/WINDOWS/Fonts/STKAITI.TTF", size=100)
    drawer = ImageDraw.Draw(img)

    x = 10
    y = 10
    bd_w = 1
    drawer.text((x-bd_w, y), "测试文字", font=font, fill=outline_color)
    drawer.text((x, y-bd_w), "测试文字", font=font, fill=outline_color)
    drawer.text((x+bd_w, y), "测试文字", font=font, fill=outline_color)
    drawer.text((x, y+bd_w), "测试文字", font=font, fill=outline_color)

    drawer.text((x+bd_w, y-bd_w), "测试文字", font=font, fill=outline_color)
    drawer.text((x-bd_w, y-bd_w), "测试文字", font=font, fill=outline_color)
    drawer.text((x-bd_w, y+bd_w), "测试文字", font=font, fill=outline_color)
    drawer.text((x+bd_w, y+bd_w), "测试文字", font=font, fill=outline_color)

    drawer.text((x, y), "测试文字", font=font, fill=text_color)

    img.show()


if __name__ == "__main__":
    main()

Pero tenga en cuenta que este método producirá texto insatisfactorio si bd_w (ancho del borde) se establece un poco más alto. Vea la siguiente imagen para ver el efecto de bd_w:

ancho del borde = 1

enter image description here

ancho del borde = 4

enter image description here

ancho del borde = 7

enter image description here

Parece que para texto grande mantener el ancho del borde por debajo de 4 es aceptable.

respondido 20 nov., 18:13

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.