Skip to content

Commit a30a93b

Browse files
committed
feat: 图片操作 新增 #宫格转gif [4/9/16/25/36]
1 parent b5a8e81 commit a30a93b

2 files changed

Lines changed: 64 additions & 3 deletions

File tree

apps/PYImageTools.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const defaultPythonPath = process.platform === 'win32' ? 'python' : 'python3'
1717
const commands = [
1818
'水平翻转', '左翻', '右翻', '竖直翻转', '上翻', '下翻', '灰度图', '黑白',
1919
'旋转', '缩放', '裁剪', '反相', '反色', '轮廓', '浮雕', '模糊', '锐化',
20-
'像素化', 'gif倒放', '倒放', 'gif变速', '四宫格', '九宫格', '横向拼接',
20+
'像素化', 'gif倒放', '倒放', 'gif变速', '宫格转gif', '四宫格', '九宫格', '横向拼接',
2121
'纵向拼接', '文字转图'
2222
];
2323

@@ -67,6 +67,7 @@ export class ImageTools extends plugin {
6767
【动图处理】
6868
#gif倒放 (或 #倒放)
6969
#gif变速 [倍率] (例: #gif变速 2x 或 #gif变速 50%)
70+
#宫格转gif [4/9/16/25/36] [帧间隔] (例: #宫格转gif 25 200ms)
7071
7172
【其他功能】
7273
#文字转图 [文字] (例: #文字转图 你好)
@@ -200,4 +201,4 @@ export class ImageTools extends plugin {
200201

201202
return true;
202203
}
203-
}
204+
}

utils/PYImagetools_core.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,39 @@ def save_gif_and_print(bytes_io):
3636
b64 = base64.b64encode(bytes_io.getvalue()).decode('utf-8')
3737
print(f"BASE64:{b64}")
3838

39+
def parse_frame_duration(arg):
40+
if not arg:
41+
return 300
42+
43+
p_float = r"\d{1,4}(?:\.\d{1,3})?"
44+
if match := re.fullmatch(rf"({p_float})ms", arg, re.I):
45+
duration = float(match.group(1))
46+
elif match := re.fullmatch(rf"({p_float})s", arg, re.I):
47+
duration = float(match.group(1)) * 1000
48+
elif match := re.fullmatch(rf"({p_float})fps", arg, re.I):
49+
fps = float(match.group(1))
50+
if fps <= 0:
51+
raise Exception("请使用正确的帧间隔格式,如:200ms、0.5s、2fps")
52+
duration = 1000 / fps
53+
else:
54+
raise Exception("请使用正确的帧间隔格式,如:200ms、0.5s、2fps")
55+
56+
if duration <= 0:
57+
raise Exception("请使用正确的帧间隔格式,如:200ms、0.5s、2fps")
58+
return max(20, int(round(duration)))
59+
60+
def parse_grid_gif_args(arg):
61+
parts = arg.split()
62+
if not parts or parts[0] not in ["4", "9", "16", "25", "36"]:
63+
raise Exception("请提供宫格数量:#宫格转gif 4/9/16/25/36 [200ms/0.5s/2fps]")
64+
if len(parts) > 2:
65+
raise Exception("请使用正确格式:#宫格转gif 4/9/16/25/36 [200ms/0.5s/2fps]")
66+
67+
total = int(parts[0])
68+
grid_size = {4: 2, 9: 3, 16: 4, 25: 5, 36: 6}[total]
69+
duration = parse_frame_duration(parts[1] if len(parts) == 2 else "")
70+
return grid_size, duration
71+
3972
def process_image():
4073
cmd = sys.argv[1]
4174
arg = sys.argv[2]
@@ -179,6 +212,33 @@ def process_image():
179212
out = BytesIO()
180213
frames[0].save(out, format="GIF", save_all=True, append_images=frames[1:], loop=0, duration=duration)
181214
save_gif_and_print(out)
215+
216+
elif cmd == "宫格转gif":
217+
grid_size, duration = parse_grid_gif_args(arg)
218+
image = imgs[0].image
219+
if image.width != image.height:
220+
raise Exception("宫格转gif 需要正方形宫格图片")
221+
222+
usable_side = image.width - (image.width % grid_size)
223+
if usable_side < grid_size:
224+
raise Exception("图片尺寸过小,无法切分宫格")
225+
226+
offset = (image.width - usable_side) // 2
227+
if usable_side != image.width:
228+
image = image.crop((offset, offset, offset + usable_side, offset + usable_side))
229+
230+
frame_size = usable_side // grid_size
231+
frames = []
232+
for row in range(grid_size):
233+
for col in range(grid_size):
234+
left = col * frame_size
235+
top = row * frame_size
236+
frame = image.crop((left, top, left + frame_size, top + frame_size)).copy()
237+
frames.append(frame)
238+
239+
out = BytesIO()
240+
frames[0].save(out, format="GIF", save_all=True, append_images=frames[1:], loop=0, duration=duration)
241+
save_gif_and_print(out)
182242

183243
elif cmd == "四宫格":
184244
img = imgs[0].square()
@@ -227,4 +287,4 @@ def process_image():
227287
print(f"ERROR: {str(e)}")
228288

229289
if __name__ == "__main__":
230-
process_image()
290+
process_image()

0 commit comments

Comments
 (0)