nmariotto commited on
Commit
3de45f5
·
verified ·
1 Parent(s): 7de9783

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -118
app.py CHANGED
@@ -16,60 +16,7 @@ from googleapiclient.http import MediaIoBaseUpload
16
  import gspread
17
  import time
18
 
19
- st.markdown("""
20
- <style>
21
- html, body, [data-testid="stApp"] {
22
- background-color: #0f1117;
23
- color: #f2f2f2;
24
- font-family: 'Helvetica Neue', sans-serif;
25
- }
26
-
27
- [data-testid="stHeader"] {
28
- background-color: #0f1117;
29
- }
30
-
31
- .stButton > button {
32
- background-color: #1d4ed8;
33
- color: white;
34
- font-weight: 600;
35
- border: none;
36
- padding: 0.5em 1.2em;
37
- border-radius: 6px;
38
- }
39
-
40
- .stButton > button:hover {
41
- background-color: #1e40af;
42
- }
43
-
44
- label, .stRadio label, .stSelectbox label,
45
- .stTextInput label, .stTextArea label {
46
- color: #d1d5db !important;
47
- font-weight: 500;
48
- opacity: 1 !important;
49
- }
50
-
51
- div[role="radiogroup"] label,
52
- .stRadio > div label {
53
- opacity: 1 !important;
54
- color: #d1d5db !important;
55
- }
56
-
57
- input, textarea {
58
- color: #ffffff !important;
59
- background-color: #2a2e39;
60
- border-radius: 4px;
61
- border: none;
62
- padding: 0.4em;
63
- }
64
-
65
- .stMarkdown h1, .stMarkdown h2, .stMarkdown h3 {
66
- color: #ffffff;
67
- }
68
- </style>
69
- """, unsafe_allow_html=True)
70
-
71
-
72
- # 🔥 Initialize Roboflow
73
  API_KEY = "mGkz7QhkhD90YfeiaOxV"
74
  rf = roboflow.Roboflow(api_key=API_KEY)
75
  project = rf.workspace().project("pre-eclampsia-vhaot")
@@ -78,10 +25,10 @@ model.confidence = 80
78
  model.overlap = 25
79
  dpi_value = 300
80
 
81
- with st.expander("⚙️ Advanced Settings", expanded=True):
82
- model.confidence = st.slider("Model Confidence (%)", 20, 100, 80)
83
 
84
- # 📁 Setup Google Drive and Sheets
85
  scope = ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/spreadsheets"]
86
  credentials_dict = json.loads(st.secrets["gcp_service_account"])
87
  credentials = service_account.Credentials.from_service_account_info(credentials_dict, scopes=scope)
@@ -89,7 +36,7 @@ drive_service = build("drive", "v3", credentials=credentials)
89
  sheets_client = gspread.authorize(credentials)
90
  sheet = sheets_client.open_by_url(st.secrets["feedback_sheet_url"]).sheet1
91
 
92
- # 📌 Helper Functions
93
  def calculate_polygon_area(points):
94
  polygon = Polygon([(p['x'], p['y']) for p in points])
95
  return polygon.area
@@ -121,7 +68,10 @@ def find_or_create_folder(folder_name, parent=None):
121
  folders = results.get('files', [])
122
  if folders:
123
  return folders[0]['id']
124
- file_metadata = {'name': folder_name, 'mimeType': 'application/vnd.google-apps.folder'}
 
 
 
125
  if parent:
126
  file_metadata['parents'] = [parent]
127
  file = drive_service.files().create(body=file_metadata, fields='id').execute()
@@ -137,18 +87,18 @@ def process_image(uploaded_file):
137
  prediction = safe_predict(temp_file.name)
138
  if not prediction:
139
  return {
140
- "Image": safe_name,
141
- "NoSegmentation": True,
142
- "Display": image,
143
  "Original": get_image_bytes(image)
144
  }
145
  prediction_data = prediction.json()
146
 
147
  if not prediction_data["predictions"]:
148
  return {
149
- "Image": safe_name,
150
- "NoSegmentation": True,
151
- "Display": image,
152
  "Original": get_image_bytes(image)
153
  }
154
 
@@ -170,19 +120,19 @@ def process_image(uploaded_file):
170
  fig2, ax2 = plt.subplots(figsize=(6, 6), dpi=dpi_value)
171
  ax2.plot(x, y, 'r-', linewidth=2)
172
  ax2.scatter(x, y, color='red', s=5)
173
- ax2.set_title("Polygon Contour")
174
  ax2.grid()
175
  plt.savefig(polygon_buffer, format="png", bbox_inches='tight')
176
  plt.close()
177
 
178
  return {
179
- "Image": safe_name,
180
- "Segmented Area (px²)": area,
181
  "Original": original_buffer,
182
- "Segmented": segmented_buffer,
183
- "Polygon": polygon_buffer,
184
- "Display": image,
185
- "NoSegmentation": False
186
  }
