likes
comments
collection
share

【Python 实战】---- 绘制透明背景方形印章

作者站长头像
站长
· 阅读数 39

1. 目标效果

【Python 实战】---- 绘制透明背景方形印章

2. 分析实现

  1. 可以使用 opencv 或者 PIL 进行绘制实现,但是由于 opencv 不能绘制中文,因此采用 PIL 进行印章制作;
  2. 先绘制边框和隔离线;
  3. 再绘制文字;
  4. 最后给印章添加纹理。

3. 绘制边框

3.1 绘制边框分析

  1. 通过 draw.rectangle 实现公用的矩形的绘制;
  2. 获取图像长宽 image.size;
  3. 计算各个矩形的起始点【三分之一宽、二分之一高】;
  4. 通过一个矩形,将印章分为上下两个矩形【绘制上下矩形, 注意边框占位一半】;
  5. 通过上边三分之一个矩形将上侧矩形平均分三份【绘制上侧中间矩形】;
  6. 绘制下侧右边的小矩形【绘制下侧右边的矩形】上下一起绘制;
  7. 绘制印章完整边框【绘制边框】。

3.2 实现代码

  # 绘制印章的格子
  def draw_seal_box(self):
    # 获取图像长宽
    w, h = self.image.size
    # 计算各个矩形的起始点
    # 三分之一宽
    third_w = w / 3
    # 二分之一高
    half_h = h / 2
    # 绘制下侧右边的矩形
    self.draw_rect([third_w * 2 - 5,0,w,h], 6)
    # 绘制上侧中间矩形
    self.draw_rect([third_w,0,third_w * 2,half_h], 6)
    # 绘制上下矩形, 注意边框占位一半
    self.draw_rect([0,0,w,half_h], 6)
    # 绘制边框
    self.draw_rect([0,0,w,h])

  # 绘制矩形
  def draw_rect(self, shape, width=8):
    self.draw.rectangle(shape, fill=None, outline=self.border_color, width=width)

3.3 实现结果

【Python 实战】---- 绘制透明背景方形印章

4. 绘制文字

4.1 绘制文字分析

  1. 绘制文字公用方法封装; 1.1 计算传入文本的占位的盒子宽高【textbbox】; 1.2 根据文字的占位,计算绘制文本【text】;
  2. 获取图像长宽【image.size】;
  3. 计算各个文本的中心点坐标【六分之一宽、四分之一高】;
  4. 文本、数字、英文对应的font;
  5. 获取传入的字段;
  6. 依次绘制对应的文本。

4.2 文字绘制

  # 绘制文本
  def draw_text(self, text, font, x, y):
    # 计算传入文本的占位的盒子宽高
    x1, y1, x2, y2 = self.draw.textbbox((x, y), text, font)
    text_size_x = x2 - x1
    text_size_y = y2 - y1
    # 根据文字的占位,计算绘制文本
    self.draw.text(
      (x - text_size_x / 2, y - text_size_y / 2), 
      text, 
      font=font, 
      fill=self.text_color
    )

4.3 印章文本绘制

  # 绘制印章中的文字
  def draw_seal_text(self, opts = {}):
    # 获取图像长宽
    w, h = self.image.size
    # 计算各个矩形的起始点
    # 六分之一宽
    half_third_w = w / 6
    # 四分之一高
    half_half_h = h / 4
    font_size = int(half_half_h)
    # 文本、数字、英文对应的font
    font = ImageFont.truetype('./lib/font/FZLTZHJW.TTF',font_size)
    font_num = ImageFont.truetype('./lib/font/HelveticaNeueLTPro-Lt.ttf',font_size)
    font_num_en = ImageFont.truetype('./lib/font/PingFang Medium.ttf',font_size)
    # 获取传入的字段
    name = opts.get("name")
    days = opts.get("days")
    money = opts.get("money")
    percent = opts.get("percent")
    time = opts.get("time")
    # 依次绘制对应的文本
    self.draw_text(name, font, half_third_w * 2, half_half_h * 3)
    self.draw_text(days, font_num, half_third_w * 5, half_half_h)
    self.draw_text(money, font_num, half_third_w * 3, half_half_h)
    self.draw_text(percent, font_num, half_third_w, half_half_h)
    self.draw_text(time, font_num, half_third_w * 5, half_half_h * 3)

