| import streamlit as st |
| import joblib |
| import numpy as np |
| import cv2 |
| from PIL import Image |
| import os |
| from skimage.feature import local_binary_pattern, graycomatrix, graycoprops |
|
|
| |
| st.set_page_config(page_title="Weather Classifier", layout="wide") |
|
|
| |
| @st.cache_resource |
| def load_models(algorithm): |
| try: |
| |
| base_path = "model" |
| |
| if algorithm == 'SVM': |
| |
| model_path = os.path.join(base_path, "svm/svm_model_optimal_80_20.joblib") |
| scaler_path = os.path.join(base_path, "svm/scaler_svm.joblib") |
| le_path = os.path.join(base_path, "svm/label_encoder.joblib") |
| |
| model = joblib.load(model_path) |
| scaler = joblib.load(scaler_path) |
| le = joblib.load(le_path) |
| |
| elif algorithm == 'XGBoost': |
| |
| model_path = os.path.join(base_path, "xgboost/xgb_model_optimal.joblib") |
| le_path = os.path.join(base_path, "xgboost/label_encoder_xgb.joblib") |
| |
| model = joblib.load(model_path) |
| scaler = None |
| le = joblib.load(le_path) |
| |
| return model, scaler, le |
| except Exception as e: |
| st.error(f"Error loading model files: {e}") |
| return None, None, None |
|
|
| |
| def extract_features_final(image_pil): |
| img_np = np.array(image_pil.convert('RGB')) |
| |
| |
| img_np = cv2.resize(img_np, (224, 224)) |
|
|
| |
| hsv_image = cv2.cvtColor(img_np, cv2.COLOR_RGB2HSV) |
| h = hsv_image[:, :, 0] |
| s = hsv_image[:, :, 1] |
| |
| gray = cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY) |
| clahe_builder = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) |
| gray_clahe = clahe_builder.apply(gray) |
| |
| |
| hist_h = cv2.calcHist([h], [0], None, [8], [0, 256]).flatten() |
| hist_s = cv2.calcHist([s], [0], None, [8], [0, 256]).flatten() |
|
|
| lbp = local_binary_pattern(gray_clahe, P=8, R=1, method='uniform') |
| lbp_hist, _ = np.histogram(lbp.ravel(), bins=np.arange(0, 10), range=(0, 9)) |
| lbp_hist = lbp_hist.astype('float') |
| lbp_hist /= (lbp_hist.sum() + 1e-6) |
|
|
| glcm = graycomatrix(gray_clahe, distances=[1], angles=[0], symmetric=True, normed=True) |
| contrast = graycoprops(glcm, 'contrast')[0, 0] |
| energy = graycoprops(glcm, 'energy')[0, 0] |
| homogeneity = graycoprops(glcm, 'homogeneity')[0, 0] |
|
|
| features = np.hstack([hist_h, hist_s, lbp_hist, contrast, energy, homogeneity]) |
| return features.reshape(1, -1) |
|
|
| |
| st.title("☁️ Weather Classification App 🌦️") |
| st.write("Klasifikasi Cuaca (Cloudy, Rainy, Shine, Sunrise) menggunakan SVM & XGBoost") |
|
|
| algo_choice = st.sidebar.selectbox("Pilih Algoritma:", ["SVM", "XGBoost"]) |
| uploaded_file = st.file_uploader("Upload foto cuaca...", type=["jpg", "png", "jpeg"]) |
|
|
| if uploaded_file is not None: |
| image = Image.open(uploaded_file) |
| col1, col2 = st.columns(2) |
| |
| with col1: |
| st.image(image, caption='Gambar Diupload', use_column_width=True) |
| |
| if st.button("Tebak Cuaca"): |
| with st.spinner("Sedang menganalisis tekstur & warna langit..."): |
| model, scaler, le = load_models(algo_choice) |
| |
| if model: |
| try: |
| features = extract_features_final(image) |
| if scaler: |
| features = scaler.transform(features) |
| |
| prediction_index = model.predict(features)[0] |
| |
| label = le.inverse_transform([prediction_index])[0] |
| |
| with col2: |
| st.success(f"### Prediksi: {label}") |
| st.info(f"Model: {algo_choice}") |
| |
| except Exception as e: |
| st.error(f"Error: {e}") |