ajustes no visual_worker
This commit is contained in:
parent
a92d95d4c0
commit
0a62a1d21a
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -28,9 +28,9 @@
|
|||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea2 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
|
||||
System.Windows.Forms.DataVisualization.Charting.Legend legend2 = new System.Windows.Forms.DataVisualization.Charting.Legend();
|
||||
System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series();
|
||||
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
|
||||
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
|
||||
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
|
||||
this.picRGB = new System.Windows.Forms.PictureBox();
|
||||
this.picHeatmap = new System.Windows.Forms.PictureBox();
|
||||
this.lblRGB = new System.Windows.Forms.Label();
|
||||
|
|
@ -63,9 +63,11 @@
|
|||
this.txtDistanciaMax = new System.Windows.Forms.TextBox();
|
||||
this.lblVelocidade = new System.Windows.Forms.Label();
|
||||
this.lblDistanciaMax = new System.Windows.Forms.Label();
|
||||
this.lblTamanhoMin = new System.Windows.Forms.Label();
|
||||
this.txtTamanhoMin = new System.Windows.Forms.TextBox();
|
||||
this.lblLarguraMin = new System.Windows.Forms.Label();
|
||||
this.txtLarguraMin = new System.Windows.Forms.TextBox();
|
||||
this.chbReqAutomatico = new System.Windows.Forms.CheckBox();
|
||||
this.lblAlturaMin = new System.Windows.Forms.Label();
|
||||
this.txtAlturaMin = new System.Windows.Forms.TextBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picRGB)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picHeatmap)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.picDebug)).BeginInit();
|
||||
|
|
@ -310,16 +312,16 @@
|
|||
//
|
||||
// chartPerfilSolo
|
||||
//
|
||||
chartArea2.Name = "ChartArea1";
|
||||
this.chartPerfilSolo.ChartAreas.Add(chartArea2);
|
||||
legend2.Name = "Legend1";
|
||||
this.chartPerfilSolo.Legends.Add(legend2);
|
||||
chartArea1.Name = "ChartArea1";
|
||||
this.chartPerfilSolo.ChartAreas.Add(chartArea1);
|
||||
legend1.Name = "Legend1";
|
||||
this.chartPerfilSolo.Legends.Add(legend1);
|
||||
this.chartPerfilSolo.Location = new System.Drawing.Point(606, 412);
|
||||
this.chartPerfilSolo.Name = "chartPerfilSolo";
|
||||
series2.ChartArea = "ChartArea1";
|
||||
series2.Legend = "Legend1";
|
||||
series2.Name = "Series1";
|
||||
this.chartPerfilSolo.Series.Add(series2);
|
||||
series1.ChartArea = "ChartArea1";
|
||||
series1.Legend = "Legend1";
|
||||
series1.Name = "Series1";
|
||||
this.chartPerfilSolo.Series.Add(series1);
|
||||
this.chartPerfilSolo.Size = new System.Drawing.Size(496, 305);
|
||||
this.chartPerfilSolo.TabIndex = 30;
|
||||
this.chartPerfilSolo.Text = "chart1";
|
||||
|
|
@ -362,7 +364,7 @@
|
|||
//
|
||||
// txtDistanciaMax
|
||||
//
|
||||
this.txtDistanciaMax.Location = new System.Drawing.Point(794, 347);
|
||||
this.txtDistanciaMax.Location = new System.Drawing.Point(888, 347);
|
||||
this.txtDistanciaMax.Name = "txtDistanciaMax";
|
||||
this.txtDistanciaMax.Size = new System.Drawing.Size(88, 20);
|
||||
this.txtDistanciaMax.TabIndex = 35;
|
||||
|
|
@ -381,29 +383,29 @@
|
|||
// lblDistanciaMax
|
||||
//
|
||||
this.lblDistanciaMax.AutoSize = true;
|
||||
this.lblDistanciaMax.Location = new System.Drawing.Point(791, 331);
|
||||
this.lblDistanciaMax.Location = new System.Drawing.Point(885, 331);
|
||||
this.lblDistanciaMax.Name = "lblDistanciaMax";
|
||||
this.lblDistanciaMax.Size = new System.Drawing.Size(91, 13);
|
||||
this.lblDistanciaMax.TabIndex = 37;
|
||||
this.lblDistanciaMax.Text = "Distancia Max (m)";
|
||||
//
|
||||
// lblTamanhoMin
|
||||
// lblLarguraMin
|
||||
//
|
||||
this.lblTamanhoMin.AutoSize = true;
|
||||
this.lblTamanhoMin.Location = new System.Drawing.Point(697, 331);
|
||||
this.lblTamanhoMin.Name = "lblTamanhoMin";
|
||||
this.lblTamanhoMin.Size = new System.Drawing.Size(89, 13);
|
||||
this.lblTamanhoMin.TabIndex = 39;
|
||||
this.lblTamanhoMin.Text = "Tamanho Min (m)";
|
||||
this.lblLarguraMin.AutoSize = true;
|
||||
this.lblLarguraMin.Location = new System.Drawing.Point(697, 331);
|
||||
this.lblLarguraMin.Name = "lblLarguraMin";
|
||||
this.lblLarguraMin.Size = new System.Drawing.Size(80, 13);
|
||||
this.lblLarguraMin.TabIndex = 39;
|
||||
this.lblLarguraMin.Text = "Largura Min (m)";
|
||||
//
|
||||
// txtTamanhoMin
|
||||
// txtLarguraMin
|
||||
//
|
||||
this.txtTamanhoMin.Location = new System.Drawing.Point(700, 347);
|
||||
this.txtTamanhoMin.Name = "txtTamanhoMin";
|
||||
this.txtTamanhoMin.Size = new System.Drawing.Size(88, 20);
|
||||
this.txtTamanhoMin.TabIndex = 38;
|
||||
this.txtTamanhoMin.Text = "0,30";
|
||||
this.txtTamanhoMin.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.txtLarguraMin.Location = new System.Drawing.Point(700, 347);
|
||||
this.txtLarguraMin.Name = "txtLarguraMin";
|
||||
this.txtLarguraMin.Size = new System.Drawing.Size(88, 20);
|
||||
this.txtLarguraMin.TabIndex = 38;
|
||||
this.txtLarguraMin.Text = "0,30";
|
||||
this.txtLarguraMin.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// chbReqAutomatico
|
||||
//
|
||||
|
|
@ -415,14 +417,34 @@
|
|||
this.chbReqAutomatico.Text = "Req Automatico";
|
||||
this.chbReqAutomatico.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// lblAlturaMin
|
||||
//
|
||||
this.lblAlturaMin.AutoSize = true;
|
||||
this.lblAlturaMin.Location = new System.Drawing.Point(791, 331);
|
||||
this.lblAlturaMin.Name = "lblAlturaMin";
|
||||
this.lblAlturaMin.Size = new System.Drawing.Size(71, 13);
|
||||
this.lblAlturaMin.TabIndex = 42;
|
||||
this.lblAlturaMin.Text = "Altura Min (m)";
|
||||
//
|
||||
// txtAlturaMin
|
||||
//
|
||||
this.txtAlturaMin.Location = new System.Drawing.Point(794, 347);
|
||||
this.txtAlturaMin.Name = "txtAlturaMin";
|
||||
this.txtAlturaMin.Size = new System.Drawing.Size(88, 20);
|
||||
this.txtAlturaMin.TabIndex = 41;
|
||||
this.txtAlturaMin.Text = "0,30";
|
||||
this.txtAlturaMin.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// frmOperarioSonar
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(1114, 759);
|
||||
this.Controls.Add(this.lblAlturaMin);
|
||||
this.Controls.Add(this.txtAlturaMin);
|
||||
this.Controls.Add(this.chbReqAutomatico);
|
||||
this.Controls.Add(this.lblTamanhoMin);
|
||||
this.Controls.Add(this.txtTamanhoMin);
|
||||
this.Controls.Add(this.lblLarguraMin);
|
||||
this.Controls.Add(this.txtLarguraMin);
|
||||
this.Controls.Add(this.lblDistanciaMax);
|
||||
this.Controls.Add(this.lblVelocidade);
|
||||
this.Controls.Add(this.txtDistanciaMax);
|
||||
|
|
@ -504,8 +526,10 @@
|
|||
private System.Windows.Forms.TextBox txtDistanciaMax;
|
||||
private System.Windows.Forms.Label lblVelocidade;
|
||||
private System.Windows.Forms.Label lblDistanciaMax;
|
||||
private System.Windows.Forms.Label lblTamanhoMin;
|
||||
private System.Windows.Forms.TextBox txtTamanhoMin;
|
||||
private System.Windows.Forms.Label lblLarguraMin;
|
||||
private System.Windows.Forms.TextBox txtLarguraMin;
|
||||
private System.Windows.Forms.CheckBox chbReqAutomatico;
|
||||
private System.Windows.Forms.Label lblAlturaMin;
|
||||
private System.Windows.Forms.TextBox txtAlturaMin;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ namespace AgroBase.Forms
|
|||
AtualizarListaCameras();
|
||||
ConfigurarGridDeteccoes(dgvAnomalias);
|
||||
ConfigurarGridDeteccoes(dgvSolo);
|
||||
tmrLeitura = new AsyncTaskTimerModel("tmrLeitura_VisalWorker", tmrLeitura_Tick, 500, this);
|
||||
tmrLeitura = new AsyncTaskTimerModel("tmrLeitura_VisalWorker", tmrLeitura_Tick, 10, this);
|
||||
tmrLeitura.Start();
|
||||
}
|
||||
|
||||
|
|
@ -48,10 +48,10 @@ namespace AgroBase.Forms
|
|||
{
|
||||
if (!chbReqAutomatico.Checked) return;
|
||||
|
||||
btnLeituraRGB_Click(new object(), new EventArgs());
|
||||
btnLeituraHeatmap_Click(new object(), new EventArgs());
|
||||
btnReqAnomalias_Click(new object(), new EventArgs());
|
||||
btnReqSolo_Click(new object(), new EventArgs());
|
||||
await EnviarComandoReqFrame(VisualWorkerCommandType.Get_RGB_Frame);
|
||||
await EnviarComandoReqFrame(VisualWorkerCommandType.Get_Heatmap_Frame);
|
||||
await EnviarComandoAnalise(VisualWorkerCommandType.Get_Analise_Anomalia);
|
||||
await EnviarComandoAnalise(VisualWorkerCommandType.Get_Analise_Solo);
|
||||
}
|
||||
|
||||
private void btnAtualizarCameras_Click(object sender, EventArgs e)
|
||||
|
|
@ -97,20 +97,23 @@ namespace AgroBase.Forms
|
|||
|
||||
private async void btnLeituraRGB_Click(object sender, EventArgs e)
|
||||
{
|
||||
await EnviarComando(VisualWorkerCommandType.Get_RGB_Frame);
|
||||
VisualWorkerMessageFrameModel frame = visualWorker.DadosLeitura.Leitura.obj.rgb_frame;
|
||||
picRGB.Image?.Dispose();
|
||||
picRGB.Image = frame?.image;
|
||||
lblLeituraRGB.Text = $"Leitura em: {FuncoesGlobais.TimestampToDate(frame?.timestamp ?? 0)}";
|
||||
await EnviarComandoReqFrame(VisualWorkerCommandType.Get_RGB_Frame);
|
||||
}
|
||||
|
||||
private async void btnLeituraHeatmap_Click(object sender, EventArgs e)
|
||||
{
|
||||
await EnviarComando(VisualWorkerCommandType.Get_Heatmap_Frame);
|
||||
VisualWorkerMessageFrameModel frame = visualWorker.DadosLeitura.Leitura.obj.heatmap_frame;
|
||||
picHeatmap.Image?.Dispose();
|
||||
picHeatmap.Image = frame?.image;
|
||||
lblLeituraHeatmap.Text = $"Leitura em: {FuncoesGlobais.TimestampToDate(frame?.timestamp ?? 0)}";
|
||||
await EnviarComandoReqFrame(VisualWorkerCommandType.Get_Heatmap_Frame);
|
||||
}
|
||||
|
||||
private async Task EnviarComandoReqFrame(VisualWorkerCommandType comando)
|
||||
{
|
||||
await EnviarComando(comando);
|
||||
VisualWorkerMessageFrameModel frame = comando == VisualWorkerCommandType.Get_RGB_Frame ? visualWorker.DadosLeitura.Leitura.obj.rgb_frame : visualWorker.DadosLeitura.Leitura.obj.heatmap_frame;
|
||||
PictureBox pic = comando == VisualWorkerCommandType.Get_RGB_Frame ? picRGB : picHeatmap;
|
||||
Label lbl = comando == VisualWorkerCommandType.Get_RGB_Frame ? lblLeituraRGB : lblLeituraHeatmap;
|
||||
pic.Image?.Dispose();
|
||||
try { pic.Image = frame?.image; } catch { }
|
||||
lbl.Text = $"Leitura em: {FuncoesGlobais.TimestampToDate(frame?.timestamp ?? 0)}";
|
||||
}
|
||||
|
||||
private async void btnReqStatusDispositivo_Click(object sender, EventArgs e)
|
||||
|
|
@ -120,13 +123,17 @@ namespace AgroBase.Forms
|
|||
|
||||
private async void btnReqAnomalias_Click(object sender, EventArgs e)
|
||||
{
|
||||
await EnviarComando(VisualWorkerCommandType.Get_Analise_Anomalia, 1000, new { velocidade_ms = txtVelocidade.Text.Replace(",", "."), distancia_max = txtDistanciaMax.Text.Replace(",", "."), tamanho_min = txtTamanhoMin.Text.Replace(",", ".") });
|
||||
AtualizarDadosAnalise();
|
||||
await EnviarComandoAnalise(VisualWorkerCommandType.Get_Analise_Anomalia);
|
||||
}
|
||||
|
||||
private async void btnReqSolo_Click(object sender, EventArgs e)
|
||||
{
|
||||
await EnviarComando(VisualWorkerCommandType.Get_Analise_Solo, 1000, new { velocidade_ms = txtVelocidade.Text.Replace(",", "."), distancia_max = txtDistanciaMax.Text.Replace(",", "."), tamanho_min = txtTamanhoMin.Text.Replace(",", ".") });
|
||||
await EnviarComandoAnalise(VisualWorkerCommandType.Get_Analise_Solo);
|
||||
}
|
||||
|
||||
private async Task EnviarComandoAnalise(VisualWorkerCommandType comando)
|
||||
{
|
||||
await EnviarComando(comando, 1000, new { velocidade_ms = txtVelocidade.Text.Replace(",", "."), distancia_max = txtDistanciaMax.Text.Replace(",", "."), largura_min = txtLarguraMin.Text.Replace(",", "."), altura_min = txtAlturaMin.Text.Replace(",", ".") });
|
||||
AtualizarDadosAnalise();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace AgroBase.Models.Operadores
|
|||
{
|
||||
public long timestamp { get; set; }
|
||||
public string frame { get; set; }
|
||||
public Bitmap image { get; set; }
|
||||
public Bitmap image { get; set; } = null;
|
||||
public VisualWorkerMessageFrameModel Clone()
|
||||
{
|
||||
return new VisualWorkerMessageFrameModel()
|
||||
|
|
@ -195,6 +195,8 @@ namespace AgroBase.Models.Operadores
|
|||
{
|
||||
if (frame?.image == null) return null;
|
||||
|
||||
try { frame.image.Clone(); } catch { return null; }
|
||||
|
||||
Bitmap img = new Bitmap(frame.image);
|
||||
using (Graphics g = Graphics.FromImage(img))
|
||||
{
|
||||
|
|
@ -207,6 +209,11 @@ namespace AgroBase.Models.Operadores
|
|||
Font font = new Font("Arial", 8);
|
||||
Brush brush = Brushes.Black;
|
||||
|
||||
// 🔸 Definir brushes semi-transparentes para preenchimento
|
||||
Brush brushObstaculo = new SolidBrush(Color.FromArgb(60, Color.Red));
|
||||
Brush brushErosao = new SolidBrush(Color.FromArgb(60, Color.Blue));
|
||||
Brush brushLombada = new SolidBrush(Color.FromArgb(60, Color.Orange));
|
||||
|
||||
int imgWidth = img.Width;
|
||||
int imgHeight = img.Height;
|
||||
|
||||
|
|
@ -217,25 +224,41 @@ namespace AgroBase.Models.Operadores
|
|||
|
||||
foreach (var bbox in bboxes)
|
||||
{
|
||||
var pen = penObstaculo;
|
||||
if (bbox.tipo == "erosao") pen = penErosao;
|
||||
if (bbox.tipo == "lombada") pen = penLombada;
|
||||
Pen pen = penObstaculo;
|
||||
Brush fill = brushObstaculo;
|
||||
|
||||
if (bbox.tipo == "erosao")
|
||||
{
|
||||
pen = penErosao;
|
||||
fill = brushErosao;
|
||||
}
|
||||
if (bbox.tipo == "lombada")
|
||||
{
|
||||
pen = penLombada;
|
||||
fill = brushLombada;
|
||||
}
|
||||
|
||||
var rect = new Rectangle(bbox.bbox[0], bbox.bbox[1], bbox.bbox[2], bbox.bbox[3]);
|
||||
|
||||
// 🔸 Preencher com opacidade
|
||||
g.FillRectangle(fill, rect);
|
||||
|
||||
// 🔸 Contorno
|
||||
g.DrawRectangle(pen, rect);
|
||||
|
||||
// 🔸 Label
|
||||
string label = $"{bbox.tipo} {bbox.distancia_m}m Δ{bbox.variacao}mm";
|
||||
g.DrawString(label, font, brush, rect.X, rect.Y - 12);
|
||||
}
|
||||
|
||||
// 🔸 Desenhar linha da faixa do solo
|
||||
// 🔸 Linha da faixa do solo
|
||||
if (solo != null)
|
||||
{
|
||||
int yInicio = solo.y_inicio;
|
||||
g.DrawLine(penFaixaSolo, 0, yInicio, imgWidth, yInicio);
|
||||
}
|
||||
|
||||
// 🔸 Desenhar gráfico do perfil de solo
|
||||
// 🔸 Gráfico do perfil de solo
|
||||
if (solo?.perfil != null && solo.perfil.Count > 1)
|
||||
{
|
||||
var perfil = solo.perfil;
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ namespace AgroBase.Services.Operadores
|
|||
case VisualWorkerCommandType.Get_Analise_Solo:
|
||||
{
|
||||
DadosLeitura.Leitura.obj.analise.timestamp = msg.obj.analise.timestamp;
|
||||
DadosLeitura.Leitura.obj.analise.solo = msg.obj.analise.solo.Clone();
|
||||
DadosLeitura.Leitura.obj.analise.solo = msg.obj.analise.solo?.Clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -1,10 +1,11 @@
|
|||
# camera_manager.py
|
||||
|
||||
import depthai as dai
|
||||
import cv2
|
||||
import numpy as np
|
||||
import time
|
||||
|
||||
from visual_worker.filtros import filtrar_depth_morfologia
|
||||
|
||||
# Parâmetros da câmera
|
||||
RGB_WIDTH, RGB_HEIGHT = 640, 480
|
||||
#DEPTH_WIDTH, DEPTH_HEIGHT = 320, 240
|
||||
|
|
@ -15,6 +16,7 @@ BASELINE = 0.075 # distância entre câmeras estéreo (em metros)
|
|||
FOV_H = 1
|
||||
FOV_V = 1
|
||||
DISTANCIA_MAXIMA = 3000.0
|
||||
QTD_SETORES_SOLO = 25
|
||||
|
||||
# Variáveis globais
|
||||
device = None
|
||||
|
|
@ -27,7 +29,9 @@ timestamp_ultimo_depth_frame = None
|
|||
ultimo_frame_rgb = None
|
||||
timestamp_ultimo_rgb_frame = None
|
||||
|
||||
def iniciar_camera(index=0, distancia_max=3000.0):
|
||||
|
||||
|
||||
def iniciar_camera(index=0, distancia_max=3000.0, qtd_setores = 25):
|
||||
global device, device_info, rgb_queue, depth_queue, camera_pronta
|
||||
|
||||
camera_pronta = False
|
||||
|
|
@ -80,16 +84,17 @@ def iniciar_camera(index=0, distancia_max=3000.0):
|
|||
|
||||
print(f"Câmera iniciada: {device_info.name} (ID: {device_info.getMxId()})")
|
||||
|
||||
set_camera_calib(distancia_max)
|
||||
set_camera_calib(distancia_max, qtd_setores)
|
||||
|
||||
def set_camera_calib(distancia_max):
|
||||
def set_camera_calib(distancia_max, qtd_setores):
|
||||
if not verificar_camera_ativa():
|
||||
return
|
||||
|
||||
global FX, FY, BASELINE, FOV_H, FOV_V, DISTANCIA_MAXIMA
|
||||
global FX, FY, BASELINE, FOV_H, FOV_V, DISTANCIA_MAXIMA, QTD_SETORES_SOLO
|
||||
calib = device.readCalibration()
|
||||
|
||||
DISTANCIA_MAXIMA = distancia_max
|
||||
QTD_SETORES_SOLO = qtd_setores
|
||||
|
||||
# Obtem matriz intrínseca da câmera LEFT (com resolução padrão 640x400)
|
||||
intrinsics = calib.getCameraIntrinsics(dai.CameraBoardSocket.LEFT, DEPTH_WIDTH, DEPTH_HEIGHT)
|
||||
|
|
@ -113,7 +118,8 @@ def get_camera_calib():
|
|||
"baseline": BASELINE,
|
||||
"fov_h": FOV_H,
|
||||
"fov_v": FOV_V,
|
||||
"distancia_maxima": DISTANCIA_MAXIMA
|
||||
"distancia_maxima": DISTANCIA_MAXIMA,
|
||||
"qtd_setores_solo": QTD_SETORES_SOLO
|
||||
}
|
||||
|
||||
def verificar_camera_ativa():
|
||||
|
|
@ -159,7 +165,7 @@ def get_rgb_frame():
|
|||
timestamp_ultimo_rgb_frame = time.time()
|
||||
return ultimo_frame_rgb, timestamp_ultimo_rgb_frame
|
||||
|
||||
def get_depth_frame():
|
||||
def get_depth_frame_atual():
|
||||
if not verificar_camera_ativa():
|
||||
return None, None
|
||||
global ultimo_frame_depth, timestamp_ultimo_depth_frame
|
||||
|
|
@ -170,8 +176,13 @@ def get_depth_frame():
|
|||
if frame is not None:
|
||||
ultimo_frame_depth = frame.getFrame()
|
||||
timestamp_ultimo_depth_frame = time.time()
|
||||
#return filtrar_depth_morfologia(ultimo_frame_depth), timestamp_ultimo_depth_frame
|
||||
return ultimo_frame_depth, timestamp_ultimo_depth_frame
|
||||
|
||||
def get_depth_frame():
|
||||
#return buffer_depth.gerar_depth_filtrado(metodo="mediana"), timestamp_ultimo_depth_frame
|
||||
return get_depth_frame_atual()
|
||||
|
||||
def get_heatmap_frame():
|
||||
frame, timestamp = get_depth_frame()
|
||||
if frame is not None:
|
||||
|
|
@ -246,3 +257,5 @@ def get_status_dispositivo():
|
|||
"fx": FX,
|
||||
"baseline": BASELINE
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import cv2
|
||||
import numpy as np
|
||||
|
||||
def filtrar_depth_morfologia(depth_frame, distancia_maxima=3000.0, kernel_size=5):
|
||||
"""
|
||||
Aplica filtros morfológicos no depth para eliminar buracos e ruídos pequenos.
|
||||
"""
|
||||
# Cria máscara dos pixels inválidos (0 ou muito longe)
|
||||
mask_invalid = (depth_frame == 0) | (depth_frame > distancia_maxima)
|
||||
mask_invalid = mask_invalid.astype(np.uint8) * 255
|
||||
|
||||
# Kernel para morfologia
|
||||
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
|
||||
|
||||
# 🔧 Fechamento → Tapa buracos
|
||||
mask_close = cv2.morphologyEx(mask_invalid, cv2.MORPH_CLOSE, kernel)
|
||||
|
||||
# 🔧 Abertura → Remove pontos isolados (opcional, pode comentar se não quiser)
|
||||
mask_clean = cv2.morphologyEx(mask_close, cv2.MORPH_OPEN, kernel)
|
||||
|
||||
# → Encontrar onde deve corrigir
|
||||
mask_corrigir = (mask_clean == 255)
|
||||
|
||||
depth_corrigido = depth_frame.copy()
|
||||
|
||||
# 🔥 Para cada pixel a corrigir → pega mediana dos vizinhos válidos
|
||||
coords = np.argwhere(mask_corrigir)
|
||||
|
||||
for y, x in coords:
|
||||
y1 = max(y - 2, 0)
|
||||
y2 = min(y + 3, depth_frame.shape[0])
|
||||
x1 = max(x - 2, 0)
|
||||
x2 = min(x + 3, depth_frame.shape[1])
|
||||
|
||||
vizinhos = depth_frame[y1:y2, x1:x2]
|
||||
vizinhos_validos = vizinhos[(vizinhos > 0) & (vizinhos < distancia_maxima)]
|
||||
|
||||
if len(vizinhos_validos) > 0:
|
||||
depth_corrigido[y, x] = np.median(vizinhos_validos)
|
||||
else:
|
||||
depth_corrigido[y, x] = 0 # Continua inválido se não há vizinhos bons
|
||||
|
||||
return depth_corrigido
|
||||
|
|
@ -15,6 +15,8 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|||
# ─────────────────────────────
|
||||
mqtt_topic = sys.argv[1] if len(sys.argv) > 1 else "agrobot/operador/sonar"
|
||||
camera_index = int(sys.argv[2]) if len(sys.argv) > 2 else 0
|
||||
distancia_max = int(sys.argv[3]) if len(sys.argv) > 3 else 3000
|
||||
qtd_setores = int(sys.argv[4]) if len(sys.argv) > 4 else 25
|
||||
|
||||
|
||||
# ─────────────────────────────
|
||||
|
|
@ -76,9 +78,10 @@ def ao_receber_comando(topico, payload):
|
|||
}
|
||||
|
||||
elif codigo == Comando.CALIBRAR_GRADES:
|
||||
from camera_manager import get_depth_frame
|
||||
from camera_manager import get_depth_frame, get_camera_calib
|
||||
from processamento.percepcao3d import calibrar
|
||||
sucesso = calibrar(get_depth_frame, n_frames=50)
|
||||
calib = get_camera_calib()
|
||||
sucesso = calibrar(get_depth_frame, calib, n_frames=50)
|
||||
resposta = {
|
||||
"calibragem": {
|
||||
"timestamp": time.time(),
|
||||
|
|
@ -92,8 +95,9 @@ def ao_receber_comando(topico, payload):
|
|||
calib = get_camera_calib()
|
||||
velocidade_ms = float(payload.get("velocidade_ms", 0.0))
|
||||
distancia_max = float(payload.get("distancia_max", 2.0))
|
||||
tamanho_min = float(payload.get("tamanho_min", 0.25))
|
||||
resultado = analisar(TipoAnalise.ANOMALIA, get_depth_frame, calib, velocidade_ms, distancia_max, tamanho_min)
|
||||
largura_min = float(payload.get("largura_min", 0.25))
|
||||
altura_min = float(payload.get("altura_min", 0.25))
|
||||
resultado = analisar(TipoAnalise.ANOMALIA, get_depth_frame, calib, velocidade_ms, distancia_max, largura_min, altura_min)
|
||||
resposta = {
|
||||
"timestamp": time.time(),
|
||||
"analise": resultado
|
||||
|
|
@ -105,8 +109,9 @@ def ao_receber_comando(topico, payload):
|
|||
calib = get_camera_calib()
|
||||
velocidade_ms = float(payload.get("velocidade_ms", 0.0))
|
||||
distancia_max = float(payload.get("distancia_max", 2.0))
|
||||
tamanho_min = float(payload.get("tamanho_min", 0.25))
|
||||
resultado = analisar(TipoAnalise.SOLO, get_depth_frame, calib, velocidade_ms, distancia_max, tamanho_min)
|
||||
largura_min = float(payload.get("largura_min", 0.25))
|
||||
altura_min = float(payload.get("altura_min", 0.25))
|
||||
resultado = analisar(TipoAnalise.SOLO, get_depth_frame, calib, velocidade_ms, distancia_max, largura_min, altura_min)
|
||||
resposta = {
|
||||
"timestamp": time.time(),
|
||||
"analise": resultado
|
||||
|
|
@ -125,7 +130,7 @@ if __name__ == "__main__":
|
|||
|
||||
# 1. Inicia a câmera
|
||||
from camera_manager import iniciar_camera
|
||||
iniciar_camera(camera_index)
|
||||
iniciar_camera(camera_index, distancia_max, qtd_setores)
|
||||
|
||||
from mqtt_handler import iniciar_mqtt, enviar_mensagem_script_carregado
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import numpy as np
|
||||
import cv2
|
||||
|
||||
from visual_worker.processamento.gestor_deteccoes import GestorDeteccoes
|
||||
|
||||
def detectar_anomalias(depth_frame, depth_referencia, limiar, distancia_max, tamanho_min, calib):
|
||||
# 🔥 Instanciar fora do loop principal (no seu Visual Worker ou processamento):
|
||||
gestor_anomalias = GestorDeteccoes(frames_minimos=3)
|
||||
|
||||
def detectar_anomalias(depth_frame, depth_referencia, limiar, distancia_max, largura_min, altura_min, calib):
|
||||
# Gera mapa de anomalias
|
||||
mapa = gerar_mapa_anomalias(depth_frame, depth_referencia, limiar)
|
||||
mapa_anomalia = aplicar_filtros_mapa_anomalias(mapa)
|
||||
|
|
@ -11,9 +15,12 @@ def detectar_anomalias(depth_frame, depth_referencia, limiar, distancia_max, tam
|
|||
lista_bboxes = extrair_bounding_boxes(mapa_anomalia, depth_frame, depth_referencia, calib)
|
||||
# Filtra as detecções relevantes
|
||||
from visual_worker.utils import filtrar_deteccoes
|
||||
lista_bboxes_filtrada = filtrar_deteccoes(lista_bboxes, distancia_max, tamanho_min)
|
||||
lista_bboxes_filtrada = filtrar_deteccoes(lista_bboxes, distancia_max, largura_min, altura_min)
|
||||
|
||||
return lista_bboxes_filtrada
|
||||
# 🔥 Aplica o filtro de persistência
|
||||
lista_filtrada_temporal = gestor_anomalias.atualizar(lista_bboxes_filtrada)
|
||||
|
||||
return lista_filtrada_temporal
|
||||
|
||||
|
||||
# Geração do Mapa de Anomalias
|
||||
|
|
|
|||
|
|
@ -1,21 +1,17 @@
|
|||
import numpy as np
|
||||
from visual_worker.utils import filtrar_depth_valid, filtrar_deteccoes
|
||||
|
||||
def analisar_solo(depth_frame, percentual_altura, n_setores, limiar_variacao_mm, distancia_max, tamanho_min, calib):
|
||||
"""
|
||||
Analisa o perfil do solo na parte inferior da imagem.
|
||||
Detecta variações como lombadas ou buracos.
|
||||
"""
|
||||
|
||||
def obter_regiao_solo(depth_frame, percentual_altura):
|
||||
altura, largura = depth_frame.shape
|
||||
|
||||
# Define a região do solo (parte inferior)
|
||||
linhas_solo = int((percentual_altura / 100) * altura)
|
||||
y_inicio = altura - linhas_solo
|
||||
depth_solo = depth_frame[y_inicio:altura, :]
|
||||
return depth_solo, y_inicio, linhas_solo, altura, largura
|
||||
|
||||
# Divide em setores laterais
|
||||
|
||||
def calcular_profundidades(depth_solo, n_setores, largura, distancia_max):
|
||||
largura_setor = largura // n_setores
|
||||
|
||||
profundidades = []
|
||||
|
||||
for i in range(n_setores):
|
||||
|
|
@ -23,26 +19,55 @@ def analisar_solo(depth_frame, percentual_altura, n_setores, limiar_variacao_mm,
|
|||
x_fim = (i + 1) * largura_setor if (i < n_setores - 1) else largura
|
||||
|
||||
setor = depth_solo[:, x_inicio:x_fim]
|
||||
from visual_worker.utils import filtrar_depth_valid
|
||||
setor_valido = filtrar_depth_valid(setor, calib["distancia_maxima"])
|
||||
|
||||
if len(setor_valido) == 0:
|
||||
z = 0
|
||||
else:
|
||||
z = np.median(setor_valido) # Usa mediana para robustez
|
||||
setor_valido = filtrar_depth_valid(setor, distancia_max)
|
||||
|
||||
z = np.median(setor_valido) if len(setor_valido) > 0 else 0
|
||||
print(f"Setor {i}: profundidade = {z}, Total pixels = {setor.size}, Válidos após filtro = {setor_valido.size}")
|
||||
profundidades.append(z)
|
||||
|
||||
profundidades = np.array(profundidades)
|
||||
return np.array(profundidades), largura_setor
|
||||
|
||||
# Detecta variações abruptas
|
||||
|
||||
def calcular_referencia(profundidades):
|
||||
desvio = np.std(profundidades[profundidades > 0])
|
||||
|
||||
if desvio > 150:
|
||||
referencia = np.mean(profundidades[profundidades > 0])
|
||||
metodo = "média"
|
||||
else:
|
||||
referencia = np.median(profundidades[profundidades > 0])
|
||||
metodo = "mediana"
|
||||
|
||||
print(f"Referência do solo: {referencia} usando {metodo}")
|
||||
return referencia
|
||||
|
||||
|
||||
def gerar_bbox(x, y, w, h, z_media, calib, largura, altura, tipo, dados_variacao):
|
||||
largura_real = z_media * 2 * np.tan(calib["fov_h"] / 2) * (w / largura)
|
||||
altura_real = z_media * 2 * np.tan(calib["fov_v"] / 2) * (h / altura)
|
||||
|
||||
centro_x = (x + w / 2) / largura * 100
|
||||
centro_y = (y + h / 2) / altura * 100
|
||||
|
||||
print(f"BBOX → X={x}, W={w}, Distancia={z_media}, Largura_real={largura_real}, Altura_real={altura_real}")
|
||||
|
||||
return {
|
||||
"tipo": tipo,
|
||||
"bbox": (int(x), int(y), int(w), int(h)),
|
||||
"centro_percentual": (float(centro_x), float(centro_y)),
|
||||
"distancia_m": round(z_media, 3),
|
||||
"largura_m": round(largura_real, 3),
|
||||
"altura_m": round(altura_real, 3),
|
||||
**dados_variacao
|
||||
}
|
||||
|
||||
|
||||
def detectar_variacao_lateral(profundidades, largura_setor, y_inicio, linhas_solo, calib, largura, altura, limiar):
|
||||
lista = []
|
||||
variacoes = np.abs(np.diff(profundidades))
|
||||
indices_var = np.where(variacoes > limiar_variacao_mm)[0]
|
||||
indices = np.where(variacoes > limiar)[0]
|
||||
|
||||
# Gera bounding boxes das variações
|
||||
lista_bboxes = []
|
||||
|
||||
for idx in indices_var:
|
||||
for idx in indices:
|
||||
x = idx * largura_setor
|
||||
y = y_inicio
|
||||
w = largura_setor * 2
|
||||
|
|
@ -50,44 +75,240 @@ def analisar_solo(depth_frame, percentual_altura, n_setores, limiar_variacao_mm,
|
|||
|
||||
tipo = "lombada" if profundidades[idx] > profundidades[idx + 1] else "erosao"
|
||||
|
||||
# Centro percentual na imagem
|
||||
centro_x = (x + w / 2) / largura * 100
|
||||
centro_y = (y + h / 2) / altura * 100
|
||||
|
||||
z1 = profundidades[idx]
|
||||
z2 = profundidades[idx + 1]
|
||||
variacao = abs(z1 - z2)
|
||||
z_media = (z1 + z2) / 2000.0 # mm → m
|
||||
|
||||
# Como no solo não usamos distância real na frente (Z em metros), podemos:
|
||||
# → Calcular uma média se quiser, ou deixar como None/0
|
||||
z_media = (z1 + z2) / 2000.0 # Converte de mm pra metros (aproximação)
|
||||
bbox = gerar_bbox(
|
||||
x, y, w, h, z_media, calib, largura, altura, tipo,
|
||||
{"profundidades": (round(z1, 1), round(z2, 1)), "variacao": round(variacao, 1)}
|
||||
)
|
||||
|
||||
lista.append(bbox)
|
||||
|
||||
return lista
|
||||
|
||||
|
||||
def detectar_desvios(profundidades, referencia, largura_setor, y_inicio, linhas_solo, calib, largura, altura, limiar):
|
||||
lista = []
|
||||
|
||||
for i, z in enumerate(profundidades):
|
||||
if z == 0:
|
||||
continue
|
||||
|
||||
desvio = abs(z - referencia)
|
||||
print(f"Setor {i}: Z = {z}, Ref = {referencia}, Desvio = {desvio}")
|
||||
|
||||
if desvio > limiar:
|
||||
print(f"--> DESVIO DETECTADO no setor {i}, Desvio = {desvio}")
|
||||
x = i * largura_setor
|
||||
y = y_inicio
|
||||
w = largura_setor
|
||||
h = linhas_solo
|
||||
|
||||
tipo = "lombada" if z < referencia else "erosao"
|
||||
|
||||
z_media = z / 1000.0 # mm → m
|
||||
|
||||
bbox = gerar_bbox(
|
||||
x, y, w, h, z_media, calib, largura, altura, tipo,
|
||||
{"profundidades": (round(referencia, 1), round(z, 1)), "variacao": round(desvio, 1)}
|
||||
)
|
||||
|
||||
lista.append(bbox)
|
||||
|
||||
return lista
|
||||
|
||||
def detectar_agrupado(profundidades, referencia, largura_setor, y_inicio, linhas_solo, calib, largura, altura, limiar):
|
||||
"""
|
||||
Detecta anomalias no padrão do perfil do solo.
|
||||
Agrupa setores que estão fora do padrão (acima ou abaixo da linha) em bounding boxes únicas.
|
||||
"""
|
||||
estados = [] # 'normal', 'lombada', 'erosao'
|
||||
for z in profundidades:
|
||||
if z == 0:
|
||||
estados.append('normal')
|
||||
continue
|
||||
|
||||
desvio = z - referencia
|
||||
if abs(desvio) > limiar:
|
||||
if desvio < 0:
|
||||
estados.append('lombada')
|
||||
else:
|
||||
estados.append('erosao')
|
||||
else:
|
||||
estados.append('normal')
|
||||
|
||||
lista_bboxes = []
|
||||
|
||||
# Agrupar por estado (lombada ou erosao)
|
||||
i = 0
|
||||
while i < len(estados):
|
||||
estado_atual = estados[i]
|
||||
if estado_atual == 'normal':
|
||||
i += 1
|
||||
continue
|
||||
|
||||
inicio = i
|
||||
while i < len(estados) and estados[i] == estado_atual:
|
||||
i += 1
|
||||
fim = i - 1
|
||||
|
||||
# Calcular bbox do grupo
|
||||
x = inicio * largura_setor
|
||||
w = (fim - inicio + 1) * largura_setor
|
||||
y = y_inicio
|
||||
h = linhas_solo
|
||||
|
||||
z_media = np.mean([profundidades[j] for j in range(inicio, fim + 1) if profundidades[j] > 0]) / 1000.0
|
||||
|
||||
largura_real = z_media * 2 * np.tan(calib["fov_h"] / 2) * (w / largura)
|
||||
altura_real = z_media * 2 * np.tan(calib["fov_v"] / 2) * (h / altura)
|
||||
|
||||
# Pode calcular largura e altura em metros (opcional) — aqui deixei None, mas pode fazer igual fizemos na anomalia
|
||||
lista_bboxes.append({
|
||||
"tipo": tipo,
|
||||
centro_x = (x + w / 2) / largura * 100
|
||||
centro_y = (y + h / 2) / altura * 100
|
||||
|
||||
bbox = {
|
||||
"tipo": estado_atual,
|
||||
"bbox": (int(x), int(y), int(w), int(h)),
|
||||
"centro_percentual": (float(centro_x), float(centro_y)),
|
||||
"distancia_m": round(z_media, 3),
|
||||
"largura_m": round(largura_real, 3),
|
||||
"altura_m": round(altura_real, 3),
|
||||
"profundidades": (round(z1, 1), round(z2, 1)),
|
||||
"variacao": round(variacao, 1)
|
||||
})
|
||||
"profundidades": (round(referencia, 1), round(z_media * 1000, 1)),
|
||||
"variacao": round(abs(z_media * 1000 - referencia), 1)
|
||||
}
|
||||
|
||||
# Filtra as detecções relevantes
|
||||
from visual_worker.utils import filtrar_deteccoes
|
||||
lista_bboxes_filtrada = filtrar_deteccoes(lista_bboxes, distancia_max, tamanho_min)
|
||||
lista_bboxes.append(bbox)
|
||||
|
||||
return lista_bboxes
|
||||
|
||||
def detectar_anomalias_com_calibragem(profundidades, grade_referencia, largura_setor, y_inicio, linhas_solo, calib, largura, altura, limiar):
|
||||
"""
|
||||
Detecta anomalias comparando diretamente com a grade de referência da calibragem.
|
||||
Só utiliza setores válidos (profundidade e referência diferentes de zero).
|
||||
"""
|
||||
estados = []
|
||||
setores_validos = []
|
||||
|
||||
# Definir estados para cada setor
|
||||
for i, (z, ref) in enumerate(zip(profundidades, grade_referencia)):
|
||||
if z == 0 or ref == 0:
|
||||
estados.append('normal')
|
||||
continue
|
||||
|
||||
desvio = z - ref
|
||||
if abs(desvio) > limiar:
|
||||
estado = 'lombada' if desvio < 0 else 'erosao'
|
||||
estados.append(estado)
|
||||
setores_validos.append(i) # 👉 Só adiciona setores válidos e fora do padrão
|
||||
else:
|
||||
estados.append('normal')
|
||||
|
||||
lista_bboxes = []
|
||||
|
||||
if not setores_validos:
|
||||
return lista_bboxes # 👉 Nenhuma anomalia detectada
|
||||
|
||||
# Agrupar setores consecutivos que estão no mesmo estado
|
||||
limiar_continuidade = 200 # mm
|
||||
|
||||
i = 0
|
||||
while i < len(setores_validos):
|
||||
idx_inicio = setores_validos[i]
|
||||
estado_atual = estados[idx_inicio]
|
||||
profundidade_inicial = profundidades[idx_inicio]
|
||||
|
||||
j = i
|
||||
while j + 1 < len(setores_validos):
|
||||
idx_atual = setores_validos[j]
|
||||
idx_proximo = setores_validos[j + 1]
|
||||
|
||||
profundidade_atual = profundidades[idx_atual]
|
||||
profundidade_proximo = profundidades[idx_proximo]
|
||||
|
||||
mesma_anomalia = estados[idx_proximo] == estado_atual
|
||||
continuidade_ok = abs(profundidade_proximo - profundidade_atual) <= limiar_continuidade
|
||||
|
||||
if idx_proximo == idx_atual + 1 and mesma_anomalia and continuidade_ok:
|
||||
j += 1
|
||||
else:
|
||||
break
|
||||
|
||||
idx_fim = setores_validos[j]
|
||||
|
||||
# Calcular bbox do grupo
|
||||
x = idx_inicio * largura_setor
|
||||
w = (idx_fim - idx_inicio + 1) * largura_setor
|
||||
y = y_inicio
|
||||
h = linhas_solo
|
||||
|
||||
# Calcular profundidade média do grupo
|
||||
valores_validos = [profundidades[k] for k in range(idx_inicio, idx_fim + 1) if profundidades[k] > 0]
|
||||
if len(valores_validos) == 0:
|
||||
i = j + 1
|
||||
continue # Ignora se não houver dados válidos
|
||||
|
||||
z_media = np.mean(valores_validos) / 1000.0 # mm → metros
|
||||
|
||||
# Calcular tamanho real
|
||||
largura_real = z_media * 2 * np.tan(calib["fov_h"] / 2) * (w / largura)
|
||||
altura_real = z_media * 2 * np.tan(calib["fov_v"] / 2) * (h / altura)
|
||||
|
||||
# Calcular centro percentual
|
||||
centro_x = (x + w / 2) / largura * 100
|
||||
centro_y = (y + h / 2) / altura * 100
|
||||
|
||||
# Montar bbox
|
||||
bbox = {
|
||||
"tipo": estado_atual,
|
||||
"bbox": (int(x), int(y), int(w), int(h)),
|
||||
"centro_percentual": (float(centro_x), float(centro_y)),
|
||||
"distancia_m": round(z_media, 3),
|
||||
"largura_m": round(largura_real, 3),
|
||||
"altura_m": round(altura_real, 3),
|
||||
"referencia_setor": np.mean([grade_referencia[k] for k in range(idx_inicio, idx_fim + 1) if grade_referencia[k] > 0]),
|
||||
"profundidade_setor": round(z_media * 1000, 1),
|
||||
"variacao": round(abs(z_media * 1000 - np.mean([grade_referencia[k] for k in range(idx_inicio, idx_fim + 1) if grade_referencia[k] > 0])), 1)
|
||||
}
|
||||
|
||||
lista_bboxes.append(bbox)
|
||||
|
||||
i = j + 1 # Avança para o próximo grupo
|
||||
|
||||
return lista_bboxes
|
||||
|
||||
|
||||
|
||||
|
||||
def analisar_solo(depth_frame, setores_referencia, percentual_altura, limiar_variacao_mm, distancia_max, largura_min, altura_min, calib):
|
||||
depth_solo, y_inicio, linhas_solo, altura, largura = obter_regiao_solo(depth_frame, percentual_altura)
|
||||
|
||||
profundidades, largura_setor = calcular_profundidades(depth_solo, calib["qtd_setores_solo"], largura, distancia_max * 1000.0)
|
||||
|
||||
bboxes_var = detectar_variacao_lateral(
|
||||
profundidades, largura_setor, y_inicio, linhas_solo,
|
||||
calib, largura, altura, limiar_variacao_mm
|
||||
)
|
||||
|
||||
bboxes_desvio = detectar_anomalias_com_calibragem(
|
||||
profundidades, setores_referencia, largura_setor, y_inicio, linhas_solo,
|
||||
calib, largura, altura, limiar_variacao_mm
|
||||
)
|
||||
|
||||
lista_bboxes = bboxes_var + bboxes_desvio
|
||||
|
||||
print(f"Total de bboxes antes do filtro: {len(lista_bboxes)}")
|
||||
|
||||
lista_bboxes_filtrada = filtrar_deteccoes(lista_bboxes, distancia_max, largura_min, altura_min)
|
||||
|
||||
# 🔥 Retorno estruturado
|
||||
resultado = {
|
||||
"perfil": [float(v) for v in profundidades.tolist()],
|
||||
"bbox_list": lista_bboxes_filtrada,
|
||||
"y_inicio": int(y_inicio),
|
||||
"altura_regiao": int(linhas_solo),
|
||||
"n_setores": int(n_setores)
|
||||
"n_setores": int(calib["qtd_setores_solo"])
|
||||
}
|
||||
|
||||
return resultado
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
class GestorDeteccoes:
|
||||
def __init__(self, frames_minimos=3, tolerancia_px=5, tolerancia_m=0.3):
|
||||
self.frames_minimos = frames_minimos
|
||||
self.tolerancia_px = tolerancia_px
|
||||
self.tolerancia_m = tolerancia_m
|
||||
self.deteccoes_ativas = {}
|
||||
|
||||
def _gerar_id(self, obj):
|
||||
centro = obj["centro_percentual"]
|
||||
distancia = obj["distancia_m"]
|
||||
tipo = obj["tipo"]
|
||||
chave = (
|
||||
round(centro[0] / self.tolerancia_px),
|
||||
round(distancia / self.tolerancia_m),
|
||||
tipo
|
||||
)
|
||||
return chave
|
||||
|
||||
def atualizar(self, lista_bboxes):
|
||||
novas_deteccoes = set()
|
||||
|
||||
for obj in lista_bboxes:
|
||||
chave = self._gerar_id(obj)
|
||||
|
||||
novas_deteccoes.add(chave)
|
||||
|
||||
if chave in self.deteccoes_ativas:
|
||||
if self.deteccoes_ativas[chave]["contador"] <= self.frames_minimos:
|
||||
self.deteccoes_ativas[chave]["contador"] += 1
|
||||
self.deteccoes_ativas[chave]["dados"] = obj
|
||||
else:
|
||||
self.deteccoes_ativas[chave] = {
|
||||
"contador": 1,
|
||||
"dados": obj
|
||||
}
|
||||
|
||||
# Zera ou remove quem não apareceu nesse frame
|
||||
chaves_atuais = set(self.deteccoes_ativas.keys())
|
||||
|
||||
for chave in chaves_atuais - novas_deteccoes:
|
||||
self.deteccoes_ativas[chave]["contador"] -= 1
|
||||
if self.deteccoes_ativas[chave]["contador"] <= 0:
|
||||
del self.deteccoes_ativas[chave]
|
||||
|
||||
# Retorna só as que passaram do filtro de persistência
|
||||
return [
|
||||
dado["dados"]
|
||||
for dado in self.deteccoes_ativas.values()
|
||||
if dado["contador"] >= self.frames_minimos
|
||||
]
|
||||
|
|
@ -2,15 +2,17 @@ import numpy as np
|
|||
import time
|
||||
|
||||
from visual_worker.enums import TipoAnalise
|
||||
from visual_worker.filtros import filtrar_depth_morfologia
|
||||
from visual_worker.utils import aplicar_mascara_margens, calcular_threshold_anomalias
|
||||
|
||||
# Variáveis globais
|
||||
depth_referencia = None
|
||||
setores_referencia = None
|
||||
calibrado = False
|
||||
|
||||
# Calibração
|
||||
def calibrar(obter_depth_frame, n_frames=50):
|
||||
global depth_referencia, calibrado
|
||||
def calibrar(obter_depth_frame, camera_calib, n_frames=50):
|
||||
global depth_referencia, setores_referencia, calibrado
|
||||
|
||||
calibrado = False
|
||||
|
||||
|
|
@ -33,13 +35,40 @@ def calibrar(obter_depth_frame, n_frames=50):
|
|||
return False
|
||||
|
||||
depth_referencia = np.median(np.stack(lista_frames), axis=0)
|
||||
salvar_grade_referencia(depth_referencia, camera_calib)
|
||||
calibrado = True
|
||||
print("✅ Calibração concluída com sucesso!")
|
||||
print(f"✅ Calibração concluída com sucesso! Definido {len(setores_referencia)} setores no solo")
|
||||
return True
|
||||
|
||||
def salvar_grade_referencia(depth_frame, calib):
|
||||
global setores_referencia
|
||||
|
||||
def analisar(analise, obter_depth_frame, camera_calib, velocidade_ms, distancia_max, tamanho_min):
|
||||
global depth_referencia, calibrado
|
||||
n_setores = calib["qtd_setores_solo"]
|
||||
distancia_max = calib["distancia_maxima"]
|
||||
|
||||
# Divide em setores igual fazemos na análise
|
||||
altura, largura = depth_frame.shape
|
||||
largura_setor = largura // n_setores
|
||||
setores_referencia = []
|
||||
|
||||
for i in range(n_setores):
|
||||
x_inicio = i * largura_setor
|
||||
x_fim = (i + 1) * largura_setor if (i < n_setores - 1) else largura
|
||||
|
||||
setor = depth_frame[:, x_inicio:x_fim]
|
||||
from visual_worker.utils import filtrar_depth_valid
|
||||
setor_valido = filtrar_depth_valid(setor, distancia_max)
|
||||
|
||||
if len(setor_valido) > 0:
|
||||
z = np.median(setor_valido) # ou média se preferir
|
||||
else:
|
||||
z = 0
|
||||
setores_referencia.append(z)
|
||||
|
||||
return setores_referencia
|
||||
|
||||
def analisar(analise, obter_depth_frame, camera_calib, velocidade_ms, distancia_max, largura_min, altura_min):
|
||||
global depth_referencia, setores_referencia, calibrado
|
||||
|
||||
resultado = {
|
||||
"timestamp": None,
|
||||
|
|
@ -54,15 +83,16 @@ def analisar(analise, obter_depth_frame, camera_calib, velocidade_ms, distancia_
|
|||
frame, timestamp = obter_depth_frame()
|
||||
resultado["timestamp"] = timestamp
|
||||
|
||||
depth_frame = aplicar_mascara_margens(frame)
|
||||
#depth_frame = aplicar_mascara_margens(frame)
|
||||
frame = filtrar_depth_morfologia(frame)
|
||||
limiar = calcular_threshold_anomalias(velocidade_ms)
|
||||
|
||||
if analise == TipoAnalise.ANOMALIA:
|
||||
from visual_worker.processamento.analise_anomalias import detectar_anomalias
|
||||
resultado["anomalias"] = detectar_anomalias(frame, depth_referencia, limiar, distancia_max, tamanho_min, camera_calib)
|
||||
resultado["anomalias"] = detectar_anomalias(frame, depth_referencia, limiar, distancia_max, largura_min, altura_min, camera_calib)
|
||||
elif analise == TipoAnalise.SOLO:
|
||||
from visual_worker.processamento.analise_solo import analisar_solo
|
||||
resultado["solo"] = analisar_solo(frame, 20, 50, limiar, distancia_max, tamanho_min, camera_calib)
|
||||
resultado["solo"] = analisar_solo(frame, setores_referencia, 20, limiar, distancia_max, largura_min, 0, camera_calib)
|
||||
|
||||
return resultado
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ def gerar_heatmap(depth_frame, dist_max_mm=3000.0):
|
|||
heatmap[~mask_valid] = [0, 0, 0]
|
||||
return heatmap
|
||||
|
||||
def filtrar_deteccoes(lista, distancia_maxima=2.0, tamanho_minimo=0.25):
|
||||
def filtrar_deteccoes(lista, distancia_maxima=2.0, largura_minima=0.0, altura_minima=0.0):
|
||||
"""
|
||||
Filtra a lista de detecções baseado em:
|
||||
- Distância máxima (em metros).
|
||||
|
|
@ -34,7 +34,13 @@ def filtrar_deteccoes(lista, distancia_maxima=2.0, tamanho_minimo=0.25):
|
|||
if obj["distancia_m"] > distancia_maxima:
|
||||
continue
|
||||
|
||||
if obj["largura_m"] < tamanho_minimo and obj["altura_m"] < tamanho_minimo:
|
||||
if largura_minima > 0 and altura_minima == 0 and obj["largura_m"] < largura_minima:
|
||||
continue
|
||||
|
||||
if largura_minima == 0 and altura_minima > 0 and obj["altura_m"] < altura_minima:
|
||||
continue
|
||||
|
||||
if obj["largura_m"] < largura_minima and obj["altura_m"] < altura_minima:
|
||||
continue
|
||||
|
||||
lista_filtrada.append(obj)
|
||||
|
|
|
|||
Loading…
Reference in New Issue