脚本-在模板图片上指定位置写字和贴图(Python+Pillow)

脚本的功能是可以在一张模板图片上贴图和写字,效果如下:
模板图和结果图


模板图和结果图

其中使用了Pilow模块6.2.1版本,代码如下

from PIL import Image, ImageDraw, ImageFont


class WritingAndPastingPicturesOnModels(object):

    def __init__(self, model, all_draw_info):
        """
        往图片上写字或者粘贴子图片
        :param model: 模型图片 路径名称或者PIL.Image.Image对象
        :param all_draw_info: 绘制信息,list 格式为
            [draw_info1,draw_info2, ...]
            其中draw_info为字典格式,其必有字段
                draw_type: int 绘制类型, 0 文本绘制 1 图片粘贴
                coordinate: list 粘贴坐标
            当draw_type=0时,其还有以下参数:
                color: (R, G ,B) 文本颜色信息
                font_size: int 字体大小
                font_path: str 字体路径
                text: str 要写文本
                newline: list 可选,当需要多行处理时,此参数要有意义,其值类型为[最大宽度, 行间距]
                stroke_width: int 可选,笔触放大倍数,默认为0
            当draw_type=1时,其还有以下参数:
                image: 需要粘贴的图片信息 路径名称或者PIL.Image.Image对象
                size: list 可选,需要粘贴的图片的尺寸,会进行缩放
        """

        self.model_image = self._get_image(model)
        self.__fonts = {}
        self.all_draw_info = all_draw_info

    def _get_image(self, image):
        """
        获取图片
        :param image:可以是本地文件路径, Image对象
        :return:
        """
        if isinstance(image, str):
            image = Image.open(image)
        elif not isinstance(image, Image.Image):
            raise Exception('未知文件类型 {}'.format(type(image)))

        return image

    def draw(self):
        for draw_info in self.all_draw_info:
            draw_type = draw_info['draw_type']  # 绘制类型, 0 文本绘制 1 图片粘贴
            assert draw_type in (0, 1)
            coordinate = draw_info['coordinate']
            if draw_type == 0:
                color = draw_info['color']
                font_size = draw_info['font_size']
                font_path = draw_info['font_path']
                text = draw_info['text']
                newline = draw_info.get('newline')
                stroke_width = draw_info.get('stroke_width', 0)
                self.write_on_picture(coordinate, color, font_size, font_path, text, newline, stroke_width)
            else:
                image = self._get_image(draw_info['image'])
                size = draw_info.get('size')
                self.picture_paste(coordinate, image, size)
        return self.model_image

    def __picture_paste_model_mask_texture_mask(self, model_rgba, texture_rgba, coordinate):
        """
        模型和贴图都是透明情况
        :param model_rgba:
        :param texture_rgba:
        :param coordinate:
        :return:
        """
        model_r, model_g, model_b, model_mask = model_rgba
        model_rgb = Image.merge('RGB', (model_r, model_g, model_b))
        tem_model = self._picture_paste_model_texture_mask(model_rgb, texture_rgba, coordinate)
        tem_model_r, tem_model_g, tem_model_b = tem_model.split()

        return Image.merge('RGBA', (tem_model_r, tem_model_g, tem_model_b, model_mask))

    def _picture_paste_model_mask_texture(self, model_rgba, texture_rgb, coordinate):
        """
        模型是透明但贴图不透明
        :param model_rgba:
        :param texture_rgb:
        :param coordinate:
        :return:
        """
        model_r, model_g, model_b, model_mask = model_rgba

        texture_r, texture_g, texture_b = texture_rgb
        model_r.paste(texture_r, coordinate)
        model_g.paste(texture_g, coordinate)
        model_b.paste(texture_b, coordinate)

        return Image.merge('RGBA', (model_r, model_g, model_b, model_mask))

    def _picture_paste_model_texture_mask(self, model, texture_rgba, coordinate):
        """
        模型是不透明贴图是透明的情况处理
        :param model:
        :param texture_rgba:
        :param coordinate:
        :return:
        """
        texture_r, texture_g, texture_b, texture_mask = texture_rgba
        fore = Image.merge('RGB', (texture_r, texture_g, texture_b))
        mask_model = Image.new('L', model.size, 0)
        fore_model = Image.new('RGB', model.size, 0)
        fore_model.paste(fore, coordinate)
        mask_model.paste(texture_mask, coordinate)
        return Image.composite(fore_model, model, mask_model)

    def picture_paste(self, coordinate, image, size=None):
        """
        将image图片粘贴到图像中
        :param coordinate: 粘贴坐标
        :param image: 粘贴图片
        :param size: 待粘贴图片的大小
        :return:
        """
        if size:
            image = image.resize(size)

        model_rgbm = self.model_image.split()

        rgbm = image.split()
        if len(rgbm) == 3 and len(model_rgbm) == 3:
            self.model_image.paste(image, coordinate)

        elif len(model_rgbm) == 4 and len(rgbm) == 4:
            self.model_image = self.__picture_paste_model_mask_texture_mask(model_rgbm, rgbm, coordinate)

        elif len(rgbm) == 4:
            self.model_image = self._picture_paste_model_texture_mask(self.model_image, rgbm, coordinate)
        else:
            self.model_image = self._picture_paste_model_mask_texture(model_rgbm, rgbm, coordinate)

    def write_on_picture(self, coordinate, color, font_size, font_path, text, newline=None, stroke_width=1):
        """
        图上写字
        :param coordinate:(x,y),起始点的X Y坐标
        :param color: 颜色
        :param font_size: 字体大小
        :param font_path: 字体路径
        :param text: 字体格式
        :param newline: 换行信息,若为None则不换行.[最大宽度(整数), 行间距(整数)]
        :return:
        """
        if isinstance(color, list):
            color = tuple(color)

        if (font_path, font_size) not in self.__fonts:
            newfont = ImageFont.truetype(font_path, font_size)
            self.__fonts[(font_path, font_size)] = newfont

        font = self.__fonts[(font_path, font_size)]
        draw = ImageDraw.Draw(self.model_image)
        if newline:
            # 多行处理
            new_text = self._count_multipleLines(text, draw, font, newline[0])
            draw.multiline_text(coordinate, new_text, fill=color, font=font, spacing=newline[1],
                                stroke_width=stroke_width)
        else:
            draw.text(coordinate, text, fill=color, font=font, stroke_width=stroke_width)

    def _count_multipleLines(self, text, draw, font, max_width):
        """
        计算处理多行字符
        """
        sum_width = 0
        new_text = ''
        for c in text:
            w = draw.textsize(c, font=font)
            sum_width += w[1]
            if sum_width > max_width:
                new_text += '\n'
                sum_width = 0
            new_text += c

        return new_text

