from fastapi import FastAPI, UploadFile, File import numpy as np import cv2 import tensorflow as tf from PIL import Image import io app = FastAPI() # Load model Keras model = tf.keras.models.load_model("lontara_model_finetuning.keras") # Label kelas sesuai model labels = [ "Tu", "He", "We", "No", "Mu", "Bu", "Ji", "Jo", "I", "Nro", "Cu", "Na", "Bo", "Yi", "Se", "Nyi", "So", "Wa", "Ko", "Ge", "E", "Yo", "Ngu", "Ra", "Wo", "Ta", "Pe", "Nra", "Da", "Ci", "Lo", "Nci", "U", "Ro", "Mo", "Nre", "Du", "Be", "Mpu", "Hu", "Ne", "Nyo", "Ncu", "Su", "Ju", "Gu", "Nu", "Di", "Nri", "Gi", "Co", "Nca", "Ri", "Si", "Ja", "Bi", "Ke", "Wu", "Nki", "Te", "Go", "Ya", "Nku", "Pu", "Nka", "Ba", "Mpe", "A", "Nya", "Me", "Nge", "Mpa", "Ma", "Mpi", "O", "Mi", "Re", "Po", "Ti", "Je", "Nco", "Pa", "Ho", "Nko", "Ce", "Li", "Nke", "Ru", "Ca", "Ke_", "Do", "Ga", "Mpo", "Nye", "Nru", "Nga", "Lu", "Pi", "Ku", "Ni", "Nce", "Le", "Ngo", "De", "Ki", "Wi", "Hi", "Ye", "Ngi", "Ka", "Nyu", "La", "Ha", "Sa" ] @app.get("/") def home(): return {"message": "Aksara Lontara API is running"} def preprocess_image(image: np.ndarray): """ Melakukan segmentasi karakter menggunakan OpenCV """ # **1️⃣ Edge Detection (Canny)** edges = cv2.Canny(image, 50, 150) # **2️⃣ Morphological Cleaning** kernel = np.ones((3, 3), np.uint8) edges_cleaned = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel, iterations=2) # **3️⃣ Connected Component Analysis (CCA)** num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(edges_cleaned, connectivity=8) # **4️⃣ Filter huruf berdasarkan area** min_area = 500 bounding_boxes = [] for i in range(1, num_labels): # Skip background x, y, w, h, area = stats[i] if area > min_area: bounding_boxes.append((x, y, w, h)) # **5️⃣ Urutkan huruf berdasarkan posisi X** bounding_boxes.sort(key=lambda b: b[0]) # **6️⃣ Gabungkan Bounding Box yang Berdekatan** merged_boxes = [] merge_threshold = 20 for i in range(len(bounding_boxes)): x, y, w, h = bounding_boxes[i] if merged_boxes and (x - (merged_boxes[-1][0] + merged_boxes[-1][2])) < merge_threshold: x_prev, y_prev, w_prev, h_prev = merged_boxes.pop() x_new = min(x_prev, x) y_new = min(y_prev, y) w_new = max(x_prev + w_prev, x + w) - x_new h_new = max(y_prev + h_prev, y + h) - y_new merged_boxes.append((x_new, y_new, w_new, h_new)) else: merged_boxes.append((x, y, w, h)) # **7️⃣ Potong dan proses karakter** segmented_chars = [] for (x, y, w, h) in merged_boxes: char_segment = image[y:y+h, x:x+w] char_segment = cv2.resize(char_segment, (128, 128), interpolation=cv2.INTER_AREA) segmented_chars.append(char_segment) return segmented_chars @app.post("/predict") async def predict(file: UploadFile = File(...)): # Baca gambar dari file upload image = Image.open(io.BytesIO(await file.read())).convert("L") image = np.array(image) # **Segmentasi huruf** segmented_chars = preprocess_image(image) # Jika tidak ada huruf terdeteksi if not segmented_chars: return {"prediction": "No characters detected"} # **Prediksi untuk setiap karakter** predictions = [] for char in segmented_chars: char_norm = np.array(char) / 255.0 # Normalisasi char_norm = char_norm.reshape(1, 128, 128, 1) # Reshape untuk model prediction = model.predict(char_norm) predicted_label = labels[np.argmax(prediction)] predictions.append(predicted_label) return {"predictions": predictions}