luxmorocco commited on
Commit
57fe26d
β€’
1 Parent(s): 9bcda32

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +286 -0
app.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit_drawable_canvas import st_canvas
3
+ from PIL import Image, ImageDraw, ImageFont
4
+ import numpy as np
5
+ import json
6
+ import io
7
+ from datetime import datetime
8
+ import os
9
+
10
+ def initialize_session_state():
11
+ """Initialize session state variables"""
12
+ if "annotations" not in st.session_state:
13
+ st.session_state["annotations"] = []
14
+ if "current_tool" not in st.session_state:
15
+ st.session_state["current_tool"] = "rect"
16
+ if "annotation_history" not in st.session_state:
17
+ st.session_state["annotation_history"] = []
18
+ if "is_authenticated" not in st.session_state:
19
+ st.session_state["is_authenticated"] = False
20
+
21
+ def authenticate_user():
22
+ """Handle user authentication"""
23
+ st.title("πŸ₯ Alyse AI Prescription Annotation Tool")
24
+
25
+ if not st.session_state["is_authenticated"]:
26
+ with st.form("login_form"):
27
+ st.write("Please enter your credentials to access the tool.")
28
+ username = st.text_input("Username:")
29
+ password = st.text_input("Password:", type="password")
30
+ submit = st.form_submit_button("Login")
31
+
32
+ if submit:
33
+ if username == "alyse" and password == "pharmacie":
34
+ st.session_state["is_authenticated"] = True
35
+ st.success("βœ… Access granted! You can now use the application.")
36
+ st.rerun()
37
+ else:
38
+ st.error("❌ Invalid credentials. Please try again.")
39
+ return False
40
+ return True
41
+
42
+ def create_sidebar_controls():
43
+ """Create sidebar controls for annotation settings"""
44
+ st.sidebar.header("πŸ“‹ Annotation Controls")
45
+
46
+ # Tool selection
47
+ tool_options = {
48
+ "rect": "Rectangle Box",
49
+ "line": "Line",
50
+ "circle": "Circle",
51
+ "freedraw": "Free Draw"
52
+ }
53
+ st.session_state["current_tool"] = st.sidebar.radio(
54
+ "Select Drawing Tool:",
55
+ options=list(tool_options.keys()),
56
+ format_func=lambda x: tool_options[x]
57
+ )
58
+
59
+ # Color selection
60
+ stroke_color = st.sidebar.color_picker("Stroke Color:", "#0000FF")
61
+ stroke_width = st.sidebar.slider("Stroke Width:", 1, 10, 2)
62
+
63
+ # Annotation categories
64
+ annotation_category = st.sidebar.selectbox(
65
+ "Annotation Category:",
66
+ ["Medication Name", "Dosage", "Frequency", "Duration", "Patient Info", "Doctor Info", "Other"]
67
+ )
68
+
69
+ return stroke_color, stroke_width, annotation_category
70
+
71
+ def preprocess_image(image):
72
+ """Resize image if too large for Streamlit Cloud"""
73
+ max_size = (800, 800) # Maximum dimensions
74
+
75
+ # Calculate aspect ratio
76
+ width_ratio = max_size[0] / image.size[0]
77
+ height_ratio = max_size[1] / image.size[1]
78
+ resize_ratio = min(width_ratio, height_ratio)
79
+
80
+ # Only resize if image is too large
81
+ if resize_ratio < 1:
82
+ new_size = (
83
+ int(image.size[0] * resize_ratio),
84
+ int(image.size[1] * resize_ratio)
85
+ )
86
+ return image.resize(new_size, Image.Resampling.LANCZOS)
87
+ return image
88
+
89
+ def handle_canvas_drawing(image, stroke_color, stroke_width, category):
90
+ """Handle canvas drawing with preprocessed image"""
91
+ processed_image = preprocess_image(image)
92
+
93
+ canvas_result = st_canvas(
94
+ fill_color="rgba(0, 0, 0, 0)",
95
+ stroke_width=stroke_width,
96
+ stroke_color=stroke_color,
97
+ background_image=processed_image,
98
+ update_streamlit=True,
99
+ width=processed_image.size[0],
100
+ height=processed_image.size[1],
101
+ drawing_mode=st.session_state["current_tool"],
102
+ display_toolbar=True,
103
+ key="canvas",
104
+ )
105
+
106
+ if canvas_result.json_data:
107
+ objects = canvas_result.json_data.get("objects", [])
108
+ for obj in objects:
109
+ if obj not in [ann.get("object_data") for ann in st.session_state["annotations"]]:
110
+ new_annotation = {
111
+ "object_data": obj,
112
+ "category": category,
113
+ "text": "",
114
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
115
+ }
116
+ st.session_state["annotations"].append(new_annotation)
117
+ st.session_state["annotation_history"].append(new_annotation)
118
+
119
+
120
+ def display_annotation_list():
121
+ """Display and manage list of annotations"""
122
+ st.sidebar.subheader("πŸ“ Current Annotations")
123
+
124
+ for i, annotation in enumerate(st.session_state["annotations"]):
125
+ with st.sidebar.expander(f"Annotation {i+1} - {annotation['category']}"):
126
+ # Update annotation text
127
+ new_text = st.text_area(
128
+ "Description:",
129
+ annotation["text"],
130
+ key=f"text_input_{i}"
131
+ )
132
+ st.session_state["annotations"][i]["text"] = new_text
133
+
134
+ # Display annotation details
135
+ st.write(f"Created: {annotation['timestamp']}")
136
+
137
+ # Delete individual annotation
138
+ if st.button("Delete", key=f"delete_{i}"):
139
+ st.session_state["annotations"].pop(i)
140
+ st.rerun()
141
+ def save_annotations(image, uploaded_file):
142
+ """Handle saving and downloading annotations"""
143
+ st.sidebar.subheader("πŸ’Ύ Save & Export")
144
+
145
+ if st.sidebar.button("Save and Download"):
146
+ # Create annotated image
147
+ annotated_image = image.copy()
148
+ draw = ImageDraw.Draw(annotated_image)
149
+
150
+ # Draw annotations
151
+ for annotation in st.session_state["annotations"]:
152
+ obj = annotation["object_data"]
153
+ if obj["type"] == "rect":
154
+ draw.rectangle(
155
+ [obj["left"], obj["top"],
156
+ obj["left"] + obj["width"],
157
+ obj["top"] + obj["height"]],
158
+ outline=obj["stroke"],
159
+ width=int(obj["strokeWidth"])
160
+ )
161
+ # Add text label
162
+ draw.text(
163
+ (obj["left"], obj["top"] - 15),
164
+ f"{annotation['category']}: {annotation['text']}",
165
+ fill=obj["stroke"]
166
+ )
167
+
168
+ # Save files
169
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
170
+
171
+ # Save annotated image
172
+ img_buffer = io.BytesIO()
173
+ annotated_image.save(img_buffer, format="JPEG", quality=95)
174
+ img_buffer.seek(0)
175
+
176
+ # Save annotations JSON
177
+ annotations_data = {
178
+ "image_id": uploaded_file.name,
179
+ "timestamp": timestamp,
180
+ "annotations": [
181
+ {
182
+ "category": ann["category"],
183
+ "text": ann["text"],
184
+ "timestamp": ann["timestamp"],
185
+ "object_data": ann["object_data"]
186
+ }
187
+ for ann in st.session_state["annotations"]
188
+ ]
189
+ }
190
+
191
+ # Convert JSON to string first, then to bytes
192
+ json_str = json.dumps(annotations_data, indent=2)
193
+ json_bytes = json_str.encode('utf-8')
194
+ json_buffer = io.BytesIO(json_bytes)
195
+
196
+ # Download buttons
197
+ col1, col2 = st.sidebar.columns(2)
198
+ with col1:
199
+ st.download_button(
200
+ "πŸ“· Download Image",
201
+ data=img_buffer,
202
+ file_name=f"annotated_{timestamp}.jpg",
203
+ mime="image/jpeg"
204
+ )
205
+ with col2:
206
+ st.download_button(
207
+ "πŸ“„ Download JSON",
208
+ data=json_buffer,
209
+ file_name=f"annotations_{timestamp}.json",
210
+ mime="application/json"
211
+ )
212
+
213
+ st.sidebar.success("βœ… Files saved successfully!")
214
+ def main():
215
+ """Main application logic"""
216
+ initialize_session_state()
217
+
218
+ if not authenticate_user():
219
+ return
220
+
221
+ # File upload
222
+ uploaded_file = st.file_uploader(
223
+ "πŸ“€ Upload Prescription Image",
224
+ type=["jpg", "jpeg", "png"],
225
+ help="Upload a clear image of the prescription to annotate"
226
+ )
227
+
228
+ if uploaded_file:
229
+ # Load and display image
230
+ image = Image.open(uploaded_file).convert("RGB")
231
+
232
+ # Create two columns for layout
233
+ col1, col2 = st.columns([2, 1])
234
+
235
+ with col1:
236
+ # Get annotation settings
237
+ stroke_color, stroke_width, category = create_sidebar_controls()
238
+
239
+ # Handle canvas drawing
240
+ handle_canvas_drawing(image, stroke_color, stroke_width, category)
241
+
242
+ with col2:
243
+ # Undo/Redo buttons
244
+ col_undo, col_redo, col_clear = st.columns(3)
245
+ with col_undo:
246
+ if st.button("↩️ Undo") and st.session_state["annotations"]:
247
+ last_annotation = st.session_state["annotations"].pop()
248
+ st.session_state["annotation_history"].append(last_annotation)
249
+
250
+ with col_redo:
251
+ if st.button("β†ͺ️ Redo") and st.session_state["annotation_history"]:
252
+ st.session_state["annotations"].append(
253
+ st.session_state["annotation_history"].pop()
254
+ )
255
+
256
+ with col_clear:
257
+ if st.button("πŸ—‘οΈ Clear All"):
258
+ st.session_state["annotations"] = []
259
+ st.session_state["annotation_history"] = []
260
+
261
+ # Display annotation list
262
+ display_annotation_list()
263
+
264
+ # Save and download options
265
+ save_annotations(image, uploaded_file)
266
+
267
+ # Footer
268
+ st.markdown("---")
269
+ st.markdown(
270
+ """
271
+ <div style='text-align: center'>
272
+ <p><strong>Alyse AI Prescription Annotation Tool v2.0</strong></p>
273
+ <p>Created by: Jad Tounsi El Azzoiani and Amine Tahiri</p>
274
+ <p>Last Updated: November 2024</p>
275
+ </div>
276
+ """,
277
+ unsafe_allow_html=True
278
+ )
279
+
280
+ if __name__ == "__main__":
281
+ st.set_page_config(
282
+ page_title="Alyse AI Prescription Annotator",
283
+ page_icon="πŸ₯",
284
+ layout="wide"
285
+ )
286
+ main()