nmariotto commited on
Commit
c4bff26
·
verified ·
1 Parent(s): f68b03c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -138
app.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import streamlit as st
2
  import roboflow
3
  import pandas as pd
@@ -10,29 +12,17 @@ from shapely.geometry import Polygon
10
  from PIL import Image
11
  from io import BytesIO
12
  from concurrent.futures import ThreadPoolExecutor
13
- from google.oauth2.credentials import Credentials
14
  from googleapiclient.discovery import build
15
  from googleapiclient.http import MediaIoBaseUpload
16
  import gspread
17
  import time
18
 
19
- # --- SEÇÃO DE SECRETS CORRIGIDA PARA LER VARIÁVEIS DE AMBIENTE ---
20
-
21
- # 🔥 Initialize Roboflow
22
- # Lê as variáveis de ambiente passadas pelo comando 'docker run -e'
23
- API_KEY = os.environ.get("roboflow_api_key" )
24
- roboflow_workspace = os.environ.get("roboflow_workspace")
25
- roboflow_project = os.environ.get("roboflow_project")
26
- roboflow_version = os.environ.get("roboflow_version")
27
-
28
- # Validação para garantir que as variáveis foram passadas
29
- if not all([API_KEY, roboflow_workspace, roboflow_project, roboflow_version]):
30
- st.error("Um ou mais segredos do Roboflow não foram encontrados! Verifique as variáveis de ambiente: roboflow_api_key, roboflow_workspace, roboflow_project, roboflow_version.")
31
- st.stop()
32
-
33
  rf = roboflow.Roboflow(api_key=API_KEY)
34
- project = rf.workspace(roboflow_workspace).project(roboflow_project)
35
- model = project.version(int(roboflow_version)).model # Garante que a versão seja um inteiro
36
  model.confidence = 80
37
  model.overlap = 25
38
  dpi_value = 300
@@ -40,37 +30,15 @@ dpi_value = 300
40
  with st.expander("⚙️ Advanced Settings", expanded=True):
41
  model.confidence = st.slider("Model Confidence (%)", 20, 100, 80)
42
 
43
- # 📁 Setup Google Drive and Sheets
44
- # a variável de ambiente para a URL da planilha
45
- feedback_sheet_url = os.environ.get("feedback_sheet_url")
46
- if not feedback_sheet_url:
47
- st.error("Secret 'feedback_sheet_url' não foi encontrado! Verifique a variável de ambiente.")
48
- st.stop()
49
-
50
- # Lê a variável de ambiente para o token do Google
51
- google_token_json = os.environ.get("GOOGLE_TOKEN_JSON")
52
- if not google_token_json:
53
- st.error("Secret 'GOOGLE_TOKEN_JSON' não foi encontrado! Verifique a variável de ambiente.")
54
- st.stop()
55
-
56
- try:
57
- # Autenticação do Google a partir do token JSON passado como variável de ambiente
58
- scope = ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/spreadsheets"]
59
- # Carrega as informações do token a partir da string JSON
60
- token_info = json.loads(google_token_json )
61
- creds = Credentials.from_authorized_user_info(token_info, scope)
62
-
63
- drive_service = build("drive", "v3", credentials=creds)
64
- sheets_client = gspread.authorize(creds)
65
- sheet = sheets_client.open_by_url(feedback_sheet_url).sheet1
66
- except Exception as e:
67
- st.error(f"Falha ao autenticar com o Google. Verifique o GOOGLE_TOKEN_JSON. Erro: {e}")
68
- st.stop()
69
-
70
- # --- FIM DA SEÇÃO DE SECRETS ---
71
-
72
-
73
- # 📌 Helper Functions
74
  def calculate_polygon_area(points):
75
  polygon = Polygon([(p['x'], p['y']) for p in points])
76
  return polygon.area
@@ -79,7 +47,7 @@ def safe_predict(image_path):
79
  for attempt in range(3):
80
  try:
81
  return model.predict(image_path)
82
- except Exception:
83
  time.sleep(1)
84
  return None
85
 
