joao-dutra commited on
Commit
3f81db6
verified
1 Parent(s): 8b40454

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +10 -171
app.py CHANGED
@@ -7,12 +7,6 @@ import numpy as np
7
  import cv2
8
  import base64
9
  import mimetypes
10
- # --- NOVAS importa莽玫es do 'teste yolo.txt' ---
11
- import io
12
- from pathlib import Path
13
- from PIL import Image
14
- from ultralytics import YOLO
15
- # --- Fim das novas importa莽玫es ---
16
  from fastapi import FastAPI, File, UploadFile, HTTPException
17
  from fastapi.middleware.cors import CORSMiddleware
18
  from fastapi.responses import JSONResponse
@@ -22,11 +16,6 @@ from botocore.exceptions import NoCredentialsError
22
  # 1. CONFIGURA脟脙O E CLIENTES (CARREGADOS NA INICIALIZA脟脙O)
23
  # ==============================================================================
24
 
25
- # --- NOVAS constantes do 'teste yolo.txt' ---
26
- LOCAL_RESULT = 'back-ciser/result'
27
- YOLO_MODEL_PATH = 'best.pt'
28
- # --- Fim das novas constantes ---
29
-
30
  # Carrega as configura莽玫es das vari谩veis de ambiente (Secrets do Hugging Face)
31
  AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
32
  AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
@@ -35,7 +24,7 @@ AWS_S3_REGION = os.getenv("AWS_S3_REGION")
35
  SUPABASE_URL = os.getenv("SUPABASE_URL")
36
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
37
 
38
- # Valida莽茫o (do back atual.txt)
39
  if not all([AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET_NAME, AWS_S3_REGION, SUPABASE_URL, SUPABASE_KEY]):
40
  raise RuntimeError("Erro: Nem todos os secrets necess谩rios foram configurados no Hugging Face Space.")
41
 
@@ -55,7 +44,7 @@ except Exception as e:
55
  # Inicializa a aplica莽茫o FastAPI
56
  app = FastAPI(title="Detector de Corros茫o Branca com Salvamento S3/Supabase")
57
 
