|
import streamlit as st |
|
from PIL import Image |
|
from io import BytesIO |
|
import requests |
|
import os |
|
import time |
|
import torch |
|
import torch.nn.functional as F |
|
from torchvision.transforms import (Compose, |
|
Normalize, |
|
CenterCrop, |
|
Resize, |
|
ToTensor) |
|
|
|
|
|
TOKEN = st.secrets["TOKEN"] |
|
|
|
|
|
headers = {"Authorization": f"Bearer {TOKEN}"} |
|
|
|
|
|
device = torch.device('cpu') |
|
|
|
|
|
pred_transforms = Compose( |
|
[ |
|
Resize(224), |
|
CenterCrop(224), |
|
ToTensor(), |
|
Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]), |
|
] |
|
) |
|
|
|
|
|
id2label = {0: 'matang', 1: 'mentah', 2: 'setengah-matang', 3: 'terlalu-matang'} |
|
|
|
|
|
|
|
def query_hf(filename): |
|
|
|
with open(filename, "rb") as f: |
|
data = f.read() |
|
|
|
|
|
response = requests.post(API_URL, headers=headers, data=data) |
|
return response.json() |
|
|
|
|
|
|
|
def query_pytorch(image, model): |
|
|
|
input_tensor = pred_transforms(image.convert("RGB")).to(device) |
|
encoding = input_tensor.unsqueeze(0) |
|
with torch.no_grad(): |
|
|
|
input_tensor = pred_transforms(image.convert("RGB")) |
|
encoding = input_tensor.unsqueeze(0) |
|
|
|
|
|
outputs = model(encoding) |
|
|
|
|
|
probabilities = F.softmax(outputs, dim=1) |
|
|
|
return probabilities |
|
|
|
|
|
|
|
|
|
def hunggingface_pred(upload): |
|
|
|
image = Image.open(upload) |
|
|
|
with st.spinner("memprediksi..."): |
|
|
|
response = query_hf(upload) |
|
|
|
|
|
while 'error' in response: |
|
estimated_time = round(response['estimated_time']) |
|
progress_text = "Model sedang dimuat. Mohon tunggu..." |
|
my_bar = st.progress(0, text=progress_text) |
|
|
|
start_time = time.time() |
|
|
|
while time.time() - start_time < estimated_time: |
|
time.sleep(0.1) |
|
elapsed_time = time.time() - start_time |
|
progress_percent = min( |
|
100, int((elapsed_time / estimated_time) * 100)) |
|
my_bar.progress(progress_percent, |
|
text=f"{progress_text} {progress_percent}%") |
|
|
|
|
|
response = query_hf(upload) |
|
|
|
|
|
if my_bar is not None: |
|
my_bar.empty() |
|
|
|
|
|
img1.image(image, width=300) |
|
for i, item in enumerate(response, start=1): |
|
label = item["label"] |
|
score = item["score"] |
|
score_percent = score * 100 |
|
labels[i - 1].text(f"{i}. {label:<15}: {score_percent:.1f}%") |
|
|
|
|
|
|
|
def pytorch_pred(upload, PATH): |
|
|
|
image = Image.open(upload) |
|
|
|
with st.spinner("memprediksi..."): |
|
|
|
model = torch.load(PATH, map_location=device) |
|
model = model.eval() |
|
|
|
|
|
response = query_pytorch(image, model) |
|
|
|
|
|
img1.image(image, width=300) |
|
|
|
|
|
sorted_indices = torch.argsort(response, descending=True) |
|
for i, idx in enumerate(sorted_indices[0]): |
|
class_name = id2label[idx.item()] |
|
score_percent = response[0, idx].item() * 100 |
|
labels[i].text(f"{i+1}. {class_name:<15}: {score_percent:.1f}%") |
|
|
|
|
|
|
|
def get_extension(file_name): |
|
_, ext = os.path.splitext(file_name) |
|
return ext.lower() |
|
|
|
|
|
def convert_image(img): |
|
buf = BytesIO() |
|
img.save(buf, format="PNG") |
|
byte_im = buf.getvalue() |
|
return byte_im |
|
|
|
|
|
|
|
|
|
st.set_page_config( |
|
layout="wide", page_title="KlaKePi", page_icon=":banana:" |
|
) |
|
|
|
|
|
st.write("## Klasifikasi Tingkat Kematangan Pisang (KlaKePi)") |
|
st.write("Unggah gambar pisang untuk memprediksi tingkat kematangannya.") |
|
|
|
st.markdown("---") |
|
|
|
st.write("## Pengaturan :wrench:") |
|
arsitektur = st.radio( |
|
"Arsitektur Model", |
|
('Vision Transformer (ViT)', 'Convolutional Neural Network (CNN)') |
|
) |
|
|
|
|
|
if arsitektur == 'Vision Transformer (ViT)': |
|
option = st.selectbox( |
|
"Varian Model", |
|
("ViT-B/16-in1k", "ViT-B/32-in1k", "ViT-L/16-in1k", "ViT-L/32-in1k", "ViT-H/14-in1k", |
|
"ViT-B/16-in21k", "ViT-B/32-in21k", "ViT-L/16-in21k", "ViT-L/32-in21k", "ViT-H/14-in21k"), |
|
index=7, help="in1k = Model pre-trained pada ImageNet-1k dan fine-tuned pada citra pisang primer\n\nin21k = Model pre-trained pada ImageNet-21k dan fine-tuned pada citra pisang primer" |
|
) |
|
|
|
|
|
elif arsitektur == 'Convolutional Neural Network (CNN)': |
|
option = st.selectbox( |
|
"Varian Model", |
|
("Alexnet-in1k", "Densenet121-in1k", "Densenet201-in1k", "MobileNet V2-in1k", "MobileNet V3-in1k", |
|
"Resnet152-in1k", "Resnet50-in1k", "Squeezenet-in1k", "VGG16-in1k", "VGG19-in1k"), |
|
index=0, help="in1k = Model pre-trained pada ImageNet-1k dan fine-tuned pada citra pisang primer" |
|
) |
|
|
|
st.markdown("---") |
|
|
|
st.write("## Unggah Gambar :arrow_down:") |
|
my_upload = st.file_uploader( |
|
"Unggah gambar pisang", type=["png", "jpg", "jpeg"] |
|
) |
|
if my_upload is not None: |
|
|
|
ext = get_extension(my_upload.name) |
|
output_filename_with_ext = f"pisangup{ext}" |
|
if not os.path.exists("uploaded_images"): |
|
os.makedirs("uploaded_images") |
|
with open(os.path.join("uploaded_images", output_filename_with_ext), "wb") as f: |
|
f.write(my_upload.read()) |
|
|
|
st.markdown("---") |
|
|
|
st.write("## Output :arrow_down:") |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
|
|
|
col1.write("### Gambar :camera:") |
|
img1 = col1.empty() |
|
|
|
|
|
col2.write("### Label dan Skor Terdeteksi:") |
|
labels = [col2.empty() for _ in range(4)] |
|
|
|
|
|
model_paths = { |
|
"Alexnet-in1k": "./models/alexnet.pt", |
|
"Densenet121-in1k": "./models/densenet121.pt", |
|
"Densenet201-in1k": "./models/densenet201.pt", |
|
"MobileNet V2-in1k": "./models/mobilenet_V2.pt", |
|
"MobileNet V3-in1k": "./models/mobilenet_V3.pt", |
|
"Resnet50-in1k": "./models/resnet50.pt", |
|
"Resnet152-in1k": "./models/resnet152.pt", |
|
"Squeezenet-in1k": "./models/squeezenet.pt", |
|
"VGG16-in1k": "./models/vgg16.pt", |
|
"VGG19-in1k": "./models/vgg19.pt", |
|
"ViT-B/16-in1k": "./models/vit_b_16.pt", |
|
"ViT-B/32-in1k": "./models/vit_b_32.pt", |
|
"ViT-L/16-in1k": "./models/vit_l_16.pt", |
|
"ViT-L/32-in1k": "./models/vit_l_32.pt", |
|
"ViT-H/14-in1k": "./models/vit_h_14.pt", |
|
} |
|
|
|
|
|
api_urls = { |
|
"ViT-B/16-in21k": "https://api-inference.huggingface.co/models/aryap2/kematangan-pisang-vit-b-16-100eph-224-v4.4", |
|
"ViT-B/32-in21k": "https://api-inference.huggingface.co/models/aryap2/kematangan-pisang-vit-b-32-100eph-224-v4.4", |
|
"ViT-L/16-in21k": "https://api-inference.huggingface.co/models/aryap2/kematangan-pisang-vit-l-16-100eph-224-v4.4", |
|
"ViT-L/32-in21k": "https://api-inference.huggingface.co/models/aryap2/kematangan-pisang-vit-l-32-100eph-224-v4.4", |
|
"ViT-H/14-in21k": "https://api-inference.huggingface.co/models/aryap2/kematangan-pisang-vit-h-14-100eph-224-telyu", |
|
} |
|
|
|
|
|
if option in model_paths: |
|
PATH = model_paths[option] |
|
if my_upload is not None: |
|
pytorch_pred(f"./uploaded_images/{output_filename_with_ext}", PATH) |
|
else: |
|
pytorch_pred("./pisang.jpg", PATH) |
|
|
|
|
|
elif option in api_urls: |
|
API_URL = api_urls[option] |
|
if my_upload is not None: |
|
hunggingface_pred(f"./uploaded_images/{output_filename_with_ext}") |
|
else: |
|
hunggingface_pred("./pisang.jpg") |
|
|