TIFNGK_E41222722/predict_image.py

415 lines
16 KiB
Python

import os
import numpy as np
import pandas as pd
import cv2
import matplotlib.pyplot as plt
from PIL import Image, ImageTk
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import joblib
from utils.preprocessing import preprocess_pipeline
from utils.feature_extraction import FeatureExtractor
from utils.helpers import load_model, estimate_prediction_confidence
class PMKDetectorApp:
def __init__(self, root):
self.root = root
self.root.title("Sistem Deteksi PMK pada Sapi")
self.root.geometry("900x700")
self.root.configure(bg='#f0f0f0')
# Load model
self.model = None
self.scaler = None
self.label_encoder = None
self.extractor = FeatureExtractor()
self.load_trained_model()
# Variables
self.image_path = None
self.prediction = None
self.confidence = None
self.features = None
# Setup UI
self.setup_ui()
def load_trained_model(self):
"""Load trained model"""
try:
self.model, self.scaler, self.label_encoder = load_model()
print("Model loaded successfully!")
except Exception as e:
print(f"Error loading model: {e}")
messagebox.showwarning("Peringatan",
"Model belum dilatih! Silakan jalankan train_model.py terlebih dahulu.")
def setup_ui(self):
"""Setup user interface"""
# Title
title_label = tk.Label(self.root,
text="SISTEM DETEKSI PENYAKIT MULUT DAN KUKU (PMK) PADA SAPI",
font=("Arial", 16, "bold"),
bg='#f0f0f0',
fg='#2c3e50')
title_label.pack(pady=10)
# Main frame
main_frame = tk.Frame(self.root, bg='#f0f0f0')
main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)
# Left frame for image
left_frame = tk.Frame(main_frame, bg='white', relief=tk.RAISED, bd=2)
left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 10))
# Image label
self.image_label = tk.Label(left_frame, text="Gambar akan ditampilkan di sini",
bg='white', fg='gray', font=("Arial", 10))
self.image_label.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
# Right frame for controls and results
right_frame = tk.Frame(main_frame, bg='#f0f0f0')
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=False)
# Button frame
btn_frame = tk.Frame(right_frame, bg='#f0f0f0')
btn_frame.pack(pady=10)
# Upload button
upload_btn = tk.Button(btn_frame, text="📁 UPLOAD GAMBAR",
command=self.upload_image,
font=("Arial", 10, "bold"),
bg='#3498db', fg='white',
padx=20, pady=10,
cursor="hand2")
upload_btn.pack(pady=5)
# Predict button
predict_btn = tk.Button(btn_frame, text="🔍 PREDIKSI",
command=self.predict_image,
font=("Arial", 10, "bold"),
bg='#2ecc71', fg='white',
padx=20, pady=10,
cursor="hand2",
state=tk.DISABLED)
predict_btn.pack(pady=5)
self.predict_btn = predict_btn
# Results frame
results_frame = tk.LabelFrame(right_frame, text="HASIL PREDIKSI",
font=("Arial", 11, "bold"),
bg='#f0f0f0', fg='#2c3e50',
padx=10, pady=10)
results_frame.pack(pady=20, fill=tk.X)
# Status label
self.status_label = tk.Label(results_frame,
text="Status: Belum diprediksi",
font=("Arial", 10),
bg='#f8f9fa',
relief=tk.SUNKEN,
padx=10, pady=10,
width=30)
self.status_label.pack(pady=5)
# Result label
self.result_label = tk.Label(results_frame,
text="",
font=("Arial", 12, "bold"),
bg='#f8f9fa',
padx=10, pady=5)
self.result_label.pack(pady=5)
# Confidence label
self.confidence_label = tk.Label(results_frame,
text="",
font=("Arial", 10),
bg='#f8f9fa',
padx=10, pady=5)
self.confidence_label.pack(pady=5)
# Progress bar
self.progress = ttk.Progressbar(results_frame,
mode='indeterminate',
length=200)
# Features frame
features_frame = tk.LabelFrame(right_frame, text="FITUR YANG DIEKSTRAKSI",
font=("Arial", 11, "bold"),
bg='#f0f0f0', fg='#2c3e50',
padx=10, pady=10)
features_frame.pack(pady=10, fill=tk.BOTH, expand=True)
# Features text box
self.features_text = tk.Text(features_frame,
height=10,
width=40,
font=("Courier", 8))
self.features_text.pack(fill=tk.BOTH, expand=True)
# Add scrollbar
scrollbar = tk.Scrollbar(features_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.features_text.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=self.features_text.yview)
# Statistics button
stats_btn = tk.Button(right_frame, text="📊 LIHAT STATISTIK",
command=self.show_statistics,
font=("Arial", 9),
bg='#9b59b6', fg='white',
padx=10, pady=5,
cursor="hand2")
stats_btn.pack(pady=10)
def upload_image(self):
"""Upload image for prediction"""
file_path = filedialog.askopenfilename(
title="Pilih gambar sapi",
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp")]
)
if file_path:
self.image_path = file_path
self.display_image(file_path)
self.predict_btn.config(state=tk.NORMAL)
self.clear_results()
def display_image(self, image_path):
"""Display selected image"""
try:
# Open and resize image
img = Image.open(image_path)
img.thumbnail((400, 400))
# Convert to PhotoImage
photo = ImageTk.PhotoImage(img)
# Update label
self.image_label.config(image=photo, text="")
self.image_label.image = photo
except Exception as e:
messagebox.showerror("Error", f"Gagal memuat gambar: {str(e)}")
def predict_image(self):
"""Predict image using trained model"""
if not self.image_path:
messagebox.showwarning("Peringatan", "Silakan pilih gambar terlebih dahulu!")
return
if not self.model:
messagebox.showwarning("Peringatan", "Model belum dimuat!")
return
try:
# Show progress
self.progress.pack(pady=5)
self.progress.start()
self.status_label.config(text="Status: Memproses...")
self.root.update()
# 1. Preprocess image (returns RGB image and grayscale equalized)
img_rgb, gray_processed = preprocess_pipeline(self.image_path)
# 2. Extract features
self.features = self.extractor.extract_all_features(img_rgb, gray_processed)
# 3. Scale features
features_scaled = self.scaler.transform([self.features])
# 4. Predict
prediction_encoded = self.model.predict(features_scaled)[0]
self.prediction = self.label_encoder.inverse_transform([prediction_encoded])[0]
# 5. Get prediction probabilities
confidence = estimate_prediction_confidence(self.model, features_scaled)
if confidence is None:
probabilities = self.model.predict_proba(features_scaled)[0]
confidence = max(probabilities) * 100
self.confidence = confidence
# 6. Display results
self.display_results()
# 7. Save prediction to CSV
self.save_prediction_to_csv()
# Stop progress
self.progress.stop()
self.progress.pack_forget()
except Exception as e:
self.progress.stop()
self.progress.pack_forget()
messagebox.showerror("Error", f"Terjadi kesalahan: {str(e)}")
self.status_label.config(text="Status: Error")
def display_results(self):
"""Display prediction results"""
# Update status
self.status_label.config(text="Status: Selesai")
# Set result label with color
if self.prediction == 'sehat':
color = 'green'
text = "✅ SAPI SEHAT"
else:
color = 'red'
text = "⚠️ SAPI SAKIT (TERDETEKSI PMK)"
self.result_label.config(text=text, fg=color)
# Display confidence
self.confidence_label.config(
text=f"Tingkat Kepercayaan: {self.confidence:.2f}%",
fg='blue' if self.confidence > 70 else 'orange'
)
# Display extracted features
self.display_features()
def display_features(self):
"""Display extracted features in text box"""
if self.features is not None:
self.features_text.delete(1.0, tk.END)
# Create formatted string
features_str = "Fitur yang diekstraksi:\n"
features_str += "=" * 40 + "\n"
for i, (name, value) in enumerate(zip(self.extractor.feature_names, self.features)):
features_str += f"{name:15}: {value:10.6f}\n"
if i == 8: # After color moments
features_str += "-" * 40 + "\n"
features_str += "=" * 40 + "\n"
features_str += f"\nHasil Prediksi: {self.prediction.upper()}\n"
features_str += f"Confidence: {self.confidence:.2f}%"
self.features_text.insert(1.0, features_str)
def save_prediction_to_csv(self):
"""Save prediction results to CSV"""
try:
os.makedirs('results', exist_ok=True)
# Create data dictionary
data = {
'image_path': [self.image_path],
'prediction': [self.prediction],
'confidence': [self.confidence],
'timestamp': [pd.Timestamp.now()]
}
# Add features
for i, name in enumerate(self.extractor.feature_names):
data[name] = [self.features[i]]
# Save to CSV
df = pd.DataFrame(data)
# Check if file exists
if os.path.exists('results/predictions.csv'):
df.to_csv('results/predictions.csv', mode='a', header=False, index=False)
else:
df.to_csv('results/predictions.csv', index=False)
print(f"Prediksi disimpan ke: results/predictions.csv")
except Exception as e:
print(f"Error saving prediction: {e}")
def show_statistics(self):
"""Show feature statistics"""
try:
dataset_path = 'features/dataset.csv'
legacy_path = 'features/all_features.csv'
if os.path.exists(dataset_path):
df = pd.read_csv(dataset_path)
elif os.path.exists(legacy_path):
df = pd.read_csv(legacy_path)
else:
df = None
if df is not None:
label_column = 'label_name' if 'label_name' in df.columns else 'label'
feature_columns = [feature for feature in self.extractor.feature_names if feature in df.columns]
if not feature_columns:
messagebox.showinfo("Info", "Kolom fitur tidak ditemukan pada CSV statistik.")
return
# Create statistics window
stats_window = tk.Toplevel(self.root)
stats_window.title("Statistik Fitur")
stats_window.geometry("600x400")
# Create text widget
text_widget = tk.Text(stats_window, font=("Courier", 9))
text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Calculate statistics
stats = df.groupby(label_column)[feature_columns].agg(['mean', 'std', 'min', 'max'])
# Format output
output = "STATISTIK FITUR PER KELAS\n"
output += "=" * 60 + "\n\n"
if label_column == 'label_name':
class_order = ['normal', 'defective']
else:
class_order = ['sehat', 'sakit']
for label in class_order:
output += f"KELAS: {label.upper()}\n"
output += "-" * 40 + "\n"
if label in stats.index:
label_stats = stats.loc[label]
for feature in feature_columns:
if feature in label_stats:
mean = label_stats[feature]['mean']
std = label_stats[feature]['std']
output += f"{feature:15}: {mean:8.4f} ± {std:8.4f}\n"
output += "\n"
# Add comparison
output += "PERBANDINGAN RATA-RATA\n"
output += "-" * 40 + "\n"
healthy_label = 'normal' if label_column == 'label_name' else 'sehat'
sick_label = 'defective' if label_column == 'label_name' else 'sakit'
if healthy_label in stats.index and sick_label in stats.index:
for feature in feature_columns:
sehat_mean = stats.loc[healthy_label, feature]['mean']
sakit_mean = stats.loc[sick_label, feature]['mean']
diff = sakit_mean - sehat_mean
output += f"{feature:15}: {sehat_mean:8.4f}{sakit_mean:8.4f} ({diff:+.4f})\n"
text_widget.insert(1.0, output)
else:
messagebox.showinfo("Info", "File statistik belum tersedia. Silakan train model terlebih dahulu.")
except Exception as e:
messagebox.showerror("Error", f"Gagal memuat statistik: {str(e)}")
def clear_results(self):
"""Clear previous results"""
self.result_label.config(text="")
self.confidence_label.config(text="")
self.features_text.delete(1.0, tk.END)
self.status_label.config(text="Status: Belum diprediksi")
def main():
root = tk.Tk()
app = PMKDetectorApp(root)
root.mainloop()
if __name__ == "__main__":
main()