After Effectsのスクリプトの可能性を広げる。 その3

github.com

いくつかの機能追加とバグフィクスを行った。マウス周りのフックは他のフックに比べ応用範囲も狭いだろうから(可能性があるのはダブルミドルクリックくらい?)熟成させていたのだが、花雪さんからバグ報告をいただき、その修正にともない熟成も止めることにした。

[bug]Add to Adobe Media Encoder Queue... · Issue #1 · atarabi/at_script · GitHub

バグの内容は、!@script_initializer.jsxを導入した状態でAdd to Adobe Media Encoder Queue...をクリックするとエラーが発生します。ということで、最初は信じられなかった。というのも、!@script_initializer.jsxはバイナリ形式にしてあるものの中身はいたって単純なのでそもそもバグを出す余地があるのかというのと、AfterFX.exeでエラーが出ているのだが、何で今更エラーが出るのかという話でもある(!@script_initializer.jsx自体Startup時に実行されるスクリプトであるし)。

詳しく調べてみると、AMEが立ち上がる際にdynamiclinkmanager経由でAfterFXを新たに起動しており、その条件下においてStartupのスクリプト内でapp.executeCommandなど(他にもあったがあまり調べられてない)のメソッドを実行するとクラッシュするらしかった。dynamiclinkmanagerには思い当たる節があって、ある時からWarp Stabilizerエフェクトがきちんと動かなくなり、それもdynamiclinkmanagerが原因の一端であることまでは分かっていたが、あまり使用することもなかったので放置していた。案の定これも同じ原因で落ちてるらしかった。理由が分かれば対処も早い。

Startup系のスクリプトには早期リターンしてもらう必要があるので、!@script_initializer.jsx内でdynamiclinkmanagerから実行されてるかの判別を行うAtarabi.isDynamicLink()を定義し、それを呼び出してもらう形にした。

(() => {

    if (Atarabi.isDynamicLink()) {
        return;
    }

    // mainの処理
})();

マウス周りのフックは上でも書いたが全然使い道が思いつかない。ただ、作っておけば賢明な人がなにか使い道を思いついてくれるだろう。

// ダブルミドルクリックをするとコンポをつくる。
const uuid = Atarabi.mouse.hook({ button: 'Middle', count: 2 }, ctx => {
    app.project.items.addComp('Comp', 1000, 1000, 1, 100, 30);
    return true; // trueを返した場合、デフォルトの処理が呼ばれなくなる。
});

同梱物

新たに追加したもの。

@effects_in_use

プロジェクト内で使われているエフェクトをスキャンするスクリプト。同種のものは既にあると思うが、自分が使いやすいと思うものを作った。

@swatch

カラースウォッチの試作。スクリプトのパネル上でミドルクリックをすると、色パラメータを選択もしくは選択したエフェクト内に初期値から変更済みの色パラメータがあればその色を、それ以外の場合かつ平面レイヤーを選択していればその平面の色を取得する。削除したい場合は、当該色の上でダブルクリックをする。

After Effectsのスクリプトの可能性を広げる。 その2

github.com

公開したことでやる気もすこし出てきたので、いくつか更新した。

自分の思想として"ソフトがこちらに合わせろ"というのがあり、既存コマンドに対するフックを容易にするというのもそれに向けての一つの手であったが、更に押し進めてキー入力をフック出来るようにした。

キー入力のフックを上手く活用した一番有名な例はFX Consoleであろうが、こういったランチャーの嚆矢はQuick Menuだと思われる。

AEではScriptsフォルダにあるスクリプトに対してショートカットを割り当てることが可能なので、それを活用している。この方法は言われてみればという感じで、当時甚く感心した。

最大20個までショートカットを設定出来るようなので、やろうと思えば色々出来る。

難点は、管理が面倒だというのと、スクリプトがその都度実行されるため、特に重い処理が含まれる場合に、毎回我慢するか、計算結果をグローバル変数などに逃がしてキャッシュしておく必要がある。Quicke Menuの場合、appオブジェクトに何か生やしているようであった。

キー入力のフックは、このような本質的でない部分による煩わしさから逃れられることが利点であると言える。ただどういった状況下でキー入力がなされたかという情報がないため、例えばレイヤー名変更のために文字入力しているという状況下でも容赦なくイベントは発火する。したがって修飾キーの指定は必須だと思われる。

使い方は至って単純。

