Spaces:
Sleeping
Sleeping
import streamlit as st | |
import tensorflow as tf | |
import numpy as np | |
import os | |
import pandas as pd | |
import altair as alt | |
from PIL import Image | |
from tensorflow.keras.preprocessing.image import ImageDataGenerator | |
import zipfile | |
import io | |
import shutil | |
# π¨ App Title | |
st.title("πΆπ± Cat vs Dog Classifier") | |
# π About the App | |
st.write( | |
""" | |
## About This App | |
This is a machine learning application that classifies images into two categories: | |
**Cats π±** and **Dogs πΆ**. The model is trained using a deep learning architecture | |
called Convolutional Neural Networks (CNNs) and is able to distinguish between images | |
of cats and dogs with high accuracy. | |
### Features: | |
- **Dataset Overview**: View the number of images in the dataset, categorized by "Cats" and "Dogs". | |
- **Model Evaluation**: Check the model's performance on the validation set, including accuracy and loss. | |
- **Image Classification**: Upload an image, and the model will predict whether it's a cat or a dog, along with the confidence level. | |
- **Download Test Folder**: Download a ZIP file containing the test images. | |
The app is powered by **Streamlit** for an interactive user interface and **TensorFlow** for image classification. | |
""" | |
) | |
# β Detect Environment & Set Dataset Path | |
BASE_DIR = "dataset" # In Hugging Face Spaces, the dataset folder should be at the root of the Space | |
ZIP_PATH = "dataset.zip" # If dataset is uploaded as a ZIP (make sure it's in the same directory as app.py) | |
TRAIN_DIR = os.path.join(BASE_DIR, "train") | |
TEST_DIR = os.path.join(BASE_DIR, "test") | |
# β Extract Dataset if Needed (Hugging Face) | |
if ZIP_PATH and os.path.exists(ZIP_PATH): | |
if not os.path.exists(BASE_DIR): # Avoid re-extracting | |
with zipfile.ZipFile(ZIP_PATH, "r") as zip_ref: | |
zip_ref.extractall(BASE_DIR) # Extract into dataset folder | |
st.success("β Dataset extracted!") | |
# π Check if dataset exists | |
if not os.path.exists(TRAIN_DIR): | |
st.error(f"β Dataset folder 'train' not found at {TRAIN_DIR}. Please upload the dataset.") | |
st.stop() | |
# π Verify Cats & Dogs Folders | |
cat_dir = os.path.join(TRAIN_DIR, "cats") | |
dog_dir = os.path.join(TRAIN_DIR, "dogs") | |
if not os.path.exists(cat_dir) or not os.path.exists(dog_dir): | |
st.error("β Missing 'cats' or 'dogs' folders inside 'train'. Please check your dataset.") | |
st.stop() | |
# π Constants | |
IMG_SIZE = (150, 150) | |
BATCH_SIZE = 32 | |
MODEL_PATH = "cats_dogs_model.h5" # Ensure the model is uploaded to Hugging Face Space | |
# π― Load Model | |
if os.path.exists(MODEL_PATH): | |
model = tf.keras.models.load_model(MODEL_PATH) | |
else: | |
st.error("β No trained model found. Please upload 'cats_dogs_model.h5' to your Hugging Face repository.") | |
st.stop() | |
# π· Image Preprocessing | |
def preprocess_image(image): | |
image = image.convert('RGB').resize(IMG_SIZE) | |
img_array = np.array(image, dtype=np.float32) / 255.0 | |
img_array = np.expand_dims(img_array, axis=0) | |
return img_array | |
# πΆπ± Classify Image | |
def classify_image(image): | |
processed_img = preprocess_image(image) | |
prediction = model.predict(processed_img)[0][0] | |
label = "Dog πΆ" if prediction > 0.5 else "Cat π±" | |
confidence = prediction if label == "Dog πΆ" else 1 - prediction | |
return label, confidence | |
# π Model Evaluation | |
def evaluate_model(): | |
datagen = ImageDataGenerator(rescale=1.0 / 255, validation_split=0.2) | |
test_data = datagen.flow_from_directory( | |
TRAIN_DIR, | |
target_size=IMG_SIZE, | |
batch_size=BATCH_SIZE, | |
class_mode='binary', | |
subset='validation' | |
) | |
loss, accuracy = model.evaluate(test_data, verbose=0) | |
return accuracy, loss | |
# π Streamlit Tabs | |
tab1, tab2, tab3 = st.tabs(["π Dataset Preview", "π Model Performance", "πΆπ± Image Classification"]) | |
# π Tab 1: Dataset Preview | |
with tab1: | |
st.write("### Dataset Overview") | |
dataset_info = { | |
"Total Images": len(os.listdir(cat_dir)) + len(os.listdir(dog_dir)), | |
"Cat Images": len(os.listdir(cat_dir)), | |
"Dog Images": len(os.listdir(dog_dir)) | |
} | |
df_info = pd.DataFrame(list(dataset_info.items()), columns=["Category", "Count"]) | |
st.dataframe(df_info) | |
# Visualization | |
st.write("### Image Distribution") | |
chart = alt.Chart(df_info).mark_bar().encode( | |
x="Category", y="Count", color="Category" | |
) | |
st.altair_chart(chart, use_container_width=True) | |
# π Tab 2: Model Performance | |
with tab2: | |
st.write("### Model Evaluation") | |
accuracy, loss = evaluate_model() | |
st.write(f"β **Validation Accuracy:** {accuracy*100:.2f}%") | |
st.write(f"β **Validation Loss:** {loss:.4f}") | |
# πΆπ± Tab 3: Image Classification | |
with tab3: | |
st.write("### Upload an Image for Classification") | |
uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "png", "jpeg"]) | |
if uploaded_file: | |
image = Image.open(uploaded_file) | |
st.image(image, caption="Uploaded Image", use_container_width=True) | |
with st.spinner("Classifying..."): | |
label, confidence = classify_image(image) | |
st.subheader("Prediction:") | |
st.write(f"This is a **{label}** with **{confidence*100:.2f}%** confidence.") | |
# **New Feature: Download the 'test' folder as a ZIP** | |
def zip_folder(folder_path): | |
# Create an in-memory zip file | |
zip_buffer = io.BytesIO() | |
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file: | |
for root, dirs, files in os.walk(folder_path): | |
for file in files: | |
zip_file.write(os.path.join(root, file), os.path.relpath(os.path.join(root, file), folder_path)) | |
zip_buffer.seek(0) # Go to the beginning of the file | |
return zip_buffer | |
# Button to download 'test' folder | |
if os.path.exists(TEST_DIR): | |
st.write("### Download Test Folder") | |
zip_buffer = zip_folder(TEST_DIR) | |
st.download_button( | |
label="Download Test Folder as ZIP", | |
data=zip_buffer, | |
file_name="test_folder.zip", | |
mime="application/zip" | |
) | |
else: | |
st.warning(f"β Test folder not found at {TEST_DIR}") |