GIMP Script-fu 批量背景替换

GIMP Script-fu Batch Background Replacement

Objective

我有一堆使用透明度的 .tga 图像。这些图像用于有时使用透明度渲染有时不使用透明度渲染的应用程序,因此 'background'(对应于包含 alpha 通道的图层蒙版中的黑色部分的 RGB 区域)非常重要。

为了更改单个图像的背景,我的手动过程(甚至可能不是最有效的)是:添加图层蒙版 > 传输图层的 Alpha 通道 > 复制图层蒙版的内容 > 应用图层蒙版 >将新的背景层粘贴到图像中(相同尺寸)> 将新层移动到堆栈底部 > 拼合图像 > 添加图层蒙版(到生成的单个图层)> 传输图层的 Alpha 通道 > 从剪贴板粘贴上一个图层蒙版的内容 > 应用图层蒙版> 导出为 .tga。

我正在尝试编写一个接受以下参数的脚本:目标文件扩展名、包含目标文件的目录、新背景图像以及创建目标文件备份的选项。批处理脚本应将上述过程应用于目录中具有目标文件扩展名的每个图像。

接近

根据 GIMP 的脚本教程及其批处理模式页面的示例,我编写了以下 script-fu(方案)。对于背景图像和目标图像具有不同尺寸或文件类型的情况,我没有进行任何类型的异常处理。出于我的目的,所有目标文件都是 .tga,所有背景都是 .tga/.png/.jpg。

(script-fu-register
    "batch-background-replacement"
    "Batch Background Replacement"
    "Replace backgrounds of all images in a directory"
    "Callistonian"
    "2021, Callistonian"
    "September 28, 2021"
    ""
    SF-STRING   "File type"             "tga"
    SF-DIRNAME  "Directory"             ""
    SF-FILENAME "New background"        (string-append "" gimp-data-directory "")
    SF-TOGGLE   "Create backups?"       0
)

(script-fu-menu-register "batch-background-replacement" "<Image>/File")

(define (batch-background-replacement fileType directory background backups)
    (let*
        (
            (filelist (cadr (file-glob (string-append directory "*." fileType) 1)))
        )
        (while (not (null? filelist))
            (let*
                (
                    (filename (car filelist))
                    (image (car (gimp-file-load 1 filename filename)))
                    (layer (car (gimp-image-get-active-layer image)))
                )
                (gimp-image-undo-disable image)
                (if (equal? backups 1)
                    (gimp-file-save 1 image layer (string-append filename "_backup") (string-append filename "_backup"))
                )
                (gimp-layer-add-mask layer (car (gimp-layer-create-mask layer 3)))
                (gimp-edit-copy (car (gimp-image-get-active-drawable image)))
                (gimp-layer-set-apply-mask (car (gimp-image-get-active-layer image)) TRUE)
                (gimp-image-insert-layer image (car (gimp-file-load-layer 1 image (car (gimp-file-load 1 background background)))) 0 1)
                (gimp-image-flatten image)
                (gimp-layer-add-mask (car (gimp-image-get-active-layer image)) (car (gimp-layer-create-mask layer 3)))
                (gimp-edit-paste (car (gimp-image-get-active-drawable image)) TRUE)
                (gimp-layer-set-apply-mask (car (gimp-image-get-active-layer image)) TRUE)
                (gimp-image-undo-enable image)
                (gimp-file-save 1 image layer filename filename)
                (gimp-image-delete image)
            )
            (set! filelist (cdr filelist))
        )
    )
)

script dialogue box

问题

解决了一些语法错误后,脚本现在可以运行但无声地失败了 - 目标图像没有任何反应,也没有证据表明 GIMP 做了任何事情。

评论

我怀疑目标图像和新背景的路径有问题,脚本根本找不到它们中的任何一个。但是我还没有找到一种方法可以在任何地方打印任何东西,这使得调试变得不可能。

这是我第一次尝试编写 GIMP 脚本或使用方案。我知道 GIMP 的批处理模式页面实际上建议使用命令行进行批处理,但我更希望所有输入都在一个漂亮的对话框中给出,就像这个脚本提供的那样。

gimp-message 是获取调试消息的命令,可以是弹出对话框或错误控制台

另外我认为你需要一个目录分隔符,DIR-SEPARATOR 在这一行:

(string-append directory "*." fileType)

否则你会得到这个: "C:\Program Files\GIMP 2.10\share\gimp\2.0*.tga" 这可能与您的任何文件都不匹配。

