クーの自由研究

マスターの かえるのクーは8年の任期を終え消失しました。クーⅡ世の中の人絶賛募集中です。(特にオリオン座&プレイアデス方向の方は優遇します)助手がメイド喫茶から生還し、リハビリを兼ねて復帰しています。

GIMPのpythonプラグインに、新たなモジュールを追加(pip)して実行してみる

RPA方式は結局「大量一括」処理には向かないので

もちは餅屋、画像処理はGIMPにおまかせします。かえるのクーの助手の「井戸中 聖」(いとなか あきら)でございます。

UWSCによる簡易的な画像前処理をやってみましたが、良好でした。

お手軽ですが、RPAの限界もすぐ見えてしまいました。大風呂敷広げたのに、カッコわるっ。

・タイミングやマシン負荷、イレギュラー状態により処理が失敗することがしばしばある。

・人向けのインターフェースとして、操作ハンドリングを自動的に行っている。もともとオペレーション速度は緩く想定されているため、自動化しても処理速度としては限界がでてくる。

・複数のソフトやオペレーションを「無理やり」結び付けているだけなので、単一プログラム/スクリプトで実行できる場合は、そちらの方が断然よいはず。*1

RPAは、人間が操作するより断然速いですが、専用プログラムで全力を出すよるも超絶遅いわけでございます。専用処理PG >>> RPA >> 人手 ...ですよねぇ。

お詫び:PythonでRPAは延期します

予定していたPyAutoGUIをつかってのPython RPAはパスします。お詫びします。

本当はPyAutoGUIをいろいろ実験するつもりでした。

 で紹介されているような機能は、簡単に実装できそうです。

現在「やりたいこと」=「学習画像の前処理」としては、汎用性は薄れるものの、GIMPのスクリプト単独で行うほうが断然早くて確実です。

そこで

GIMPでPythonスクリプト実行

やります。GIMPはMACでもLinuxでも、さらにWindowsでも動作させることができますし、こちらのほうが汎用的かもしれません。 画像処理ソフトのGIMPには、現在LISP系のスクリプトとPython系のスクリプト実行の機能があります。ただし、プラグインのPythonは2系です。GIMP自体が3系になったときにPython3系に対応すると思われますが、現在は2系を使うしかありません。

f:id:np2LKoo:20210523085650p:plain f:id:np2LKoo:20210523104722p:plain f:id:AssistantOfKoo:20210523023516j:plain

あくまで、PythonからGIMPを制御するのではなく、GIMPからPython/LISPスクリプトを実行する。該当スクリプト用にGIMPのAPIを公開しているといったところです。現行pythonからGIMPモジュール自体をimportして、インスタンス化してなんでも行えるわけではないようです。(こんなのできると最高ですが)

やりたいこと

・yamlファイルで共通パラメータ、個別のパラメータを指定して、それによってGMPの操作を実行する。

・順次ファイルを読み込んで、定型的な処理をしていく動作を大量にさせたい。

確認したいこと

・Pythonスクリプトを実行するとき、そのpythonスクリプトの中で、numpyとかの各種モジュールが通常とおり使えるかどうか。プラグインなのでがちがちに囲まれて制約が多いかもしれません。

・画像ファイル以外の情報をpythonとして扱うことができるか。

ひさびさ(30年ぶりくらい?)にLISPを触りたい気持ちを抑えてPythonで行います。

先輩の説明

よろしくお願いします。

本当に5分で理解できました!感謝大盛り、つゆだくです!

10分くらいでスクリプトの登録と所定のメニューの場所へプラグイン機能を表示する方法を理解できました!あたま大盛、たまご付きです!

今私が知りたいすべてがここにありました。トロだく、つゆだくだく、もう食べられません!!

一括処理も全く問題ないようです。import os も問題なく使えるので、私のやりたい処理にはなんの支障もなさげです!!

では、UWSCで作成したスクリプトと同様の動作をGIMPのプラグイン/Pythonだけで作成してみます。

実食!実装!

ようやく完成しました。

必ずUTF-8で保存しておいてください。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from gimpfu import *
import os, glob, time, math
import yaml

PIX2PIX_YAML='yaml' + os.sep + 'pyFu-pix2pix-preprocess.yaml'
PREFIEX_TRAIN='Train_' #学習用ファイル(生成)のPrefix
g_common = [ ]       #yaml情報の格納配列
DEBUG = True        #デバッグフラグ
g_display = None    #確認要画面表示のためのハンドル

