科技感图片处理

使用 Python 处理图片,达到科技感效果,如下图所示:

运行结果
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
import random
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
import os

# 根据文字生成图片,并有中文支持
# 文字对齐方式对应的变量名为 anchor
def create_image(text, bg_color, word_color, font_path=None, font_size=100, max_len=0, anchor="center"):
if font_path is None:
font_path = "/usr/share/fonts/truetype/ms-core-fonts/msyhbd.ttf"

# 展开用户目录
font_path = os.path.expanduser(font_path)

# 创建字体对象
font = ImageFont.truetype(font_path, font_size)

# 分割文本为行
lines = text.split('\n')

# 计算每一行的实际宽度
max_len = max(max_len, max([font.getbbox(line)[2] for line in lines]))
print(max_len)

total_height = sum([font.getbbox(line)[3] for line in lines])

# 创建画布
im = Image.new("RGB", (max_len, total_height), bg_color)
dr = ImageDraw.Draw(im)

# 绘制文字,根据锚点位置
y = 0
for line in lines:
bbox = font.getbbox(line)
line_width = bbox[2] - bbox[0]
if anchor == "lt":
x = 0
elif anchor == "center":
x = (max_len - line_width) // 2
elif anchor == "rt":
x = max_len - line_width
else:
raise ValueError("Invalid anchor value. Use 'lt', 'center', or 'rt'.")

dr.text((x, y), line, font=font, fill=word_color)
y += bbox[3] - bbox[1]

return im

# 将图片进行像素化处理(先缩小再放大)
def pixelate(im, scale=5):
return im.resize((im.size[0] // scale, im.size[1] // scale), Image.BILINEAR).resize(im.size, Image.NEAREST)

# 对图片中的颜色不等于 bg_color 的像素进行块状亮度调整
# 取一个 block_size * block_size 的块,遍历每个像素,进行判断并调整
# 亮度调整的范围为 min_factor 到 max_factor
def apply_random_brightness(im, bg_color, min_factor=0.5, max_factor=2, block_size=None, block_count=None, size_shift=0.1):
bg_color_rgb = Image.new("RGB", (1, 1), bg_color).getpixel((0, 0))
pixels = im.load()
width, height = im.size

if block_count is None:
# block_count 与 block_size 成反比,并小于 (width * height) // (block_size ** 2)
block_count = (width * height) // (block_size ** 2) // 10

if block_size is None:
raise ValueError("block_size must be specified.")

# 随机选择 block_count 个块
for _ in range(0, block_count):
x = random.randint(0, width - block_size)
y = random.randint(0, height - block_size)

# 遍历块中的每个像素,判断是否需要调整亮度
block = []
for j in range(y, min(y + block_size, height)):
for i in range(x, min(x + block_size, width)):
if pixels[i, j] != bg_color_rgb:
block.append((i, j))

if block:
factor = random.uniform(min_factor, max_factor)
for i, j in block:
r, g, b = pixels[i, j]
r = min(255, int(r * factor))
g = min(255, int(g * factor))
b = min(255, int(b * factor))
pixels[i, j] = (r, g, b)

return im

# 另一种情况是,对所有的小块都加一个非常小的随机亮度
def apply_random_brightness2(im, bg_color, min_factor=0.9, max_factor=1.1, block_size=None):
pixels = im.load()
width, height = im.size

if block_size is None:
raise ValueError("block_size must be specified.")

for y in range(0, height, block_size):
for x in range(0, width, block_size):
block = []
for j in range(y, min(y + block_size, height)):
for i in range(x, min(x + block_size, width)):
if pixels[i ,j] != bg_color:
block.append((i, j))

if block:
factor = random.uniform(min_factor, max_factor)
for i, j in block:
r, g, b = pixels[i, j]
r = min(255, int(r * factor))
g = min(255, int(g * factor))
b = min(255, int(b * factor))
pixels[i, j] = (r, g, b)

return im

# 蓝移:
# 若 (x - shift, y) 为背景,(x, y) 为文字,则 (x, y) 处改蓝色;
# 若 (x - shift, y) 为文字,则 (x, y) 处改为 (x - shift, y) 的颜色;
# 从右向左遍历。
def apply_random_blue_shift(im, bg_color, blue_shift= 5.0, x_shift=3):
bg_color_rgb = Image.new("RGB", (1, 1), bg_color).getpixel((0, 0))
pixels = im.load()
width, height = im.size

for y in range(height):
for x in range(width - x_shift, x_shift, -1):
if pixels[x, y] != bg_color_rgb:
if pixels[x - x_shift, y] == bg_color_rgb:
pixels[x, y] = pixels[x, y][:2] + (int(pixels[x, y][2] * blue_shift),)
else:
pixels[x, y] = pixels[x - x_shift, y]


return im

# 进行随机的 x 轴平移
def apply_random_x_shift(im, bg_color, block_size, max_shift=20):
bg_color_rgb = Image.new("RGB", (1, 1), bg_color).getpixel((0, 0))
pixels = im.load()
width, height = im.size

y = random.randint(0, height - block_size)
shift = random.randint(-max_shift, max_shift)

max_height = random.randint(0, block_size)

for i in range(max_height):
for x in range(width):
new_x = x + shift
if 0 <= new_x < width:
im.putpixel((new_x, y + i), im.getpixel((x, y + i)))
else:
im.putpixel((x, y + i), (40,44,52)) # 用黑色填充背景

return im


def create_gif(base_image, bg_color, block_size=15, min_factor=0.5, max_factor=3, num_frames=30):
frames = []
for _ in range(num_frames):
frame = base_image.copy()
frame = apply_random_brightness(frame, bg_color=bg_color, block_size=block_size, min_factor=min_factor, max_factor=max_factor)
frame = apply_random_brightness2(frame, block_size=block_size, bg_color=bg_color)
# 20% 的概率应用蓝移
if random.random() < 0.2:
frame = apply_random_blue_shift(frame, bg_color=bg_color)
frame = apply_random_x_shift(frame, block_size=20, max_shift=20, bg_color=bg_color)
frames.append(frame)

frames[0].save("output.gif", save_all=True, append_images=frames[1:], duration=100, loop=0)

def main():
# 定义颜色
bg_color = "#000000"
word_color = "#07C160"
text = "Hello, World!\n你好,世界!"

im = create_image(text, bg_color=bg_color, word_color=word_color)
im = pixelate(im)
create_gif(base_image=im, bg_color=bg_color)


if __name__ == "__main__":
main()

说明

  • 总体思路:
    1. 生成文字图片;
    2. 对图片进行像素化处理;
    3. 对图片中的颜色不等于背景色的像素进行块状亮度调整;
    4. 对所有的小块都加一个非常小的随机亮度;
    5. 对图片进行蓝移;
    6. 进行随机的 x 轴平移;
    7. 生成 gif。
  • 默认的 font_path/usr/share/fonts/truetype/ms-core-fonts/msyhbd.ttf。此为微软雅黑字体,是生成图片中中文正常显示的关键;实际使用你可能需要改为你的字体路径。
  • 重要的崩坏效果由 apply_random_brightnessapply_random_x_shift 实现。其中为使效果更棒,apply_random_brightness 会对图片中的颜色不等于背景色的像素进行块状亮度调整,而 apply_random_x_shift 会进行随机的 x 轴平移,并且偏移条带的高度也是随机的。
  • 蓝移的想法是将文字侧边的像素蓝色化,使得文字看起来有一种发光的效果,但效果不理想。
  • 生成的 gif 会保存在当前目录下的 output.gif 文件中。