| import streamlit as st |
| import numpy as np |
| import pickle |
| import tensorflow as tf |
| import os |
| import pandas as pd |
| from datetime import datetime |
| from tensorflow.keras.models import load_model |
| from tensorflow.keras.preprocessing.sequence import pad_sequences |
| from tensorflow.keras.applications.mobilenet_v2 import MobileNetV2, preprocess_input |
| from tensorflow.keras.preprocessing.image import img_to_array |
| from PIL import Image |
|
|
| |
| |
| |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
| MODEL_DIR = os.path.join(BASE_DIR, 'models') |
|
|
| PATH_MODEL_CAPTION = os.path.join(MODEL_DIR, 'fruit_caption_model.h5') |
| PATH_TOKENIZER = os.path.join(MODEL_DIR, 'tokenizer.pkl') |
| PATH_LOG = os.path.join(MODEL_DIR, 'riwayat_penggunaan.csv') |
| PATH_GRAFIK = os.path.join(MODEL_DIR, '1_grafik_akurasi.png') |
|
|
| |
| |
| |
| st.set_page_config(page_title="AI Fruit Captioning", page_icon="๐ฅญ", layout="wide") |
|
|
| st.title("๐ Identifikasi Citra Buah Digital") |
| st.write("Sistem otomatis yang mendeskripsikan jenis buah dan manfaatnya menggunakan Deep Learning.") |
|
|
| |
| |
| |
|
|
| def save_to_log(nama, vitamin, deskripsi): |
| now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
| new_data = {'Waktu': [now], 'Nama Buah': [nama], 'Vitamin': [vitamin], 'Deskripsi': [deskripsi]} |
| df_new = pd.DataFrame(new_data) |
| |
| if not os.path.exists(MODEL_DIR): |
| os.makedirs(MODEL_DIR) |
| |
| if not os.path.isfile(PATH_LOG): |
| df_new.to_csv(PATH_LOG, index=False) |
| else: |
| df_new.to_csv(PATH_LOG, mode='a', header=False, index=False) |
|
|
| @st.cache_resource |
| def load_assets(): |
| if not os.path.exists(PATH_MODEL_CAPTION) or not os.path.exists(PATH_TOKENIZER): |
| raise FileNotFoundError("File model atau tokenizer tidak ditemukan!") |
| |
| model = load_model(PATH_MODEL_CAPTION) |
| with open(PATH_TOKENIZER, 'rb') as f: |
| tokenizer = pickle.load(f) |
| |
| base_model = MobileNetV2(weights='imagenet') |
| fe_model = tf.keras.Model(inputs=base_model.inputs, outputs=base_model.layers[-2].output) |
| |
| return model, tokenizer, fe_model |
|
|
| def generate_caption(image, model, tokenizer, fe_model): |
| img = image.convert('RGB').resize((224, 224)) |
| img = img_to_array(img) |
| img = np.expand_dims(img, axis=0) |
| img = preprocess_input(img) |
| |
| feature = fe_model.predict(img, verbose=0) |
| max_length = 21 |
| in_text = 'startseq' |
| |
| for i in range(max_length): |
| sequence = tokenizer.texts_to_sequences([in_text])[0] |
| sequence = pad_sequences([sequence], maxlen=max_length) |
| yhat = model.predict([feature, sequence], verbose=0) |
| idx = np.argmax(yhat) |
| |
| word = tokenizer.index_word.get(idx) |
| if word is None or word == 'endseq': |
| break |
| in_text += ' ' + word |
| |
| return in_text.replace('startseq', '').strip() |
|
|
| |
| |
| |
|
|
| try: |
| model, tokenizer, fe_model = load_assets() |
| except Exception as e: |
| st.error(f"โ ๏ธ Gagal memuat sistem: {e}") |
| st.stop() |
|
|
| uploaded_file = st.file_uploader("Unggah foto buah...", type=["jpg", "png", "jpeg"]) |
|
|
| if uploaded_file is not None: |
| img_display = Image.open(uploaded_file).convert('RGB') |
| |
| col1, col2 = st.columns([1, 1]) |
| |
| with col1: |
| st.subheader("๐ผ๏ธ Citra Input") |
| st.image(img_display, use_column_width=True) |
| |
| with col2: |
| st.subheader("๐ค Hasil Analisis AI") |
| with st.spinner('Menganalisis citra...'): |
| try: |
| caption = generate_caption(img_display, model, tokenizer, fe_model) |
| |
| if "b9" in caption or len(caption.split()) < 2: |
| st.warning("Model belum mengenali gambar ini. Pastikan file tokenizer sudah sesuai.") |
| st.write(f"Output mentah: `{caption}`") |
| |
| elif ' deskripsi ' in caption and ' mengandung vitamin ' in caption: |
| parts = caption.split(' deskripsi ') |
| nama_buah = parts[0].replace('buah ', '').title() |
| detail = parts[1].split(' mengandung vitamin ') |
| deskripsi_clean = detail[0].capitalize() |
| vitamin_clean = detail[1].upper() |
| |
| st.success(f"**Jenis Buah:** {nama_buah}") |
| st.metric(label="Kandungan Utama", value=f"Vitamin {vitamin_clean}") |
| st.info(f"**Keterangan:**\n{deskripsi_clean}") |
| save_to_log(nama_buah, vitamin_clean, deskripsi_clean) |
| else: |
| st.success("Analisis Selesai") |
| st.write(f"**Hasil Prediksi:** {caption}") |
| |
| except Exception as e: |
| st.error(f"Terjadi kesalahan: {e}") |
|
|
| |
| |
| |
| with st.sidebar: |
| st.header("๐ Informasi & Riwayat") |
| if os.path.exists(PATH_GRAFIK): |
| st.image(PATH_GRAFIK, caption="Grafik Performa Model", use_column_width=True) |
| |
| st.markdown("---") |
| if os.path.exists(PATH_LOG): |
| df_log = pd.read_csv(PATH_LOG) |
| csv_data = df_log.to_csv(index=False).encode('utf-8') |
| |
| |
| st.download_button( |
| label="Download Riwayat (CSV)", |
| data=csv_data, |
| file_name=f"riwayat_buah_{datetime.now().strftime('%Y%m%d')}.csv", |
| mime="text/csv" |
| ) |