Photoshop,Python 実践PythonでPhotoshopスクリプト 開いてる画像をPNG形式で全部保存

実践PythonでPhotoshopスクリプト 開いてる画像をPNG形式で全部保存

py_ps_pngsave_topimage

以前にPythonでPhotoshopにアクセスする記事を書きましたが、
今回はその記事の実践編として、実際にツールとして使えるモノを作りたいと思います。

スクリプト名
「開いている画像を名前に連番を付けてPNG形式で全部保存するツール。」

3枚の新規ファイルを名前の後に連番を付けてPNG形式で保存します。

image3


必要な機能の整理

この操作を一連で行えば目的は果たせそうです。

完成したPythonスクリプト

上記の機能を入れたスクリプトです。

このスクリプトを実行すると、
保存するフォルダを選択しファイル名を入力するダイアログが表示されます。
[保存]をクリックすると、すべて連番の名前付きで保存されます。

import os
import win32com.client


folderList=[]
folderPath_all =""

psCom = win32com.client.Dispatch('PhotoShop.Application')

saveFileFllPath = psCom.DoJavaScript("File.saveDialog('select folder').fsName;")

fileName = os.path.splitext(os.path.basename(saveFileFllPath))[0]

folderPath_Only = os.path.dirname(saveFileFllPath)

while os.path.split(folderPath_Only)[1] != "Users":
    folder_Only = os.path.split(folderPath_Only)[1]

    folderList.insert(0,folder_Only)

    folderPath_Only = os.path.split(folderPath_Only)[0]


for i_folder in folderList:
    folderPath_all += i_folder + "/"


documentNum =psCom.Documents.Count

for i in range(documentNum):
    psCom.ActiveDocument = psCom.Documents.Item(i+1)

    jsxScript = """var idsave = charIDToTypeID( 'save' );
    var desc20 = new ActionDescriptor();
    var idAs = charIDToTypeID( 'As  ' );
    var desc21 = new ActionDescriptor();
    var idMthd = charIDToTypeID( 'Mthd' );
    var idPNGMethod = stringIDToTypeID( 'PNGMethod' );
    var idthorough = stringIDToTypeID( 'thorough' );
    desc21.putEnumerated( idMthd, idPNGMethod, idthorough );
    var idPNGF = charIDToTypeID( 'PNGF' );
    desc20.putObject( idAs, idPNGF, desc21 );
    var idIn = charIDToTypeID( 'In  ' );
    desc20.putPath( idIn, new File( '%s' ) );
    var idDocI = charIDToTypeID( 'DocI' );
    desc20.putInteger( idDocI, 385 );
    var idsaveStage = stringIDToTypeID( 'saveStage' );
    var idsaveStageType = stringIDToTypeID( 'saveStageType' );
    var idsaveBegin = stringIDToTypeID( 'saveBegin' );
    desc20.putEnumerated( idsaveStage, idsaveStageType, idsaveBegin );
    executeAction( idsave, desc20, DialogModes.NO );"""   % ("C:/Users/" + folderPath_all + fileName+"_" + str(i).zfill(2)+".png")


    psCom.DoJavaScript(jsxScript)

実行してみます。

ダイアログが表示されたらフォルダと名前を指定 [保存]

ダイアログで指定

すべて指定のフォルダにPNGで保存されました。

PNGで出力ファイル

スクリプトの解説

Photoshopにアクセス


import win32com.client

psCom = win32com.client.Dispatch('PhotoShop.Application')

COMでアクセスします。この部分の詳しい説明は以前の記事で紹介しているのでそちらをご覧になってもらえると良いと思います。

これでPythonからPhotoshopを操作できるようになりました。

保存するファイル名を取得

saveFileFllPath = psCom.DoJavaScript("File.saveDialog('select folder').fsName;")

fileName = os.path.splitext(os.path.basename(saveFileFllPath))[0]

保存するフォルダ、ファイル名の入力のダイアログを表示して、
ファル名と保存先のパスを受け取ります。

基本的にGUIに絡むところはPhotoshop依存にならざるを得ないので、
Javascriptで対応できるところは無理をせずDoJavaScript()で書きます。

File.saveDialog('select folder').fsName;


いわゆる「別名で保存」のダイアログですが、
最後に .fsName とオプションを入れてます。

オプションを入れないで書くと

~\フォルダ名\フォルダ名\ファイル名


とホームディレクトリの前までしか返してくれません。

.fsNameオプションを入れると

c:\フォルダ名\フォルダ名\フォルダ名\ファイル名


とホームディレクトリを含めたフルパスが受け取れます。

ホームディレクトリはPythonで取得して後から取得したパスと合わせれば良いだけですが、今回は.fsName のオプションを付けてフルパスを受け取りました。

この部分はどちらでも良いと思います。むしろこのスクリプトではPythonでホームディレクトリを取得して後でパスと合わせるほうが正解かもしれません。

#
os.path.splitext(os.path.basename(saveFileFllPath))[0]


basename()でダイアログから受け取ったパスから拡張子付きのファイル名を抜き取りました。

その拡張子付きファイル名をsplitext()でファイル名と拡張子で分割してファイル名のみ取得しました。

実際はダイアログに入力する名前、そして受け取るパスには拡張子はあってもなくてもどちらでも良いです。

拡張子が無い名前でもsplitext()で返されるのは

[“ファイル名”,””]


と返されます。

なので os.path.splitext(“ファイル名”)[0] と[0]を指定するとファイル名だけが取得出来ます。

保存するディレクトパスを取得

#
folderPath_Only = os.path.dirname(saveFileFllPath)


dirname()でダイアログから受け取ったパスからファイル名を除いたディレクトリパスを取得しました。

ただココで問題があります。
受け取ったパスの記述が少なくともWindowsでPythonを扱うには喜ばしくない記述方法で返されます。

