Stable Diffusionで空いた時間にLineスタンプ大量生成！

こんにちは、ひよっこエンジニア兼デザイナーのウエハラ　シンペイです。

プログラミングを初めて早１ヶ月。今回は話題のAIを使って何かできないかと思い、空いた時間にLineスタンプメーカーを作ってみようと思います！レッツ小銭稼ぎ！

# Stable Diffusion×LINEでスタンプ用のイラストをつくる
色々方法はあると思うのですが、今回は基本のプロンプトを記述して、なおかつ空き時間にサクッと送って置けるようにLINE Message APIを使って構築していきます。

![素材.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/d1bb9885-a3f3-c65a-c11e-73becab491d6.png)



# 1.Hugging Faceでアカウント作成

[Hugging Face](URL "https://huggingface.co/")でアカウント作成し、導入方法や使用する上でのライセンスについてを調べると、アクセストークンの発行をしました。モデルについてはStabble Diffsionは[こちら](URL "https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0")を使用(詳しいことはわかってないけど人気だったんで笑)
![スクリーンショット 2023-12-28 3.01.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/7af475ac-9017-13a7-61d6-7979d8d0c651.png)


# 2.Node.jsでコードを書いてLINEで送ると画像生成
hugging face、AWS S3、LINE Messaging APIと名前は知ってても初めて自分で設定したのでかなり苦戦しましたが無事画像生成することができました。特にAWSはUIが変わったのか参考記事で書いてある内容と違っててかなり苦戦して、なんとか自力で設定しましたが、多分セキュリティ的には良くない方法で実装しているので、勉強不足を痛感しました。


<details><summary>コードはこちら</summary>

```javascript:sample.js

import express from 'express';
import { Client, middleware } from '@line/bot-sdk';
import fetch from 'node-fetch';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { randomBytes } from 'crypto';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// AWS SDKの設定
const s3Client = new S3Client({
    region: 'ap-northeast-1',
    credentials: {
        accessKeyId: 'XXXXXXXXXXXXXXXXXXXXXXXXX',
        secretAccessKey: 'XXXXXXXXXXXXXXXXXXXXXXXXX'
    }
});
const bucketName = 'stampmaker';

const config = {
    channelAccessToken: 'XXXXXXXXXXXXXXXXXXXXXXXXX',
    channelSecret: 'XXXXXXXXXXXXXXXXXXXXXXXXX',
};

const client = new Client(config);

const app = express();

app.post('/webhook', middleware(config), async (req, res) => {
    try {
        const results = await Promise.all(req.body.events.map(handleEvent));
        res.json(results);
    } catch (err) {
        console.error(err);
        res.status(500).end();
    }
});

async function query(data) {
    const response = await fetch(
        "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0",
        {
            headers: { Authorization: "Bearer XXXXXXXXXXXXXXXXXXXXXXXXX" },
            method: "POST",
            body: JSON.stringify(data),
        }
    );
    return response;
}

async function handleEvent(event) {
    if (event.type !== 'message' || event.message.type !== 'text') {
        return null;
    }

    const userText = event.message.text;
    const prompt = createPrompt(userText);

    const modifiedPrompt = `${prompt}, pose 1`;
    const response = await query({ inputs: modifiedPrompt });

    const fileName = `tempImage_1_${generateRandomString()}.png`;
    const imagePath = path.join(__dirname, fileName);
    await saveImage(response, imagePath);

    const imageUrl = await uploadToS3(imagePath, fileName);
    await sendImage(event.replyToken, imageUrl);

    fs.unlinkSync(imagePath);
    console.log('success');
    console.log(imageUrl);
}

async function saveImage(response, filePath) {
  const arrayBuffer = await response.arrayBuffer();
  const buffer = Buffer.from(arrayBuffer);
  return fs.promises.writeFile(filePath, buffer);
}

async function uploadToS3(filePath, fileName) {
  const fileContent = fs.readFileSync(filePath);
  const uploadParams = {
      Bucket: 'xxxxxxxxxx', // 文字列としてバケット名を指定
      Key: fileName,
      Body: fileContent,
      ContentType:'image/png',
  };

  await s3Client.send(new PutObjectCommand(uploadParams));
  return `https://${uploadParams.Bucket}.s3.amazonaws.com/${fileName}`;
}

function generateRandomString() {
    return randomBytes(16).toString('hex');
}

async function sendImage(replyToken, imageUrl) {
    const message = {
        type: 'image',
        originalContentUrl: imageUrl,
        previewImageUrl: imageUrl
    };
    await client.replyMessage(replyToken, message);
}

function createPrompt(userText) {
  return `${userText}　square, simple background, animal, Only one, -human , -realistic tone,`;
}


app.listen(3000, () => {
    console.log('Application is running on port 3000');
});


```

</details>

### 何はともあれプロンプトも細かく調整しながら、実際にできた画像は！

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/34c005a1-ae3d-48f4-d631-46a1e329d9a1.jpeg">

かなりかわいい！王道のかわいいワンちゃんでございます！


# 3.LINEスタンプメーカーでテキストを入れて登録
[LINEスタンプメーカー](URL "https://creator.line.me/ja/stickermaker/")を使って画像の背景を消したり、テキストを追加をしていきます。最低8個登録すれば登録・販売することができます！

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/b43de328-7810-492d-d1bb-1902ba5149f0.png">　<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/39a385d9-c144-9f53-e2ad-e7d54ed383ba.png">　<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618353/8037779d-621a-8a0a-1adc-1cc461c5d07e.png">

ここまでが画像生成からLINE　Store出品に向けたざっくりした流れになります！慣れればかなり早く作れそうで、あとはプロンプトを工夫して生成AIならではのスタンプも作れるかもしれません。

例えば１つ当たりの月平均¥10でも100個作れば月に1000円！1000個なら１万円！10000個なら!?

まぁ、そううまくはいかないでしょうが、初めて触ったプラットフォームが多い中でまずは一連の流れができてよかったです。あとは効率化とスタンプのアイデア次第で不労収益がっぽがっぽや！！


### 権利関係はこちらをチェック

https://huggingface.co/spaces/CompVis/stable-diffusion-license

https://creator.line.me/ja/guideline/sticker/