Spaces:
Running on Zero
Running on Zero
Download app.py from jatinkarma/Humanly: direct link, hf CLI and curl.
- Browser
- Download file 7.19 kB
-
https://huggingface.co/spaces/jatinkarma/Humanly/resolve/main/app.py
- Command line
-
hf download hf://spaces/jatinkarma/Humanly/app.py
-
curl -L -o app.py https://huggingface.co/spaces/jatinkarma/Humanly/resolve/main/app.py
7.19 kB
| import os | |
| import sys | |
| import numpy as np | |
| from PIL import Image | |
| import keras | |
| import gradio as gr | |
| import spaces | |
| # 1. Dummy GPU function to satisfy HuggingFace ZeroGPU startup check | |
| def _dummy_startup(): | |
| pass | |
| # 2. Load trained MobileNetV2 model once at startup | |
| MODEL_PATH = os.getenv("MODEL_PATH", "models/human_face_v1.keras") | |
| model = keras.models.load_model(MODEL_PATH) | |
| CLASS_NAMES = ["human", "non_human"] | |
| THRESHOLD = 0.5 | |
| # 3. CPU prediction function (runs cleanly in ~30ms without ZeroGPU hang) | |
| def predict_face(img): | |
| if img is None: | |
| return """ | |
| <div style="background: #f8fafc; border: 2px dashed #cbd5e1; border-radius: 20px; padding: 40px 20px; text-align: center; color: #94a3b8; font-weight: 600;"> | |
| Upload an image on the left to see the AI decision! | |
| </div> | |
| """ | |
| # Preprocess image | |
| image = img.convert("RGB").resize((224, 224)) | |
| pixels = np.asarray(image, dtype=np.float32)[None, ...] | |
| p_non_human = float(model.predict(pixels, verbose=0)[0][0]) | |
| p_human = 1.0 - p_non_human | |
| if p_human >= THRESHOLD: | |
| pct = round(p_human * 100, 1) | |
| return f""" | |
| <div style="background: white; border: 2px solid #10b981; border-radius: 20px; padding: 24px; box-shadow: 0 10px 30px rgba(16,185,129,0.12);"> | |
| <div style="display: flex; align-items: center; gap: 14px; margin-bottom: 12px;"> | |
| <div style="width: 44px; height: 44px; border-radius: 50%; background: #10b981; color: white; display: flex; align-items: center; justify-content: center; font-size: 22px; font-weight: 900;">β</div> | |
| <div> | |
| <h2 style="font-size: 24px; font-weight: 800; color: #1e293b; margin: 0;">It's a Human!</h2> | |
| <p style="color: #64748b; font-weight: 600; margin: 0; font-size: 15px;">Confidence: <span style="color: #10b981; font-weight: 800;">{pct}%</span></p> | |
| </div> | |
| </div> | |
| <div style="background: #f1f5f9; border-radius: 9999px; height: 10px; overflow: hidden; margin: 16px 0;"> | |
| <div style="background: #10b981; height: 100%; width: {pct}%; border-radius: 9999px;"></div> | |
| </div> | |
| <div style="font-family: 'Caveat', cursive, sans-serif; font-size: 24px; color: #1e293b; text-align: right; margin-top: 10px;"> | |
| Looks like a human! π | |
| </div> | |
| </div> | |
| """ | |
| else: | |
| pct = round(p_non_human * 100, 1) | |
| return f""" | |
| <div style="background: white; border: 2px solid #ef4444; border-radius: 20px; padding: 24px; box-shadow: 0 10px 30px rgba(239,68,68,0.12);"> | |
| <div style="display: flex; align-items: center; gap: 14px; margin-bottom: 12px;"> | |
| <div style="width: 44px; height: 44px; border-radius: 50%; background: #ef4444; color: white; display: flex; align-items: center; justify-content: center; font-size: 22px; font-weight: 900;">β</div> | |
| <div> | |
| <h2 style="font-size: 24px; font-weight: 800; color: #1e293b; margin: 0;">Not a Human!</h2> | |
| <p style="color: #64748b; font-weight: 600; margin: 0; font-size: 15px;">Confidence: <span style="color: #ef4444; font-weight: 800;">{pct}%</span></p> | |
| </div> | |
| </div> | |
| <div style="background: #f1f5f9; border-radius: 9999px; height: 10px; overflow: hidden; margin: 16px 0;"> | |
| <div style="background: #ef4444; height: 100%; width: {pct}%; border-radius: 9999px;"></div> | |
| </div> | |
| <div style="font-family: 'Caveat', cursive, sans-serif; font-size: 22px; color: #1e293b; text-align: right; margin-top: 10px;"> | |
| Looks like AI or synthetic! π€ | |
| </div> | |
| </div> | |
| """ | |
| custom_css = """ | |
| @import url('https://fonts.googleapis.com/css2?family=Caveat:wght@600;700&family=Outfit:wght@600;700;800&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap'); | |
| .gradio-container { | |
| max-width: 1080px !important; | |
| margin: 0 auto !important; | |
| font-family: 'Plus Jakarta Sans', sans-serif !important; | |
| background: #f8faff !important; | |
| } | |
| """ | |
| with gr.Blocks(title="Humanly - Human or Not?") as demo: | |
| # 1. Top Navbar | |
| gr.HTML(""" | |
| <div style="display: flex; justify-content: space-between; align-items: center; padding: 16px 0; border-bottom: 1px solid #e2e8f0; margin-bottom: 24px;"> | |
| <div style="display: flex; align-items: center; gap: 8px; font-family: 'Outfit', sans-serif; font-size: 22px; font-weight: 800; color: #1e293b;"> | |
| <span style="color: #5c54e5;">β¦</span> VisionMind | |
| </div> | |
| <div style="display: flex; align-items: center; gap: 20px;"> | |
| <a href="https://github.com/jatin-karma/Humanly" target="_blank" style="text-decoration: none; color: #64748b; font-weight: 600; font-size: 14px;">GitHub</a> | |
| <div style="display: flex; align-items: center; gap: 6px; background: #dcfce7; color: #15803d; padding: 6px 14px; border-radius: 9999px; font-size: 13px; font-weight: 700;"> | |
| <span style="width: 8px; height: 8px; border-radius: 50%; background: #16a34a;"></span> Model Online | |
| </div> | |
| </div> | |
| </div> | |
| """) | |
| # 2. Hero Section with Mascot & Title | |
| gr.HTML(""" | |
| <div style="text-align: center; margin-bottom: 32px;"> | |
| <div style="font-size: 54px; margin-bottom: 8px;">π€</div> | |
| <h1 style="font-family: 'Outfit', sans-serif; font-size: 42px; font-weight: 800; color: #4338ca; margin: 0 0 6px 0;"> | |
| Human <span style="color: #6366f1;">or</span> Not? | |
| </h1> | |
| <p style="color: #64748b; font-size: 17px; font-weight: 500; margin: 0;">Upload an image and let AI decide!</p> | |
| </div> | |
| """) | |
| # 3. Main Two-Column Card Layout | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| image_input = gr.Image( | |
| type="pil", | |
| label="Upload Face Image (JPG, PNG, JPEG)", | |
| sources=["upload", "clipboard"], | |
| ) | |
| classify_btn = gr.Button("π Classify Face", variant="primary", size="lg") | |
| with gr.Column(scale=1): | |
| result_output = gr.HTML( | |
| value=""" | |
| <div style="background: #f8fafc; border: 2px dashed #cbd5e1; border-radius: 20px; padding: 50px 20px; text-align: center; color: #94a3b8; font-weight: 600; min-height: 180px; display: flex; align-items: center; justify-content: center;"> | |
| Upload an image on the left and click Classify! | |
| </div> | |
| """ | |
| ) | |
| # 4. Triggers: Auto on upload + manual on button click | |
| image_input.change(fn=predict_face, inputs=image_input, outputs=result_output) | |
| classify_btn.click(fn=predict_face, inputs=image_input, outputs=result_output) | |
| # 5. Footer | |
| gr.HTML(""" | |
| <div style="text-align: center; padding: 32px 0 16px 0; color: #94a3b8; font-size: 14px; font-weight: 500; border-top: 1px solid #e2e8f0; margin-top: 40px;"> | |
| Built by <strong>Jatin Karma β€οΈ</strong> | |
| </div> | |
| """) | |
| if __name__ == "__main__": | |
| demo.launch(css=custom_css, ssr_mode=False) |