2016年11月30日水曜日

BlenderからPhotoshopを制御3-画像をPhotoshpで開くアドオン作成

前回までは基本的な動作を中心に説明してきましたが
今回からはBlenderアドオンとして仕上げます

アドオン作成の細かい説明についてはnutti様が公開している「はじめてのBlenderアドオン開発」がよくまとまっているので、そちらを参考にしていただくといいかと思います

前回までの解説の通り
VB_HubやPhotoshop制御用のJavaScript等複数のファイルで構成される形になっているので
複数ファイルで構成されるBlenderアドオンの作り方で作成を進めていきます。

今回は「UV/画像エディッター」で表示している画像をPhotoshopの現在の書類で開くものを作ります
前のものはレンダリング画像のみが対象でしたが テクスチャペイントで作成した画像等も開くことができるようになります

まずは「img_to_photoshop」という名前でのフォルダ作成してます
ここに今回使用するスクリプトファイル(__init__.py VB_Hub.vbs img_open.jsx)を収めることとします
以下それそれのファイルを解説をします

bl_info = {
    "name": "image to Photoshop",
    "description": "イメージをPhotoshopで開く",
    "author": "Yukimi",
    "version": (0,3),
    "blender": (2, 6, 0),
    "location": "image",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Import-Export"}

import bpy
import os
import subprocess
import time
import random

#実行するjavascriptの名前
js_name = "img_open.jsx"
#このスクリプトのあるディレクトリのパス
mydoc_dir = os.path.dirname(__file__)
#実行するスクリプトのパス
VB_Hub = os.path.abspath(os.path.join(mydoc_dir, "VB_Hub.vbs"))
jscript = os.path.abspath(os.path.join(mydoc_dir, js_name))
#Blenderの一時ファイルのパス
tmp_dir = bpy.context.user_preferences.filepaths.temporary_directory

def to_photoshop(context):
    #現在の.blendファイルのあるフォルダに一時ファイルを保存する場合は以下2行をコメントアウト
    #blendpath = bpy.data.filepath
    #tmp_dir  = os.path.join(os.path.split(blendpath)[0],"tmp")
    #日時+3桁のランダムアルファベットでファイル名の作成
    source_str = 'abcdefghijklmnopqrstuvwxyz'
    f_name = time.strftime("%y%m%d%H%M") + "".join([random.choice(source_str) for x in range(3)]) + ".png"
    img_path  = os.path.join(tmp_dir,f_name)
    #表示している画像の保存
    bpy.ops.image.save_as(save_as_render=False, filepath=img_path, relative_path=True)
    #スクリプトの実行
    subprocess.call(["CScript", VB_Hub, jscript, img_path, "//nologo"])
    

###################################################
class DupulicateActionAtCurrentTime(bpy.types.Operator):
    '''add Roop  '''
    bl_idname = "action.to_photoshop"
    bl_label = "open imege on Photoshop"
    def execute(self, context):
        to_photoshop(context)
        return {'FINISHED'}


def menu_func(self, context):
    self.layout.operator("action.to_photoshop", 
        text="Photoshopで開く" )

def register():
    bpy.utils.register_module(__name__)
    bpy.types.IMAGE_MT_image.prepend(menu_func)

def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.IMAGE_MT_image.remove(menu_func)

if __name__ == "__main__":
    register()
##########################################################
フォルダにあるファイルをモジュールとして読み込ませるのに必要なファイルです
通常はアドオンとして読み込ませるための設定と メニューに項目を登録する処理を記述しますが
今回は 本体のスクリプトも短いために こちらに記述しています

処理自体は前回の画像読み込みのスクリプトとほとんど同じですが
前回ではデスクトップに置いていた関連ファイルが同じフォルダ内にあるようにする処理と
表示している画像に日時+ランダムな文字列で Blenderのテンポラリフォルダに保存する処理が加えてあります


他のVB_Hub.vbs img_open.jsxは前回使ったものと同じですが書いておきます
VB_Hub.vbs
Option Explicit
'コマンドライン引数の取得
Dim oParam
Set oParam = WScript.Arguments
'Photoshopを指定
Dim objApp
Set objApp = CreateObject("Photoshop.Application")
Dim arg_count
arg_count = oParam.Count -1 

If arg_count = 0 Then
    '追加の引数がない場合
    objApp.DoJavaScriptFile oParam(0)
Else
    Dim id
 '引数をjavascriptに渡すための配列
    Dim ArgArray()
 ReDim ArgArray(arg_count - 1  )
    '第一引数がスクリプトのパスのため取り除く
    For id = 0 To arg_count - 1
        ArgArray(id) = oParam(id + 1)
    Next
    ''スクリプトの実行
    objApp.DoJavaScriptFile oParam(0),ArgArray
End If


var active_doc = app.activeDocument
//引数で指定したドキュメントを開いて現在のPSDに複製
if (arguments.length > 0){
    var img_file =  File(arguments[0])
    var img = app.open( img_file );
    img.artLayers[0].duplicate(active_doc)
    img.close()
    }
以上のファイルを保存したらフォルダごとアドオンのフォルダにコピーするか
フォルダをzip圧縮してBlenderの「ユーザー設定」の「アドオン」タブ下部にある「ファイルからインストール」をでzipファイルを読み込んで
アドオンを有効にします

正常に読み込まれた場合は
「UV/画像エディッター」の画像メニューに「Photoshopで開く」という項目が追加され
実行するとビュー上で表示している画像をPhotoshopの開いている画像に送ることができるようになります

今回のZipファイルをこちらにアップロードしておきます
いかがでしょうか

2016年11月10日木曜日

BlenderからPhotoshopを制御2-レンダリングした画像をPhotoshpで開く


前回はBlenderのPythonからJavaScriptを実行しましたが
今回はさらにPhotoshopにデータを受け渡すことを考えます。

Blenderのレンダリング画像を保存して、引数で保存した画像のファイルパスを渡しPhotoshopで開くものとします
今回もスクリプト等はデスクトップに置きます

VBHubに受け取った引数をそのままJavaScriptに受け渡す処理を追加します
VB_Hub.vbs
Option Explicit
'コマンドライン引数の取得
Dim oParam
Set oParam = WScript.Arguments
'Photoshopを指定
Dim objApp
Set objApp = CreateObject("Photoshop.Application")
Dim arg_count
arg_count = oParam.Count -1 

If arg_count = 0 Then
    '追加の引数がない場合
    objApp.DoJavaScriptFile oParam(0)
Else
    Dim id
 '引数をjavascriptに渡すための配列
    Dim ArgArray()
 ReDim ArgArray(arg_count - 1  )
    '第一引数がスクリプトのパスのため取り除く
    For id = 0 To arg_count - 1
        ArgArray(id) = oParam(id + 1)
    Next
    ''スクリプトの実行
    objApp.DoJavaScriptFile oParam(0),ArgArray
End If
VBscriptのWScript.Argumentsで引数を配列の形で受け取ります
配列の最初の項目はJavaScriptのパスになるので それ以降をJavaScriptに渡しています


次にJavaScriptです

Photoshopで画像を開くだけなら 実はJavaScriptを使わなくてもいいので
開いた画像を今開いている書類に複製する処理にします。
var active_doc = app.activeDocument
//引数で指定したドキュメントを開いて現在のPSDに複製
if (arguments.length > 0){
    var img_file =  File(arguments[0])
    var img = app.open( img_file );
    img.artLayers[0].duplicate(active_doc)
    img.close()
    }
引数で設定された値は arguments で受け取ることができ、
配列形式で値を取り出すことができます
ここではarguments[0]でファイルパスを受け取り 画像を開くし処理をしています

最後にBlenderのスクリプト
import bpy
import os
import subprocess

#デスクトップフォルダのパス
desktop_path = os.getenv("HOMEDRIVE") + os.getenv("HOMEPATH") + "\\Desktop"
#実行するjavascriptのファイル名
js_name = "img_open.jsx"
#ファイルパスを実行するOSの形式で取得
js_path = os.path.normcase( os.path.join(desktop_path, js_name) )
VB_Hub = os.path.normcase( os.path.join(desktop_path, "VB_Hub.vbs") )
#画像を保存するパス
img_path  = os.path.join(desktop_path, "render_img.png")
#レンダリング画像を保存
bpy.data.images['Render Result'].save_render( filepath= img_path)
#VBScript経由でjavascriptを実行
subprocess.call([ "CScript",VB_Hub, js_path, img_path, "//nologo" ])
レンダリング画像をデスクトップに保存して JavaScriptのパスといっしょに保存したファイルのパスを渡しています。

実行前にPhotoshopには適当な書類を開いて置き Blenderでレンダリングで画像を生成しておいてください
スクリプトの実行で Potoshopにレンダリング画像がレイヤーとして読み込まれたでしょうか?

ここまでは説明のためにシンプルな動作のものを作成しましたが
次は実用的なようにBlenderのアドオンにしたものを紹介したいと思います

BlenderからPhotoshopを制御1-Blender-PyhtonとPhotoshop-JavaScriptについて


3Dソフトと Photoshop等と連携してより効率的に作業するためのメモ
同じようなやり方でMacOSXでもできますが 今回はWindows環境での話

3Dソフトのレンダリング結果をPhotoshopの画像処理に利用したり
UVテクスチャを作画したりしていると、よりシームレスに作業できればと感じることが多々あります。

PhotoshopはJavaScript等を使って色々な処理を追加できるので、
Blenderの自動処理言語でもあるPythonを使った連携を考えます

どうやってjavascriptを実行するのか?

Pythonではsubprocessモジュールを使えば
コマンドプロンプトで実行するように他のプログラムを実行させたりできます 

これでjavascriptをBlenderから直接実行できればいいのですが 色々と問題がありうまくいきません

そこで、一旦VBScriptの中継ソフトを介してしてJavaScriptを実行することにします
今回作成するのは実行したいJavaScriptのファイルパスをPhotoshopに受け渡すだけのシンプルなものです
この中継ソフトをVB_Hubと名づけることにします。
(実はPhotoshopはVBScriptでも操作ができるのですが、JavaScriptの方が情報が多かったり扱いやすいです)




今回はデスクトップに 中継ソフトの VB_Hub.vbs テスト用のJavaScriptのtest.jsx を置いて
それを実行してみます

デスクトップにVB_Hub.vbsとtest.jsxという名前でテキストファイルを作成して下記のコードをそれぞれに保存し
Photoshopを起動して空白の書類を開いておいてください

VB_Hub.vbs
Option Explicit
'コマンドライン引数の取得
Dim oParam
Set oParam = WScript.Arguments
'実行するアプリケーションとしてPhotoshopを指定
Dim objApp
Set objApp = CreateObject("Photoshop.Application")

''スクリプトの実行
objApp.DoJavaScriptFile oParam(0)
var active_doc = activeDocument
//レイヤの作成
active_doc.artLayers.add()
JavaScriptはPhotoshopで開いている書類に新しいレイヤを作る簡単なものです

最後にBlenderのテキストエディッタに下記コードをコピペしてスクリプト実行をします
import subprocess
import os
#デスクトップフォルダのパス
desktop_path = os.getenv("HOMEDRIVE") + os.getenv("HOMEPATH") + "\\Desktop"
#実行するjavascriptのファイル名
js_name = "test.jsx"
#ファイルパスを実行するOSの形式で取得
js_path = os.path.normcase( os.path.join(desktop_path,js_name) )
VB_Hub = os.path.normcase( os.path.join(desktop_path,"VB_Hub.vbs") )
#VBScript経由でjavascriptを実行
subprocess.call([ "CScript",VB_Hub, js_path, "//nologo" ])
うまく新しいレイヤが作成されたでしょうか

今回は単純にJavaScriptを実行しただけですが
BlenderのPyhtonから情報を引数で受け渡して処理をかえることもできます
それは今後の記事で書いていきたいと思います