再帰処理

スクリプトを他人に説明する時、一番説明しにくいのは、再帰処理です。「自分自身を呼び出す処理」とか言っても、即座には何のことやらイメージできないですもんネ。

 

前回記事のレイヤーをファイルとして書き出す処理も、「もしレイヤーがレイヤーフォルダだったら、レイヤーフォルダの中身に潜って、同じ処理を繰り返す」再帰関数を作って処理していましたが、スクリプト文の引用を避けたのは、再帰関数の説明が長くなりそうだったからです。

 

レイヤーフォルダは、Photoshopの内部では「LayerSet」と呼ばれています。typename(当該項目がどんな種別かを判別する文字列)を調べると、普通の画像レイヤー(背景レイヤーやテキストレイヤーも含む)は「ArtLayer」、レイヤーフォルダは「LayerSet」という文字列が返ります。もし処理するレイヤーのtypenameが「LayerSet」だったら、中身に潜って処理を続行しないと、1階層しか処理できないポンコツスクリプトになってしまいます。

 

かと言って、前回のニーズだと、「レイヤーフォルダ階層の底まで潜ると潜り過ぎ」という条件がありましたので、「セルごとの連番レイヤーフォルダなら潜る。そうでない場合は潜らない。」というIF分岐を設けました。IF分岐はレイヤーフォルダ名で判別しました。

 

 

 

再帰処理は、私がスクリプトを覚え始めた20年前の初心の頃に、ウンウン唸りながら理解を進めました。まあ、普通の日常に、再帰処理なんて意識することはないので、もしかしたら「オブジェクト指向」よりも解りにくいかも知れません。オブジェクト指向的アプローチは、日々の料理のバリエーション展開など、身の回りに豊富ですが、再帰ルーチンは日常ではイメージしにくいです。

 

例えば、レイヤーフォルダに含まれるアートレイヤーの数を数えるスクリプトを考えた場合、レイヤーフォルダの階層が3階層までだったら、以下のように3回「for」ループで中身を総当たりで潜って処理できます‥‥が、まあ、こういうやり方は逆に大変です。

 

var artLayerCount=0;
for (var i=0;i<app.activeDocument.layers.length;i++){//第1階層
    if(app.activeDocument.layers[i].typename=="LayerSet"){
        for (var ii=0;ii<app.activeDocument.layers[i].layers.length;ii++){//第2階層
            if(app.activeDocument.layers[i].layers[ii].typename=="LayerSet"){
                for (var iii=0;iii<app.activeDocument.layers[i].layers[ii].layers.length;iii++){//第3階層〜これ以上潜らない
                    if(app.activeDocument.layers[i].layers[ii].layers[iii].typename=="ArtLayer"){artLayerCount++;}
                }
            }else{
                if(app.activeDocument.layers[i].layers[ii].typename=="ArtLayer"){artLayerCount++;}
            }
        }
    }else{
        if(app.activeDocument.layers[i].typename=="ArtLayer"){artLayerCount++;}
    }

}
alert(artLayerCount);

 

 

最大3階層までのレイヤーフォルダの中身のアートレイヤーをカウントして‥‥

 

 

‥‥と、一応は正確なレイヤー数はカウントしてくれます。

 

しかし、この3階層分の入れ子を直に文で書いてしまうと、正直、「今、何階層目か」、文を書いてて解らなくなります。非常に煩わしくて面倒です。ループを回す「i」変数を ii iii と切り替えるくだりは、煩雑そのもので書いてて混乱します。

 

書くのが面倒で読みにくい深い構造で、メンテナンス性(後で書き換えるなど)が最悪なのに、第4階層以下は潜ってくれず、処理してくれません。大変なわりに機能がショボい。

 

第4階層以上は潜れないので、

 

 

‥‥のような深い階層を持つレイヤー構造に対応できず、あくまで第3階層までしかカウントできません。

 

 

 

なので、再帰関数の出番。

 

var artLayerCount=0;
alert(artLayerCounter(app.activeDocument.layers,artLayerCount));

 

function artLayerCounter(_layers,_count){
    for (var i=0;i<_layers.length;i++){
        if(_layers[i].typename=="LayerSet"){
            _count=
artLayerCounter(_layers[i].layers,_count);//自分自身を呼びだす
        }else{
            if(_layers[i].typename=="ArtLayer"){_count++;}
        }
    }
    return _count;
}

 

 

「artLayerCounter()」という関数の中で、処理対象のレイヤーの種別がレイヤーフォルダだった場合は、関数自身にレイヤーフォルダの中身を入れ子で投げ込んでいます。

 

この構造ならば、階層が10階層だろうが、延々と中に潜って処理します。

 

しかも、文字数も少なく、見た目の階層は1階層なので、メンテナンス性も高いです。

 

 

 

 

ちゃんと、テキストレイヤー、画像レイヤー、背景レイヤーの合計を正確にカウントできています。

 

 

 

再帰処理を使わないと、文が長く読みにくくなる上に、処理対象の階層が深くなると機能は限定されて、いいとこなし‥‥です。

 

私は、一回でも再帰的な入れ子の処理が出てきた時は、迷わず再帰処理を使います。「潜るのは一度きりで今回限り」とは限らない事例が多いからです。後日に、もっと階層を潜れるニーズが発生した時に、再帰処理であらかじめ組み立てておけば、簡単な手直しで対応できます。

 

Photoshopに限らず、After Effectsのプリコンポーズした入れ子にも使えますし、OSのフォルダ階層にも応用できます。再帰処理を使えば、どんなに深いフォルダのファイルでも総当たりで一覧を作成できますから、過去作品のディスクアーカイブの内容一覧にも使えますネ。

 

再帰処理は、プログラムを覚えて色々な自動処理のパワーを実感する中で、「これは人力では不可能だ」と特に感じるものです。

 

アニメ制作における様々な事務的な雑事は、まさに再帰的な処理、反復処理の連続ですから、コンピュータを「絵や映像を作る時だけ」でなく、「作る前と作った後」にも活用して、全体的な作業効率を高めましょう。

 

アニメの映像そのものを作るのに精魂を使い果たした後で、事務的な処理にさらに1〜2時間居残りで作業‥‥なんて、できるだけ回避したいですもんネ。

 

 


原画PSDファイルから書き出し

Photoshop形式の原画上がり=各セルの連番をまとめたレイヤーフォルダやレイアウトやフレーム指示のレイヤーなどを、あたかもタイムシートに挟んだ原画上がり一式として別ファイル出力するPhotoshopのスクリプトを今日必要に迫られて作りました。前々から作っておくべき!‥‥と考えていたのですが、ようやく重い腰を上げて作った次第です。

 

今回作ったのはコレ。

 

 

書き出すファイル形式は、当座の都合でTargaにしていますが、レイヤーなしのTIFFでもPSDでも可能です。絵が荒れるのでJPEGは使う予定はないですが、JPEGでもGIFでもPNGでも可能です。アルファチャンネルはファイル形式と状況によって適宜。

 

そして、(いつ作るかはニーズに応じて未定ですが)コレも用意しておくと、特定のプラットフォームやソフトウェアに縛られず、ファイルとレイヤーの行き来が可能になりましょう。

 

 

もはや、PSDファイルのレイヤー構造は、AdobeのPhotoshopでの使用に限定したものではなく、「レイヤー付き画像フォーマットといえばPSD」というくらい各所で普及していますから(ProcreateでもPhotoshopのレイヤー構造を保ったまま読み書き可能)、この2つのスクリプトは便利に使えると思います。

 

スクリプト自体には何の難しいところもないです。レイヤーの表示(=visible)を出力の基準にして、レイヤーの種別(ArtLayerやLayerSet)に応じて、当該のファイルフォーマットで複製保存するだけです。まあ、「常時表示するレイヤー」とかをちゃんと見分ける仕込みは必要ですが、基本的には難しいことはないです。

 

難しいのは、そのPhotoshopの「レイヤー構造の成り立ち」のほうです。

 

階層の深さの規定は、絶対に必要です。また、レイヤー名に基づいて出力するので、レイヤー名とその親フォルダの命名規則は極めて重要です。

 

でも、そういう部分が一番ルーズになるものです。人間が介在する部分が、一番「揺れ」ます。

 

