import cv2 import onnxruntime import numpy as np import gradio as gr from typing import List, Tuple class YOLOv9: def __init__(self, model_path: str, classes: List[str], original_size: Tuple[int, int] = (640, 640), device: str = "CPU") -> None: self.model_path = model_path self.classes = classes self.device = device self.image_width, self.image_height = original_size self.create_session() def create_session(self) -> None: self.session = onnxruntime.InferenceSession(self.model_path, providers=['CPUExecutionProvider']) self.input_shape = self.session.get_inputs()[0].shape self.input_height, self.input_width = self.input_shape[2:4] self.color_palette = np.random.uniform(0, 255, size=(len(self.classes), 3)) def preprocess(self, img: np.ndarray) -> np.ndarray: image_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) resized = cv2.resize(image_rgb, (self.input_width, self.input_height)) input_image = resized / 255.0 return input_image.transpose(2, 0, 1)[np.newaxis, :, :, :].astype(np.float32) def xywh2xyxy(self, x): y = np.copy(x) y[..., 0] = x[..., 0] - x[..., 2] / 2 # x_min y[..., 1] = x[..., 1] - x[..., 3] / 2 # y_min y[..., 2] = x[..., 0] + x[..., 2] / 2 # x_max y[..., 3] = x[..., 1] + x[..., 3] / 2 # y_max return y def postprocess(self, outputs, score_threshold: float, iou_threshold: float): predictions = np.squeeze(outputs).T scores = np.max(predictions[:, 4:], axis=1) predictions = predictions[scores > score_threshold, :] scores = scores[scores > score_threshold] class_ids = np.argmax(predictions[:, 4:], axis=1) boxes = predictions[:, :4] boxes = self.xywh2xyxy(boxes) boxes[:, [0, 2]] *= self.image_width / self.input_width boxes[:, [1, 3]] *= self.image_height / self.input_height boxes = boxes.astype(np.int32) detections = [] indices = cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(), score_threshold, iou_threshold) if len(indices) > 0: indices = indices.flatten() for i in indices: bbox = boxes[i] detections.append({ "class_index": class_ids[i], "confidence": scores[i], "box": bbox, "class_name": self.classes[class_ids[i]] }) return detections def detect(self, img: np.ndarray, score_threshold: float, iou_threshold: float) -> List: input_tensor = self.preprocess(img) outputs = self.session.run(None, {self.session.get_inputs()[0].name: input_tensor})[0] return self.postprocess(outputs, score_threshold, iou_threshold) def draw_detections(self, img, detections: List): h, w = img.shape[:2] font_scale = max(w, h) / 1000 for detection in detections: x1, y1, x2, y2 = detection['box'] class_id = detection['class_index'] confidence = detection['confidence'] color = self.color_palette[class_id] cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) label = f"{self.classes[class_id]}: {confidence:.2f}" text_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, font_scale, 2)[0] text_w, text_h = text_size cv2.rectangle(img, (x1, y1 - text_h - 5), (x1 + text_w, y1), color, -1) cv2.putText(img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 255, 255), 2) def process_image(image: np.ndarray, score_threshold: float, iou_threshold: float) -> np.ndarray: if image is None: print("Tidak ada gambar yang diunggah.") return None weight_path = "best.onnx" classes = ["RBC", "WBC", "Difficult", "P-Falciparum", "P-Malariae", "P-Ovale", "P-Vivax"] detector = YOLOv9(model_path=weight_path, classes=classes, original_size=(image.shape[1], image.shape[0])) detections = detector.detect(image, score_threshold, iou_threshold) detector.draw_detections(image, detections) return image # Gradio interface iface = gr.Interface( fn=process_image, inputs=[ gr.Image(type="numpy", label="Upload an image"), gr.Slider(0.1, 1.0, value=0.1, step=0.05, label="Score Threshold"), gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="IOU Threshold"), ], outputs=gr.Image(type="numpy", label="Detected Image"), live=True ) iface.launch()