(() => {
    Atarabi.keyboard.hook({ code: ']', altKey: true }, ctx => {
        const comp = Atarabi.comp.getMostRecentlyUsedComp();
        if (!comp) {
            return false; // falseを返した場合、(存在すれば)AEのデフォルトの操作が実行される。
        }
        const layers = comp.selectedLayers;
        if (!layers.length) {
            return false;
        }
        try {
            app.beginUndoGroup(']');
            for (const layer of layers) {
                layer.outPoint = comp.time;
            }
        } catch (e) {
            // pass
        } finally {
            app.endUndoGroup();
        }
        return true; // trueを返した場合、AEのデフォルトの操作は実行されない。
    });
})();

Alt+] コマンドは、アウトポイントが現在フレームではなくその次のフレームに設定される。それが煩わしいという人も時たま見るので、上のスクリプトでは現在フレームをそのままアウトポイントに設定するようにしている。

AEのコマンドには、コマンドIDというものが設定されていてそれ経由で実行されるコマンドと、そうでないコマンドとがあり、]Alt+] は後者に属する。なので、コマンドのフックで何とかなる前者だけでなく、キー入力のフックもあれば後者も網羅出来て完璧、とはならず、先程書いた通り、修飾キーのない ] をフックするのは難がある。残念。


同梱物

新たに追加したもの。

Startup

!@script_UI

以前は、スタートアップ時にUI周りのコードを使用しないだろうと思っていたが、割りと使いうると分かったので、リネームして頭に ! をつけることで読み込み順を早めている。

@effect_launcer

www.youtube.com 簡易的なエフェクトランチャー。Ctrl+SpsaceでエフェクトをFuzzy Search出来る。 !@script_UIを用いているので、@script_UIのままの人は頭に!をつけてください。

@hook_]

上では、Alt+]の例を紹介しているが、使用例として一応]も用意してある。

@toggle_mfr

(() => {
    const SECTION = 'Concurrent Frame Rendering';
    const KEY = 'Enable Concurrent Frame Renders';
    Atarabi.keyboard.hook({ code: 'M', altKey: true }, ctx => {
        const now = app.preferences.getPrefAsBool(SECTION, KEY);
        app.preferences.savePrefAsBool(SECTION, KEY, !now);
        writeLn(`MFR: ${!now}`);
        return true;
    });
})();

短いので全部掲載するが、Alt+MでMulti-Frame Renderingのオンオフを切り替える。

@save_to_desktop

(() => {
    Atarabi.keyboard.hook({ code: 'S', altKey: true }, ctx => {
        const comp = Atarabi.comp.getMostRecentlyUsedComp();
        if (comp) {
            const file = new File(`${Folder.desktop.absoluteURI}/${comp.name.replace(/\//g, '-')}_${Date.now()}.png`);
            Atarabi.comp.saveFrameToPng(comp, file);
        }
        return true;
    });
})();

Alt+Sで現在表示されているコンポの画をデスクトップにpngで保存する。


余談。

エフェクトのFuzzy Search周りでは、EditTextのonChangingのコールバック関数にdebounce処理をほどこしている。すべてのイベント発火に対して処理を行うとかなりもたつくからである。

debounce処理の実装にはsetTimeoutとclearTimeoutが使われるが、AEのスクリプトにはないので、app.scheduleTask()、app.cancelTask()を代用することになるが、残念ながら実行したい処理を文字列で指定する必要があるので、グローバルに関数を晒す必要があり非常に使いにくい。

幸いにもCC2015から、app.setTimeout()、app.cancelTimeout()が隠れ機能として追加され(saveFrameToPngと同じ立ち位置?)、これは普通に関数を指定できる。ただ自分が調べた印象だと、app.cancelTimeout()は機能してないように思われる。

After Effectsのスクリプトの可能性を広げる。

AEで処理を自動化する際によく用いられるのはスクリプトであるが、プラグインにもエフェクト以外にAEGPというスクリプト的なことも出来るタイプのものがある。大概のことはスクリプトでも出来るので、あまり出る幕がないのだが、AEGPのAPIを用いないと出来ないこともままあって、そういう場合は渋々C++を書かざるを得ない。しかし、特に単機能のものを書くためだけに、わざわざC++を持ち出すのも面倒であるし、何とかしたい。そこで、スクリプト側からAEGPのAPIを呼び出せるようなラッパー的なライブラリを作ることを考える。

つまり、斜線部を考える。こういうのをあらかじめ作ってさえおけば、あとはC++に立ち入ることなしに、スクリプトだけで完結するので、非常に便利である。ということで作った。


github.com

初代はどういうことが出来るのか色々実験した結果、迷走し複雑化したので、それを糧に二代目をあらためて作り直したものの、今度は別の構想がなかなか煮詰まらず、遅れに遅れた。

at_script/script/types/@script.d.ts at main · atarabi/at_script · GitHub

主なAPIはここを参照すればよいが、 document は まだ整備されてない (@script — documentation 最低限の体裁は整えた。(2023/9/6))。

Releases · atarabi/at_script · GitHub

必要なものは上から @script.zip をダウンロードすれば手に入る*1


最低限必要なものは、 プラグイン*2と Startup 内の !@script_initializer.jsx。後者は必要不可欠というわけではないが、Startup時にAPIを使用したい場合に、初期化処理のために用いる。というのも、AEのスクリプト機能自体がプラグインで実装されているため、こちらのプラグインがそれより先に読み込まれた場合に、スクリプト周りの初期化処理が出来ない。したがって、初期化処理を遅延させているのだが、そのタイミングがStartupのスクリプトの実行タイミングより遅れるため、こちらから促す必要がある。頭に ! をつけているのは、Startupスクリプトの中でも最初に実行してほしいため*3

いにしえより伝わる global にオブジェクトをひとつ作り*4、その下に根を生やしていく方式で構築している。使い方としては、

(() => {
    const comp = app.project.activeItem;
    if (!(comp instanceof CompItem)) {
        return;
    }
    const layers = comp.selectedLayers;
    const isAVLayer = (layer: Layer): layer is AVLayer => {
        return layer instanceof TextLayer || layer instanceof ShapeLayer || layer instanceof AVLayer;
    };
    for (const layer of layers) {
        if (!isAVLayer(layer)) {
            continue;
        }
        Atarabi.layer.setNull(layer, true); // ココ!
    }
})();

といった感じで利用する。上のは選択レイヤーにヌル属性を設定する例である。スクリプトではヌル属性が read-only であるが、AEGPのAPIだと書き込みもできる。


同梱物

APIを使用したスクリプト(使用していないものもある)を同梱してある。

Startup

!@script_initializer

上記通り、StartupにおいてAPIを利用したい場合は、これをStartupのフォルダに入れる必要がある。

@command_set_null

選択レイヤーにヌル属性を追加するSet NullコマンドをLayerメニューに追加する。

(() => {
    Atarabi.register.insertCommand('Layer', 'AtTop', 'Set Null', () => {
        // 内容は上の例とほぼ同じ
    }, 'LayerSelected');
})();

このように簡単にコマンドを差し込むことができる。'LayerSelected'は、レイヤーを選択しているときだけコマンドを有効化させることを意味する。当初はここも関数で指定できるようにしてあったが、Mac環境だとエラーが出るため、ご破産となった。

@fold_layers

一時期話題になっていたGM_FoldLayersのクローン。LayerメニューにAdd Separatorコマンドが追加され、実行すると、▾ Separatorという名前のシェイプレイヤーが出来る。これが区切り線がわりとなっており、ダブルクリックをすると、シャイレイヤーの機能によって開閉がなされる。が区切り線用のレイヤーか否かのマーカーになっているので、Separator部分の名前は変更可能。

Atarabi.register.hookCommand(3974/* レイヤーをダブルクリックする際に発行されるコマンド */, ctx => {
    const markedLayer = getMarkedLayer();
    if (!markedLayer) {
        ctx.fallback = true;
        return;
    }
    // メインの処理
});

既存のコマンドを上記のように簡単にhook出来るようにしてある。マーカー以外のレイヤーではデフォルトの挙動をして欲しい。そこで、渡されたctxfallbacktrueにすることで、デフォルトのコマンドの挙動にフォールバックするようにしてある。ここらへんはTJ氏がWQというプラグインを作る際にも、色々試行錯誤されていた。

@hook_adjustment, @hook_null, @hook_solid

調整レイヤーにしろヌルレイヤーにしろ平面レイヤーにしろ、既存の平面レイヤーを出来るだけ使いまわして欲しい。そこで、そこらへんのコマンドをフックすることで使い回すようにしてある。平面レイヤーは白色固定なので、カラフルにしたい場合は各自、改造してみるのもいいかもしれない。

