Markdown AIを使ってラクラク画像作成サイトを作ってみた！

# はじめに

**無料でAIサイトを公開できる！？**（2025年3月現在）

いつものようにQiitaのトレンドをあさっていた私は、以下の記事をきっかけに、遅ればせながら「**Markdown AI**」なるものを知り大変驚きました。

https://qiita.com/nft/items/8c40ff02a9854747cb4a

生成AIに興味はありながらも、利用料がネックとなっていた私にとって、Markdown AIは大変魅力的なツールでした。

今回はMarkdown AIを使って、 [**ラクラク画像生成サイト「ラクガゾ」**](https://mdown.ai/content/da494aaa-b751-4c7c-869c-f9ef76d359d7) を構築しました。
用途は名前そのままで、より楽に"いい感じ"の絵を生成するためのサイトです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3665927/fb965044-09c7-48e6-a63a-fb66253e740d.png)

# 本記事の対象読者

- Markdown AIに興味がある人
- Markdown AIで、お手軽にちょっぴり見栄えがいいUIを作りたい人
- 画像生成で"いい感じ"の画像を楽に作りたい人

# Markdown AIとは

Markdown AIの公式が出している以下の記事がわかりやすいです。

https://qiita.com/mdown_ai_jpn/items/d3e281565c876a0bd64f

通常Webサイトを公開するだけでも、インフラの構築・維持に時間もお金もかかります。
しかし、Markdown AIならサクッと作ってサクッと公開できてしまいます。
さらに、生成AIを組み込めて、なんと画像生成もできるではないですか！
そして、なんといっても **無料！** すばらしい！

私が、サクッと作ってみた「自己紹介のコツ」について解説したサイトがこちらです。
※内容はChatGPTに手伝っていただきました。
https://mdown.ai/content/3f8a0213-6c49-4610-8cc4-9be9f4596a05

