1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
| from PIL import Image, ImageFont, ImageDraw, ImageFilter from math import pi, cos, sin, tan from random import randint import random
def is_Chinese(ch): '''判断字符是否为中文''' if '\u4e00' <= ch <= '\u9fff': return True return False
def pentagram(x, y, R, yDegree=0): ''' 计算五角星各个顶点 int R:五角星的长轴 int x, y:五角星的中心点 int yDegree:长轴与y轴的夹角 ''' rad = pi / 180 # 每度的弧度值 r = R * sin(18 * rad) / cos(36 * rad) # 五角星短轴的长度 # 求取外圈点坐标 RVertex = [(x - (R * cos((90 + k * 72 + yDegree) * rad)), y - (R * sin((90 + k * 72 + yDegree) * rad))) for k in range(5)] # 求取内圈点坐标 rVertex = [(x - (r * cos((90 + 36 + k * 72 + yDegree) * rad)), y - (r * sin((90 + 36 + k * 72 + yDegree) * rad))) for k in range(5)] # 顶点左边交叉合并 vertex = [x for y in zip(RVertex, rVertex) for x in y] return vertex
def circle(x, y, r): '''计算圆的上下左右切点''' return (x - r, y - r, x + r, y + r)
def draw_rotated_text(image, angle, xy, r, word, fill, font_size, font_xratio, stroke_width, font_flip=False, *args, **kwargs): """ image:底层图片 angle:旋转角度 xy:旋转中心 r:旋转半径 text:绘制的文字 fill:文字颜色 font_size:字体大小 font_xratio:x方向缩放比例(印章字体宽度较标准宋体偏窄) stroke_width: 文字笔画粗细 font_flip:文字是否垂直翻转(印章下部文字与上部是相反的) """ if is_Chinese(word): font = ImageFont.truetype("fangsong.ttf", font_size, encoding="utf-8") else: font = ImageFont.truetype("fangsong.ttf", font_size, encoding="utf-8")
# 获取底层图片的size width, height = image.size max_dim = max(width, height)
# 创建透明背景的文字层,大小4倍的底层图片 mask_size = (max_dim * 2, max_dim * 2) mask_resize = (int(max_dim * 2 * font_xratio), max_dim * 2) mask = Image.new('L', mask_size, 0)
# 在上面文字层的中心处写字,字的左上角与中心对其 draw = ImageDraw.Draw(mask)
# 获取当前设置字体的宽高 bd = draw.textbbox((max_dim, max_dim), word, font=font, align="center", *args, **kwargs) font_width = bd[2] - bd[0] font_hight = bd[3] - bd[1]
# 文字在圆圈上下的方向,需要通过文字所在文字图层的位置修正,保证所看到的文字不会上下颠倒 if font_flip: word_pos = (int(max_dim - font_width / 2), max_dim + r - font_hight) else: word_pos = (int(max_dim - font_width / 2), max_dim - r)
# 写字, 以xy为中心,r为半径,文字上边中点为圆周点,绘制文字 draw.text(word_pos, word, 255, font=font, align="center", stroke_width=stroke_width, *args, **kwargs)
# 调整角度,对于Π*n/2的角度,直接rotate即可,对于非Π*n/2的角度,需要先放大图片以减少旋转带来的锯齿 if angle % 90 == 0: rotated_mask = mask.resize(mask_resize).rotate(angle) else: bigger_mask = mask.resize((int(max_dim * 8 * font_xratio), max_dim * 8), resample=Image.BICUBIC) rotated_mask = bigger_mask.rotate(angle).resize(mask_resize, resample=Image.LANCZOS)
# 切割文字的图片 mask_xy = (max_dim * font_xratio - xy[0], max_dim - xy[1]) b_box = mask_xy + (mask_xy[0] + width, mask_xy[1] + height) mask = rotated_mask.crop(b_box)
# 粘贴到目标图片上 color_image = Image.new('RGBA', image.size, fill) image.paste(color_image, mask)
def create_stamp(words_up, words_mid, words_down, fill=(255, 0, 0, 255), H=160, R=250, border=13, r=90, img_wl_path="bg.png", save_path="output.png"): img_wl = Image.open(img_wl_path) img = Image.new("RGBA", (2 * (R + 5), 2 * (R + 5)), (255, 255, 255, 0)) draw = ImageDraw.Draw(img)
# 绘制圆弧 draw.arc(circle(R + 5, R + 5, R), start=0, end=360, fill=fill, width=border)
# 绘制五角星 draw.polygon(pentagram(R + 5, R + 5, r), fill=fill, outline=fill)
# 绘制上圈文字 angle_word = 270 / len(words_up) angle_word_curr = ((len(words_up) - 1) / 2) * angle_word for word in words_up: draw_rotated_text(img, angle_word_curr, (R + 5, R + 5), R - border * 2, word, fill, 80, 0.66, 2) angle_word_curr -= angle_word
# 绘制中圈文字 angle_word = 72 / len(words_mid) angle_word_curr = -((len(words_mid) - 1) / 2) * angle_word for word in words_mid: draw_rotated_text(img, 0, (R + 5 + H * tan(angle_word_curr * pi / 180), R + 5), H, word, fill, 60, 0.7, 1, font_flip=True) angle_word_curr += angle_word
# 绘制下圈文字 angle_word = 60 / len(words_down) angle_word_curr = -((len(words_down) - 1) / 2) * angle_word for word in words_down: draw_rotated_text(img, angle_word_curr, (R + 5, R + 5), R - border * 2, word, fill, 20, 1, 1, font_flip=True) angle_word_curr += angle_word
# 随机圈一部分纹理图 pos_random = (randint(0, 200), randint(0, 100)) box = (pos_random[0], pos_random[1], pos_random[0] + 300, pos_random[1] + 300) img_wl_random = img_wl.crop(box).rotate(randint(0, 360)) img_wl_random = img_wl_random.resize(img.size).convert('L').filter(ImageFilter.GaussianBlur(1))
L, H = img.size for h in range(H): for l in range(L): dot = (l, h) img.putpixel(dot, img.getpixel(dot)[:3] + (int(img_wl_random.getpixel(dot) / 255 * img.getpixel(dot)[3]),))
# 高斯模糊 img = img.filter(ImageFilter.GaussianBlur(0.6)) img.save(save_path)
def add_varied_noise(image_path, output_path, noise_size=5, noise_prob=0.05): """ 给图片添加不同形状和大小的透明噪点 :param image_path: 输入图片路径 :param output_path: 输出图片路径 :param noise_size: 噪点的最大大小(像素为单位) :param noise_prob: 噪点出现的概率 """ image = Image.open(image_path).convert("RGBA") # 确保图片有透明通道 draw = ImageDraw.Draw(image)
width, height = image.size for _ in range(int(width * height * noise_prob)): # 根据概率生成噪点数量 # 随机生成噪点的形状和位置 shape_type = random.choice(['circle', 'rectangle', 'line']) # 随机选择形状
# 随机选择噪点的位置 x0 = random.randint(0, width - 1) y0 = random.randint(0, height - 1)
# 随机选择噪点的大小 size = random.randint(1, noise_size)
# 使用透明颜色(透明度设置为随机值,RGB固定为0) noise_color = (0, 0, 0, 0) # 透明噪点,透明度随机
# 绘制噪点 if shape_type == 'circle': draw.ellipse([x0, y0, x0 + size, y0 + size], fill=noise_color) elif shape_type == 'rectangle': x1 = x0 + size y1 = y0 + size draw.rectangle([x0, y0, x1, y1], fill=noise_color) elif shape_type == 'line': # 随机绘制一条线段 x1 = x0 + size y1 = y0 + size draw.line([x0, y0, x1, y1], fill=noise_color, width=size)
# 保存带有噪点的图片 image.save(output_path)
def create_white_bg(file_path): white_image = Image.new("RGB", (1080, 1080), "white") white_image.save(file_path)
# 示例使用 company_name = "符十三郎云技术工作室" user_for = "fushisanlang" company_num = "123" img_wl_path = "bg.png" temp_path = f'{company_name}_temp.png' save_path = f"{company_name}.png"
create_white_bg(img_wl_path)
create_stamp(words_up=company_name, words_mid=user_for, words_down=company_num, img_wl_path=img_wl_path, save_path=temp_path) add_varied_noise(temp_path, save_path, noise_size=2, noise_prob=0.01) print(f"{save_path} created..")
|