CoolFace
Apppublic

developer0hye/D-FINE

sourceHugging Facemitupdated 2y agoView on Hugging Face
3likes
app.py301 linesDownload Raw Back to root
1"""2Copyright (c) 2024 The D-FINE Authors. All Rights Reserved.3"""4import gradio as gr5import spaces6import os7import sys8import torch9import torch.nn as nn10import torchvision.transforms as T11import supervision as sv12from PIL import Image13import requests14import yaml15import numpy as np16import gc17 18from src.core import YAMLConfig19 20 21model_configs = {22    "dfine_n_coco":23        {"cfgfile": "configs/dfine/dfine_hgnetv2_n_coco.yml",24         "classinfofile": "configs/coco.yml",25         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_n_coco.pth"},26    "dfine_s_coco":27        {"cfgfile": "configs/dfine/dfine_hgnetv2_s_coco.yml",28         "classinfofile": "configs/coco.yml",29         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_s_coco.pth"},30    "dfine_m_coco":31        {"cfgfile": "configs/dfine/dfine_hgnetv2_m_coco.yml",32         "classinfofile": "configs/coco.yml",33         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_m_coco.pth"},34    "dfine_l_coco":35        {"cfgfile": "configs/dfine/dfine_hgnetv2_l_coco.yml",36         "classinfofile": "configs/coco.yml",37         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_l_coco.pth"},38    "dfine_x_coco":39        {"cfgfile": "configs/dfine/dfine_hgnetv2_x_coco.yml",40         "classinfofile": "configs/coco.yml",41         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_x_coco.pth"},42    "dfine_s_obj365":43        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_s_obj365.yml",44         "classinfofile": "configs/obj365.yml",45         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_s_obj365.pth"},46    "dfine_m_obj365":47        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_m_obj365.yml",48         "classinfofile": "configs/obj365.yml",49         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_m_obj365.pth"},50    "dfine_l_obj365":51        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_l_obj365.yml",52         "classinfofile": "configs/obj365.yml",53         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_l_obj365.pth"},54    "dfine_l_obj365_e25":55        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_l_obj365.yml",56         "classinfofile": "configs/obj365.yml",57         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_l_obj365_e25.pth"},58    "dfine_x_obj365":59        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_x_obj365.yml",60         "classinfofile": "configs/obj365.yml",61         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_x_obj365.pth"},62    "dfine_s_obj2coco":63        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_s_obj2coco.yml",64         "classinfofile": "configs/coco.yml",65         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_s_obj2coco.pth"},66    "dfine_m_obj2coco":67        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_m_obj2coco.yml",68         "classinfofile": "configs/coco.yml",69         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_m_obj2coco.pth"},70    "dfine_l_obj2coco_e25":71        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_l_obj2coco.yml",72         "classinfofile": "configs/coco.yml",73         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_l_obj2coco_e25.pth"},74    "dfine_x_obj2coco":75        {"cfgfile": "configs/dfine/objects365/dfine_hgnetv2_x_obj2coco.yml",76         "classinfofile": "configs/coco.yml",77         "weights": "https://github.com/Peterande/storage/releases/download/dfinev1.0/dfine_x_obj2coco.pth"},78}79 80 81def download_weights(model_name):82    """Download model weights if not already present"""83    weights_url = model_configs[model_name]["weights"]84    # Directory path to save weight files85    weights_dir = os.path.join(os.path.dirname(__file__), "weights")86    # Weight file path87    weights_path = os.path.join(weights_dir, model_name + ".pth")88    89    # Create weights directory if it doesn't exist90    if not os.path.exists(weights_dir):91        os.makedirs(weights_dir)92        print(f"Created directory: {weights_dir}")93    94    # Check if file already exists95    if os.path.exists(weights_path):96        print(f"Weights file already exists at: {weights_path}")97        return weights_path98    99    # Download file100    print(f"Downloading weights from {weights_url} to {weights_path}...")101    102    response = requests.get(weights_url, stream=True)103    response.raise_for_status()  # Check for download errors104    105    with open(weights_path, 'wb') as f:106        for chunk in response.iter_content(chunk_size=8192):107            f.write(chunk)108    109    print(f"Downloaded weights to: {weights_path}")110    return weights_path111 112@torch.no_grad()113def process_image_for_gradio(model, device, image, model_name, threshold=0.4):114    """Process image function for Gradio interface"""115    if isinstance(image, np.ndarray):116        # Convert NumPy array to PIL image117        im_pil = Image.fromarray(image)118    else:119        im_pil = image120    121    # Load class information122    classinfofile = model_configs[model_name]["classinfofile"]123    classinfo = yaml.load(open(classinfofile, "r"), Loader=yaml.FullLoader)["names"]124    indexing_method = "0-based" if "coco" in classinfofile else "1-based"125    126    w, h = im_pil.size127    orig_size = torch.tensor([[w, h]]).to(device)128 129    transforms = T.Compose(130        [131            T.Resize((640, 640)),132            T.ToTensor(),133        ]134    )135    im_data = transforms(im_pil).unsqueeze(0).to(device)136 137    output = model(im_data, orig_size)138    labels, boxes, scores = output139 140    # Visualize results141    detections = sv.Detections(142        xyxy=boxes[0].detach().cpu().numpy(),143        confidence=scores[0].detach().cpu().numpy(),144        class_id=labels[0].detach().cpu().numpy().astype(int),145    )146    detections = detections[detections.confidence > threshold]147 148    text_scale = sv.calculate_optimal_text_scale(resolution_wh=im_pil.size)149    line_thickness = sv.calculate_optimal_line_thickness(resolution_wh=im_pil.size)150 151    box_annotator = sv.BoxAnnotator(thickness=line_thickness)152    label_annotator = sv.LabelAnnotator(text_scale=text_scale, smart_position=True)153 154    label_texts = [155        f"{classinfo[class_id if indexing_method == '0-based' else class_id - 1]} {confidence:.2f}"156        for class_id, confidence157        in zip(detections.class_id, detections.confidence)158    ]159 160    result_image = im_pil.copy()161    result_image = box_annotator.annotate(scene=result_image, detections=detections)162    result_image = label_annotator.annotate(163        scene=result_image,164        detections=detections,165        labels=label_texts166    )167 168    detection_info = [169        f"{classinfo[class_id if indexing_method == '0-based' else class_id - 1]}: {confidence:.2f}, bbox: [{xyxy[0]:.1f}, {xyxy[1]:.1f}, {xyxy[2]:.1f}, {xyxy[3]:.1f}]"170        for class_id, confidence, xyxy171        in zip(detections.class_id, detections.confidence, detections.xyxy)172    ]173    174    return result_image, "\n".join(detection_info)175 176 177class ModelWrapper(nn.Module):178    def __init__(self, cfg):179        super().__init__()180        self.model = cfg.model.deploy()181        self.postprocessor = cfg.postprocessor.deploy()182 183    def forward(self, images, orig_target_sizes):184        outputs = self.model(images)185        outputs = self.postprocessor(outputs, orig_target_sizes)186        return outputs187 188 189# YAMLConfig 클래스의 내부 상태를 초기화하는 함수 추가190def reset_yaml_config():191    """YAMLConfig 클래스의 내부 상태를 초기화"""192    # 클래스 내부에 캐싱된 정보가 있다면 삭제193    if hasattr(YAMLConfig, '_instances'):194        YAMLConfig._instances = {}195    if hasattr(YAMLConfig, '_configs'):196        YAMLConfig._configs = {}197    198    # 가능한 다른 모든 모듈 캐시 리셋199    import importlib200    for module_name in list(sys.modules.keys()):201        if module_name.startswith('src.'):202            try:203                importlib.reload(sys.modules[module_name])204            except:205                pass206 207def load_model(model_name):208    # 모델 로드 전에 CUDA 캐시와 가비지 컬렉션 정리209    if torch.cuda.is_available():210        torch.cuda.empty_cache()211    gc.collect()212    213    # YAMLConfig 내부 상태 초기화214    reset_yaml_config()215    216    cfgfile = model_configs[model_name]["cfgfile"]217    weights_path = download_weights(model_name)218    219    # 완전히 새로운 YAMLConfig 인스턴스 생성220    cfg = YAMLConfig(cfgfile, resume=weights_path)221 222    if "HGNetv2" in cfg.yaml_cfg:223        cfg.yaml_cfg["HGNetv2"]["pretrained"] = False224 225    checkpoint = torch.load(weights_path, map_location="cpu")226    state = checkpoint["ema"]["module"] if "ema" in checkpoint else checkpoint["model"]227    228    # 모델 생성 전 한번 더 확인229    torch.cuda.empty_cache()230    gc.collect()231    232    cfg.model.load_state_dict(state, strict=False)233 234    device = "cuda" if torch.cuda.is_available() else "cpu"235    model = ModelWrapper(cfg).to(device)236    model.eval()237 238    return model, device239 240@spaces.GPU241def process_image(image, model_name, confidence_threshold):242    """Main processing function for Gradio interface"""243    244    # 모든 사용 가능한 CUDA 장치 메모리 확보245    if torch.cuda.is_available():246        torch.cuda.empty_cache()247    248    # 모든 Python 객체 가비지 컬렉션249    gc.collect()250    251    try:252        print(f"Loading model: {model_name}")253        model, device = load_model(model_name)254        255        # 이미지 처리256        result = process_image_for_gradio(model, device, image, model_name, confidence_threshold)257        258        # 모델 객체 및 관련 데이터 명시적 제거259        del model260        261    finally:262        # 항상 메모리 정리 보장263        if torch.cuda.is_available():264            torch.cuda.empty_cache()265        gc.collect()266    267    return result268 269 270# Create Gradio interface271demo = gr.Interface(272    fn=process_image,273    inputs=[274        gr.Image(type="pil", label="Input Image"),275        gr.Dropdown(276            choices=list(model_configs.keys()), 277            value="dfine_n_coco", 278            label="Model Selection"279        ),280        gr.Slider(281            minimum=0.1, 282            maximum=0.9, 283            value=0.4, 284            step=0.05, 285            label="Confidence Threshold"286        )287    ],288    outputs=[289        gr.Image(type="pil", label="Detection Result"),290        gr.Textbox(label="Detected Objects")291    ],292    title="D-FINE Object Detection Demo",293    description="Upload an image to see object detection results using the D-FINE model. You can select different models and adjust the confidence threshold.",294    examples=[295        ["examples/image1.jpg", "dfine_n_coco", 0.4],296    ]297)298 299if __name__ == "__main__":300    # Launch the Gradio app301    demo.launch(share=True)