(string-append directory DIR-SEPARATOR "*." fileType) 得到这个:

"C:\Program Files\GIMP 2.10\share\gimp\2.0\*.tga"

解决了目标文件路径(感谢 paynekj,如果可以的话,我会投票给你),我能够继续调试并提出一个功能脚本。有一些错误的函数调用已得到纠正,并且我在任何目标文件尺寸与所选背景图像不匹配的情况下添加了警告。

虽然脚本按预期工作,但现在的问题是它花费的时间太长了 - 在我的机器上每张 69x96 图像大约需要 3 秒,处理大约 6,000 张图像需要几个小时才能完成所有这些图像.我关于尺寸不匹配的警告消息似乎不会影响处理时间。禁用撤消似乎也没有太大影响(我之所以添加它是因为我在其他地方读到它会有所帮助)。如果有人对提高脚本效率有任何建议,请赐教。

(define (batch-background-replacement fileType directory background backups)
    (let*
        (
            (filelist (cadr (file-glob (string-append directory DIR-SEPARATOR "*." fileType) 1)))
            (filelistLength (length filelist))
            (backgroundImage (car (gimp-file-load 1 background background)))
            (backgroundWidth (car (gimp-image-width backgroundImage)))
            (backgroundHeight (car (gimp-image-height backgroundImage)))
        )
        (gimp-image-delete backgroundImage)
        (gimp-message-set-handler 0)
        (while (not (null? filelist))
            (let*
                (
                    (filename (car filelist))
                    (image (car (gimp-file-load 1 filename filename)))
                    (layer (car (gimp-image-get-active-layer image)))
                    (width (car (gimp-image-width image)))
                    (height (car (gimp-image-height image)))
                )
                
                ;warning message for mismatched dimensions
                (if (and (= width backgroundWidth) (= height backgroundHeight)) ()
                    (gimp-message (string-append "WARNING: target image dimensions do not match selected background image:\n" filename "\n\nBatch Background Replacement will continue processing images but the new backgrounds may not line up correctly."))
                )
                
                (gimp-image-undo-disable image) ;purely to improve performance, doesn't seem to have much effect
                
                ;save backups
                (if (= backups 1)
                    (gimp-file-save 1 image layer (string-append filename "_backup." fileType) (string-append filename "_backup." fileType))
                )
                
                ;core process
                (gimp-layer-add-mask layer (car (gimp-layer-create-mask layer 3)))  ;'Transfer layer's alpha channel'
                (gimp-edit-copy (car (gimp-image-get-active-drawable image)))   ;copy contents of alpha channel
                (gimp-image-insert-layer image (car (gimp-file-load-layer 1 image background)) 0 1) ;insert new background as bottom layer
                (gimp-image-flatten image)  ;flatten image without applying layer mask, this doesn't work in interactive mode but seems to work fine in script
                (gimp-layer-add-mask (car (gimp-image-get-active-layer image)) (car (gimp-layer-create-mask (car (gimp-image-get-active-layer image)) 3)))  ;'Transfer layer's alpha channel'
                (gimp-edit-paste (car (gimp-image-get-active-drawable image)) TRUE) ;paste alpha channel from previous layer mask
                (gimp-floating-sel-anchor (car (gimp-image-get-active-layer image)))    ;'Anchor the floating layer'
                (gimp-layer-remove-mask (car (gimp-image-get-active-layer image)) 0)    ;'Apply layer mask'
                (gimp-image-undo-enable image)  ;probably unnecessary
                (gimp-file-save 1 image (car (gimp-image-get-active-layer image)) filename filename)    ;export
                (gimp-image-delete image)   ;close
            )
            (set! filelist (cdr filelist))
        )
        (gimp-message (string-append "Batch Background Replacement has finished processing " (number->string filelistLength) " images."))
    )
)

编辑:对脚本添加了一些改进以及至少解释了我认为正在发生的事情的注释。每个文件仍然需要大约 3-4 秒,在首选项中使用 16 个线程。我尝试取消选中 'Keep record of used files in the Recent Documents list' 但没有明显效果。

运行 控制台中的各个命令,大多数是瞬时的,但打开文件和导出肯定不是 - 这些 运行 交互需要大约 3 秒,这表明几乎所有的处理时间只是打开和导出。在开始每个目标图像之前打开所有目标图像,然后在最后全部导出它们可能会更快?