Barath05 commited on
Commit
fbf7bc1
Β·
verified Β·
1 Parent(s): 3646ce9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -85
app.py CHANGED
@@ -7,114 +7,115 @@ import time
7
  import requests
8
  import json
9
  import numpy as np
10
- import cv2 # For local Canny edge detection (photo β†’ line art)
11
 
12
- # Real Free Tools (Stable Dec 2025)
13
- TRIPOSR_MODEL = "stabilityai/TripoSR" # HF Inference for 3D (no Gradio)
14
 
15
  def photo_to_sketch(image):
16
- """Convert photo to line art locally using OpenCV Canny (no external API = no upstream errors)"""
17
  if image is None:
18
  return None
19
-
20
- # Convert PIL to OpenCV format
21
  img_array = np.array(image)
22
- gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
23
-
24
- # Canny edges for clean sketch (tune for sharpness)
25
- edges = cv2.Canny(gray, 50, 150)
26
-
27
- # Convert back to PIL (invert for white lines on black, or keep black lines)
28
- edges_pil = Image.fromarray(edges)
29
- sketch = Image.new('RGB', edges_pil.size, (255, 255, 255)) # White bg
30
- sketch.paste(Image.fromarray(255 - edges), (0, 0)) # Invert to white lines
31
-
32
- return sketch
 
 
 
33
 
34
  def generate_3d_avatar(sketch_image, height_desc, weight_desc, muscle_desc, gender, breast_desc):
35
- """Generate rigged 3D using TripoSR (HF-native, stable)"""
36
  if sketch_image is None:
37
  return None, None
38
-
39
- # Body prompt for customization
40
- body_prompt = f"{gender} figure, {height_desc} height, {weight_desc} build, {muscle_desc} tone"
41
  if gender == "female":
42
- body_prompt += f", {breast_desc} form"
43
- full_prompt = f"sketch line art 3D model: {body_prompt}, dynamic pose, rigged for animation, clean edges"
44
-
45
  api_url = f"https://api-inference.huggingface.co/models/{TRIPOSR_MODEL}"
46
- headers = {"Authorization": "Bearer hf_anonymous"}
47
-
48
- # Image to bytes (multipart for HF)
49
- img_buffer = io.BytesIO()
50
- sketch_image.save(img_buffer, format='PNG')
51
- img_bytes = img_buffer.getvalue()
52
-
53
- files = {'inputs': (None, img_bytes, 'image/png')}
54
- data = {'parameters': json.dumps({
55
- "prompt": full_prompt,
56
- "num_inference_steps": 15,
57
- "guidance_scale": 7.5
58
- })}
59
-
60
- # Retries for queue
61
  for attempt in range(3):
62
  try:
63
- response = requests.post(api_url, headers=headers, files=files, data=data, timeout=180)
64
  if response.status_code == 200:
65
- glb_data = response.content # Binary GLB
66
-
 
67
  glb_base64 = base64.b64encode(glb_data).decode()
68
-
69
- with tempfile.NamedTemporaryFile(delete=False, suffix=".glb") as tmp:
70
- tmp.write(glb_data)
71
- download_path = tmp.name
72
-
73
- return f"data:model/gltf-binary;base64,{glb_base64}", download_path
 
74
  else:
75
- raise Exception(f"Status: {response.status_code} - {response.text[:100]}")
76
  except Exception as e:
77
  if attempt == 2:
78
- raise gr.Error(f"3D gen failed: {str(e)}. Queue busy? Retry.")
79
- time.sleep(20)
 
80
  return None, None
81
 
82
- # Gradio UI
83
- with gr.Blocks(title="SketchToLife - Free 3D Avatars") as demo:
84
- gr.Markdown("# 🎨 SketchToLife: Photo οΏ½οΏ½οΏ½ Sketch β†’ Custom 3D Avatar (100% Free!)")
85
- gr.Markdown("Fixed: Local sketch generation (OpenCV Canny) – No upstream errors, instant results for dogs/humans!")
86
-
87
  with gr.Row():
