pjsr commited on
Commit
2dfc921
·
verified ·
1 Parent(s): 9ad516f

Update test.py

Browse files
Files changed (1) hide show
  1. test.py +56 -87
test.py CHANGED
@@ -5,6 +5,8 @@ import cv2
5
  import numpy as np
6
  import os
7
  import math
 
 
8
 
9
  # Caminho para o modelo treinado
10
  modelo_path = "runs/segment/meu_yolo_seg20/weights/best.pt"
@@ -25,71 +27,41 @@ def contar_pixels_conexos(mask, minimo):
25
  def calcular_distancia(p1, p2):
26
  return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
27
 
28
- #import pytesseract
29
- import re
30
-
31
  def detectar_escala(img_bgr):
32
- """
33
- Detecta linha preta horizontal da régua no canto inferior direito
34
- usando HoughLinesP, sem OCR.
35
- """
36
- import cv2
37
- import numpy as np
38
-
39
  h, w = img_bgr.shape[:2]
40
- roi = img_bgr[h-100:h-10, int(w*0.6):w] # região inferior direita
41
-
42
  gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
43
  edges = cv2.Canny(gray, 50, 150, apertureSize=3)
44
-
45
  lines = cv2.HoughLinesP(edges, rho=1, theta=np.pi/180, threshold=30,
46
  minLineLength=30, maxLineGap=5)
47
-
48
  longest = 0
49
  if lines is not None:
50
  for line in lines:
51
  x1, y1, x2, y2 = line[0]
52
- if abs(y2 - y1) < 5: # horizontal
53
  length = abs(x2 - x1)
54
  if length > longest:
55
  longest = length
 
56
 
57
- if longest > 0:
58
- mm_per_pixel = 0.500 / longest
59
- print(f"📏 Régua detetada sem OCR: {longest}px = 0.500 mm → {mm_per_pixel:.6f} mm/pixel")
60
- return mm_per_pixel
61
- else:
62
- print("⚠️ Nenhuma linha horizontal de régua encontrada.")
63
- return None
64
-
65
-
66
- def segmentar(imagem_path, dispositivo, conf_min, pixels_minimos):
67
  device = "cuda" if dispositivo == "GPU" and torch.cuda.is_available() else "cpu"
68
  model = carregar_modelo(device)
69
-
70
  results = model.predict(source=imagem_path, device=device, conf=conf_min, verbose=False)
71
  r = results[0]
72
-
73
  img_bgr = cv2.imread(imagem_path)
74
  if img_bgr is None:
75
- return "❌ Erro ao carregar imagem.", None, None
76
  img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
77
  h, w = img_rgb.shape[:2]
78
-
79
  mm_por_pixel = detectar_escala(img_bgr)
80
-
81
- total_detectadas = 0
82
  mascaras_binarias = []
83
-
84
  if r.masks is not None and r.masks.data is not None:
85
- total_detectadas = len(r.masks.data)
86
  for mask in r.masks.data:
87
  m = mask.cpu().numpy().astype(np.uint8) * 255
88
  m = cv2.resize(m, (w, h), interpolation=cv2.INTER_NEAREST)
89
  if contar_pixels_conexos(m, pixels_minimos) > 0:
90
  mascaras_binarias.append((m > 0).astype(np.uint8))
91
-
92
- # Fusão de máscaras sobrepostas
93
  mascaras_fundidas = []
94
  for m in mascaras_binarias:
95
  fundida = False
@@ -100,10 +72,7 @@ def segmentar(imagem_path, dispositivo, conf_min, pixels_minimos):
100
  break
101
  if not fundida:
102
  mascaras_fundidas.append(m)
103
-
104
- total_usadas = len(mascaras_fundidas)
105
  centroides = []
106
-
107
  for m in mascaras_fundidas:
108
  M = cv2.moments(m)
109
  if M["m00"] != 0:
@@ -111,7 +80,6 @@ def segmentar(imagem_path, dispositivo, conf_min, pixels_minimos):
111
  cy = int(M["m01"] / M["m00"])
112
  centroides.append((cx, cy))
113
  cv2.circle(img_rgb, (cx, cy), 12, (0, 255, 0), 2)
114
-
115
  distancia_px = None
116
  distancia_mm = None
117
  if len(centroides) == 2:
@@ -121,53 +89,54 @@ def segmentar(imagem_path, dispositivo, conf_min, pixels_minimos):
121
  distancia_px = calcular_distancia(centroides[0], centroides[1])
122
  if mm_por_pixel:
123
  distancia_mm = distancia_px * mm_por_pixel
124
-
125
- # Salvar imagem anotada
126
- output_path = "output_segmentada.png"
127
  cv2.imwrite(output_path, cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR))
