用来快速合并贴图

前言

  忽然接到一个需求 写一个 PS 的工具。
  自动合并图片序列的通道。

  之前写过一个 PS 的小脚本,10-20 行的。
  解决了 PS 动作进行批处理保存贴图,输出的时候却带有副本的名称。
  用脚本替换掉动作的输出,就可以保存出不带 副本 字样的图片。

  但是感受了这次给的需求,做 PS 的工具感觉不太香,如果不是 立足 于 PS 的环境,做 PS 的脚本其实并不是好方案。
  毕竟 Python 也有 cv 和 PIL 等等的图像处理库,直接 Python 来处理那不香吗~

github 地址

GUI 开发

  这次界面依然选择了 Tkinter 进行开发,因为最近做 Bilibili 字幕上传工具。
  我发现 Tkinter 用得好也可以实现和 PyQt 差不多的界面效果,但是 Tkinter 打包起来比起 Qt 可小了很多。

  因为有了之前搞 Tkinter 工具经验,这次开发就很快了,毕竟需求不复杂界面也相对简单。

alt

  这次依然利用之前搞过的 ConfigDumperMixin 类,实现 GUI 界面的数据记录。
  然后统一数据之后将数据存储到 临时目录里面。

  关于进度条处理,这里使用了之前写好的进度条类

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
class ProgressDialog(tk.Toplevel):

canceled = False

def __init__(self, *args, **kwargs):
self.parent = kwargs.pop("parent", None)
tk.Toplevel.__init__(self, self.parent, *args, **kwargs)

# NOTE 阻断其他窗口
self.grab_set()
self.progress = ttk.Progressbar(
self, orient=tk.HORIZONTAL, length=100, mode="determinate"
)
self.progress.pack(side="top", fill="x", expand=1, padx=5, pady=5)
self.button = tk.Button(
self, text="Cancel", command=lambda: [None for self.canceled in [True]]
)
self.button.pack()

@classmethod
def loop(cls, seq, **kwargs):
self = cls(**kwargs)
maximum = len(seq)
for i, item in enumerate(seq):
if self.canceled:
break

try:
yield i, item # with body executes here
except:
import traceback

traceback.print_exc()
self.destroy()

self.progress["value"] = i / maximum * 100
self.update()

self.destroy()

  用 classmethod 方便实例化对象
  通过 yield 实现迭代器直接直接放到 for 循环里面使用~

图像处理

  图像处理上使用的是简单 Pillow PIL 图像库。
  其实混合通道不难,可以晚上搜索一下,又很多文章 链接
  我这里只是读取两个目录里图片的序号批量匹配,然后融合通道之后批量根据序号进行输出。

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
@error_log
def img_combine(self):
output_path = self.output_path.get()
input_path_1 = self.input_path_1.get()
input_path_2 = self.input_path_2.get()
if not os.path.exists(output_path):
self.output_path.set("")
messagebox.showwarning("警告", "输出路径不存在")
return
elif not os.path.exists(input_path_1):
self.self.input_path_1.set("")
messagebox.showwarning("警告", "第一种贴图路径不存在")
return
elif not os.path.exists(input_path_2):
self.input_path_2.set("")
messagebox.showwarning("警告", "第二种贴图路径不存在")
return

# NOTE 匹配图片命名序号
regx = re.compile(r".*?(\d+)\..*?$")
input_path_1_dict = {
regx.search(f).group(1): os.path.join(input_path_1, f)
for f in os.listdir(input_path_1)
if regx.search(f)
}
for i, img in ProgressDialog.loop(os.listdir(input_path_2)):
match = regx.search(img)
if not match:
continue
num = match.group(1)

img_1 = input_path_1_dict.get(num, "")
img_2 = os.path.join(input_path_2, img)
if not os.path.exists(img_1):
continue

img_1 = Image.open(img_1).convert("RGBA")
img_2 = Image.open(img_2).convert("RGBA")

r_1, g_1, b_1, a_1 = img_1.split()
r_2, g_2, b_2, a_2 = img_2.split()

R = r_1 if self.R.get() == "1" else r_2
G = g_1 if self.G.get() == "1" else g_2
B = b_1 if self.B.get() == "1" else b_2
A = a_1 if self.A.get() == "1" else a_2

image = Image.merge("RGBA", [R, G, B, A])

img = os.path.splitext(img)[0]
ext = self.image_extension.get()
image.save(os.path.join(output_path, f"{img}.{ext}"))

messagebox.showinfo("恭喜您", "合并完成")
subprocess.Popen(["start", "", output_path], shell=True)

alt

总结

  这个工具的功能不复杂,一个早上就写完了。
  简单记录一下。

补充 2020-12-30

  混合通道使用 imagemagick 命令行高效秒杀这个需求。

分离
合并