GeminiとPhotoAIで実装するAI画像生成Webアプリ「IMGCREATE」の開発

## はじめに

こんにちは！今回は、Google GeminiのAPIを活用したAI画像生成Webアプリ「IMGCREATE」の開発について紹介します。このアプリケーションでは、テキストプロンプトからAI画像を生成したり、既存の画像をAIで編集したりする機能を実装しています。

このプロジェクトの特徴は以下の通りです：

- GeminiのAPIを使用した高品質な画像生成
- フロントエンドは純粋なHTML/CSS/JavaScriptで実装
- バックエンドはPHPによる軽量なAPI構造
- レスポンシブデザインとダークモード対応のUI

今回の記事では、このプロジェクトの実装方法と、特に重要なポイントについて解説します。

## アプリケーション概要

IMGCREATEは、以下の機能を持つAI画像生成Webアプリケーションです：

1. テキストプロンプトからの画像生成
2. アップロード画像をベースにした画像生成
3. 生成された画像の編集
4. 複数のモデル（Gemini 2.0、Imagen 3）の選択
5. スタイル設定（写真風、アニメ風など）
6. 編集履歴の管理

## システム構成

プロジェクトの構成は以下のようになっています：

```
index.html          # メインのHTMLファイル
api/                # API関連のPHPファイル
  debug.php         # デバッグ用API
  generate.php      # 画像生成API
cache/              # キャッシュディレクトリ
config/             # 設定ファイル
  config.php        # メインの設定ファイル
css/                # スタイルシート
  style.css         # メインのCSSファイル
js/                 # JavaScriptファイル
  app.js            # メインのJSファイル
  api/
    imageGenerator.js  # 画像生成関連の機能
  core/
    handlers.js     # イベントハンドラー
  ui/
    interface.js    # UI操作関連の機能
logs/               # ログファイル
```

## 主要機能の実装解説

### 1. 画像生成APIの実装

画像生成の中核となる部分は、`generate.php`で実装されています。ここではGemini APIに対してリクエストを送信します：

```php
// APIリクエストの構築部分
$requestParts = [['text' => $prompt]];

// 編集モードの場合、元画像を含める
if ($isEditMode && $baseImage) {
    // Base64データURIから実際のBase64データを抽出
    $baseImageData = preg_replace('/^data:image\/\w+;base64,/', '', $baseImage);
    
    // 画像パートを追加
    $requestParts[] = [
        'inlineData' => [
            'mimeType' => 'image/jpeg',
            'data' => $baseImageData
        ]
    ];
}

// リクエストデータの構造を作成
$requestData = [
    'contents' => [
        [
            'parts' => $requestParts
        ]
    ],
    'generationConfig' => [
        'responseModalities' => ['IMAGE', 'TEXT'],
        'temperature' => 0.7,
        'topP' => 0.98,
        'topK' => 40,
        'maxOutputTokens' => 8192
    ],
    'safetySettings' => [
        [
            'category' => 'HARM_CATEGORY_HARASSMENT',
            'threshold' => 'BLOCK_NONE',
            'category' => 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
            'threshold' => 'BLOCK_NONE',
        ]
    ]
];
```

特に注目すべき点は、画像を含めた複合的なリクエストの構築方法と、複数回のリトライ処理を実装していることです。

### 2. フロントエンドの画像生成処理

フロントエンド側では、`imageGenerator.js`が画像生成のAPIを呼び出す役割を担っています：

```javascript
// 並列で複数の画像を生成
async generateParallel(prompt, mode = 'generate', baseImage = null, count = 2) {
    try {
        // ローディング表示
        const loadingIndicator = document.getElementById('loadingIndicator');
        const outputContainer = document.getElementById('outputContainer');
        
        if (loadingIndicator) loadingIndicator.classList.remove('hidden');
        if (outputContainer) outputContainer.classList.add('hidden');

        // 画像のリサイズ処理
        let processedBaseImage = baseImage;
        if (baseImage) {
            processedBaseImage = await this.resizeImage(baseImage);
        }

        // 並列リクエストを作成
        const requests = Array(count).fill().map(async () => {
            const requestData = {
                prompt: prompt,
                mode: mode,
                model: window.selectedModel,
                styles: window.selectedStyles
            };

            if (processedBaseImage) {
                requestData.baseImage = processedBaseImage;
            }

            const response = await fetch('/ch/imgc/api/generate.php', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(requestData)
            });

            // レスポンス処理
            // ...
        });

        // すべてのリクエストを並列実行
        const results = await Promise.allSettled(requests);
        
        // ...
    }
}
```

ここでは、画像を並列生成するための実装が見られます。これにより、複数のバリエーションを一度に生成することが可能になっています。

