Stable DiffusionによるAI画像生成BOTを作った話(その2)

概要

  • 先日、TypeScript + Discord.js + Stable Diffusion で表題のモノをつくりました…が。
    • ソシャゲのキャラに似せた画像を生成するまでは良かったのですが、局部が露出していなくても、肌色面積が多いと、R-18指定されたり削除されたりするそうです。
    • 代替手段はDropboxでした。アーティストがセンシティブな絵を書く場合に使用されることもあるそうで、imgurよりかなり規制が緩い(らしい)。でも児ポはダメだぞ!

リポジトリ

環境変数

  • 予めDiscord Developer Portalで以下を取得して.envにコピーしておきます。
    • DISCORD_BOT_TOKEN
    • DISCORD_APP_ID
  • Discordサーバーから以下を取得して.envにコピーしておきます。
    • DISCORD_GUILD_ID
  • Dropbox DeveloperのApp Consoleから以下を取得して.envにコピーしておきます。
    • DROPBOX_APP_KEY
    • DROPBOX_APP_SECRET

image01

  • 下記トークンについては後述します。
    • DROPBOX_REFRESH_TOKEN
    • DROPBOX_ACCESS_TOKEN

アクセストークンとリフレッシュトークンの取得・利用

  • アクセストークンの有効期間は非常に短いらしく、定期的に取得し直す必要があります。
    • これを対策するために必要なものが、リフレッシュトークンと呼ばれるトークンです。
    • リフレッシュトークンを利用することで、アクセストークンを自動で更新してくれます。
  1. 認証コードを取得します。下記URLの YOUR_CLIENT_IDDROPBOX_APP_KEY と同じ文字列に置換してアクセスします。

    https://www.dropbox.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&token_access_type=offline
  2. リフレッシュトークンを取得するために curl コマンドを実行します。下記の通り置換し、コンソールで実行します。

    • <AUTHORIZATION_CODE> : 1. で取得した認証コード
    • <APP_KEY> : DROPBOX_APP_KEY と同じ文字列
    • <APP_SECRET> : DROPBOX_APP_SECRET と同じ文字列
    curl https://api.dropbox.com/oauth2/token -d code=<AUTHORIZATION_CODE> -d grant_type=authorization_code -u <APP_KEY>:<APP_SECRET>
  3. アクセストークンを取得するために、再び curl コマンドを実行します。下記の通り置換し、コンソールで実行します。

    • <REFRESH_TOKEN> : 2. で取得したリフレッシュトークン
    • <APP_KEY> : DROPBOX_APP_KEY と同じ文字列
    • <APP_SECRET> : DROPBOX_APP_SECRET と同じ文字列
    curl https://api.dropbox.com/oauth2/token -d grant_type=refresh_token -d refresh_token=<REFRESH_TOKEN> -u <APP_KEY>:<APP_SECRET>
  4. .envファイルに、2. と 3. で取得したリフレッシュトークンとアクセストークンをコピーします。

実装

  • 指定したファイルのアップロード/削除を実装しました。

    • Discordのチャンネルに直接アップロードするのはリスクが伴う上に3MB未満の制限があります。
    • Discord仕様上、画像のURLからサムネイルが自動生成されるため、間接的にDiscord上で画像を見ることが可能になります(前回説明してなかった…)。
    • Stable Diffusionはガチャです。Seed値によってハズレがあります(急に局部を露出する、キメラが生成される、腕が3本生えるetc…)。不要なファイルが溜まっても仕方ないので、スラッシュコマンドを実行する度に、直前にアップロードした画像を削除する機能も実装します。
    import fs from 'fs';
    import { Dropbox } from 'dropbox';
    import { getEnv } from '../lib/env';
    
    const {
    dropboxAccessToken,
    dropboxRefreshToken,
    dropboxAppKey,
    dropboxAppSecret,
    } = getEnv();
    
    export const deleteDropboxImage = async (fileName: string) => {
    
    try {
        const dropboxClient = new Dropbox({
        accessToken: dropboxAccessToken,
        refreshToken: dropboxRefreshToken,
        clientId: dropboxAppKey,
        clientSecret: dropboxAppSecret,
        });
    
        await dropboxClient.filesDeleteV2({
        path: `/${fileName}`,
        });
    
        console.log(`${fileName} deleted successfully from Dropbox.`);
    } catch (error) {
        console.log(`Dropboxの${fileName}の削除に失敗しました。`);
    }
    };
    
    export const uploadImageToDropbox = async (fileName: string) => {
    
    try {
        const dropboxClient = new Dropbox({
        accessToken: dropboxAccessToken,
        refreshToken: dropboxRefreshToken,
        clientId: dropboxAppKey,
        clientSecret: dropboxAppSecret,
        });
    
        const buffer = fs.readFileSync(`./out/${fileName}`);
        await dropboxClient.filesUpload({
        path: `/${fileName}`,
        contents: buffer,
        });
        await dropboxClient.sharingCreateSharedLinkWithSettings({
        path: `/${fileName}`,
        });
        const sharingLinks = await dropboxClient.sharingListSharedLinks({
        path: `/${fileName}`,
        direct_only: true,
        });
    
        return sharingLinks.result.links.map((link) =>
        link.url.replace('www.dropbox', 'dl.dropboxusercontent')
        );
    } catch (error) {
        console.log(error);
        throw new Error(`Dropboxへのアップロードに失敗しました。`);
    }
    };

いざ実行

  • やったぜ。

    • 繰り返しますが、Dropboxにアップした画像を、Discordのサムネイルで見ているだけです。

    image02

感想した完走

  • 父が、三線(さんしん)という楽器にハマっていて「DALL-E-3では三線の画像生成できないのか」という言葉がキッカケで挑戦してみました。
  • RTX2070という、そこそこのグラフィックボードを持て余していたため、追加学習ができ、表現の自由度も高いStable Diffusionを、まずは誰でも気軽に使えるようにしてみました。
  • 三線の追加学習が上手く行くかは不明です。
この記事をシェア

弊社では、一緒に会社を面白くしてくれる仲間を募集しています。
お気軽にお問い合わせください!