モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

ぼくのかんがえたさいきょうのgimp用python-fuスクリプト「automate-workflow.py」

追記あり:恥ずかしながらpython-fuで別に登録しなくてもバッチ処理できることが判明したので、このスクリプトは意味が無くなってしまいました…(恥)

はてなブログでは晒しませんが、オイラの趣味はお絵かきでございます。gimpでカキカキするのが楽しいのですね。

そしてお絵かきする際には、作品ごとにディレクトリを分けてファイルを置いています。

まずスキャナでスキャンした原画、それをpotrace->png化した原画、そしてgimpのxcfファイルというように。

それでもって本当に他人様から見ればどうでもいい修正をちょくちょく、ちょくちょくしては自分のお絵かきブログに上げ直したりしているわけですがw 殆ど誰の目にも触れない作品ですが、これが結構楽しいのですな。なんというか庭弄りと似た感覚と申しますか。

余談ですが、ピクシブでは修正は有料オプションですね。実にいいところに目をつけたものです。ピクシブという場の特性を考えれば、皆さん絵の評価を受け継ぎたいところでしょうからね…頭の良い人たちだ…

それはともかく、そうしているうちに絵ごとに特殊なワークフローが発生してくるのです。

たとえば

  1. 塗りまで済ませたキャラ絵を可視レイヤ合成。もしbackgroundという名前のレイヤがあれば、それは不可視化しておく。
  2. そのレイヤを50%縮小して最終出力xcfの中にコピー
  3. キャラレイヤの位置を(x,y)=(40,-106)に移動
  4. キャラレイヤの重なりを特定の場所まで下げ、他のレイヤの下に入れる
  5. 最終出力xcfを横1440px・縦自動計算に縮小して95%圧縮のjpgでエクスポート

というように。

キャラに修正が入ると毎回このワークフローを手動で繰り返すわけです。

一回や二回ならまぁいいのですが、5回ぐらい修正しているとさすがに自分でも何くだらないことやっているんだ俺は…みたいな気分になってまいります。そして楽しいはずのプチ更新いじりがだんだん苦痛になってくると…でも変なところが気になって仕方ない!ビクビクッ!*1

これがどの絵でも共通なワークフローならば普通のpython-fuで定型化することもできますが、いかんせん絵ごとに異なるのでどうにもならない。

そこで「ディレクトリごとにpythonファイルを置いて、それを呼び出すようにしてはどうだろう?」と思いつきました。

で、バッチ処理とはどう違うのか?

gimpバッチ処理スクリプトを登録して行いますよね?違うかな? そうするとメニューに増殖すると思うのですよ。プロジェクト分(ディレクトリ分)の固有スクリプトが…?

…よく分かってません。すみません。というかpython-fuでもバッチ処理って出来るんですかね*2

ワタクシ、lisp系言語はできるだけ見たくないヘタレですので…

追記: 出来ますね、python-fuでバッチ処理。しかもその場限りの.pyで…(;´Д`)

ryunosinfx様の

Python-fuバッチ起動まとめ - ryunosinfxの日記

一体何だったのか(トホホ

いや、なんか簡単にことが進みすぎるなとは思ったんですよ………

automate-workflow.py

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#[license] GPLv3
#[plugin]
#[name] automate-workflow
#[desc] 
# 画像ファイルのディレクトリにgimp_automate.pyを置くことで、そのファイルのmain関数を呼び出す。
# これにより、その画像ファイルに対する固定的ワークフローを自動化するためのスクリプト。
#
# main関数の引数は (gimpモジュール , Imageオブジェクト,  drawableオブジェクト)
# pdbへのアクセスはgimp.pdbで可能
#[version]
#0.1 初期リリース
#0.2 gimp.pdbがあることに気づいたので、pdbはgimp.pdbを使うように変更
#[end]

#  このプログラムはGPLライセンスver3で公開します。
# 
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You may have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.           
#
#   Copyright (C) 2013 dothiko(http://dothiko.blog.fc2.com/) 


from gimpfu import *
import os
import sys

DYNEXE_NAME="gimp_automate"

def python_fu_automate_workflow(a_img,a_drawable,sample_arg=True):

    if a_img.filename==None:
        gimp.message("このスクリプトは、新規ファイルでは使えません")
        return


    basename,ext=os.path.splitext(a_img.filename)
    basedir=os.path.dirname(a_img.filename)
    modname="%s.py" % DYNEXE_NAME 
    modpath="%s/%s" % (basedir,modname)
    if not os.path.exists(modpath):
        gimp.message("画像のディレクトリに%sがありません" % modpath)
        return

    if not basedir in sys.path:
        sys.path.append(basedir)
    else:
        print("already exist in sys.path")


    # start of groping undoable operations
    pdb.gimp_image_undo_group_start(a_img)

    try:

        module=__import__(DYNEXE_NAME,globals(),locals(),[],-1)
        try:
            module.main(gimp,a_img,a_drawable)
        except Exception,e:
            import traceback,time
            lt=time.localtime()
            datestr="%04d%02d%02d-%02d%02d%02d" % (lt.tm_year,lt.tm_mon,lt.tm_mday,
                                                       lt.tm_hour,lt.tm_min,lt.tm_sec)

            # for no-console environment,dump exception log at /tmp
            with open('/tmp/automate_%s.log' % datestr,'wt') as ofp:
                exc=traceback.format_exc()
                ofp.write(exc)
                print(exc)
            gimp.message("呼び出しに失敗しました")
        finally:
            del sys.modules[DYNEXE_NAME]
            del module

    finally:
        # end of grouping undoable operations
        pdb.gimp_image_undo_group_end(a_img)



register(
        "python_fu_automate_workflow",
        "automate-workflow",
        "自動化用スクリプト",
        "dothiko",
        "kakukaku world",
        "Apr 2015", 
        "<Image>/Python-Fu/others/automate-workflow", 
        "RGB*,GRAY*",
        [
        ],
        [],
        python_fu_automate_workflow)


main()


編集中のファイルと同一のディレクトリにgimp-automate.pyというファイルが置いてあると、ソレの中の

main(gimp, 編集中のimage, アクティブなdrawable)

という関数を呼び出します。

後はgimpgimp.pdbを使ってレイヤを操作すればいいわけですね。

また、同一ディレクトリに複数のファイルがあり、それらが別々のワークフローを持っていることも考えられます。

この場合は、image.filenameを基に正規表現などで対象を検出、別々の処理を行うという方法で対処可能でしょう。

と思ったんですがバッチ処理可能と発覚した今、あんま意味ねえ〜ということで(;´Д`)