なので、当座はスクリプト処理の都合も含めて、以下のように定めました。

 

  • スクリプトが掘る階層は、レイヤーフォルダの第1階層まで。
  • 静止画としてのレイヤーフォルダと連番格納のレイヤーフォルダは名前で判別する。
  • 連番レイヤーは、親フォルダ名とレイヤー名を連結してセルのファイル名とする。
  • 常時表示のレイヤーはレイヤー名先頭に判別文字を付与する。

 

 

とはいうものの、私のメインはあくまでCO/KFアニメーションなので、「従来作画のタブレット化・データ化への関与」はやんわりと距離を置くべきと考えており、上記程度の決め事で様子見です。

 

 

 

「デジタル作画」のここらへんの決まりって、誰か決める人、いないのかな‥‥。「デジタル作画」を自分のメインにしていこうと思っている人たちが、自分たちで決めていくのが良いと思います。私がフガフガ鼻息荒くして言う立場じゃないです。

 

一方、CO/KFアニメーションでの線画作業については、徐々にガイドラインを構築しつつあります。レイヤー名の命名規則も、

 

object.line

object.shadow.region

object.shadow.line

 

‥‥など、何度かの紆余曲折を経て、固まりつつあります。コンピュータで用いる規則ですから、プログラム・スクリプトでも処理しやすいように考えています。

 

 

 

Photoshopのスクリプトは久々に書きましたが、After Effectsとは似ているようでちょいちょい異なるので、多少混乱しました。

 

「layers」って、リファレンスには「The collection of layer objects」って書いてありますが、After Effectsでは「collection」は「コレクションオブジェクト」といいまして、

 

インデックス番号1から始まり()で呼び出す集合

 

‥‥なのに、Photoshopでは、

 

インデックス番号0から始まり [ ] で呼び出す配列

 

‥‥なのは、当初混乱しました。

 

また、Targaを書き出す際に用いる「TargaSaveOptions」のプロパティに、

 

resolution :The number of bits per pixel

 

‥‥があって、「タルガって解像度があったんか!」と一瞬驚きましたが、よく読むと、

 

1ピクセルあたりのビット数

 

‥‥で、初期値は「TargaBitsPerPixels.TWENTYFOUR」=24bitとありますから、いわゆるピクセルの色深度(各チャンネルの深度)のことですネ。1ピクセルあたりに使えるビット数という定義なのか、depthとは言わんのネ。

 

スクリプトの内容は、普通なら手作業でレイヤーのオンオフを切り替えて別名保存するのを、自動処理するものなので、何の特別な技も必要ないです。ただ、段取りが面倒なだけ‥‥です。

 

スクリプト動作中は、Photoshopが固まったようになるので、スクリプトが動いているんだかエラー停止しているのかイマイチ判別ができませんが、Finder上でフォルダが自動で作成されて、その中にファイルがどんどん増えていくのを見ながら、動作を確認しました。

 

 

 

なんとなく手作業のまま処理して、結構な時間を消費していた作業が自動化できました。今回はTarga書き出し版ですが、TIFF書き出し版(dpi〜解像度をもつ)も一部変更するだけで作れます。

 

必要なマニュアルは、以下のPDFにて。

 

Photoshop CC JavaScript Reference

 

フォルダなどファイルシステム管轄の操作は「JAVASCRIPT TOOLS GUIDE」(ESTKのSDKフォルダの中にあります)で「Folderオブジェクト」の項を読めば解ります。

 

 

 

 


sips

macOSには「Image Events」という裏方のアプリケーションがあって、AppleScriptからも利用することができます。以下はその機能の一部です。

 

save v : Save an image to a file in one of various formats

save specifier : the object for the command

[as BMP/‌JPEG/‌JPEG2/‌PICT/‌PNG/‌PSD/‌QuickTime Image/‌TIFF] : file type in which to save the image ( default is to make no change )

[icon boolean] : Shall an icon be added? ( default is false )

[in text] : file path in which to save the image, in HFS or POSIX form

[PackBits boolean] : Are the bytes to be compressed with PackBits? ( default is false, applies only to TIFF )

[with compression level high/‌low/‌medium] : specifies the compression level of the resultant file ( applies only to JPEG )

 

rotate v : Rotate an image

rotate specifier : the object for the command

to angle real : rotate using an angle

 

scale v : Scale an image

scale specifier : the object for the command

[by factor real] : scale using a scalefactor

[to size integer] : scale using a max width/length

 

 

つまり、Image Eventsを使って、AppleScriptをユーザーの窓口にすれば、ちょっとした画像形式変換ソフトが作れるわけです。書き出せる形式は、上の通り、「BMP JPEG JPEG2 PICT PNG PSD QuickTime Image TIFF」と、とりあえずは困らないくらいの幅広さはあります。画像の回転やサイズ変更、切り取りも可能です。‥‥PICTはさすがにもう使わんけど。

 

AppleScriptに慣れない場合は、シェル経由の「sips」コマンドでも同じことが可能です。AppleScriptとImage Eventsの組み合わせだととかくスクリプト文が長くなりがちですが、シェルならば多少冗長にはなりますが1行で済みます。

 

まずは「man sips」でマニュアルに目を通してみれば、結構な画像変換ができることがわかります。「sips -h」や「sips -H」でも、色々なオプションが確認できます。‥‥文量が多いので、ここではあえて引用しませんが。

 

処理できる内容を見てみると、どうもImage Eventsとsipsは同じもの‥‥なんでしょうかね。その辺の内部事情は知らないので何とも言えませんが、できることは共通しています。

*ただ、Image Eventsの方の用語辞書をみると(上記の)、Targaが抜けてますね。書き忘れ?

 

 

 

例えば、中に144ファイルのPSD連番が入ったフォルダを丸ごとJPEGに変換する‥‥なんていうのも1行コマンドで簡単に、しかも144ファイルの2KサイズPSD程度なら数秒で処理できます。

 