def load_yaml_file():
    default_yaml_fullpath = os.path.dirname(
        os.path.abspath(__file__)) + os.sep + PIX2PIX_YAML
    gimp.message(default_yaml_fullpath)
    with open(default_yaml_fullpath, 'r'as yml:
        config = yaml.safe_load(yml)   #yamlファイルの読み込み
        global g_common
        g_common = config['common']    #yamlファイルからの情報を設定(グローバル変数に保存)

def save_image_file(imagelayerout_dirout_file):
    file_path = out_dir +os.sep + out_file
    pdb.file_jpeg_save(image, layer, file_path, file_path, 0.9000"Card"0000)

def operate_image(image):
        # Core Process see https://sites.google.com/site/non2title/
        pdb.gimp_image_resize(image, g_common['work_area_x'], g_common['work_area_y'], 00)    #キャンパスサイズをリサイズする(元の画像を横に並べるエリアを準備)
        origin_layer = image.active_layer    #元のレイヤーを記録
        new_layer = pdb.gimp_layer_copy(image.active_layer, TRUE)    #複写して新レイヤーを作成
        image.add_layer(new_layer, 0)    #イメージに新レイヤを追加
        pdb.gimp_layer_set_offsets(new_layer, g_common['copy_move_x'], g_common['copy_move_y'])    #新レイヤを右に1024移動
        pdb.gimp_layer_scale(origin_layer, g_common['exp_resize_x'], g_common['exp_resize_y'], TRUE)    #元のレイヤーを中心より拡大
        pdb.gimp_item_transform_rotate(origin_layer , math.radians(g_common['rotaion']) ,TRUE ,0 ,0)    #指定度数回転
        merge_layer = pdb.gimp_image_merge_down (image, new_layer, EXPAND_AS_NECESSARY)  #下のレイヤと統合し新レイヤとする
        image.crop(g_common['work_area_x'], g_common['work_area_y'], 00)    #指定範囲でクロップする
        pdb.gimp_layer_scale(merge_layer, g_common['shrink_resize_x'], g_common['shrink_resize_y'], FALSE)      #統合済レイヤをリサイズする
        pdb.gimp_image_resize(image, g_common['final_campus_size_x'], g_common['final_campus_size_y'], 00)    #キャンパスサイズをリサイズする
        # For Debug 確認用に画像を表示(表示しないようにしても思うほど速度は向上しなかった)
        if DEBUG:
            global g_display
            if g_display is not None:
                pdb.gimp_display_delete(g_display)   #前回画像を削除
            g_display=pdb.gimp_display_new(image)    #動作確認で画像を表示する

def pix2pix_preprocess(imagelayerin_dirout_dire_type):
    gimp.message("pix2pix_preprocess start")
    start_time = time.time()
    load_yaml_file()
    i_count = 0
    for jpg in glob.glob(os.path.join(in_dir, "*.jpg")):
        lap_time = time.time()
        work_image = pdb.file_jpeg_load(jpg, jpg) #画像の読み込み
        operate_image(work_image)    #画像の操作
        out_jpg = PREFIEX_TRAIN + os.path.basename(jpg) #画像の出力
        save_image_file(work_image, work_image.active_layer, out_dir, out_jpg)
        lap =  time.time() - lap_time
        gimp.message("processed:" + out_dir + os.sep + out_jpg + ' :{:.3f} sec'.format(lap))
        i_count += 1
        if e_type == "2" and i_count >= 3:
            break
    #最後の画像のみ表示を残す
    elapsed = time.time() - start_time
    gimp.message('pix2pix_preprocess End: in {:.3f} sec'.format(elapsed))

register(
    "pix2pix_preprocess",#コマンドラインまたはスクリプトから呼び出す場合のコマンドの名前
    "Pre process for pix2pix traning images",#プラグインの説明
    "Batch execution of specified processing\nThis plug-in use pix2pix_preproces.yaml",#プラグインの詳細な説明
    "AssistantOfKoo",#プラグインの作成者
    "(c)KooWells and thy servant",#プラグインの著作権保有者
    "2021/05/23",#著作権の日付
    "<Image>/PyPlugin/Pix2pix PreProcess"#メニューの中でプラグインに使用されるラベル
    "RGB*, GRAY*"#プラグインで処理する対象となる画像のタイプex. RGB*, GRAY* など
    [
        (PF_STRING, "input_dir""Input Image Dir:""C:\work\GIMP_Ope1\Target"),
        (PF_STRING, "output_dir""Output Image Dir:""C:\work\GIMP_Ope1\Train"),
        (PF_RADIO, "exe_type""Excecute Type:""1", ( ("Full Execute.""1"),
                                                       ("ためしに3個やってみる。""2") ) )
    ],
    [ ], # 戻り値
    pix2pix_preprocess) # メソッド名
main()

デバッグがしにくくて泣きました。リモートデバッグ(ステップ実行)する方法はあるようです。そちらに流れるか不明です。

メニューとダイアログの表示

まずは、メニューと画面がでるようにしました。

f:id:AssistantOfKoo:20210525213859p:plain

必要パッケージの追加導入(鬼門)

さて、今時ならyamlを使った方が簡単だとおもいましたが、GIMPのpython2.7系のsite-packageは最小限のものしかはいっていません。osが入っているので、入出力系のだいたいのことはできるので、通常は問題ありませんが、応用的な使い方をする場合は、どうしてもpip install したくななります。

(ここからの手順は環境変更に自信のある方、環境がおかしくなっても復旧可能な方のみ参照ください。生半可な情報でやると必ず現環境を破壊します!ドキュメントを読み込めてませんが、GIMPの内部python環境にpipするのは想定外のような気がします。)

・GIMP内にpipコマンドがない あるいは、python -m pip install xxxxではエラーになる ので、本家python2.7系を入れることになります。(エラーにならなければOKですが、わたくしの環境ではNGでした。2.7系を別にいれる時点で大部分の方は無理のはずです)*2

おおまかな手順(設定はあくまで一例です)

・python 2.7系をインストールします。(例:私の環境はWindows10です。  C:\Python27\ に入れました。)猛者以外はanacondaを避けたほうがよいです。:anacondaは導入済のモジュールの相互関連性が非常に高いため)

