joao-dutra ChristianSardo commited on
Commit
30b2bda
·
verified ·
1 Parent(s): 8d2818d

Update app.py (#6)

Browse files

- Update app.py (67a6844314563a88cbe5165e99947457281de5bc)


Co-authored-by: Sardo <ChristianSardo@users.noreply.huggingface.co>

Files changed (1) hide show
  1. app.py +104 -3
app.py CHANGED
@@ -58,20 +58,24 @@ app.add_middleware(
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:
65
  raise ValueError("Não foi possível decodificar a imagem.")
66
 
67
  hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
 
 
68
  lower_bg = np.array([0, 0, 0], dtype=np.uint8)
69
  upper_bg = np.array([180, 255, 50], dtype=np.uint8)
70
  mask_bg = cv2.inRange(hsv, lower_bg, upper_bg)
71
  mask_obj = cv2.bitwise_not(mask_bg)
 
72
  kernel = np.ones((5, 5), np.uint8)
73
  mask_obj = cv2.morphologyEx(mask_obj, cv2.MORPH_OPEN, kernel)
74
  mask_obj = cv2.morphologyEx(mask_obj, cv2.MORPH_CLOSE, kernel)
 
75
  contours, _ = cv2.findContours(mask_obj, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
76
  if not contours:
77
  raise ValueError("Nenhum objeto principal detectado na imagem.")
@@ -79,8 +83,11 @@ def process_image_bytes(img_bytes: bytes):
79
  largest = max(contours, key=cv2.contourArea)
80
  mask_clean = np.zeros_like(mask_obj)
81
  cv2.drawContours(mask_clean, [largest], -1, 255, cv2.FILLED)
 
82
  isolated = cv2.bitwise_and(img, img, mask=mask_clean)
83
  hsv_iso = cv2.cvtColor(isolated, cv2.COLOR_BGR2HSV)
 
 
84
  lower_white = np.array([0, 0, 180], dtype=np.uint8)
85
  upper_white = np.array([180, 60, 255], dtype=np.uint8)
86
  mask_white = cv2.inRange(hsv_iso, lower_white, upper_white)
@@ -96,7 +103,8 @@ def process_image_bytes(img_bytes: bytes):
96
  def to_data_uri(img_arr_rgb):
97
  bgr = cv2.cvtColor(img_arr_rgb, cv2.COLOR_RGB2BGR)
98
  ok, buf = cv2.imencode(".png", bgr)
99
- if not ok: return None
 
100
  b64 = base64.b64encode(buf.tobytes()).decode("ascii")
101
  return f"data:image/png;base64,{b64}"
102
 
@@ -107,8 +115,101 @@ def process_image_bytes(img_bytes: bytes):
107
  "isolated_image": to_data_uri(isolated_rgb),
108
  "corrosion_image": to_data_uri(corrosion_vis_rgb),
109
  }
 
 
 
110
 
111
- return analysis_results, corrosion_vis_rgb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  # ==============================================================================
114
  # 3. ENDPOINTS DA API
 
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 (RGB)."""
62
  nparr = np.frombuffer(img_bytes, np.uint8)
63
  img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
64
  if img is None:
65
  raise ValueError("Não foi possível decodificar a imagem.")
66
 
67
  hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
68
+
69
+ # Segmentação do objeto principal (fundo escuro)
70
  lower_bg = np.array([0, 0, 0], dtype=np.uint8)
71
  upper_bg = np.array([180, 255, 50], dtype=np.uint8)
72
  mask_bg = cv2.inRange(hsv, lower_bg, upper_bg)
73
  mask_obj = cv2.bitwise_not(mask_bg)
74
+
75
  kernel = np.ones((5, 5), np.uint8)
76
  mask_obj = cv2.morphologyEx(mask_obj, cv2.MORPH_OPEN, kernel)
77
  mask_obj = cv2.morphologyEx(mask_obj, cv2.MORPH_CLOSE, kernel)
78
+
79
  contours, _ = cv2.findContours(mask_obj, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
80
  if not contours:
81
  raise ValueError("Nenhum objeto principal detectado na imagem.")
 
83
  largest = max(contours, key=cv2.contourArea)
84
  mask_clean = np.zeros_like(mask_obj)
85
  cv2.drawContours(mask_clean, [largest], -1, 255, cv2.FILLED)
86
+
87
  isolated = cv2.bitwise_and(img, img, mask=mask_clean)
88
  hsv_iso = cv2.cvtColor(isolated, cv2.COLOR_BGR2HSV)
89
+
90
+ # Corrosão branca (faixa de brancos)
91
  lower_white = np.array([0, 0, 180], dtype=np.uint8)
92
  upper_white = np.array([180, 60, 255], dtype=np.uint8)
93
  mask_white = cv2.inRange(hsv_iso, lower_white, upper_white)
 
103
  def to_data_uri(img_arr_rgb):
104
  bgr = cv2.cvtColor(img_arr_rgb, cv2.COLOR_RGB2BGR)
105
  ok, buf = cv2.imencode(".png", bgr)
106
+ if not ok:
107
+ return None
108
  b64 = base64.b64encode(buf.tobytes()).decode("ascii")
109
  return f"data:image/png;base64,{b64}"
110
 
 
115
  "isolated_image": to_data_uri(isolated_rgb),
116
  "corrosion_image": to_data_uri(corrosion_vis_rgb),
117
  }
118
+
119
+ return analysis_results, corrosion_vis_rgb
120
+
121
 
122
+ def analyze_and_persist(img_bytes: bytes, logical_name: str):
123
+ """
124
+ Processa a imagem em memória (process_image_bytes), faz upload no S3 (original & resultado)
125
+ e insere na tabela 'amostras'. Retorna o dict da análise + chaves S3 + database_id.
126
+ """
127
+ # 1) Processa
128
+ analysis_results, corrosion_image_np = process_image_bytes(img_bytes)
129
+
130
+ # 2) Upload ORIGINAL
131
+ content_type = mimetypes.guess_type(logical_name)[0] or "application/octet-stream"
132
+ extensao = mimetypes.guess_extension(content_type) or ".jpg"
133
+
134
+ s3_key_original = f"imagens_originais/{uuid.uuid4()}{extensao}"
135
+ s3_client.put_object(
136
+ Bucket=AWS_S3_BUCKET_NAME,
137
+ Key=s3_key_original,
138
+ Body=img_bytes,
139
+ ContentType=content_type
140
+ )
141
+
142
+ # 3) Upload RESULTADO (PNG)
143
+ bgr_result = cv2.cvtColor(corrosion_image_np, cv2.COLOR_RGB2BGR)
144
+ ok, buffer = cv2.imencode('.png', bgr_result)
145
+ if not ok:
146
+ raise ValueError("Falha ao codificar a imagem de resultado para PNG.")
147
+ s3_key_resultado = f"imagens_resultados/{uuid.uuid4()}.png"
148
+ s3_client.put_object(
149
+ Bucket=AWS_S3_BUCKET_NAME,
150
+ Key=s3_key_resultado,
151
+ Body=buffer.tobytes(),
152
+ ContentType='image/png'
153
+ )
154
+
155
+ # 4) Insert Supabase (na mesma tabela 'amostras')
156
+ dados_para_inserir = {
157
+ "imagem_original": s3_key_original,
158
+ "imagem_resultado": s3_key_resultado,
159
+ "resultado": analysis_results["percent"]
160
+ }
161
+ response = supabase_client.from_("amostra").insert(dados_para_inserir).execute()
162
+ new_record_id = response.data[0]['id']
163
+
164
+ # Retorno enriquecido
165
+ out = {
166
+ **analysis_results,
167
+ "database_id": new_record_id,
168
+ "s3_original": s3_key_original,
169
+ "s3_resultado": s3_key_resultado
170
+ }
171
+ return out
172
+
173
+ def segmentate(file_bytes: bytes):
174
+ """
175
+ Roda o YOLO, salva predição e crops, e retorna:
176
+ - save_dir (Path): diretório raiz onde o Ultralytics salvou esta execução
177
+ - crop_paths (list[Path]): lista com todos os arquivos de imagem dentro de /crops
178
+ """
179
+ modelo = YOLO('back-ciser/best.pt')
180
+
181
+ # Passa a imagem como array (sem salvar a original só para inferir)
182
+ img = Image.open(io.BytesIO(file_bytes)).convert("RGB")
183
+ np_img = np.array(img)
184
+ np_img_bgr = cv2.cvtColor(np_img, cv2.COLOR_RGB2BGR)
185
+
186
+ resultados = modelo.predict(
187
+ source=np_img,
188
+ imgsz=1280,
189
+ conf=0.7,
190
+ iou=0.3,
191
+ save=True,
192
+ save_crop=True,
193
+ project=LOCAL_RESULT,
194
+ name="predict", # deixa previsível
195
+ exist_ok=True,
196
+ verbose=False
197
+ )
198
+
199
+ # Diretório real onde esta execução foi salva
200
+ save_dir = Path(resultados[0].save_dir)
201
+
202
+ crops_dir = save_dir / "crops"
203
+ if not crops_dir.exists():
204
+ return save_dir, []
205
+
206
+ # Varre recursivamente (crops/<classe>/*.jpg|png|...)
207
+ crop_paths = []
208
+ for ext in ("*.jpg", "*.png", "*.jpeg", "*.bmp", "*.tif", "*.tiff"):
209
+ crop_paths.extend(crops_dir.rglob(ext))
210
+
211
+ crop_paths = sorted(crop_paths, key=lambda p: str(p).lower())
212
+ return save_dir, crop_paths
213
 
214
  # ==============================================================================
215
  # 3. ENDPOINTS DA API