見出し画像

【"Cheat"GPT】知識ゼログラミング:8会話でstable diffusion の無限増殖modを作る

私はちょっと絵のAIの方は大して興味なく、まあ本気で興味があるなら手で描いた方が楽しそうではある。

だが、とりあえずゼログラミングは楽しい。

ということでstable diffusionをはじめ画像生成はガチャ要素が多く、ダラダラと生成しているので、なにもしていない時も自動でどんどん生成できるようになるmodがあれば便利そうなので作ったのである。

世の中に「stable diffusion」記事、画像生成記事は多いが、”ゼログラマー”アプローチとして一石を投じているものはどうやらなさそうなので、以前私が紹介したキラーテク「検証画像貼り付け」のキラーさ加減を伝える意味でもその応用編として作ってみた、その流れなのである。


■1会話目「tampermonkeyって便利だなあ」

👆なにはともあれこれから

まずはなにはともあれ最初の一言目は「~って便利だなあ」だ。

ブラウザ操作はtamperがゼログラミングにとって最強。これは便利。ほのかにとっつきにくく、ほのかに難しいのがAI時代になんとも絶妙である。

stableはブラウザツールであることが幸いして、これもtamperで操作できる。

■2会話目「ページ上の画像を自動で保存する操作はできますか?」

👆生成画像をダウンロードできなければしょうがない

まずは目的のツールが作れそうかどうかを確認してみる。

modのイメージとしては

生成終了

保存

生成開始

の繰り返しができればよいわけであるが、そもそも画像保存の操作は自動化できるのかわからなかったのである。

簡単そうだけれども、こういう操作はブラウザで禁止されているケースも多い。たとえば以前音楽ファイルを自動で再生するといったことをしようと思ったのだが、簡単そうであるものの、サーバーがどうとか、結構複雑らしく3日かかってもできず撤退したトラウマがある。

画像保存はどうやらできるらしいということはわかった。

■3会話目「このinterruptのdisplay状態をローカルストレージに保存するようにしてくれ。」

👆前に紹介した「検証スクショ貼り付け」技

「このinterruptのdisplay状態をローカルストレージに保存するようにしてくれ。キーはGeneratingStateにしますか。blockだったら、running。none だったら、ready」

具体的にコードに入っていく。まずは今が「生成中」なのか「準備中」なのかという情報を記述するスクリプトを作る。

生成している間はstableのボタン表示が「generate」から「interrupt」に変わるので、変わっている間が「生成中」である。

蕎麦屋の看板のようなものを作り、別のスクリプトがその看板を見て「ああ、今営業中ですか」とか「まだやってないのか」と確認させてタイミングよく動かせるようにするわけである。

👆ボタンの構造をチェック

構造がわかれば、以前紹介したキラーテク「検証のスクショ貼り付け」と「ローカルストレージ」でできる。

しかしChatGPTが書いたコードを早速tamperにコピペして実行してみると、まったくうまくいかなかったのである。ローカルストレージを見てみるとrunningとreadyがころころ入れ替わるはずが、runningになっただけで、そのまま微動だにしない。

ChatGPTが書いたプログラムが少ない感じがあったのでよく見てみると、1回しかチェックを行っていないようである。常にチェックして、看板をどんどん裏返してもらいたい。

確かに上記指示では、私の中で「常に監視するもの」と思い込んでその点について触れるのを忘れていた。ChatGPT側のミスというわけでもないのである。こういうことはよくある。

■4会話目「常に監視です。ポーリング1秒おきでいきましょう。」

👆常に監視だと言ったのである。ChatGPTは「先言えよ」とも言わず、要件を理解して淡々とコードを書きなおした


■5会話目「ありがとうございます。では次なるスクリプトに参りましょう。」

👆本体のコードを書き始める

「ありがとうございます。では次なるスクリプトに参りましょう。

上記のローカルストレージを1秒おきにチェックして、runningからreadyに変わったら、その時だけ"E:\新しいフォルダー" ここに、上記でimg要素の画像を保存するスクリプトを書いてください。」

まず次からスクリプトを変えろと宣言。1個のスクリプトで書くと複雑化してくるので、こういった分離・統合操作がAIで重要である。

まずは生成した画像の保存操作だけを指示。「生成が終わったタイミングでページ上の画像をダウンロードするスクリプトを書け。」

やはりセキュリティ的に指定フォルダに保存するのは無理とのことだ。でもクロームのダウンロード機能を動かす保存はできるらしい。しょうがないのでクロームの設定で、ダウンロードを「保存先を指定する」から「自動」にして対処し、コードを動かしてみると確かに画像がダウンロード先に保存された。

ここは詰めれば保存先を指定するということもなんとなくできそうな気もしないでもない。まあ私はそんなに画像生成が重要じゃないので撤退した。こうとあまり決めつけず、個人個人のライフスタイルに合わせて進退を見極めるのが真のゼログラマーだ。

■6会話目「ダウンロード先はクロームで設定したやつってわけですな。」

「ああ、だからダウンロード先はクロームで設定したやつってわけですな。」

いちおう私の理解で正しいのか、画像をダウンロードする方法の再度確認したのである。

■7会話目「ダウンロード処理をしたあとに、このGenerateボタンを押すようにしてください。」

あとは保存したあとに再び生成をはじめさせれば完成と。例によって、ボタンの要素、構造を調べてスクショして貼り付ければオッケ。

早速動かしてみると、どんどんフォルダに画像が湧き出してきた。ゼログラマー至福の時である。プロンプトを少し変えたくなっても、生成中にやればオッケーで、使い勝手もそんなに悪くない。