@@ -127,18 +95,18 @@ def process_image(uploaded_file):
127
  prediction = safe_predict(temp_file.name)
128
  if not prediction:
129
  return {
130
- "Image": safe_name,
131
- "NoSegmentation": True,
132
- "Display": image,
133
  "Original": get_image_bytes(image)
134
  }
135
  prediction_data = prediction.json()
136
 
137
  if not prediction_data["predictions"]:
138
  return {
139
- "Image": safe_name,
140
- "NoSegmentation": True,
141
- "Display": image,
142
  "Original": get_image_bytes(image)
143
  }
144
 
@@ -160,25 +128,25 @@ def process_image(uploaded_file):
160
  fig2, ax2 = plt.subplots(figsize=(6, 6), dpi=dpi_value)
161
  ax2.plot(x, y, 'r-', linewidth=2)
162
  ax2.scatter(x, y, color='red', s=5)
163
- ax2.set_title("Polygon Outline")
164
  ax2.grid()
165
  plt.savefig(polygon_buffer, format="png", bbox_inches='tight')
166
  plt.close()
167
 
168
  return {
169
- "Image": safe_name,
170
- "Segmented Area (px²)": area,
171
  "Original": original_buffer,
172
- "Segmented": segmented_buffer,
173
- "Polygon": polygon_buffer,
174
- "Display": image,
175
- "NoSegmentation": False
176
  }
177
 
178
- except Exception:
179
  return None
180
 
181
- # 🗂️ Main Interface
182
  st.title("IA Model Segmentation")
183
  upload_option = st.radio("Choose upload type:", ["Single image", "Image folder"])
184
  results = []
@@ -189,16 +157,16 @@ if upload_option == "Single image":
189
  result = process_image(uploaded_file)
190
  if result:
191
  results.append(result)
192
- st.image(result["Display"], caption=f"Original Image - {result['Image']}", use_container_width=True)
193
- if not result["NoSegmentation"]:
194
- st.image(result["Segmented"], caption="Segmentation", use_container_width=True)
195
- st.image(result["Polygon"], caption="Polygon", use_container_width=True)
196
- st.write(f"📏 **Segmented Area:** {result['Segmented Area (px²)']:.2f} pixels²")
197
 
198
  st.download_button(
199
  label="📥 Download Segmented Image",
200
- data=result["Segmented"],
201
- file_name="segmented_image.png",
202
  mime="image/png"
203
  )
204
  else:
@@ -210,28 +178,28 @@ elif upload_option == "Image folder":
210
  with ThreadPoolExecutor(max_workers=4) as executor:
211
  processed = list(executor.map(process_image, uploaded_files))
212
 
213
- failures = [f.name for f, r in zip(uploaded_files, processed) if r and r.get("NoSegmentation")]
214
- if failures:
215
- st.warning(f"⚠️ {len(failures)} image(s) with no segmentation detected:\n\n- " + "\n- ".join(failures))
216
 
217
  zip_images_buffer = BytesIO()
218
  with zipfile.ZipFile(zip_images_buffer, "w") as zip_file:
219
  for result in processed:
220
  if result:
221
  results.append(result)
222
- st.image(result["Display"], caption=f"Original Image - {result['Image']}", use_container_width=True)
223
- if not result["NoSegmentation"]:
224
- st.image(result["Segmented"], caption="Segmentation", use_container_width=True)
225
- st.image(result["Polygon"], caption="Polygon", use_container_width=True)
226
- st.write(f"📏 **Segmented Area:** {result['Segmented Area (px²)']:.2f} pixels²")
227
- zip_file.writestr(f"segmented_{result['Image']}.png", result["Segmented"].getvalue())
228
- zip_file.writestr(f"polygon_{result['Image']}.png", result["Polygon"].getvalue())
229
 
230
  zip_images_buffer.seek(0)
231
 
232
  if results:
233
  df = pd.DataFrame([
234
- { "Image": r["Image"], "Segmented Area (px²)": r["Segmented Area (px²)"] if not r["NoSegmentation"] else "No Segmentation" }
235
  for r in results
236
  ])