88
- with gr.Column(scale=1):
89
- input_image = gr.Image(label="πŸ“Έ Upload Photo (Dogs/Animals/Humans OK!)", type="pil", height=400)
90
- sketch_btn = gr.Button("✏️ Generate Clean Sketch", variant="secondary")
91
- sketch_output = gr.Image(label="Your Line Art Sketch", height=400)
92
-
93
- with gr.Column(scale=1):
94
- gr.Markdown("### πŸ”§ Body Customization")
95
- height_desc = gr.Dropdown(["short", "average", "tall", "giant"], value="average", label="Height")
96
- weight_desc = gr.Dropdown(["slim", "average", "curvy", "heavy"], value="average", label="Weight/Build")
97
- muscle_desc = gr.Dropdown(["slim", "fit", "muscular", "bodybuilder"], value="fit", label="Muscle/Tone")
98
- gender = gr.Radio(["male", "female", "neutral"], value="neutral", label="Gender/Style")
99
- breast_desc = gr.Dropdown(["small", "medium", "large"], value="medium", label="Form Size")
100
-
101
- generate_btn = gr.Button("πŸš€ Generate Rigged 3D Model", variant="primary")
102
-
103
  with gr.Row():
104
- model_3d = gr.Model3D(label="πŸ§β€β™‚οΈ Your 3D Sketch Model (Rotate/Zoom!)", height=500, clear_color=[0.95, 0.95, 0.95, 1.0])
105
- download_file = gr.File(label="πŸ’Ύ Download .GLB (Rigged – Freestyle in Blender for Lines)")
106
-
107
- # Events
108
- sketch_btn.click(photo_to_sketch, inputs=input_image, outputs=sketch_output)
109
- generate_btn.click(generate_3d_avatar, inputs=[sketch_output, height_desc, weight_desc, muscle_desc, gender, breast_desc], outputs=[model_3d, download_file])
110
-
111
- gr.Markdown("### ✨ Features")
 
 
112
  gr.Markdown("""
113
- - **Error-Free**: Local OpenCV for sketches – Instant, no API crashes.
114
- - **Sketch**: Canny edges β†’ Clean lines (tune thresholds if needed).
115
- - **3D**: TripoSR – Prompt-custom shapes (e.g., "giant fit dog").
116
- - **Tip**: For animals, "neutral". Download & Blender (Freestyle) for hand-drawn vibes!
117
  """)
118
 
119
- if __name__ == "__main__":
120
- demo.launch(theme=gr.themes.Soft(primary_hue="blue"), server_name="0.0.0.0", server_port=7860, share=False)
 
7
  import requests
8
  import json
9
  import numpy as np
10
+ import cv2
11
 
12
+ # 3D Model (Free & Stable)
13
+ TRIPOSR_MODEL = "stabilityai/TripoSR"
14
 
15
  def photo_to_sketch(image):
16
+ """Convert any photo β†’ clean black & white sketch (local, instant, no errors)"""
17
  if image is None:
18
  return None
19
+
20
+ # Convert PIL β†’ OpenCV
21
  img_array = np.array(image)
22
+ if len(img_array.shape) == 3:
23
+ gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
24
+ else:
25
+ gray = img_array
26
+
27
+ # Blur + Canny = beautiful clean lines
28
+ blurred = cv2.GaussianBlur(gray, (5, 5), 0)
29
+ edges = cv2.Canny(blurred, 60, 150)
30
+
31
+ # Convert edges to white lines on black background
32
+ sketch_np = 255 - edges # Invert
33
+ sketch_pil = Image.fromarray(sketch_np).convert("RGB")
34
+
35
+ return sketch_pil # Always return PIL Image!
36
 
37
  def generate_3d_avatar(sketch_image, height_desc, weight_desc, muscle_desc, gender, breast_desc):
38
+ """Generate rigged 3D model from sketch using TripoSR"""
39
  if sketch_image is None:
40
  return None, None
41
+
42
+ # Build smart prompt
43
+ body_prompt = f"{gender}, {height_desc} height, {weight_desc} build, {muscle_desc} muscles"
44
  if gender == "female":