4.4 绘制结果

【Python 实战】---- 绘制透明背景方形印章

5. 添加纹理

5.1 实现分析

  1. 获取纹理图像;
  2. 随机截取一部分纹理图;
  3. 重新设置截取图片的的大小,并进行一次高斯模糊;
  4. 将纹理图的灰度映射到原图的透明度,由于纹理图片自带灰度,映射后会有透明效果,所以fill的透明度不能太低;
  5. 进行一次高斯模糊,提高真实度。

5.2 实现代码

  # 给印章添加纹理
  def add_texture_to_image(self):
    # 打开纹理图像,随机截取旋转
    img_wl = Image.open('./lib/icon/bg.png')
    # 随机截取一部分纹理图
    pos_random = (randint(0,220), randint(0,340))
    box = (pos_random[0], pos_random[1], pos_random[0]+180, pos_random[1]+60)
    img_wl_random = img_wl.crop(box)
    # 重新设置截取图片的的大小,并进行一次高斯模糊
    img_wl_random = img_wl_random.resize(self.image.size).convert('L').filter(ImageFilter.GaussianBlur(1))
    # 将纹理图的灰度映射到原图的透明度,由于纹理图片自带灰度,映射后会有透明效果,所以fill的透明度不能太低
    L, H = self.image.size
    for h in range(H):
      for l in range(L):
        dot = (l, h)
        color = self.image.getpixel(dot)[:3]+(int(img_wl_random.getpixel(dot)/255*self.image.getpixel(dot)[3]),)
        self.image.putpixel(dot, color)
    # 进行一次高斯模糊,提高真实度
    self.image = self.image.filter(ImageFilter.GaussianBlur(0.6))

5.3 实现结果

【Python 实战】---- 绘制透明背景方形印章

6. 完整代码


import os
import openpyxl
from random import randint
from PIL  import Image
from PIL  import ImageFont
from PIL  import ImageDraw
from PIL  import ImageFilter