237
  st.markdown("### 📊 Results Table")
@@ -241,63 +209,42 @@ elif upload_option == "Image folder":
241
  df.to_excel(excel_buffer, index=False)
242
  excel_buffer.seek(0)
243
 
244
- st.download_button("📥 Download Table (Excel)", data=excel_buffer, file_name="segmentation_results.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
245
- st.download_button("📥 Download Segmented Images", data=zip_images_buffer, file_name="segmented_images.zip", mime="application/zip")
246
 
247
  # 📝 Manual Feedback
248
  if results:
249
  st.markdown("## 📝 Feedback")
250
-
251
- available_images = [r["Image"] for r in results]
252
- if not available_images:
253
- st.warning("No images were processed to provide feedback.")
254
- else:
255
- chosen_image = st.selectbox("Select an image to evaluate:", available_images)
256
- evaluation = st.radio("How do you rate this segmentation?", ["Great", "Acceptable", "Bad", "No segmentation"], horizontal=True)
257
- observation = st.text_area("Observations (optional):")
258
-
259
- if st.button("Save Feedback"):
260
- # 1. Save feedback to the spreadsheet
261
- row = [chosen_image, evaluation, observation]
262
- sheet.append_row(row)
263
- st.info("Feedback saved to spreadsheet...")
264
-
265
- # 2. If the evaluation is not "Great", upload images to Google Drive
266
- if evaluation in ["Acceptable", "Bad", "No segmentation"]:
267
- st.info("Starting image upload to Google Drive...")
268
- try:
269
- suffix_map = {"Acceptable": "acceptable", "Bad": "bad", "No segmentation": "no_segmentation"}
270
- suffix = suffix_map.get(evaluation, "feedback")
271
-
272
- parent_folder = find_or_create_folder("Segmentation Feedback")
273
- folder_name = os.path.splitext(chosen_image)[0]
274
- subfolder = find_or_create_folder(folder_name, parent=parent_folder)
275
-
276
- result_to_upload = next((r for r in results if r["Image"] == chosen_image), None)
277
-
278
- if result_to_upload:
279
- # Helper function to resize and upload
280
- def upload_resized_image(image_obj, file_prefix, folder_id):
281
- resized_pil_img = resize_image(image_obj)
282
- buffer = get_image_bytes(resized_pil_img)
283
- upload_to_drive(buffer, f"{file_prefix}_{suffix}.png", folder_id)
284
- st.write(f" - Upload of {file_prefix}_{suffix}.png complete.")
285
-
286
- # Upload original image
287
- original_pil = result_to_upload["Display"]
288
- upload_resized_image(original_pil, "original", subfolder)
289
-
290
- # Upload segmented and polygon images, if applicable
291
- if evaluation != "No segmentation" and not result_to_upload.get("NoSegmentation", True):
292
- segmented_pil = Image.open(result_to_upload["Segmented"])
293
- upload_resized_image(segmented_pil, "segmented", subfolder)
294
-
295
- polygon_pil = Image.open(result_to_upload["Polygon"])
296
- upload_resized_image(polygon_pil, "polygon", subfolder)
297
-
298
- st.success("✅ Feedback and images saved successfully to Google Drive!")
299
-
300
- except Exception as e:
301
- st.error(f"An error occurred during the upload to Google Drive: {e}")
302
- else:
303
- st.success("✅ Feedback saved successfully!")
 
1
+ Quero que as saídas do codigo para o usuario sejam em ingles, como nesse outro codigo que nao funcionou
2
+
3
  import streamlit as st
4
  import roboflow
5
  import pandas as pd
 
12
  from PIL import Image
13
  from io import BytesIO
14
  from concurrent.futures import ThreadPoolExecutor
15
+ from google.oauth2 import service_account
16
  from googleapiclient.discovery import build
17
  from googleapiclient.http import MediaIoBaseUpload
18
  import gspread
19
  import time
20
 
21
+ # 🔥 Inicializar Roboflow
22
+ API_KEY = st.secrets["roboflow_api_key"]
 
 
 
 
 
 
 
 
 
 
 
 
23
  rf = roboflow.Roboflow(api_key=API_KEY)
24
+ project = rf.workspace(st.secrets["roboflow_workspace"]).project(st.secrets["roboflow_project"])
25
+ model = project.version(st.secrets["roboflow_version"]).model
26
  model.confidence = 80
27
  model.overlap = 25
28
  dpi_value = 300
 
30
  with st.expander("⚙️ Advanced Settings", expanded=True):
31
  model.confidence = st.slider("Model Confidence (%)", 20, 100, 80)
32
 
33
+ # 📁 Setup Google Drive e Sheets
34
+ scope = ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/spreadsheets"]
35
+ credentials_dict = json.loads(st.secrets["gcp_service_account"])
36
+ credentials = service_account.Credentials.from_service_account_info(credentials_dict, scopes=scope)
37
+ drive_service = build("drive", "v3", credentials=credentials)
38
+ sheets_client = gspread.authorize(credentials)
39
+ sheet = sheets_client.open_by_url(st.secrets["feedback_sheet_url"]).sheet1
40
+
41
+ # 📌 Funções auxiliares
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def calculate_polygon_area(points):
43
  polygon = Polygon([(p['x'], p['y']) for p in points])
44
  return polygon.area
 
47
  for attempt in range(3):
48
  try:
49
  return model.predict(image_path)
50
+ except:
51
  time.sleep(1)
52
  return None
53
 
 
95
  prediction = safe_predict(temp_file.name)
96
  if not prediction:
97
  return {
98
+ "Imagem": safe_name,
99
+ "SemSegmentacao": True,
100
+ "Exibir": image,
101
  "Original": get_image_bytes(image)
102
  }
103
  prediction_data = prediction.json()
104
 
105
  if not prediction_data["predictions"]:
106
  return {
107
+ "Imagem": safe_name,
108
+ "SemSegmentacao": True,
109
+ "Exibir": image,
110
  "Original": get_image_bytes(image)
111
  }
112
 
 
128
  fig2, ax2 = plt.subplots(figsize=(6, 6), dpi=dpi_value)
129
  ax2.plot(x, y, 'r-', linewidth=2)
130
  ax2.scatter(x, y, color='red', s=5)
131
+ ax2.set_title("Contorno do Polígono")
132
  ax2.grid()
133
  plt.savefig(polygon_buffer, format="png", bbox_inches='tight')
134
  plt.close()
135
 
136
  return {
137
+ "Imagem": safe_name,
138
+ "Área Segmentada (px²)": area,
139
  "Original": original_buffer,
140
+ "Segmentada": segmented_buffer,
141
+ "Poligono": polygon_buffer,
142
+ "Exibir": image,
143
+ "SemSegmentacao": False
144
  }
145
 
146
+ except:
147
  return None
148
 
149
+ # 🗂️ Interface principal
150
  st.title("IA Model Segmentation")
151
  upload_option = st.radio("Choose upload type:", ["Single image", "Image folder"])
152
  results = []
 
157
  result = process_image(uploaded_file)
158
  if result:
159
  results.append(result)
160
+ st.image(result["Exibir"], caption=f"Original Image - {result['Imagem']}", use_container_width=True)
161
+ if not result["SemSegmentacao"]:
162
+ st.image(result["Segmentada"], caption="Segmentation", use_container_width=True)
163
+ st.image(result["Poligono"], caption="Polygon", use_container_width=True)
164
+ st.write(f"📏 **Segmented Area:** {result['Área Segmentada (px²)']:.2f} pixels²")
165
 
166
  st.download_button(
167
  label="📥 Download Segmented Image",
168
+ data=result["Segmentada"],
169
+ file_name="imagem_segmentada.png",
170
  mime="image/png"
171
  )
172
  else:
 
178
  with ThreadPoolExecutor(max_workers=4) as executor:
179
  processed = list(executor.map(process_image, uploaded_files))
180
 
181
+ falhas = [f.name for f, r in zip(uploaded_files, processed) if r and r.get("SemSegmentacao")]
182
+ if falhas:
183
+ st.warning(f"⚠️ {len(falhas)} image(s) with no segmentation detected:\n\n- " + "\n- ".join(falhas))
184
 
185
  zip_images_buffer = BytesIO()
186
  with zipfile.ZipFile(zip_images_buffer, "w") as zip_file:
187
  for result in processed:
188
  if result:
189
  results.append(result)
190
+ st.image(result["Exibir"], caption=f"Original Image - {result['Imagem']}", use_container_width=True)
191
+ if not result["SemSegmentacao"]:
192
+ st.image(result["Segmentada"], caption="Segmentation", use_container_width=True)
193
+ st.image(result["Poligono"], caption="Polygon", use_container_width=True)
194
+ st.write(f"📏 **Segmented Area:** {result['Área Segmentada (px²)']:.2f} pixels²")
195
+ zip_file.writestr(f"segmentada_{result['Imagem']}.png", result["Segmentada"].getvalue())
196
+ zip_file.writestr(f"poligono_{result['Imagem']}.png", result["Poligono"].getvalue())
197
 
198
  zip_images_buffer.seek(0)
199
 
200
  if results:
201
  df = pd.DataFrame([
202
+ { "Imagem": r["Imagem"], "Área Segmentada (px²)": r["Área Segmentada (px²)"] if not r["SemSegmentacao"] else "Sem Segmentação" }
203
  for r in results
204
  ])
205
  st.markdown("### 📊 Results Table")
 
209
  df.to_excel(excel_buffer, index=False)
210
  excel_buffer.seek(0)
211
 
212
+ st.download_button("📥 Download Table (Excel)", data=excel_buffer, file_name="resultados_segmentacao.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
213
+ st.download_button("📥 Download Segmented Images", data=zip_images_buffer, file_name="imagens_segmentadas.zip", mime="application/zip")
214
 
215
  # 📝 Manual Feedback
216
  if results:
217
  st.markdown("## 📝 Feedback")
218
+ imagem_escolhida = st.selectbox("Select an image to evaluate:", [r["Imagem"] for r in results])
219
+ avaliacao = st.radio("How do you evaluate this segmentation?", ["Great", "Acceptable", "Bad", "No segmentation"], horizontal=True)
220
+ observacao = st.text_area("Observations (optional):")
221
+
222
+ if st.button("Save Feedback"):
223
+ row = [imagem_escolhida, avaliacao, observacao]
224
+ sheet.append_row(row)
225
+
226
+ if avaliacao in ["Acceptable", "Bad", "No segmentation"]:
227
+ sufixo = "aceitavel" if avaliacao == "Acceptable" else "ruim" if avaliacao == "Bad" else "sem_segmentacao"
228
+ parent_folder = find_or_create_folder("Feedback Segmentacoes")
229
+ subfolder = find_or_create_folder(imagem_escolhida.replace(".png", ""), parent_folder)
230
+
231
+ for r in results:
232
+ if r["Imagem"] == imagem_escolhida:
233
+ resized_original = resize_image(r["Exibir"])
234
+ buffer = BytesIO()
235
+ resized_original.save(buffer, format="PNG")
236
+ buffer.seek(0)
237
+ upload_to_drive(buffer, f"original_{sufixo}.png", subfolder)
238
+
239
+ if avaliacao != "No segmentation" and "Segmentada" in r and "Poligono" in r:
240
+ resized_segmented = resize_image(Image.open(BytesIO(r["Segmentada"].getvalue())))
241
+ resized_polygon = resize_image(Image.open(BytesIO(r["Poligono"].getvalue())))
242
+
243
+ for img_obj, nome in zip([resized_segmented, resized_polygon], ["segmentada", "poligono"]):
244
+ buffer = BytesIO()
245
+ img_obj.save(buffer, format="PNG")
246
+ buffer.seek(0)
247
+ upload_to_drive(buffer, f"{nome}_{sufixo}.png", subfolder)
248
+ break
249
+
250
+ st.success("✅ Feedback saved successfully!")