| | """ |
| | Streamlit app for CV200 Pet Classifier |
| | |
| | Run: |
| | streamlit run streamlit_app.py |
| | """ |
| | import os |
| | import streamlit as st |
| | import requests |
| | from PIL import Image |
| | import io |
| |
|
| | |
| | def _get_api_url_default() -> str: |
| | try: |
| | secret_val = st.secrets.get("API_URL") |
| | except Exception: |
| | secret_val = None |
| | return secret_val or os.environ.get("API_URL") or "https://solarevat-cv200.hf.space" |
| |
|
| | st.set_page_config( |
| | page_title="Pet Classifier", |
| | page_icon="πΎ", |
| | layout="wide", |
| | initial_sidebar_state="expanded", |
| | ) |
| |
|
| | |
| | if "api_url" not in st.session_state: |
| | st.session_state.api_url = _get_api_url_default() |
| |
|
| | st.title("πΎ Pet Classifier") |
| | st.markdown("Upload an image of a pet to classify it using our CV200 model!") |
| |
|
| | |
| | with st.sidebar: |
| | st.header("Settings") |
| | st.session_state.api_url = st.text_input( |
| | "API_URL", |
| | value=st.session_state.api_url, |
| | help="FastAPI base URL. Default: https://solarevat-cv200.hf.space", |
| | ) |
| | |
| | st.header("About") |
| | st.markdown(""" |
| | This app uses a deep learning model trained on 200 pet classes. |
| | |
| | **How to use:** |
| | 1. Upload an image of a pet |
| | 2. Click "Classify Pet" |
| | 3. See the top 5 predictions with confidence scores |
| | """) |
| | |
| | st.header("API Info") |
| | st.markdown("**Endpoints:**") |
| | st.markdown("- `/predict` - Single image") |
| | st.markdown("- `/predict_batch` - Multiple images") |
| | st.markdown("- `/docs` - API documentation") |
| | st.markdown("- `/healthz` - Health check") |
| |
|
| | st.info( |
| | f"Current API_URL: `{st.session_state.api_url}` β [View API docs]({st.session_state.api_url}/docs)" |
| | ) |
| |
|
| | |
| | uploaded_file = st.file_uploader( |
| | "Choose an image...", |
| | type=['jpg', 'jpeg', 'png', 'webp'], |
| | help="Upload an image of a pet to classify" |
| | ) |
| |
|
| | if uploaded_file is not None: |
| | |
| | image = Image.open(uploaded_file) |
| | st.image(image, caption="Uploaded Image", use_container_width=True) |
| | |
| | |
| | if st.button("π Classify Pet", type="primary"): |
| | with st.spinner("Classifying pet... Please wait."): |
| | try: |
| | |
| | files = {'file': (uploaded_file.name, uploaded_file.getvalue(), uploaded_file.type)} |
| | data = {'top_k': 5} |
| | |
| | |
| | response = requests.post( |
| | f"{st.session_state.api_url}/predict", |
| | files=files, |
| | data=data, |
| | timeout=30 |
| | ) |
| | |
| | if response.status_code == 200: |
| | result = response.json() |
| | |
| | st.success("Classification complete!") |
| | |
| | |
| | st.subheader("Top Predictions:") |
| | |
| | predictions = result.get('predictions', []) |
| | if predictions: |
| | |
| | cols = st.columns(min(len(predictions), 3)) |
| | |
| | for idx, pred in enumerate(predictions): |
| | col_idx = idx % 3 |
| | with cols[col_idx]: |
| | confidence = pred['confidence'] * 100 |
| | class_name = pred['class_name'] |
| | |
| | |
| | st.metric( |
| | label=class_name, |
| | value=f"{confidence:.1f}%" |
| | ) |
| | st.progress(confidence / 100) |
| | |
| | |
| | top_pred = predictions[0] |
| | st.markdown("---") |
| | st.markdown(f"### π Top Prediction: **{top_pred['class_name']}**") |
| | st.markdown(f"**Confidence:** {top_pred['confidence'] * 100:.2f}%") |
| | |
| | |
| | with st.expander("View all predictions"): |
| | import pandas as pd |
| | df = pd.DataFrame(predictions) |
| | df['confidence'] = df['confidence'].apply(lambda x: f"{x*100:.2f}%") |
| | df = df.rename(columns={ |
| | 'class_name': 'Pet Breed', |
| | 'confidence': 'Confidence', |
| | 'class_id': 'Class ID' |
| | }) |
| | st.dataframe(df[['Pet Breed', 'Confidence']], use_container_width=True) |
| | else: |
| | st.warning("No predictions returned.") |
| | |
| | else: |
| | st.error(f"API Error: {response.status_code}") |
| | st.json(response.json() if response.content else {"error": "No response"}) |
| | |
| | except requests.exceptions.RequestException as e: |
| | st.error(f"Failed to connect to API: {str(e)}") |
| | st.info(f"Make sure the API is running at {st.session_state.api_url}") |
| | except Exception as e: |
| | st.error(f"An error occurred: {str(e)}") |
| |
|
| | |
| | st.markdown("---") |
| | st.markdown( |
| | """ |
| | <div style='text-align: center; color: #666;'> |
| | <p>Powered by CV200 Pet Classifier API</p> |
| | <p><a href="{}/docs" target="_blank">View API Documentation</a></p> |
| | </div> |
| | """.format(st.session_state.api_url), |
| | unsafe_allow_html=True |
| | ) |
| |
|