sips -s format jpeg PSD連番フォルダパス/*.psd --out 書き出すフォルダパス

 

JPEGの圧縮率なども指定可能ですが、まずは簡単な1文にて。

 

フォルダの中身はもう自分で承知しているから大丈夫‥‥という人は、

 

sips -s format jpeg PSD連番フォルダパス/* --out 書き出すフォルダパス

 

‥‥という書き方でもOKです。/* はフォルダの中身全部‥‥という意味です。

 

アニメ現場ではおなじみのTargaも書き出せます。

 

sips -s format tga PSD連番フォルダパス/* --out 書き出すフォルダパス

 

ただし、Targaで書き出すと、解像度は72dpi‥‥というか「値無し」になります。アニメ業界でこれだけ常用されているのに意外に思いますが、Targaにはもともと「平方あたりの画素・ドットの数」という概念がありません。ビデオに特化したフォーマットだったらしく、印刷とか実物スキャンは全く考慮されていないのです。

*ただ、Targaには「開発者が使えるデータ領域」があるので、そこに独自拡張で解像度を記録することはできると思います。でもまあ、あくまで独自拡張なので一般では通用しないですけどネ。

 

 

コマンドは使い続けていないとすぐに忘れてしまいがちなので、そんな時はAppleScriptと組み合わせて、ドラッグ&ドロップのJPEG書き出しドロップレットを作ってしまいましょう。

 

処理するフォルダと新作するフォルダをAppleScriptのFinder命令で指定して、POSIX Path(スラッシュ区切りのUNIX形式のパス)に変換して、シェルに渡します。

 

どうせなので、アプレットとドロップレット両方で動作するアプリケーションにします。

 

on run

    tell application "Finder"

        set importFolder to choose folder with prompt "変換するフォルダを指定"

        set exportFolder to choose folder with prompt "書き出すフォルダを指定"

    end tell

    my saveAsJPEG(importFolder as Unicode text, exportFolder as Unicode text)

end run

 

on open theItems

    repeat with theItem in theItems

        set importItem to theItem as Unicode text

        tell application "Finder"

            if folder importItem exists then

                make new folder at container of theItem with properties {name:((name of theItem) as Unicode text) & "-JPEG"}

                set exportItem to result as Unicode text

            else

                set exportItem to ((characters 1 thru -((length of ((name extension of theItem) as Unicode text)) + 2) of importItem) as Unicode text) & ".jpg"

            end if

        end tell

        my saveAsJPEG(importItem, exportItem)

    end repeat

end open

 

to saveAsJPEG(_i, _e)

    set _all to ""

    if character -1 of _i is ":" then set _all to "*"

    do shell script "sips -s format jpeg " & (quoted form of POSIX path of (_i as Unicode text)) & _all & " --out " & (quoted form of POSIX path of (_e as Unicode text))

end saveAsJPEG

 

 

このスクリプト文にテキトーな名前をつけてアプリケーション形式で保存すれば、ダブルクリックでもドラッグ&ドロップでも動作する、自作のmacOS用アプリの完成です。

 

 

シェルに投げるまでのお膳立てをAppleScriptとFinderでおこなう仕組みですので、実は肝心の「画像形式変換」の部分は1行のみです。アプリを作るのは前置きが長いのです‥‥って、これでもかなり短い部類です。

 

ブログにちょろっとペーストできるほど短い文で済んでいるのは、なによりも「エラー対策」をほぼ全部端折っているからです。もし、同階層に同じ名前のフォルダやJPEGファイルがあった場合はエラーで止まりますし、アイテムを分別する条件分岐もアマアマです。テキストファイルをドロップしても動作を開始してしまう構造です。

 

でも、作った本人だけが使う分には問題はありません。自分で文を書いて作ったがゆえに、隅々まで処理内容を知っているので、うっかりでもない限り、エラーを自ら呼び込む使い方をしないからです。

 

 

まあ、2019年現在にAppleScriptを覚えよう!‥‥なんて薦めませんが、何かしらのプログラム・スクリプト言語を日頃から手に馴染ませておくことで、自分で使うための自分専用アプリを、思いついた時、もしくは必要に迫られた時に、サクッと作れるのは、コンピュータを使う醍醐味・利点でもあります。

 

‥‥そういえば、sipsはTargaも書き出せますから、画像連番を一気にTargaに変換するのも作っておきましょう。

 

on run

    tell application "Finder"

        set importFolder to choose folder with prompt "変換するフォルダを指定"

        set exportFolder to choose folder with prompt "書き出すフォルダを指定"

    end tell

    my saveAsTarga(importFolder as Unicode text, exportFolder as Unicode text)

end run

 

on open theItems

    repeat with theItem in theItems

        set importItem to theItem as Unicode text

        tell application "Finder"

            if folder importItem exists then

                make new folder at container of theItem with properties {name:((name of theItem) as Unicode text) & "-TARGA"}

                set exportItem to result as Unicode text

            else

                set exportItem to ((characters 1 thru -((length of ((name extension of theItem) as Unicode text)) + 2) of importItem) as Unicode text) & ".tga"

            end if

        end tell

        my saveAsTarga(importItem, exportItem)

    end repeat

end open

 

to saveAsTarga(_i, _e)

    set _all to ""

    if character -1 of _i is ":" then set _all to "*"

    do shell script "sips -s format tga " & (quoted form of POSIX path of (_i as Unicode text)) & _all & " --out " & (quoted form of POSIX path of (_e as Unicode text))

end saveAsTarga

 

 

数カ所変更するだけで、主要な画像形式のファイルを連番丸ごと変換するmacOSアプリの出来上がりです。

 

 

 

ちなみに、今回は画像保存時のオプションを一切省いていますが、JPEGなら圧縮率とかTIFFならLZW圧縮の有無とかを指定できます。拡大縮小や、回転(現場ではあまり必要ないけど)なども処理できます。

 

 


604800

Mac版のESTKはひっそりと提供が終了しており、昔のフォルダから移植して使っている状態です。

 

去年の終わり頃に、ESTKでアラートが出て動作しないトラブルがありましたが、私は最近まで対処しないままでした。絵を描く方がメインになって、ESTKは使う機会が減っていたので。

 

ESTKのappのパッケージを掘っていって「604800000」を「604800」に書き換えれば終了‥‥なのですが(説明を端折ってスミマセン。詳細はネットで検索してください。)、あくまで当座ローカルにあるESTK.appが書き換わっただけで、他の環境でも同じことをせねばなりません。

 

う〜ん。覚えてられない。階層も深いし。

 

なので、さくっとスクリプト。AppleScriptで動作する「6048自動書き換えスクリプト」です。このスクリプトをクラウドに保存しておいて、未処置のESTKと遭遇した場合に実行すれば、どこにいても解決できます。

 

 

tell application "Finder"

    set estk to choose file with prompt "ExtendScript Toolkit.appを指定してください"

    if (name of estk) is not "ExtendScript Toolkit.app" then

        beep

        return false

    end if

    set targetFile to (estk as Unicode text) & "Contents:SharedSupport:Required:cdic:11BTBackend.jsx"

end tell

do shell script "sed -i'-bak' -e 's/604800000/604800/' " & (quoted form of POSIX path of targetFile)

 

 

最後の1行はAppleScriptというよりは、sed。

 

最後の1行が肝心の部分です。それより前はESTKを指定するまでの前置きです。

 

sedは初めて使ってみましたが、便利ですネ。

 

一応バックアップを取った後で書き換えています。バックアップがiオプションでできるのも便利ですネ。AppleScriptでは全部自分でサブルーチンを作らねばならんスから。

 

AppleScriptは、シェルコマンドも実行できるし、Adobeのスクリプトも実行できるので、今でも現役です。今から大掛かりな新しい何かをAppleScriptで作ろうとは思いませんが、文房具のテープやカッターくらいの気持ちで気軽に使えるのが今でも現役の理由です。

 

 

 

 


ESTKのスクリプトをAppleScriptに埋め込む

前々回のブログで、せっかく、それなりに高速処理のリネームスクリプトができたので、ESTKエディタからの実行だけでなく、AppleScriptでGUIをくっつけて、気軽にドラッグ&ドロップでも処理できるようにしました。

 

アプレットでもドロップレットでも動作するように、「on open」と「on run」を併用しています。

 

macOSのアプリケーションフォルダにある「スクリプトエディタ」を起動して、おもむろにAppleScriptとESTKの合体コードを作ります。

 

 

property defaultItem : false

 

on open theItems

    my main(theItems)

end open

 

on run

    tell application "Finder" to if defaultItem is false or not (defaultItem exists) then set defaultItem to home as alias

    set res to choose folder with prompt "処理するフォルダを選択してください." default location defaultItem with multiple selections allowed

    tell application "Finder" to set defaultItem to (parent of (item 1 of res)) as alias

    my main(res)

end run

 

to main(_folders)

    set errorText to {"フォルダ指定が不正です", "フォルダが空です"}

    set errorLog to ""

    repeat with _folder in _folders

        set res to my runAdobeScript(_folder as Unicode text)

        activate

        if res as integer > 0 then

            tell application "Finder" to set folderName to name of _folder

            set errorLog to errorLog & folderName & tab & res & tab & (item (res as integer) of errorText) & return

        end if

    end repeat

    activate

    if errorLog is "" then

        display dialog "処理が正常に終了しました" with icon 1

    else

        display dialog "エラーが発生しました

下記のエラーログを確認してください" with icon caution default answer errorLog

    end if

end main

 

to runAdobeScript(folder_path)

    tell application "Adobe After Effects CC 2018"

        DoScript "app.exitCode=0;

_1801_RENAME_RE=new RegExp('[_-]*[0-9]+¥¥.[A-Za-z0-9]{2,}');

app.exitCode=main();

 

function main(){

    var theFolder=new Folder('" & folder_path & "');

    if(!theFolder){return 1;}

    var theFiles=theFolder.getFiles();

    if(theFiles.length<1){return 2;}

    for(var i=0;i<theFiles.length;i++){

        var reMatch=theFiles[i].name.match(_1801_RENAME_RE);

        if(reMatch&&theFiles[i].name.match(/^[A-Za-z0-9]/)){

            theFiles[i].rename(theFolder.name+String(reMatch));

        }

    }

    return 0;

}"

    end tell

end runAdobeScript

*前回のバックスラッシュの文字化けを防止するために、ARIELフォントにしてみました。‥‥けど、やっぱり、¥になっちゃった。

*このスクリプトのコードはサンプルです。実際に使用する際は、自己責任でお願いします。

*あ‥‥そういえば、パスに日本語(2バイト文字)が混ざっている場合の処理は盛り込まれていないです。忘れてました。なので、その辺はよしなに。

 

書いたら、アプリケーション形式で保存すれば、ドラッグ&ドロップで動作する、小さなアプリケーションみたいなのが出来上がります。

 

 

 

実際に動作している映像はこちら。毎度のGIFアニメですまんす。

 

 

 

フォルダの名前で、内包するファイルの名前をリネームしている様子が、小さくて見にくいですが、なんとか伝わると思います。

 

合計500ファイル以上あるリネームを瞬時に処理します。フォルダを複数処理すると、ウィンドウの更新に時間がかかるようですが、それでも数秒の処理です。

 

 

‥‥で。

 

ここまで作ってなんですが、「この方法はないな‥‥」と思いました。

 

ESTKのエディタからであれば、実行ボタンで処理を実行できます。しかし、ESTKエディタではなく、AppleScriptからですと、なんらかの「ホストアプリケーション」〜つまり、実行する環境が必要になります。

 

なので、上述のAppleScriptのコード文では「After Effects」を呼び出していますが‥‥

 

そりゃないでしょ。

 

After Effectsの機能は全く使わず、リネームの実行環境としてのみ使うためにAfter Effectsを起動するなんて、スーパーへ買い物に行くのにヘリコプターを飛ばすようなもんです。段取りが大袈裟すぎます。

 

 

まあ、なので、結論としては、

 

ESTKのスクリプトはjsxで保存して、After EffectsやPhotoshopを起動している時に、メニューから呼び出す

 

‥‥のが、使い方の基本ですかね。やっぱり。

 

リネームのため「だけ」に、PhotoshopやAfter Effectsを起動するのは‥‥さすがに、ないわ。

 

 

 

まあでも、せっかく作ったので、なんかあった時に使ってみるかも、です。

 

どうせ、After EffectsやPhotoshopは起動しっぱなしだし。

 

 

 


プログラムとファイル名

数行のスクリプトであっても、コンピュータプログラムを実際に作って、ファイルやフォルダのリネームを処理するようになると、明確に「それ以前と以後」で当人に変化があらわれます。それは‥‥

 

人間もプログラムも、両方で扱いやすい、ファイル&フォルダの命名規則

 

‥‥を考えるようになるのです。

 

なぜかというと、人間だけでなく、プログラム的に見ても、扱いやすいファイル名のほうが、運用が円滑で効率的になるからです。

 

例えば、

 

a0001.tga

 

‥‥というファイル名があったとします。そしてこの名前の「a」を「b」へと自動処理で変えたいとします。

 

その時、どのようにプログラム上で名前を変更するでしょうか。

 

「a」は1文字だから、2文字目から最後までを抜き出して、先頭に「b」を足せば良い。つまり‥‥

 

"b"+"a0001.tga".substr(1);

 

‥‥とスクリプト文を書けば、結果は‥‥

 

「b0001.tga」

 

‥‥で、めでたし、めでたし。

 

‥‥と考えるのは、よくあることでしょう。

 

しかし、それはハッキリ申しまして「浅知恵」と言わずばなりません。

 

もし、「A上セル」の場合にファイル名が‥‥

 

a-ue0001.tga

 

‥‥だった場合は‥‥

 

"b"+"a-un0001.tga".substr(1);

 

‥‥とスクリプト文が以前と同じままだと、結果は‥‥

 

「b-un0001.tga」

 

‥‥で、全然ダメ。

 

なので、スクリプト文を部分的に書き直して、

 

"b"+"a-un0001.tga".substr(4);

 

‥‥と書けば、「a-ue0001.tga」を「b0001.tga」に変更できる。‥‥めでたし、めでたし‥‥?

 

‥‥のようになりましょう。本当に「めでたし」でしょうか?

 

問題は明らかです。ファイル名の状況によって、毎度毎度、スクリプトのコード文を修正しなければ使えないのです。

 

「大した手間じゃないし」と考える人もいましょうが、スクリプト・プログラムを導入するのは「自動処理で効率化」する目的なのですから、「毎回毎回スクリプトを手動で書き換えていたら、それはもはや自動処理と言えない」わけです。

 

「じゃあ、後ろから数えて、連番と拡張子を抜き出せば良い」と思いますよネ。つまり‥‥

 

"b"+"a-un0001.tga".slice(-8);

 

‥‥と書けば、後ろから8文字以降を抜き出せるので、"b"と合体して‥‥

 

b0001.tga

 

‥‥となり、思った通りの結果を得られる。

 

 

「ならば、ソレでいいじゃん」と思いがちです。でも、よく考えて見れば、連番は必ず4ケタと言い切れるでしょうか? セル素材の場合は2ケタだったり、コンポジット後の連番は5ケタだってあり得ます。連番のケタ数が変われば、その時点でスクリプトは台無しです。

 

"b"+"a-un_01.tga".slice(-8);

結果:「bn_01.tga」〜余計な文字が入り込み、「b_01.tga」に変換できない

 

毎回異なる状況に合わせて、「-8」を「-7」「-9」などその都度スクリプトを書き換えなければ、正常に機能しません。つまり、「何度も使いまわせる自動処理」とはなりません。

 

前から数えようが、後ろから数えようが、文字数に合わせて「1」とか「-8」などの決まった数字を使っている限り、臨機応変に対応する自動処理にはならない‥‥ということです。

 

 

‥‥と書いてて何ですが、正規表現を使えば、かなりの柔軟度でファイル名を修正できます。以下の正規表現でアニメ制作の実質的な連番関連の文字列を抜き出せるからです。

 

new RegExp("[_-]*[0-9]+¥¥.[A-Za-z0-9]{2,}");

 

*「¥」は、実際はバックスラッシュです。このブログのフォントが日本語フォントの影響だか、「¥」に見えてしまうようです。ブログのコード直書き機能でフォントを英語フォントにすれば良いのかな‥‥。

 

上記正規表現ならば、「アンダーバーかハイフンの区切り文字(=無くても可)のあとに、連番と拡張子をもつ、ファイル名」を全て共通のコード内容で処理できます。

 

"a0001.tga"を"b0001.tga"に変えたい

"b"+"a0001.tga".match(/[_-]*[0-9]+¥.[A-Za-z0-9]{2,}/);

結果:"b0001.tga"

 

 

"a-un_001.tga"を"b_001.tga"に変えたい

"b"+"a-un_001.tga".match(/[_-]*[0-9]+¥.[A-Za-z0-9]{2,}/);

結果:"b_001.tga"

 

"a-01.tga"を"b-01.tga"に変えたい

"b"+"a-01.tga".match(/[_-]*[0-9]+¥.[A-Za-z0-9]{2,}/);

結果:"b-01.tga"

 

*注)match()を実行すると、実際は「配列」が返ります。例では「暗黙の型変換」で処理していますが、After Effectsなどで実行する際はString()で囲んで明示的に型変換=キャストしないと、エラー(ゼロによる除算)で構文チェックにひっかかる可能性もあります。

 

ファイル名がセル名と連番が直結していようが、アンダーバー(アンダースコア)やハイフンで区切られていようが、何桁だろうが、全て連番と拡張子以外を名前変更します。実際は「a0001.tga」や「b」は動的に自動入力されますから(ファイルの繰り返し処理や親フォルダ名から取得するなどして)、スクリプトを変更せずに処理できます。

 

 

でも、こうした、まるで呪文のような複雑な正規表現を、アニメ制作上で頻繁に用いるフォルダ名・ファイル名に適用しなければ、名前の変更1つすら自動化できない‥‥なんて、いかにも「運用上の命名規則の甘さ」のリカバーそのものです。

 

運用開始時点で、プログラムにも人間にも判りやすい、汎用性の高い命名規則を制定すれば良いのです。

 

最初っから、先を見越して、決めておけば良いのです。その場その場で場当たり的に対応するのではなく。

 

プログラムが、現場のルーズな運用規則をリカバーするばかりに徹するのではなく、現場のファイル運用もプログラムによる運用効率化に歩み寄るべきです。自動処理、スクリプト処理云々以前に、作業者ごとでまるでバラバラな命名規則で運用し、その都度対応して無用な手間を増やし続けて、口にするのは「手間が多くて大変だ」なんて、窮状の自作自演と言われても何も言い返せません。

 

 

ファイル名・フォルダ名は「その場の雰囲気でつける」ものではありません。たとえ、簡素な命名規則でも、実は根っこに色々な工夫が張り巡らされているものです。

 

そして、その命名規則は、人間だけでなく、プログラム・スクリプトにとっても活用性の豊富な規則であれば、効率化はどんどん加速します。制作進捗状況を記録するデータベースにおいても、円滑に処理できる基盤となるでしょう。

 

例えば、アンダーバーは「カット固有や素材ファイルの要素を区切る文字」と決め、ハイフンは「注釈や派生を付記する文字」と決めれば、人間でもコンピュータでも、ファイル名から解釈は容易です。

 

anim_05_234_mo_t1.mov

意味:作品「anim」の「05」話のカット「234」の出力種別「mo」の「テイク1」のQuickTimeファイル

 

a-ue_0004.psd

意味:「a-ue」素材の「4」つめのPhotoshopファイル

 

b_01.tga

意味:「b」素材の「1」つめのTargaファイル

 

 

こうした、シンプルだけども汎用性や拡張性の高い命名規則を規定しておけば、ファイル名を変更するスクリプトを何度もそのままで流用可能になりますし、データベースなどへの情報記録もフォルダやファイル名から一貫できます。

 

ファイル名やフォルダ名は、制作運用上のメタデータと心得るべきです。

 

そのためには何よりも、ファイル命名規則を規定する人間が、人間主観だけでなく、プログラムの観点からも鑑みることが、必要になります。

 

例えば、After Effectsでテキストレイヤーのエクスプレッションなどで形成する「カットボールド」は、作品・話数・カットの各文字列がアンダーバーでセパレートできれば、何度でも無修正で使いまわせる雛形コンポが作れます。substrで「何文字目」なんて毎回変更せずとも、配列のインデックスでどんな作品でも要素を確定できます。

 

「thisComp.name.split("_")[0];」は作品キーワード(作品略号)

「thisComp.name.split("_")[1];」は話数またはシーン

「thisComp.name.split("_")[2];」はカット番号

 

同じ「数字指定」をしても、セパレートの文字=デリミタをあらかじめ規定しておき、そのデリミタで文字列を「split()」で分解して処理すれば、作品ごとに「数字指定を変更せずとも通用する」のです。

 

 

プログラムを知れば、ファイル命名規則の考え方も変わってくる。

 

ファイル命名規則の考え方が変われば、ファイルやフォルダの運用も変わってくる。

 

ファイルやフォルダの運用が規則的になれば、制作現場のデータ運用も変わってくる。

 

データ運用の無駄を抑制できれば、金銭や時間や人的資源の運用コストも変わってくる。

 

運用コストを改善できれば、現場全体を改善へと導くきっかけとなる。

 

 

‥‥と、以前、「地道に状況をレゴブロックのように積み重ねる」と書いたことは、まさにこうした流れのことを指します。秘密兵器、必勝兵器なんて存在せず、地道で先見性のある「積み重ね」だけが現場を変えていけると、少なくとも私は考えます。

 

改善の積み重ねも無しに、業界外部の門外漢の人々に、いくら「私たちアニメ業界はお金に困っています。増額してください。」なんて訴えたところで、「まず、自分らの基礎的で地道な改善をしてから、訴えてくださいよ」とひと蹴りされておしまいです。効率改善、現場の改善なんて、どんな業種だって重要なテーマです。アニメ業界だけが野放図に状況にひきづられてダダ漏れコスト消費のなすがままで良い‥‥なんて「あるわけがない」のです。

 

構造の改善は全く着手せず、現場作業者の目先の報酬を徐々にカットしていく‥‥なんて、アホ丸出しです。ゴツ石だらけ、ゴミだらけの畑に、いくらタネと水を撒いても、まともな作物なんて育ちません。上の人間も下の人間も、制作構造の「土壌」から改善することを、2020年代の目標にすべきと思います。

 

「コンピュータを毎日使っていながら、コンピュータの基本的かつ絶大な能力を使っていない」なんてことが起こらないよう、プログラムの「ちから」を制作現場にもっと導入すべき‥‥と考えます。

 

 

 

* * * * * * * * *

 

おまけ:正規表現によるフォルダ内リネーム

 

今回のブログ記事用にせっかく「アンダーバーかハイフンで区切られた(区切られていなくても良い)、1桁以上の連番と、ドットと、拡張子」にマッチする正規表現を書いたので、以前ブログで書いた「ESTKからフォルダ内をリネームするスクリプト」を改造して、汎用度を高くしました。

 

以下。

 

_1801_RENAME_RE=new RegExp("[_-]*[0-9]+¥¥.[A-Za-z0-9]{2,}");
alert(main());

 

function main(){
    var theFolder=Folder.selectDialog("フォルダを選択してください");
    if(!theFolder){return "処理をキャンセルしました";}
    var theFiles=theFolder.getFiles();
    if(theFiles.length<1){return "フォルダが空です";}
    for(var i=0;i<theFiles.length;i++){
        var reMatch=theFiles[i].name.match(_1801_RENAME_RE);
        if(reMatch&&theFiles[i].name.match(/^[A-Za-z0-9]/)){
            theFiles[i].rename(theFolder.name+String(reMatch));
        }
    }
    return "処理が正常に終了しました";
}

 

これなら、「a-01.tga」でも、「a_1.tif」でも「a_001.tiff」でも、「a0001.psd」でも、全て「ファイル名に含まれる素材名部分を親フォルダ名に書き換えてリネーム」できます。

 

ドットで始まる「隠しファイル」がわんさか存在するOSXでの運用を鑑みて、「match(/^[A-Za-z0-9]/)」の条件は念のため入れときました。先頭に記号を付与して、事前に処理対象から意図的に外すことも可能です。

 

まあ、まだ穴はある(エラーで止まる条件は残っています。例えば、「a0001.tga」と「b0001.tga」が同じフォルダに入っていた場合、2つとも「親フォルダ名0001.tga」にしようとして失敗する‥‥など)のですが、かなり現場の作業傾向を吸収できそうです。

 

私は正規表現の使い手ではなく、必要に応じてチョコっと‥‥という程度なので、「.」を「¥.」にするまでは良かったものの、「""」で囲む時に「¥自身のエスケープ」を忘れて、期待通りの動作をせずにウンウン唸ってました。「""」で囲む=クォーティングする場合は、「¥¥.」ですよネ。正規表現に限らす、「" "」や「' '」のクォーティングの際にエスケープ文字自身をエスケープするのは、基本ですよね。

 

そんな程度の私でも、日々の制作作業において、自作スクリプトは大活躍です。‥‥というか、自作のスクリプトやヘルパーソフトウェア、エクスプレッションがなければ、「できることもできなくなる」のが未来の制作技術だと痛感しています。数百にも及ぶキーフレームを意のままに操作するなんて、手作業でいちいち操作してたら無理ですもんネ。

 

 


最初は覚えることいっぱい

前回、スクリプトの補足を書いてて、どんどん解説文が長くなるのを我ながら見て、たった3行のスクリプトでも覚えることはそれなりに多いな‥‥と実感しました。あれだけ長く書いた割に、「var」とか、「 = 」とか、for構文の「{ }」とか、行末の「 ; 」は、しれっと何も説明しないまま、スルーしてますもんネ。

 

例えば、「var theNumber = 120;」の「var」はvariable〜バリアブル〜「変数」の英単語の頭文字3文字です。しかし、変数といっても、ローカル変数=その場だけで有効な変数もあれば、グローバル変数=After Effectsが起動している間はずっと有効な変数もあります。また、値やオブジェクトを格納するのは、変数だけでなく、定数でも可能です。

 

varは、ローカル変数を明示的に生成する時に用います。そうしないと、特にAfter Effectsの場合、昼は作業、夜はレンダリング‥‥みたいにAfter Effectsが起動しっぱなしだと、偶然、複数のスクリプトで同じ変数名を使っていた際に、思わぬ事故が起こることがあります。空だと思っていた変数が、実は全く違うスクリプトで数日前に代入済みで、思わぬ動作をする‥‥みたいに。

 

ですから、After Effectsのスクリプトの場合、

 

theNumber = 120;

 

のような、グローバル変数の代入ではなく、

 

var theNumber = 120;

 

‥‥のようなローカル変数の代入が基本になります。After Effectsのグローバル変数は、After Effectsが起動している限り、たとえスクリプトが終了しても生き続けるので、注意が必要です。

 

「え? エクスプレッションだとvarなんて使わなくてもトラブルはないけどな」

 

‥‥というのは、その通りで、After Effectsのスクリプトとエクスプレッションは、グローバル変数の「スコープ=範囲」が違うのです。

 

エクスプレッションは逆に、変数の寿命‥‥というか有効範囲が「そのフレームだけ」と極端に狭いです。毎フレームごとにエクスプレッションが実行されますが、そのたびに、グローバルもローカルも関係なく変数の寿命は終了して空になります。

 

なので、エクスプレッションは、

 

theNumber = 120;

 

‥‥でも、全く問題がないのです。

 

反面、エクスプレッションは、タイムライン〜フレームにまたがって変数を持てません。ですので、裏技‥‥というか、他の方法でフレーム共通のグローバル変数もしくは定数を実現することになります。マーカーを作ってコメントを書いて参照して定数にしたり(直にエクスプレッション文をイジらなくても定数の変更が可能)、他のテキストレイヤーのテキスト内容の読み取り(連動して動作させるには、結構、構造は複雑になります〜正確には変数ではなくクリップボードのような扱い)など、「本来、変数・定数の格納場所として想定されていないものを無理矢理活用」すれば実現できます。

 

 

‥‥とまあ、変数1つで、ちょっと説明しただけで、これくらいは必要になります。

 

最初は覚えることが多くて、それが初心段階の大きな障壁ともなります。

 

なので、いっきに覚えて習得するのは、そもそも無理だと諦めて、今、自分の差し迫って必要な雑事を軽減するためのスクリプトを書くことだけに集中して覚えて、それを繰り返していけば、いつのまにか、「あれ? 自分、プログラムのことをある程度、理解できてるよな?」と我ながら驚くわけです。

 

日本語だってそうだったはず。

 

喋りたいこと、伝えたいことのために、まず言葉を覚えていったのを思い出せばよいです。

 

‥‥で、プログラム、スクリプトは、20〜30代の若い頃に覚えちゃうのが、絶対にお得です。だって、何よりもまず、覚えるための脳の柔軟性がありますもんネ。アラウンド40以降になると、何度も反復しないと覚えられないことが、アラウント30だとあっさり覚えられたりします。ん〜、自身で実感。

 

普通に現代社会の状況の成り行きを考えて、10〜20〜30代の人間は、コンピュータを絡めて仕事をする未来がほぼ確定しています。よほど特殊な職種でない限り、コンピュータと無縁ではいられないはずです。だったら、プログラムの基礎を覚えておいて、損はないはず。

 

代々の農業を兼業するのだって、田畠の管理にコンピュータを使っても良いわけですから、デジタルデータで運用する映像産業に従事するのなら、なおさら‥‥ですよネ。

 

 

 


スクリプト補足

前回に載せた「フォルダ名で、中身のファイルをリネームする」スクリプトですが、あまりにもちゃちゃっとブログ文中で済ませたので、補足しておきます。

 

ちなみに、私はアニメーター100%だった頃、コンピュータやプログラムとは真逆の人間だと周囲から思われていましたし、自分もプログラムなんてとてもじゃないが‥‥と思っていました。学校でコンピュータやプログラムを教わったことなど皆無でしたし。

 

でも、今はこんな‥‥です。

 

なので、本人の気概次第でプログラムの基礎くらいは習得できると思っています。プログラム言語よりも遥かに難解な日本語を話せるのですから。

 

で、前回のスクリプトは‥‥

 

var theFolder=Folder.selectDialog("フォルダを選択してください");
var theFiles=theFolder.getFiles("*_?????.tga");
for(var i=0;i<theFiles.length;i++){theFiles[i].rename(theFolder.name+"_"+theFiles[i].name.split("_")[theFiles[i].name.split("_").length-1]);}

 

‥‥の3行スクリプトでした。

*ブログの表示幅の都合で、自動回り込み改行で4行に見えることもありますが、実際は3行となります。

 

これに注釈を添えると‥‥

 

var theFolder=Folder.selectDialog("フォルダを選択してください");

 

ユーザにフォルダを選択してもらうよう催促するのが、Folder.selectDialog() です。選択したフォルダを記憶しておくために、変数「theFolder」に代入します。変数の名前はtheFolderでなくても構いませんが、予約された単語とは被らないようにします。「folderA」「target_folder」などでも大丈夫です。

 


var theFiles=theFolder.getFiles("*_?????.tga");

 

getFiles() は、文字通り、フォルダの中にあるファイルをゲットする命令です。ただし、ファイル全てをゲットしてしまうと、不要なファイルまでゲットしてしまう可能性も否めません。そこで、「連番ファイルだけを選び出す」ために、条件をつけます。

 

今回の連番のファイル名は、「親フォルダの名前」+「アンダーバー」と「数字5桁」と「ドット」と「TGA拡張子」で構成されていますので、その連番ファイルだけを選び出してゲットします。「選び出し」を可能にするのが、getFiles() の括弧内の指定です。

 

「*」は0文字以上の何らかの文字列、「?」は何らかの1文字を表し、その他はそのままの文字として認識されて、選び出しの条件となります。ゆえに、「*_?????.tga」で「a_00001.tga」などの連番ファイルだけを抜き出すことが可能になります。同じ条件で「zz_xx_cdefg.tga」も条件に引っかかることになりますが、映像制作の素材フォルダにはそのようなファイル名が含まれていないことがわかっていますので、甘い条件でも十分機能します。

 

もし「b_0001.tif」や「b_0122.tif」などの4桁&TIFF連番を抜き出したい場合は「*_????.tif」にすれば良いです。

 

もっと条件をユルくして、「*.???」なら、「b_0001.tif」も「a-ue_001.tga」「anim_06_231_t2_0048.dpx」でも、全部対象になります。「ドット&3文字拡張子」なら何でも抜き出せる‥‥というわけです。中身に何が入っているか、自分自身で確信があるのならそのくらいユルくてもOKですが、スクリプトプログラムを作った本人以外の他人が使う時は注意が必要です。

 


for(var i=0;i<theFiles.length;i++){theFiles[i].rename(theFolder.name+"_"+theFiles[i].name.split("_")[theFiles[i].name.split("_").length-1]);}

*ブログの表示幅の都合で、自動回り込み改行で2行に見えることもありますが、実際は1行となります。

 

for」は繰り返しを処理する構文で、プログラム初心者の「最初のハードル」と言えるかも知れません。「for(var i=0;i<theFiles.length;i++)」は、「まず最初に変数iにゼロを代入し、変数iが変数theFilesの中身の総数より小さい間は処理を繰り返し、繰り返しごとの最後に変数iに1をプラスしてループ先頭に戻る」という意味になります。

 

一方、変数theFilesの中身は、先ほどの「getFiles("*_?????.tga")」によって、TGAの連番ファイルが格納されているので、この、for構文で順番に中身を取り出して処理していきます。theFilesの中にあるTGA連番ファイルは、一番目のファイルを「theFiles[0]」、七番目のファイルは「theFiles[6]」というように「ゼロスタートに読み替えて」指定して取り出すことができます。

 

for構文において、変数iは、繰り返しごとに、0, 1, 2, 3, 4, ....と増えていくので、変数iを呼び出し番号として活用し、theFiles[i]のように用いれば、theFIlesの中身を順番に取り出すことが可能になります。

 

次に、theFiles[i]で呼び出した個々のTGA連番ファイルを、いよいよ、「rename()」命令でファイル名変更しますが、そのためには、新しいファイル名を、親フォルダの名前と古いファイル名を部分的に合体させて生成する必要があります。

 

変更後の名前は、「親フォルダの名前」+「アンダーバー」+「元の連番と拡張子」にしたいので‥‥

 

親フォルダの名前を得る>>theFolder.name

 

‥‥は簡単だとしても、面倒なのは‥‥

 

元のファイル名から連番と拡張子だけを得る>>「a_00001.tga」の「00001.tga」の部分

 

‥‥です。でも、映像制作では連番の前を「アンダーバーなどで区切る」のが一般的なので、それを有効活用します。つまり、ファイル名をアンダーバーで区切れば、「a」と「00001.tga」を分割=split() できます。

 

例えば、"a_b_c_d" という文字列があった場合、"a_b_c_d".split("_") を実行すると、["a","b","c","d"] という配列に分解されます。この配列から3番目の要素である "c" を抜き出したい場合、ゼロから数え直して、[2]という呼び出しかたで抜き出します。つまり、"a_b_c_d".split("_")[2] で "c" を抜き出せます。

 

なので、"a_00001.tga" から "00001.tga" を抜き出すには‥‥

 

"a_00001.tga".split("_")[1]

 

‥‥でうまくいきます。

 

しかし、今回は「a_00001.tga」でしたが、もし「a_un_00001.tga」みたいなファイル名だったら‥‥

 

"a_un_00001.tga".split("_")[1]

 

‥‥では「un」が抜き出されてしまいます。前方から数えて抜き出すのではなく、最後の要素を抜き出せば、必ず、「00001.tga」が抜き出せます。しかし、残念ながら、最後から数えるのに [-1] のような指定方法は通用しません。

 

なので、split() で分割した要素数を数えて、1スタートではなく0スタートで指定するために要素数からマイナス1すれば、最後の要素を指定して抜き出すことが可能になります。要素数を数えるには「length」を使います。

 

"a_00001.tga".split("_")["a_00001.tga".split("_").length-1]

 

"a_un_00001.tga".split("_")["a_un_00001.tga".split("_").length-1]

 

この方法なら、ちょっと文が長くて解りにくくなりますが、どの場合も確実に「00001.tga」を取り出すことができます。スクリプト文中ですと‥‥

 

theFiles[i].name.split("_")[theFiles[i].name.split("_").length-1]

 

‥‥という具合になります。

 

連番と拡張子が取り出せたのなら、あとは親フォルダの名前と合体させるだけです。アンダーバーも忘れずに。

 

theFolder.name+"_"+theFiles[i].name.split("_")[theFiles[i].name.split("_").length-1]

 

新しい名前を生成できたら、rename()」命令で実際にファイル名を変更して終了です。

*ブログの表示幅の都合で、自動回り込み改行で2行に見えることもありますが、実際は1行となります。

 

theFiles[i].rename(theFolder.name+"_"+theFiles[i].name.split("_")[theFiles[i].name.split("_").length-1])

 

 

実際に処理をすると、72ファイル程度のリネームなど「瞬殺」で処理終了です。

 

 

↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓

 

 

 

 

たった3行のプログラムですが、最後の行に「for」「rename()」「split()」などが一気に盛り込まれたので、ちょっと難しいようにも思えますが、落ち着いて、行頭から1つ1つ意味を理解して進めば、混乱することはありません。

 

リネームはこのやり方の他にもたくさん方法があります。今回は「親フォルダ名と元の連番&拡張子を合体させる」方針なので、上記の方法になりましたが、文字列の「置換」を使ったり、ソートしてリナンバーする方法も考えられます。

 

何か1つの段取りを守らないとリネームができないわけではなく、工夫次第で色々なアプローチを考えて実践できるのが、プログラムの「実は楽しいところ」です。

 

でも‥‥

 

工夫次第で色々なアプローチを考えて実践

 

‥‥って、絵を描いたり、話を考えたり、視覚効果を追加したり、音楽を作ったりすることと、何だか似てます。料理や運転、経営・経済などにも共通すること‥‥かも知れませんネ。

 

 

 

 

*追記:

今回の3行のサンプルスクリプトは、できるだけ簡潔にするために、例えば「ユーザがキャンセルボタンをクリックした」「フォルダの中身が空だった」などの「false」「null」「[ ]」「undfined」に対応する処理が全く書かれていません。その点に関しては、ESTKの元になっているJava Scriptの指南Webなどで独学してください。

 

例えば‥‥

 

main();

 

function main(){

var theFolder=Folder.selectDialog("フォルダを選択してください");

if (!theFolder) {return false;}

}

 

‥‥などのようにキャンセルに対する処理を施します。

 

 

 


最初の言語

AppleScriptは冗長です。自然言語処理をするために、ファイルのパスなどはいちいち面倒です。私は最初AppleScriptばかりやっていたので、他の言語を覚え始めた時にはじめてAppleScriptの「浮いてる感じ」が意識できました。

 

file "06_201_bg-ok.psd" of folder "BG" of folder "201" of folder "06" of folder "ABC" of disk "MacHD"

 

いかにも冗長ですが、それこそAppleScriptの真骨頂で、プログラム文が英文として理解できるように設計されていたのです。

 

賛否や好みは別として、それはそれで有意義な言語だったと思います。少なくとも私は、その言語スタイルゆえに「最初のハードル」を独習のみで超えられた実感があります。

 

AppleScriptで全てをこなすことはできませんし、AppleScriptの限界を感じて他の言語を追加で習得したわけですが、最初の言語としてはAppleScriptの開発者の方々には感謝しています。

 

 

ちなみに、AppleScriptでも、もっとチャチャッとファイルパスは記述できて、

 

file "MacHD:ABC:06:201:BG:06_201_bg-ok.psd"

 

‥‥と書くこともできますし、

 

POSIX file "/ABC/06/201/BG/06_201_bg-ok.psd"

 

‥‥とUNIXスタイルで書くこともできます。そんな便利な方法、超初心者の頃はまるで知らなくて、延々と「ファイルオブ、フォルダーオブ、フォルダーオブ、フォルダーオブ...」と書いてましたけどネ。

 

プログラム言語は、習得する過程、上達して色々な方法を体得する過程で、コンピュータやネットワークやオペレーションシステムの理解も深くなり、コンピュータがどんどん「自分の中に吸収されて」いきます。

 

例えば、macOSのシステムのパスだと、外付けハードディスクの中にあるファイルは、「HHD1」という名前のハードディスクの場合、

 

file "HDD1:footages:001.mov"

 

‥‥とAppleScriptで書きますが、同じくAppleScriptでUNIXのパスを扱う場合は、

 

POSIX file "/Volumes/HDD1/footages/001.mov"

 

‥‥という文字列になります。

 

単にファイルを読み書きするスクリプトを作りたいだけなのに、色々とナゾ仕様な「Volumes」が現れて、初心者を混乱させますが、実はその混乱は福音でもあります。当人はウンウン悩んで苦しみますが、混乱を収拾する過程で知識ががっぽがっぽ蓄積していくのです。

 

ボリュームズ? マウントポイント? ルート? ルートディスクとルートユーザーとか色々とあるの? ターミナル? コマンドライン? 不可視項目?

 

プログラムの門を開いて中に進む‥‥ということは、鉄の門の向こうにあってブラックボックスだったコンピュータの中にズンズン入っていく‥‥ということです。プログラムを覚えると、「コンピュータの知識特典」が山のようにオマケでくっついてきます。(‥‥オマケという言い方は、少々乱暴ですけどネ)

 

ある程度の規模のプログラムを作るようになると、否が応でも、コンピュータの知識は高まっていきます。

 

自動車の単なる乗客でしかなかった人間が、たとえ50ccでも自動車モドキを自分で製作すれば、エンジンの中身も開けるし溶接もするしで、格段に基本メカニズムや製作技術に詳しくなっていくのと同じです。つまり、「メカ音痴」ではなくなるわけです。

 

同様に、コンピュータ音痴ではなくなります。

 

そればかりか、色々とコンピュータ言語を扱うようになると、使っている言語の弱点がわかって、自分で言語を強化することすら可能になってきます。

 

例えばAppleScriptは文字列処理が「異様に弱い」のですが、他の言語にあるような「split()」や「slice()」などのサブルーチンを作ってモジュール化し、色々なスクリプト作成時にロードすれば、毎回いちいち「AppleScript's text item delimiters」なんて冗長なコードを書かずに済みます。ちなみに、splitは文字列を分割、sliceは文字列を切り抜く処理です。AppleScriptはソート関連も弱いので、ソート関数も強化して使っていました。

 

 

 

人間は、生まれてすぐに「コンストラクタがどうだ、インヘリタンスがどうだ」なんて語り始めるわけもなく、最初は誰もが「ど素人」だったわけです。

 

私なんか、(何度も書くけど)最初のころ、MacOSだったせいもありますけど、拡張子の「.psd」を堂々と全角で力強く「.PSD」とかファイル名に使ってましたし、「PICTファイル」なのに「.psd」という拡張子をつけたり、メチャクチャでしたが、‥‥‥そんなのさ、初心者は超能力者じゃないんだから、「初心者に向かって最初から解ってろという方がオカしい」んですよ。

*注記)Macの場合、MacOS9までは「拡張子」における動作の仕組みはありませんでした。クリエータタイプ&ファイルタイプという独自のリソースで動作していたので、ぶっちゃけ、どんな名前でも構わなかったのです。

 

ただ、いつまでも初心者ぶって、覚える努力もしなければ、人に聞くことだけで乗り切ろうとするのも、ダメダメです。

 

コンピュータで映像制作をおこなうということは、ほぼ一日中、コンピュータをイジりまくっているのです。だったら、プログラム言語の1つ2つ習得しておいて損はないでしょう。

 

使えないより、使えた方が良いのは、明白‥‥ですよネ。

 

 

 

 


言語のおぼえかた

プログラム言語に関して、他人にアレコレ事細かく指南できるほどのスキルは持ち合わせていませんが、経験からくる「覚え方のコツ」は自分なりに理解しています。

 

単語は辞書を引けば良いので、何よりも構造を覚えてしまうのが、プログラム言語習得のコツです。

 

例えば、各国の言語でも、

 

私の名前は江面です。

My name is Ezura. 〜マイ ネーム イズ エズラ

Mein Name ist Ezura. 〜マイン ナーメ イスト エズラ

Меня зовут Эзура. 〜ミニャ ザブゥ エズラ(カタカナにするのが難しい‥‥)

 

‥‥と、構造はかなり似ています。特に上の例文においては英語とドイツ語はクリソツ。

 

これと同じように、プログラム言語も基本的な構造は同じです。以下の内容は「このCompの名前は『ezura Comp』である」という文です。「比較演算子」を用いた文で、「である=true:真」「ではない=false:偽」=真偽値が返ります。

 

thisComp.name == "ezura Comp";

name of thisComp = "ezura Comp"

name of thisComp is "ezura Comp"

name of thisComp is equal to "ezura Comp"

 

辺境のAppleScriptの例が多いですが、AppleScriptは言い回しが多様なので、例として挙げてみました。AppleScriptは「=」だの「is」だの「is equal to」だの色々と言い回しがあってにぎやかです。一方、「==」を使うプログラム言語は多く、ほとんどの言語で通用します。

 

私は一番最初にAppleScriptから入り、やがてシェルスクリプトやPerl、REALbasic、PHP、JavaScriptなどを色々な言語を必要に応じて順次覚えていきました。現在は目下、SwiftやPythonですが、参考書に目を通していても、円滑に理解が進みます。

 

思うに、私がAppleScriptからプログラム言語の習得を始めたのは、「ラッキー」だったと思います。なぜかというと、AppleScriptは今までの経緯で「すったもんだ」があって、「なりゆきとして」言語のスタイルを覚え直すハメになったからです。

 

いわば、「不幸中の幸い」です。

 

私がAppleScriptを最初に覚えたのは、ズバリ、日本語で記述できるのがAppleScriptのウリだったからです。1997年の事です。

 

例えば、

 

アプリケーション “Finder”について

起動項目のフォルダ “テスト”のファイル “picture.psd”の 名前を “picture1.psd”にする

以上

 

‥‥といった具合(記憶で書いてるので半角スペースの入れ方とかは曖昧です)で、日本語でスクリプトの自動処理が書けたのです。これは日本人かつ今までアニメーター100%だった私にとって、とても馴染みやすい仕様でした。

 

しかし、運命とは、残酷なもの。

 

突然のApple(開発元ですネ)による「日本語環境打ち切り」。

 

AppleScriptは英文のみとなり、私にとっては目の前が真っ暗になる仕様変更でした。「アップルのばかーーーーー!」と叫びたい心境‥‥いや、実際に叫んだかもネ。

 

でも、随分と使えるようになった言語を諦めるのもシャクだったので、英文で仕方なく覚え直し始めました。

 

英文で書くと、こんな感じ。

 

tell application "Finder"

    set name of file "picture.psd" of folder "テスト" of startup disk to "picture1.psd"

end tell

 

まあ、見事、無残に、ファイルパスの表記スタイルが真逆です。細かい点ではありますが、 ” が " になったりと、ほとんどゼロから覚え直しでした。

 

しかし、日本語が英語に変わっただけで、構造は同一なことに気づきました。日本語表現形式か、英語表現形式の違いだけで、中身は一緒です。

 

かえって、漫然と日本語スクリプトを覚えていた時よりも、構造に目を向けるようになって、ある程度英語表現に慣れたあとは、今まで以上に理解が進むようになりました。

 

わからない単語は辞書を引けばすみますが、構造がわからなければ、全体のプログラム動作が掴めません。構造を把握することで、プログラム言語で何を覚えれば良いかが開眼できたのです。

 

同じ例文をAdobeのESTKで書いてみると、

 

File("/テスト/picture.psd").rename("picture1.psd");

 

‥‥となります。

 

名前変更の際に、ファイルオブジェクトのファンクション「File(ほにゃらら).rename(新しい名前);」を用いるところとか、macOSゆえのUNIXパスだったりと、色々と違う点もありますが、「対象の項目を指定して処理を加える」という基本動作には何ら変わりはありません。

 

つまり、何らかのプログラム言語を覚えて、その後にもう1つ別のスタイルの言語も覚えれば、言語の差から色々な「覚えるべき有益な要素」があぶりだされて、自然と習得できるようになります。

 

よく聞かれる質問で、「どんな言語を覚えれば有利か」とかがありますが、ぶっちゃけ、自分の今すぐ役に立つ言語を覚えとけば良いです。何が基準かも釈然としない有利不利や損得基準で言語を選ぶよりも、今すぐに活用できる言語を選べば良いです。

 

例えば、アニメ制作でコンポジット周りなら、迷わずAdobeのESTK、すなわちJavaScriptベースの拡張言語でしょう。すぐに役立つもんネ。

 

After EffectsやPhotoshopなどの主要アプリケーションを自動で操作して、様々な業務に活用できます。例えば私は、After Effectsのレンダリングエンジンが「レンダリングするだけしか使えない」のがイヤで、自家製のレンダリングエンジンを作っていました。独自の「キューファイル」(SJISやUTF-8などのテキストファイル)を規定してサーバにキューファイルを書き出し、他のマシンで待ち受け中のAfter Effectsがそのキューファイルを読み取り、キューの記述を読み取って動的に内容変更してレンダリングする仕組みでした。常駐型のスクリプトもESTKでは書けるんですヨ。

 

ESTKでは他の言語同様にテキストファイルの読み書きが可能なので、キューファイルのような「伝票」を作ることなど朝飯前。初心者の腕試しにちょうど良いです。AEPのプロジェクトを次々と開いて、After Effectsの最終コンポジションの内容をリスト化(テーブル化)したHTMLだって、繰り返し文を用いて何のヒネリもなく作れます。

 

長いコード文を掲載するのもブログでは大変なので、お約束のアレをHTMLで書き出してみます。

 

var myFile = new File("/テスト/hello.html");//macOSの場合のパスです
myFile.encoding="UTF-8";
myFile.open("w");
myFile.write('<!doctype html>¥n');
myFile.write('<html>¥n');
myFile.write('<head><meta charset="UTF-8">¥n<title>Hello, World.</title>¥n</head>¥n');
myFile.write('<body>¥n');
myFile.write('<h1>こんにちは、みなさん。</h1>¥n');
myFile.write('<p>いざ、プログラムの世界へ。</p>¥n');
myFile.write('</body>¥n');
myFile.write('</html>');
myFile.close();

 

バックスラッシュが「¥」になっちゃってますが、読み替えてください。¥nはコードの改行です。Pタグの行をどんどん増やせば、どんどん書きたい文章を増やせますネ。

 

このスクリプト文をESTKで実行すると、以下のようなHTMLファイルが自動生成されます。

 

ハロー、ワールド。

 

 

とりあえず、今、使えそうな言語を習得しておけば、その言語が有利か不利かなんて「後でどうにでもなり」ます。

 

一番不利なのは、グズグズ何しようかとぶーたれている間に、全く言語を習得できないこと‥‥ですネ。

 

 

 

*久々にひっぱりだしてきたロシア語(キリル文字)のキーボード。ローマ字入力の英語キーボードではロシア語は無理なので‥‥。

*ちなみに、私は最初、筆記体から入っちゃったので、活字(ずいぶん外見が違うんですよ)が読めないアホな状態で苦しんでおりました。キリル文字のキーボードは手に入らなかったので、ロジテックのK120キリル文字ステッカーを貼っています。

*今日届いたスイフトのリファレンス。新刊です。

*写真の机が随分とカスれてエグれているのは、絵を描く際に袖のボタンが当たってエグれたのです。私は左利きなので、机の左部分だけ木材が露出しています。よほど硬いボタンだったんでしょうかねえ‥‥。記憶にないです。‥‥‥‥でもまあ、ほんとに、駆け出しの頃(18〜22歳)は、死に物狂いで必死だったんでしょうねえ‥‥我ながら。

 



calendar

S M T W T F S
     12
3456789
10111213141516
17181920212223
2425262728  
<< February 2019 >>

selected entries

categories

archives

profile

search this site.

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM