AIイラストを雑にアニメーションしつつ動画にするツールを試作してみた
先日、「AIイラストのスカートをめくりたい。…めくりたくない?」を投稿した際、コメント欄でアニメーションGIFの話が出て、なるほどと思いました。
HTMLなら自分のWebサイトやブログ上で、エロ漫画風のAIイラストやモーションコミック風のアニメーションを自由自在に掲載できるので便利だし、画像で欲しいならスクショ、動画で欲しいならOBS Studioで、と考えていましたが、手軽さという観点を考えていませんでした。
AIイラストだって鼻くそほじりながらボタン1つで絵が出てくるのがウケているのでしょうしね。ボタンひとつで動画作ってほしいよね。きっと。
というわけで
AIイラストを雑にアニメーションさせつつ録画するツールを作ってみた(試作版)
- 画像をクリックするとお好きな自分の画像へ入れ替え可能です。
- 完全にブラウザ内だけで動作しているため、選択した画像や録画した動画がどこかへ送信されるようなことはありません。
- 何等かの理由(バグや、録画開始後30秒経過する等)でアニメーションが止まった場合は、「UPDOWN Animation」をクリックすると再開されるはずです。ほんとはこのラジオボタンをアニメーションの種類分用意するつもりが、ひとつで力尽きました。
- 動画形式はH.264/webm固定です。技術的にVP8やVP9へ変更することは可能だけれど、webm固定はどうしようもないっぽい。
実はSNSやってないので、H.264/webmでTwitter等にアップロードできるかは知りません(本末転倒)
技術解説
「AIイラストの衣装を脱がしたい。…脱がしたくない?」に引き続き、JavaScriptによるcanvas操作で実現しています。
スタイルシートのお手軽アニメーションとは異なり、自分でアニメーション部分のアルゴリズムも書かないといけないので面倒です。あまりに面倒なので上下のアニメーションしか作っていません。雑に要所の部分だけ抜き出して軽く解説します。
document.getElementById('mode_updown').onclick = function () {
if (flg_animation) return;
let direction = 1;
flg_animation = true;
(function render() {
ctx.clearRect(Math.floor(canvas.height * param_x / 100), Math.round(canvas.height * (param_y + y) / 100), canvas.width, canvas.height);
y += (param_speed * direction);
ctx.drawImage(img, Math.floor(canvas.height * param_x / 100), Math.round(canvas.height * (param_y + y) / 100));
if (y >= param_move || y <= (param_move*-1)) {
direction *= -1; //上限・下限のいずれかに到達したら方向を逆転する
}
if (flg_animation) {
requestAnimationFrame(render);
}
})();
};
mode_updownというのがラジオボタンの名称で、これがクリックされることでアニメーションを開始。
param_x / param_yは画面上で「POSITION (X)」「POSITION (Y)」と書かれている部分で、画像を配置する座標。
どんなサイズの画像が指定されるかわからないし、AIイラストで出力した絵を動かしたいと思った場合は、たぶん普通は上の例のように尻とか拡大してアップにし、絵の端っこが見切れない程度の範囲で上下、左右に動かして遊ぶと思うので、「SCALE」…拡大倍率、「POSITION (X)」…X座標、「POSITION (Y)」…Y座標の指定は必須と考えました。
特に基準となるX/Y座標は重要で、その位置からどのくらい動かしたか、を計算するために、
ctx.drawImage(img, Math.floor(canvas.height * param_x / 100), Math.round(canvas.height * (param_y + y) / 100));
といった少々長たらしい指定をしています。
ちなみにこのX/Y座標の単位はピクセルではなく、パーセンテージ。-100%~+100%の範囲で指定されることが前提となっています。ここでいう100%というのは画像のサイズのこと。
つまり、1024x1024pxの画像が指定されたら、X座標の下限~上限は-1024px~+1024pxということ。1920pxなら… ってそこまで書かなくてもさすがにわかるよね。
ということで、やっていることは単純なのだけれど、レスポンシブ対応やいくつの画像サイズが指定されても大丈夫なように、と考えるとあちこちで割合計算をしなきゃいけないのがぼくの残念な頭ではけっこうややこしいです。
それから、param_moveというのは移動量。これも割合指定なのだけれど、+1%~+100%の範囲としています。1024pxだとしたら10px~1024pxの範囲で移動するということ。
アニメーション開始時はdirection(移動方向)がプラスなので、0,1,2,3....と移動量の上限に達するまで加算されるのだけれど、上限を超えたら、directionをマイナス値にして、座標をマイナスしていきます。これで上と下を行ったりきたりするアルゴリズムになるわけだ。単純でしょ。
それから、requestAnimationFrameについてはcanvasプログラミングをしたことのない初心者にとっては聞きなれない関数かも知れないですが、要はsetTimeoutに60/1000秒を指定したようなもの。
……………らしい。ぼくも今回はじめて使いました。今まではsetTimeout使って、30秒に1フレームずつ描画するくらいで十分だったから。ただ、なんかcanvasのドキュメント読んでたらrequestAnimationFrameのほうがより高性能だとかなんとか。例えば144Hzのモニターだと秒間144回呼べる…のかなぁ? うち75HzのPCモニターしかないから60と75で切り替えても違いわかんなかったわ。
リフレッシュレートの高いグラボとPCモニター使ってる人でパンツの動きおかしかったら教えろください。たぶん60Hzに比べて倍ちょっと早い程度だと思うけども。
録画について
ここは実はすごく簡単。
canvas要素自体がストリームを持っているので、MediaRecorderクラスに渡してあげるだけ。
let stream = canvas.captureStream();
let recorder = new MediaRecorder(stream, {mimeType:'video/webm;codecs=h264'});
このMediaRecorderクラスって元々はたぶんWebカメラとかを録画するために使われるクラスなんでしょうね。それが入力装置としてカメラではなくcanvasも指定できるって感じ。
コーデックの指定はvideo/webm;codec=h264の部分だけれど、ブラウザによっても対応コーデック違うかも。Chromeだとh264、vp8、vp9あたりが選べました。
あとはrecorder.start();で録画開始して、recorder.stop();で録画停止するだけ。
動画ファイルとしてダウンロードするためには、BlobデータをURLへ変換してあげないといけないので、こんな感じの処理をします。
recorder.ondataavailable = function(e) {
let videoBlob = new Blob([e.data], { type: e.data.type });
let blobUrl = window.URL.createObjectURL(videoBlob);
let anchor = document.getElementById('downloadlink');
anchor.download = 'rec.webm';
anchor.href = blobUrl;
anchor.style.display = 'block';
}
canvasに描画した画像をファイルへ変換するときと同じですね。
ベース絵
なかなか正面(?)向いた良いケツが出てきてくれなくて、プログラム書いてる時間よりケツ探してる時間のほうが長かったかも知れない…。
Prompt
anime,1girl,loli,kawaii,small breasts,school uniform,pleated skirt,white panties
,bend over
,from behind center
,(simple white background)
,<lora:jyojipan_ver2_NP:1:OUTALL>
Negative prompt: (worst quality, low quality, monochrome:1.2), bad anatomy, lowres, watermark, signature, text, username, bad hands, extra hands, extra arms, bad fingers, missing fingers, extra fingers, multipul angle, split view, 3d, photorealistic,camel toe
Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 461761172, Size: 1024x1024, Model hash: be03be0110, Model: hassakuHentaiModel_hassakuv1m, Denoising strength: 0.7, Clip skip: 2
普通の立ち姿の尻をアップで上下ふりふりしても楽しくなかったので、bend over(前かがみ?)と入れてみました。効果があったのかどうかはよくわからない。
<lora:jyojipan_ver2_NP:1:OUTALL>はロリコンご用達の以下略。
あ、そうそう、OUTALLはLoRA Block Weightという拡張機能の指定です。
LoRAって追加学習の仕組みなわけだけど、例えばこの女児パンツLoRAでも多少は顔を含んだ絵を学習しているだろうし、どうしたってパンツ以外の絵柄に影響を与えちゃうんですね。ひどいときには顔面が貞子より怖い状態になったりします。
LoRAで女児パン出した後、顔だけ後で修正、という二度手間をしていましたが、このLoRA Block WeightでLoRAの影響範囲をある程度制御できるようになり、OUTALLの場合、おおむね、衣装への影響だけで済む感じ。完全ではないけれども。あるとないとでは大違い。
…まぁ、今回のケツ絵には関係ないんですけどね。いつもPromptコピペしてるので、今回も気が付いたら入れてたってだけです。
まとめ
朝に作り始めたときはアレコレ構想を練っていたのに、ケツの生成に思ったより時間食って上下アニメーションの一種類しか作れなかったのは残念無念。
気が向いたら機能UPしますが、1日経つとすぐ別のことに興味を持つ性分なのでたぶん明日は別のことに夢中になってる。














