Spaces:
Sleeping
Sleeping
| """ | |
| MedOS Medicine Scanner β HuggingFace Space (Gradio SDK) | |
| Scan medicine labels with AI (Qwen2.5-VL) and return structured JSON. | |
| Uses HF Spaces native Gradio SDK β no Docker needed. | |
| """ | |
| import json | |
| import logging | |
| import os | |
| import io | |
| import time | |
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image | |
| from scanner import scan_medicine | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") | |
| logger = logging.getLogger(__name__) | |
| def scan_image_ui(image_dict, hf_token: str) -> tuple[str, str]: | |
| """Gradio interface handler.""" | |
| if image_dict is None: | |
| return "Please upload or capture a medicine image.", "{}" | |
| start = time.time() | |
| # gr.Image returns filepath string or numpy array depending on type | |
| if isinstance(image_dict, str): | |
| pil_image = Image.open(image_dict) | |
| elif isinstance(image_dict, np.ndarray): | |
| pil_image = Image.fromarray(image_dict) | |
| else: | |
| pil_image = Image.fromarray(np.asarray(image_dict)) | |
| result = scan_medicine(pil_image, hf_token=(hf_token or "").strip()) | |
| elapsed = time.time() - start | |
| if not result["success"]: | |
| err = f"Scan failed: {result['error']}" | |
| return err, json.dumps(result, indent=2) | |
| med = result["medicine"] | |
| model_short = (result.get("model_used") or "unknown").split("/")[-1] | |
| lines = [f"Medicine: {med['name']}"] | |
| if med.get("brandName"): | |
| lines.append(f"Brand: {med['brandName']}") | |
| lines.append(f"Dose: {med['dose']}") | |
| lines.append(f"Form: {med['form'].capitalize()}") | |
| if med.get("activeIngredient"): | |
| lines.append(f"Active ingredient: {med['activeIngredient']}") | |
| if med.get("category"): | |
| lines.append(f"Category: {med['category']}") | |
| if med.get("expiryDate"): | |
| lines.append(f"Expiry: {med['expiryDate']}") | |
| if med.get("notes"): | |
| lines.append(f"Instructions: {med['notes']}") | |
| lines.append(f"\nScanned in {elapsed:.1f}s using {model_short}") | |
| summary = "\n".join(lines) | |
| api_json = json.dumps( | |
| {"success": True, "medicine": med, "model_used": result.get("model_used"), | |
| "scan_time_ms": int(elapsed * 1000)}, | |
| indent=2, ensure_ascii=False, | |
| ) | |
| return summary, api_json | |
| # ============================================================ | |
| # Gradio Interface | |
| # ============================================================ | |
| EXAMPLES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "examples") | |
| example_images = [] | |
| if os.path.isdir(EXAMPLES_DIR): | |
| for fname in sorted(os.listdir(EXAMPLES_DIR)): | |
| if fname.lower().endswith((".jpg", ".jpeg", ".png")): | |
| example_images.append([os.path.join(EXAMPLES_DIR, fname), ""]) | |
| demo = gr.Interface( | |
| fn=scan_image_ui, | |
| inputs=[ | |
| gr.Image(label="Medicine Image", type="filepath"), | |
| gr.Textbox( | |
| label="HuggingFace Token (optional β auto-provided when used from MedOS)", | |
| placeholder="hf_... β only needed for standalone use", | |
| type="password", | |
| ), | |
| ], | |
| outputs=[ | |
| gr.Textbox(label="Extracted Information", lines=10), | |
| gr.Textbox(label="API Response (JSON)", lines=12), | |
| ], | |
| examples=example_images if example_images else None, | |
| cache_examples=False, | |
| title="MedOS Medicine Scanner", | |
| description=( | |
| "Scan medicine packages, labels, or prescriptions with your camera. " | |
| "The AI extracts drug name, dosage, form, expiry date, and instructions automatically.\n\n" | |
| "**From MedOS:** Token is provided automatically β just scan.\n" | |
| "**Standalone:** Enter a free [HuggingFace token](https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained) " | |
| "with *'Make calls to Inference Providers'* permission." | |
| ), | |
| article=( | |
| "**Privacy:** Images are processed via HuggingFace Inference API and are not stored.\n\n" | |
| "**Disclaimer:** For informational purposes only. Always verify with a pharmacist." | |
| ), | |
| flagging_mode="never", | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |