Commit
Β·
3f0ccc8
1
Parent(s):
8a45f7a
feat: Add UI for prediction app
Browse files- .gitattributes +1 -0
- assets/animal.jpg +3 -0
- assets/building.jpg +3 -0
- assets/object.jpg +3 -0
- assets/vehicle.jpg +3 -0
- src/streamlit_app.py +147 -4
.gitattributes
CHANGED
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
|
|
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
37 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
assets/animal.jpg
ADDED
![]() |
Git LFS Details
|
assets/building.jpg
ADDED
![]() |
Git LFS Details
|
assets/object.jpg
ADDED
![]() |
Git LFS Details
|
assets/vehicle.jpg
ADDED
![]() |
Git LFS Details
|
src/streamlit_app.py
CHANGED
@@ -1,6 +1,149 @@
|
|
1 |
import streamlit as st
|
|
|
2 |
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
+
from PIL import Image
|
3 |
|
4 |
+
# π PAGE SETUP
|
5 |
+
st.set_page_config(page_title="Image Classifier App", page_icon="π€", layout="centered")
|
6 |
+
st.html("""
|
7 |
+
<style>
|
8 |
+
.stMainBlockContainer {
|
9 |
+
max-width: 70rem;
|
10 |
+
}
|
11 |
+
</style>
|
12 |
+
""")
|
13 |
+
|
14 |
+
# π INITIALIZE SESSION STATE
|
15 |
+
# We initialize session state variables to manage app state
|
16 |
+
if "selected_image" not in st.session_state:
|
17 |
+
st.session_state["selected_image"] = None
|
18 |
+
if "prediction_placeholder" not in st.session_state:
|
19 |
+
st.session_state["prediction_placeholder"] = {"label": "A Dog", "score": 0.9558}
|
20 |
+
|
21 |
+
# π MAIN APP LAYOUT
|
22 |
+
with st.container():
|
23 |
+
st.title(
|
24 |
+
body="πΌοΈ Image Classifier with CNN",
|
25 |
+
help="An interactive application to classify images into over 1000 categories.",
|
26 |
+
)
|
27 |
+
st.html("<br>")
|
28 |
+
|
29 |
+
# Use tabs for different sections of the app
|
30 |
+
tab_app, tab_description = st.tabs(["**App**", "**Description**"])
|
31 |
+
|
32 |
+
# π APP TAB
|
33 |
+
with tab_app:
|
34 |
+
# Create a two-column layout for the app interface
|
35 |
+
col_upload, col_results = st.columns(2, gap="large")
|
36 |
+
|
37 |
+
# π IMAGE UPLOAD & EXAMPLE SELECTION
|
38 |
+
with col_upload:
|
39 |
+
st.header("Upload an Image", divider=True)
|
40 |
+
|
41 |
+
# File uploader widget
|
42 |
+
uploaded_image = st.file_uploader(
|
43 |
+
label="Drag and drop an image here or click to browse",
|
44 |
+
type=["jpg", "jpeg", "png"],
|
45 |
+
help="Maximum file size is 200MB",
|
46 |
+
key="image_uploader",
|
47 |
+
)
|
48 |
+
|
49 |
+
st.html("<br>")
|
50 |
+
st.subheader("Or Try an Example", divider=True)
|
51 |
+
|
52 |
+
# Segmented control for selecting example images
|
53 |
+
selected_example = st.segmented_control(
|
54 |
+
label="Categories",
|
55 |
+
options=["Animal", "Vehicle", "Object", "Building"],
|
56 |
+
default="Animal",
|
57 |
+
help="Select one of the pre-loaded examples",
|
58 |
+
)
|
59 |
+
|
60 |
+
st.html("<br>")
|
61 |
+
|
62 |
+
# --- THE SINGLE CLASSIFY BUTTON ---
|
63 |
+
classify_button = st.button(
|
64 |
+
label="Classify Image",
|
65 |
+
key="classify_btn",
|
66 |
+
type="primary",
|
67 |
+
icon="β¨",
|
68 |
+
)
|
69 |
+
|
70 |
+
# π PREDICTION RESULTS
|
71 |
+
with col_results:
|
72 |
+
st.header("Results", divider=True)
|
73 |
+
|
74 |
+
# This message is shown before any image is processed
|
75 |
+
if st.session_state["selected_image"] is None and not classify_button:
|
76 |
+
st.info("Choose an image to get a prediction.")
|
77 |
+
|
78 |
+
# If the button is clicked, run the prediction logic
|
79 |
+
if classify_button:
|
80 |
+
# Check if an image is selected before running prediction
|
81 |
+
if uploaded_image is not None:
|
82 |
+
st.session_state["selected_image"] = uploaded_image
|
83 |
+
elif selected_example:
|
84 |
+
# Load the selected example image
|
85 |
+
try:
|
86 |
+
img_path = f"./assets/{selected_example.lower()}.jpg"
|
87 |
+
st.session_state["selected_image"] = Image.open(img_path)
|
88 |
+
except FileNotFoundError:
|
89 |
+
st.error(
|
90 |
+
f"Error: The example image '{selected_example.lower()}.jpg' was not found."
|
91 |
+
)
|
92 |
+
st.stop()
|
93 |
+
|
94 |
+
if st.session_state["selected_image"] is not None:
|
95 |
+
st.image(
|
96 |
+
st.session_state["selected_image"],
|
97 |
+
caption="Image to be classified",
|
98 |
+
)
|
99 |
+
st.markdown("---")
|
100 |
+
st.subheader("Prediction")
|
101 |
+
|
102 |
+
# Call the prediction function and display results
|
103 |
+
with st.spinner("Analyzing image..."):
|
104 |
+
# Call our modularized prediction function!
|
105 |
+
try:
|
106 |
+
predicted_label, predicted_score = predict_image(
|
107 |
+
st.session_state["selected_image"]
|
108 |
+
)
|
109 |
+
|
110 |
+
st.metric(
|
111 |
+
label="Prediction",
|
112 |
+
value=f"Prediction: {predicted_label.replace('_', ' ').title()}",
|
113 |
+
delta=f"{predicted_score * 100:.2f}%",
|
114 |
+
help="The predicted category and its confidence score.",
|
115 |
+
delta_color="normal",
|
116 |
+
)
|
117 |
+
st.balloons()
|
118 |
+
except Exception as e:
|
119 |
+
st.error(f"An error occurred during prediction: {e}")
|
120 |
+
else:
|
121 |
+
st.error("Please upload an image or select an example to classify.")
|
122 |
+
|
123 |
+
|
124 |
+
# π DESCRIPTION TAB
|
125 |
+
with tab_description:
|
126 |
+
st.header("About This Project", divider=True)
|
127 |
+
st.markdown(
|
128 |
+
"""
|
129 |
+
This project showcases a Convolutional Neural Network (CNN) model that automatically
|
130 |
+
classifies images into over 1000 different categories.
|
131 |
+
|
132 |
+
### Original Architecture
|
133 |
+
The original project was built as a multi-service architecture, featuring:
|
134 |
+
* **Streamlit:** For the web user interface.
|
135 |
+
* **FastAPI:** As a RESTful API to handle image processing and model serving.
|
136 |
+
* **Redis:** A message broker for communication between the services.
|
137 |
+
|
138 |
+
### Portfolio Adaptation
|
139 |
+
For a live and cost-effective demo, this application has been adapted into a single-service
|
140 |
+
solution. The core logic of the FastAPI backend has been integrated directly into
|
141 |
+
the Streamlit app. This demonstrates the ability to adapt a solution for
|
142 |
+
specific deployment and resource constraints.
|
143 |
+
|
144 |
+
### Technologies Used
|
145 |
+
* **Streamlit:** For the interactive web interface.
|
146 |
+
* **TensorFlow:** For loading and running the pre-trained CNN model.
|
147 |
+
* **Pre-trained Model:** ResNet50 with weights trained on the ImageNet dataset.
|
148 |
+
"""
|
149 |
+
)
|