👆car, rally
👆2、3分でフォルダがラリーカーだらけだ!

ただ今度は動き出すと、まったく止められないことに気づいたのである。停止ボタンが必要なのを忘れていたのである。

■8会話目「スタートは俺がGenerateボタン押せばいいけど、停められなくなるね。停止ボタンいりますね。」

👆停め方を考えるのを忘れていたのである

「ChatGPT様のご献身、ならびに素晴らしいご回答に、こころより御礼を申しあげつかまつります。スタートは俺がGenerateボタン押せばいいけど、停められなくなるね。停止ボタンいりますね。ついでにループ回数をカウントして表示するようにしてください。」

使ってみるとうっかり見落としがあるので修正。永遠に生成を続けて停められなくなるのは少し不便である。ついでに何回生成したかを表示するカウンターのようなものも作った。

■完成と。

以上の8会話でできたのである。以下がその2つのスクリプトだ。

質問、批判は俺ではなく、ChatGPTにぶつけてくれ。

◇看板スクリプト

// ==UserScript==
// @name         Continuously Save Display State to Local Storage
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Continuously monitor and save the display state of the interrupt button to local storage
// @author       You
// @match        http://127.localURL
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // displayの状態をローカルストレージに保存する関数
    function saveDisplayState() {
        // 'interrupt'ボタンの要素を取得
        var interruptBtn = document.getElementById('txt2img_interrupt');

        if (interruptBtn) {
            // display状態を取得し、ローカルストレージに保存
            var displayState = window.getComputedStyle(interruptBtn).display;
            var state = displayState === 'none' ? 'ready' : 'running';
            localStorage.setItem('GeneratingState', state);
        }
    }

    // 1秒おきに状態をチェックする
    setInterval(saveDisplayState, 1000);
})();

◇生成、ダウンロード処理

// ==UserScript==
// @name         Download Image, Click, Count and Reset
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Manage image download and clicking with count and reset functionality
// @author       You
// @match        http://127.localURL
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let previousState = localStorage.getItem('GeneratingState') || 'ready';
    let loopCounter = 0;
    let isRunning = true; // 制御フラグ

    // ループ回数を表示する要素を作成
    const counterDisplay = document.createElement('div');
    counterDisplay.style.position = 'fixed';
    counterDisplay.style.bottom = '10px';
    counterDisplay.style.right = '10px';
    counterDisplay.style.backgroundColor = 'white';
    counterDisplay.style.padding = '5px';
    counterDisplay.textContent = `Loop Count: ${loopCounter}`;
    document.body.appendChild(counterDisplay);

    // ループ停止・再開ボタンを作成
    const toggleButton = document.createElement('button');
    toggleButton.textContent = 'Stop Generating';
    toggleButton.style.position = 'fixed';
    toggleButton.style.bottom = '10px';
    toggleButton.style.right = '130px';
    document.body.appendChild(toggleButton);
    
    toggleButton.addEventListener('click', function() {
        isRunning = !isRunning;
        toggleButton.textContent = isRunning ? 'Stop Generating' : 'Start Generating';
        if (!isRunning) {
            // 停止時にカウンタをリセット
            loopCounter = 0;
            counterDisplay.textContent = `Loop Count: ${loopCounter}`;
        }
    });

    function checkStateAndDownloadImage() {
        let currentState = localStorage.getItem('GeneratingState');

        // 停止後、状態がreadyになったら再開
        if (previousState !== 'running' && currentState === 'ready' && !isRunning) {
            isRunning = true;
            toggleButton.textContent = 'Stop Generating';
        }

        if (!isRunning) return; // 停止フラグが立っていたら処理を終了

        if (previousState === 'running' && currentState === 'ready') {
            let image = document.querySelector('img[data-testid="detailed-image"]');
            if (image && image.src) {
                downloadImage(image.src, () => {
                    let generateButton = document.getElementById('txt2img_generate');
                    if (generateButton) {
                        generateButton.click();
                        loopCounter++;
                        counterDisplay.textContent = `Loop Count: ${loopCounter}`;
                    }
                });
            }
        }
        previousState = currentState;
    }

    function downloadImage(url, callback) {
        const a = document.createElement('a');
        a.href = url;
        a.download = '';
        document.body.appendChild(a);
        a.addEventListener('click', () => {
            setTimeout(callback, 1000);
        }, {once: true});
        a.click();
        document.body.removeChild(a);
    }

    setInterval(checkStateAndDownloadImage, 1000);
})();

さらにゼログラマーが本気を出せば「100回の出力するたびに、プロンプト内容を順次変える」といったスクリプトなども可能であろう。プロンプトエリアに文字を入力することも自動化できるので、それを順次差し替えるプロンプトセットをテキストファイルなどに書いておき、それを参照してプロンプトを順次差し替えさせるようにすればいい。

そしてとりあえずいくつかのプロンプト・セットを作ってから寝て、起きたら1000枚くらいできているだろうから、結果を軽くチェックしてプロンプトの方針を考えるみたいな作業も可能である。

さらに本気でやるなら、手描きの方が楽しいことは間違いないだろう。

■まとめ

慣れればこれで大体20分くらい。以前3日かけて泣きながら「GPT4タイマー」を作った記事を読んでいないとむずかしく見えるかもしれないが、1個なにかしら作っていると、その技術が応用できて知識ゼロでもよっぽどstable diffusionの導入よりも簡単にできる。特に最近はアップデートなどで一枚の生成時間が長くなっているらしいので、このようなmodを作ることも考えてみるといいだろう。

ただまあさらに本気でやるなら、手描きの方が楽しいことは間違いないだろう。

この記事が気に入ったらサポートをしてみませんか?