gearmachine commited on
Commit
7bde0cd
·
1 Parent(s): d9350e1

feat: 背景画像表示機能の実装とJavaScript統合の安定化

Browse files

- CLAUDE.md: 背景画像表示機能の実装状況を更新し、JavaScriptの不安定性に関する情報を追加
- app.py: 画像アップロード時に背景画像をポーズデータに含める処理を追加し、JavaScriptファイルの読み込み時にエラーハンドリングを実装
- pose_editor.js: 背景画像が含まれている場合の処理を追加し、デバッグログを強化

CLAUDE.md CHANGED
@@ -159,6 +159,8 @@ Key Features:
159
  - **State Management**: Use `gr.update()` to prevent unintended event triggers
160
  - **Debugging**: Add comprehensive logging for complex event flows
161
  - **Performance**: Minimize unnecessary redraws and event handlers
 
 
162
 
163
  ## Current Implementation Status
164
 
@@ -169,25 +171,37 @@ Key Features:
169
  - ✅ Dependencies and priorities clearly defined
170
 
171
  ### ✅ **Recent Completed Issues**
 
 
 
 
 
 
 
172
  - **Issue #029**: 手顔表示リアルタイム反映修正 (2025-01-13) 🎨
173
  * JavaScript側からのチェックボックス直接監視で解決
174
  * `setupGradioCheckboxListeners()`による自動監視機能実装
175
  * GradioのHTMLコンポーネント経由の制約を回避
176
  * リアルタイム表示設定変更が完全に動作
177
 
178
- ### 🔧 **Ready for Implementation**
179
- **Phase 1 (Project Foundation)**: Continue with remaining issues
180
  - Basic Gradio application structure ✅
181
  - Canvas element initialization ✅
182
  - JavaScript integration ✅
183
  - Display settings real-time update ✅
184
-
185
- **Subsequent Phases**: Follow `docs/実装計画.md`
186
- - DWPose model integration (Phase 2)
187
- - Drawing and image processing (Phase 3)
188
- - Basic editing features (Phase 4)
189
- - Advanced editing features (Phase 5)
190
- - Export functionality (Phase 6)
 
 
 
 
 
191
 
192
  ### 📝 Notes
193
  - Focus on simplicity compared to dwpose_modifier
 
159
  - **State Management**: Use `gr.update()` to prevent unintended event triggers
160
  - **Debugging**: Add comprehensive logging for complex event flows
161
  - **Performance**: Minimize unnecessary redraws and event handlers
162
+ - **JavaScript Integration**: Gradio HTMLコンポーネントのJS実行は不安定、データ経由の関数呼び出しが確実
163
+ - **Background Image Implementation**: `pose_result`にデータ含めて`gradioCanvasUpdate`で自動処理が最適
164
 
165
  ## Current Implementation Status
166
 
 
171
  - ✅ Dependencies and priorities clearly defined
172
 
173
  ### ✅ **Recent Completed Issues**
174
+ - **Canvas背景画像表示機能**: 画像ロード背景表示修正 (2025-06-13) 🖼️
175
+ * 画像アップロード時にCanvas背景への画像表示機能を実装
176
+ * HTMLコンポーネントJavaScript実行問題を解決
177
+ * `pose_result['background_image']`経由でbase64データを受け渡し
178
+ * `gradioCanvasUpdate`で自動的に`setBackgroundImage`呼び出し
179
+ * アスペクト比保持・30%透明度での背景画像描画が完全動作
180
+
181
  - **Issue #029**: 手顔表示リアルタイム反映修正 (2025-01-13) 🎨
182
  * JavaScript側からのチェックボックス直接監視で解決
183
  * `setupGradioCheckboxListeners()`による自動監視機能実装
184
  * GradioのHTMLコンポーネント経由の制約を回避
185
  * リアルタイム表示設定変更が完全に動作
186
 
187
+ ### 🔧 **Current Implementation Status**
188
+ **Phase 1 (Project Foundation)**: COMPLETED
189
  - Basic Gradio application structure ✅
190
  - Canvas element initialization ✅
191
  - JavaScript integration ✅
192
  - Display settings real-time update ✅
193
+ - Canvas background image display ✅
194
+
195
+ **Phase 2 (Core Features)**: IN PROGRESS 🚧
196
+ - DWPose model integration
197
+ - Image upload and pose detection ✅
198
+ - Basic pose drawing
199
+ - Background image display
200
+
201
+ **Remaining Phases**: Follow `docs/実装計画.md`
202
+ - Enhanced editing features (Phase 3)
203
+ - Advanced editing features (Phase 4)
204
+ - Export functionality (Phase 5)
205
 
206
  ### 📝 Notes
207
  - Focus on simplicity compared to dwpose_modifier
app.py CHANGED
@@ -20,9 +20,14 @@ _is_updating = False # Issue 038: データ同期処理中フラグ(refs issu
20
 
21
  def load_javascript():
22
  """JavaScriptファイルを読み込む"""
23
- js_path = os.path.join(os.path.dirname(__file__), "static", "pose_editor.js")
24
- with open(js_path, "r", encoding="utf-8") as f:
25
- return f"<script>{f.read()}</script>"
 
 
 
 
 
26
 
27
  def image_to_base64(image):
28
  """PIL画像をBase64データURLに変換"""
@@ -230,15 +235,11 @@ def main():
230
  _current_frame_index = 0
231
  print(f"[DEBUG] ✅ グローバル変数更新完了(画像アップロード・refs互換)")
232
 
233
- # 🎨 背景画像をJavaScriptに設定
234
  image_base64 = image_to_base64(image)
235
- js_code = f"""
236
- if (window.setBackgroundImage) {{
237
- window.setBackgroundImage('{image_base64}');
238
- }}
239
- """
240
 
241
- return pose_result, pose_result, gr.update(value=js_code)
242
  else:
243
  print(f"[DEBUG] ❌ Pose detection failed")
244
  return None, {}, gr.update()
 
20
 
21
  def load_javascript():
22
  """JavaScriptファイルを読み込む"""
23
+ try:
24
+ js_path = os.path.join(os.path.dirname(__file__), "static", "pose_editor.js")
25
+ with open(js_path, "r", encoding="utf-8") as f:
26
+ js_content = f.read()
27
+ return f"<script>{js_content}</script>"
28
+ except FileNotFoundError:
29
+ print(f"[ERROR] JavaScript file not found: {js_path}")
30
+ return "<script>console.error('pose_editor.js not found');</script>"
31
 
32
  def image_to_base64(image):
33
  """PIL画像をBase64データURLに変換"""
 
235
  _current_frame_index = 0
236
  print(f"[DEBUG] ✅ グローバル変数更新完了(画像アップロード・refs互換)")
237
 
238
+ # 🎨 背景画像をポーズデータに含める
239
  image_base64 = image_to_base64(image)
240
+ pose_result['background_image'] = image_base64
 
 
 
 
241
 
242
+ return pose_result, pose_result, gr.update()
243
  else:
244
  print(f"[DEBUG] ❌ Pose detection failed")
245
  return None, {}, gr.update()
issues/027_画像背景表示実装.md CHANGED
@@ -1,4 +1,4 @@
1
- # Issue 027: 画像背景表示実装 🖼️💖
2
 
3
  ## 📋 問題概要
4
 
