import gradio as gr import cv2 import numpy as np from PIL import Image from ultralytics import YOLO from paddleocr import PaddleOCR import re import os # --- Inisialisasi Model --- print("Mulai inisialisasi model...") # Inisialisasi YOLO try: print("Memuat model YOLOv8 ('best2.pt')...") yolo_model = YOLO('best2.pt') # Ganti path jika modelmu di folder lain print("Model YOLOv8 berhasil dimuat.") except Exception as e: print(f"Error saat memuat model kustom 'best2.pt': {e}") print("Mencoba memuat model YOLOv8 standar ('yolov8n-obb.pt')...") yolo_model = YOLO('yolov8n-obb.pt') print("Model YOLOv8 standar berhasil dimuat.") # Inisialisasi PaddleOCR ocr_engine = None try: print("Memuat model PaddleOCR...") ocr_engine = PaddleOCR( use_angle_cls=True, lang='en', det_db_box_thresh=0.3, show_log=False ) print("Model PaddleOCR berhasil dimuat.") except Exception as e: print("!!! Gagal memuat PaddleOCR:", e) def find_plate_in_text(text): cleaned = re.sub(r'[^A-Z0-9]', '', text.upper()) match = re.match(r'^([A-Z]{1,2})(\d{1,4})([A-Z]{1,3})', cleaned) if match: return f"{match.group(1)} {match.group(2)} {match.group(3)}" return f"[Tidak Valid]: {cleaned}" def detect_crop_and_ocr(image, yolo, ocr, margin=10): print("-> Mulai deteksi...") image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) if isinstance(image, Image.Image) else image if image_cv is None or image_cv.size == 0: return None, None, "Gambar tidak valid." results = yolo(image_cv, task='obb')[0] img_annotated_rgb = cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB) crop_rgb = None ocr_text = "Plat tidak terdeteksi." if len(results.obb.xyxyxyxy) > 0: obb = results.obb.xyxyxyxy[0].cpu().numpy().reshape((4, 2)) xs, ys = obb[:, 0], obb[:, 1] x1, x2 = int(xs.min()), int(xs.max()) y1, y2 = int(ys.min()), int(ys.max()) h, w = image_cv.shape[:2] x1m, y1m = max(0, x1 - margin), max(0, y1 - margin) x2m, y2m = min(w, x2 + margin), min(h, y2 + margin) cv2.rectangle(img_annotated_rgb, (x1m, y1m), (x2m, y2m), (0, 255, 0), 3) crop_cv = image_cv[y1m:y2m, x1m:x2m] if crop_cv.size > 0 and ocr is not None: h, w = crop_cv.shape[:2] crop_resized = cv2.resize(crop_cv, (w * 2, h * 2), interpolation=cv2.INTER_CUBIC) blurred = cv2.GaussianBlur(crop_resized, (0, 0), 3) sharpened = cv2.addWeighted(crop_resized, 1.5, blurred, -0.5, 0) crop_rgb = cv2.cvtColor(sharpened, cv2.COLOR_BGR2RGB) try: ocr_results = ocr.ocr(crop_rgb, cls=True) if ocr_results and ocr_results[0]: texts = [line[1][0] for line in ocr_results[0] if line and line[1]] raw_text = "".join(texts) ocr_text = find_plate_in_text(raw_text) else: ocr_text = "OCR tidak dapat membaca teks." except Exception as e: ocr_text = f"Error OCR: {e}" else: print("-> Tidak ada plat yang terdeteksi.") status_text = "Plat Terdeteksi" if crop_rgb is not None else "Plat Tidak Terdeteksi" cv2.putText(img_annotated_rgb, status_text, (15, 40), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 0, 0), 3) return img_annotated_rgb, crop_rgb, ocr_text def process_image_for_gradio(uploaded_image): print("\n==============================") print("Request baru diterima...") if ocr_engine is None: placeholder_img = np.zeros((300, 500, 3), dtype=np.uint8) cv2.putText(placeholder_img, "OCR Engine Gagal", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2) return placeholder_img, None, "ERROR: OCR Engine gagal dimuat." if uploaded_image is None: return None, None, "Mohon unggah gambar terlebih dahulu." try: full_img, crop_img, text = detect_crop_and_ocr(uploaded_image, yolo_model, ocr_engine) if crop_img is None: crop_img = np.zeros((100, 300, 3), dtype=np.uint8) cv2.putText(crop_img, "Tidak ada plat", (40, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2) return full_img, crop_img, text except Exception as e: error_msg = f"Terjadi kesalahan: {e}" placeholder_img = np.zeros((300, 500, 3), dtype=np.uint8) return placeholder_img, None, error_msg # === INTERFACE GRADIO === example_img_path = "AB2638XU.jpg" example_img = None if os.path.exists(example_img_path): example_img = Image.open(example_img_path) with gr.Blocks(theme=gr.themes.Soft()) as iface: gr.Markdown(""" # 🚗 Deteksi & OCR Plat Nomor Kendaraan Indonesia Unggah gambar kendaraan, deteksi plat dengan YOLOv8 OBB, dan baca teks dengan PaddleOCR. """) with gr.Row(): with gr.Column(scale=2): image_input = gr.Image(type="pil", label="Unggah Gambar", value=example_img) submit_button = gr.Button("Proses", variant="primary") with gr.Column(scale=3): image_output = gr.Image(type="numpy", label="Hasil Deteksi") with gr.Row(): crop_output = gr.Image(type="numpy", label="Plat Terpotong") text_output = gr.Textbox(label="Teks Plat Nomor") submit_button.click( fn=process_image_for_gradio, inputs=image_input, outputs=[image_output, crop_output, text_output] ) if __name__ == '__main__': iface.launch(debug=True)