128
-
129
- # Mensagem
130
- mensagem = f"✅ {total_usadas} máscara(s) fundidas (de {total_detectadas} detectadas com conf ≥ {conf_min:.2f} e ≥ {pixels_minimos} px conectados)"
131
- if distancia_px:
132
- mensagem += f"\n📏 Distância: {distancia_px:.1f} px"
133
- if distancia_mm:
134
- mensagem += f" {distancia_mm:.3f} mm"
135
-
136
- return img_rgb, output_path, mensagem
137
-
138
- # Gradio UI
139
- with gr.Blocks(title="Medição de asas") as demo:
140
- gr.Markdown("## Medição do comprimento de asas de abelha")
141
-
142
- with gr.Row():
143
- imagem_input = gr.Image(type="filepath", label="Subir imagem")
144
- dispositivo = gr.Radio(["CPU", "GPU"], label="Dispositivo", value="CPU", visible=False)
145
-
146
- with gr.Row():
147
- conf_slider = gr.Slider(0.01, 1.0, value=0.1, step=0.01, label="Confiança mínima", visible=False)
148
- pixel_slider = gr.Slider(1, 1000, value=10, step=1, label="Pixels conectados mínimos por máscara", visible=False)
149
-
150
- with gr.Row():
151
- img_output = gr.Image(label="Imagem anotada")
152
- download_output = gr.File(label="Download da imagem")
153
-
154
- resumo = gr.Textbox(label="Resumo da segmentação", lines=3)
155
-
156
- # Botão de execução
157
- executar = gr.Button("Medir Asa")
158
-
159
- # Liga a função ao botão
160
- executar.click(
161
- fn=segmentar,
162
- inputs=[imagem_input, dispositivo, conf_slider, pixel_slider],
163
- outputs=[img_output, download_output, resumo]
 
 
 
 
 
 
 
 
 
 
164
  )
165
-
166
- # Limpa saídas quando nova imagem é carregada
167
- imagem_input.change(
168
- fn=lambda _: (None, None, ""),
169
- inputs=imagem_input,
170
- outputs=[img_output, download_output, resumo]
171
- )
172
-
173
- demo.launch(share = True)
 
5
  import numpy as np
6
  import os
7
  import math
8
+ import pandas as pd
9
+ from datetime import datetime
10
 
11
  # Caminho para o modelo treinado
12
  modelo_path = "runs/segment/meu_yolo_seg20/weights/best.pt"
 
27
  def calcular_distancia(p1, p2):
28
  return math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
29
 
 
 
 
30
  def detectar_escala(img_bgr):
 
 
 
 
 
 
 
31
  h, w = img_bgr.shape[:2]
32
+ roi = img_bgr[h-100:h-10, int(w*0.6):w]
 
33
  gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
34
  edges = cv2.Canny(gray, 50, 150, apertureSize=3)
 
35
  lines = cv2.HoughLinesP(edges, rho=1, theta=np.pi/180, threshold=30,
36
  minLineLength=30, maxLineGap=5)
 
37
  longest = 0
38
  if lines is not None:
39
  for line in lines:
40
  x1, y1, x2, y2 = line[0]
41
+ if abs(y2 - y1) < 5:
42
  length = abs(x2 - x1)
43
  if length > longest:
44
  longest = length
45
+ return 0.500 / longest if longest > 0 else None
46
 
47
+ def processar_uma_imagem(imagem_path, dispositivo, conf_min, pixels_minimos):
 
 
 
 
 
 
 
 
 
48
  device = "cuda" if dispositivo == "GPU" and torch.cuda.is_available() else "cpu"
49
  model = carregar_modelo(device)
 
50
  results = model.predict(source=imagem_path, device=device, conf=conf_min, verbose=False)
51
  r = results[0]
 
52
  img_bgr = cv2.imread(imagem_path)
53
  if img_bgr is None:
54
+ return None, None, "Erro ao carregar imagem."
55
  img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
56
  h, w = img_rgb.shape[:2]
 
57
  mm_por_pixel = detectar_escala(img_bgr)
 
 
58
  mascaras_binarias = []
 
59
  if r.masks is not None and r.masks.data is not None:
 
60
  for mask in r.masks.data:
61
  m = mask.cpu().numpy().astype(np.uint8) * 255
62
  m = cv2.resize(m, (w, h), interpolation=cv2.INTER_NEAREST)
63
  if contar_pixels_conexos(m, pixels_minimos) > 0:
64
  mascaras_binarias.append((m > 0).astype(np.uint8))
 
 
65
  mascaras_fundidas = []
66
  for m in mascaras_binarias:
67
  fundida = False
 
72
  break
