Skier8402 commited on
Commit
3e5dac2
·
verified ·
1 Parent(s): 895e0c3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +215 -40
app.py CHANGED
@@ -4,48 +4,189 @@ import pandas as pd
4
  import numpy as np
5
  import io
6
  import os
 
 
 
7
 
8
- def zoom_at(img, x, y, zoom):
9
- w, h = img.size
10
- zoom2 = zoom * 2
11
- img = img.crop((x - w / zoom2, y - h / zoom2,
12
- x + w / zoom2, y + h / zoom2))
13
- return img.resize((w, h), Image.LANCZOS)
14
 
15
- st.title("Cell Explorer")
 
 
 
 
 
 
 
 
 
16
 
17
- uploaded_files = st.file_uploader("Upload Images", accept_multiple_files=True, type="jpg")
 
 
 
 
 
 
 
18
 
19
- if uploaded_files:
20
- img_index = st.selectbox("Select Image", range(len(uploaded_files)))
21
- x = st.slider("X Coordinate", 0, 500, 205)
22
- y = st.slider("Y Coordinate", 0, 500, 250)
23
- zoom = st.slider("Zoom", 1, 10, 5)
24
- contrast = st.slider("Contrast", 0.0, 5.0, 1.0)
25
- brightness = st.slider("Brightness", 0.0, 5.0, 1.0)
26
- sharpness = st.slider("Sharpness", 0.0, 2.0, 1.0)
27
- save_image = st.checkbox("Save Image")
28
 
29
- img_data = uploaded_files[img_index].read()
30
- img = Image.open(io.BytesIO(img_data)).resize((500, 500))
31
- img_zoomed = zoom_at(img, x, y, zoom)
32
- img_contrast = ImageEnhance.Contrast(img_zoomed).enhance(contrast)
33
- img_bright = ImageEnhance.Brightness(img_contrast).enhance(brightness)
34
- img_sharp = ImageEnhance.Sharpness(img_bright).enhance(sharpness)
35
 