ま、まぁ、バッチ処理呼び出しより簡単ということは言えるかなっ!

サンプル

某所のトップ絵用のgimp_automate.pyを置いておきます。実際には自分用ライブラリ「mylib」からユーティリティー関数を呼び出しているのでもっと長いプログラムなのですが、ここに載せるには余白がたりないというか需要もないだろうということで…一応言い出しっぺの責任感から一部だけ、載せておきます。(^^;

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import re
import os
import glob
from mylib.gimp.autoutils import *


# トップ絵用オートメーション

def main(gimp,img,drawable):
    pdb=gimp.pdb
    basedir=os.path.dirname(img.filename)
    # まず最初に画像を複製
    dimg=img.duplicate()
    
    # bgがあればそれを消す
    BG_NAME="background"
    idx,bl=find_layer(dimg,BG_NAME)
    if bl:
        bl.visible=False
    else:
        print 'not found bg'

    # 可視レイヤを統合
    nl=dimg.merge_visible_layers(1) # crop to image
    
    # そいつをbicubicで50%に縮小
    scale_layer(pdb,nl,nl.width/2,nl.height/2, 2) # the last 2 means 'bicubic interp'
    
    # それをfinalimage_base.xcfを開いてそれにコピー、以下finalimageでの処理となる
    fpath='%s/finalsource_base.xcf' % basedir
    fimg=pdb.gimp_xcf_load(0,fpath,os.path.basename(fpath))
    tl=pdb.gimp_layer_new_from_drawable(nl, fimg)

    #このタイミングで、複製イメージは削除
    pdb.gimp_image_delete(dimg)

    # レイヤ「mop-adj」の下に移動
    idx,layer=find_layer(fimg,'mop-over')
    pdb.gimp_image_insert_layer(fimg,tl,None,idx+1)
    
    # 位置を(40,-106)に調整
    tl.set_offsets(40,-106)
    
    # 可視レイヤを統合
    nl=fimg.merge_visible_layers(1)
    
    # 画像を1440px幅にスケーリング
    nw,nh=get_scale_size(fimg,1440,-1)
    scale_image(pdb,fimg,nw,nh, 2) # the last 2 means 'bicubic interp'
    
    # fullhdX.jpgという連番でエクスポート、品質は95%
   #idx=get_maximum_number_of_filename(basedir,'fullhd','.jpg')
   #ofpath='%s/fullhd%d.jpg' % (basedir,idx+1)
   #save_as_jpeg(pdb,fimg,ofpath,0.95)
    export_numbered_jpg_file(pdb,fimg,basedir,'fullhd',0.95)
    

    # finalimage_base.xcfを閉じる
    pdb.gimp_image_delete(fimg)



if __name__ == '__main__':

    pass

fullhdというプリフィクスなのに解像度がノーマルHDなのは笑うところです。

*1:全体的に絵としてオカシイと言われると何も言えない

*2:出来ました