#説明
コンソールからTexToolGUI.pywを起動して、GUIを表示させてから、
結合対象のテクスチャマスクの場所を指定してから、RUNボタンを押すことで、
複数枚のテクスチャマスクを、一枚のテクスチャに結合することが出来る。
また、コンソールから直接モジュールを起動して、CUIを表示させてから、
テクスチャの場所を指定しておいたcsvを読み込ませることで、
テクスチャを、各チャンネルに分離することが出来る。
他にも、テクスチャを左右・上下反転させたり、
あるチャンネルを、別のチャンネルに移動させることも出来る。
#動機
テクスチャマスクを纏めるの意外とめんどい
これまでは、以下の手順で、テクスチャマスクを纏めていた。
- Kritaにテクスチャマスクをインポート
- 各チャンネルのグループレイヤーを追加
- 各チャンネルのグループレイヤーに、テクスチャマスクを移動させる
- 各チャンネルの色を設定した、塗りつぶしレイヤーを追加
- グループレイヤーは加算、塗りつぶしレイヤーは乗算に変更
- 出来上がったテクスチャをエクスポート
手間と時間が非常に掛かる。あとAチャンネルが使い物にならない。
Python3でこういうのを制作しているけど、まともなツールも制作できる事を証明したかった。
#仕様
- Pillow(画像編集モジュール)を活用する。
- カラーモードはモノクロ・RGB・RGBAのみ。
- モジュールとしての組み込みを前提とする。基本的にはPyQt5製のGUIから呼び出す。
- 単独で使用したときは、バッチ処理を行えるようにcsvを読み込めるようにする。1
- csvは指定可能。(デフォルトでは、同ディレクトリの同名のcsvを指定)
#CSVの読み込み
csvを読み込んで、モジュールを実行する。(各モジュールにおける実装の違いはほとんど無い2)
- 入力の取得
- ファイルの読み込み(入力が空欄であれば、既定のcsv[BondTexture.csv]を使用)
- csvの読み込み
- 出力先が指定されている事を確認してから実行
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)を持っているテクスチャとして出力する。
def Bond(texture, Output):
#1 テクスチャマスクの読み込み
img = [Image.open(i) if i==i else None for i in texture]
#2 サイズの確認(Noneなら0と判定)
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)を持っているテクスチャを、
単一のチャンネルを持つテクスチャマスクに分離して出力する。
- テクスチャの読み込み
- テクスチャの分離
- チャンネル別に画像データに変換して出力(出力先が空欄なら無視)
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を行う。
- テクスチャの読み込み・分離
- 左右反転・上下反転
- チャンネル移動6
- テクスチャマスクの結合
- 画像データに変換して出力
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)
#まとめ
やっていることは非常にシンプルである。なぜ今まで誰もやらなかったのか?
これからは、テクスチャマスクを纏めることは、一瞬で出来るようになる。
作者としては、「出来れば多くの場所で使われて欲しい」と願っている。