### 3. イベントハンドラーとユーザーインターフェース

ユーザーの操作に応じた処理は、`handlers.js`に実装されています：

```javascript
// 画像生成処理
async handleGenerate() {
    const promptInput = document.getElementById('promptInput');
    const prompt = promptInput.value.trim();
    
    if (!prompt) {
        alert('画像を生成するためのプロンプトを入力してください');
        return;
    }
    
    try {
        // プロンプトを強化
        const enhancedPrompt = window.imageGenerator.enhancePrompt(prompt);
        
        // アップロードされた画像をベースに使用
        const baseImage = window.uploadedImages.length > 0 ? 
            window.uploadedImages[window.uploadedImages.length - 1].dataUrl : null;
        
        // 並列生成
        const results = await window.imageGenerator.generateParallel(enhancedPrompt, 'generate', baseImage, 2);
        
        // 結果の処理
        if (results[0]) {
            window.currentImage = results[0].images[0];
            window.generatedContent = results[0];
        }
        
        // 結果表示
        window.userInterface.displayGeneratedContent(results);
        
        // アップロードされた画像のクリア（現在はコメントアウト）
        // window.uploadedImages = [];
        // const uploadedImagesGrid = document.getElementById('uploadedImages');
        // if (uploadedImagesGrid) {
        //     uploadedImagesGrid.innerHTML = '';
        // }
        // document.getElementById('uploadedImagesContainer').classList.add('hidden');
        
        // 履歴に追加
        if (window.currentImage) {
            window.userInterface.addToHistory(prompt, window.currentImage);
        }
        
    } catch (error) {
        console.error('画像生成エラー:', error);
        alert('画像の生成中にエラーが発生しました: ' + error.message);
    }
}
```

注目すべきポイントとして、生成後にアップロードされた画像をクリアする処理がコメントアウトされています。これにより、ユーザーは一度アップロードした画像を用いて、複数回の異なるプロンプトで画像生成を試せるようになっています。

### 4. UIコンポーネントの実装

UIに関する処理は`interface.js`に集約されています。特に、編集ダイアログや結果の表示など、複雑なUI要素の実装が特徴的です：

```javascript
// 編集ダイアログの表示
editDialog: {
    show(imageData) {
        const dialog = document.createElement('div');
        dialog.className = 'edit-dialog';
        
        const historyContent = window.editHistory.length > 0 ? `
            <div class="edit-history-area">
                <h4>編集履歴</h4>
                <div class="edit-history-list">
                    ${this.generateEditHistoryHTML()}
                </div>
            </div>
        ` : '';
        
        dialog.innerHTML = `
            <h3>画像を編集</h3>
            <div class="edit-image-preview">
                <img src="${imageData}" alt="編集する画像" />
            </div>
            <div class="edit-input-area">
                <textarea id="edit-prompt" placeholder="画像をどのように編集しますか？例：「空の色を青くする」「背景をぼかす」"></textarea>
                <div class="edit-suggestions">
                    <span class="suggestion-tag">明るさを調整</span>
                    <span class="suggestion-tag">色調を変更</span>
                    <span class="suggestion-tag">背景を変更</span>
                    <span class="suggestion-tag">テイストを変更</span>
                </div>
            </div>
            
            <div class="edit-buttons">
                <button class="primary-button" id="executeEditButton" onclick="window.handlers.editImage()">編集を実行</button>
                <button class="secondary-button" onclick="this.closest('.edit-dialog').remove()">キャンセル</button>
            </div>

            <div class="edit-status hidden">
                <div class="edit-spinner"></div>
                <p>画像を編集中...</p>
            </div>

            ${historyContent}
        `;
        
        document.body.appendChild(dialog);
        
        // イベントリスナーの設定
        // ...
    }
}
```

このようにJavaScriptで動的にダイアログを生成し、編集操作を実装しています。

## 実装上の工夫点

### 1. プロンプト強化機能

ユーザーが入力したプロンプトを自動的に強化する機能を実装しています：

```javascript
// プロンプトを強化する関数
enhancePrompt(originalPrompt) {
    // 選択されたスタイルに基づいてプロンプトを拡張
    let enhancedPrompt = originalPrompt;
    
    // 基本的なシーン設定を追加（プロンプトが短い場合）
    if (originalPrompt.length < 30) {
        enhancedPrompt += ", クリアな背景, 高解像度, 詳細なディテール";
    }
    
    // スタイルタグに基づく拡張
    if (window.selectedStyles.includes('photorealistic')) {
        enhancedPrompt += ", 高解像度の写真のような画像, 写実的な表現, ハイディテール";
    } else if (window.selectedStyles.includes('anime')) {
        enhancedPrompt += ", アニメスタイル, 鮮やかな色彩, スタイライズされたデザイン";
    }
    // ...その他のスタイル
    
    return enhancedPrompt;
}
```

