nmariotto commited on
Commit
d0089d0
·
verified ·
1 Parent(s): 314dad3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +220 -101
app.py CHANGED
@@ -16,109 +16,228 @@ 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
 
25
- st.set_page_config(
26
- page_title="IA Model Segmentation",
27
- layout="wide"
28
- )
29
-
30
- st.title("IA Model Segmentation")
31
-
32
- with st.expander("Advanced settings"):
33
- st.write("Select the desired Roboflow model")
34
- # Add advanced settings components if needed
35
-
36
- uploaded_files = st.file_uploader(
37
- "Upload one or more images for segmentation:",
38
- type=['jpg', 'jpeg', 'png', 'tif', 'tiff'],
39
- accept_multiple_files=True,
40
- help="Drag and drop files here or click to select"
41
- )
42
-
43
- if uploaded_files:
44
- st.success("Files uploaded successfully!")
45
- else:
46
- st.info("No image selected")
47
-
48
- if st.button("Segment Images"):
49
- if not uploaded_files:
50
- st.warning("No image selected")
51
- else:
52
- st.info("Segmenting images, please wait...")
53
- segmented_images = []
54
- polygons = []
55
- for uploaded_file in uploaded_files:
56
- try:
57
- image = Image.open(uploaded_file)
58
- # Assuming image is processed here
59
- st.image(image, caption="Original Image")
60
- # Dummy segmented image (replace with model output)
61
- st.image(image, caption="Segmented Image")
62
- st.write("Polygon")
63
- # Add your polygon visualization here
64
- segmented_images.append(image)
65
- polygons.append([]) # Placeholder
66
- except Exception as e:
67
- st.error("Error loading image")
68
-
69
- st.success("Processing completed")
70
-
71
- with tempfile.TemporaryDirectory() as temp_dir:
72
- zip_path = os.path.join(temp_dir, "segmented_images.zip")
73
- with zipfile.ZipFile(zip_path, "w") as zipf:
74
- for i, img in enumerate(segmented_images):
75
- img_byte_arr = BytesIO()
76
- img.save(img_byte_arr, format='PNG')
77
- img_byte_arr = img_byte_arr.getvalue()
78
- zipf.writestr(f"segmented_image_{i+1}.png", img_byte_arr)
79
- with open(zip_path, "rb") as f:
80
- st.download_button(
81
- label="Download results as ZIP",
82
- data=f,
83
- file_name="segmented_images.zip",
84
- mime="application/zip"
85
- )
86
-
87
- st.subheader("Manual feedback")
88
- feedback_images = st.multiselect(
89
- "Select images for manual feedback:",
90
- options=[f"segmented_image_{i+1}.png" for i in range(len(segmented_images))]
91
- )
92
- for img_name in feedback_images:
93
- st.write(f"Manual feedback for {img_name}")
94
- quality = st.radio(
95
- "Segmentation quality",
96
- options=["Good", "Acceptable", "Poor"],
97
- key=f"quality_{img_name}"
98
- )
99
- observation = st.text_area(
100
- "Add observation",
101
- key=f"observation_{img_name}"
102
- )
103
- if st.button("Submit feedback", key=f"submit_{img_name}"):
104
- try:
105
- # Save feedback (implement as needed)
106
- st.success("Feedback successfully submitted")
107
- except Exception as e:
108
- st.error("Failed to submit feedback")
109
-
110
- # Results table (dummy for example)
111
- df_results = pd.DataFrame({
112
- "Image": [f"segmented_image_{i+1}.png" for i in range(len(segmented_images))],
113
- "Quality": [""] * len(segmented_images),
114
- "Observations": [""] * len(segmented_images)
115
- })
116
- st.dataframe(df_results)
117
- st.download_button(
118
- label="Download results table",
119
- data=df_results.to_csv(index=False).encode("utf-8"),
120
- file_name="segmentation_results.csv",
121
- mime="text/csv"
122
- )
123
- else:
124
- st.write("Choose the images to segment")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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)
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
43
+
44
+ def safe_predict(image_path):
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 process_image(uploaded_file):
81
+ try:
82
+ safe_name = uploaded_file.name.replace(" ", "_")
83
+ image = Image.open(uploaded_file).convert("RGB")
84
+
85
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=True) as temp_file:
86
+ image.save(temp_file.name)
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
+
105
+ points = prediction_data["predictions"][0]["points"]
106
+ area = calculate_polygon_area(points)
107
+ x = [p['x'] for p in points] + [points[0]['x']]
108
+ y = [p['y'] for p in points] + [points[0]['y']]
109
+
110
+ original_buffer = get_image_bytes(image)
111
+
112
+ segmented_buffer = BytesIO()
113
+ fig, ax = plt.subplots(figsize=(6, 6), dpi=dpi_value)
114
+ ax.imshow(image)
115
+ ax.plot(x, y, color='red', linewidth=2)
116
+ plt.savefig(segmented_buffer, format="png", bbox_inches='tight')
117
+ plt.close()
118
+
119
+ polygon_buffer = BytesIO()
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:
139
+ return None
140
+
141
+ def get_image_bytes(image):
142
+ buf = BytesIO()
143
+ image.save(buf, format="PNG")
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
+ # Só 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!")