if __name__ == "__main__":
    # 模板路径,当前路径下需要有此文件
    model_path = 'message_card_model.png'

    # 绘制参数,其中有'head.png'需要放到当前目录下
    conf = [{'color': [50, 50, 50], 'draw_type': 0, 'font_path': 'simhei.ttf', 'font_size': 80, 'coordinate': [70, 100],
             'stroke_width': 1, 'text': '星期一'},
            {'color': [50, 50, 50], 'draw_type': 0, 'font_path': 'simhei.ttf', 'font_size': 80, 'coordinate': [70, 200],
             'stroke_width': 1, 'text': '12月19号'}, {'draw_type': 1, 'coordinate': [70, 630], 'image': 'head.png'},
            {'color': [50, 50, 50], 'draw_type': 0, 'font_path': 'simhei.ttf', 'font_size': 50,
             'coordinate': [240, 655], 'text': 'Jack.Ma'},
            {'color': [50, 50, 50], 'newline': [800, 18], 'draw_type': 0, 'font_path': 'simhei.ttf', 'font_size': 40,
             'coordinate': [70, 800], 'text': '啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦'}]

    wppm = WritingAndPastingPicturesOnModels(
        model_path,
        conf
    )
    image = wppm.draw()
    image.show()

这是上面示例中用到的head.pngmessage_card_model.png文件

message_card_model.png

head.png

你可能感兴趣的:(脚本-在模板图片上指定位置写字和贴图(Python+Pillow))