class Seal:
  def __init__(self):
    # 印章边框颜色
    self.border_color = (234, 24, 27, 180)
    # 印章文本颜色
    self.text_color = (234, 24, 27, 180)
    # 印章背景颜色
    self.bg_color = (255,255,255,1)
    # 保存印章的图像对象
    self.image = None
    # 印章图像绘制对象
    self.draw = None

  # 创建印章
  def create_seal(self, opts = {}):
    # 印章大小
    base = [950,240]
    # 创建印章图像
    self.image = Image.new('RGBA',tuple(base),self.bg_color)
    self.draw = ImageDraw.Draw(self.image)
    # 绘制印章格子
    self.draw_seal_box()
    # 绘制印章文字
    self.draw_seal_text(opts)
    # 添加纹理
    self.add_texture_to_image()

  # 绘制印章的格子
  def draw_seal_box(self):
    # 获取图像长宽
    w, h = self.image.size
    # 计算各个矩形的起始点
    # 三分之一宽
    third_w = w / 3
    # 二分之一高
    half_h = h / 2
    # 绘制下侧右边的矩形
    self.draw_rect([third_w * 2 - 5,0,w,h], 6)
    # 绘制上侧中间矩形
    self.draw_rect([third_w,0,third_w * 2,half_h], 6)
    # 绘制上下矩形, 注意边框占位一半
    self.draw_rect([0,0,w,half_h], 6)
    # 绘制边框
    self.draw_rect([0,0,w,h])

  # 绘制印章中的文字
  def draw_seal_text(self, opts = {}):
    # 获取图像长宽
    w, h = self.image.size
    # 计算各个文本的中心点坐标
    # 六分之一宽
    half_third_w = w / 6
    # 四分之一高
    half_half_h = h / 4
    font_size = int(half_half_h)
    # 文本、数字、英文对应的font
    font = ImageFont.truetype('./lib/font/FZLTZHJW.TTF',font_size)
    font_num = ImageFont.truetype('./lib/font/HelveticaNeueLTPro-Lt.ttf',font_size)
    font_num_en = ImageFont.truetype('./lib/font/PingFang Medium.ttf',font_size)
    # 获取传入的字段
    name = opts.get("name")
    days = opts.get("days")
    money = opts.get("money")
    percent = opts.get("percent")
    time = opts.get("time")
    # 依次绘制对应的文本
    self.draw_text(name, font, half_third_w * 2, half_half_h * 3)
    self.draw_text(days, font_num, half_third_w * 5, half_half_h)
    self.draw_text(money, font_num, half_third_w * 3, half_half_h)
    self.draw_text(percent, font_num, half_third_w, half_half_h)
    self.draw_text(time, font_num, half_third_w * 5, half_half_h * 3)

  # 给印章添加纹理
  def add_texture_to_image(self):
    # 打开纹理图像,随机截取旋转
    img_wl = Image.open('./lib/icon/bg.png')
    # 随机截取一部分纹理图
    pos_random = (randint(0,220), randint(0,340))
    box = (pos_random[0], pos_random[1], pos_random[0]+180, pos_random[1]+60)
    img_wl_random = img_wl.crop(box)
    # 重新设置截取图片的的大小,并进行一次高斯模糊
    img_wl_random = img_wl_random.resize(self.image.size).convert('L').filter(ImageFilter.GaussianBlur(1))
    # 将纹理图的灰度映射到原图的透明度,由于纹理图片自带灰度,映射后会有透明效果,所以fill的透明度不能太低
    L, H = self.image.size
    for h in range(H):
      for l in range(L):
        dot = (l, h)
        color = self.image.getpixel(dot)[:3]+(int(img_wl_random.getpixel(dot)/255*self.image.getpixel(dot)[3]),)
        self.image.putpixel(dot, color)
    # 进行一次高斯模糊,提高真实度
    self.image = self.image.filter(ImageFilter.GaussianBlur(0.6))

  # 测试使用查看当前图片绘制情况
  def show(self):
    self.image.show()

  # 绘制矩形
  def draw_rect(self, shape, width=8):
    self.draw.rectangle(shape, fill=None, outline=self.border_color, width=width)

  # 绘制文本
  def draw_text(self, text, font, x, y):
    # 计算传入文本的占位的盒子宽高
    x1, y1, x2, y2 = self.draw.textbbox((x, y), text, font)
    text_size_x = x2 - x1
    text_size_y = y2 - y1
    # 根据文字的占位,计算绘制文本
    self.draw.text(
      (x - text_size_x / 2, y - text_size_y / 2), 
      text, 
      font=font, 
      fill=self.text_color
    )


if __name__ == "__main__":
  seal = Seal()
  seal.create_seal({
    "name": "美邦服饰",
    "days": "89",
    "money": "40114.82",
    "percent": "24.59",
    "time": "20210331"
  })
  seal.show()

7. 读取 xlsx 文件

7.1 代码分析

  1. 获取路径下的所有 excel 表;
  2. 获取 path 路径下的所有文件和文件夹名称;
  3. 筛选文件列表中的 xlsx 后缀的文件名称;
  4. 返回筛选结果;
  5. 初始化印章类【Seal】;
  6. 循环获取 xlsx 中的内容。

7.2 代码实现

# 获取路径下的所有 excel 表
def get_all_xlsx(path):
  # 获取 path 路径下的所有文件和文件夹名称
  names = os.listdir(path)
  # 筛选文件列表中的 xlsx 后缀的文件名称
  xlsx_names = list(filter(lambda x : x.split('.').pop() in ['xlsx'], names))
  # 返回筛选结果
  return xlsx_names

# 循环读取 xlsx 文件
def loop_read_xlsx_file(path, names):
  seal = Seal()
  for name in names:
    read_xlsx_data(f'{path}/{name}', seal)

8. 批量生成印章

8.1 实现分析

  1. 读取 xlsx 文件中的数据;
  2. 获取印章文件名以及印章中的文本字段;
  3. 判断印章需要的文字是否都存在;
  4. 去掉字段中印章不需要的符号或者文本;
  5. 创建印章,调用【Seal】的生成印章方法;
  6. 将生成的印章进行保存。

8.2 实现代码