昔、ここらへんの平面レイヤーを用いるコマンドをシェイプレイヤーに置き換えるAEGPプラグインを作ったことがあったが、このときは基本の処理はスクリプトで書き、プラグイン内でそれを実行し、ヌル属性を付加することろだけは、別途C++で書いていた。今は全てスクリプトで書けるので、いい時代になったものである。

@hook_fit_to_comp

Fit to Compコマンドは、縦横比を無視してコンポサイズに拡大縮小する。個人的に、縦横比を維持して欲しいなということが多いので、フックして縦横比を維持するようにしている。現状、background-size: contain;的な処理をしているが、background-size: cover;的な処理も欲しいなと思うことが時たまあるので、いずれ選択式にしたい。

@script_UI

私がScriptUIのGUIを楽して作る用のライブラリ。下記のScriptUI系のスクリプトは全てこれを用いて作っている。

ScriptUI

上記通り、@script_UIが必須。

選択しているエフェクトのドロップダウンリスト系のパラメータのテキストを羅列する。ダブルクリックすることで値を変更でき、さらにReal Timeをチェックすれば、クリックするだけで値が変わる。

@paramdefs_viewer

選択しているエフェクトのパラメータの詳細を得る。APIを通じて、Custom Propertyの値もBase64エンコードされた形で取得できる。これを用いて、Custom Propertyの書き込みも出来るが、内部的にポインタを用いてるものに対しては、無理に書き込もうとすると落ちる*5

エフェクトをFuzzy Search出来る。

@effect_preset

気軽にエフェクトのプリセットを作れる。ffxよりもうちょっと気軽にプリセット的なのを扱いたい用。上で書いたように、CurvesのようなCustom Propertyの値も扱えるので割りと使える。

@still_maker

コンポないし選択レイヤーの現在フレームを静止画として保存できる。

@movie_maker

コンポないし選択レイヤーをGIF, APNGとして保存出来る。最適化されているわけではないので、適当に扱ってもよいとき用。

@rpp_loader

REAPERのプロジェクトをAEに読み込む - Atarabi-Tumblr

これの移植、修正版。


余談。

{
    "compilerOptions": {
        "jsx": "preserve",
        "rootDir": "./src",
        "outDir": "./dst"
    },
    "include": [
        "./types/aftereffects.d.ts/ae.d.ts",
        "./src/**/*.tsx"
    ]
}

TypeScriptでスクリプトを書く際は、設定を上記のようにするのが楽。味噌は、スクリプトのファイルの拡張子をtsxにすることと、"jsx": "preserve"にすること。こうすることで、トランスパイルした際に自動的に拡張子がjsxになるので、リネームせずに済む。

*1:スクリプト関連は自分でトランスパイルしてもよい。

*2:Windowsの場合は@script.aex、Macの場合は@script.plugin。

*3:スクリプトはおそらくASCII順で実行される。そうでなかった場合は、ただのおまじないと化す。

*4:このライブラリに関してはAtarabi

*5:Mesh Warp、 Reshapeなど。

vending,machine,Candela,

興福寺の夜を待つために日の沈むのを眺めていたら、南円堂の前の自販機が目に入った。前々からこれを上手く撮りたいと思っていて、南円堂も枠内におさめ、幾枚か撮影するも自販機の灯りがか弱い。
南円堂は東面し、自販機は一つを除き北面しており、直交している。いい具合に顔をこちらに向けてもらうには、斜めから撮る他ない。自販機の機嫌をうかがいすぎると南円堂の正面性が損なわれるし、加減が難しい。


いい塩梅に松も立っている。松の全体を入れ込むと自販機とお堂の印象が弱くなるか、否、松が左に視線が行くのを遮るのでかえって強くなるのでは、などと構図を模索している内に自販機の輝きは増し、ついには空に伍するまでになった。


普段、マジックアワーのような時間帯を決めて出掛けることもないので、これがあのブルーアワーかと得心した。




東大寺大仏殿と猫段上の売店横の自販機も、ちょうど同じ位置関係にあるな、と思い出したので、後日、天気の良い日に向かった。二月堂で日が沈むのを見届け、一目散に向かう。


南円堂に比べ、大仏殿から自販機の距離があるので、角度に余裕がなく、木々も遠くを隠し、カメラ位置の制約は大きい。また、南円堂の経験から、石畳も視線をお堂へ誘う線として用いることができる、と想定していたが、自販機や外灯からやや離れており、暗く厳しい。
制約が大きいということは、探るべき domain も絞られるので、いい面でもあるのだが、得られる極大が高々ということもあり得る。地形の現状変更は倫理的に行えないので、どうしようもないことではあるが。


