Spaces:
Running on Zero
Running on Zero
| import spaces # MUST come before torch / any CUDA-touching import | |
| import torch | |
| import torchvision.transforms as T | |
| import gradio as gr | |
| import numpy as np | |
| import cv2 | |
| from PIL import Image | |
| from copy import deepcopy | |
| from detrpose import DETR | |
| # Load the model at module scope, .to("cuda") eagerly. | |
| model = DETR(model="detrpose_hgnetv2_l", device="cuda") | |
| transforms = T.Compose( | |
| [ | |
| T.Resize((640, 640)), | |
| T.ToTensor(), | |
| ] | |
| ) | |
| def predict(image: "Image.Image", threshold: float = 0.5) -> "Image.Image": | |
| """Detect multi-person poses in an image using DETRPose-L. | |
| Args: | |
| image: Input PIL image. | |
| threshold: Confidence threshold for keeping detected poses. | |
| """ | |
| if image is None: | |
| return None | |
| im_pil = image.convert("RGB") | |
| w, h = im_pil.size | |
| orig_size = torch.tensor([[w, h]]).to("cuda") | |
| im_data = transforms(im_pil).unsqueeze(0).to("cuda") | |
| with torch.no_grad(): | |
| outputs = model.model.model(im_data) | |
| results = model.model.postprocessor(outputs, orig_size) | |
| scores, labels, keypoints = results | |
| scores = scores[0].detach().cpu().numpy() | |
| keypoints = keypoints[0].detach().cpu().numpy() | |
| idx = scores > threshold | |
| valid_keypoints = keypoints[idx] | |
| im_cv2 = cv2.cvtColor(np.array(im_pil), cv2.COLOR_RGB2BGR) | |
| model.annotator.draw_on(im_cv2, valid_keypoints) | |
| result_rgb = cv2.cvtColor(im_cv2, cv2.COLOR_BGR2RGB) | |
| return Image.fromarray(result_rgb) | |
| CSS = """ | |
| #col-container { max-width: 1100px; margin: 0 auto; } | |
| .dark .gradio-container { color: var(--body-text-color); } | |
| """ | |
| with gr.Blocks() as demo: | |
| with gr.Column(elem_id="col-container"): | |
| gr.Markdown( | |
| "# DETRPose: Real-Time Multi-Person Pose Estimation\n" | |
| "Upload an image to detect human poses with skeleton keypoints overlaid. " | |
| "Powered by [DETRPose-L](https://huggingface.co/SebasJanampa/DETRPose_L_COCO) — " | |
| "the first real-time end-to-end transformer for multi-person pose estimation." | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| input_image = gr.Image( | |
| type="pil", label="Input Image", sources=["upload", "clipboard"] | |
| ) | |
| threshold_slider = gr.Slider( | |
| minimum=0.1, | |
| maximum=0.9, | |
| value=0.5, | |
| step=0.05, | |
| label="Confidence Threshold", | |
| ) | |
| run_btn = gr.Button("Run", variant="primary") | |
| with gr.Column(scale=1): | |
| output_image = gr.Image( | |
| type="pil", label="Pose Estimation Result" | |
| ) | |
| with gr.Accordion("Advanced settings", open=False): | |
| gr.Markdown( | |
| "**Confidence Threshold**: Lower this to detect more poses (may include " | |
| "false positives). Raise it for stricter, more confident detections." | |
| ) | |
| gr.Examples( | |
| examples=[ | |
| ["example1.jpg", 0.5], | |
| ["example2.jpg", 0.5], | |
| ], | |
| inputs=[input_image, threshold_slider], | |
| outputs=output_image, | |
| fn=predict, | |
| cache_examples=True, | |
| cache_mode="lazy", | |
| ) | |
| run_btn.click( | |
| fn=predict, | |
| inputs=[input_image, threshold_slider], | |
| outputs=output_image, | |
| api_name="predict", | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=CSS) |