Mermaid記法も使えました！ほんとに便利！（[参考](https://qiita.com/mdown_ai_jpn/items/4acc4460c8fc123599fb)）


# このAIサイトを作ったきっかけ

Markdown AIを早速色々使ってみると、dall-e-3による無料の画像生成がシンプルに楽しい！
楽しさのあまり、妻（ITは詳しくない）に見せようとしましたが、そういうときに限って、なんか生成される画像が微妙です(´･ω･`) ｼｮﾎﾞｰﾝ

例えば、dall-e-3に「遠くから見たシマウマの群れ」をプロンプトとして入力した際に生成された画像がこちらです。
![ダウンロード (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3665927/a1f736c2-e973-4813-be12-4f2d5d921bec.png)

なんかちょっと違いますよね。。。

**そこで、Markdown AIについて勉強する一環として、楽に"いい感じ"の画像を生成して遊べるサイトを作ってみることにしました**。

冒頭でも紹介しましたが、その結果、作ってみたサイトがこちらです。
[**ラクラク画像生成サイト「ラクガゾ」**](https://mdown.ai/content/da494aaa-b751-4c7c-869c-f9ef76d359d7) 

このサイトを使うと、同じ「遠くから見たシマウマの群れ」というキーワードを与えただけで、以下のような画像を生成してくれるようになりました。

![ダウンロード (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3665927/7099ebeb-4992-4f12-9542-317fb4abc38b.png)

こっちの方が、**"いい感じ"** ですよね！


# いい感じの画像を生成するには

プロンプトを工夫することが重要ということを知りました。
以下のサイトが初学者である私にとってはとてもわかりやすかったです。

https://note.com/data_teller/n/n57404e6460d7

そこで、gpt-4o-miniで入力されたキーワードをもとにデザインし、dall-e-3用のプロンプトを出力して、そのプロンプトをもとに画像を生成するというフローにしました。

```mermaid
sequenceDiagram
    actor user
    user->>gpt-4o-mini: keyword
    gpt-4o-mini->>user: prompt for dall-e-3
    user->>dall-e-3: prompt for dall-e-3
    dall-e-3->>user: image
```

gpt-4o-miniのAIモデルには以下のプロンプトを与えました。

```
Role:あなたはデザイナーであり、DALL-E3による画像生成用のプロンプトを考える人です。
Request:画像生成用のプロンプトを英語で出力してください。
Rule:
- 画像生成のプロンプトを考える際には以下のステップを段階的に踏んでください。
-- STEP1：イメージのアイデアをブレストして4つ作成する 
-- STEP2：各アイデアに対して適切な表現やタッチを選択し、その後配色、構図を決めてください。
-- STEP3：ブレストしたアイデアを「入力キーワードをよく表現できているか」という観点で評価して最適な1つを選んでください
-- STEP4：アイデアがまとまったらそれを描写するためのプロンプトを英語で作成してください。 
- 上記STEP1～STEP3は内部で処理を行うのみとし、画面に出力してはいけません。
- 出力するのは、最終的な英語のプロンプトのみとしてください。
Reason:キーワードを入力します。そのキーワードを元にイメージのアイデアを考えてください。
Recommend: 生成するプロンプトの単語数は、50個以上100個未満にしてください。
```
プロンプトの書き方としては、以下を参考にさせていただきました。

https://qiita.com/pdc-ujoy/items/81009936ddf843665e40#prompt%E3%83%97%E3%83%AD%E3%83%B3%E3%83%97%E3%83%88


# ちょっぴり使いやすいUIにするために

BootstrapをCDNで使いました。

以下、採用理由です。
- Markdown AIに組み込むにあたって、CDNで使えるもの
- スマホでも使えるように簡単にレスポンシブデザインにしたかった
- スタイル用のコードをできるだけ書かずに、出来合いの部品で綺麗な見た目にしたかった
- 以前、少し触ったことがある

https://getbootstrap.jp/

# このAIサイトで作った画像たち

## ピラミッドとラクダ

- キーワード：ピラミッドとラクダと砂漠
- スタイル：指定なし
- 視点：指定なし

![ダウンロード (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3665927/5cd6f8b8-015a-44da-9d8f-0d0d69589ac6.png)


## 子猫と子犬

- キーワード: 子猫と子犬がじゃれ合う様子
- スタイル：モノクロスケッチスタイル
- 視点：指定なし

![ダウンロード (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3665927/ada17996-a4b0-428f-aafd-7135c77df534.png)


## 残業している会社員

- キーワード：残業している会社員
- スタイル：ミニマリストスタイル
- 視点：指定なし

![ダウンロード (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3665927/db966b4b-9370-4fc2-832c-a314e9ba6be7.png)


# Full code

- `<script>`の部分は、なぜか`<body>`の中に書くとうまく動かなかったので、外に出しています
- `<body>`の中に空行があると、なぜかレイアウトが崩れてしまうのでご注意ください
- 画像のサイズも選べるようにしたかったんですが、`size: "1792x1024"`としてもうまく効かず断念しました
- AIモデルのAPIキーは、「Insert」ボタンで挿入したソースコードを読むとわかります
- スタイルの選択肢は[こちら](https://kuragedao.com/2023/11/24/%E3%80%90chatgpt%E3%80%91%E3%82%A4%E3%83%A9%E3%82%B9%E3%83%88%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%81%BE%E3%81%A8%E3%82%81/)を参考にさせていただきました

<details><summary>Full code</summary>

```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>ラクガゾ - ラクラク画像生成</title>
    <link
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
      crossorigin="anonymous"
    />
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
      crossorigin="anonymous"
    ></script>
    <style>
      body {
        font-family: "Hiragino Kaku Gothic ProN", "メイリオ", sans-serif;
        background-color: #edf6f6;
        height: 100%;
        min-height: 100vh;
      }
    </style>
  </head>
  <body>
    <div class="container col-lg-6">
      <main>
        <h3 class="text-primary fw-bold mt-2">ラクガゾ</h3>
        <div class="my-3">
          <p class="text-secondary">~ ラクラク画像生成 ~</p>
        </div>
        <form id="imageForm" novalidate>
          <div class="row g-3">
            <div class="col-12">
              <label for="thema" class="form-label">テーマ</label>
              <input type="text" class="form-control" id="thema" placeholder="元気に遊ぶ猫" required />
              <div class="invalid-feedback">テーマを入力してください</div>
            </div>
            <div class="col-12">
              <label for="style" class="form-label">スタイル</label>
              <select class="form-select" id="style" required>
                <option value="指定なし" selected>指定なし</option>
                <option value="フォトリアリスティックスタイル">フォトリアリスティックスタイル</option>
                <option value="手描きスタイル">手描きスタイル</option>
                <option value="モノクロスケッチスタイル">モノクロスケッチスタイル</option>
                <option value="ミニマリストスタイル">ミニマリストスタイル</option>
                <option value="フラットデザインスタイル">フラットデザインスタイル</option>
                <option value="水彩画・ウォーターカラースタイル">水彩画・ウォーターカラースタイル</option>
              </select>
            </div>
            <div class="col-12">
              <label for="perspective" class="form-label">視点</label>
              <select class="form-select" id="perspective" required>
                <option value="指定なし" selected>指定なし</option>
                <option value="正面の視点">正面の視点</option>
                <option value="上からの視点">上からの視点</option>
                <option value="下からの視点">下からの視点</option>
                <option value="背面からの視点">背面からの視点</option>
                <option value="遠くからの視点">遠くからの視点</option>
              </select>
            </div>
          </div>
          <hr class="my-4" />
          <div class="col-12">
            <button id="generateButton" class="w-100 btn btn-primary" type="button">
              <span class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
              <span class="button-text">作成</span>
            </button>
          </div>
        </form>
        <div class="col-12 my-4">
          <div class="label">生成されたプロンプト</div>
          <div id="dalle3Prompt"></div>
        </div>
        <div class="col-12 my-4">
          <div class="label">生成された画像</div>
          <div id="generatedImage"></div>
        </div>
      </main>
    </div>
  </body>
</html>

<script>
  (() => {
    const gpt4ApiKey = "AIモデルのキー";
    const dalle3ApiKey = "AIモデルのキー";
    const validate = (params) => {
      let isValid = true;
      if (!params.thema) {
        isValid = false;
      }
      return isValid;
    };
    const showLoading = (button) => {
      button.disabled = true;
      button.querySelector(".spinner-border").classList.remove("d-none");
      button.querySelector(".button-text").textContent = "作成中";
    };
    const stopLoading = (button) => {
      button.disabled = false;
      button.querySelector(".spinner-border").classList.add("d-none");
      button.querySelector(".button-text").textContent = "作成";
    };
    const initialize = () => {
      document.getElementById("imageForm").classList.remove("was-validated");
      document.getElementById("dalle3Prompt").innerText = "";
      document.getElementById("generatedImage").innerHTML = "";
    };
    const generateImage = async (button) => {
      showLoading(button);
      initialize();
      const thema = document.getElementById("thema").value;
      const style = document.getElementById("style").value;
      const perspective = document.getElementById("perspective").value;
      const params = {
        thema,
        style,
        perspective,
      };
      const gptPrompt = [thema, style, perspective].filter((input) => input !== "指定なし").join(" ");
      try {
        if (!validate(params)) {
          document.getElementById("imageForm").classList.add("was-validated");
          return false;
        }
        const serverAi = new ServerAI();
        const dalle3Prompt = await serverAi.getAnswerText(gpt4ApiKey, "", gptPrompt);
        document.getElementById("dalle3Prompt").innerText = dalle3Prompt;
        const answer = await serverAi.getAnswer(dalle3ApiKey, {
          prompt: dalle3Prompt,
          size: "1024x1024",
          n: 1,
          type: "image",
        });
        document.getElementById("generatedImage").innerHTML = `<img src="${answer.imageUrl}" alt="${thema}">`;
      } catch (error) {
        document.getElementById("dalle3Prompt").innerText = "Error!";
      } finally {
        stopLoading(button);
      }
    };
    const button = document.getElementById("generateButton");
    button.addEventListener("click", async (event) => {
      generateImage(button);
    });
  })();
</script>
```
</details>

# おわりに

今回は、Markdown AIを使って画像生成を楽にできるサイトを作ってみました。
Markdown AIは、ベータ版ということもあり、うまく使えない部分もありましたが、無料でAIサイトをサクッと構築し公開できる点は、大変魅力的に感じました。
「楽に"いい感じ"の画像生成」を目指して作ったこのサイトですが、たいていのキーワードであれば、そのままDALL-E-3に与えても、"いい感じ"の画像を作成してくれます( ˊᵕˋ ;)。
また、このサイトでもキーワードによっては、"いい感じ"の画像を作ってくれないことも結構あります（笑）。
しかし、このサイトの良さとしては、スタイルや視点を変えた違うパターンの画像をサクッと作れるところにもあると思います。
また、デバッグ用に出力したDALLE-E-3用の英語のプロンプトが、画像生成を待つ間の読み物としてちょっとした暇つぶしになって個人的には良かったです。

ここまで読んでいただき、ありがとうございました！

# 参考文献

https://qiita.com/mdown_ai_jpn/items/d3e281565c876a0bd64f

https://qiita.com/mdown_ai_jpn/items/4acc4460c8fc123599fb

https://qiita.com/nft/items/8c40ff02a9854747cb4a

https://note.com/data_teller/n/n57404e6460d7

https://kuragedao.com/2023/11/24/%E3%80%90chatgpt%E3%80%91%E3%82%A4%E3%83%A9%E3%82%B9%E3%83%88%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%81%BE%E3%81%A8%E3%82%81/