# 读取 xlsx 文件中的文本字段
def read_xlsx_data(path, seal):
  # 读取 xlsx 文件中的数据
  wb = openpyxl.load_workbook(path,data_only=True)
  sheets = wb.get_sheet_names()
  sheet_first = sheets[0]
  ws = wb.get_sheet_by_name(sheet_first)
  for r in range(2,ws.max_row + 1):
    # 获取印章文件名以及印章中的文本字段
    name = ws.cell(row=r, column=1).value
    days = ws.cell(row=r, column=2).value
    money = ws.cell(row=r, column=3).value
    percent = ws.cell(row=r, column=4).value
    time = ws.cell(row=r, column=5).value
    # print(name, days, money, percent, time)
    if name and days and money and percent and time:
      # 创建印章
      name = name.replace("*", "")
      money = money.replace("+", "").replace("-", "").replace(",", "")
      percent = percent.replace("+", "").replace("-", "").replace("%", "")
      time = time.replace("清仓", "")
      seal.create_seal({
        "name": name,
        "days": days,
        "money": money,
        "percent": percent,
        "time": time
      })
      seal.save(f'./out_images/{name}{time}.png')

8.3 生成结果

【Python 实战】---- 绘制透明背景方形印章

9. 加入批量操作的全部代码

import os
import openpyxl
from random import randint
from PIL  import Image
from PIL  import ImageFont
from PIL  import ImageDraw
from PIL  import ImageFilter

class Seal:
  def __init__(self):
    # 印章边框颜色
    self.border_color = (234, 24, 27, 180)
    # 印章文本颜色
    self.text_color = (234, 24, 27, 180)
    # 印章背景颜色
    self.bg_color = (255,255,255,1)
    # 保存印章的图像对象
    self.image = None
    # 印章图像绘制对象
    self.draw = None

  # 创建印章
  def create_seal(self, opts = {}):
    # 印章大小
    base = [950,240]
    # 创建印章图像
    self.image = Image.new('RGBA',tuple(base),self.bg_color)
    self.draw = ImageDraw.Draw(self.image)
    # 绘制印章格子
    self.draw_seal_box()
    # 绘制印章文字
    self.draw_seal_text(opts)
    # 添加纹理
    self.add_texture_to_image()

  # 绘制印章的格子
  def draw_seal_box(self):
    # 获取图像长宽
    w, h = self.image.size
    # 计算各个矩形的起始点
    # 三分之一宽
    third_w = w / 3
    # 二分之一高
    half_h = h / 2
    # 绘制下侧右边的矩形
    self.draw_rect([third_w * 2 - 5,0,w,h], 6)
    # 绘制上侧中间矩形
    self.draw_rect([third_w,0,third_w * 2,half_h], 6)
    # 绘制上下矩形, 注意边框占位一半
    self.draw_rect([0,0,w,half_h], 6)
    # 绘制边框
    self.draw_rect([0,0,w,h])

  # 绘制印章中的文字
  def draw_seal_text(self, opts = {}):
    # 获取图像长宽
    w, h = self.image.size
    # 计算各个文本的中心点坐标
    # 六分之一宽
    half_third_w = w / 6
    # 四分之一高
    half_half_h = h / 4
    font_size = int(half_half_h)
    # 文本、数字、英文对应的font
    font = ImageFont.truetype('./lib/font/FZLTZHJW.TTF',font_size)
    font_num = ImageFont.truetype('./lib/font/HelveticaNeueLTPro-Lt.ttf',font_size)
    font_num_en = ImageFont.truetype('./lib/font/PingFang Medium.ttf',font_size)
    # 获取传入的字段
    name = opts.get("name")
    days = opts.get("days")
    money = opts.get("money")
    percent = opts.get("percent")
    time = opts.get("time")
    # 依次绘制对应的文本
    self.draw_text(name, font, half_third_w * 2, half_half_h * 3)
    self.draw_text(days, font_num, half_third_w * 5, half_half_h)
    self.draw_text(money, font_num, half_third_w * 3, half_half_h)
    self.draw_text(percent, font_num, half_third_w, half_half_h)
    self.draw_text(time, font_num, half_third_w * 5, half_half_h * 3)

  # 给印章添加纹理
  def add_texture_to_image(self):
    # 打开纹理图像,随机截取旋转
    img_wl = Image.open('./lib/icon/bg.png')
    # 随机截取一部分纹理图
    pos_random = (randint(0,220), randint(0,340))
    box = (pos_random[0], pos_random[1], pos_random[0]+180, pos_random[1]+60)
    img_wl_random = img_wl.crop(box)
    # 重新设置截取图片的的大小,并进行一次高斯模糊
    img_wl_random = img_wl_random.resize(self.image.size).convert('L').filter(ImageFilter.GaussianBlur(1))
    # 将纹理图的灰度映射到原图的透明度,由于纹理图片自带灰度,映射后会有透明效果,所以fill的透明度不能太低
    L, H = self.image.size
    for h in range(H):
      for l in range(L):
        dot = (l, h)
        color = self.image.getpixel(dot)[:3]+(int(img_wl_random.getpixel(dot)/255*self.image.getpixel(dot)[3]),)
        self.image.putpixel(dot, color)
    # 进行一次高斯模糊,提高真实度
    self.image = self.image.filter(ImageFilter.GaussianBlur(0.6))

  # 测试使用查看当前图片绘制情况
  def show(self):
    if self.image:
      self.image.show()

  # 保存图片
  def save(self, path):
    if self.image:
      self.image.save(path)

  # 绘制矩形
  def draw_rect(self, shape, width=8):
    self.draw.rectangle(shape, fill=None, outline=self.border_color, width=width)

  # 绘制文本
  def draw_text(self, text, font, x, y):
    # 计算传入文本的占位的盒子宽高
    x1, y1, x2, y2 = self.draw.textbbox((x, y), text, font)
    text_size_x = x2 - x1
    text_size_y = y2 - y1
    # 根据文字的占位,计算绘制文本
    self.draw.text(
      (x - text_size_x / 2, y - text_size_y / 2), 
      text, 
      font=font, 
      fill=self.text_color
    )

