Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| import torch | |
| from transformers import ViTImageProcessor, ViTForImageClassification | |
| from PIL import Image | |
| import gradio as gr | |
| import numpy as np | |
| from typing import Dict, Tuple, Optional | |
| import logging | |
| from pathlib import Path | |
| import spaces | |
| from gradio_client import Client | |
| import json | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Medical-grade CSS styling | |
| medical_css = """ | |
| .gradio-container { | |
| background: linear-gradient(135deg, #e8f4f8 0%, #d1e8e4 50%, #c3d9e1 100%); | |
| font-family: 'Inter', 'Segoe UI', sans-serif; | |
| } | |
| .main-header { | |
| background: linear-gradient(135deg, #2c5282 0%, #2d3748 100%); | |
| color: white; | |
| padding: 2rem; | |
| border-radius: 15px; | |
| margin-bottom: 2rem; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.1); | |
| } | |
| .diagnosis-card { | |
| background: white; | |
| border-radius: 15px; | |
| padding: 1.5rem; | |
| box-shadow: 0 5px 20px rgba(0,0,0,0.08); | |
| border-left: 4px solid #3182ce; | |
| margin: 1rem 0; | |
| } | |
| .result-container { | |
| background: linear-gradient(to right, #f7fafc, #edf2f7); | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| margin-top: 1rem; | |
| border: 1px solid #cbd5e0; | |
| } | |
| .confidence-bar { | |
| background: linear-gradient(90deg, #48bb78 0%, #38a169 100%); | |
| height: 8px; | |
| border-radius: 4px; | |
| transition: width 0.5s ease; | |
| } | |
| .medical-badge { | |
| display: inline-block; | |
| padding: 0.5rem 1rem; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border-radius: 25px; | |
| font-weight: 600; | |
| font-size: 0.9rem; | |
| margin: 0.25rem; | |
| } | |
| .warning-box { | |
| background: #fff5f5; | |
| border-left: 4px solid #fc8181; | |
| padding: 1rem; | |
| border-radius: 8px; | |
| margin: 1rem 0; | |
| } | |
| .info-box { | |
| background: #ebf8ff; | |
| border-left: 4px solid #4299e1; | |
| padding: 1rem; | |
| border-radius: 8px; | |
| margin: 1rem 0; | |
| } | |
| .button-primary { | |
| background: linear-gradient(135deg, #3182ce 0%, #2c5282 100%); | |
| color: white; | |
| border: none; | |
| padding: 0.75rem 2rem; | |
| border-radius: 8px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: transform 0.2s; | |
| } | |
| .button-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(49, 130, 206, 0.3); | |
| } | |
| .status-indicator { | |
| display: inline-block; | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 50%; | |
| margin-right: 8px; | |
| animation: pulse 2s infinite; | |
| } | |
| .status-ready { | |
| background-color: #48bb78; | |
| } | |
| .status-processing { | |
| background-color: #ed8936; | |
| } | |
| .status-error { | |
| background-color: #f56565; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| box-shadow: 0 0 0 0 rgba(72, 187, 120, 0.7); | |
| } | |
| 70% { | |
| box-shadow: 0 0 0 10px rgba(72, 187, 120, 0); | |
| } | |
| 100% { | |
| box-shadow: 0 0 0 0 rgba(72, 187, 120, 0); | |
| } | |
| } | |
| .diagnosis-severity { | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 15px; | |
| font-size: 0.85rem; | |
| font-weight: 600; | |
| display: inline-block; | |
| margin-left: 0.5rem; | |
| } | |
| .severity-low { | |
| background: #c6f6d5; | |
| color: #22543d; | |
| } | |
| .severity-medium { | |
| background: #fed7aa; | |
| color: #7c2d12; | |
| } | |
| .severity-high { | |
| background: #fed7d7; | |
| color: #742a2a; | |
| } | |
| """ | |
| class MedicalSkinDiagnosisSystem: | |
| """Medical-grade skin disease diagnosis system with GPT-OSS integration.""" | |
| def __init__(self, model_name: str = '0xnu/skincare-detection'): | |
| """Initialize the diagnosis system.""" | |
| self.model_name = model_name | |
| self.processor = None | |
| self.model = None | |
| self.id2label = None | |
| self.device = "cuda" if torch.cuda.is_available() else "cpu" | |
| self.gpt_client = None | |
| self.load_model() | |
| # Load disease database AFTER model is loaded to get actual classes | |
| self.disease_info_db = self._load_disease_database() | |
| self._init_gpt_client() | |
| def _load_disease_database(self) -> Dict: | |
| """Load comprehensive disease information database based on actual model classes.""" | |
| # Get actual classes from the loaded model | |
| if self.model and self.id2label: | |
| logger.info(f"Loaded classes from model: {list(self.id2label.values())}") | |
| # Comprehensive database covering common skin conditions found in skincare datasets | |
| database = { | |
| # Acne and related conditions | |
| "acne": { | |
| "name_ko": "์ฌ๋๋ฆ", | |
| "severity": "low", | |
| "description_ko": "๋ชจ๋ญ๊ณผ ํผ์ง์ ์ ๋ง์ฑ ์ผ์ฆ์ฑ ์งํ์ผ๋ก, ๋ฉดํฌ, ๊ตฌ์ง, ๋ํฌ, ๊ฒฐ์ ๋ฑ์ด ๋ํ๋ฉ๋๋ค.", | |
| "treatment_ko": "๊ตญ์ ๋ ํฐ๋ ธ์ด๋, ๋ฒค์กฐ์ผ ํผ์ฅ์ฌ์ด๋, ํญ์์ ์ฐ๊ณ , ์ฌํ ๊ฒฝ์ฐ ์ด์ํธ๋ ํฐ๋ ธ์ธ ๊ฒฝ๊ตฌ ๋ณต์ฉ", | |
| "symptoms_ko": "๋ธ๋ํค๋, ํ์ดํธํค๋, ๋ถ์ ๊ตฌ์ง, ๋ํฌ, ํต์ฆ์ฑ ๊ฒฐ์ " | |
| }, | |
| "blackhead": { | |
| "name_ko": "๋ธ๋ํค๋/๊ฐ๋ฐฉ๋ฉดํฌ", | |
| "severity": "low", | |
| "description_ko": "๋ชจ๊ณต์ด ํผ์ง์ ๊ฐ์ง๋ก ๋งํ ๊ฒ๊ฒ ๋ณด์ด๋ ๊ฐ๋ฐฉ์ฑ ๋ฉดํฌ์ ๋๋ค.", | |
| "treatment_ko": "์ด๋ฆฌ์ค์ฐ, BHA ์ ํ, ๋ ํฐ๋ ธ์ด๋, ์ ๊ธฐ์ ์ธ ๊ฐ์ง ์ ๊ฑฐ", | |
| "symptoms_ko": "๋ชจ๊ณต ๋ด ๊ฒ์ ์ , ์ฃผ๋ก T์กด์ ๋ฐ์" | |
| }, | |
| "whitehead": { | |
| "name_ko": "ํ์ดํธํค๋/ํ์๋ฉดํฌ", | |
| "severity": "low", | |
| "description_ko": "ํผ์ง์ ๊ฐ์ง์ด ๋ชจ๊ณต์ ๋ง์ ์๊ธฐ๋ ํ์์ฑ ๋ฉดํฌ์ ๋๋ค.", | |
| "treatment_ko": "๋ ํฐ๋ ธ์ด๋, AHA/BHA ์ ํ, ๋ถ๋๋ฌ์ด ๊ฐ์ง ์ ๊ฑฐ", | |
| "symptoms_ko": "ํผ๋ถ ํ๋ฉด์ ์์ ํฐ์ ๋๊ธฐ" | |
| }, | |
| "papule": { | |
| "name_ko": "๊ตฌ์ง", | |
| "severity": "low", | |
| "description_ko": "์ผ์ฆ์ด ์๋ ์์ ๋ถ์ ๋๊ธฐ๋ก, ์ฌ๋๋ฆ์ ํ ํํ์ ๋๋ค.", | |
| "treatment_ko": "ํญ์ผ์ฆ ์น๋ฃ์ , ๊ตญ์ ํญ์์ , ๋ฒค์กฐ์ผ ํผ์ฅ์ฌ์ด๋", | |
| "symptoms_ko": "๋ถ๊ณ ๋จ๋จํ ์์ ๋๊ธฐ, ์ํต" | |
| }, | |
| "pustule": { | |
| "name_ko": "๋ํฌ", | |
| "severity": "medium", | |
| "description_ko": "๊ณ ๋ฆ์ด ์ฐฌ ์ผ์ฆ์ฑ ๋ณ๋ณ์ผ๋ก, ์ฌ๋๋ฆ์ ์งํ๋ ํํ์ ๋๋ค.", | |
| "treatment_ko": "๊ตญ์ ๋๋ ๊ฒฝ๊ตฌ ํญ์์ , ๋ฐฐ๋, ํญ์ผ์ฆ ์น๋ฃ", | |
| "symptoms_ko": "์ค์ฌ๋ถ์ ๋ ธ๋ ๊ณ ๋ฆ์ด ์๋ ๋ถ์ ๋๊ธฐ" | |
| }, | |
| "nodule": { | |
| "name_ko": "๊ฒฐ์ ", | |
| "severity": "medium", | |
| "description_ko": "ํผ๋ถ ๊น์์ด ํ์ฑ๋ ํฌ๊ณ ๋จ๋จํ ์ผ์ฆ์ฑ ๋ณ๋ณ์ ๋๋ค.", | |
| "treatment_ko": "๊ฒฝ๊ตฌ ํญ์์ , ์ด์ํธ๋ ํฐ๋ ธ์ธ, ์คํ ๋ก์ด๋ ์ฃผ์ฌ", | |
| "symptoms_ko": "ํฌ๊ณ ๋จ๋จํ ํผํ ๋ฉ์ด๋ฆฌ, ์ฌํ ํต์ฆ" | |
| }, | |
| # Eczema and dermatitis | |
| "eczema": { | |
| "name_ko": "์ต์ง", | |
| "severity": "medium", | |
| "description_ko": "๊ฐ๋ ค์์ฆ๊ณผ ์ผ์ฆ์ ๋๋ฐํ ๋ง์ฑ ํผ๋ถ ์งํ์ผ๋ก, ํผ๋ถ ์ฅ๋ฒฝ ๊ธฐ๋ฅ ์ ํ๊ฐ ํน์ง์ ๋๋ค.", | |
| "treatment_ko": "๋ณด์ต์ , ๊ตญ์ ์คํ ๋ก์ด๋, ์นผ์๋ด๋ฆฐ ์ต์ ์ , ํญํ์คํ๋ฏผ์ ", | |
| "symptoms_ko": "์ฌํ ๊ฐ๋ ค์, ๋ถ์ ๋ฐ์ง, ํผ๋ถ ๊ฑด์กฐ, ์ธ์ค" | |
| }, | |
| "atopic dermatitis": { | |
| "name_ko": "์ํ ํผ ํผ๋ถ์ผ", | |
| "severity": "medium", | |
| "description_ko": "์ ์ ์ ์์ธ๊ณผ ํ๊ฒฝ ์์ธ์ด ๋ณตํฉ์ ์ผ๋ก ์์ฉํ๋ ๋ง์ฑ ์ฌ๋ฐ์ฑ ์ผ์ฆ์ฑ ํผ๋ถ ์งํ์ ๋๋ค.", | |
| "treatment_ko": "๋ณด์ต์ , ๊ตญ์ ์คํ ๋ก์ด๋, ๋ฉด์ญ์กฐ์ ์ , ์๋ฌผํ์ ์ ์ (๋ํผ์ ํธ)", | |
| "symptoms_ko": "๊ทน์ฌํ ๊ฐ๋ ค์, ๊ฑด์กฐ์ฆ, ํ์ ํ, ๋ฐ๋ณต์ ์ ํ์ ํธ์ " | |
| }, | |
| "contact dermatitis": { | |
| "name_ko": "์ ์ด ํผ๋ถ์ผ", | |
| "severity": "medium", | |
| "description_ko": "ํน์ ๋ฌผ์ง๊ณผ์ ์ ์ด์ผ๋ก ์ธํ ์๋ ๋ฅด๊ธฐ์ฑ ๋๋ ์๊ทน์ฑ ํผ๋ถ ๋ฐ์์ ๋๋ค.", | |
| "treatment_ko": "์์ธ ๋ฌผ์ง ํํผ, ๊ตญ์ ์คํ ๋ก์ด๋, ๊ฒฝ๊ตฌ ํญํ์คํ๋ฏผ์ ", | |
| "symptoms_ko": "์ ์ด ๋ถ์์ ๋ฐ์ , ๋ถ์ข , ์ํฌ, ๊ฐ๋ ค์" | |
| }, | |
| "seborrheic dermatitis": { | |
| "name_ko": "์ง๋ฃจ์ฑ ํผ๋ถ์ผ", | |
| "severity": "low", | |
| "description_ko": "ํผ์ง์ ์ด ๋ฐ๋ฌํ ๋ถ์์ ๋ฐ์ํ๋ ๋ง์ฑ ์ผ์ฆ์ฑ ์งํ์ ๋๋ค.", | |
| "treatment_ko": "ํญ์ง๊ท ์ดํธ(์ผํ ์ฝ๋์กธ), ๊ตญ์ ์คํ ๋ก์ด๋, ์นผ์๋ด๋ฆฐ ์ต์ ์ ", | |
| "symptoms_ko": "๋น๋ฌ, ๋ํผ ๊ฐ๋ ค์, ์ผ๊ตด ํ๋ฐ, ๊ธฐ๋ฆ์ง ์ธ์ค" | |
| }, | |
| "perioral dermatitis": { | |
| "name_ko": "๊ตฌ์ฃผ์ ํผ๋ถ์ผ", | |
| "severity": "low", | |
| "description_ko": "์ ์ฃผ๋ณ์ ๋ฐ์ํ๋ ์ผ์ฆ์ฑ ํผ๋ถ ์งํ์ผ๋ก, ์ฃผ๋ก ์ ์ ์ฌ์ฑ์๊ฒ ๋ํ๋ฉ๋๋ค.", | |
| "treatment_ko": "๊ตญ์ ํญ์์ (๋ฉํธ๋ก๋๋ค์กธ), ๊ฒฝ๊ตฌ ํ ํธ๋ผ์ฌ์ดํด๋ฆฐ, ์คํ ๋ก์ด๋ ์ค๋จ", | |
| "symptoms_ko": "์ ์ฃผ๋ณ์ ์์ ๊ตฌ์ง๊ณผ ๋ํฌ, ๊ฒฝ๋ฏธํ ํ๋ฐ" | |
| }, | |
| # Rosacea and vascular conditions | |
| "rosacea": { | |
| "name_ko": "์ฃผ์ฌ๋น/์ฅ๋ฏธ์ฆ", | |
| "severity": "medium", | |
| "description_ko": "์ผ๊ตด ์ค์๋ถ์ ๋ง์ฑ ์ผ์ฆ์ฑ ํผ๋ถ ์งํ์ผ๋ก, ํ์กฐ์ ํ๊ด ํ์ฅ์ด ํน์ง์ ๋๋ค.", | |
| "treatment_ko": "๋ฉํธ๋ก๋๋ค์กธ ์ ค, ์์ ค๋ผ์ฐ, ๋ ์์ฌ์ดํด๋ฆฐ, IPL ๋ ์ด์ ", | |
| "symptoms_ko": "์๋ฉด ํ์กฐ, ์ง์์ ํ๋ฐ, ๊ตฌ์ง, ๋ํฌ, ๋ชจ์ธํ๊ด ํ์ฅ" | |
| }, | |
| "skin redness": { | |
| "name_ko": "ํผ๋ถ ํ๋ฐ", | |
| "severity": "low", | |
| "description_ko": "๋ค์ํ ์์ธ์ผ๋ก ์ธํ ํผ๋ถ์ ๋ถ์ด์ง ํ์์ ๋๋ค.", | |
| "treatment_ko": "์์ธ ์น๋ฃ, ์ง์ ํฌ๋ฆผ, ํญ์ผ์ฆ์ , ๋์ฐ์ง", | |
| "symptoms_ko": "ํผ๋ถ์ ๋ถ์ ๋ณ์, ์ด๊ฐ, ๊ฒฝ๋ฏธํ ๋ถ์ข " | |
| }, | |
| # Pigmentation disorders | |
| "melasma": { | |
| "name_ko": "๊ธฐ๋ฏธ", | |
| "severity": "low", | |
| "description_ko": "์ผ๊ตด์ ๋ํ๋๋ ๋์นญ์ ์ธ ๊ฐ์ ์์ ์นจ์ฐฉ์ผ๋ก, ์ฃผ๋ก ์์ ์ด๋ ํธ๋ฅด๋ชฌ ๋ณํ์ ๊ด๋ จ๋ฉ๋๋ค.", | |
| "treatment_ko": "ํ์ด๋๋กํด๋ ผ, ํธ๋ ํฐ๋ ธ์ธ, ํํ ๋ฐํผ, ๋ ์ด์ ์น๋ฃ, ์์ธ์ ์ฐจ๋จ", | |
| "symptoms_ko": "๋บจ, ์ด๋ง, ์ฝ, ์์๋ถ์ ๊ฐ์ ๋ฐ์ " | |
| }, | |
| "dark spots": { | |
| "name_ko": "์์ ์นจ์ฐฉ/๊ธฐ๋ฏธ", | |
| "severity": "low", | |
| "description_ko": "๊ณผ๋ํ ๋ฉ๋ผ๋ ์์ฑ์ผ๋ก ์ธํ ํผ๋ถ์ ์ด๋์ด ๋ฐ์ ์ ๋๋ค.", | |
| "treatment_ko": "๋ฏธ๋ฐฑ ํฌ๋ฆผ, ๋นํ๋ฏผ C, ๋ ํฐ๋ ธ์ด๋, ํํ ๋ฐํผ, ๋ ์ด์ ", | |
| "symptoms_ko": "๋ถ๊ท์นํ ๊ฐ์ ๋๋ ๊ฒ์ ๋ฐ์ " | |
| }, | |
| "freckles": { | |
| "name_ko": "์ฃผ๊ทผ๊นจ", | |
| "severity": "none", | |
| "description_ko": "์ ์ ์ ์์ธ๊ณผ ์์ธ์ ๋ ธ์ถ๋ก ์ธํ ์์ ๊ฐ์ ๋ฐ์ ์ ๋๋ค.", | |
| "treatment_ko": "์์ธ์ ์ฐจ๋จ, ๋ ์ด์ ์น๋ฃ, ๋ฏธ๋ฐฑ ํฌ๋ฆผ (์ ํ์ )", | |
| "symptoms_ko": "์ผ๊ตด๊ณผ ํ์ ์์ ๊ฐ์ ์ " | |
| }, | |
| "vitiligo": { | |
| "name_ko": "๋ฐฑ๋ฐ์ฆ", | |
| "severity": "low", | |
| "description_ko": "๋ฉ๋ผ๋ ์ธํฌ ์์ค๋ก ์ธํ ํผ๋ถ ํ์์ ์งํ์ ๋๋ค.", | |
| "treatment_ko": "๊ตญ์ ์คํ ๋ก์ด๋, ์นผ์๋ด๋ฆฐ ์ต์ ์ , ๊ด์ ์น๋ฃ, ์์์ด์", | |
| "symptoms_ko": "๊ฒฝ๊ณ๊ฐ ๋ช ํํ ํฐ์ ๋ฐ์ , ๋์นญ์ ๋ถํฌ" | |
| }, | |
| # Infections | |
| "cellulitis": { | |
| "name_ko": "๋ด์์ง์ผ", | |
| "severity": "high", | |
| "description_ko": "ํผ๋ถ์ ํผํ์กฐ์ง์ ๊ธ์ฑ ์ธ๊ท ๊ฐ์ผ์ผ๋ก, ์ฃผ๋ก ์ฐ์์๊ตฌ๊ท ์ด๋ ํฌ๋์๊ตฌ๊ท ์ด ์์ธ์ ๋๋ค.", | |
| "treatment_ko": "๊ฒฝ๊ตฌ ๋๋ ์ ๋งฅ ํญ์์ , ํด์, ๊ฑฐ์, ์งํต์ , ์ ์ ์น๋ฃ", | |
| "symptoms_ko": "๋ฐ์ , ๋ถ์ข , ์ด๊ฐ, ํต์ฆ, ๋ฐ์ด, ์คํ" | |
| }, | |
| "impetigo": { | |
| "name_ko": "๋๊ฐ์ง", | |
| "severity": "medium", | |
| "description_ko": "ํ์ฌ์ฑ ์ธ๊ท ๊ฐ์ผ์ผ๋ก, ์ฃผ๋ก ์ด๋ฆฐ์ด์๊ฒ ๋ฐ์ํ๋ฉฐ ์ ์ผ์ฑ์ด ๊ฐํฉ๋๋ค.", | |
| "treatment_ko": "๊ตญ์ ํญ์์ (๋ฌดํผ๋ก์ ), ๊ฒฝ๊ตฌ ํญ์์ , ์์ ๊ด๋ฆฌ", | |
| "symptoms_ko": "๊ฟ์ ๊ฐํผ, ์ํฌ, ๊ฐ๋ ค์, ์ฃผ๋ณ ํผ๋ถ๋ก ํ์ฐ" | |
| }, | |
| "fungal": { | |
| "name_ko": "์ง๊ท ๊ฐ์ผ/๋ฌด์ข", | |
| "severity": "low", | |
| "description_ko": "ํผ๋ถ ์ง๊ท ์ ์ํ ๊ฐ์ผ์ผ๋ก, ๋ฐฑ์ , ์นธ๋๋ค์ฆ ๋ฑ์ด ํฌํจ๋ฉ๋๋ค.", | |
| "treatment_ko": "ํญ์ง๊ท ์ (๊ตญ์ ๋๋ ๊ฒฝ๊ตฌ), ์์ ๊ด๋ฆฌ, ํตํ ๊ฐ์ ", | |
| "symptoms_ko": "์ธ์ค, ๊ฐ๋ ค์, ๋ฐ์ , ๊ท ์ด, ์ ์ทจ" | |
| }, | |
| "herpes": { | |
| "name_ko": "ํค๋ฅดํ์ค", | |
| "severity": "medium", | |
| "description_ko": "ํค๋ฅดํ์ค ๋ฐ์ด๋ฌ์ค์ ์ํ ์ํฌ์ฑ ํผ๋ถ ๊ฐ์ผ์ ๋๋ค.", | |
| "treatment_ko": "ํญ๋ฐ์ด๋ฌ์ค์ (์์ํด๋ก๋ฒ, ๋ฐ๋ผ์ํด๋ก๋ฒ), ์งํต์ ", | |
| "symptoms_ko": "๊ตฐ์ง์ฑ ์ํฌ, ํต์ฆ, ์์ด๊ฐ, ์ฌ๋ฐ์ฑ" | |
| }, | |
| # Keratosis and scaling conditions | |
| "keratosis": { | |
| "name_ko": "๊ฐํ์ฆ", | |
| "severity": "low", | |
| "description_ko": "ํผ๋ถ์ ๊ฐ์ง์ธต์ด ๊ณผ๋ํ๊ฒ ๋๊บผ์์ง๋ ์ํ์ ๋๋ค.", | |
| "treatment_ko": "๊ฐ์ง์ฉํด์ (์ด๋ฆฌ์ค์ฐ, ์์), ๋ ํฐ๋ ธ์ด๋, ๋ณด์ต์ ", | |
| "symptoms_ko": "๊ฑฐ์น ํผ๋ถ, ์ธ์ค, ๋๊บผ์์ง ๊ฐ์ง" | |
| }, | |
| "actinic keratosis": { | |
| "name_ko": "๊ด์ ๊ฐํ์ฆ", | |
| "severity": "medium", | |
| "description_ko": "๋ง์ฑ ์์ธ์ ๋ ธ์ถ๋ก ์ธํ ์ ์์ฑ ๋ณ๋ณ์ ๋๋ค.", | |
| "treatment_ko": "๋๋์น๋ฃ, 5-FU ํฌ๋ฆผ, ์ด๋ฏธํด๋ชจ๋, ๊ด์ญํ ์น๋ฃ", | |
| "symptoms_ko": "๊ฑฐ์น ์ธ์ค์ฑ ๋ฐ์ , ์ฃผ๋ก ๋ ธ์ถ ๋ถ์" | |
| }, | |
| "psoriasis": { | |
| "name_ko": "๊ฑด์ ", | |
| "severity": "medium", | |
| "description_ko": "ํผ๋ถ์ธํฌ์ ๊ณผ๋ํ ์ฆ์์ผ๋ก ์ธํ ๋ง์ฑ ์๊ฐ๋ฉด์ญ ์งํ์ ๋๋ค.", | |
| "treatment_ko": "๊ตญ์ ์คํ ๋ก์ด๋, ๋นํ๋ฏผ D ์ ๋์ฒด, ๊ด์ ์น๋ฃ, ์๋ฌผํ์ ์ ์ ", | |
| "symptoms_ko": "์๋ฐฑ์ ์ธ์ค, ํ๋ฐ์ฑ ํ, ๊ฐ๋ ค์, ๊ด์ ํต" | |
| }, | |
| # Skin cancers | |
| "melanoma": { | |
| "name_ko": "ํ์์ข ", | |
| "severity": "high", | |
| "description_ko": "๋ฉ๋ผ๋ ์ธํฌ์์ ๋ฐ์ํ๋ ์ ์ฑ ํผ๋ถ์์ผ๋ก, ์ ์ด ์ํ์ด ๋์ต๋๋ค.", | |
| "treatment_ko": "์์ ์ ์ ์ , ๋ฉด์ญ์น๋ฃ, ํ์ ์น๋ฃ, ๋ฐฉ์ฌ์ ์น๋ฃ", | |
| "symptoms_ko": "๋น๋์นญ ๋ชจ์, ๋ถ๊ท์นํ ๊ฒฝ๊ณ, ๋ค์ํ ์์กฐ, 6mm ์ด์ ํฌ๊ธฐ" | |
| }, | |
| "basal cell carcinoma": { | |
| "name_ko": "๊ธฐ์ ์ธํฌ์", | |
| "severity": "high", | |
| "description_ko": "๊ฐ์ฅ ํํ ํผ๋ถ์์ผ๋ก, ์ฒ์ฒํ ์ฑ์ฅํ๋ฉฐ ๊ตญ์ ์นจ์ต์ฑ์ด ์์ต๋๋ค.", | |
| "treatment_ko": "๋ชจ์ค ์์ , ์ ์ ์ , ๋ฐฉ์ฌ์ ์น๋ฃ, ์ด๋ฏธํด๋ชจ๋", | |
| "symptoms_ko": "์ง์ฃผ ๊ดํ ๊ฒฐ์ , ์ค์ฌ๋ถ ๊ถค์, ์ถํ, ๊ฐํผ" | |
| }, | |
| "squamous cell carcinoma": { | |
| "name_ko": "ํธํ์ธํฌ์", | |
| "severity": "high", | |
| "description_ko": "๋ ๋ฒ์งธ๋ก ํํ ํผ๋ถ์์ผ๋ก, ์ ์ด ๊ฐ๋ฅ์ฑ์ด ์์ต๋๋ค.", | |
| "treatment_ko": "์์ ์ ์ ์ , ๋ฐฉ์ฌ์ ์น๋ฃ, ํํ์๋ฒ", | |
| "symptoms_ko": "์ธ์ค์ฑ ํ๋ฐ, ๊ถค์, ์ถํ, ๋จ๋จํ ๊ฒฐ์ " | |
| }, | |
| "carcinoma": { | |
| "name_ko": "ํผ๋ถ์", | |
| "severity": "high", | |
| "description_ko": "ํผ๋ถ์์ ๋ฐ์ํ๋ ์ ์ฑ ์ข ์์ ์ด์นญํฉ๋๋ค.", | |
| "treatment_ko": "์ข ์ ์ ํ์ ๋ฐ๋ฅธ ์์ , ๋ฐฉ์ฌ์ , ํํ์๋ฒ", | |
| "symptoms_ko": "๋น์ ์์ ์ธ ์ฑ์ฅ, ๊ถค์, ์ถํ, ์์กฐ ๋ณํ" | |
| }, | |
| # Other common conditions | |
| "milia": { | |
| "name_ko": "๋น๋ฆฝ์ข /์์์ข ", | |
| "severity": "low", | |
| "description_ko": "๊ฐ์ง์ด ํผ๋ถ ํ๋ฉด ์๋ ๊ฐํ ์๊ธฐ๋ ์์ ํฐ์ ๋ญ์ข ์ ๋๋ค.", | |
| "treatment_ko": "์์ฐ ์์ค, ์์ถ, ๋ ํฐ๋ ธ์ด๋, ํํ ๋ฐํผ", | |
| "symptoms_ko": "1-2mm ํฌ๊ธฐ์ ํฐ์ ๋๋ ๋ ธ๋์ ๊ตฌ์ง" | |
| }, | |
| "warts": { | |
| "name_ko": "์ฌ๋ง๊ท", | |
| "severity": "low", | |
| "description_ko": "์ธ์ ๋์ข ๋ฐ์ด๋ฌ์ค(HPV)์ ์ํ ํผ๋ถ ์ฆ์์ฑ ์งํ์ ๋๋ค.", | |
| "treatment_ko": "๋๋์น๋ฃ, ์ด๋ฆฌ์ค์ฐ, ๋ ์ด์ , ๋ฉด์ญ์น๋ฃ", | |
| "symptoms_ko": "๊ฑฐ์น ํ๋ฉด์ ๋๊ธฐ, ๊ฒ์ ์ , ๊ตฐ์ง์ฑ ๋ณ๋ณ" | |
| }, | |
| "urticaria": { | |
| "name_ko": "๋๋๋ฌ๊ธฐ", | |
| "severity": "low", | |
| "description_ko": "ํผ๋ถ์ ์ผ์์ ์ธ ๋ถ์ข ๊ณผ ๊ฐ๋ ค์์ ๋๋ฐํ ์๋ ๋ฅด๊ธฐ ๋ฐ์์ ๋๋ค.", | |
| "treatment_ko": "ํญํ์คํ๋ฏผ์ , ์๋ ๋ฅด๊ธฐ ์์ธ ํํผ, ์คํ ๋ก์ด๋", | |
| "symptoms_ko": "ํฝ์ง, ์ฌํ ๊ฐ๋ ค์, ํ๊ด๋ถ์ข " | |
| }, | |
| "hives": { | |
| "name_ko": "๋๋๋ฌ๊ธฐ", | |
| "severity": "low", | |
| "description_ko": "๊ธ์ฑ ์๋ ๋ฅด๊ธฐ ๋ฐ์์ผ๋ก ์ธํ ํผ๋ถ ํฝ์ง์ ๋๋ค.", | |
| "treatment_ko": "ํญํ์คํ๋ฏผ์ , ์ํผ๋คํ๋ฆฐ(์ฌํ ๊ฒฝ์ฐ)", | |
| "symptoms_ko": "๋ถ๊ณ ๋ถํผ ํฝ์ง, ๊ฐ๋ ค์, ์ด๋์ฑ ๋ณ๋ณ" | |
| }, | |
| # Aging and cosmetic concerns | |
| "wrinkles": { | |
| "name_ko": "์ฃผ๋ฆ", | |
| "severity": "none", | |
| "description_ko": "๋ ธํ์ ์์ธ์ ๋ ธ์ถ๋ก ์ธํ ํผ๋ถ ํ๋ ฅ ๊ฐ์์ ์ฃผ๋ฆ ํ์ฑ์ ๋๋ค.", | |
| "treatment_ko": "๋ ํฐ๋ ธ์ด๋, ๋ณดํก์ค, ํ๋ฌ, ๋ ์ด์ , ์์ธ์ ์ฐจ๋จ", | |
| "symptoms_ko": "๋ฏธ์ธ์ฃผ๋ฆ, ๊น์ ์ฃผ๋ฆ, ํ๋ ฅ ๊ฐ์" | |
| }, | |
| "dark circles": { | |
| "name_ko": "๋คํฌ์ํด", | |
| "severity": "none", | |
| "description_ko": "๋ ์๋ ํผ๋ถ๊ฐ ์ด๋ก๊ฒ ๋ณด์ด๋ ํ์์ผ๋ก, ํผ๋ก, ์ ์ , ๋ ธํ ๋ฑ์ด ์์ธ์ ๋๋ค.", | |
| "treatment_ko": "์ถฉ๋ถํ ์๋ฉด, ๋นํ๋ฏผ K ํฌ๋ฆผ, ๋ ํฐ๋ ธ์ด๋, ํ๋ฌ", | |
| "symptoms_ko": "๋ ์๋ ์ด๋์ด ๋ณ์, ๋ถ์ข " | |
| }, | |
| "eye bags": { | |
| "name_ko": "๋๋ฐ ์ง๋ฐฉ", | |
| "severity": "none", | |
| "description_ko": "๋ ์๋ ์ง๋ฐฉ์ด ๋์ถ๋์ด ์๊ธฐ๋ ๋ถ์ข ์ ๋๋ค.", | |
| "treatment_ko": "๋์ฐ์ง, ์นดํ์ธ ํฌ๋ฆผ, ์์ (์ฌํ ๊ฒฝ์ฐ)", | |
| "symptoms_ko": "๋ ์๋ ๋ถํ์ด ์ค๋ฆ, ์ฒ์ง" | |
| }, | |
| "enlarged pores": { | |
| "name_ko": "๋ชจ๊ณต ํ๋", | |
| "severity": "none", | |
| "description_ko": "ํผ์ง ๊ณผ๋ค ๋ถ๋น์ ํ๋ ฅ ๊ฐ์๋ก ์ธํ ๋ชจ๊ณต ํ๋์ ๋๋ค.", | |
| "treatment_ko": "๋ ํฐ๋ ธ์ด๋, BHA, ๋ ์ด์ , ๋ชจ๊ณต ์ถ์ ์์ ", | |
| "symptoms_ko": "๋์ ๋๋ ๋ชจ๊ณต, ํผ์ง ๊ณผ๋ค" | |
| }, | |
| # Skin types | |
| "normal": { | |
| "name_ko": "์ ์ ํผ๋ถ", | |
| "severity": "none", | |
| "description_ko": "๊ท ํ์กํ ๊ฑด๊ฐํ ํผ๋ถ ์ํ์ ๋๋ค.", | |
| "treatment_ko": "๊ธฐ๋ณธ ์คํจ์ผ์ด ์ ์ง, ์์ธ์ ์ฐจ๋จ, ๋ณด์ต", | |
| "symptoms_ko": "ํน๋ณํ ๋ฌธ์ ์์, ์ ์ ํ ์ ์๋ถ ๋ฐธ๋ฐ์ค" | |
| }, | |
| "normal skin": { | |
| "name_ko": "์ ์ ํผ๋ถ", | |
| "severity": "none", | |
| "description_ko": "๊ฑด๊ฐํ๊ณ ๊ท ํ์กํ ํผ๋ถ ์ํ์ ๋๋ค.", | |
| "treatment_ko": "์ผ๋ฐ์ ์ธ ์คํจ์ผ์ด ๋ฃจํด ์ ์ง", | |
| "symptoms_ko": "๋งค๋๋ฌ์ด ์ง๊ฐ, ๊ท ์ผํ ํค" | |
| }, | |
| "dry skin": { | |
| "name_ko": "๊ฑด์ฑ ํผ๋ถ", | |
| "severity": "low", | |
| "description_ko": "์๋ถ๊ณผ ํผ์ง๊ฐ ๋ถ์กฑํ ํผ๋ถ ์ํ์ ๋๋ค.", | |
| "treatment_ko": "๋ณด์ต์ , ์ธ๋ผ๋ง์ด๋, ํ์๋ฃจ๋ก ์ฐ, ๋ถ๋๋ฌ์ด ํด๋ ์ ", | |
| "symptoms_ko": "๋น๊น, ๊ฐ์ง, ์น์นํจ, ๋ฏธ์ธ์ฃผ๋ฆ" | |
| }, | |
| "oily skin": { | |
| "name_ko": "์ง์ฑ ํผ๋ถ", | |
| "severity": "low", | |
| "description_ko": "ํผ์ง ๋ถ๋น๊ฐ ๊ณผ๋ํ ํผ๋ถ ์ํ์ ๋๋ค.", | |
| "treatment_ko": "์ค์ผํ๋ฆฌ ์ ํ, BHA, ํด๋ ์ด ๋ง์คํฌ, ๊ฐ๋ฒผ์ด ๋ณด์ต", | |
| "symptoms_ko": "๋ฒ๋ค๊ฑฐ๋ฆผ, ๋์ ๋ชจ๊ณต, ์ฌ๋๋ฆ ๋ฐ์" | |
| }, | |
| "combination skin": { | |
| "name_ko": "๋ณตํฉ์ฑ ํผ๋ถ", | |
| "severity": "none", | |
| "description_ko": "T์กด์ ์ง์ฑ, U์กด์ ๊ฑด์ฑ์ธ ๋ณตํฉ์ ํผ๋ถ ์ํ์ ๋๋ค.", | |
| "treatment_ko": "๋ถ์๋ณ ๋ง์ถค ์ผ์ด, ๋ฐธ๋ฐ์ฑ ์ ํ", | |
| "symptoms_ko": "T์กด ๋ฒ๋ค๊ฑฐ๋ฆผ, ๋ณผ ๊ฑด์กฐ" | |
| }, | |
| "sensitive skin": { | |
| "name_ko": "๋ฏผ๊ฐ์ฑ ํผ๋ถ", | |
| "severity": "low", | |
| "description_ko": "์ธ๋ถ ์๊ทน์ ์ฝ๊ฒ ๋ฐ์ํ๋ ํผ๋ถ ์ํ์ ๋๋ค.", | |
| "treatment_ko": "์ ์๊ทน ์ ํ, ๋ฌดํฅ๋ฃ, ์ง์ ์ฑ๋ถ", | |
| "symptoms_ko": "์ฌ์ด ์๊ทน, ๋ถ์ด์ง, ๋ฐ๊ฐ์" | |
| } | |
| } | |
| return database | |
| def _init_gpt_client(self): | |
| """Initialize GPT-OSS client for detailed explanations.""" | |
| try: | |
| # This would connect to the GPT-OSS API | |
| # For now, we'll use a placeholder | |
| self.gpt_client = None | |
| logger.info("GPT-OSS client initialized (placeholder)") | |
| except Exception as e: | |
| logger.error(f"Failed to initialize GPT client: {e}") | |
| def load_model(self) -> None: | |
| """Load the vision model for skin disease classification.""" | |
| try: | |
| logger.info(f"Loading model: {self.model_name}") | |
| self.processor = ViTImageProcessor.from_pretrained(self.model_name) | |
| self.model = ViTForImageClassification.from_pretrained(self.model_name) | |
| self.model.eval() | |
| self.id2label = self.model.config.id2label | |
| logger.info(f"Model loaded: {len(self.id2label)} disease classes") | |
| except Exception as e: | |
| logger.error(f"Failed to load model: {e}") | |
| raise RuntimeError(f"Model loading failed: {str(e)}") | |
| def diagnose(self, image: Image.Image) -> Tuple[Dict[str, float], str, str]: | |
| """ | |
| Perform diagnosis on the skin image. | |
| Returns: | |
| Tuple of (probabilities, formatted_output, gpt_analysis) | |
| """ | |
| if self.model is None or self.processor is None: | |
| return {}, "โ ๋ชจ๋ธ ๋ก๋ฉ ์คํจ / Model not loaded", "" | |
| try: | |
| # Image preprocessing | |
| if image.mode != 'RGB': | |
| image = image.convert('RGB') | |
| # Move model to GPU | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| self.model = self.model.to(device) | |
| # Process image | |
| inputs = self.processor(images=image, return_tensors="pt") | |
| inputs = {k: v.to(device) for k, v in inputs.items()} | |
| # Get predictions | |
| with torch.no_grad(): | |
| outputs = self.model(**inputs) | |
| probabilities = torch.softmax(outputs.logits, dim=-1)[0] | |
| probabilities = probabilities.cpu() | |
| # Convert to dictionary | |
| class_probs = {} | |
| for class_id, prob in enumerate(probabilities): | |
| class_name = self.id2label[class_id] | |
| class_probs[class_name] = float(prob) | |
| # Sort by confidence | |
| sorted_probs = dict(sorted(class_probs.items(), | |
| key=lambda x: x[1], reverse=True)) | |
| # Format results with medical information | |
| formatted_output = self._format_medical_results(sorted_probs, image.size) | |
| # Generate GPT analysis | |
| gpt_analysis = self._generate_gpt_analysis(sorted_probs) | |
| return sorted_probs, formatted_output, gpt_analysis | |
| except Exception as e: | |
| error_msg = f"โ ์ง๋จ ์คํจ / Diagnosis failed: {str(e)}" | |
| logger.error(error_msg) | |
| return {}, error_msg, "" | |
| def _format_medical_results(self, class_probs: Dict[str, float], | |
| image_size: Tuple[int, int]) -> str: | |
| """Format results in medical report style with Korean.""" | |
| top_class = next(iter(class_probs)) | |
| top_confidence = class_probs[top_class] | |
| # Get disease info if available | |
| disease_info = self.disease_info_db.get(top_class.lower(), {}) | |
| name_ko = disease_info.get("name_ko", top_class) | |
| severity = disease_info.get("severity", "unknown") | |
| # Determine severity badge | |
| severity_badge = { | |
| "low": '<span class="diagnosis-severity severity-low">๊ฒฝ์ฆ / Mild</span>', | |
| "medium": '<span class="diagnosis-severity severity-medium">์ค๋ฑ๋ / Moderate</span>', | |
| "high": '<span class="diagnosis-severity severity-high">์ค์ฆ / Severe</span>', | |
| "unknown": '<span class="diagnosis-severity">๋ฏธ๋ถ๋ฅ / Unclassified</span>' | |
| }.get(severity, "") | |
| # Build medical report | |
| output = f""" | |
| <div class="diagnosis-card"> | |
| <h3>๐ฅ AI ํผ๋ถ ์ง๋จ ๊ฒฐ๊ณผ / AI Skin Diagnosis Report</h3> | |
| <div class="result-container"> | |
| <h4>๐ ์ฃผ์ ์ง๋จ / Primary Diagnosis</h4> | |
| <p><strong>์งํ๋ช / Condition:</strong> {top_class.title()} ({name_ko}) {severity_badge}</p> | |
| <p><strong>์ ๋ขฐ๋ / Confidence:</strong> {top_confidence:.1%}</p> | |
| <div class="confidence-bar" style="width: {top_confidence*100}%"></div> | |
| </div> | |
| <div class="result-container"> | |
| <h4>๐ ์์ธ ๋ถ์ / Detailed Analysis</h4> | |
| <p><strong>์ด๋ฏธ์ง ํฌ๊ธฐ / Image Size:</strong> {image_size[0]} ร {image_size[1]} pixels</p> | |
| <p><strong>๋ถ์ ์๊ฐ / Analysis Time:</strong> <span class="status-indicator status-ready"></span>์ค์๊ฐ / Real-time</p> | |
| </div> | |
| <div class="result-container"> | |
| <h4>๐ ๊ฐ๋ณ ์ง๋จ / Differential Diagnosis (Top 5)</h4> | |
| """ | |
| # Add top 5 differential diagnoses | |
| for idx, (class_name, confidence) in enumerate(class_probs.items()): | |
| if idx >= 5: | |
| break | |
| disease_ko = self.disease_info_db.get(class_name.lower(), {}).get("name_ko", class_name) | |
| if confidence >= 0.01: # Only show > 1% | |
| bar_length = int(confidence * 20) | |
| bar = "โ" * bar_length + "โ" * (20 - bar_length) | |
| output += f""" | |
| <p>โข <strong>{idx+1}. {class_name.title()} ({disease_ko}):</strong> {confidence:.1%} | |
| <code>{bar}</code></p> | |
| """ | |
| output += """ | |
| </div> | |
| <div class="warning-box"> | |
| <strong>โ ๏ธ ์ฃผ์์ฌํญ / Important Notice:</strong><br> | |
| ์ด ๊ฒฐ๊ณผ๋ AI ๊ธฐ๋ฐ ์์ธก์ด๋ฉฐ, ์ค์ ์ํ์ ์ง๋จ์ ๋์ฒดํ ์ ์์ต๋๋ค.<br> | |
| ์ ํํ ์ง๋จ๊ณผ ์น๋ฃ๋ฅผ ์ํด ๋ฐ๋์ ํผ๋ถ๊ณผ ์ ๋ฌธ์์ ์๋ดํ์ธ์.<br> | |
| <em>This is an AI-based prediction and cannot replace actual medical diagnosis.<br> | |
| Please consult a dermatologist for accurate diagnosis and treatment.</em> | |
| </div> | |
| </div> | |
| """ | |
| return output | |
| def _generate_gpt_analysis(self, class_probs: Dict[str, float]) -> str: | |
| """Generate detailed analysis using GPT-OSS model.""" | |
| top_class = next(iter(class_probs)) | |
| top_confidence = class_probs[top_class] | |
| # Get disease information from database - check multiple variations | |
| disease_info = None | |
| # Try different case variations to find the disease | |
| search_keys = [ | |
| top_class.lower(), | |
| top_class.lower().replace('_', ' '), | |
| top_class.lower().replace('-', ' '), | |
| top_class.replace('_', ' ').lower(), | |
| top_class.replace('-', ' ').lower() | |
| ] | |
| for key in search_keys: | |
| if key in self.disease_info_db: | |
| disease_info = self.disease_info_db[key] | |
| logger.info(f"Found disease info for: {key}") | |
| break | |
| # If still not found, create comprehensive default information | |
| if disease_info is None: | |
| logger.warning(f"No database entry for: {top_class}") | |
| disease_info = self._create_default_disease_info(top_class) | |
| # Get detailed symptoms if available | |
| symptoms_text = disease_info.get('symptoms_ko', '') | |
| if symptoms_text: | |
| symptoms_html = f""" | |
| <div class="result-container"> | |
| <h4>๐ ์ฃผ์ ์ฆ์ / Key Symptoms</h4> | |
| <p>{symptoms_text}</p> | |
| </div> | |
| """ | |
| else: | |
| symptoms_html = "" | |
| # Create comprehensive GPT analysis with all available information | |
| analysis = f""" | |
| <div class="diagnosis-card"> | |
| <h3>๐ค GPT-OSS ์์ธ ๋ถ์ / GPT-OSS Detailed Analysis</h3> | |
| <div class="info-box"> | |
| <h4>๐ ์ง๋จ๋ ์งํ ์ ๋ณด / Diagnosed Condition Information</h4> | |
| <p><strong>์งํ๋ช / Disease:</strong> {top_class.title()} ({disease_info['name_ko']})</p> | |
| <p><strong>์ ๋ขฐ๋ / Confidence:</strong> {top_confidence:.1%}</p> | |
| <p><strong>์ค์ฆ๋ / Severity:</strong> {self._get_severity_badge(disease_info.get('severity', 'unknown'))}</p> | |
| </div> | |
| <div class="result-container"> | |
| <h4>๐ ์งํ ์ค๋ช / Disease Description</h4> | |
| <p><strong>{disease_info['name_ko']} ({top_class.title()})</strong></p> | |
| <p>{disease_info['description_ko']}</p> | |
| </div> | |
| {symptoms_html} | |
| <div class="result-container"> | |
| <h4>๐ ๊ถ์ฅ ์น๋ฃ๋ฒ / Recommended Treatment</h4> | |
| <p><strong>์น๋ฃ ๋ฐฉ๋ฒ:</strong></p> | |
| <p>{disease_info['treatment_ko']}</p> | |
| </div> | |
| <div class="result-container"> | |
| <h4>๐ ์ํ ๊ด๋ฆฌ ์ง์นจ / Lifestyle Management Guidelines</h4> | |
| {self._get_lifestyle_guidelines(top_class, disease_info.get('severity', 'low'))} | |
| </div> | |
| <div class="result-container"> | |
| <h4>๐ฅ ์๋ฃ์ง ์๋ด ํ์ ์์ / When to Consult Healthcare Provider</h4> | |
| {self._get_consultation_guidelines(disease_info.get('severity', 'low'))} | |
| </div> | |
| <div class="result-container"> | |
| <h4>๐ฌ ์ถ๊ฐ ๊ฒ์ฌ ๊ถ์ฅ์ฌํญ / Recommended Additional Tests</h4> | |
| {self._get_test_recommendations(top_class, disease_info.get('severity', 'low'))} | |
| </div> | |
| <div class="result-container"> | |
| <h4>โ ๏ธ ์ฃผ์์ฌํญ / Precautions</h4> | |
| {self._get_precautions(top_class, disease_info.get('severity', 'low'))} | |
| </div> | |
| </div> | |
| """ | |
| return analysis | |
| def _create_default_disease_info(self, disease_name: str) -> Dict: | |
| """Create default disease information when not in database.""" | |
| # Try to infer information from the disease name | |
| name_lower = disease_name.lower() | |
| # Determine Korean name | |
| name_ko = self._translate_disease_name(disease_name) | |
| # Determine severity | |
| severity = self._estimate_severity(disease_name) | |
| # Create appropriate description based on keywords | |
| if 'cancer' in name_lower or 'carcinoma' in name_lower or 'melanoma' in name_lower: | |
| description = f"์ ์ฑ ์ข ์์ ๊ฐ๋ฅ์ฑ์ด ์๋ ํผ๋ถ ๋ณ๋ณ์ ๋๋ค. ์ฆ์ ์ ๋ฌธ์ ์ง๋จ์ด ํ์ํฉ๋๋ค." | |
| treatment = "์ฆ์ ํผ๋ถ๊ณผ ๋๋ ์ข ์ ์ ๋ฌธ์ ์๋ด, ์กฐ์ง๊ฒ์ฌ ํ์, ์กฐ๊ธฐ ์ง๋จ๊ณผ ์น๋ฃ๊ฐ ์ค์" | |
| elif 'infection' in name_lower or 'bacterial' in name_lower: | |
| description = f"์ธ๊ท ๊ฐ์ผ์ด ์์ฌ๋๋ ํผ๋ถ ์งํ์ ๋๋ค. ์ ์ ํ ํญ์์ ์น๋ฃ๊ฐ ํ์ํ ์ ์์ต๋๋ค." | |
| treatment = "ํญ์์ ์น๋ฃ(๊ตญ์ ๋๋ ๊ฒฝ๊ตฌ), ์์ฒ ์๋ , ์์ ๊ด๋ฆฌ" | |
| elif 'fungal' in name_lower: | |
| description = f"์ง๊ท ๊ฐ์ผ์ด ์์ฌ๋๋ ํผ๋ถ ์งํ์ ๋๋ค." | |
| treatment = "ํญ์ง๊ท ์ ์น๋ฃ, ํ๋ถ ๊ฑด์กฐ ์ ์ง, ํตํ ๊ฐ์ " | |
| elif 'dermatitis' in name_lower or 'eczema' in name_lower: | |
| description = f"ํผ๋ถ์ ์ผ์ฆ์ฑ ์งํ์ผ๋ก, ๊ฐ๋ ค์๊ณผ ๋ฐ์ ์ด ๋๋ฐ๋ ์ ์์ต๋๋ค." | |
| treatment = "๋ณด์ต์ ์ฌ์ฉ, ๊ตญ์ ์คํ ๋ก์ด๋, ํญํ์คํ๋ฏผ์ , ์๊ทน ๋ฌผ์ง ํํผ" | |
| elif 'acne' in name_lower or 'pimple' in name_lower: | |
| description = f"๋ชจ๋ญ๊ณผ ํผ์ง์ ์ ์ผ์ฆ์ฑ ์งํ์ ๋๋ค." | |
| treatment = "์ ์ ํ ํด๋ ์ง, ๊ตญ์ ์น๋ฃ์ (๋ ํฐ๋ ธ์ด๋, ๋ฒค์กฐ์ผ ํผ์ฅ์ฌ์ด๋), ํญ์์ " | |
| else: | |
| description = f"ํผ๋ถ ์งํ์ผ๋ก ์ ํํ ์ง๋จ์ ์ํด ์ ๋ฌธ์ ์๋ด์ด ๊ถ์ฅ๋ฉ๋๋ค." | |
| treatment = "ํผ๋ถ๊ณผ ์ ๋ฌธ์ ์๋ด์ ํตํ ์ ํํ ์ง๋จ ํ ์ ์ ํ ์น๋ฃ" | |
| return { | |
| "name_ko": name_ko, | |
| "severity": severity, | |
| "description_ko": description, | |
| "treatment_ko": treatment, | |
| "symptoms_ko": "์ก์ ๊ฒ์ฌ์ ์ถ๊ฐ ๊ฒ์ฌ๋ฅผ ํตํด ์ ํํ ์ฆ์ ํ์ ํ์" | |
| } | |
| def _get_severity_badge(self, severity: str) -> str: | |
| """Get HTML badge for severity level.""" | |
| badges = { | |
| "none": '<span class="diagnosis-severity">์ ์ / Normal</span>', | |
| "low": '<span class="diagnosis-severity severity-low">๊ฒฝ์ฆ / Mild</span>', | |
| "medium": '<span class="diagnosis-severity severity-medium">์ค๋ฑ๋ / Moderate</span>', | |
| "high": '<span class="diagnosis-severity severity-high">์ค์ฆ / Severe</span>', | |
| "unknown": '<span class="diagnosis-severity">๋ฏธ๋ถ๋ฅ / Unclassified</span>' | |
| } | |
| return badges.get(severity, badges["unknown"]) | |
| def _get_lifestyle_guidelines(self, disease: str, severity: str) -> str: | |
| """Get lifestyle management guidelines based on disease and severity.""" | |
| guidelines = "<ul>" | |
| # General guidelines for all conditions | |
| guidelines += "<li>์ถฉ๋ถํ ์๋ถ ์ญ์ทจ (ํ๋ฃจ 8์ ์ด์) / Drink plenty of water (8+ glasses daily)</li>" | |
| guidelines += "<li>๊ท ํ ์กํ ์๋จ ์ ์ง / Maintain a balanced diet</li>" | |
| guidelines += "<li>์ถฉ๋ถํ ์๋ฉด (7-8์๊ฐ) / Get adequate sleep (7-8 hours)</li>" | |
| # Specific guidelines based on disease type | |
| disease_lower = disease.lower() | |
| if 'acne' in disease_lower or 'pimple' in disease_lower: | |
| guidelines += "<li>์ ์๊ทน์ฑ ํด๋ ์ ์ฌ์ฉ / Use gentle, non-comedogenic cleanser</li>" | |
| guidelines += "<li>์์ผ๋ก ์ผ๊ตด ๋ง์ง์ง ์๊ธฐ / Avoid touching face with hands</li>" | |
| guidelines += "<li>์ ์ ํ๊ณผ ๊ณ ๋น๋ถ ์ํ ์ ํ / Limit dairy and high-sugar foods</li>" | |
| if 'eczema' in disease_lower or 'dermatitis' in disease_lower: | |
| guidelines += "<li>ํ๋ฃจ 2ํ ์ด์ ๋ณด์ต์ ์ฌ์ฉ / Apply moisturizer at least twice daily</li>" | |
| guidelines += "<li>๋จ๊ฑฐ์ด ๋ฌผ ์ค์ ํผํ๊ธฐ / Avoid hot water showers</li>" | |
| guidelines += "<li>๋ฉด ์์ฌ ์๋ฅ ์ฐฉ์ฉ / Wear cotton clothing</li>" | |
| if 'psoriasis' in disease_lower: | |
| guidelines += "<li>์คํธ๋ ์ค ๊ด๋ฆฌ (์๊ฐ, ๋ช ์) / Manage stress (yoga, meditation)</li>" | |
| guidelines += "<li>์์ฝ์ฌ ์ญ์ทจ ์ ํ / Limit alcohol consumption</li>" | |
| guidelines += "<li>์ ์ ํ ํ๋น ๋ ธ์ถ / Moderate sun exposure</li>" | |
| if severity == "high": | |
| guidelines += "<li>์ฆ์ ์ ๋ฌธ์ ์๋ด ์์ฝ / Schedule immediate specialist consultation</li>" | |
| guidelines += "<li>์ฒ๋ฐฉ๋ ์ฝ๋ฌผ ๊ท์น์ ๋ณต์ฉ / Take prescribed medications regularly</li>" | |
| guidelines += "<li>์์ธ์ ์ฐจ๋จ์ ๋งค์ผ ์ฌ์ฉ (SPF 30+) / Apply sunscreen daily (SPF 30+)</li>" | |
| guidelines += "</ul>" | |
| return guidelines | |
| def _get_consultation_guidelines(self, severity: str) -> str: | |
| """Get consultation guidelines based on severity.""" | |
| guidelines = "<ul>" | |
| if severity == "high": | |
| guidelines += "<li><strong style='color: red;'>์ฆ์ ์๋ฃ์ง ์๋ด ํ์ / Immediate medical consultation required</strong></li>" | |
| guidelines += "<li>24์๊ฐ ์ด๋ด ํผ๋ถ๊ณผ ๋ฐฉ๋ฌธ ๊ถ์ฅ / Visit dermatologist within 24 hours</li>" | |
| guidelines += "<li>์ฆ์์ด 2์ฃผ ์ด์ ์ง์๋ ๊ฒฝ์ฐ / If symptoms persist for more than 2 weeks</li>" | |
| guidelines += "<li>ํต์ฆ, ์ถํ, ๋ถ๋น๋ฌผ์ด ์์ ๊ฒฝ์ฐ / If pain, bleeding, or discharge occurs</li>" | |
| guidelines += "<li>๋ณ๋ณ์ด ๊ธ์ํ ํ์ฐ๋๋ ๊ฒฝ์ฐ / If lesions spread rapidly</li>" | |
| guidelines += "<li>๋ฐ์ด, ์คํ ๋ฑ ์ ์ ์ฆ์ ๋๋ฐ ์ / If accompanied by fever or chills</li>" | |
| guidelines += "<li>์ผ์์ํ์ ์ง์ฅ์ ์ค ์ ๋๋ก ์ฌํ ๊ฒฝ์ฐ / If severe enough to affect daily life</li>" | |
| guidelines += "<li>๊ธฐ์กด ์น๋ฃ์ ๋ฐ์ํ์ง ์๋ ๊ฒฝ์ฐ / If not responding to current treatment</li>" | |
| guidelines += "</ul>" | |
| return guidelines | |
| def _get_test_recommendations(self, disease: str, severity: str) -> str: | |
| """Get test recommendations based on disease type.""" | |
| tests = "<ul>" | |
| disease_lower = disease.lower() | |
| # Cancer-related tests | |
| if 'cancer' in disease_lower or 'carcinoma' in disease_lower or 'melanoma' in disease_lower: | |
| tests += "<li>ํผ๋ถ ์กฐ์ง๊ฒ์ฌ (Biopsy) - <strong>ํ์</strong></li>" | |
| tests += "<li>๋๋ชจ์ค์ฝํผ ๊ฒ์ฌ / Dermoscopy examination</li>" | |
| tests += "<li>์ ์ ํผ๋ถ ๊ฒ์ง / Full body skin examination</li>" | |
| tests += "<li>๋ฆผํ์ ๊ฒ์ฌ / Lymph node examination</li>" | |
| # Infection tests | |
| elif 'infection' in disease_lower or 'bacterial' in disease_lower or 'fungal' in disease_lower: | |
| tests += "<li>์ธ๊ท ๋ฐฐ์ ๊ฒ์ฌ / Bacterial culture</li>" | |
| tests += "<li>์ง๊ท ๊ฒ์ฌ (KOH test) / Fungal examination</li>" | |
| tests += "<li>ํญ์์ ๊ฐ์์ฑ ๊ฒ์ฌ / Antibiotic sensitivity test</li>" | |
| # Allergy/Dermatitis tests | |
| elif 'dermatitis' in disease_lower or 'eczema' in disease_lower: | |
| tests += "<li>ํจ์น ํ ์คํธ (์๋ ๋ฅด๊ธฐ ๊ฒ์ฌ) / Patch testing</li>" | |
| tests += "<li>ํ์ก IgE ๊ฒ์ฌ / Blood IgE test</li>" | |
| tests += "<li>ํผ๋ถ ๋จ์ ๊ฒ์ฌ / Skin prick test</li>" | |
| # General tests | |
| else: | |
| tests += "<li>ํผ๋ถ ํ๋๊ฒฝ ๊ฒ์ฌ / Magnified skin examination</li>" | |
| tests += "<li>Wood's lamp ๊ฒ์ฌ (ํ์์) / Wood's lamp examination (if needed)</li>" | |
| if severity == "high": | |
| tests += "<li>ํ์ก ๊ฒ์ฌ (CBC, CRP) / Blood tests (CBC, CRP)</li>" | |
| tests += "<li>์์ ๊ฒ์ฌ (ํ์์) / Imaging studies (if needed)</li>" | |
| tests += "</ul>" | |
| return tests | |
| def _get_precautions(self, disease: str, severity: str) -> str: | |
| """Get precautions based on disease type.""" | |
| precautions = "<ul>" | |
| if severity == "high": | |
| precautions += "<li><strong>์๊ฐ ์น๋ฃ ๊ธ์ง - ๋ฐ๋์ ์ ๋ฌธ์ ์๋ด</strong></li>" | |
| precautions += "<li>์ฒ๋ฐฉ ์์ด ์คํ ๋ก์ด๋ ์ฐ๊ณ ์ฅ๊ธฐ ์ฌ์ฉ ๊ธ์ง / Avoid long-term steroid use without prescription</li>" | |
| precautions += "<li>๋ณ๋ณ ๋ถ์ ๊ธ๊ฑฐ๋ ์๊ทนํ์ง ์๊ธฐ / Do not scratch or irritate affected areas</li>" | |
| precautions += "<li>๊ฒ์ฆ๋์ง ์์ ๋ฏผ๊ฐ์๋ฒ ํผํ๊ธฐ / Avoid unverified home remedies</li>" | |
| precautions += "<li>ํ์ธ๊ณผ ์๊ฑด, ์๋ฅ ๊ณต์ ๊ธ์ง / Do not share towels or clothing</li>" | |
| precautions += "<li>์ฆ์ ์ ํ ์ ์ฆ์ ์๋ฃ์ง ์๋ด / Consult immediately if symptoms worsen</li>" | |
| precautions += "</ul>" | |
| return precautions | |
| def _translate_disease_name(self, disease_name: str) -> str: | |
| """Translate disease name to Korean if not in database.""" | |
| # Common translations for diseases not in main database | |
| translations = { | |
| "healthy": "์ ์", | |
| "benign": "์์ฑ", | |
| "malignant": "์ ์ฑ", | |
| "infection": "๊ฐ์ผ", | |
| "inflammation": "์ผ์ฆ", | |
| "allergy": "์๋ ๋ฅด๊ธฐ", | |
| "autoimmune": "์๊ฐ๋ฉด์ญ", | |
| "bacterial": "์ธ๊ท ์ฑ", | |
| "viral": "๋ฐ์ด๋ฌ์ค์ฑ" | |
| } | |
| # Check if any keyword matches | |
| lower_name = disease_name.lower() | |
| for key, value in translations.items(): | |
| if key in lower_name: | |
| return f"{value} ๊ด๋ จ ์งํ" | |
| # Return original if no translation found | |
| return disease_name | |
| def _estimate_severity(self, disease_name: str) -> str: | |
| """Estimate severity based on disease name keywords.""" | |
| high_severity_keywords = ["cancer", "carcinoma", "melanoma", "malignant", "cellulitis", "severe"] | |
| medium_severity_keywords = ["infection", "inflammatory", "chronic", "dermatitis", "eczema", "psoriasis"] | |
| lower_name = disease_name.lower() | |
| for keyword in high_severity_keywords: | |
| if keyword in lower_name: | |
| return "high" | |
| for keyword in medium_severity_keywords: | |
| if keyword in lower_name: | |
| return "medium" | |
| return "low" | |
| # Initialize the system | |
| try: | |
| diagnosis_system = MedicalSkinDiagnosisSystem() | |
| system_ready = True | |
| except Exception as e: | |
| logger.error(f"Failed to initialize system: {e}") | |
| diagnosis_system = None | |
| system_ready = False | |
| def perform_diagnosis(image: Image.Image) -> Tuple[str, str]: | |
| """ | |
| Main diagnosis function for Gradio interface. | |
| Returns: | |
| Tuple of (diagnosis_result, gpt_analysis) | |
| """ | |
| if not system_ready or diagnosis_system is None: | |
| return "โ ์์คํ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค / System unavailable", "" | |
| if image is None: | |
| return "โ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํด์ฃผ์ธ์ / Please upload an image", "" | |
| try: | |
| class_probs, formatted_output, gpt_analysis = diagnosis_system.diagnose(image) | |
| if not class_probs: | |
| return formatted_output, "" | |
| return formatted_output, gpt_analysis | |
| except Exception as e: | |
| logger.error(f"Diagnosis error: {e}") | |
| return f"โ ์ฒ๋ฆฌ ์คํจ / Processing failed: {str(e)}", "" | |
| def create_medical_interface(): | |
| """Create medical-grade Gradio interface.""" | |
| with gr.Blocks( | |
| title="AI ํผ๋ถ ์ง๋จ ์์คํ / AI Skin Diagnosis System", | |
| theme=gr.themes.Soft(), | |
| css=medical_css | |
| ) as interface: | |
| # Header | |
| gr.HTML(""" | |
| <div class="main-header"> | |
| <h1 style="text-align: center; margin: 0;"> | |
| ๐ฅ AI ํผ๋ถ ์งํ ์ง๋จ ์์คํ <br> | |
| <span style="font-size: 0.6em; opacity: 0.9;"> | |
| AI-Powered Skin Disease Diagnosis System | |
| </span> | |
| </h1> | |
| <p style="text-align: center; margin-top: 1rem; opacity: 0.9;"> | |
| GPT-OSS 120B ๋ชจ๋ธ ํตํฉ ์ง๋จ / Integrated with GPT-OSS 120B Model | |
| </p> | |
| </div> | |
| """) | |
| # System status | |
| with gr.Row(): | |
| gr.HTML(f""" | |
| <div style="display: flex; justify-content: center; gap: 20px; margin: 1rem 0;"> | |
| <span class="medical-badge"> | |
| <span class="status-indicator status-ready"></span> | |
| ์์คํ ์ํ: {"์ ์" if system_ready else "์ค๋ฅ"} / System: {"Ready" if system_ready else "Error"} | |
| </span> | |
| <span class="medical-badge"> | |
| Vision Model: {diagnosis_system.model_name if diagnosis_system else "N/A"} | |
| </span> | |
| <span class="medical-badge"> | |
| GPT-OSS: 120B Model | |
| </span> | |
| <span class="medical-badge"> | |
| GPU: {"ํ์ฑํ" if torch.cuda.is_available() else "๋นํ์ฑํ"} / {"Enabled" if torch.cuda.is_available() else "Disabled"} | |
| </span> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| # Input section | |
| gr.Markdown("### ๐ธ ์ด๋ฏธ์ง ์ ๋ก๋ / Image Upload") | |
| image_input = gr.Image( | |
| type="pil", | |
| label="ํผ๋ถ ์ด๋ฏธ์ง ์ ๋ก๋ / Upload Skin Image", | |
| height=400 | |
| ) | |
| with gr.Row(): | |
| diagnose_btn = gr.Button( | |
| "๐ฌ AI ์ง๋จ ์์ / Start AI Diagnosis", | |
| variant="primary", | |
| size="lg", | |
| elem_classes="button-primary" | |
| ) | |
| clear_btn = gr.Button( | |
| "๐ ์ด๊ธฐํ / Clear", | |
| variant="secondary", | |
| size="lg" | |
| ) | |
| # Advanced settings | |
| with gr.Accordion("โ๏ธ ๊ณ ๊ธ ์ค์ / Advanced Settings", open=False): | |
| confidence_threshold = gr.Slider( | |
| minimum=0.1, | |
| maximum=0.9, | |
| value=0.5, | |
| step=0.1, | |
| label="์ต์ ์ ๋ขฐ๋ ์๊ณ๊ฐ / Minimum Confidence Threshold" | |
| ) | |
| analysis_depth = gr.Radio( | |
| choices=["๊ธฐ๋ณธ / Basic", "์์ธ / Detailed", "์ ๋ฌธ๊ฐ / Expert"], | |
| value="์์ธ / Detailed", | |
| label="๋ถ์ ๊น์ด / Analysis Depth" | |
| ) | |
| with gr.Column(scale=2): | |
| # Results section | |
| gr.Markdown("### ๐ ์ง๋จ ๊ฒฐ๊ณผ / Diagnosis Results") | |
| diagnosis_output = gr.HTML( | |
| label="Vision Model ์ง๋จ / Vision Model Diagnosis", | |
| value="<div class='info-box'>์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ๊ณ ์ง๋จ์ ์์ํ์ธ์.<br>Upload an image and start diagnosis.</div>" | |
| ) | |
| gpt_analysis = gr.HTML( | |
| label="GPT-OSS ๋ถ์ / GPT-OSS Analysis", | |
| value="" | |
| ) | |
| # Example images with proper paths | |
| gr.Markdown("### ๐ ์์ ์ด๋ฏธ์ง / Example Images") | |
| # Define the actual example images that were uploaded | |
| example_images = [ | |
| ["andrea.jpeg"], | |
| ["clare.jpeg"], | |
| ["disorder.jpeg"], | |
| ["joe.jpeg"], | |
| ["woman.jpeg"] | |
| ] | |
| # Check if images exist and create examples | |
| available_examples = [] | |
| for img_path in example_images: | |
| # Check various possible locations | |
| possible_paths = [ | |
| img_path[0], # Current directory | |
| f"examples/{img_path[0]}", # examples folder | |
| f"./{img_path[0]}", # Explicit current directory | |
| f"./examples/{img_path[0]}" # Explicit examples folder | |
| ] | |
| for path in possible_paths: | |
| if Path(path).exists(): | |
| available_examples.append([path]) | |
| break | |
| if available_examples: | |
| gr.Examples( | |
| examples=available_examples, | |
| inputs=[image_input], | |
| label="์ํ ํผ๋ถ ์ด๋ฏธ์ง / Sample Skin Images", | |
| examples_per_page=5 | |
| ) | |
| else: | |
| # If images not found in expected locations, show instructions | |
| gr.HTML(""" | |
| <div class="info-box"> | |
| <p><strong>๐ธ ์ํ ์ด๋ฏธ์ง ์์น / Sample Image Location:</strong></p> | |
| <p>๋ค์ ์ด๋ฏธ์ง๋ค์ด ํ๋ก์ ํธ ํด๋์ ์์ด์ผ ํฉ๋๋ค / The following images should be in your project folder:</p> | |
| <ul> | |
| <li>andrea.jpeg (57.5 KB)</li> | |
| <li>clare.jpeg (49.8 KB)</li> | |
| <li>disorder.jpeg (55.5 KB)</li> | |
| <li>joe.jpeg (85.5 KB)</li> | |
| <li>woman.jpeg</li> | |
| </ul> | |
| <p style="margin-top: 1rem;"> | |
| <strong>์ด๋ฏธ์ง๋ฅผ ํ๋ก์ ํธ ๋ฃจํธ ๋๋ examples ํด๋์ ๋ฐฐ์นํ์ธ์</strong><br> | |
| <em>Place images in project root or examples folder</em> | |
| </p> | |
| </div> | |
| """) | |
| # Event handlers | |
| diagnose_btn.click( | |
| fn=perform_diagnosis, | |
| inputs=[image_input], | |
| outputs=[diagnosis_output, gpt_analysis] | |
| ) | |
| clear_btn.click( | |
| fn=lambda: (None, | |
| "<div class='info-box'>์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ๊ณ ์ง๋จ์ ์์ํ์ธ์.<br>Upload an image and start diagnosis.</div>", | |
| ""), | |
| inputs=[], | |
| outputs=[image_input, diagnosis_output, gpt_analysis] | |
| ) | |
| image_input.change( | |
| fn=lambda x: ("<div class='info-box'>์ง๋จ ๋ฒํผ์ ํด๋ฆญํ์ธ์.<br>Click the diagnosis button.</div>", "") if x else ("", ""), | |
| inputs=[image_input], | |
| outputs=[diagnosis_output, gpt_analysis] | |
| ) | |
| # Footer | |
| gr.HTML(""" | |
| <div style="margin-top: 3rem; padding: 2rem; background: rgba(255,255,255,0.9); border-radius: 15px;"> | |
| <h4 style="color: #2d3748;">๐ ์๋ฃ ๋ฉด์ฑ ์กฐํญ / Medical Disclaimer</h4> | |
| <p style="color: #4a5568; line-height: 1.6;"> | |
| ๋ณธ ์์คํ ์ AI ๊ธฐ๋ฐ ๋ณด์กฐ ๋๊ตฌ์ด๋ฉฐ, ์ํ์ ์ง๋จ์ ๋์ฒดํ์ง ์์ต๋๋ค. | |
| ๋ชจ๋ ๊ฒฐ๊ณผ๋ ์ฐธ๊ณ ์ฉ์ด๋ฉฐ, ์ ํํ ์ง๋จ๊ณผ ์น๋ฃ๋ฅผ ์ํด์๋ ๋ฐ๋์ ์๋ฃ ์ ๋ฌธ๊ฐ์ ์๋ดํ์๊ธฐ ๋ฐ๋๋๋ค.<br><br> | |
| <em>This system is an AI-based assistive tool and does not replace medical diagnosis. | |
| All results are for reference only. Please consult with healthcare professionals for accurate diagnosis and treatment.</em> | |
| </p> | |
| <hr style="margin: 1rem 0; opacity: 0.3;"> | |
| <p style="text-align: center; color: #718096; font-size: 0.9rem;"> | |
| Powered by Vision Transformer & GPT-OSS 120B | ยฉ 2024 Medical AI Systems | |
| </p> | |
| </div> | |
| """) | |
| return interface | |
| # Health check function | |
| def health_check() -> Dict[str, str]: | |
| """System health check for monitoring.""" | |
| return { | |
| "status": "healthy" if system_ready else "unhealthy", | |
| "vision_model": diagnosis_system.model_name if diagnosis_system else "not_loaded", | |
| "gpt_model": "gpt-oss-120b", | |
| "classes": len(diagnosis_system.id2label) if diagnosis_system else 0, | |
| "device": diagnosis_system.device if diagnosis_system else "unknown", | |
| "gpu_available": str(torch.cuda.is_available()) | |
| } | |
| # Main execution | |
| if __name__ == "__main__": | |
| try: | |
| app = create_medical_interface() | |
| # Launch with HuggingFace Spaces configuration | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| show_error=True | |
| ) | |
| except Exception as e: | |
| logger.error(f"Failed to launch interface: {e}") | |
| print(f"โ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์คํจ / Application failed to start: {e}") | |
| # Export key functions | |
| __all__ = ['MedicalSkinDiagnosisSystem', 'perform_diagnosis', 'health_check'] |