@@ -32,11 +32,85 @@ function drawBackgroundImage(image) {
32
 
33
  ## ✅ 完了条件
34
 
35
- - [ ] アップロードした画像がCanvas背景に表示される
36
- - [ ] 画像が適切に暗く表示される
37
- - [ ] アスペクト比が保持される
38
- - [ ] 正方形でない画像も正しく表示される
39
- - [ ] refs/dwpose_modifier と同等の表示
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  ## 📝 参考資料
42
 
 
1
+ # Issue 027: 画像背景表示実装 🖼️💖 ✅ COMPLETED (2025-06-13)
2
 
3
  ## 📋 問題概要
4
 
 
32
 
33
  ## ✅ 完了条件
34
 
35
+ - [x] アップロードした画像がCanvas背景に表示される
36
+ - [x] 画像が適切に暗く表示される
37
+ - [x] アスペクト比が保持される
38
+ - [x] 正方形でない画像も正しく表示される
39
+ - [x] refs/dwpose_modifier と同等の表示
40
+
41
+ ## 🚀 実装完了 (2025-06-13)
42
+
43
+ ### 🔍 問題の原因分析
44
+ 1. **GradioのHTMLコンポーネント制約**: JavaScriptコードの動的実行が不安定
45
+ 2. **タイミング問題**: 画像アップロード時の関数呼び出しが実行されない
46
+ 3. **データ受け渡し方法**: 直接的なJavaScript実行に依存していた
47
+
48
+ ### 💡 解決アプローチ
49
+ **従来の失敗した方法**:
50
+ ```python
51
+ # HTMLコンポーネント経由でJavaScript実行 - 動作せず
52
+ js_code = f"window.setBackgroundImage('{image_base64}');"
53
+ return gr.update(value=js_code)
54
+ ```
55
+
56
+ **成功した解決方法**:
57
+ ```python
58
+ # ポーズデータに背景画像を含める
59
+ pose_result['background_image'] = image_base64
60
+ return pose_result, pose_result, gr.update()
61
+ ```
62
+
63
+ ### 🎨 技術実装詳細
64
+
65
+ #### Python側 (app.py)
66
+ ```python
67
+ # 画像アップロード処理内
68
+ image_base64 = image_to_base64(image)
69
+ pose_result['background_image'] = image_base64
70
+ ```
71
+
72
+ #### JavaScript側 (static/pose_editor.js)
73
+ ```javascript
74
+ // gradioCanvasUpdate関数内で自動検出・設定
75
+ if (poseData && poseData.background_image) {
76
+ if (window.setBackgroundImage) {
77
+ window.setBackgroundImage(poseData.background_image);
78
+ }
79
+ }
80
+
81
+ // setBackgroundImage関数の既存実装を活用
82
+ window.setBackgroundImage = function(imageData) {
83
+ const img = new Image();
84
+ img.onload = function() {
85
+ window.poseEditorGlobals.backgroundImage = img;
86
+ // Canvas再描画
87
+ drawPose(currentPoseData, enableHands, enableFace);
88
+ };
89
+ img.src = imageData;
90
+ };
91
+
92
+ // drawBackground関数で30%透明度描画
93
+ function drawBackground() {
94
+ if (window.poseEditorGlobals.backgroundImage) {
95
+ ctx.globalAlpha = 0.3;
96
+ ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
97
+ ctx.globalAlpha = 1.0;
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### 🎯 キーポイント
103
+ 1. **確実なデータ受け渡し**: `pose_result`オブジェクトにデータ含有
104
+ 2. **自動的な関数呼び出し**: `gradioCanvasUpdate`で背景画像検出時に自動実行
105
+ 3. **既存機能の再利用**: `setBackgroundImage`や`drawBackground`の実装活用
106
+ 4. **アスペクト比保持**: 画像の縦横比を維持してCanvas内に最適配置
107
+ 5. **透明度制御**: 30%透明度でポーズが見やすい背景表示
108
+
109
+ ### 📈 技術的な学び
110
+ - **Gradioの制約**: HTMLコンポーネントのJS実行は不安定、データ経由が確実
111
+ - **refs実装の有効活用**: 既存の成功パターンを最大限活用
112
+ - **段階的デバッグ**: ログ追加→問題特定→解決方法検証→クリーンアップ
113
+ - **確実な実装**: 複雑なタイミング制御より、シンプルなデータフロー
114
 
115
  ## 📝 参考資料
116
 
static/pose_editor.js CHANGED
@@ -1783,8 +1783,6 @@ function drawBackground() {
1783
  ctx.globalAlpha = 0.3;
1784
  ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
1785
  ctx.globalAlpha = 1.0; // 透明度を元に戻す
1786
-
1787
- debugLog(`🎨 Background image drawn: ${drawWidth}x${drawHeight} at (${offsetX}, ${offsetY})`);
1788
  }
1789
  }
1790
 
@@ -2145,9 +2143,17 @@ window.gradioCanvasUpdate = function(pose_json_str) {
2145
  hasFace: !!poseData?.people?.[0]?.face_keypoints_2d,
2146
  hasBodies: !!poseData?.bodies,
2147
  hasHands: !!poseData?.hands,
2148
- hasFaces: !!poseData?.faces
 
2149
  });
2150
 
 
 
 
 
 
 
 
2151
  // 💥 既存の手・顔データを保護!Python側データで上書きしない
2152
  const existingPoseData = window.poseEditorGlobals.poseData;
2153
  if (existingPoseData && existingPoseData.people && existingPoseData.people[0] &&
@@ -2320,6 +2326,8 @@ window.showToast = function(type, message) {
2320
 
2321
  // 🎨 背景画像設定機能(refs互換)
2322
  window.setBackgroundImage = function(imageData) {
 
 
2323
  if (!imageData) {
2324
  // 背景画像をクリア
2325
  window.poseEditorGlobals.backgroundImage = null;
@@ -2345,7 +2353,7 @@ window.setBackgroundImage = function(imageData) {
2345
  }
2346
  };
2347
 
2348
- img.onerror = function() {
2349
  debugLog("🚨 Failed to load background image");
2350
  };
2351
 
@@ -3456,4 +3464,6 @@ function drawEstimatedConnections(candidates, originalRes, scaleX, scaleY) {
3456
  // スタイルをリセット
3457
  ctx.setLineDash([]); // 実線に戻す
3458
  ctx.globalAlpha = 1.0; // 不透明に戻す
3459
- }
 
 
 
1783
  ctx.globalAlpha = 0.3;
1784
  ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
1785
  ctx.globalAlpha = 1.0; // 透明度を元に戻す
 
 
1786
  }
1787
  }
1788
 
 
2143
  hasFace: !!poseData?.people?.[0]?.face_keypoints_2d,
2144
  hasBodies: !!poseData?.bodies,
2145
  hasHands: !!poseData?.hands,
2146
+ hasFaces: !!poseData?.faces,
2147
+ hasBackgroundImage: !!poseData?.background_image
2148
  });
2149
 
2150
+ // 🎨 背景画像が含まれている場合は設定
2151
+ if (poseData && poseData.background_image) {
2152
+ if (window.setBackgroundImage) {
2153
+ window.setBackgroundImage(poseData.background_image);
2154
+ }
2155
+ }
2156
+
2157
  // 💥 既存の手・顔データを保護!Python側データで上書きしない
2158
  const existingPoseData = window.poseEditorGlobals.poseData;
2159
  if (existingPoseData && existingPoseData.people && existingPoseData.people[0] &&
 
2326
 
2327
  // 🎨 背景画像設定機能(refs互換)
2328
  window.setBackgroundImage = function(imageData) {
2329
+ debugLog("🎨 setBackgroundImage called");
2330
+
2331
  if (!imageData) {
2332
  // 背景画像をクリア
2333
  window.poseEditorGlobals.backgroundImage = null;
 
2353
  }
2354
  };
2355
 
2356
+ img.onerror = function(e) {
2357
  debugLog("🚨 Failed to load background image");
2358
  };
2359
 
 
3464
  // スタイルをリセット
3465
  ctx.setLineDash([]); // 実線に戻す
3466
  ctx.globalAlpha = 1.0; // 不透明に戻す
3467
+ }
3468
+
3469
+ // 🎨 pose_editor.js initialization complete