73
  if not fundida:
74
  mascaras_fundidas.append(m)
 
 
75
  centroides = []
 
76
  for m in mascaras_fundidas:
77
  M = cv2.moments(m)
78
  if M["m00"] != 0:
 
80
  cy = int(M["m01"] / M["m00"])
81
  centroides.append((cx, cy))
82
  cv2.circle(img_rgb, (cx, cy), 12, (0, 255, 0), 2)
 
83
  distancia_px = None
84
  distancia_mm = None
85
  if len(centroides) == 2:
 
89
  distancia_px = calcular_distancia(centroides[0], centroides[1])
90
  if mm_por_pixel:
91
  distancia_mm = distancia_px * mm_por_pixel
92
+ nome = os.path.basename(imagem_path)
93
+ output_path = os.path.splitext(nome)[0] + "_out.png"
 
94
  cv2.imwrite(output_path, cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR))
95
+ return output_path, nome, distancia_px, distancia_mm
96
+
97
+ def processar_varias(imagens, dispositivo, conf_min, pixels_minimos):
98
+ imagens_resultado = []
99
+ nomes, pxs, mms = [], [], []
100
+ for imagem in imagens:
101
+ out_path, nome, dist_px, dist_mm = processar_uma_imagem(imagem, dispositivo, conf_min, pixels_minimos)
102
+ imagens_resultado.append(out_path)
103
+ nomes.append(nome)
104
+ pxs.append(f"{dist_px:.1f}" if dist_px else "-")
105
+ mms.append(f"{dist_mm:.3f}" if dist_mm else "-")
106
+ valores_mm = [float(x) for x in mms if x != "-"]
107
+ media = np.mean(valores_mm) if valores_mm else 0
108
+ desvio = np.std(valores_mm) if valores_mm else 0
109
+ tabela = pd.DataFrame({"Imagem": nomes, "Distância (px)": pxs, "Distância (mm)": mms})
110
+ resumo_df = pd.DataFrame({"Métrica": ["Média", "Desvio padrão"], "Valor (mm)": [f"{media:.3f}", f"{desvio:.3f}"]})
111
+ agora = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
112
+ resultados_xlsx_path = f"resultados_medidas_{agora}.xlsx"
113
+ with pd.ExcelWriter(resultados_xlsx_path) as writer:
114
+ tabela.to_excel(writer, sheet_name="Resultados", index=False)
115
+ resumo_df.to_excel(writer, sheet_name="Resumo", index=False)
116
+ # Criar pasta para imagens
117
+ pasta_saida = f"imagens_processadas_{agora}"
118
+ os.makedirs(pasta_saida, exist_ok=True)
119
+ for img_path in imagens_resultado:
120
+ novo_caminho = os.path.join(pasta_saida, os.path.basename(img_path))
121
+ os.replace(img_path, novo_caminho)
122
+ imagens_resultado = [os.path.join(pasta_saida, os.path.basename(p)) for p in imagens_resultado]
123
+ legendas = [f"{n} - {m} mm" for n, m in zip(nomes, mms)]
124
+ return list(zip(imagens_resultado, legendas)), tabela, resumo_df, resultados_xlsx_path
125
+
126
+ # Interface Gradio
127
+ with gr.Blocks(title="Medição de asas múltiplas") as demo:
128
+ gr.Markdown("## Medição automática de asas de abelha")
129
+ imagens_input = gr.File(label="Subir imagens", file_types=[".png", ".jpg", ".jpeg"], file_count="multiple")
130
+ dispositivo = gr.Radio(["CPU", "GPU"], label="Dispositivo", value="CPU", visible=False)
131
+ conf_slider = gr.Slider(0.01, 1.0, value=0.1, step=0.01, label="Confiança mínima", visible=False)
132
+ pixel_slider = gr.Slider(1, 1000, value=10, step=1, label="Pixels conectados mínimos por máscara", visible=False)
133
+ imagens_saida = gr.Gallery(label="Resultados das imagens segmentadas", columns=4, preview=True, show_label=True)
134
+ tabela_resultados = gr.Dataframe(label="Resultados", interactive=False, headers=["Imagem", "Distância (px)", "Distância (mm)"])
135
+ tabela_resumo = gr.Dataframe(label="Resumo estatístico", interactive=False, headers=["Métrica", "Valor (mm)"])
136
+ download_btn = gr.File(label="Download XLSX")
137
+ imagens_input.change(
138
+ fn=lambda imagens: processar_varias(imagens, "CPU", 0.1, 10),
139
+ inputs=imagens_input,
140
+ outputs=[imagens_saida, tabela_resultados, tabela_resumo, download_btn]
141
  )
142
+ demo.launch(share=False)