パスの区切り文字が “\” バックスラッシュ(円マーク)です。

c:\フォルダ名\フォルダ名\フォルダ名\

このままではPNGでセーブをしようとするとエラーが出てしまいます。

文字列を replace() などで差し替えようとしましたが 改行コード になってうまくいきません。

なのでフォルダ名だけを抜き出して後でパスを書き直す事にしました。


while os.path.split(folderPath_Only)[1] != "Users":
    folder_Only = os.path.split(folderPath_Only)[1]

    folderList.insert(0,folder_Only)

    folderPath_Only = os.path.split(folderPath_Only)[0]

os.path.split(folderPath_Only)[1]


パスの最後のフォルダ名を抜き出します。
それをwhileを使って”Users”になるまで順番に抜き出して配列folderListに入れます。


for i_folder in folderList:
    folderPath_all += i_folder + "/"


folderListに入れたフォルダ名をパスの区切り文字 ”/” で書き直してます。

これでエラーの出ないパスが出来ます。

/フォルダ名/フォルダ名/フォルダ名 

画像が幾つあるのかを取得

#
documentNum =psCom.Documents.Count


Photoshopで画像が幾つあるのかを取得します。.Count でドキュメントの数を返してくれます。

Pythonなのでlen(psCom.Documents)でも取得できます。

PNGですべて保存

for i in range(documentNum):
    psCom.ActiveDocument = psCom.Documents.Item(i+1)

取得した画像ドキュメントの数で ループ処理します。

Documents.Item()でPhotoshopで開いている画像の番号を指定してアクティブにします。

Photoshopの画像の番号は 0番からではなく1番からなので.Item(i+1)としています。

PNGで保存するところはScriptingListenerのログを使いました。

ScriptingListenerの説明は別の記事で書いたので、
まだ読んでない方はそちらの記事も読んでもらえると良いと思います。

まだScriptingListenerをダウンロードしてない方はダウンロードしてください。

アドビシステムズの公式ページからダウンロード出来ます。

zipファイルがダウンロードされました。

plug-in-zipファイル

解凍します。

フォルダ Scripting Utilities を

C:\Program Files\Adobe\Adobe Photoshop 2020\Plug-ins

の下に置きます。
Photoshopを起動します。


新規ファイルを作成
別名で保存
保存形式にPNGを選択
保存をクリック
オプション画面で任意の選択 [OK]

PNGオプション

Photoshopを閉じます。

できるだけ必要な操作以外はしないで操作を終わらせましょう。
操作すればするだけログが大量に出力されるので、
必要な箇所を探すのが困難になります。

デスクトップに一連の操作が Javascript と VBScript のログとして出力されました。
Javascriptのファイル
 ScriptingListenerJS.log を使うので間違えないでください。

js_log_file  VB_log_file



C:\Program Files\Adobe\Adobe Photoshop 2020\Plug-ins

からScripting Utilitiesのフォルダを別の場所に移しておきましょう。
フォルダを置いておくとログが出力されつづけてしまいます。


ログの内容を見てみましょう

出力されたファイルScriptingListenerJS.logをテキストエディタ等で開きます。


var idsave = charIDToTypeID( “save” );

“save”と書いある場所を見つけてください。

new File(“ファイルパス”)

と保存先のパスが書かれているところが、セーブの箇所だと思われます。
この箇所をコピーします。

js_logcode

これをJavaScriptのコード部分 jsxScript =”””javascript code””” にコピー・ペーストします。

ただ幾つか修正しないとこのままのコードでは使えません。

(” “)ダブルクォーテーションで書かれている所は(‘ ‘)シングルクォーテーションに変更します。

new File(“保存パス”)

の所は ダイアログから取得したパスとファイル名を指定したいので 

new File(‘%s’)と変更します。

#
% ("C:/Users/" + folderPath_all + fileName+"_" + str(i).zfill(2)+".png")


パス+ファイル名+番号+拡張子を合わせました。
 new File(‘%s’) に差し込みます。

パスの先頭部分 “C:/Users/” は直書きしてますが、
Python でホームディレクトリを取得しても良いと思います。

str(i) で数字を文字列に変更、
.zfill(2) で 01 02 03 となる 0埋めになるようにしました。

#
psCom.DoJavaScript(jsxScript)


そして .DoJavaScript(jsxScript) でJavaScriptを実行、PNGで保存します。

まとめ

PythonでPhotoshopのツールを作ろうとすると、Photoshopの固有のコードが入ったJavaScriptが必要になります。

GUIに絡む所はどうしてもPhotoshop固有のコードが必要になります。

戻り値もPythonに都合の良い返され方ではありません。

ScriptingListener と DoJavaScript() を活用しよう。

戻り値を扱う場合は一旦Pythonに都合の良い状態にしてから処理するのが良いと思います。
むりやりそのままの値で処理しようとしてドツボにハマるよりは全然良いかと。

特にプラグインを入れて特殊なファイルフォーマットで保存する場合などでは、コード自体がわかりません。

そんな時はScriptingListenerDoJavaScript()を使って対応するのが良いと思います。

PhotoshopのスクリプトをPythonで書く利点は、Photoshopを始めとしたAdobe製品以外との連携にあると思います。

なのでPhotoshop固有の部分はJavaScriptで、外部ツールとの連携等それ以外の部分はPythonで、のようにするのがベストかなと考えます。

PythonでPhotoshopを扱うのは面倒くさい部分が色々ありますが、外部ツールとの連携などPhotoshop固有のスクリプトでは困難な部分をクリアしてくれる利点があります。

是非「PythonでPhotoshop」を検討してみてはどうでしょうか?


おすすめの関連記事:
コチラの記事も読まれています
あわせてよく読まれている記事