关于 gimp
这是一个通过 GIMP 内置 Script-Fu 服务器控制 GIMP 3 的技能,面向需要自动构建、检查和导出图像的用户。它在用户当前打开的 GUI 中运行 Python 脚本,并提供 API 参考与操作示例。
主要功能
- 01通过 Script-Fu 发送 Python 脚本
- 02控制图像、图层、选区和文字
- 03应用可编辑的 GEGL 滤镜
- 04调用 PDB 过程并导出文件
- 05生成缩略图检查合成结果
适用场景
- 01构建海报并导出 XCF 与 PNG
- 02在用户现有图像上执行编辑
- 03批量处理文件夹或逐图层导出
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jablonkai/ skills --skill gimp安装后在 Claude Code 中输入/gimp确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api-reference.mdrecipes.mdscripts/example-poster.pygimp_client.pygimp-send.shgimp-start.sh详细介绍
GIMP 控制
GIMP(/Applications/GIMP.app)内嵌 Python 3,并通过 GObject Introspection(gi.repository.Gimp)使用 libgimp 暴露完整的文档模型,像素和滤镜则由 GEGL 提供支持。通过 GIMP 自带的 Script-Fu 服务器来控制它——这是 GIMP 随附的 TCP 端点,无需安装任何东西——服务器会将每个脚本交给 GIMP 的 python-fu-eval。脚本针对实时 GIMP 运行:画布会更新,图层堆栈就是用户当前查看的那个,而且不会在无头模式下运行。本技能中的每个调用都在 GIMP 3.2.6 上运行过。
- scripts/gimp-send.sh — 发送
.py文件(或-c 'inline code')并打印捕获的输出;--ping检查连接,--scheme则运行原始 Script-Fu。 - scripts/gimp-start.sh — 启动 GIMP 图形界面并开启 Script-Fu 服务器,等待服务器响应。
- scripts/gimp_client.py — 两个脚本使用的线路协议。
- scripts/example-poster.py — 完整的构建 → 验证 → 导出脚本,可参照其结构。
- references/api-reference.md — 经版本核验的 libgimp API(图像、图层、选区、绘图、文本、GEGL 滤镜、PDB 调用、像素读回、导出)。在编写超出下方速查表范围的内容前,请先阅读此文档。
- references/recipes.md — 处理用户自己的图像、批量处理文件夹、按图层导出、蒙版、动画,以及 API 无法实现的功能。
控制循环
-
检查连接:
bash scripts/gimp-send.sh --ping→{"ok": true, "bridge": "gimp", "port": 10008, "reply": "3.2.6 | open images: 2"}。如果没有响应,可能是两种不同情况,需要采取不同的操作:
- GIMP 未运行 →
bash scripts/gimp-start.sh会启动图形界面并开启服务器,然后等待服务器就绪。这会在用户屏幕上打开一个窗口,因此请告知用户你正在启动它。 - GIMP 正在运行,但服务器未启动 → 你无法远程开启服务器,也不得为了绕过此问题而启动第二个实例:两个 GIMP 实例共用一个配置目录,而且用户尚未保存的工作就在已打开的实例中。请让用户点击一次:Filters ▸ Development ▸ Script-Fu ▸ Start Server…,监听地址为
127.0.0.1,端口为10008。(本地化版本的 GIMP 中,Development 仍显示为英文;在匈牙利语版本中,路径为 Szűrők ▸ Development ▸ Script-Fu ▸ Kiszolgáló indítása…。)
- GIMP 未运行 →
-
将构建脚本写入暂存区并发送:
OUT=/path/to/outdir bash scripts/gimp-send.sh /path/build.py。OUT会同时作为全局变量和os.environ["OUT"]传入——它是唯一会传入 GIMP 的变量:运行SRC=... bash gimp-send.sh会让脚本报KeyError,因为脚本运行在 GIMP 进程中,而不是你的 shell 中。请将输入路径写成脚本中的绝对路径字面值。GIMP_SEND_TIMEOUT=600(秒)可用于批量处理和大型导出。 -
**读取反馈。**发送器会返回脚本捕获的 stdout/stderr;如果失败,还会返回 Python 回溯,并以非零状态退出——因此,
print(...)是你接收反馈的渠道。其他内容不会报告:GIMP 自带的错误控制台仍在应用程序内。 -
查看结果。
image.get_thumbnail(w, h, Gimp.PixbufTransparency.SMALL_CHECKS)会将合成图像直接渲染为 PNG,你可以读取该文件。请在每个构建脚本结束时都执行此操作——只需一行,就能发现断言无法发现的问题。 -
迭代,然后交付。修复脚本并重新发送。脚本必须可重复运行:每个版本都创建新图像,而不是修改已打开的图像,以免重新发送时将图层叠加到上一次尝试的结果上。
每次发送都会启动一个新的 python-fu-eval 进程,因此不同发送之间不会保留任何状态,只有 GIMP 中的内容(已打开的图像)和磁盘上的内容除外。往返大约需要一秒——与其猜测 API,不如用一次临时发送来试探。
脚本速查表
import gi
gi.require_version("Babl", "0.1"); gi.require_version("Gegl", "0.4")
gi.require_version("Gimp", "3.0")
from gi.repository import Babl, Gegl, Gimp, Gio
Gegl.init(None)
图像和图层堆栈
image = Gimp.Image.new(1000, 1400, Gimp.ImageBaseType.RGB)
layer = Gimp.Layer.new(image, "background", 1000, 1400,
Gimp.ImageType.RGBA_IMAGE, 100.0, Gimp.LayerMode.NORMAL)
image.insert_layer(layer, None, 0) # parent=None -> top level; 0 = topmost
layer.set_opacity(70.0) # 0–100 float, not 0–255
layer.set_mode(Gimp.LayerMode.SCREEN)
Gimp.Display.new(image) # show it — the user watches the build happen
Gimp.displays_flush() # nothing repaints without this
要处理用户已有的内容:Gimp.get_images()[0],最近激活的图像排在
最前面。进行破坏性操作前先复制图像——参见
recipes.md。
颜色:输入和输出均为 sRGB
Gegl.Color 存储的是线性 RGB,因此构造函数和 setter 并非一回事,两者的差异会让图像明显褪色:
Gegl.Color.new("#1a73e8") # parses sRGB — use this
colour.set_rgba(0.10, 0.45, 0.85, 1.0) # LINEAR components — rarely what you meant
list(colour.get_bytes(Babl.format("R'G'B'A u8")).get_data()) # [26,115,232,255]
Gimp.context_set_foreground(Gegl.Color.new("#ffb703"))
选区、填充和绘制
image.select_rectangle(Gimp.ChannelOps.REPLACE, x, y, w, h) # also ellipse, polygon,
layer.edit_fill(Gimp.FillType.FOREGROUND) # round_rectangle, color
Gimp.Selection.none(image) # every later edit respects the selection
layer.fill(Gimp.FillType.FOREGROUND) # whole layer, ignores the selection
Gimp.context_set_gradient_fg_bg_rgb()
layer.edit_gradient_fill(Gimp.GradientType.LINEAR, 0.0, False, 1.0, 0.0, True,
x1, y1, x2, y2)
Gimp.context_set_brush(Gimp.Brush.get_by_name("2. Hardness 100"))
Gimp.context_set_brush_size(20.0)
Gimp.paintbrush_default(layer, [40.0, 40.0, 200.0, 200.0]) # ONE drawable, flat x/y list
文本
font = Gimp.context_get_font() # safer than naming one: get_by_name
text = Gimp.TextLayer.new(image, "GIMP", font, 150.0, Gimp.Unit.pixel())
image.insert_layer(text, None, 0)
text.set_color(Gegl.Color.new("#f8f9fa"))
text.set_justification(Gimp.TextJustification.CENTER)
text.set_offsets((image.get_width() - text.get_width()) // 2, 300) # measure AFTER
滤镜——优先选择非破坏性方式
GIMP 3 的滤镜会保持启用且可编辑,这是处理他人作品时的正确默认选择:
blur = Gimp.DrawableFilter.new(layer, "gegl:gaussian-blur", "glow blur")
config = blur.get_config()
config.set_property("std-dev-x", 90.0); config.set_property("std-dev-y", 90.0)
blur.update() # push config into the filter
layer.append_filter(blur) # or layer.merge_filter(blur) to bake it in
不要靠猜测来确定操作和属性名称,应使用 Gegl.list_operations() 和
Gegl.Operation.list_properties("gegl:gaussian-blur") 来查找。旧版 plug-in-gauss
系列在 GIMP 3 中已不再提供。
没有绑定的功能:PDB
proc = Gimp.get_pdb().lookup_procedure("script-fu-drop-shadow") # None if absent
config = proc.create_config()
config.set_property("run-mode", Gimp.RunMode.NONINTERACTIVE)
config.set_property("image", image)
config.set_core_object_array("drawables", [layer]) # NOT set_property
result = proc.run(config)
导出
Gimp.file_save(Gimp.RunMode.NONINTERACTIVE, image,
Gio.File.new_for_path(OUT + "/poster.xcf"), None) # .png/.jpg/... too
flat = image.duplicate(); flat.flatten() # flatten a copy, not the original
Gimp.file_save(Gimp.RunMode.NONINTERACTIVE, flat,
Gio.File.new_for_path(OUT + "/poster.png"), None)
flat.delete()
.xcf 会保留所有图层、蒙版和实时滤镜,供用户编辑;除非用户另有说明,否则应与合并后的导出文件一并交付。格式选项(JPEG 质量、GIF 时间设置)
需要通过 PDB 调用导出插件来设置——参见
api-reference.md。
验证
- 返回的标准输出是即时信号——
print(...)的结果会直接返回,失败时则会返回回溯信息并以非零状态退出。 - 缩略图 PNG用于视觉检查:将
image.get_thumbnail(500, 700, Gimp.PixbufTransparency.SMALL_CHECKS)保存到$OUT,然后读取。它是合成后的图像,因此能如实呈现混合模式和滤镜效果,而逐层描述无法做到这一点。 - 数值:通过
Babl.format("R'G'B'A u8")对image.pick_color([...], x, y, True, False, 0.0)采样,可确认颜色是否精确到位;layer.histogram(...)会以 0–255 为范围给出平均值和中位数。将尺寸、图层名称、偏移量、模式和已附加的滤镜导出为metrics.json,然后读取该文件,而不是凭肉眼检查图层堆栈。 - 交付:导出的 PNG/JPEG 可以在任何地方打开;
.xcf则会保留可编辑的工作内容。
注意事项
Gegl.Color.set_rgba使用线性色彩空间,Gegl.Color.new("#hex")使用 sRGB。 混淆两者会悄悄让所有颜色变亮;读取值时应使用get_bytes(Babl.format("R'G'B'A u8")),而不是get_rgba()。- 数组中包含 GIMP 对象时使用
set_core_object_array。config.set_property( "drawables", [layer])会抛出could not convert [...] to type 'GimpCoreObjectArray', 读取时则需要使用result.get_core_object_array(i)——result.index(i)返回的是无法迭代的GBoxed。 - 绘制调用接收单个可绘制对象,而不是列表(
Gimp.paintbrush_default(layer, pts)), 而pick_color接收的是可绘制对象列表。两者确实不一致。 - 找不到字体时,
Gimp.Font.get_by_name会返回None,且只有在TextLayer.new中才会显现为错误:Argument 2 does not allow None as a value。除非用户指定了字体,否则请使用Gimp.context_get_font(),并检查返回值。 - 文本图层会自动调整大小——设置文本、字号和字间距后,再进行居中和定位,否则测量到的会是之前的布局。
- 编辑后调用
Gimp.displays_flush(),否则用户看到的画布不会更新,你们都会以为脚本没有执行。 - 在
get_offsets、pick_color、histogram和selection.bounds中,成功标志位于返回值首位。 请解包元组,不要盲目按索引读取。 - 资源名称会本地化——在匈牙利语版 GIMP 中,渐变名称是
Előtérből háttérbe (RGB),而不是FG to BG (RGB)。请在运行时列出名称 (Gimp.gradients_get_list("")及类似方法),不要硬编码英文名称。 image.delete()会直接丢弃未保存的更改,不会询问。 只对脚本自己创建的图像调用它;绝不要对用户的图像调用。- 使用
image.undo_group_start()/undo_group_end()将编辑操作分组,这样一次 Ctrl+Z 就能撤销整个构建过程,而不是四百个单独操作。 - 发送后不会保留状态——每次都会启动新的
python-fu-eval进程,脚本会获得一个仅包含OUT的全新命名空间:请自行导入os、json和gi仓库。为图像添加版本号(如"poster_v2"),避免重复运行时与上一次尝试冲突。 - 发送失败会留下打开的半成品图像。 创建图像后立即调用
image.set_file(Gio.File.new_for_path(OUT + "/poster_v2.xcf"));这样下次发送时,就能通过get_file()在Gimp.get_images()中找到并删除脚本留下的文件,不会碰到用户打开的任何图像。 flatten()会用上下文背景色填充透明区域,而该颜色可能是先前填充操作留下的值。展平前请设置背景色(Gimp.context_set_background),或者使用merge_visible_layers(Gimp.MergeType.CLIP_TO_IMAGE)来保留 Alpha 通道。- 在 GIMP 3 中,
--batch需要搭配--batch-interpreter;否则 GIMP 会打印解释器列表后退出。gimp-start.sh已经传入该参数。 - GIMP 崩溃后端口可能仍处于绑定状态。
script-fu-server插件是独立进程,GIMP 退出后它还会存活一段时间,因此仅检查端口会显示“运行中”,但端口后面其实没有服务。--ping能识别这种情况(连接会在回复中途关闭,随后孤立进程也会退出)——如果报告了这种情况,只需重新启动 GIMP。 - GIMP 2.10 教程无法直接移植。
gimpfu、pdb.gimp_*、RGB 元组和plug-in-gauss滤镜都已不存在,仍然可用的调用也已改变形式——现在edit_bucket_fill(fill_type, x, y)会从Gimp.context_set_sample_threshold获取阈值。在信任从其他地方看到的过程名称之前,请检查lookup_procedure是否返回None。 - 工具无法脚本化——没有变换控件、自由选区或菜单操作触发功能。可用的全部操作只有填充、绘制调用、选区和 GEGL 滤镜;请参阅 recipes.md。
安全性
这里无需安装任何内容,但启动 Script-Fu 服务仍会让 GIMP 变成代码执行服务。在让用户点击之前,请先说明这一点。
- 服务监听
127.0.0.1:10008,并会将收到的任何内容交给python-fu-eval——以用户权限运行的任意 Python 代码,可操作用户打开的图像。它没有身份验证:任何本地进程,以及共享计算机上的其他用户,都可以通过它控制 GIMP。 - 始终使用 127.0.0.1。“启动服务”对话框接受任意监听地址,而绑定到
0.0.0.0或局域网地址的服务会让整个网络面临远程代码执行风险。gimp-start.sh会固定使用环回地址;如果用户手动启动,请告知他们使用的地址。 - 这不是 HTTP——该协议使用带长度前缀的数据,并以
G作为魔数,因此浏览器的请求会在第一个字节处被拒绝。这是线缆格式带来的副作用,并非来源检查。 - **要停止服务,请退出 GIMP。**GIMP 退出后,端口可能会在插件进程结束运行期间短暂保持绑定,因此检查端口并不能证明服务仍在运行——
--ping才可以(gimp-send.sh --ping)。