mcpField
G
gimp

gimp

github.com/jablonkai创意与媒体442 次安装今天更新
代码仓库

关于 gimp

这是一个通过 GIMP 内置 Script-Fu 服务器控制 GIMP 3 的技能,面向需要自动构建、检查和导出图像的用户。它在用户当前打开的 GUI 中运行 Python 脚本,并提供 API 参考与操作示例。

主要功能

  1. 01通过 Script-Fu 发送 Python 脚本
  2. 02控制图像、图层、选区和文字
  3. 03应用可编辑的 GEGL 滤镜
  4. 04调用 PDB 过程并导出文件
  5. 05生成缩略图检查合成结果

适用场景

  1. 01构建海报并导出 XCF 与 PNG
  2. 02在用户现有图像上执行编辑
  3. 03批量处理文件夹或逐图层导出

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jablonkai/skills --skill gimp

安装后在 Claude Code 中输入/gimp确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api-reference.md
recipes.md
scripts/
example-poster.py
gimp_client.py
gimp-send.sh
gimp-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 无法实现的功能。

控制循环

  1. 检查连接: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…。)
  2. 将构建脚本写入暂存区并发送: 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(秒)可用于批量处理和大型导出。

  3. **读取反馈。**发送器会返回脚本捕获的 stdout/stderr;如果失败,还会返回 Python 回溯,并以非零状态退出——因此,print(...) 是你接收反馈的渠道。其他内容不会报告:GIMP 自带的错误控制台仍在应用程序内。

  4. 查看结果。image.get_thumbnail(w, h, Gimp.PixbufTransparency.SMALL_CHECKS) 会将合成图像直接渲染为 PNG,你可以读取该文件。请在每个构建脚本结束时都执行此操作——只需一行,就能发现断言无法发现的问题。

  5. 迭代,然后交付。修复脚本并重新发送。脚本必须可重复运行:每个版本都创建新图像,而不是修改已打开的图像,以免重新发送时将图层叠加到上一次尝试的结果上。

每次发送都会启动一个新的 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)。

评论

支持 Markdown