aiqtech commited on
Commit
d9e7eed
·
verified ·
1 Parent(s): 4fea6f9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +277 -268
app.py CHANGED
@@ -4,7 +4,6 @@ import json
4
  import requests
5
  import os
6
  from typing import Dict, List, Tuple
7
- import base64
8
  from PIL import Image, ImageDraw
9
  import io
10
 
@@ -33,7 +32,7 @@ POSE_CONNECTIONS = [
33
 
34
  # Pose templates
35
  POSE_TEMPLATES = {
36
- "서있기 (Standing)": {
37
  "Nose": [256, 80], "Neck": [256, 120],
38
  "RShoulder": [220, 140], "RElbow": [200, 220], "RWrist": [190, 300],
39
  "LShoulder": [292, 140], "LElbow": [312, 220], "LWrist": [322, 300],
@@ -41,7 +40,7 @@ POSE_TEMPLATES = {
41
  "LHip": [272, 280], "LKnee": [277, 380], "LAnkle": [282, 480],
42
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
43
  },
44
- "앉기 (Sitting)": {
45
  "Nose": [256, 120], "Neck": [256, 160],
46
  "RShoulder": [220, 180], "RElbow": [200, 240], "RWrist": [190, 300],
47
  "LShoulder": [292, 180], "LElbow": [312, 240], "LWrist": [322, 300],
@@ -49,7 +48,7 @@ POSE_TEMPLATES = {
49
  "LHip": [272, 320], "LKnee": [232, 380], "LAnkle": [192, 400],
50
  "REye": [246, 110], "LEye": [266, 110], "REar": [236, 115], "LEar": [276, 115]
51
  },
52
- "달리기 (Running)": {
53
  "Nose": [256, 80], "Neck": [256, 120],
54
  "RShoulder": [220, 140], "RElbow": [180, 180], "RWrist": [150, 140],
55
  "LShoulder": [292, 140], "LElbow": [332, 200], "LWrist": [362, 260],
@@ -57,7 +56,7 @@ POSE_TEMPLATES = {
57
  "LHip": [272, 280], "LKnee": [252, 360], "LAnkle": [222, 440],
58
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
59
  },
60
- "요가 (Yoga)": {
61
  "Nose": [256, 100], "Neck": [256, 140],
62
  "RShoulder": [200, 160], "RElbow": [150, 120], "RWrist": [100, 100],
63
  "LShoulder": [312, 160], "LElbow": [362, 120], "LWrist": [412, 100],
@@ -65,20 +64,26 @@ POSE_TEMPLATES = {
65
  "LHip": [272, 300], "LKnee": [292, 400], "LAnkle": [312, 480],
66
  "REye": [246, 90], "LEye": [266, 90], "REar": [236, 95], "LEar": [276, 95]
67
  },
68
- "춤추기 (Dancing)": {
69
  "Nose": [256, 80], "Neck": [256, 120],
70
  "RShoulder": [220, 140], "RElbow": [180, 120], "RWrist": [140, 100],
71
  "LShoulder": [292, 140], "LElbow": [332, 160], "LWrist": [372, 140],
72
  "RHip": [240, 280], "RKnee": [260, 380], "RAnkle": [250, 480],
73
  "LHip": [272, 280], "LKnee": [252, 380], "LAnkle": [262, 480],
74
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
 
 
 
 
 
 
 
 
75
  }
76
  }
77
 
78
  def draw_pose(keypoints: Dict, width: int = 512, height: int = 512) -> Image.Image:
79
- """
80
- 키포인트를 기반으로 포즈 이미지 그리기
81
- """
82
  img = Image.new('RGB', (width, height), color='white')
83
  draw = ImageDraw.Draw(img)
84
 
@@ -96,13 +101,13 @@ def draw_pose(keypoints: Dict, width: int = 512, height: int = 512) -> Image.Ima
96
  x, y = point
97
  radius = 5
98
  draw.ellipse([x-radius, y-radius, x+radius, y+radius], fill='red', outline='darkred')
 
 
99
 
100
  return img
101
 
102
  def generate_pose_from_llm(prompt: str) -> Dict:
103
- """
104
- LLM을 사용하여 텍스트로부터 포즈 생성
105
- """
106
  system_prompt = """You are an expert in generating human pose keypoints.
107
  Given a description, generate 18 keypoint coordinates for OpenPose.
108
 
@@ -150,86 +155,72 @@ def generate_pose_from_llm(prompt: str) -> Dict:
150
  return get_template_from_prompt(prompt)
151
 
152
  def get_template_from_prompt(prompt: str) -> Dict:
153
- """
154
- 프롬프트에서 키워드를 찾아 적절한 템플릿 선택
155
- """
156
  prompt_lower = prompt.lower()
157
 
158
- if any(word in prompt_lower for word in ["앉", "sit", "chair", "의자"]):
159
- return POSE_TEMPLATES["앉기 (Sitting)"]
160
- elif any(word in prompt_lower for word in ["달리", "run", "jog", ""]):
161
- return POSE_TEMPLATES["달리기 (Running)"]
162
- elif any(word in prompt_lower for word in ["요가", "yoga", "명상", "meditation"]):
163
- return POSE_TEMPLATES["요가 (Yoga)"]
164
- elif any(word in prompt_lower for word in ["춤", "dance", "댄스"]):
165
- return POSE_TEMPLATES["춤추기 (Dancing)"]
 
 
166
  else:
167
- return POSE_TEMPLATES["서있기 (Standing)"]
168
 
169
  def refine_pose(current_keypoints: Dict, instruction: str) -> Dict:
170
- """
171
- 기존 포즈를 지시사항에 따라 수정
172
- """
173
- if FIREWORKS_API_KEY == "YOUR_API_KEY_HERE":
174
- # Simple rule-based refinement
175
- keypoints = current_keypoints.copy()
176
- instruction_lower = instruction.lower()
177
-
178
- if "팔" in instruction_lower or "arm" in instruction_lower:
179
- if "올리" in instruction_lower or "raise" in instruction_lower:
180
- # Raise arms
181
- if "RWrist" in keypoints:
182
- keypoints["RWrist"][1] -= 50
183
  if "LWrist" in keypoints:
184
  keypoints["LWrist"][1] -= 50
185
- elif "내리" in instruction_lower or "lower" in instruction_lower:
186
- # Lower arms
 
187
  if "RWrist" in keypoints:
188
- keypoints["RWrist"][1] += 50
189
- if "LWrist" in keypoints:
190
- keypoints["LWrist"][1] += 50
191
-
192
- return keypoints
193
-
194
- # Use LLM for refinement
195
- system_prompt = """Modify the given pose keypoints based on the instruction.
196
- Return the modified keypoints in the same JSON format."""
 
197
 
198
- headers = {
199
- "Accept": "application/json",
200
- "Content-Type": "application/json",
201
- "Authorization": f"Bearer {FIREWORKS_API_KEY}"
202
- }
203
 
204
- payload = {
205
- "model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
206
- "max_tokens": 1024,
207
- "temperature": 0.2,
208
- "messages": [
209
- {"role": "system", "content": system_prompt},
210
- {"role": "user", "content": f"Current keypoints: {json.dumps(current_keypoints)}\nInstruction: {instruction}"}
211
- ]
212
- }
213
 
214
- try:
215
- response = requests.post(FIREWORKS_API_URL, headers=headers, json=payload, timeout=30)
216
- if response.status_code == 200:
217
- data = response.json()
218
- content = data['choices'][0]['message']['content']
219
-
220
- import re
221
- json_match = re.search(r'\{.*\}', content, re.DOTALL)
222
- if json_match:
223
- return json.loads(json_match.group())
224
- except Exception as e:
225
- print(f"Refinement error: {e}")
226
 
227
- return current_keypoints
228
 
229
  def keypoints_to_openpose_format(keypoints: Dict) -> str:
230
- """
231
- 키포인트를 OpenPose JSON 형식으로 변환
232
- """
233
  candidate = []
234
  for i in range(18):
235
  part_name = None
@@ -249,213 +240,231 @@ def keypoints_to_openpose_format(keypoints: Dict) -> str:
249
 
250
  return json.dumps({"candidate": candidate, "subset": subset}, indent=2)
251
 
252
- # Gradio Interface
253
- with gr.Blocks(title="AI Pose Generator", theme=gr.themes.Soft()) as demo:
254
- current_keypoints = gr.State({})
255
-
256
- gr.Markdown("""
257
- # 🎨 AI 포즈 생성기 (Line Art Pose Generator)
258
- ### 텍스트 설명으로 정확한 포즈를 생성합니다
259
- """)
260
-
261
- with gr.Tabs():
262
- with gr.TabItem("🤖 AI 포즈 생성"):
263
- with gr.Row():
264
- with gr.Column(scale=1):
265
- # LLM 설정
266
- use_llm = gr.Checkbox(
267
- label="🚀 고급 AI 모델 사용 (Fireworks API)",
268
- value=False,
269
- info="체크하면 더 정확한 포즈 생성 (API 키 필요)"
270
- )
271
-
272
- api_status = gr.Markdown("⚠️ API 키 미설정 - 템플릿 모드")
273
-
274
- # 텍스트 입력
275
- prompt = gr.Textbox(
276
- label="포즈 설명",
277
- placeholder="예: 의자에 앉아 책을 읽는 사람",
278
- lines=3
279
- )
280
-
281
- # 예제
282
- gr.Examples(
283
- examples=[
284
- "팔을 높이 들고 승리의 포즈",
285
- "의자에 앉아 노트북 타이핑",
286
- "한쪽 다리로 서서 요가 포즈",
287
- "양손을 허리에 올린 자신감 있는 포즈",
288
- "달리기 자세",
289
- "무릎 꿇고 기도하는 자세"
290
- ],
291
- inputs=prompt
292
- )
293
-
294
- generate_btn = gr.Button("🎯 포즈 생성", variant="primary", size="lg")
295
-
296
- # 템플릿 선택
297
- with gr.Accordion("📚 템플릿 선택", open=False):
298
- template_select = gr.Dropdown(
299
- choices=list(POSE_TEMPLATES.keys()),
300
- label="포즈 템플릿",
301
- value="서있기 (Standing)"
302
  )
303
- use_template_btn = gr.Button("템플릿 적용")
304
-
305
- with gr.Column(scale=1):
306
- # 포즈 이미지 출력
307
- pose_image = gr.Image(
308
- label="생성된 포즈",
309
- type="pil",
310
- height=512
311
- )
312
-
313
- # JSON 출력
314
- with gr.Accordion("📋 OpenPose JSON", open=False):
315
- json_output = gr.Code(
316
- label="JSON 데이터",
317
- language="json",
318
- lines=10
319
  )
320
 
321
- download_btn = gr.Button("💾 JSON 다운로드", size="sm")
322
-
323
- with gr.TabItem("✏️ 포즈 수정"):
324
- with gr.Row():
325
- with gr.Column():
326
- refinement_instruction = gr.Textbox(
327
- label="수정 지시사항",
328
- placeholder="예: 왼쪽 팔을 높이 들어주세요",
329
- lines=2
330
- )
 
 
 
 
 
 
 
 
 
 
 
 
331
 
332
- refine_btn = gr.Button("✨ 포즈 수정", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
 
334
- # 미세 조정
335
- with gr.Accordion("🎛️ 수동 조정", open=False):
336
- selected_part = gr.Dropdown(
337
- choices=list(BODY_PARTS.keys()),
338
- label="조정할 부위",
339
- value="RWrist"
340
  )
341
- x_adjust = gr.Slider(-50, 50, 0, label="X 조정")
342
- y_adjust = gr.Slider(-50, 50, 0, label="Y 조정")
343
- apply_adjust_btn = gr.Button("적용")
 
 
 
 
 
 
 
 
 
 
 
 
 
344
 
345
- with gr.Column():
346
- refined_image = gr.Image(
347
- label="수정된 포즈",
348
- type="pil",
349
- height=512
350
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
 
352
- with gr.TabItem("ℹ️ 사용법"):
353
- gr.Markdown("""
354
- ## 사용 방법
 
 
 
 
 
 
 
 
 
 
355
 
356
- ### 1. AI 포즈 생성
357
- - **텍스트 설명**: 원하는 포즈를 자연어로 설명하세요
358
- - **고급 AI 모델**: Fireworks API 키가 있으면 더 정확한 생성 가능
359
- - **템플릿**: 빠른 시작을 위한 기본 포즈 제공
360
 
361
- ### 2. 포즈 수정
362
- - **자연어 수정**: "팔을 올려주세요" 같은 지시로 수정
363
- - **수동 조정**: 특정 관절을 직접 이동
 
 
 
 
 
 
 
 
 
364
 
365
- ### 3. 내보내기
366
- - OpenPose 형식 JSON으로 다운로드
367
- - ControlNet 등에서 사용 가능
 
 
 
 
 
 
368
 
369
- ### API 설정 (선택사항)
370
- ```bash
371
- export FIREWORKS_API_KEY="your_api_key"
372
- ```
373
 
374
- ### 특징
375
- - 🚀 GPU 불필요 - CPU만으로 작동
376
- - 🎨 깔끔한 라인 아트 스타일
377
- - 📊 OpenPose 호환 형식
378
- - 🔧 쉬운 수정 도구
379
- """)
380
-
381
- # Event handlers
382
- def check_api_status():
383
- if FIREWORKS_API_KEY != "YOUR_API_KEY_HERE":
384
- return "✅ API 키 설정됨 - 고급 AI 사용 가능"
385
- return "⚠️ API 키 미설정 - 템플릿 모드"
386
-
387
- def generate_pose(prompt_text, use_llm_flag):
388
- if not prompt_text and not use_llm_flag:
389
- # Use default template
390
- keypoints = POSE_TEMPLATES["서있기 (Standing)"]
391
- elif use_llm_flag and FIREWORKS_API_KEY != "YOUR_API_KEY_HERE":
392
- keypoints = generate_pose_from_llm(prompt_text)
393
- else:
394
- keypoints = get_template_from_prompt(prompt_text)
395
 
396
- # Draw pose
397
- pose_img = draw_pose(keypoints)
398
- json_str = keypoints_to_openpose_format(keypoints)
399
 
400
- return pose_img, json_str, keypoints
401
-
402
- def use_template(template_name):
403
- keypoints = POSE_TEMPLATES[template_name]
404
- pose_img = draw_pose(keypoints)
405
- json_str = keypoints_to_openpose_format(keypoints)
406
- return pose_img, json_str, keypoints
407
-
408
- def refine_existing_pose(instruction, keypoints_state):
409
- if not keypoints_state:
410
- return None
411
 
412
- refined_keypoints = refine_pose(keypoints_state, instruction)
413
- pose_img = draw_pose(refined_keypoints)
414
- return pose_img, refined_keypoints
415
-
416
- def manual_adjust(part, x_adj, y_adj, keypoints_state):
417
- if not keypoints_state or part not in keypoints_state:
418
- return None, keypoints_state
419
 
420
- adjusted_keypoints = keypoints_state.copy()
421
- adjusted_keypoints[part][0] += x_adj
422
- adjusted_keypoints[part][1] += y_adj
 
 
423
 
424
- pose_img = draw_pose(adjusted_keypoints)
425
- return pose_img, adjusted_keypoints
426
-
427
- # Connect events
428
- demo.load(check_api_status, outputs=api_status)
429
-
430
- generate_btn.click(
431
- generate_pose,
432
- inputs=[prompt, use_llm],
433
- outputs=[pose_image, json_output, current_keypoints]
434
- )
435
-
436
- use_template_btn.click(
437
- use_template,
438
- inputs=[template_select],
439
- outputs=[pose_image, json_output, current_keypoints]
440
- )
441
-
442
- refine_btn.click(
443
- refine_existing_pose,
444
- inputs=[refinement_instruction, current_keypoints],
445
- outputs=[refined_image, current_keypoints]
446
- )
447
-
448
- apply_adjust_btn.click(
449
- manual_adjust,
450
- inputs=[selected_part, x_adjust, y_adjust, current_keypoints],
451
- outputs=[refined_image, current_keypoints]
452
- )
453
 
454
- # Launch
455
  if __name__ == "__main__":
456
- demo.launch(
457
- server_name="0.0.0.0",
458
- server_port=7860,
459
- share=False,
460
- show_api=False
461
- )
 
4
  import requests
5
  import os
6
  from typing import Dict, List, Tuple
 
7
  from PIL import Image, ImageDraw
8
  import io
9
 
 
32
 
33
  # Pose templates
34
  POSE_TEMPLATES = {
35
+ "Standing": {
36
  "Nose": [256, 80], "Neck": [256, 120],
37
  "RShoulder": [220, 140], "RElbow": [200, 220], "RWrist": [190, 300],
38
  "LShoulder": [292, 140], "LElbow": [312, 220], "LWrist": [322, 300],
 
40
  "LHip": [272, 280], "LKnee": [277, 380], "LAnkle": [282, 480],
41
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
42
  },
43
+ "Sitting": {
44
  "Nose": [256, 120], "Neck": [256, 160],
45
  "RShoulder": [220, 180], "RElbow": [200, 240], "RWrist": [190, 300],
46
  "LShoulder": [292, 180], "LElbow": [312, 240], "LWrist": [322, 300],
 
48
  "LHip": [272, 320], "LKnee": [232, 380], "LAnkle": [192, 400],
49
  "REye": [246, 110], "LEye": [266, 110], "REar": [236, 115], "LEar": [276, 115]
50
  },
51
+ "Running": {
52
  "Nose": [256, 80], "Neck": [256, 120],
53
  "RShoulder": [220, 140], "RElbow": [180, 180], "RWrist": [150, 140],
54
  "LShoulder": [292, 140], "LElbow": [332, 200], "LWrist": [362, 260],
 
56
  "LHip": [272, 280], "LKnee": [252, 360], "LAnkle": [222, 440],
57
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
58
  },
59
+ "Yoga": {
60
  "Nose": [256, 100], "Neck": [256, 140],
61
  "RShoulder": [200, 160], "RElbow": [150, 120], "RWrist": [100, 100],
62
  "LShoulder": [312, 160], "LElbow": [362, 120], "LWrist": [412, 100],
 
64
  "LHip": [272, 300], "LKnee": [292, 400], "LAnkle": [312, 480],
65
  "REye": [246, 90], "LEye": [266, 90], "REar": [236, 95], "LEar": [276, 95]
66
  },
67
+ "Dancing": {
68
  "Nose": [256, 80], "Neck": [256, 120],
69
  "RShoulder": [220, 140], "RElbow": [180, 120], "RWrist": [140, 100],
70
  "LShoulder": [292, 140], "LElbow": [332, 160], "LWrist": [372, 140],
71
  "RHip": [240, 280], "RKnee": [260, 380], "RAnkle": [250, 480],
72
  "LHip": [272, 280], "LKnee": [252, 380], "LAnkle": [262, 480],
73
  "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
74
+ },
75
+ "Waving": {
76
+ "Nose": [256, 80], "Neck": [256, 120],
77
+ "RShoulder": [220, 140], "RElbow": [200, 100], "RWrist": [180, 60],
78
+ "LShoulder": [292, 140], "LElbow": [312, 220], "LWrist": [322, 300],
79
+ "RHip": [240, 280], "RKnee": [235, 380], "RAnkle": [230, 480],
80
+ "LHip": [272, 280], "LKnee": [277, 380], "LAnkle": [282, 480],
81
+ "REye": [246, 70], "LEye": [266, 70], "REar": [236, 75], "LEar": [276, 75]
82
  }
83
  }
84
 
85
  def draw_pose(keypoints: Dict, width: int = 512, height: int = 512) -> Image.Image:
86
+ """Draw pose skeleton on image"""
 
 
87
  img = Image.new('RGB', (width, height), color='white')
88
  draw = ImageDraw.Draw(img)
89
 
 
101
  x, y = point
102
  radius = 5
103
  draw.ellipse([x-radius, y-radius, x+radius, y+radius], fill='red', outline='darkred')
104
+ # Add label
105
+ draw.text((x+8, y-8), part[:3], fill='black')
106
 
107
  return img
108
 
109
  def generate_pose_from_llm(prompt: str) -> Dict:
110
+ """Generate pose using LLM"""
 
 
111
  system_prompt = """You are an expert in generating human pose keypoints.
112
  Given a description, generate 18 keypoint coordinates for OpenPose.
113
 
 
155
  return get_template_from_prompt(prompt)
156
 
157
  def get_template_from_prompt(prompt: str) -> Dict:
158
+ """Select appropriate template based on prompt"""
 
 
159
  prompt_lower = prompt.lower()
160
 
161
+ if any(word in prompt_lower for word in ["sit", "chair", "seated"]):
162
+ return POSE_TEMPLATES["Sitting"]
163
+ elif any(word in prompt_lower for word in ["run", "jog", "sprint"]):
164
+ return POSE_TEMPLATES["Running"]
165
+ elif any(word in prompt_lower for word in ["yoga", "meditation", "stretch"]):
166
+ return POSE_TEMPLATES["Yoga"]
167
+ elif any(word in prompt_lower for word in ["dance", "dancing"]):
168
+ return POSE_TEMPLATES["Dancing"]
169
+ elif any(word in prompt_lower for word in ["wave", "waving", "hello"]):
170
+ return POSE_TEMPLATES["Waving"]
171
  else:
172
+ return POSE_TEMPLATES["Standing"]
173
 
174
  def refine_pose(current_keypoints: Dict, instruction: str) -> Dict:
175
+ """Refine existing pose based on instruction"""
176
+ keypoints = current_keypoints.copy()
177
+ instruction_lower = instruction.lower()
178
+
179
+ # Simple rule-based refinement
180
+ if "raise" in instruction_lower or "lift" in instruction_lower:
181
+ if "arm" in instruction_lower or "hand" in instruction_lower:
182
+ if "left" in instruction_lower:
 
 
 
 
 
183
  if "LWrist" in keypoints:
184
  keypoints["LWrist"][1] -= 50
185
+ if "LElbow" in keypoints:
186
+ keypoints["LElbow"][1] -= 30
187
+ elif "right" in instruction_lower:
188
  if "RWrist" in keypoints:
189
+ keypoints["RWrist"][1] -= 50
190
+ if "RElbow" in keypoints:
191
+ keypoints["RElbow"][1] -= 30
192
+ else: # Both arms
193
+ for part in ["LWrist", "RWrist"]:
194
+ if part in keypoints:
195
+ keypoints[part][1] -= 50
196
+ for part in ["LElbow", "RElbow"]:
197
+ if part in keypoints:
198
+ keypoints[part][1] -= 30
199
 
200
+ elif "lower" in instruction_lower:
201
+ if "arm" in instruction_lower or "hand" in instruction_lower:
202
+ for part in ["LWrist", "RWrist"]:
203
+ if part in keypoints:
204
+ keypoints[part][1] += 50
205
 
206
+ elif "spread" in instruction_lower or "wide" in instruction_lower:
207
+ if "leg" in instruction_lower:
208
+ if "LAnkle" in keypoints:
209
+ keypoints["LAnkle"][0] -= 30
210
+ if "RAnkle" in keypoints:
211
+ keypoints["RAnkle"][0] += 30
 
 
 
212
 
213
+ elif "bend" in instruction_lower:
214
+ if "knee" in instruction_lower:
215
+ for part in ["LKnee", "RKnee"]:
216
+ if part in keypoints:
217
+ keypoints[part][1] += 20
218
+ keypoints[part][0] += 10 if "L" in part else -10
 
 
 
 
 
 
219
 
220
+ return keypoints
221
 
222
  def keypoints_to_openpose_format(keypoints: Dict) -> str:
223
+ """Convert keypoints to OpenPose JSON format"""
 
 
224
  candidate = []
225
  for i in range(18):
226
  part_name = None
 
240
 
241
  return json.dumps({"candidate": candidate, "subset": subset}, indent=2)
242
 
243
+ # Create Gradio interface
244
+ def create_demo():
245
+ with gr.Blocks(title="AI Pose Generator", theme=gr.themes.Soft()) as demo:
246
+ current_keypoints = gr.State({})
247
+
248
+ gr.Markdown("""
249
+ # 🎨 AI Line Art Pose Generator
250
+ ### Generate precise poses from text descriptions using AI
251
+ """)
252
+
253
+ with gr.Tabs():
254
+ with gr.TabItem("🤖 Generate Pose"):
255
+ with gr.Row():
256
+ with gr.Column(scale=1):
257
+ # Input section
258
+ use_llm = gr.Checkbox(
259
+ label="🚀 Use Advanced AI Model (Fireworks API)",
260
+ value=False,
261
+ info="Enable for more accurate pose generation (requires API key)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  )
263
+
264
+ api_status = gr.Markdown("⚠️ API key not set - Template mode active")
265
+
266
+ prompt = gr.Textbox(
267
+ label="Pose Description",
268
+ placeholder="e.g., A person sitting cross-legged reading a book",
269
+ lines=3
 
 
 
 
 
 
 
 
 
270
  )
271
 
272
+ gr.Examples(
273
+ examples=[
274
+ "A person standing with arms raised in victory",
275
+ "Someone sitting at a desk typing on a laptop",
276
+ "A dancer in arabesque position",
277
+ "A person doing yoga warrior pose",
278
+ "Someone waving hello",
279
+ "A person running with arms pumping"
280
+ ],
281
+ inputs=prompt
282
+ )
283
+
284
+ generate_btn = gr.Button("🎯 Generate Pose", variant="primary", size="lg")
285
+
286
+ # Template selection
287
+ with gr.Accordion("📚 Quick Templates", open=False):
288
+ template_select = gr.Dropdown(
289
+ choices=list(POSE_TEMPLATES.keys()),
290
+ label="Select Template",
291
+ value="Standing"
292
+ )
293
+ use_template_btn = gr.Button("Apply Template")
294
 
295
+ with gr.Column(scale=1):
296
+ # Output section
297
+ pose_image = gr.Image(
298
+ label="Generated Pose",
299
+ type="pil",
300
+ height=512
301
+ )
302
+
303
+ with gr.Accordion("📋 OpenPose JSON", open=False):
304
+ json_output = gr.Code(
305
+ label="JSON Data",
306
+ language="json",
307
+ lines=10
308
+ )
309
+
310
+ with gr.TabItem("✏️ Refine Pose"):
311
+ with gr.Row():
312
+ with gr.Column():
313
+ refinement_instruction = gr.Textbox(
314
+ label="Refinement Instructions",
315
+ placeholder="e.g., Raise the left arm higher",
316
+ lines=2
317
+ )
318
+
319
+ refine_btn = gr.Button("✨ Refine Pose", variant="secondary")
320
+
321
+ gr.Markdown("""
322
+ **Quick commands:**
323
+ - "Raise left/right arm"
324
+ - "Lower arms"
325
+ - "Spread legs wider"
326
+ - "Bend knees"
327
+ """)
328
+
329
+ # Manual adjustment
330
+ with gr.Accordion("🎛️ Manual Adjustment", open=False):
331
+ selected_part = gr.Dropdown(
332
+ choices=list(BODY_PARTS.keys()),
333
+ label="Select Body Part",
334
+ value="RWrist"
335
+ )
336
+ x_adjust = gr.Slider(-100, 100, 0, label="X Adjustment")
337
+ y_adjust = gr.Slider(-100, 100, 0, label="Y Adjustment")
338
+ apply_adjust_btn = gr.Button("Apply Adjustment")
339
 
340
+ with gr.Column():
341
+ refined_image = gr.Image(
342
+ label="Refined Pose",
343
+ type="pil",
344
+ height=512
 
345
  )
346
+
347
+ with gr.Accordion("📋 Updated JSON", open=False):
348
+ refined_json = gr.Code(
349
+ label="JSON Data",
350
+ language="json",
351
+ lines=10
352
+ )
353
+
354
+ with gr.TabItem("ℹ️ Help"):
355
+ gr.Markdown("""
356
+ ## How to Use
357
+
358
+ ### 1. Generate Pose
359
+ - Enter a natural language description of the pose
360
+ - Click "Generate Pose" to create the pose
361
+ - Or select a template for quick start
362
 
363
+ ### 2. Refine Pose (Optional)
364
+ - Use natural language commands to adjust the pose
365
+ - Or manually adjust individual body parts
366
+
367
+ ### 3. Export
368
+ - Copy the OpenPose JSON format for use in other applications
369
+ - Compatible with ControlNet and other pose-based tools
370
+
371
+ ### API Setup (Optional)
372
+ For better results, set up Fireworks API:
373
+ ```bash
374
+ export FIREWORKS_API_KEY="your_api_key"
375
+ ```
376
+
377
+ ### Features
378
+ - 🚀 No GPU required - runs on CPU
379
+ - 🎨 Clean line art style
380
+ - 📊 OpenPose compatible format
381
+ - 🔧 Easy refinement tools
382
+ - 💾 JSON export for integration
383
+ """)
384
 
385
+ # Event handlers
386
+ def check_api_status():
387
+ if FIREWORKS_API_KEY != "YOUR_API_KEY_HERE":
388
+ return "✅ API key configured - Advanced AI ready"
389
+ return "⚠️ API key not set - Template mode active"
390
+
391
+ def generate_pose(prompt_text, use_llm_flag):
392
+ if not prompt_text:
393
+ keypoints = POSE_TEMPLATES["Standing"]
394
+ elif use_llm_flag and FIREWORKS_API_KEY != "YOUR_API_KEY_HERE":
395
+ keypoints = generate_pose_from_llm(prompt_text)
396
+ else:
397
+ keypoints = get_template_from_prompt(prompt_text)
398
 
399
+ pose_img = draw_pose(keypoints)
400
+ json_str = keypoints_to_openpose_format(keypoints)
 
 
401
 
402
+ return pose_img, json_str, keypoints
403
+
404
+ def use_template(template_name):
405
+ keypoints = POSE_TEMPLATES[template_name]
406
+ pose_img = draw_pose(keypoints)
407
+ json_str = keypoints_to_openpose_format(keypoints)
408
+ return pose_img, json_str, keypoints
409
+
410
+ def refine_existing_pose(instruction, keypoints_state):
411
+ if not keypoints_state:
412
+ gr.Warning("Please generate a pose first")
413
+ return None, None, keypoints_state
414
 
415
+ refined_keypoints = refine_pose(keypoints_state, instruction)
416
+ pose_img = draw_pose(refined_keypoints)
417
+ json_str = keypoints_to_openpose_format(refined_keypoints)
418
+ return pose_img, json_str, refined_keypoints
419
+
420
+ def manual_adjust(part, x_adj, y_adj, keypoints_state):
421
+ if not keypoints_state:
422
+ gr.Warning("Please generate a pose first")
423
+ return None, None, keypoints_state
424
 
425
+ if part not in keypoints_state:
426
+ gr.Warning(f"Part {part} not found in current pose")
427
+ return None, None, keypoints_state
 
428
 
429
+ adjusted_keypoints = keypoints_state.copy()
430
+ adjusted_keypoints[part][0] += x_adj
431
+ adjusted_keypoints[part][1] += y_adj
432
+
433
+ pose_img = draw_pose(adjusted_keypoints)
434
+ json_str = keypoints_to_openpose_format(adjusted_keypoints)
435
+ return pose_img, json_str, adjusted_keypoints
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
 
437
+ # Connect events
438
+ demo.load(check_api_status, outputs=api_status)
 
439
 
440
+ generate_btn.click(
441
+ generate_pose,
442
+ inputs=[prompt, use_llm],
443
+ outputs=[pose_image, json_output, current_keypoints]
444
+ )
 
 
 
 
 
 
445
 
446
+ use_template_btn.click(
447
+ use_template,
448
+ inputs=[template_select],
449
+ outputs=[pose_image, json_output, current_keypoints]
450
+ )
 
 
451
 
452
+ refine_btn.click(
453
+ refine_existing_pose,
454
+ inputs=[refinement_instruction, current_keypoints],
455
+ outputs=[refined_image, refined_json, current_keypoints]
456
+ )
457
 
458
+ apply_adjust_btn.click(
459
+ manual_adjust,
460
+ inputs=[selected_part, x_adjust, y_adjust, current_keypoints],
461
+ outputs=[refined_image, refined_json, current_keypoints]
462
+ )
463
+
464
+ return demo
465
+
466
+ # Create and launch the app
467
+ app = create_demo()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
 
 
469
  if __name__ == "__main__":
470
+ app.launch()