187
 
188
  except:
@@ -194,98 +144,100 @@ def get_image_bytes(image):
194
  buf.seek(0)
195
  return buf
196
 
197
- # 🗂️ Main Interface
198
- st.title("Image Segmentation - Roboflow")
199
- upload_option = st.radio("Select upload type:", ["Single Image", "Folder of Images"])
200
  results = []
201
 
202
- if upload_option == "Single Image":
203
- uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg", "tiff"])
204
  if uploaded_file:
205
  result = process_image(uploaded_file)
206
  if result:
207
  results.append(result)
208
- st.image(result["Display"], caption=f"Original Image - {result['Image']}", use_container_width=True)
209
- if not result["NoSegmentation"]:
210
- st.image(result["Segmented"], caption="Segmentation", use_container_width=True)
211
- st.image(result["Polygon"], caption="Polygon", use_container_width=True)
212
- st.write(f"📏 **Segmented Area:** {result['Segmented Area (px²)']:.2f} pixels²")
213
  else:
214
- st.warning("⚠️ No segmentation was detected in this image.")
215
 
216
- elif upload_option == "Folder of Images":
217
- uploaded_files = st.file_uploader("Upload multiple images", type=["png", "jpg", "jpeg", "tiff"], accept_multiple_files=True)
218
  if uploaded_files:
219
  with ThreadPoolExecutor(max_workers=4) as executor:
220
  processed = list(executor.map(process_image, uploaded_files))
221
 
222
- failures = [f.name for f, r in zip(uploaded_files, processed) if r and r.get("NoSegmentation")]
223
- if failures:
224
- st.warning(f"⚠️ {len(failures)} image(s) without detected segmentation:\n\n- " + "\n- ".join(failures))
225
 
226
  zip_images_buffer = BytesIO()
227
  with zipfile.ZipFile(zip_images_buffer, "w") as zip_file:
228
  for result in processed:
229
  if result:
230
  results.append(result)
231
- st.image(result["Display"], caption=f"Original Image - {result['Image']}", use_container_width=True)
232
- if not result["NoSegmentation"]:
233
- st.image(result["Segmented"], caption="Segmentation", use_container_width=True)
234
- st.image(result["Polygon"], caption="Polygon", use_container_width=True)
235
- st.write(f"📏 **Segmented Area:** {result['Segmented Area (px²)']:.2f} pixels²")
236
- zip_file.writestr(f"segmented_{result['Image']}.png", result["Segmented"].getvalue())
237
- zip_file.writestr(f"polygon_{result['Image']}.png", result["Polygon"].getvalue())
238
 
239
  zip_images_buffer.seek(0)
240
 
241
  if results:
242
  df = pd.DataFrame([
243
- { "Image": r["Image"], "Segmented Area (px²)": r["Segmented Area (px²)"] if not r["NoSegmentation"] else "No Segmentation" }
244
  for r in results
245
  ])
246
- st.markdown("### 📊 Results Table")
247
  st.dataframe(df)
248
 
249
  excel_buffer = BytesIO()
250
  df.to_excel(excel_buffer, index=False)
251
  excel_buffer.seek(0)
252
 
