nmariotto commited on
Commit
0b4eabd
·
verified ·
1 Parent(s): 0edd68b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -129
app.py CHANGED
@@ -16,9 +16,9 @@ from googleapiclient.http import MediaIoBaseUpload
16
  import gspread
17
  import time
18
 
19
- # 🔥 Initialize Roboflow
20
  API_KEY = st.secrets["roboflow_api_key"]
21
- rf = roboflow.Roboflow(api_key=API_KEY )
22
  project = rf.workspace(st.secrets["roboflow_workspace"]).project(st.secrets["roboflow_project"])
23
  model = project.version(st.secrets["roboflow_version"]).model
24
  model.confidence = 80
@@ -28,26 +28,15 @@ dpi_value = 300
28
  with st.expander("⚙️ Advanced Settings", expanded=True):
29
  model.confidence = st.slider("Model Confidence (%)", 20, 100, 80)
30
 
31
- # 📁 Setup Google Drive and Sheets (seção aprimorada)
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)
35
  drive_service = build("drive", "v3", credentials=credentials)
36
  sheets_client = gspread.authorize(credentials)
37
  sheet = sheets_client.open_by_url(st.secrets["feedback_sheet_url"]).sheet1
38
 
39
- # Verifica e insere o cabeçalho se a planilha estiver vazia
40
- header = ["Image", "Evaluation", "Observation"]
41
- try:
42
- # get_all_records() falha se o cabeçalho não existir ou a planilha estiver vazia
43
- sheet.get_all_records()
44
- except gspread.exceptions.GSpreadException:
45
- # Se falhar, verificamos se a primeira linha está vazia para decidir se inserimos o cabeçalho
46
- if not sheet.row_values(1):
47
- sheet.insert_row(header, 1)
48
-
49
-
50
- # 📌 Helper Functions
51
  def calculate_polygon_area(points):
52
  polygon = Polygon([(p['x'], p['y']) for p in points])
53
  return polygon.area
@@ -56,55 +45,36 @@ def safe_predict(image_path):
56
  for attempt in range(3):
57
  try:
58
  return model.predict(image_path)
59
- except Exception:
60
  time.sleep(1)
61
  return None
62
 
63
  def resize_image(image):
64
  return image.resize((640, 640))
65
 
66
- # --- CÓDIGO CORRIGIDO ---
67
  def upload_to_drive(image_bytes, filename, folder_id):
68
  media = MediaIoBaseUpload(image_bytes, mimetype='image/png')
69
  drive_service.files().create(
70
  body={"name": filename, "parents": [folder_id]},
71
  media_body=media,
72
- fields='id',
73
- supportsAllDrives=True # <-- CORREÇÃO APLICADA
74
  ).execute()
75
 
76
- # --- CÓDIGO CORRIGIDO ---
77
  def find_or_create_folder(folder_name, parent=None):
78
  query = f"name='{folder_name}' and mimeType='application/vnd.google-apps.folder' and trashed=false"
79
  if parent:
80
  query += f" and '{parent}' in parents"
81
-
82
- # Adiciona os parâmetros para buscar em todos os drives/pastas compartilhadas
83
- results = drive_service.files().list(
84
- q=query,
85
- spaces='drive',
86
- fields='files(id, name)',
87
- supportsAllDrives=True, # <-- CORREÇÃO APLICADA
88
- includeItemsFromAllDrives=True # <-- CORREÇÃO APLICADA
89
- ).execute()
90
-
91
  folders = results.get('files', [])
92
  if folders:
93
  return folders[0]['id']
94
-
95
  file_metadata = {
96
  'name': folder_name,
97
  'mimeType': 'application/vnd.google-apps.folder'
98
  }
99
  if parent:
100
  file_metadata['parents'] = [parent]
101
-
102
- # Adiciona o parâmetro para criar a pasta em um ambiente compartilhado
103
- file = drive_service.files().create(
104
- body=file_metadata,
105
- fields='id',
106
- supportsAllDrives=True # <-- CORREÇÃO APLICADA
107
- ).execute()
108
  return file.get('id')