# 获取路径下的所有 excel 表
def get_all_xlsx(path):
  # 获取 path 路径下的所有文件和文件夹名称
  names = os.listdir(path)
  # 筛选文件列表中的 xlsx 后缀的文件名称
  xlsx_names = list(filter(lambda x : x.split('.').pop() in ['xlsx'], names))
  # 返回筛选结果
  return xlsx_names

# 循环读取 xlsx 文件
def loop_read_xlsx_file(path, names):
  seal = Seal()
  for name in names:
    read_xlsx_data(f'{path}/{name}', seal)

# 读取 xlsx 文件中的文本字段
def read_xlsx_data(path, seal):
  # 读取 xlsx 文件中的数据
  wb = openpyxl.load_workbook(path,data_only=True)
  sheets = wb.get_sheet_names()
  sheet_first = sheets[0]
  ws = wb.get_sheet_by_name(sheet_first)
  for r in range(2,ws.max_row + 1):
    # 获取印章文件名以及印章中的文本字段
    name = ws.cell(row=r, column=1).value
    days = ws.cell(row=r, column=2).value
    money = ws.cell(row=r, column=3).value
    percent = ws.cell(row=r, column=4).value
    time = ws.cell(row=r, column=5).value
    # print(name, days, money, percent, time)
    if name and days and money and percent and time:
      # 创建印章
      name = name.replace("*", "")
      money = money.replace("+", "").replace("-", "").replace(",", "")
      percent = percent.replace("+", "").replace("-", "").replace("%", "")
      time = time.replace("清仓", "")
      seal.create_seal({
        "name": name,
        "days": days,
        "money": money,
        "percent": percent,
        "time": time
      })
      seal.save(f'./out_images/{name}{time}.png')

if __name__ == "__main__":
  # 获取所有的xlsx文件
  xlsxs = get_all_xlsx('./')
  # 读取文件内容
  loop_read_xlsx_file('./', xlsxs)

10. 总结

  1. 配合 excel 录入印章的文本,可以批量生成印章;
  2. 将生成的印章添加到对应的文件中,实现批量盖章。
转载自:https://juejin.cn/post/7387305065802153994
评论
请登录