58
- # Middleware (do back atual.txt)
59
  app.add_middleware(
60
  CORSMiddleware,
61
  allow_origins=["*"],
@@ -68,10 +57,8 @@ app.add_middleware(
68
  # 2. FUN脟脮ES HELPER (L脫GICA REUTILIZ脕VEL)
69
  # ==============================================================================
70
 
71
- # --- Fun莽茫o ORIGINAL do 'back atual.txt' (usada por ambas as l贸gicas) ---
72
  def process_image_bytes(img_bytes: bytes):
73
- """L贸gica de an谩lise de imagem com OpenCV.
74
- Retorna um dict com m茅tricas e a imagem de corros茫o em numpy."""
75
  nparr = np.frombuffer(img_bytes, np.uint8)
76
  img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
77
  if img is None:
@@ -123,106 +110,6 @@ def process_image_bytes(img_bytes: bytes):
123
 
124
  return analysis_results, corrosion_vis_rgb
125
 
126
-
127
- # --- NOVAS fun莽玫es helper do 'teste yolo.txt' (para o fluxo de segmenta莽茫o) ---
128
-
129
- def analyze_and_persist(img_bytes: bytes, logical_name: str):
130
- """
131
- Processa a imagem em mem贸ria (process_image_bytes), faz upload no S3 (original & resultado)
132
- e insere na tabela 'amostras'.
133
- Retorna o dict da an谩lise + chaves S3 + database_id.
134
- """
135
- # 1) Processa
136
- analysis_results, corrosion_image_np = process_image_bytes(img_bytes)
137
-
138
- # 2) Upload ORIGINAL
139
- content_type = mimetypes.guess_type(logical_name)[0] or "application/octet-stream"
140
- extensao = mimetypes.guess_extension(content_type) or ".jpg"
141
-
142
- s3_key_original = f"imagens_originais/{uuid.uuid4()}{extensao}"
143
- s3_client.put_object(
144
- Bucket=AWS_S3_BUCKET_NAME,
145
- Key=s3_key_original,
146
- Body=img_bytes,
147
- ContentType=content_type
148
- )
149
-
150
- # 3) Upload RESULTADO (PNG)
151
- bgr_result = cv2.cvtColor(corrosion_image_np, cv2.COLOR_RGB2BGR)
152
- ok, buffer = cv2.imencode('.png', bgr_result)
153
- if not ok:
154
- raise ValueError("Falha ao codificar a imagem de resultado para PNG.")
155
- s3_key_resultado = f"imagens_resultados/{uuid.uuid4()}.png"
156
- s3_client.put_object(
157
- Bucket=AWS_S3_BUCKET_NAME,
158
- Key=s3_key_resultado,
159
- Body=buffer.tobytes(),
160
- ContentType='image/png'
161
- )
162
-
163
- # 4) Insert Supabase (na mesma tabela 'amostra')
164
- dados_para_inserir = {
165
- "imagem_original": s3_key_original,
166
- "imagem_resultado": s3_key_resultado,
167
- "resultado": analysis_results["percent"]
168
- }
169
- response = supabase_client.from_("amostra").insert(dados_para_inserir).execute()
170
- new_record_id = response.data[0]['id']
171
-
172
- # Retorno enriquecido
173
- out = {
174
- **analysis_results,
175
- "database_id": new_record_id,
176
- "s3_original": s3_key_original,
177
- "s3_resultado": s3_key_resultado
178
- }
179
- return out
180
-
181
-
182
- def segmentate(file_bytes: bytes):
183
- """
184
- Roda o YOLO, salva predi莽茫o e crops, e retorna:
185
- - save_dir (Path): diret贸rio raiz onde o Ultralytics salvou esta execu莽茫o
186
- - crop_paths (list[Path]): lista com todos os arquivos de imagem dentro de /crops
187
- """
188
- modelo = YOLO(YOLO_MODEL_PATH) # Usando a constante definida no in铆cio
189
-
190
- # Passa a imagem como array
191
- img = Image.open(io.BytesIO(file_bytes)).convert("RGB")
192
- np_img = np.array(img)
193
- np_img_bgr = cv2.cvtColor(np_img, cv2.COLOR_RGB2BGR)
194
-
195
- resultados = modelo.predict(
196
- source=np_img,
197
- imgsz=1280,
198
- conf=0.7,
199
- iou=0.3,
200
- save=True,
201
- save_crop=True,
202
- project=LOCAL_RESULT,
203
- name="predict",
204
- exist_ok=True,
205
- verbose=False
206
- )
207
-
208
- # Diret贸rio real onde esta execu莽茫o foi salva
209
- save_dir = Path(resultados[0].save_dir)
210
-
211
- crops_dir = save_dir / "crops"
212
- if not crops_dir.exists():
213
- return save_dir, []
214
-
215
- # Varre recursivamente
216
- crop_paths = []
217
- for ext in ("*.jpg", "*.png", "*.jpeg", "*.bmp", "*.tif", "*.tiff"):
218
- crop_paths.extend(crops_dir.rglob(ext))
219
-
220
- crop_paths = sorted(crop_paths, key=lambda p: str(p).lower())
221
- return save_dir, crop_paths
222
-
223
- # --- Fim das novas fun莽玫es helper ---
224
-
225
-
226
  # ==============================================================================
227
  # 3. ENDPOINTS DA API
228
  # ==============================================================================
@@ -231,13 +118,11 @@ def segmentate(file_bytes: bytes):
231
  def read_root():
232
  return {"status": "ok", "message": "API de An谩lise de Corros茫o no ar!"}
233
 
234
-
235
- # --- Endpoint ORIGINAL do 'back atual.txt' (Sem altera莽玫es) ---
236
  @app.post("/analyze")
237
  async def analyze(file: UploadFile = File(...)):
238
  """
239
  Endpoint principal: Analisa a imagem, salva original e resultado no S3,
240
- e persiste os dados no Supabase. (L脫GICA ANTIGA)
241
  """
242
  content = await file.read()
243
  s3_key_original = None
@@ -260,7 +145,6 @@ async def analyze(file: UploadFile = File(...)):
260
  ContentType=content_type
261
  )
262
 
263
-
264
  # 3. Faz o upload da IMAGEM DE RESULTADO (em mem贸ria) para o S3
265
  s3_key_resultado = f"imagens_resultados/{uuid.uuid4()}.png"
266
 
@@ -280,6 +164,10 @@ async def analyze(file: UploadFile = File(...)):
280
  # 4. Salva os metadados e as chaves S3 no Supabase
281
  print("Inserindo registro no Supabase...")
282
  dados_para_inserir = {
 
 
 
 
283
  "imagem_original": s3_key_original,
284
  "imagem_resultado": s3_key_resultado,
285
  "resultado": analysis_results["percent"]
@@ -293,7 +181,7 @@ async def analyze(file: UploadFile = File(...)):
293
  return JSONResponse(content=analysis_results)
294
 
295
  except Exception as e:
296
- # L贸gica de Rollback:
297
  print(f"ERRO no processo de an谩lise: {e}")
298
  if s3_key_original:
299
  print(f"Removendo objeto 贸rf茫o do S3: {s3_key_original}")
@@ -301,59 +189,10 @@ async def analyze(file: UploadFile = File(...)):
301
  if s3_key_resultado:
302
  print(f"Removendo objeto 贸rf茫o do S3: {s3_key_resultado}")
303
  s3_client.delete_object(Bucket=AWS_S3_BUCKET_NAME, Key=s3_key_resultado)
304
-
305
- raise HTTPException(status_code=500, detail=f"Ocorreu um erro interno: {e}")
306
-
307
-
308
- # --- NOVO Endpoint (l贸gica do 'teste yolo.txt') ---
309
- @app.post("/segment_and_analyze")
310
- async def segment_and_analyze(file: UploadFile = File(...)):
311
- """
312
- Endpoint com YOLO: Segmenta a imagem, analisa a original E cada crop,
313
- e salva tudo no S3/Supabase.
314
- (L贸gica dos sources 36-39)
315
- """
316
- content = await file.read()
317
-
318
- # Para rollback em caso de erro
319
- created_s3_keys = []
320
-
321
- try:
322
- # 0) Segmenta e obt茅m caminhos dos crops
323
- save_dir, crop_paths = segmentate(content)
324
-
325
- # 1) Analisa a imagem ORIGINAL
326
- original_res = analyze_and_persist(content, logical_name=file.filename or "upload_sem_nome")
327
- created_s3_keys.extend([original_res["s3_original"], original_res["s3_resultado"]])
328
-
329
- # 2) Analisa cada CROP
330
- crop_analyses = []
331
- for crop_path in crop_paths:
332
- crop_bytes = crop_path.read_bytes()
333
- logical_name = str(crop_path.relative_to(save_dir)).replace("\\", "/") # nome amig谩vel
334
- crop_res = analyze_and_persist(crop_bytes, logical_name=logical_name)
335
- created_s3_keys.extend([crop_res["s3_original"], crop_res["s3_resultado"]])
336
- crop_res["crop_path"] = logical_name
337
- crop_analyses.append(crop_res)
338
-
339
- # 3) Resposta consolidada
340
- return JSONResponse(content={
341
- "original": original_res,
342
- "crops_count": len(crop_analyses),
343
- "crops": crop_analyses
344
- })
345
-
346
- except Exception as e:
347
- # Rollback simples: tenta apagar do S3 o que j谩 foi criado
348
- for key in created_s3_keys:
349
- try:
350
- s3_client.delete_object(Bucket=AWS_S3_BUCKET_NAME, Key=key)
351
- except Exception:
352
- pass
353
  raise HTTPException(status_code=500, detail=f"Ocorreu um erro interno: {e}")
354
 
355
 
356
- # --- Endpoint ORIGINAL do 'back atual.txt' (Sem altera莽玫es) ---
357
  @app.get("/samples/{sample_id}")
358
  async def get_sample_images(sample_id: int):
359
  """
 
7
  import cv2
8
  import base64
9
  import mimetypes
 
 
 
 
 
 
10
  from fastapi import FastAPI, File, UploadFile, HTTPException
11
  from fastapi.middleware.cors import CORSMiddleware
12
  from fastapi.responses import JSONResponse
 
16
  # 1. CONFIGURA脟脙O E CLIENTES (CARREGADOS NA INICIALIZA脟脙O)
17
  # ==============================================================================
18
 
 
 
 
 
 
19
  # Carrega as configura莽玫es das vari谩veis de ambiente (Secrets do Hugging Face)
20
  AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
21
  AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
 
24
  SUPABASE_URL = os.getenv("SUPABASE_URL")
25
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
26
 
27
+ # Valida莽茫o para garantir que todos os secrets foram configurados
28
  if not all([AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET_NAME, AWS_S3_REGION, SUPABASE_URL, SUPABASE_KEY]):
29
  raise RuntimeError("Erro: Nem todos os secrets necess谩rios foram configurados no Hugging Face Space.")
30
 
 
44
  # Inicializa a aplica莽茫o FastAPI
45
  app = FastAPI(title="Detector de Corros茫o Branca com Salvamento S3/Supabase")
46
 
47
+ # Em produ莽茫o, restrinja ao dom铆nio do seu frontend
48
  app.add_middleware(
49
  CORSMiddleware,
50
  allow_origins=["*"],
 
57
  # 2. FUN脟脮ES HELPER (L脫GICA REUTILIZ脕VEL)
58
  # ==============================================================================
59
 
 
60
  def process_image_bytes(img_bytes: bytes):
61
+ """L贸gica de an谩lise de imagem com OpenCV. Retorna um dict com m茅tricas e a imagem de corros茫o em numpy."""
 
62
  nparr = np.frombuffer(img_bytes, np.uint8)
63
  img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
64
  if img is None:
 
110
 
111
  return analysis_results, corrosion_vis_rgb
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  # ==============================================================================
114
  # 3. ENDPOINTS DA API
115
  # ==============================================================================
 
118
  def read_root():
119
  return {"status": "ok", "message": "API de An谩lise de Corros茫o no ar!"}
120
 
 
 
121
  @app.post("/analyze")
122
  async def analyze(file: UploadFile = File(...)):
123
  """
124
  Endpoint principal: Analisa a imagem, salva original e resultado no S3,
125
+ e persiste os dados no Supabase.
126
  """
127
  content = await file.read()
128
  s3_key_original = None
 
145
  ContentType=content_type
146
  )
147
 
 
148
  # 3. Faz o upload da IMAGEM DE RESULTADO (em mem贸ria) para o S3
149
  s3_key_resultado = f"imagens_resultados/{uuid.uuid4()}.png"
150
 
 
164
  # 4. Salva os metadados e as chaves S3 no Supabase
165
  print("Inserindo registro no Supabase...")
166
  dados_para_inserir = {
167
+ #"nome_amostra": file.filename,
168
+ #"percentual_corrosao": analysis_results["percent"],
169
+ #"pixels_totais_obj": analysis_results["total_pixels"],
170
+ #"pixels_corrosao": analysis_results["corrosion_pixels"],
171
  "imagem_original": s3_key_original,
172
  "imagem_resultado": s3_key_resultado,
173
  "resultado": analysis_results["percent"]
 
181
  return JSONResponse(content=analysis_results)
182
 
183
  except Exception as e:
184
+ # L贸gica de Rollback: Se algo falhar, tenta limpar os arquivos do S3
185
  print(f"ERRO no processo de an谩lise: {e}")
186
  if s3_key_original:
187
  print(f"Removendo objeto 贸rf茫o do S3: {s3_key_original}")
 
189
  if s3_key_resultado:
190
  print(f"Removendo objeto 贸rf茫o do S3: {s3_key_resultado}")
191
  s3_client.delete_object(Bucket=AWS_S3_BUCKET_NAME, Key=s3_key_resultado)
192
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  raise HTTPException(status_code=500, detail=f"Ocorreu um erro interno: {e}")
194
 
195
 
 
196
  @app.get("/samples/{sample_id}")
197
  async def get_sample_images(sample_id: int):
198
  """