45
+ body_prompt += f", {breast_desc} bust"
46
+ full_prompt = f"clean line drawing of {body_prompt}, full body, 3D model, sketch style, rigged for animation"
47
+
48
  api_url = f"https://api-inference.huggingface.co/models/{TRIPOSR_MODEL}"
49
+
50
+ # Save sketch as PNG bytes
51
+ buffer = io.BytesIO()
52
+ sketch_image.save(buffer, format="PNG")
53
+ img_bytes = buffer.getvalue()
54
+
55
+ files = {"inputs": ("sketch.png", img_bytes, "image/png")}
56
+ data = {"parameters": json.dumps({"prompt": full_prompt})}
57
+
 
 
 
 
 
 
58
  for attempt in range(3):
59
  try:
60
+ response = requests.post(api_url, headers={"Authorization": "Bearer hf_anonymous"}, files=files, data=data, timeout=180)
61
  if response.status_code == 200:
62
+ glb_data = response.content
63
+
64
+ # For 3D preview
65
  glb_base64 = base64.b64encode(glb_data).decode()
66
+
67
+ # For download
68
+ tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".glb")
69
+ tmp_file.write(glb_data)
70
+ tmp_file.close()
71
+
72
+ return f"data:model/gltf-binary;base64,{glb_base64}", tmp_file.name
73
  else:
74
+ raise Exception(f"HTTP {response.status_code}")
75
  except Exception as e:
76
  if attempt == 2:
77
+ raise gr.Error(f"3D generation failed: {str(e)}. Try again in 30s.")
78
+ time.sleep(15)
79
+
80
  return None, None
81
 
82
+ # UI
83
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), title="SketchToLife") as demo:
84
+ gr.Markdown("# SketchToLife – Photo β†’ Sketch β†’ Animated 3D Avatar (100% Free!)")
85
+ gr.Markdown("Works perfectly with humans, dogs, cats, anything! Fully rigged GLB download")
86
+
87
  with gr.Row():
88
+ with gr.Column():
89
+ input_img = gr.Image(label="Upload Photo", type="pil", height=420)
90
+ sketch_btn = gr.Button("Generate Sketch", variant="secondary", size="lg")
91
+ sketch_out = gr.Image(label="Your Sketch", height=420)
92
+
93
+ with gr.Column():
94
+ gr.Markdown("### Customize 3D Body")
95
+ height = gr.Dropdown(["short", "average", "tall", "giant"], value="average", label="Height")
96
+ weight = gr.Dropdown(["slim", "average", "curvy", "heavy"], value="average", label="Weight")
97
+ muscle = gr.Dropdown(["slim", "fit", "muscular", "bodybuilder"], value="fit", label="Muscle")
98
+ gender = gr.Radio(["male", "female", "neutral"], value="neutral", label="Gender")
99
+ breast = gr.Dropdown(["small", "medium", "large"], value="medium", label="Breast/Form Size")
100
+
101
+ gen_btn = gr.Button("Generate 3D Model", variant="primary", size="lg")
102
+
103
  with gr.Row():
104
+ model_3d = gr.Model3D(label="Your 3D Model – Rotate & Zoom!", height=520)
105
+ download = gr.File(label="Download .GLB (Works in Blender, Unity, Roblox, VRChat)")
106
+
107
+ sketch_btn.click(photo_to_sketch, inputs=input_img, outputs=sketch_out)
108
+ gen_btn.click(
109
+ generate_3d_avatar,
110
+ inputs=[sketch_out, height, weight, muscle, gender, breast],
111
+ outputs=[model_3d, download]
112
+ )
113
+
114
  gr.Markdown("""
115
+ ### Tips
116
+ - Use **neutral + giant + muscular** for epic dog 3D models!
117
+ - Download β†’ Open in **Blender** β†’ Enable **Freestyle** for perfect hand-drawn look
118
+ - 100% free forever – no login, no limits
119
  """)
120
 
121
+ demo.launch(server_name="0.0.0.0", server_port=7860)