Stable DiffusionによるAI画像生成BOTを作った話(その2)
2023年 11月 27日 月曜日
概要
- 先日、TypeScript + Discord.js + Stable Diffusion で表題のモノをつくりました…が。
- ソシャゲのキャラに似せた画像を生成するまでは良かったのですが、局部が露出していなくても、肌色面積が多いと、R-18指定されたり削除されたりするそうです。
- 代替手段はDropboxでした。アーティストがセンシティブな絵を書く場合に使用されることもあるそうで、imgurよりかなり規制が緩い(らしい)。でも児ポはダメだぞ!
リポジトリ
- https://github.com/n1gaur1/generate-image-bot/tree/add_dropbox_api
- 元ソースから、ある程度リファクタリングしました。
- 一応mainブランチに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
- 下記トークンについては後述します。
- DROPBOX_REFRESH_TOKEN
- DROPBOX_ACCESS_TOKEN
アクセストークンとリフレッシュトークンの取得・利用
- アクセストークンの有効期間は非常に短いらしく、定期的に取得し直す必要があります。
- これを対策するために必要なものが、リフレッシュトークンと呼ばれるトークンです。
- リフレッシュトークンを利用することで、アクセストークンを自動で更新してくれます。
-
認証コードを取得します。下記URLの
YOUR_CLIENT_ID
はDROPBOX_APP_KEY
と同じ文字列に置換してアクセスします。https://www.dropbox.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&token_access_type=offline
-
リフレッシュトークンを取得するために
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>
-
アクセストークンを取得するために、再び
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>
-
.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へのアップロードに失敗しました。`); } };
いざ実行
感想した完走
- 父が、三線(さんしん)という楽器にハマっていて「DALL-E-3では三線の画像生成できないのか」という言葉がキッカケで挑戦してみました。
- RTX2070という、そこそこのグラフィックボードを持て余していたため、追加学習ができ、表現の自由度も高いStable Diffusionを、まずは誰でも気軽に使えるようにしてみました。
- 三線の追加学習が上手く行くかは不明です。
この記事をシェア