joao-dutra commited on
Commit
fddf7b4
·
verified ·
1 Parent(s): cdcdb0a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +100 -0
app.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ from fastapi import FastAPI, File, UploadFile, HTTPException
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi.responses import JSONResponse
5
+ import numpy as np
6
+ import cv2
7
+ import base64
8
+ import io
9
+
10
+ app = FastAPI(title="Detector de Corrosão Branca")
11
+
12
+ # PARA PROTOTIPO: permitir todas origens. Em produção restrinja ao domínio do frontend.
13
+ app.add_middleware(
14
+ CORSMiddleware,
15
+ allow_origins=["*"],
16
+ allow_credentials=True,
17
+ allow_methods=["*"],
18
+ allow_headers=["*"],
19
+ )
20
+
21
+ def process_image_bytes(img_bytes: bytes):
22
+ # lê bytes em numpy + OpenCV
23
+ nparr = np.frombuffer(img_bytes, np.uint8)
24
+ img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
25
+ if img is None:
26
+ raise ValueError("Não foi possível decodificar a imagem.")
27
+
28
+ # 1) Converter para HSV
29
+ hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
30
+
31
+ # 2) máscara do fundo preto (V baixo)
32
+ lower_bg = np.array([0, 0, 0], dtype=np.uint8)
33
+ upper_bg = np.array([180, 255, 50], dtype=np.uint8)
34
+ mask_bg = cv2.inRange(hsv, lower_bg, upper_bg)
35
+
36
+ # 3) objeto = invertendo máscara do fundo
37
+ mask_obj = cv2.bitwise_not(mask_bg)
38
+
39
+ # 4) limpar máscara (morfologia)
40
+ kernel = np.ones((5, 5), np.uint8)
41
+ mask_obj = cv2.morphologyEx(mask_obj, cv2.MORPH_OPEN, kernel)
42
+ mask_obj = cv2.morphologyEx(mask_obj, cv2.MORPH_CLOSE, kernel)
43
+
44
+ # 5) maior contorno (supõe um parafuso)
45
+ contours, _ = cv2.findContours(mask_obj, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
46
+ if not contours:
47
+ return {"error": "Nenhum objeto detectado"}
48
+
49
+ largest = max(contours, key=cv2.contourArea)
50
+ mask_clean = np.zeros_like(mask_obj)
51
+ cv2.drawContours(mask_clean, [largest], -1, 255, cv2.FILLED)
52
+
53
+ # 6) isolar objeto
54
+ isolated = cv2.bitwise_and(img, img, mask=mask_clean)
55
+
56
+ # 7) detectar corrosão BRANCA (S baixa, V alta)
57
+ hsv_iso = cv2.cvtColor(isolated, cv2.COLOR_BGR2HSV)
58
+ lower_white = np.array([0, 0, 180], dtype=np.uint8)
59
+ upper_white = np.array([180, 60, 255], dtype=np.uint8)
60
+ mask_white = cv2.inRange(hsv_iso, lower_white, upper_white)
61
+ mask_white = cv2.bitwise_and(mask_white, mask_white, mask=mask_clean)
62
+
63
+ # 8) métricas
64
+ total_pixels = int(np.count_nonzero(mask_clean))
65
+ corrosion_pixels = int(np.count_nonzero(mask_white))
66
+ percent = (corrosion_pixels / max(1, total_pixels)) * 100.0
67
+
68
+ # 9) preparar imagens para frontend (PNG base64)
69
+ # isolado em RGB para visualização
70
+ isolated_rgb = cv2.cvtColor(isolated, cv2.COLOR_BGR2RGB)
71
+ corrosion_vis = cv2.bitwise_and(isolated_rgb, isolated_rgb, mask=mask_white)
72
+
73
+ def to_data_uri(img_arr):
74
+ # img_arr: RGB uint8
75
+ bgr = cv2.cvtColor(img_arr, cv2.COLOR_RGB2BGR)
76
+ ok, buf = cv2.imencode(".png", bgr)
77
+ if not ok:
78
+ return None
79
+ b64 = base64.b64encode(buf.tobytes()).decode("ascii")
80
+ return f"data:image/png;base64,{b64}"
81
+
82
+ isolated_b64 = to_data_uri(isolated_rgb)
83
+ corrosion_b64 = to_data_uri(corrosion_vis)
84
+
85
+ return {
86
+ "percent": round(percent, 4),
87
+ "total_pixels": total_pixels,
88
+ "corrosion_pixels": corrosion_pixels,
89
+ "isolated_image": isolated_b64,
90
+ "corrosion_image": corrosion_b64,
91
+ }
92
+
93
+ @app.post("/analyze")
94
+ async def analyze(file: UploadFile = File(...)):
95
+ content = await file.read()
96
+ try:
97
+ result = process_image_bytes(content)
98
+ except ValueError as e:
99
+ raise HTTPException(status_code=400, detail=str(e))
100
+ return JSONResponse(result)