diff --git a/AgroBase/.vs/AgroBase/FileContentIndex/08bd4770-dc75-4f49-ac52-f998e5cff6f9.vsidx b/AgroBase/.vs/AgroBase/FileContentIndex/08bd4770-dc75-4f49-ac52-f998e5cff6f9.vsidx new file mode 100644 index 000000000..0f4da8c6c Binary files /dev/null and b/AgroBase/.vs/AgroBase/FileContentIndex/08bd4770-dc75-4f49-ac52-f998e5cff6f9.vsidx differ diff --git a/AgroBase/.vs/AgroBase/FileContentIndex/306e5ebe-4c11-4859-bdb3-7ab6e1be4a57.vsidx b/AgroBase/.vs/AgroBase/FileContentIndex/306e5ebe-4c11-4859-bdb3-7ab6e1be4a57.vsidx deleted file mode 100644 index d6e14b3f4..000000000 Binary files a/AgroBase/.vs/AgroBase/FileContentIndex/306e5ebe-4c11-4859-bdb3-7ab6e1be4a57.vsidx and /dev/null differ diff --git a/AgroBase/.vs/AgroBase/FileContentIndex/23296f30-29ae-48a6-aa25-c44041c646a8.vsidx b/AgroBase/.vs/AgroBase/FileContentIndex/4b702ad9-3c8e-4646-aedd-8efab73b3d5f.vsidx similarity index 96% rename from AgroBase/.vs/AgroBase/FileContentIndex/23296f30-29ae-48a6-aa25-c44041c646a8.vsidx rename to AgroBase/.vs/AgroBase/FileContentIndex/4b702ad9-3c8e-4646-aedd-8efab73b3d5f.vsidx index d1606cbd6..d83a5dc71 100644 Binary files a/AgroBase/.vs/AgroBase/FileContentIndex/23296f30-29ae-48a6-aa25-c44041c646a8.vsidx and b/AgroBase/.vs/AgroBase/FileContentIndex/4b702ad9-3c8e-4646-aedd-8efab73b3d5f.vsidx differ diff --git a/AgroBase/.vs/AgroBase/FileContentIndex/56036b22-3c97-46a1-b577-ca7fbc6025f8.vsidx b/AgroBase/.vs/AgroBase/FileContentIndex/56036b22-3c97-46a1-b577-ca7fbc6025f8.vsidx new file mode 100644 index 000000000..be7599fbe Binary files /dev/null and b/AgroBase/.vs/AgroBase/FileContentIndex/56036b22-3c97-46a1-b577-ca7fbc6025f8.vsidx differ diff --git a/AgroBase/.vs/AgroBase/FileContentIndex/b2b64049-8e44-4fe4-9531-6446f4086864.vsidx b/AgroBase/.vs/AgroBase/FileContentIndex/b2b64049-8e44-4fe4-9531-6446f4086864.vsidx deleted file mode 100644 index 89197f170..000000000 Binary files a/AgroBase/.vs/AgroBase/FileContentIndex/b2b64049-8e44-4fe4-9531-6446f4086864.vsidx and /dev/null differ diff --git a/AgroBase/.vs/AgroBase/FileContentIndex/57495774-0927-42cd-aef0-dbe63597ddaa.vsidx b/AgroBase/.vs/AgroBase/FileContentIndex/e413670a-c708-447b-8f04-20329f6176fa.vsidx similarity index 99% rename from AgroBase/.vs/AgroBase/FileContentIndex/57495774-0927-42cd-aef0-dbe63597ddaa.vsidx rename to AgroBase/.vs/AgroBase/FileContentIndex/e413670a-c708-447b-8f04-20329f6176fa.vsidx index 748c83cb7..727df87cd 100644 Binary files a/AgroBase/.vs/AgroBase/FileContentIndex/57495774-0927-42cd-aef0-dbe63597ddaa.vsidx and b/AgroBase/.vs/AgroBase/FileContentIndex/e413670a-c708-447b-8f04-20329f6176fa.vsidx differ diff --git a/AgroBase/.vs/AgroBase/v17/.suo b/AgroBase/.vs/AgroBase/v17/.suo index df3fa1df0..777912269 100644 Binary files a/AgroBase/.vs/AgroBase/v17/.suo and b/AgroBase/.vs/AgroBase/v17/.suo differ diff --git a/AgroBase/AgroBase/Forms/frmOperarioSonar.Designer.cs b/AgroBase/AgroBase/Forms/frmOperarioSonar.Designer.cs index 445792df1..5c8dca8b6 100644 --- a/AgroBase/AgroBase/Forms/frmOperarioSonar.Designer.cs +++ b/AgroBase/AgroBase/Forms/frmOperarioSonar.Designer.cs @@ -28,9 +28,9 @@ /// 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; } } \ No newline at end of file diff --git a/AgroBase/AgroBase/Forms/frmOperarioSonar.cs b/AgroBase/AgroBase/Forms/frmOperarioSonar.cs index b1ac83670..7fe04fae9 100644 --- a/AgroBase/AgroBase/Forms/frmOperarioSonar.cs +++ b/AgroBase/AgroBase/Forms/frmOperarioSonar.cs @@ -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(); } diff --git a/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs b/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs index e53127a0f..355c5e491 100644 --- a/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs +++ b/AgroBase/AgroBase/Models/Operadores/VisualWorkerModel.cs @@ -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; diff --git a/AgroBase/AgroBase/Services/Operadores/VisualWorkerService.cs b/AgroBase/AgroBase/Services/Operadores/VisualWorkerService.cs index f702ab15c..b587b8691 100644 --- a/AgroBase/AgroBase/Services/Operadores/VisualWorkerService.cs +++ b/AgroBase/AgroBase/Services/Operadores/VisualWorkerService.cs @@ -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; } } diff --git a/AgroBase/AgroBase/bin/x64/Debug/AgroBase.exe b/AgroBase/AgroBase/bin/x64/Debug/AgroBase.exe index d14e5fa2f..2c6776c32 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/AgroBase.exe and b/AgroBase/AgroBase/bin/x64/Debug/AgroBase.exe differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/AgroBase.pdb b/AgroBase/AgroBase/bin/x64/Debug/AgroBase.pdb index fbb475097..580eef4ac 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/AgroBase.pdb and b/AgroBase/AgroBase/bin/x64/Debug/AgroBase.pdb differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/camera_manager.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/camera_manager.py index 02d21f2c3..260d68d17 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/camera_manager.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/camera_manager.py @@ -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 } + + diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/filtros.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/filtros.py index e69de29bb..5be7b268c 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/filtros.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/filtros.py @@ -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 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/main.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/main.py index d8d85fa8d..5598dcc19 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/main.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/main.py @@ -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 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/analise_anomalias.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/analise_anomalias.py index c70d84ccb..0f6e9d830 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/analise_anomalias.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/analise_anomalias.py @@ -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 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/analise_solo.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/analise_solo.py index 14ea34c64..0ec8b0bf6 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/analise_solo.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/analise_solo.py @@ -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 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/gestor_deteccoes.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/gestor_deteccoes.py new file mode 100644 index 000000000..a7aa47014 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/gestor_deteccoes.py @@ -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 + ] \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/percepcao3d.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/percepcao3d.py index af9063496..138173c3f 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/percepcao3d.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/processamento/percepcao3d.py @@ -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 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/utils.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/utils.py index a7496d728..2ad4fd66c 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/utils.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/visual_worker/utils.py @@ -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)