Ari1009/dicom
0
1#ref: https://huggingface.co/spaces/Prgckwb/dicom-viewer/blob/main/app.py2#ref: https://huggingface.co/spaces/basilshaji/Lung_Nodule_Segmentation3 4import gradio as gr5import numpy as np6import polars as pl7import pydicom8from PIL import Image9from pydicom.errors import InvalidDicomError10 11import gradio as gr12import cv213import requests14import os15import torch16import numpy as np17from yolov5.models.experimental import attempt_load18from yolov5.utils.general import non_max_suppression19from yolov5.utils.augmentations import letterbox20 21# Load YOLOv5 model (placeholder)22model_path = "best.pt" # Path to your YOLOv5 model23device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # Use GPU if available24model = attempt_load(model_path, device=device) # Placeholder for model loading25model.eval() # Set the model to evaluation mode26 27def preprocess_image(image):28 img = letterbox(image, 640, stride=32, auto=True)[0] # Resize and pad to 640x64029 img = img.transpose(2, 0, 1)[::-1] # Convert BGR to RGB, 30 img = np.ascontiguousarray(img)31 img = torch.from_numpy(img).to(device)32 img = img.float() # uint8 to fp16/3233 img /= 255.0 # 0 - 255 to 0.0 - 1.034 if img.ndimension() == 3:35 img = img.unsqueeze(0)36 37 return img, image38 39def infer(model, img):40 with torch.no_grad():41 pred = model(img)[0]42 return pred43 44def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None):45 if ratio_pad is None: # calculate from img0_shape46 gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new47 pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding48 else:49 gain = ratio_pad[0]50 pad = ratio_pad[1]51 52 coords[:, [0, 2]] -= pad[0] # x padding53 coords[:, [1, 3]] -= pad[1] # y padding54 coords[:, :4] /= gain55 coords[:, :4].clip_(min=0, max=img1_shape[0]) # clip boxes56 return coords57 58def postprocess(pred, img0, img): 59 pred = non_max_suppression(pred, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False)60 results = []61 for det in pred: # detections per image62 if len(det):63 det[:, :4] = scale_coords(img.shape[2:], det[:, :4], img0.shape).round()64 for *xyxy, conf, cls in reversed(det):65 results.append((xyxy, conf, cls))66 return results67 68def detect_objects(image_path):69 dicom_image, dicom_meta = read_and_preprocess_dicom(image_path)70 img, img0 = preprocess_image(dicom_image)71 pred = infer(model, img)72 results = postprocess(pred, dicom_image, img)73 return results, dicom_image, dicom_meta74 75def draw_bounding_boxes(img, results, dicom_meta):76 dets = []77 for (x1, y1, x2, y2), conf, cls in results:78 zc = dicom_meta.loc[dicom_meta.Key == 'Instance Number', 'Value'].iloc[0]79 x1, y1, x2, y2, zc, cls = map(int, [x1, y1, x2, y2, zc, cls])80 xc = x1+(x2-x1)/281 yc = y1+(y2-y1)/282 conf = round(conf.detach().item(), 4)83 84 dets.append([(xc, yc, zc), conf, cls])85 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2)86 cv2.putText(img, f'{model.names[int(cls)]} {conf:.2f}', (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36, 255, 12), 2)87 return img, dets88 89def show_preds_image(filepath):90 results, img0, dicom_meta = detect_objects(filepath)91 img_with_boxes, results = draw_bounding_boxes(img0, results, dicom_meta)92 print("Detections:", dicom_meta.loc[dicom_meta.Key == 'Series Instance UID', 'Value'].iloc[0], results)93 return cv2.cvtColor(img_with_boxes, cv2.COLOR_BGR2RGB), results, dicom_meta94 95def read_and_preprocess_dicom(file_path: str):96 """97 Function to read and preprocess DICOM files98 :param file_path: Path to the DICOM file99 :return: Image data (in CV2 format) and metadata (in pandas DataFrame format)100 """101 try:102 # Read the DICOM file103 dicom_data = pydicom.dcmread(file_path)104 except InvalidDicomError:105 raise gr.Error("The uploaded file is not a valid DICOM file.")106 107 # Get the pixel data108 try:109 pixel_array = dicom_data.pixel_array110 except AttributeError:111 raise gr.Error("The uploaded DICOM file has no pixel data.")112 113 # Normalize the pixel data to 8-bit and convert to a PIL image114 if pixel_array.dtype != np.uint8:115 pixel_array = ((pixel_array - np.min(pixel_array)) / (np.max(pixel_array) - np.min(pixel_array)) * 255).astype(116 np.uint8)117 image_pil = Image.fromarray(pixel_array)118 119 image = image_pil.convert('RGB')120 121 image = np.array(image)[:,:,::-1].copy()122 123 # Collect metadata in dictionary format and convert to DataFrame124 metadata_dict = {elem.name: str(elem.value) for elem in dicom_data.iterall() if elem.name != 'Pixel Data'}125 df_metadata = pl.DataFrame({126 "Key": list(metadata_dict.keys()),127 "Value": list(metadata_dict.values())128 })129 130 return image, df_metadata.to_pandas() # Convert to pandas DataFrame for Gradio compatibility131 132 133# Define Gradio components134input_component = gr.File(label="Input DICOM Data")135dicom_image = gr.Image(type="numpy", label="Output Image")136dicom_meta = gr.Dataframe(headers=None, label="Metadata")137dets_res = gr.Text(label="Detections")138output_component = [dicom_image, dets_res, dicom_meta]139 140# Create Gradio interface141interface = gr.Interface(142 fn=show_preds_image,143 inputs=input_component,144 outputs=output_component,145 title="Lung Nodule Detection",146 examples=['samples/110_109.dcm','samples/189_188.dcm'],147 description= "This online deployment proves the effectiveness and efficient function of the machine learning model in identifying lung cancer nodules.",148 live=False,149)150 151interface.launch(share=True)152 