また、二台の自販機を結ぶ線の先に大仏殿が大まかにあってほしいし、大仏の瓦は暗く見づらいので、最低限、鴟尾と屋根の反りのシルエットははっきりと見えて欲しい。
こんな感じでどんどん条件を課していくと、あまりよろしくない局所解に嵌まる危険性もあり、帰って写真を見返した際に後悔することも多いのだが、時間も限られるし仕方がない。




そういえば、自販機と雪の素晴らしい写真があったな、と調べたら、大橋英児氏の作品であった。

eijiohashi.com

Roadside Lights I

太陽なきみそらのもとでは自販機がお山の大将であり、ダミーサンプルが煌々としている。したがって、街の景も取り込もうとすると、自販機が明るすぎる。一方、カラー写真であるし、ダミーサンプルのカラフルさも見せたい。となると、露出を変えて幾枚か撮影してHDR合成を、ということになる。しかし、ハイライトを抑えてラベルをよく見せようとすると、今度は神々しさが薄れる。眩しきもののディティールとグローの両立はなかなかに難しい。

Being There

モノクロのシリーズとなると、色彩は影を潜めるので、輝きが重視され、自由気ままに白飛びさせてある。カラーと白黒とで何を重視するのか、というのがはっきりと表れている。




興福寺五重塔のライトアップがしばらくお休みに入るので、その最終日に投光器を撮りに行った*1


帰りしな、南円堂と自販機をHDR合成用に撮影した。現像は、ハイライトを白飛びさせつつも、ダミーサンプルの範囲だけは銘柄が微かにわかる程度に明るさを抑えた。眩しきもののディティールとグローの両立は難しい。

*1:照明のデータ・シート (No.208) に投光器設置時当初の仕様が載っているが、それ以降どう更新されていったのかは追えなかった。

#sailormoonredraw gan

www.youtube.com

発端

スクレイピング

twitterAPI代わりにtogetterを代用するのはよくやっていて、togetterでとりあえず一日単位で#sailormoonredrawの画像付きツイートをまとめて、それをスクレイピングして画像を収集。

前々処理

今回の場合、オリジナルの画像(複数種類あった)も比較対象として同時に上げているツイートが多かったのでそれを取り除く必要があった。画像の比較にはPerceptual Hashを使用。性質上オリジナルと似た画像が多いため、閾値を少し上げただけで結構な数の画像を弾いてしまった。そこで、ハッシュ値が完全に一致した場合のみ弾くことにした。

前処理

学習用のデータとするために、顔の部分を中心として512x512の矩形に切り抜く必要がある。顔検出にはlbpcascade_animeface.xmlを用いた。
ultraist.hatenablog.com

どう切り抜くかであるが、リファレンスとしてオリジナルの画像を512x512に切り抜いたものを顔検出。
f:id:kareobana:20200613225715p:plain
画像の外枠の正方形と顔検出された矩形との対応関係が求まるので、後はそれを元に個々の収集した画像を顔検出して切り抜き。誤検知、関係ない画像を目視で取り除いて、計9578枚。

学習

StyleGANを使用。Google Colaboratoryで学習させるために、下記のを少し改変したものを用いた。
github.com
Google Colaboratoryはセッション切れ、タイムアウトがあるので、それを考慮してモデルの途中保存の頻度を大きくした。

プレ映像

GANの潜在変数をぐるぐるさせる系の映像作品は、実際の展示でも既に3回ぐらいは見ていて食傷気味である。画像を大量に学習させて少しずつ潜在変数を移動させれば、絵となる映像が容易に出力出来るので、流行るのもむべなるかな、といった感じではあるが、見せ方が中央に一つドンと見せるかグリッド状に見せるかのどちらかが多かったので、そこに多少のアレンジを加えた。
f:id:kareobana:20200613232115p:plain
つまり、複数解像度の正方形を組み合わせて、タイル状に並べるようにした(StyleGANが低解像度から徐々に高解像度へと学習をすすめる仕組みなのでそれを利用したというのもある)。

final int width= 60;
final int height = 34;
final int scaling = 16;

int grid[][];
ArrayList<Cell> cells;

class Cell {
    int x, y, size;
    color col;

