Draw text on an angle (rotated) in Python

前端 未结 2 735
日久生厌
日久生厌 2021-01-13 12:27

I am drawing text onto a numpy array image in Python (using a custom font). Currently I am converting the image to PIL, drawing the text and then converting back to a numpy

相关标签:
2条回答
  • 2021-01-13 12:59

    You can use PIL to draw rotated text. I suggest drawing the text onto a blank image, rotating that image, and then pasting the rotated image into the main image. Something like:

    Code:

    def draw_rotated_text(image, angle, xy, text, fill, *args, **kwargs):
        """ Draw text at an angle into an image, takes the same arguments
            as Image.text() except for:
    
        :param image: Image to write text into
        :param angle: Angle to write text at
        """
        # get the size of our image
        width, height = image.size
        max_dim = max(width, height)
    
        # build a transparency mask large enough to hold the text
        mask_size = (max_dim * 2, max_dim * 2)
        mask = Image.new('L', mask_size, 0)
    
        # add text to mask
        draw = ImageDraw.Draw(mask)
        draw.text((max_dim, max_dim), text, 255, *args, **kwargs)
    
        if angle % 90 == 0:
            # rotate by multiple of 90 deg is easier
            rotated_mask = mask.rotate(angle)
        else:
            # rotate an an enlarged mask to minimize jaggies
            bigger_mask = mask.resize((max_dim*8, max_dim*8),
                                      resample=Image.BICUBIC)
            rotated_mask = bigger_mask.rotate(angle).resize(
                mask_size, resample=Image.LANCZOS)
    
        # crop the mask to match image
        mask_xy = (max_dim - xy[0], max_dim - xy[1])
        b_box = mask_xy + (mask_xy[0] + width, mask_xy[1] + height)
        mask = rotated_mask.crop(b_box)
    
        # paste the appropriate color, with the text transparency mask
        color_image = Image.new('RGBA', image.size, fill)
        image.paste(color_image, mask)
    

    How does it work:

    1. Create a transparency mask.
    2. Draw the text onto the mask.
    3. Rotate the mask, and crop to proper size.
    4. Paste the desired color into the image, using the rotated transparency mask containing the text.

    Test Code:

    import numpy as np
    
    from PIL import Image
    from PIL import ImageDraw
    from PIL import ImageFont
    
    char_image = np.zeros((100, 150, 3), np.uint8)
    
    # convert to pillow image
    pillowImage = Image.fromarray(char_image)
    
    # draw the text
    font = ImageFont.truetype("arial.ttf", 32)
    draw_rotated_text(pillowImage, 35, (50, 50), 'ABC', (128, 255, 128), font=font)
    
    pillowImage.show()
    

    Results:

    0 讨论(0)
  • 2021-01-13 13:02

    Using matplotlib, first visualize array and draw on it, get the raw data from the figure back. Pro: both tools are quite high level and let you deal with many details of the process. ax.annotate() offers flexibility for where and how to draw and set font properties, and plt.matshow() offers flexibility that lets you deal with aspects of array visualization.

    import matplotlib.pyplot as plt
    import scipy as sp
    
    # make Data array to draw in
    M = sp.zeros((500,500))
    
    dpi = 300.0
    
    # create a frameless mpl figure
    fig, axes = plt.subplots(figsize=(M.shape[0]/dpi,M.shape[1]/dpi),dpi=dpi)
    axes.axis('off')
    fig.subplots_adjust(bottom=0,top=1.0,left=0,right=1)
    axes.matshow(M,cmap='gray')
    
    # set custom font
    import matplotlib.font_manager as fm
    ttf_fname = '/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf'
    prop = fm.FontProperties(fname=ttf_fname)
    
    # annotate something
    axes.annotate('ABC',xy=(250,250),rotation=45,fontproperties=prop,color='white')
    
    # get fig image data and read it back to numpy array
    fig.canvas.draw()
    w,h = fig.canvas.get_width_height()
    Imvals = sp.fromstring(fig.canvas.tostring_rgb(),dtype='uint8')
    ImArray = Imvals.reshape((w,h,3))
    

    0 讨论(0)
提交回复
热议问题