109
 
110
  def get_image_bytes(image):
@@ -123,18 +93,18 @@ def process_image(uploaded_file):
123
  prediction = safe_predict(temp_file.name)
124
  if not prediction:
125
  return {
126
- "Image": safe_name,
127
- "NoSegmentation": True,
128
- "Display": image,
129
  "Original": get_image_bytes(image)
130
  }
131
  prediction_data = prediction.json()
132
 
133
  if not prediction_data["predictions"]:
134
  return {
135
- "Image": safe_name,
136
- "NoSegmentation": True,
137
- "Display": image,
138
  "Original": get_image_bytes(image)
139
  }
140
 
@@ -156,25 +126,25 @@ def process_image(uploaded_file):
156
  fig2, ax2 = plt.subplots(figsize=(6, 6), dpi=dpi_value)
157
  ax2.plot(x, y, 'r-', linewidth=2)
158
  ax2.scatter(x, y, color='red', s=5)
159
- ax2.set_title("Polygon Outline")
160
  ax2.grid()
161
  plt.savefig(polygon_buffer, format="png", bbox_inches='tight')
162
  plt.close()
163
 
164
  return {
165
- "Image": safe_name,
166
- "Segmented Area (px²)": area,
167
  "Original": original_buffer,
168
- "Segmented": segmented_buffer,
169
- "Polygon": polygon_buffer,
170
- "Display": image,
171
- "NoSegmentation": False
172
  }
173
 
174
- except Exception:
175
  return None
176
 
177
- # 🗂️ Main Interface
178
  st.title("IA Model Segmentation")
179
  upload_option = st.radio("Choose upload type:", ["Single image", "Image folder"])
180
  results = []
@@ -185,16 +155,16 @@ if upload_option == "Single image":
185
  result = process_image(uploaded_file)
186
  if result:
187
  results.append(result)
188
- st.image(result["Display"], caption=f"Original Image - {result['Image']}", use_container_width=True)
189
- if not result["NoSegmentation"]:
190
- st.image(result["Segmented"], caption="Segmentation", use_container_width=True)
191
- st.image(result["Polygon"], caption="Polygon", use_container_width=True)
192
- st.write(f"📏 **Segmented Area:** {result['Segmented Area (px²)']:.2f} pixels²")
193
 
194
  st.download_button(
195
  label="📥 Download Segmented Image",
196
- data=result["Segmented"],
197
- file_name="segmented_image.png",
198
  mime="image/png"
199
  )
200
  else:
@@ -206,28 +176,28 @@ elif upload_option == "Image folder":
206
  with ThreadPoolExecutor(max_workers=4) as executor:
207
  processed = list(executor.map(process_image, uploaded_files))
208
 
209
- failures = [f.name for f, r in zip(uploaded_files, processed) if r and r.get("NoSegmentation")]
210
- if failures:
211
- st.warning(f"⚠️ {len(failures)} image(s) with no segmentation detected:\n\n- " + "\n- ".join(failures))
212
 
213
  zip_images_buffer = BytesIO()
214
  with zipfile.ZipFile(zip_images_buffer, "w") as zip_file:
215
  for result in processed:
216
  if result:
217
  results.append(result)
218
- st.image(result["Display"], caption=f"Original Image - {result['Image']}", use_container_width=True)
219
- if not result["NoSegmentation"]:
220
- st.image(result["Segmented"], caption="Segmentation", use_container_width=True)
221
- st.image(result["Polygon"], caption="Polygon", use_container_width=True)
222
- st.write(f"📏 **Segmented Area:** {result['Segmented Area (px²)']:.2f} pixels²")
223
- zip_file.writestr(f"segmented_{result['Image']}.png", result["Segmented"].getvalue())
224
- zip_file.writestr(f"polygon_{result['Image']}.png", result["Polygon"].getvalue())
225
 
226
  zip_images_buffer.seek(0)
227
 
228
  if results:
229
  df = pd.DataFrame([
230
- { "Image": r["Image"], "Segmented Area (px²)": r["Segmented Area (px²)"] if not r["NoSegmentation"] else "No Segmentation" }
231
  for r in results
232
  ])
233
  st.markdown("### 📊 Results Table")
@@ -237,64 +207,42 @@ elif upload_option == "Image folder":
237
  df.to_excel(excel_buffer, index=False)
238
  excel_buffer.seek(0)
239
 
240
- st.download_button("📥 Download Table (Excel)", data=excel_buffer, file_name="segmentation_results.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
241
- st.download_button("📥 Download Segmented Images", data=zip_images_buffer, file_name="segmented_images.zip", mime="application/zip")
242
 
243
  # 📝 Manual Feedback
244
  if results:
245
  st.markdown("## 📝 Feedback")
246
-
247
- available_images = [r["Image"] for r in results]
248
- if not available_images:
249
- st.warning("No images were processed to provide feedback.")
250
- else:
251
- chosen_image = st.selectbox("Select an image to evaluate:", available_images)
252
- evaluation = st.radio("How do you rate this segmentation?", ["Great", "Acceptable", "Bad", "No segmentation"], horizontal=True)
253
- observation = st.text_area("Observations (optional):")
254
-
255
- if st.button("Save Feedback"):
256
- # 1. Save feedback to the spreadsheet
257
- row = [chosen_image, evaluation, observation]
258
- sheet.append_row(row)
259
- st.info("Feedback saved to spreadsheet...")
260
-
261
- # 2. If the evaluation is not "Great", upload images to Google Drive
262
- if evaluation in ["Acceptable", "Bad", "No segmentation"]:
263
- st.info("Starting image upload to Google Drive...")
264
- try:
265
- suffix_map = {"Acceptable": "acceptable", "Bad": "bad", "No segmentation": "no_segmentation"}
266
- suffix = suffix_map.get(evaluation, "feedback")
267
-
268
- parent_folder = find_or_create_folder("Feedback Segmentacoes")
269
-
270
- folder_name = os.path.splitext(chosen_image)[0]
271
- subfolder = find_or_create_folder(folder_name, parent=parent_folder)
272
-
273
- result_to_upload = next((r for r in results if r["Image"] == chosen_image), None)
274
-
275
- if result_to_upload:
276
- # Helper function to resize and upload
277
- def upload_resized_image(image_obj, file_prefix, folder_id):
278
- resized_pil_img = resize_image(image_obj)
279
- buffer = get_image_bytes(resized_pil_img)
280
- upload_to_drive(buffer, f"{file_prefix}_{suffix}.png", folder_id)
281
- st.write(f" - Upload of `{file_prefix}_{suffix}.png` complete.")
282
-
283
- # Upload original image
284
- original_pil = result_to_upload["Display"]
285
- upload_resized_image(original_pil, "original", subfolder)
286
-
287
- # Upload segmented and polygon images, if applicable
288
- if evaluation != "No segmentation" and not result_to_upload.get("NoSegmentation", True):
289
- segmented_pil = Image.open(result_to_upload["Segmented"])
290
- upload_resized_image(segmented_pil, "segmented", subfolder)
291
-
292
- polygon_pil = Image.open(result_to_upload["Polygon"])
293
- upload_resized_image(polygon_pil, "polygon", subfolder)
294
-
295
- st.success("✅ Feedback and images saved successfully to Google Drive!")
296
-
297
- except Exception as e:
298
- st.error(f"An error occurred during the upload to Google Drive: {e}")
299
- else:
300
- st.success("✅ Feedback saved successfully!")
 
16
  import gspread
17
  import time
18
 
19
+ # 🔥 Inicializar Roboflow
20
  API_KEY = st.secrets["roboflow_api_key"]
21
+ rf = roboflow.Roboflow(api_key=API_KEY)
22
  project = rf.workspace(st.secrets["roboflow_workspace"]).project(st.secrets["roboflow_project"])
23
  model = project.version(st.secrets["roboflow_version"]).model
24
  model.confidence = 80
 
28
  with st.expander("⚙️ Advanced Settings", expanded=True):
29
  model.confidence = st.slider("Model Confidence (%)", 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)
35
  drive_service = build("drive", "v3", credentials=credentials)
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
 
45
  for attempt in range(3):
46
  try:
47
  return model.predict(image_path)
48
+ except:
49
  time.sleep(1)
50
  return None
51
 
52
  def resize_image(image):
53
  return image.resize((640, 640))
54
 
 
55
  def upload_to_drive(image_bytes, filename, folder_id):
56
  media = MediaIoBaseUpload(image_bytes, mimetype='image/png')
57
  drive_service.files().create(
58
  body={"name": filename, "parents": [folder_id]},
59
  media_body=media,
60
+ fields='id'
 
61
  ).execute()
62
 
 
63
  def find_or_create_folder(folder_name, parent=None):
64
  query = f"name='{folder_name}' and mimeType='application/vnd.google-apps.folder' and trashed=false"
65
  if parent:
66
  query += f" and '{parent}' in parents"
67
+ results = drive_service.files().list(q=query, spaces='drive', fields='files(id, name)').execute()
 
 
 
 
 
 
 
 
 
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()
 
 
 
 
 
 
78
  return file.get('id')
79
 
80
  def get_image_bytes(image):
 
93
  prediction = safe_predict(temp_file.name)
94
  if not prediction:
95
  return {
96
+ "Imagem": safe_name,
97
+ "SemSegmentacao": True,
98
+ "Exibir": image,
99
  "Original": get_image_bytes(image)
100
  }
101
  prediction_data = prediction.json()
102
 
103
  if not prediction_data["predictions"]:
104
  return {
105
+ "Imagem": safe_name,
106
+ "SemSegmentacao": True,
107
+ "Exibir": image,
108
  "Original": get_image_bytes(image)
109
  }
110
 
 
126
  fig2, ax2 = plt.subplots(figsize=(6, 6), dpi=dpi_value)
127
  ax2.plot(x, y, 'r-', linewidth=2)
128
  ax2.scatter(x, y, color='red', s=5)
129
+ ax2.set_title("Contorno do Polígono")
130
  ax2.grid()
131
  plt.savefig(polygon_buffer, format="png", bbox_inches='tight')
132
  plt.close()
133
 
134
  return {
135
+ "Imagem": safe_name,
136
+ "Área Segmentada (px²)": area,
137
  "Original": original_buffer,
138
+ "Segmentada": segmented_buffer,
139
+ "Poligono": polygon_buffer,
140
+ "Exibir": image,
141
+ "SemSegmentacao": False
142
  }
143
 
144
+ except:
145
  return None
146
 
147
+ # 🗂️ Interface principal
148
  st.title("IA Model Segmentation")
149
  upload_option = st.radio("Choose upload type:", ["Single image", "Image folder"])
150
  results = []
 
155
  result = process_image(uploaded_file)
156
  if result:
157
  results.append(result)
158
+ st.image(result["Exibir"], caption=f"Original Image - {result['Imagem']}", use_container_width=True)
159
+ if not result["SemSegmentacao"]:
160
+ st.image(result["Segmentada"], caption="Segmentation", use_container_width=True)
161
+ st.image(result["Poligono"], caption="Polygon", use_container_width=True)
162
+ st.write(f"📏 **Segmented Area:** {result['Área Segmentada (px²)']:.2f} pixels²")
163
 
164
  st.download_button(
165
  label="📥 Download Segmented Image",
166
+ data=result["Segmentada"],
167
+ file_name="imagem_segmentada.png",
168
  mime="image/png"
169
  )
170
  else:
 
176
  with ThreadPoolExecutor(max_workers=4) as executor:
177
  processed = list(executor.map(process_image, uploaded_files))
178
 
179
+ falhas = [f.name for f, r in zip(uploaded_files, processed) if r and r.get("SemSegmentacao")]
180
+ if falhas:
181
+ st.warning(f"⚠️ {len(falhas)} image(s) with no segmentation detected:\n\n- " + "\n- ".join(falhas))
182
 
183
  zip_images_buffer = BytesIO()
184
  with zipfile.ZipFile(zip_images_buffer, "w") as zip_file:
185
  for result in processed:
186
  if result:
187
  results.append(result)
188
+ st.image(result["Exibir"], caption=f"Original Image - {result['Imagem']}", use_container_width=True)
189
+ if not result["SemSegmentacao"]:
190
+ st.image(result["Segmentada"], caption="Segmentation", use_container_width=True)
191
+ st.image(result["Poligono"], caption="Polygon", use_container_width=True)
192
+ st.write(f"📏 **Segmented Area:** {result['Área Segmentada (px²)']:.2f} pixels²")
193
+ zip_file.writestr(f"segmentada_{result['Imagem']}.png", result["Segmentada"].getvalue())
194
+ zip_file.writestr(f"poligono_{result['Imagem']}.png", result["Poligono"].getvalue())
195
 
196
  zip_images_buffer.seek(0)
197
 
198
  if results:
199
  df = pd.DataFrame([
200
+ { "Imagem": r["Imagem"], "Área Segmentada (px²)": r["Área Segmentada (px²)"] if not r["SemSegmentacao"] else "Sem Segmentação" }
201
  for r in results
202
  ])
203
  st.markdown("### 📊 Results Table")
 
207
  df.to_excel(excel_buffer, index=False)
208
  excel_buffer.seek(0)
209
 
210
+ st.download_button("📥 Download Table (Excel)", data=excel_buffer, file_name="resultados_segmentacao.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
211
+ st.download_button("📥 Download Segmented Images", data=zip_images_buffer, file_name="imagens_segmentadas.zip", mime="application/zip")
212
 
213
  # 📝 Manual Feedback
214
  if results:
215
  st.markdown("## 📝 Feedback")
216
+ imagem_escolhida = st.selectbox("Select an image to evaluate:", [r["Imagem"] for r in results])
217
+ avaliacao = st.radio("How do you evaluate this segmentation?", ["Great", "Acceptable", "Bad", "No segmentation"], horizontal=True)
218
+ observacao = st.text_area("Observations (optional):")
219
+
220
+ if st.button("Save Feedback"):
221
+ row = [imagem_escolhida, avaliacao, observacao]
222
+ sheet.append_row(row)
223
+
224
+ if avaliacao in ["Acceptable", "Bad", "No segmentation"]:
225
+ sufixo = "aceitavel" if avaliacao == "Acceptable" else "ruim" if avaliacao == "Bad" else "sem_segmentacao"
226
+ parent_folder = find_or_create_folder("Feedback Segmentacoes")
227
+ subfolder = find_or_create_folder(imagem_escolhida.replace(".png", ""), parent_folder)
228
+
229
+ for r in results:
230
+ if r["Imagem"] == imagem_escolhida:
231
+ resized_original = resize_image(r["Exibir"])
232
+ buffer = BytesIO()
233
+ resized_original.save(buffer, format="PNG")
234
+ buffer.seek(0)
235
+ upload_to_drive(buffer, f"original_{sufixo}.png", subfolder)
236
+
237
+ if avaliacao != "No segmentation" and "Segmentada" in r and "Poligono" in r:
238
+ resized_segmented = resize_image(Image.open(BytesIO(r["Segmentada"].getvalue())))
239
+ resized_polygon = resize_image(Image.open(BytesIO(r["Poligono"].getvalue())))
240
+
241
+ for img_obj, nome in zip([resized_segmented, resized_polygon], ["segmentada", "poligono"]):
242
+ buffer = BytesIO()
243
+ img_obj.save(buffer, format="PNG")
244
+ buffer.seek(0)
245
+ upload_to_drive(buffer, f"{nome}_{sufixo}.png", subfolder)
246
+ break
247
+
248
+ st.success("✅ Feedback saved successfully!")