36
- if save_image:
37
- img_sharp.save("image-processed.jpg")
38
- st.success("Image saved as image-processed.jpg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
- st.image(img_sharp, caption="Processed Image", use_column_width=True)
 
 
 
 
 
 
 
41
 
42
- description = st.text_area("Describe the image", "")
43
- if st.button("Save Description"):
44
- with open("saved_image_description.txt", "w") as f:
 
 
 
 
 
 
 
 
 
 
 
 
45
  f.write(description)
46
- st.success("Description saved as saved_image_description.txt")
47
 
48
- if st.button("Save Image Parameters"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  params = {
50
  "coordinates_x": x,
51
  "coordinates_y": y,
@@ -54,13 +195,47 @@ if uploaded_files:
54
  "brightness": brightness,
55
  "sharpness": sharpness
56
  }
57
- with open("saved_image_parameters.json", "w") as f:
58
- f.write(pd.DataFrame([params]).to_json(orient="records"))
59
- st.success("Image parameters saved as saved_image_parameters.json")
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  if st.button("Rename Files"):
62
- file_ext = str(np.random.randint(100))
63
- os.rename("image-processed.jpg", f"img_processed{file_ext}.jpg")
64
- os.rename("saved_image_parameters.json", f"saved_image_parameters{file_ext}.json")
65
- os.rename("saved_image_description.txt", f"saved_image_description{file_ext}.txt")
66
- st.success("Files renamed successfully")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import numpy as np
5
  import io
6
  import os
7
+ import tempfile
8
+ import zipfile
9
+ import cv2
10
 
11
+ @st.cache_data
12
+ def zoom_at_cv(img, x, y, zoom):
13
+ """
14
+ Zoom into an image at a specific location using OpenCV.
 
 
15
 
16
+ Parameters:
17
+ ----------
18
+ img : PIL.Image
19
+ Input image.
20
+ x : float
21
+ X-coordinate of the zoom center.
22
+ y : float
23
+ Y-coordinate of the zoom center.
24
+ zoom : float
25
+ Zoom factor.
26
 
27
+ Returns:
28
+ -------
29
+ PIL.Image
30
+ Zoomed image resized to 500x500 pixels.
31
+ """
32
+ # Convert PIL Image to OpenCV format
33
+ img_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
34
+ h, w = img_cv.shape[:2]
35
 
36
+ # Calculate the region to crop
37
+ zoom_factor = zoom / 2
38
+ left = max(int(x - w * zoom_factor), 0)
39
+ top = max(int(y - h * zoom_factor), 0)
40
+ right = min(int(x + w * zoom_factor), w)
41
+ bottom = min(int(y + h * zoom_factor), h)
 
 
 
42
 
43
+ # Crop and resize
44
+ cropped = img_cv[top:bottom, left:right]
45
+ resized = cv2.resize(cropped, (500, 500), interpolation=cv2.INTER_LANCZOS4)
 
 
 
46
 
47
+ # Convert back to PIL format
48
+ pil_img = Image.fromarray(cv2.cvtColor(resized, cv2.COLOR_BGR2RGB))
49
+ return pil_img
50
+
51
+ @st.cache_data
52
+ def apply_enhancements_cv(img, x, y, zoom, contrast, brightness, sharpness):
53
+ """
54
+ Apply zoom and image enhancements using OpenCV.
55
+
56
+ Parameters:
57
+ ----------
58
+ img : PIL.Image
59
+ Input image.
60
+ x : float
61
+ X-coordinate of the zoom center.
62
+ y : float
63
+ Y-coordinate of the zoom center.
64
+ zoom : float
65
+ Zoom factor.
66
+ contrast : float
67
+ Contrast adjustment factor.
68
+ brightness : float
69
+ Brightness adjustment factor.
70
+ sharpness : float
71
+ Sharpness adjustment factor.
72
+
73
+ Returns:
74
+ -------
75
+ PIL.Image
76
+ Enhanced image resized to 500x500 pixels.
77
+ """
78
+ # Zoom the image
79
+ zoomed = zoom_at_cv(img, x, y, zoom)
80
+
81
+ # Apply image enhancements using PIL
82
+ enhanced_contrast = ImageEnhance.Contrast(zoomed).enhance(contrast)
83
+ enhanced_brightness = ImageEnhance.Brightness(enhanced_contrast).enhance(brightness)
84
+ enhanced_sharpness = ImageEnhance.Sharpness(enhanced_brightness).enhance(sharpness)
85
+
86
+ return enhanced_sharpness
87
+
88
+ def create_zip(processed_img, description, params):
89
+ """
90
+ Create a zip archive containing the processed image and annotations.
91
 
92
+ Parameters:
93
+ ----------
94
+ processed_img : PIL.Image
95
+ The processed image.
96
+ description : str
97
+ Description of the image.
98
+ params : dict
99
+ Image parameters.
100
 
101
+ Returns:
102
+ -------
103
+ bytes
104
+ Byte content of the zip file.
105
+ """
106
+ with tempfile.TemporaryDirectory() as tmpdirname:
107
+ img_path = os.path.join(tmpdirname, "processed_image.jpg")
108
+ desc_path = os.path.join(tmpdirname, "description.txt")
109
+ params_path = os.path.join(tmpdirname, "parameters.json")
110
+
111
+ # Save processed image
112
+ processed_img.save(img_path)
113
+
114
+ # Save description
115
+ with open(desc_path, "w") as f:
116
  f.write(description)
 
117
 
118
+ # Save parameters
119
+ pd.DataFrame([params]).to_json(params_path, orient="records")
120
+
121
+ # Create zip
122
+ zip_buffer = io.BytesIO()
123
+ with zipfile.ZipFile(zip_buffer, "w") as zipf:
124
+ zipf.write(img_path, arcname="processed_image.jpg")
125
+ zipf.write(desc_path, arcname="description.txt")
126
+ zipf.write(params_path, arcname="parameters.json")
127
+
128
+ zip_buffer.seek(0)
129
+ return zip_buffer
130
+
131
+ # Streamlit App Configuration
132
+ st.set_page_config(page_title="CLL Explorer", layout="wide")
133
+ st.title("CLL Explorer: Cell Image Analysis Prep Tool")
134
+
135
+ st.markdown("""
136
+ ### About This Application
137
+ This tool assists researchers in analyzing microscope images of any cell type.
138
+ - **Upload** microscope images.
139
+ - **Adjust** image view with zoom and enhancement controls.
140
+ - **Detect** and measure cells automatically.
141
+ - **Save** analysis results and annotations.
142
+ """)
143
+
144
+ uploaded_files = st.file_uploader("Upload Images", accept_multiple_files=True, type=["jpg", "png"])
145
+
146
+ if uploaded_files:
147
+ img_index = st.selectbox(
148
+ "Select Image",
149
+ range(len(uploaded_files)),
150
+ format_func=lambda x: uploaded_files[x].name
151
+ )
152
+ img_data = uploaded_files[img_index].read()
153
+ img = Image.open(io.BytesIO(img_data)).convert("RGB").resize((500, 500))
154
+
155
+ # Create columns with image on the left and controls on the right
156
+ image_col, controls_col = st.columns([3, 1])
157
+
158
+ with image_col:
159
+ st.subheader("Processed Image")
160
+ if 'processed_img' in st.session_state:
161
+ st.image(st.session_state.processed_img, use_column_width=True, caption="Processed Image")
162
+ else:
163
+ st.image(img, use_column_width=True, caption="Processed Image")
164
+
165
+ with controls_col:
166
+ st.subheader("Image Controls")
167
+ x = st.slider("X Coordinate", 0.0, 500.0, 250.0, step=1.0)
168
+ y = st.slider("Y Coordinate", 0.0, 500.0, 250.0, step=1.0)
169
+ zoom = st.slider("Zoom", 1.0, 10.0, 5.0, step=0.1)
170
+
171
+ with st.expander("Enhancement Settings", expanded=True):
172
+ contrast = st.slider("Contrast", 0.0, 5.0, 1.0, step=0.1)
173
+ brightness = st.slider("Brightness", 0.0, 5.0, 1.0, step=0.1)
174
+ sharpness = st.slider("Sharpness", 0.0, 2.0, 1.0, step=0.1)
175
+
176
+ if st.button("Apply Adjustments"):
177
+ processed_img = apply_enhancements_cv(img, x, y, zoom, contrast, brightness, sharpness)
178
+ st.session_state.processed_img = processed_img
179
+
180
+ # Display Original Image Below
181
+ st.subheader("Original Image")
182
+ st.image(img, use_column_width=True, caption="Original Image")
183
+
184
+ # Save and Export Options
185
+ st.markdown("---")
186
+ st.subheader("Save and Export Options")
187
+
188
+ with st.expander("Add Annotations", expanded=True):
189
+ description = st.text_area("Describe the image", "")
190
  params = {
191
  "coordinates_x": x,
192
  "coordinates_y": y,
 
195
  "brightness": brightness,
196
  "sharpness": sharpness
197
  }
 
 
 
198
 
199
+ if st.button("Prepare Download"):
200
+ if 'processed_img' in st.session_state and description:
201
+ zip_buffer = create_zip(st.session_state.processed_img, description, params)
202
+ st.download_button(
203
+ label="Download Zip",
204
+ data=zip_buffer,
205
+ file_name="processed_image_and_annotations.zip",
206
+ mime="application/zip"
207
+ )
208
+ st.success("Zip file is ready for download.")
209
+ else:
210
+ st.warning("Ensure that the processed image is available and description is provided.")
211
+
212
+ # Optional: Save Processed Image Locally
213
+ save_image = st.checkbox("Save Processed Image Locally")
214
+ if save_image:
215
+ if 'processed_img' in st.session_state:
216
+ processed_img_path = os.path.join("processed_image_500x500.jpg")
217
+ st.session_state.processed_img.save(processed_img_path)
218
+ st.success(f"Image saved as `{processed_img_path}`")
219
+ else:
220
+ st.warning("No processed image to save.")
221
+
222
+ # Optional: Rename Files
223
  if st.button("Rename Files"):
224
+ if 'processed_img' in st.session_state:
225
+ file_ext = str(np.random.randint(100))
226
+ new_img_name = f"img_processed_{file_ext}.jpg"
227
+ processed_img_path = "processed_image_500x500.jpg"
228
+ if os.path.exists(processed_img_path):
229
+ os.rename(processed_img_path, new_img_name)
230
+
231
+ # Save parameters and description
232
+ params_path = f"parameters_{file_ext}.json"
233
+ description_path = f"description_{file_ext}.txt"
234
+
235
+ pd.DataFrame([params]).to_json(params_path, orient="records")
236
+ with open(description_path, "w") as f:
237
+ f.write(description)
238
+
239
+ st.success(f"Files renamed to `{new_img_name}`, `{params_path}`, and `{description_path}`")
240
+ else:
241
+ st.warning("No processed image to rename.")