253
- st.download_button("📥 Download Table (Excel)", data=excel_buffer, file_name="segmentation_results.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
254
- st.download_button("📥 Download Segmented Images", data=zip_images_buffer, file_name="segmented_images.zip", mime="application/zip")
255
 
256
- # 📝 Manual Feedback
257
  if results:
258
  st.markdown("## 📝 Feedback")
259
- selected_image = st.selectbox("Select an image to evaluate:", [r["Image"] for r in results])
260
- evaluation = st.radio("How do you rate this segmentation?", ["Excellent", "Acceptable", "Poor", "No segmentation"], horizontal=True)
261
- comment = st.text_area("Comments (optional):")
262
 
263
- if st.button("Save Feedback"):
264
- row = [selected_image, evaluation, comment]
265
  sheet.append_row(row)
266
 
267
- if evaluation in ["Acceptable", "Poor", "No segmentation"]:
268
- suffix = "acceptable" if evaluation == "Acceptable" else "poor" if evaluation == "Poor" else "no_segmentation"
269
- parent_folder = find_or_create_folder("Segmentation Feedback")
270
- subfolder = find_or_create_folder(selected_image.replace(".png", ""), parent_folder)
271
 
272
  for r in results:
273
- if r["Image"] == selected_image:
274
- resized_original = resize_image(r["Display"])
 
275
  buffer = BytesIO()
276
  resized_original.save(buffer, format="PNG")
277
  buffer.seek(0)
278
- upload_to_drive(buffer, f"original_{suffix}.png", subfolder)
279
 
280
- if evaluation != "No segmentation" and "Segmented" in r and "Polygon" in r:
281
- resized_segmented = resize_image(Image.open(BytesIO(r["Segmented"].getvalue())))
282
- resized_polygon = resize_image(Image.open(BytesIO(r["Polygon"].getvalue())))
 
283
 
284
- for img_obj, name in zip([resized_segmented, resized_polygon], ["segmented", "polygon"]):
285
  buffer = BytesIO()
286
  img_obj.save(buffer, format="PNG")
287
  buffer.seek(0)
288
- upload_to_drive(buffer, f"{name}_{suffix}.png", subfolder)
289
  break
290
 
291
- st.success("✅ Feedback successfully saved!")
 
16
  import gspread
17
  import time
18
 
19
+ # 🔥 Inicializar Roboflow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  API_KEY = "mGkz7QhkhD90YfeiaOxV"
21
  rf = roboflow.Roboflow(api_key=API_KEY)
22
  project = rf.workspace().project("pre-eclampsia-vhaot")
 
25
  model.overlap = 25
26
  dpi_value = 300
27
 
28
+ with st.expander("⚙️ Configurações Avançadas", expanded=True):
29
+ model.confidence = st.slider("Confiança do Modelo (%)", 20, 100, 80)
30
 
31
+ # 📁 Setup Google Drive e Sheets
32
  scope = ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/spreadsheets"]
33
  credentials_dict = json.loads(st.secrets["gcp_service_account"])
34
  credentials = service_account.Credentials.from_service_account_info(credentials_dict, scopes=scope)
 
36
  sheets_client = gspread.authorize(credentials)
37
  sheet = sheets_client.open_by_url(st.secrets["feedback_sheet_url"]).sheet1
38
 
39
+ # 📌 Funções auxiliares
40
  def calculate_polygon_area(points):
41
  polygon = Polygon([(p['x'], p['y']) for p in points])
42
  return polygon.area
 
68
  folders = results.get('files', [])
69
  if folders:
70
  return folders[0]['id']
71
+ file_metadata = {
72
+ 'name': folder_name,
73
+ 'mimeType': 'application/vnd.google-apps.folder'
74
+ }
75
  if parent:
76
  file_metadata['parents'] = [parent]
77
  file = drive_service.files().create(body=file_metadata, fields='id').execute()
 
87
  prediction = safe_predict(temp_file.name)
88
  if not prediction:
89
  return {
90
+ "Imagem": safe_name,
91
+ "SemSegmentacao": True,
92
+ "Exibir": image,
93
  "Original": get_image_bytes(image)
94
  }
95
  prediction_data = prediction.json()
96
 
97
  if not prediction_data["predictions"]:
98
  return {
99
+ "Imagem": safe_name,
100
+ "SemSegmentacao": True,
101
+ "Exibir": image,
102
  "Original": get_image_bytes(image)
103
  }
104
 
 
120
  fig2, ax2 = plt.subplots(figsize=(6, 6), dpi=dpi_value)
121
  ax2.plot(x, y, 'r-', linewidth=2)
122
  ax2.scatter(x, y, color='red', s=5)
123
+ ax2.set_title("Contorno do Polígono")
124
  ax2.grid()
125
  plt.savefig(polygon_buffer, format="png", bbox_inches='tight')
126
  plt.close()
127
 
128
  return {
129
+ "Imagem": safe_name,
130
+ "Área Segmentada (px²)": area,
131
  "Original": original_buffer,
132
+ "Segmentada": segmented_buffer,
133
+ "Poligono": polygon_buffer,
134
+ "Exibir": image,
135
+ "SemSegmentacao": False
136
  }
137
 
138
  except:
 
144
  buf.seek(0)
145
  return buf
146
 
147
+ # 🗂️ Interface principal
148
+ st.title("Segmentação de Imagens - Roboflow")
149
+ upload_option = st.radio("Escolha o tipo de upload:", ["Imagem única", "Pasta de imagens"])
150
  results = []
151
 
152
+ if upload_option == "Imagem única":
153
+ uploaded_file = st.file_uploader("Escolha uma imagem", type=["png", "jpg", "jpeg", "tiff"])
154
  if uploaded_file:
155
  result = process_image(uploaded_file)
156
  if result:
157
  results.append(result)
158
+ st.image(result["Exibir"], caption=f"Imagem Original - {result['Imagem']}", use_container_width=True)
159
+ if not result["SemSegmentacao"]:
160
+ st.image(result["Segmentada"], caption="Segmentação", use_container_width=True)
161
+ st.image(result["Poligono"], caption="Polígono", use_container_width=True)
162
+ st.write(f"📏 **Área segmentada:** {result['Área Segmentada (px²)']:.2f} pixels²")
163
  else:
164
+ st.warning("⚠️ Nenhuma segmentação foi detectada nesta imagem.")
165
 
166
+ elif upload_option == "Pasta de imagens":
167
+ uploaded_files = st.file_uploader("Envie várias imagens", type=["png", "jpg", "jpeg", "tiff"], accept_multiple_files=True)
168
  if uploaded_files:
169
  with ThreadPoolExecutor(max_workers=4) as executor:
170
  processed = list(executor.map(process_image, uploaded_files))
171
 
172
+ falhas = [f.name for f, r in zip(uploaded_files, processed) if r and r.get("SemSegmentacao")]
173
+ if falhas:
174
+ st.warning(f"⚠️ {len(falhas)} imagem(ns) sem segmentação detectada:\n\n- " + "\n- ".join(falhas))
175
 
176
  zip_images_buffer = BytesIO()
177
  with zipfile.ZipFile(zip_images_buffer, "w") as zip_file:
178
  for result in processed:
179
  if result:
180
  results.append(result)
181
+ st.image(result["Exibir"], caption=f"Imagem Original - {result['Imagem']}", use_container_width=True)
182
+ if not result["SemSegmentacao"]:
183
+ st.image(result["Segmentada"], caption="Segmentação", use_container_width=True)
184
+ st.image(result["Poligono"], caption="Polígono", use_container_width=True)
185
+ st.write(f"📏 **Área segmentada:** {result['Área Segmentada (px²)']:.2f} pixels²")
186
+ zip_file.writestr(f"segmentada_{result['Imagem']}.png", result["Segmentada"].getvalue())
187
+ zip_file.writestr(f"poligono_{result['Imagem']}.png", result["Poligono"].getvalue())
188
 
189
  zip_images_buffer.seek(0)
190
 
191
  if results:
192
  df = pd.DataFrame([
193
+ { "Imagem": r["Imagem"], "Área Segmentada (px²)": r["Área Segmentada (px²)"] if not r["SemSegmentacao"] else "Sem Segmentação" }
194
  for r in results
195
  ])
196
+ st.markdown("### 📊 Tabela de Resultados")
197
  st.dataframe(df)
198
 
199
  excel_buffer = BytesIO()
200
  df.to_excel(excel_buffer, index=False)
201
  excel_buffer.seek(0)
202
 
203
+ st.download_button("📥 Baixar Tabela (Excel)", data=excel_buffer, file_name="resultados_segmentacao.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
204
+ st.download_button("📥 Baixar Imagens Segmentadas", data=zip_images_buffer, file_name="imagens_segmentadas.zip", mime="application/zip")
205
 
206
+ # 📝 Feedback manual
207
  if results:
208
  st.markdown("## 📝 Feedback")
209
+ imagem_escolhida = st.selectbox("Selecione uma imagem para avaliar:", [r["Imagem"] for r in results])
210
+ avaliacao = st.radio("Como você avalia essa segmentação?", ["Ótima", "Aceitável", "Ruim", "Sem segmentação"], horizontal=True)
211
+ observacao = st.text_area("Observações (opcional):")
212
 
213
+ if st.button("Salvar Feedback"):
214
+ row = [imagem_escolhida, avaliacao, observacao]
215
  sheet.append_row(row)
216
 
217
+ if avaliacao in ["Aceitável", "Ruim", "Sem segmentação"]:
218
+ sufixo = "aceitavel" if avaliacao == "Aceitável" else "ruim" if avaliacao == "Ruim" else "sem_segmentacao"
219
+ parent_folder = find_or_create_folder("Feedback Segmentacoes")
220
+ subfolder = find_or_create_folder(imagem_escolhida.replace(".png", ""), parent_folder)
221
 
222
  for r in results:
223
+ if r["Imagem"] == imagem_escolhida:
224
+ # Sempre salva a original
225
+ resized_original = resize_image(r["Exibir"])
226
  buffer = BytesIO()
227
  resized_original.save(buffer, format="PNG")
228
  buffer.seek(0)
229
+ upload_to_drive(buffer, f"original_{sufixo}.png", subfolder)
230
 
231
+ # salva segmentada e polígono se houver segmentação
232
+ if avaliacao != "Sem segmentação" and "Segmentada" in r and "Poligono" in r:
233
+ resized_segmented = resize_image(Image.open(BytesIO(r["Segmentada"].getvalue())))
234
+ resized_polygon = resize_image(Image.open(BytesIO(r["Poligono"].getvalue())))
235
 
236
+ for img_obj, nome in zip([resized_segmented, resized_polygon], ["segmentada", "poligono"]):
237
  buffer = BytesIO()
238
  img_obj.save(buffer, format="PNG")
239
  buffer.seek(0)
240
+ upload_to_drive(buffer, f"{nome}_{sufixo}.png", subfolder)
241
  break
242
 
243
+ st.success("✅ Feedback salvo com sucesso!")