joao-dutra commited on
Commit
76396cf
·
verified ·
1 Parent(s): 688d726

teste voltando versão

Browse files
Files changed (1) hide show
  1. app.py +37 -55
app.py CHANGED
@@ -5,9 +5,11 @@ from fastapi.responses import JSONResponse
5
  import numpy as np
6
  import cv2
7
  import base64
 
8
 
9
  app = FastAPI(title="Detector de Corrosão Branca")
10
 
 
11
  app.add_middleware(
12
  CORSMiddleware,
13
  allow_origins=["*"],
@@ -16,107 +18,87 @@ app.add_middleware(
16
  allow_headers=["*"],
17
  )
18
 
19
- def to_data_uri_rgb(img_rgb: np.ndarray) -> str:
20
- # img_rgb: HxWx3 uint8
21
- if img_rgb is None or img_rgb.size == 0:
22
- return None
23
- bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
24
- ok, buf = cv2.imencode(".png", bgr)
25
- if not ok:
26
- return None
27
- import base64
28
- b64 = base64.b64encode(buf.tobytes()).decode("ascii")
29
- return f"data:image/png;base64,{b64}"
30
-
31
  def process_image_bytes(img_bytes: bytes):
32
- # 0) decodificar bytes
33
  nparr = np.frombuffer(img_bytes, np.uint8)
34
- img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED)
35
-
36
  if img is None:
37
- raise ValueError("Não foi possível decodificar a imagem enviada.")
38
-
39
- # 0.1) garantir BGR (remover alpha se existir)
40
- if img.ndim == 2:
41
- img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
42
- elif img.shape[2] == 4:
43
- img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
44
-
45
- # 1) equalização local leve para realçar contraste (CLAHE no L do LAB)
46
- lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
47
- l, a, b = cv2.split(lab)
48
- clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
49
- l = clahe.apply(l)
50
- img = cv2.cvtColor(cv2.merge([l, a, b]), cv2.COLOR_LAB2BGR)
51
-
52
- # 2) HSV e máscara de fundo preto
53
  hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
 
 
54
  lower_bg = np.array([0, 0, 0], dtype=np.uint8)
55
  upper_bg = np.array([180, 255, 50], dtype=np.uint8)
56
  mask_bg = cv2.inRange(hsv, lower_bg, upper_bg)
 
 
57
  mask_obj = cv2.bitwise_not(mask_bg)
58
 
59
- # 3) limpeza morfológica
60
  kernel = np.ones((5, 5), np.uint8)
61
  mask_obj = cv2.morphologyEx(mask_obj, cv2.MORPH_OPEN, kernel)
62
  mask_obj = cv2.morphologyEx(mask_obj, cv2.MORPH_CLOSE, kernel)
63
 
64
- # 4) maior componente (parafuso)
65
  contours, _ = cv2.findContours(mask_obj, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
66
  if not contours:
67
- return {
68
- "percent": 0.0,
69
- "total_pixels": 0,
70
- "corrosion_pixels": 0,
71
- "isolated_image": None,
72
- "corrosion_image": None,
73
- "warning": "Nenhum objeto detectado (fundo pode não ser preto)."
74
- }
75
 
76
  largest = max(contours, key=cv2.contourArea)
77
  mask_clean = np.zeros_like(mask_obj)
78
  cv2.drawContours(mask_clean, [largest], -1, 255, cv2.FILLED)
79
 
80
- # 5) isolar objeto
81
  isolated = cv2.bitwise_and(img, img, mask=mask_clean)
82
 
83
- # 6) detectar CORROSÃO BRANCA (S baixa e V alta)
84
  hsv_iso = cv2.cvtColor(isolated, cv2.COLOR_BGR2HSV)
85
  lower_white = np.array([0, 0, 180], dtype=np.uint8)
86
  upper_white = np.array([180, 60, 255], dtype=np.uint8)
87
  mask_white = cv2.inRange(hsv_iso, lower_white, upper_white)
88
  mask_white = cv2.bitwise_and(mask_white, mask_white, mask=mask_clean)
89
 
90
- # 7) métricas
91
  total_pixels = int(np.count_nonzero(mask_clean))
92
  corrosion_pixels = int(np.count_nonzero(mask_white))
93
  percent = (corrosion_pixels / max(1, total_pixels)) * 100.0
94
 
95
- # 8) imagens para o front (RGB + data URI)
 
96
  isolated_rgb = cv2.cvtColor(isolated, cv2.COLOR_BGR2RGB)
97
  corrosion_vis = cv2.bitwise_and(isolated_rgb, isolated_rgb, mask=mask_white)
98
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  return {
100
  "percent": round(percent, 4),
101
  "total_pixels": total_pixels,
102
  "corrosion_pixels": corrosion_pixels,
103
- "isolated_image": to_data_uri_rgb(isolated_rgb),
104
- "corrosion_image": to_data_uri_rgb(corrosion_vis),
105
  }
106
 
107
  @app.post("/analyze")
108
  async def analyze(file: UploadFile = File(...)):
 
109
  try:
110
- content = await file.read()
111
  result = process_image_bytes(content)
112
- return JSONResponse(result, status_code=200)
113
  except ValueError as e:
114
- # erro esperado do usuário/entrada -> 400 com JSON
115
- return JSONResponse({"error": str(e)}, status_code=400)
116
- except Exception as e:
117
- # qualquer outra falha -> ainda responder JSON, não HTML
118
- return JSONResponse({"error": f"Falha interna: {type(e).__name__}: {str(e)}"}, status_code=500)
119
 
120
  @app.get("/")
121
  def read_root():
122
- return {"status": "ok"}
 
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=["*"],
 
18
  allow_headers=["*"],
19
  )
20
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def process_image_bytes(img_bytes: bytes):
22
+ # 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)
 
 
 
101
 
102
  @app.get("/")
103
  def read_root():
104
+ return {"status": "ok"}