これにより、AIによる画像生成の品質を向上させています。

### 2. 複数画像の並列生成

一度のボタンクリックで複数の画像を並列生成し、バリエーションを提供しています：

```javascript
// 並列で2つの画像を生成
const results = await window.imageGenerator.generateParallel(enhancedPrompt, 'generate', baseImage, 2);
```

これにより、ユーザーは複数のバリエーションから好みのものを選べるようになります。

### 3. 編集履歴の管理

画像編集の履歴を保持し、以前の状態に戻せる機能も実装しています：

```javascript
// 履歴から特定の状態に戻る（履歴を維持）
restoreFromHistory(index) {
    if (index < 0 || index >= window.editHistory.length) return;
    
    // クリックした編集項目の状態を復元
    const historyItem = window.editHistory[index];
    
    // 編集実行後の画像を表示
    window.currentImage = historyItem.resultImageUrl || historyItem.imageUrl;
    
    // 現在のアクティブなインデックスを更新
    window.activeHistoryIndex = index;
    
    // UIを更新
    window.userInterface.displayGeneratedContent({
        images: [window.currentImage]
    });
    
    // 編集履歴のUIを更新（アクティブな状態を反映）
    window.userInterface.updateEditHistory();
}
```

### 4. キャッシュ機能と環境設定の分離

`config.php`では、キャッシュ機能と環境設定の分離も実装しています：

```php
// キャッシュ設定
'cache_enabled' => true,
'cache_dir' => '../cache',
'cache_lifetime' => 60 * 60 * 24, // 24時間

// キャッシュディレクトリが存在しない場合は作成
if ($config['cache_enabled'] && !file_exists($config['cache_dir'])) {
    mkdir($config['cache_dir'], 0755, true);
}

// 環境設定ファイルが存在する場合はマージ
$env_file = __DIR__ . '/env.php';
if (file_exists($env_file)) {
    $env_config = require_once $env_file;
    $config = array_merge($config, $env_config);
}
```

これにより、開発環境と本番環境での設定の違いを簡単に管理できます。

## 最適化と改善点

現在のコードには、いくつかの最適化ポイントがあります：


1. **アップロード画像の管理** - 現在はコメントアウトされていますが、ユーザビリティ向上のためにオプション化することも検討できます：

   ```javascript
   // アップロードされた画像をクリアするオプションボタンの追加
   const clearUploadsButton = document.createElement('button');
   clearUploadsButton.textContent = 'アップロード画像をクリア';
   clearUploadsButton.addEventListener('click', () => {
       window.uploadedImages = [];
       document.getElementById('uploadedImages').innerHTML = '';
       document.getElementById('uploadedImagesContainer').classList.add('hidden');
   });
   ```

2. **レスポンシブ設計の強化** - 特にモバイルでの編集操作の使いやすさの向上：

   ```css
   @media (max-width: 768px) {
       .edit-dialog {
           width: 90%;
           padding: 10px;
       }
       
       .edit-image-preview img {
           max-height: 200px;
       }
   }
   ```

## まとめ

今回紹介した「IMGCREATE」は、Gemini APIを活用したAI画像生成Webアプリケーションです。特に以下の点に力を入れて開発しました：

- 並列画像生成による複数バリエーションの提供
- ユーザーフレンドリーな編集インターフェース
- 編集履歴管理による作業状態の保存

このプロジェクトは、最小限のフレームワークを使用しながらも、モジュール化された構造により拡張性と保守性を高めています。

また、AIとのインタラクションの効率化を図るために、プロンプト強化機能や複数結果の並列生成など、ユーザー体験を向上させる工夫も取り入れています。

環境設定とアプリケーションコードの分離、キャッシュ機能の実装など、運用面での考慮も行っており、実用的なWebアプリケーションとして機能します。

今後の展開としては、更なるUIの改善、新しいAIモデルの追加、画像処理機能の拡充などを検討しています。

このプロジェクトがAI画像生成アプリケーション開発の参考になれば幸いです。

---

--プロジェクト全体をClaude 3.7 Sonnet thinkingに見てもらって、記事を書いてもらいました！

重要なところしかピックアップされていませんが、
画像を持ち越して編集してもらう処理とか
並列で生成させるところとか
必要なところはわかると思いますので、
ご自身のプロジェクトに組み込んでみてください！

どんな感じのツールかというのは、こっちのnoteにまとめています。

https://note.com/choro201/n/n3f8f6ea44cb2

質問やフィードバックがありましたら、コメント欄でお待ちしております！