Update app.py
Browse files
app.py
CHANGED
|
@@ -34,31 +34,27 @@ def check_backend_connection():
|
|
| 34 |
"""Ping the HF Space and cache the client object."""
|
| 35 |
try:
|
| 36 |
test_client = Client("SnapwearAI/SAKS-backend", hf_token=HF_TOKEN)
|
| 37 |
-
backend_status.update(
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
}
|
| 44 |
-
)
|
| 45 |
logger.info("β
Backend connection established")
|
| 46 |
return True, "π’ Model is ready"
|
| 47 |
except Exception as e:
|
| 48 |
-
backend_status.update(
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
}
|
| 55 |
-
)
|
| 56 |
err = str(e).lower()
|
| 57 |
if "timeout" in err or "read operation timed out" in err:
|
| 58 |
return False, "π‘ Model is starting up. Please wait 3β4β―min."
|
| 59 |
return False, f"π΄ Backend error: {e}"
|
| 60 |
|
| 61 |
-
#
|
| 62 |
check_backend_connection()
|
| 63 |
|
| 64 |
# βββββββββ Helpers βββββββββ
|
|
@@ -83,6 +79,7 @@ def base64_to_image(b64: str) -> Image.Image | None:
|
|
| 83 |
# βββββββββ UI β Backend bridge βββββββββ
|
| 84 |
def call_backend_with_retry(input_image: Image.Image, category: str, gender: str, *, max_retries: int = MAX_RETRIES):
|
| 85 |
"""Singleβshot call (no more than `max_retries` times)."""
|
|
|
|
| 86 |
if input_image is None:
|
| 87 |
return None, None, "β Please upload an image."
|
| 88 |
|
|
@@ -129,55 +126,11 @@ def call_backend_with_retry(input_image: Image.Image, category: str, gender: str
|
|
| 129 |
|
| 130 |
return None, None, "β Unknown error"
|
| 131 |
|
| 132 |
-
# βββββββββ CSS
|
| 133 |
css = """
|
| 134 |
body, .gradio-container {
|
| 135 |
font-family: 'Inter', sans-serif;
|
| 136 |
-
background-color: #f9fafb;
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
h1, h3 {
|
| 140 |
-
font-weight: 700;
|
| 141 |
-
color: #333333;
|
| 142 |
-
}
|
| 143 |
-
|
| 144 |
-
h1 {
|
| 145 |
-
font-size: 2.2em;
|
| 146 |
-
margin-bottom: 0.3em;
|
| 147 |
-
}
|
| 148 |
-
|
| 149 |
-
h3 {
|
| 150 |
-
font-size: 1.3em;
|
| 151 |
-
margin-bottom: 0.2em;
|
| 152 |
-
}
|
| 153 |
-
|
| 154 |
-
p {
|
| 155 |
-
color: #555555;
|
| 156 |
-
}
|
| 157 |
-
|
| 158 |
-
.status-banner {
|
| 159 |
-
padding: 10px;
|
| 160 |
-
border-radius: 8px;
|
| 161 |
-
font-weight: 500;
|
| 162 |
-
text-align: center;
|
| 163 |
-
margin-bottom: 15px;
|
| 164 |
-
}
|
| 165 |
-
|
| 166 |
-
.status-ready {
|
| 167 |
-
background-color: #d1fae5;
|
| 168 |
-
color: #065f46;
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
.status-starting {
|
| 172 |
-
background-color: #fef9c3;
|
| 173 |
-
color: #92400e;
|
| 174 |
}
|
| 175 |
-
|
| 176 |
-
.status-error {
|
| 177 |
-
background-color: #fee2e2;
|
| 178 |
-
color: #991b1b;
|
| 179 |
-
}
|
| 180 |
-
|
| 181 |
#button {
|
| 182 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 183 |
color: #ffffff;
|
|
@@ -186,48 +139,22 @@ p {
|
|
| 186 |
border-radius: 12px;
|
| 187 |
padding: 12px 24px;
|
| 188 |
transition: all 0.3s ease;
|
| 189 |
-
font-size: 1em;
|
| 190 |
}
|
| 191 |
-
|
| 192 |
#button:hover {
|
| 193 |
transform: translateY(-2px);
|
| 194 |
box-shadow: 0 8px 25px rgba(102,126,234,0.3);
|
| 195 |
}
|
| 196 |
-
|
| 197 |
-
.gr-examples {
|
| 198 |
-
background: #f3f4f6;
|
| 199 |
-
border-radius: 8px;
|
| 200 |
-
padding: 10px;
|
| 201 |
-
margin-top: 10px;
|
| 202 |
-
}
|
| 203 |
-
|
| 204 |
-
.gr-examples p {
|
| 205 |
-
font-weight: 600;
|
| 206 |
-
color: #444444;
|
| 207 |
-
margin-bottom: 8px;
|
| 208 |
-
}
|
| 209 |
-
|
| 210 |
-
.markdown-output h3 {
|
| 211 |
-
color: #4b5563;
|
| 212 |
-
font-size: 1.1em;
|
| 213 |
-
margin-bottom: 6px;
|
| 214 |
-
}
|
| 215 |
-
|
| 216 |
-
.markdown-output p {
|
| 217 |
-
color: #6b7280;
|
| 218 |
-
font-size: 0.95em;
|
| 219 |
-
}
|
| 220 |
"""
|
| 221 |
|
| 222 |
# βββββββββ Gradio Blocks βββββββββ
|
| 223 |
with gr.Blocks(css=css, title="Jewellery Photography Preview") as demo:
|
| 224 |
# Hero
|
| 225 |
gr.HTML("""
|
| 226 |
-
<div style="
|
| 227 |
-
<h1 style="
|
|
|
|
| 228 |
</div>
|
| 229 |
-
|
| 230 |
-
""")
|
| 231 |
|
| 232 |
# Status banner
|
| 233 |
status_html = gr.HTML()
|
|
@@ -238,39 +165,53 @@ with gr.Blocks(css=css, title="Jewellery Photography Preview") as demo:
|
|
| 238 |
return f'<div class="status-banner {cls}">{msg}</div>'
|
| 239 |
|
| 240 |
status_html.value = _update_status()
|
| 241 |
-
gr.Button("π Check
|
| 242 |
|
| 243 |
# 2βcolumn layout
|
| 244 |
with gr.Row():
|
| 245 |
# left inputs
|
| 246 |
with gr.Column(scale=0.4):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
with gr.Tabs():
|
| 248 |
-
with gr.TabItem("
|
| 249 |
category = gr.Dropdown(label="Jewellery category", choices=["Rings", "Bracelets", "Watches", "Earrings"], value="Bracelets")
|
| 250 |
gender = gr.Dropdown(label="Model gender", choices=["male", "female"], value="female")
|
| 251 |
input_img = gr.Image(label="Upload image", type="pil", height=400)
|
| 252 |
|
|
|
|
| 253 |
gr.Examples(
|
| 254 |
-
label="Example Images",
|
| 255 |
-
inputs=input_img,
|
| 256 |
examples=[
|
| 257 |
-
["examples/
|
| 258 |
-
["examples/
|
| 259 |
-
["examples/
|
| 260 |
-
|
|
|
|
|
|
|
|
|
|
| 261 |
)
|
| 262 |
|
| 263 |
# right outputs
|
| 264 |
with gr.Column():
|
| 265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
with gr.Row():
|
| 267 |
run_btn = gr.Button("π―Β Generate", elem_id="button", variant="primary")
|
| 268 |
with gr.Row():
|
| 269 |
with gr.Column():
|
| 270 |
-
info1 = gr.Markdown(value="### Detection overlay"
|
| 271 |
out_overlay = gr.Image(height=400)
|
| 272 |
with gr.Column():
|
| 273 |
-
info2 = gr.Markdown("### Final result"
|
| 274 |
out_bg = gr.Image(height=400)
|
| 275 |
out_status = gr.Text(label="Status", interactive=False)
|
| 276 |
|
|
|
|
| 34 |
"""Ping the HF Space and cache the client object."""
|
| 35 |
try:
|
| 36 |
test_client = Client("SnapwearAI/SAKS-backend", hf_token=HF_TOKEN)
|
| 37 |
+
backend_status.update({
|
| 38 |
+
"client": test_client,
|
| 39 |
+
"connected": True,
|
| 40 |
+
"error_message": "",
|
| 41 |
+
"last_check": time.time(),
|
| 42 |
+
})
|
|
|
|
|
|
|
| 43 |
logger.info("β
Backend connection established")
|
| 44 |
return True, "π’ Model is ready"
|
| 45 |
except Exception as e:
|
| 46 |
+
backend_status.update({
|
| 47 |
+
"client": None,
|
| 48 |
+
"connected": False,
|
| 49 |
+
"last_check": time.time(),
|
| 50 |
+
"error_message": str(e),
|
| 51 |
+
})
|
|
|
|
|
|
|
| 52 |
err = str(e).lower()
|
| 53 |
if "timeout" in err or "read operation timed out" in err:
|
| 54 |
return False, "π‘ Model is starting up. Please wait 3β4β―min."
|
| 55 |
return False, f"π΄ Backend error: {e}"
|
| 56 |
|
| 57 |
+
# initial probe
|
| 58 |
check_backend_connection()
|
| 59 |
|
| 60 |
# βββββββββ Helpers βββββββββ
|
|
|
|
| 79 |
# βββββββββ UI β Backend bridge βββββββββ
|
| 80 |
def call_backend_with_retry(input_image: Image.Image, category: str, gender: str, *, max_retries: int = MAX_RETRIES):
|
| 81 |
"""Singleβshot call (no more than `max_retries` times)."""
|
| 82 |
+
|
| 83 |
if input_image is None:
|
| 84 |
return None, None, "β Please upload an image."
|
| 85 |
|
|
|
|
| 126 |
|
| 127 |
return None, None, "β Unknown error"
|
| 128 |
|
| 129 |
+
# βββββββββ CSS βββββββββ
|
| 130 |
css = """
|
| 131 |
body, .gradio-container {
|
| 132 |
font-family: 'Inter', sans-serif;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
#button {
|
| 135 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 136 |
color: #ffffff;
|
|
|
|
| 139 |
border-radius: 12px;
|
| 140 |
padding: 12px 24px;
|
| 141 |
transition: all 0.3s ease;
|
|
|
|
| 142 |
}
|
|
|
|
| 143 |
#button:hover {
|
| 144 |
transform: translateY(-2px);
|
| 145 |
box-shadow: 0 8px 25px rgba(102,126,234,0.3);
|
| 146 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
"""
|
| 148 |
|
| 149 |
# βββββββββ Gradio Blocks βββββββββ
|
| 150 |
with gr.Blocks(css=css, title="Jewellery Photography Preview") as demo:
|
| 151 |
# Hero
|
| 152 |
gr.HTML("""
|
| 153 |
+
<div style="text-align: center; margin-bottom: 20px;">
|
| 154 |
+
<h1 style="font-size: 2.5em;">π¨ Raresence: AI-Powered Jewellery Photo Preview</h1>
|
| 155 |
+
<p style="color: #666;">Upload a jewellery image, select model, and get professional photos instantly</p>
|
| 156 |
</div>
|
| 157 |
+
""")
|
|
|
|
| 158 |
|
| 159 |
# Status banner
|
| 160 |
status_html = gr.HTML()
|
|
|
|
| 165 |
return f'<div class="status-banner {cls}">{msg}</div>'
|
| 166 |
|
| 167 |
status_html.value = _update_status()
|
| 168 |
+
gr.Button("π Check Status").click(fn=_update_status, outputs=status_html)
|
| 169 |
|
| 170 |
# 2βcolumn layout
|
| 171 |
with gr.Row():
|
| 172 |
# left inputs
|
| 173 |
with gr.Column(scale=0.4):
|
| 174 |
+
gr.HTML("""
|
| 175 |
+
<div style="text-align:center; margin-bottom: 10px;">
|
| 176 |
+
<h3>πΌοΈ Upload Jewellery Image</h3>
|
| 177 |
+
<p style="color: #666; font-size: 14px;">Select a clear jewellery image for best results</p>
|
| 178 |
+
</div>
|
| 179 |
+
""")
|
| 180 |
with gr.Tabs():
|
| 181 |
+
with gr.TabItem("Setting"):
|
| 182 |
category = gr.Dropdown(label="Jewellery category", choices=["Rings", "Bracelets", "Watches", "Earrings"], value="Bracelets")
|
| 183 |
gender = gr.Dropdown(label="Model gender", choices=["male", "female"], value="female")
|
| 184 |
input_img = gr.Image(label="Upload image", type="pil", height=400)
|
| 185 |
|
| 186 |
+
# Example images
|
| 187 |
gr.Examples(
|
|
|
|
|
|
|
| 188 |
examples=[
|
| 189 |
+
["examples/ring1.jpg"],
|
| 190 |
+
["examples/bracelet1.jpg"],
|
| 191 |
+
["examples/watch1.jpg"],
|
| 192 |
+
["examples/earring1.jpg"],
|
| 193 |
+
],
|
| 194 |
+
inputs=[input_img],
|
| 195 |
+
label="Example Images",
|
| 196 |
)
|
| 197 |
|
| 198 |
# right outputs
|
| 199 |
with gr.Column():
|
| 200 |
+
gr.HTML("""
|
| 201 |
+
<div style="text-align:center; margin-bottom: 10px;">
|
| 202 |
+
<h3>π¨ AI Generated Results</h3>
|
| 203 |
+
<p style="color: #666; font-size: 14px;">Preview overlay detection and final professional background</p>
|
| 204 |
+
</div>
|
| 205 |
+
""")
|
| 206 |
+
info = gr.Markdown(value="...")
|
| 207 |
with gr.Row():
|
| 208 |
run_btn = gr.Button("π―Β Generate", elem_id="button", variant="primary")
|
| 209 |
with gr.Row():
|
| 210 |
with gr.Column():
|
| 211 |
+
info1 = gr.Markdown(value="### Detection overlay")
|
| 212 |
out_overlay = gr.Image(height=400)
|
| 213 |
with gr.Column():
|
| 214 |
+
info2 = gr.Markdown(value="### Final result")
|
| 215 |
out_bg = gr.Image(height=400)
|
| 216 |
out_status = gr.Text(label="Status", interactive=False)
|
| 217 |
|