（Python製）テクスチャ編集ユーティリティ：Ｔｅｘ－Ｔｏｏｌ

#説明
コンソールからTexToolGUI.pywを起動して、ＧＵＩを表示させてから、
結合対象のテクスチャマスクの場所を指定してから、ＲＵＮボタンを押すことで、
![s1.png](https://qiita-image-store.s3.amazonaws.com/0/239082/3cb5320f-a72c-a0c2-0be0-d3d6baf29055.png)　![s2.png](https://qiita-image-store.s3.amazonaws.com/0/239082/35d2a02e-e85e-0ba3-a08c-a405baeb54c5.png)
複数枚のテクスチャマスクを、一枚のテクスチャに結合することが出来る。
![Result_RGBA.png](https://qiita-image-store.s3.amazonaws.com/0/239082/ab9b3ae3-b3c2-6a64-56e3-c381693b19ca.png)
また、コンソールから直接モジュールを起動して、ＣＵＩを表示させてから、
テクスチャの場所を指定しておいたcsvを読み込ませることで、
![s3.png](https://qiita-image-store.s3.amazonaws.com/0/239082/d24c2856-8b18-d84d-ce1c-e51f54d783d5.png)
![s4.png](https://qiita-image-store.s3.amazonaws.com/0/239082/cd2ec043-ddda-79ee-f41e-dc5cb7fca4d9.png)
テクスチャを、各チャンネルに分離することが出来る。
![RGB_R.png](https://qiita-image-store.s3.amazonaws.com/0/239082/ee52c7ac-cdf4-eb1f-cc4e-cf4a19dc3876.png)　![RGB_G.png](https://qiita-image-store.s3.amazonaws.com/0/239082/9e0fc21d-745a-5e0a-4888-aaa726c6d16f.png)　![RGB_B.png](https://qiita-image-store.s3.amazonaws.com/0/239082/f3df076e-4270-d349-6cd1-1804c2e23260.png)
他にも、テクスチャを左右・上下反転させたり、
![flip.png](https://qiita-image-store.s3.amazonaws.com/0/239082/b99591ea-5bc2-23f6-2088-30d3096bfed4.png)　![mirror.png](https://qiita-image-store.s3.amazonaws.com/0/239082/2da81237-4aab-1355-579b-31998158ee97.png)
あるチャンネルを、別のチャンネルに移動させることも出来る。
![move-B.png](https://qiita-image-store.s3.amazonaws.com/0/239082/fc5e3df6-78d3-5aff-e7af-c06581eaaa4c.png)　![move-G.png](https://qiita-image-store.s3.amazonaws.com/0/239082/da57199e-42f7-a25c-45cb-62fd3f296554.png)　![move-R.png](https://qiita-image-store.s3.amazonaws.com/0/239082/821271c6-7ab3-f0a3-00d5-b5973332f86a.png)

#動機
***テクスチャマスクを纏めるの意外とめんどい***
![s5.png](https://qiita-image-store.s3.amazonaws.com/0/239082/0049b86a-da28-5ed5-1171-f39e1043a253.png)
これまでは、以下の手順で、テクスチャマスクを纏めていた。

1. Kritaにテクスチャマスクをインポート
2. 各チャンネルのグループレイヤーを追加
3. 各チャンネルのグループレイヤーに、テクスチャマスクを移動させる
4. 各チャンネルの色を設定した、塗りつぶしレイヤーを追加
4. グループレイヤーは加算、塗りつぶしレイヤーは乗算に変更
5. 出来上がったテクスチャをエクスポート

**手間と時間が非常に掛かる。あとＡチャンネルが使い物にならない。**

~~[Python3でこういうのを制作しているけど](https://qiita.com/Polaris1080/items/3fdabed9567effb69781)、まともなツールも制作できる事を証明したかった。~~

#仕様

+ Pillow（画像編集モジュール）を活用する。
+ カラーモードはモノクロ・ＲＧＢ・ＲＧＢＡのみ。  
+ モジュールとしての組み込みを前提とする。基本的にはPyQt5製のGUIから呼び出す。  
+ 単独で使用したときは、バッチ処理を行えるようにcsvを読み込めるようにする。[^1]
+ csvは指定可能。（デフォルトでは、同ディレクトリの同名のcsvを指定）

[^1]: jsonよりも、csvのほうが、デザイナーでも扱いやすいため

#CSVの読み込み

*csvを読み込んで、モジュールを実行する。（各モジュールにおける実装の違いはほとんど無い[^2]）*
[^2]: 引数が各モジュールで違うので、csvのデータの解釈が違ってくる。

1. 入力の取得
2. ファイルの読み込み（入力が空欄であれば、既定のcsv[BondTexture.csv]を使用）
3. csvの読み込み
4. 出力先が指定されている事を確認してから実行

```python3:BondTexture.py
if __name__ == '__main__': #単独で呼び出した時のみ実行（モジュールとして呼び出した時には実行しない）
    #1 入力の取得
    print("Please enter the location of 'BondTexture.csv'");
    name_csv = input()

    try:            #2 ファイルの読み込み
        csv_input = pd.read_csv(name_csv if name_csv else '../csv/BondTexture.csv', dtype=str)
    except IOError: #読み込み失敗
        print("File can't be found")
    else:           #読み込み成功
        for i in range(len(csv_input)):
            row    = csv_input.iloc[i].tolist() #3 csvの読み込み
            Output = row[4]     
            if (Output == Output): #4 出力先が指定されている事を確認してから実行
                Bond(row[0:4], Output)
            else:                  #空欄ならエラーメッセージを出力
                print('Output not specified / Row:{}'.format(i))
```
#テクスチャマスクの結合

*単一のチャンネルを持つテクスチャマスクを結合し、
複数のチャンネル(__RGB__A)を持っているテクスチャとして出力する。*

1. テクスチャマスクの読み込み（空欄ならNone）
2. サイズの確認（Noneなら０）[^3] 
3. 前処理
4. テクスチャマスクの結合[^4] 
5. 画像データに変換して出力

[^3]: 前処理でのテクスチャ生成時に、テクスチャサイズが必要
[^4]: texture[3]がNoneの時は、Ａチャンネルが減らせるので、ＲＧＢモードで出力できる。

```python3:BondTexture.py
def Bond(texture, Output):
    #1 テクスチャマスクの読み込み
    img = [Image.open(i) if i==i else None for i in texture]

    #2 サイズの確認（Noneなら０と判定）
    tex_size = max([i.size if i else (0,0) for i in img]) 

    #3 前処理
    def Convert(p):
        #空欄(None)なら、空のテクスチャを生成。
        if   (p == None):     return Image.new("L", tex_size, 0)
        #モノクロなら、変換せずに出力。
        elif (p.mode == "L"): return p
        #カラーなら、赤チャンネルを抽出。
        else:                 return p.split()[0]

    #4 テクスチャマスクの結合
    #5 画像データに変換して出力
    if None == img[-1]: #RGB
        del img[-1] #リストの最後尾を削除してRGBに合わせる
        Image.merge("RGB",  [Convert(i) for i in img]).save(Output)
    else: #RGBA
        Image.merge("RGBA", [Convert(i) for i in img]).save(Output)
```
#テクスチャの分離

*複数のチャンネル(__RGB__A)を持っているテクスチャを、
単一のチャンネルを持つテクスチャマスクに分離して出力する。*

1. テクスチャの読み込み
2. テクスチャの分離
3. チャンネル別に画像データに変換して出力（出力先が空欄なら無視）

```python3:BondTexture.py（解説版）
def UnBond(Target, Input):
    #1 テクスチャの読み込み
    image = Image.open(Input)

    #2 テクスチャの分離
    img = image.split()

    #3 チャンネル別に画像データに変換して出力
    if (image.mode == 'RGBA') or (image.mode == 'RGB'):
        [img[i].save(Target[i]) for i in range(len(img)) if Target[i]==Target[i]]
    else: #RGBA・RGB以外
        print("Can UnBond 'RGB(A)-Texture' only")
```
#左右反転・上下反転・チャンネル移動

テクスチャの各チャンネル(__RGB__A)に対し、左右反転・上下反転・チャンネルの移動[^5]を行う。
[^5]: 例えば、ＲチャンネルをＧチャンネルに移動させて、ＧチャンネルをＲチャンネルに移動できる。

1. テクスチャの読み込み・分離
2. 左右反転・上下反転
3. チャンネル移動[^6]
4. テクスチャマスクの結合
5. 画像データに変換して出力

[^6]: RGBAモードでの出力は、テクスチャのカラーモードがRGBAモードかつ、mode = 'RGBA'と指定されている時のみ。

```python3:BondTexture.py
def Frip(Input, Output, mode, mirror, flip, Order):
    #1 テクスチャの読み込み・分離
    image_splited = Image.open(Input).split()

    #左右反転
    def _mirror(p): return ImageOps.mirror(p) if mirror[len(img)] else p

    #上下反転
    def _flip(p)  : return ImageOps.  flip(p) if flip  [len(img)] else p

    #2 左右反転・上下反転
    img = [];  [img.append(_flip(_mirror(t))) for t in image_splited]

    #3 チャンネル移動
    #4 テクスチャマスクの結合
    #5 画像データに変換して出力
    if   (mode == "RGBA") and (len(image_splited) == 4): #RGBA mode
        Image.merge("RGBA", [img[i] for i in Order[0:4]]).save(Output)
    elif (mode == "RGB")  or  (len(image_splited) == 3): #RGB  mode
        Image.merge("RGB",  [img[i] for i in Order[0:3]]).save(Output)
```
#まとめ
やっていることは非常にシンプルである。~~なぜ今まで誰もやらなかったのか？~~
これからは、テクスチャマスクを纏めることは、一瞬で出来るようになる。
作者としては、「出来れば多くの場所で使われて欲しい」と願っている。