    Cell(int x, int y, int size) {
        this.x = x;
        this.y = y;
        this.size = size;
        this.col = color(random(255), random(255), random(255));
    }

    void draw() {
        fill(this.col);
        rect(this.x * scaling, this.y * scaling, this.size * scaling, this.size * scaling);
    }

    void write(PrintWriter writer) {
        writer.println(this.x + "\t" + this.y + "\t" + this.size);
    }
}

void generateCells() {
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            grid[y][x] = 0;
        }
    }

    cells.clear();

    for (int size : new int[]{8, 4, 2}) {
        for (int retry = 0; retry< 10000; ++retry) {
            int x = (int)random(0, width - size + 1);
            int y = (int)random(0, height - size + 1);
            boolean ok = true;
            for (int yy = y; yy < y + size; ++yy) {
                for (int xx = x; xx < x + size; ++xx) {
                    if (grid[yy][xx] == 1) {
                        ok = false;
                        break;
                    }
                }
                if (!ok) {
                    break;
                }
            }
            if (ok) {
                for (int yy = y; yy < y + size; ++yy) {
                    for (int xx = x; xx < x + size; ++xx) {
                        grid[yy][xx] = 1;
                    }
                }
                cells.add(new Cell(x, y, size));
            }
        }
    }

    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            if (grid[y][x] == 0) {
                grid[y][x] = 1;
                cells.add(new Cell(x, y, 1));
            }
        }
    }
}

void settings() {
    size(width * scaling, height * scaling);
}

void setup() {
    stroke(0);
    grid = new int[height][width];
    cells = new ArrayList<Cell>();
    generateCells();
}

void draw() {
    noLoop();
    background(255);
    for (Cell cell : cells) {
        cell.draw();
    }
}

void mousePressed() {
    generateCells();
    loop();
}

String zfill(int number) {
    return String.format("%02d", number);
}

void keyPressed() {
    if ((key == 'S') || (key == 's')) {
        String name = "cells_" + zfill(month()) + zfill(day()) + zfill(hour()) + zfill(minute()) + zfill(second());
        PrintWriter writer = createWriter(name +  ".txt"); 
        for (Cell cell : cells) {
            cell.write(writer);
        }
        writer.flush();
        writer.close();
        save(name + ".png");
    }
}

具合の良いタイルにしたいので、簡単にprocessingで生成出来るようにして、tsvとして個々のタイルの位置とサイズを出力するようにした。

映像

あとは、潜在変数が動き回る映像をランダムシードを変えて複数解像度で大量に生成した後、タイル状に並べるだけである。当初は、AEに映像を読み込んでスクリプトでtsvの指示通りに配置した後、少し加工して出力するつもりであったが、レンダリングの想定時間が一日半を示したため断念した。AEだと大量のファイルを読み込んで1フレーム出力また大量のファイルを読み込んで1フレーム出力といった処理となるため、I/O部分がネックになるのである。そこで、メモリ上に画像バッファを大量に確保しておいて、そこに映像を一つずつ読み込んで書き込んでいき、最後に、一気に画像バッファを出力するというPythonスクリプトを書いた。これだと一時間足らずで処理が終わった。
f:id:kareobana:20200613234654p:plain
AEでノイズを抑えるためにCC Wide Timeを掛けたり、色調を整えたりして完成。

おわり

ここらへんの技術はアイディア次第で上手くオリジナルの映像作品へと昇華出来そうではあるが自分の中にまだ確固たるものがない。

驟雨

今週のお題はてなブログ フォトコンテスト 2017夏」


f:id:kareobana:20170709154834j:plain雨は突然に、貴賤、人種関係なく等しく降り注ぐ。


f:id:kareobana:20170709155753j:plain雨具を忘れた彼、彼女らも、僕と同様、どこか屋根のある場所を探しているみたいだ。


f:id:kareobana:20170709155800j:plainやっとの思いで到着した軒下は、既に先客でいっぱいだ。


f:id:kareobana:20170709161116j:plain遠慮して隅の方で休憩。早く雨やまないかな。でも、しばらくこのままでもいいかも。

AfterEffectsで機械学習 その4

下記の線画着色と、スタイル変換で遊んだ。両者ともにCPUで処理したのだけども、線画の方は1分くらい処理に時間が掛かったので、早送りしてある。今のところAEをただのGUIとしてしか扱っていないのでもうちょっと何とかしたい。


qiita.com


qiita.com