・環境変数を一旦2.7系にかえます。(ローカルPathの先頭にC:\Program Files\GIMP 2\bin; C:\Python27\Scriptsを追加)

pip が2.7系のものであることを確認(where pip; pip -V など)したあと、管理者権限コマンドプロンプトで

pip install pyyaml

します。

・どこかのsite-packagesフォルダに格納されています。(わたくしの環境では C:\Python27\Lib\python2.7\site-packages に入りました)

通常だったらPYTHONPATHに上記パスを追加するところですが、GIMPが環境変数設定を(GIMP内で動作するpythonへ)通してくれないので、以下をおこ言います。

・GIMP/pythonのsite-packagesにインストールされた「ディレクトリごとファイルの複写」する。

※直接pip install 時にGIMPのsite-packagesの指定が可能であれば、そちらでもOKです。

(わたくしの環境では、_yaml, yaml, PyYAML-5.4.1.dist-info ファイルをGIMP 2 配下のsite-packagesへ複写しました(管理者権限が必要です))

このあとGIMPを再起動して、コンソールで

import  yaml

として、エラーがでなければOKです。これでGIMP のpythonスクリプトでyamlが使えます。基本的にどのモジュール追加もこのやり方でOKです。(ただしGIMPの既存モジュールのバージョンを変えないもの!、機能的衝突しないもの!!DLL依存でないもの:numpyとかはDLL依存なのでできません)

f:id:np2LKoo:20210523181053p:plain


yamlが使えるようになったので、yamlパラメータファイルを考えます。

UWSCのときのControl.xlsx ファイルと同様にしてみます。

 pyFu-pix2pix-preprocess.yaml  (ドラフト版)

common:
    work_area_x2048
    work_area_y1024
    copy_move_x1024
    copy_move_y0
    exp_resize_x1280
    exp_resize_y1280
    rotaion10
    shrink_resize_x512
    shrink_resize_y256
    final_campus_size_x512
    final_campus_size_y256
  

実行の補足:

バッチ実行するときはgimpの実行オプションの後ろに

--console-messages

を付加します。

f:id:AssistantOfKoo:20210525214651p:plain

これでコンソールに実行経過が表示されます。

f:id:AssistantOfKoo:20210525215351p:plain

1ファイルあたり4秒以上かかっています。感覚的に1ファイル1秒程度(悪くて2秒程度)と想定していたので、とても悲しいです。※他のマシンでは2秒程度のものもあったので使えないことはない!?

究極の高速化を求めるためには、既存のソフトの助けは借りず、(高速化したいならGIMPではなく)通常Python+numpyさらにはC++でやりましょう。と、いまさら天の声が聞こえました。

※GIMP + python-Fu/numpyは成功していないことをあらためて申し上げます。

GIMPでのバッチ画像処理が速ければ、どんどん詰めていこうと思っていましたが、このあたりでやめておきます。

 

f:id:np2LKoo:20210523102733j:plain 

皆様、ゴルシ様の指導、鞭撻お疲れさまでした!

※注:上記画像はウマ娘ファンの方の作品です!機械学習で創成変換したものではございませんので、誤解なきようお願いします。(でもできるんじゃね?)

 

*1:余談ですが:個人的にはRPAが超絶普及してくれば、RPAインターフェース標準が策定/デファクトスタンダードされ、画面操作/表示の「画面部品を介さない」汎用高速連携ができるようになってくると予想しています。(サーバアプリ - HTML = Webサービスなら、クライアント入口アプリ - UI画面 = RPAサービスみたいなの)

*2:いまや2系は化石扱いです。まぁ、サポート終了ですからねぇ。時の流れは速いものです。わたくしがpythonをやりはじめたときは機械学習は3.xは全然こなれていないので、機械学習をするなら断然2.x系がよい!と断言されていた時代でした。つい先日なのにね。