otimizacoes no modelo de IA para ervas
This commit is contained in:
parent
56b268a804
commit
2266ddc84d
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -182,7 +182,7 @@ namespace AgroBase.Forms.Operacoes
|
|||
ReqFrameCam = true;
|
||||
try
|
||||
{
|
||||
var frame = await WeedWorkerService.GetCameraFrame(CameraFrameType.Rgb);
|
||||
var frame = await WeedWorkerService.GetCameraFrame(CameraFrameType.Debug);
|
||||
Panel pnl = FuncoesGlobais.FindControlRecursive<Panel>(flwCamerasSolo, "pnlCamSolo_" + Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo[0].Name);
|
||||
AtualizarImagemPainel(pnl, frame?.image());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3518,10 +3518,12 @@
|
|||
//
|
||||
// flwCameras
|
||||
//
|
||||
this.flwCameras.AutoScroll = true;
|
||||
this.flwCameras.Location = new System.Drawing.Point(4, 18);
|
||||
this.flwCameras.Name = "flwCameras";
|
||||
this.flwCameras.Size = new System.Drawing.Size(489, 170);
|
||||
this.flwCameras.Size = new System.Drawing.Size(489, 180);
|
||||
this.flwCameras.TabIndex = 97;
|
||||
this.flwCameras.WrapContents = false;
|
||||
//
|
||||
// lblATU_LatenciaLoop
|
||||
//
|
||||
|
|
@ -3559,20 +3561,20 @@
|
|||
this.chartAtuador.ChartAreas.Add(chartArea4);
|
||||
legend4.Name = "Legend1";
|
||||
this.chartAtuador.Legends.Add(legend4);
|
||||
this.chartAtuador.Location = new System.Drawing.Point(4, 276);
|
||||
this.chartAtuador.Location = new System.Drawing.Point(4, 286);
|
||||
this.chartAtuador.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.chartAtuador.Name = "chartAtuador";
|
||||
series4.ChartArea = "ChartArea1";
|
||||
series4.Legend = "Legend1";
|
||||
series4.Name = "Series1";
|
||||
this.chartAtuador.Series.Add(series4);
|
||||
this.chartAtuador.Size = new System.Drawing.Size(489, 241);
|
||||
this.chartAtuador.Size = new System.Drawing.Size(489, 231);
|
||||
this.chartAtuador.TabIndex = 95;
|
||||
this.chartAtuador.Text = "chart1";
|
||||
//
|
||||
// pnlAtuadores
|
||||
//
|
||||
this.pnlAtuadores.Location = new System.Drawing.Point(4, 193);
|
||||
this.pnlAtuadores.Location = new System.Drawing.Point(4, 203);
|
||||
this.pnlAtuadores.Margin = new System.Windows.Forms.Padding(2);
|
||||
this.pnlAtuadores.Name = "pnlAtuadores";
|
||||
this.pnlAtuadores.Size = new System.Drawing.Size(489, 79);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ namespace AgroBase.Forms.Operacoes
|
|||
List<List<CameraWorkerItemModel>> LogsCam_Solo = new List<List<CameraWorkerItemModel>>();
|
||||
List<string> pathImagensSolo = new List<string>();
|
||||
List<PictureBox> picsCamSolo = new List<PictureBox>();
|
||||
List<PictureBox> picsCamSoloSeg = new List<PictureBox>();
|
||||
List<PictureBox> picsCamSoloOverlay = new List<PictureBox>();
|
||||
List<CameraWorkerItemModel> LogsCam_Caminho = new List<CameraWorkerItemModel>();
|
||||
string pathImagensCaminho = "";
|
||||
|
||||
|
|
@ -131,6 +133,8 @@ namespace AgroBase.Forms.Operacoes
|
|||
|
||||
pathImagensSolo = new List<string>();
|
||||
picsCamSolo = new List<PictureBox>();
|
||||
picsCamSoloSeg = new List<PictureBox>();
|
||||
picsCamSoloOverlay = new List<PictureBox>();
|
||||
flwCameras.Controls.Clear();
|
||||
foreach (string cam in data.cam_solo)
|
||||
{
|
||||
|
|
@ -144,8 +148,26 @@ namespace AgroBase.Forms.Operacoes
|
|||
Height = 166,
|
||||
SizeMode = PictureBoxSizeMode.Zoom
|
||||
};
|
||||
PictureBox picSeg = new PictureBox()
|
||||
{
|
||||
Name = "picCamSoloSeg_" + cam,
|
||||
Width = 250,
|
||||
Height = 166,
|
||||
SizeMode = PictureBoxSizeMode.Zoom
|
||||
};
|
||||
PictureBox picOverlay = new PictureBox()
|
||||
{
|
||||
Name = "picCamSoloOverlay_" + cam,
|
||||
Width = 250,
|
||||
Height = 166,
|
||||
SizeMode = PictureBoxSizeMode.Zoom
|
||||
};
|
||||
flwCameras.Controls.Add(pic);
|
||||
flwCameras.Controls.Add(picSeg);
|
||||
flwCameras.Controls.Add(picOverlay);
|
||||
picsCamSolo.Add(pic);
|
||||
picsCamSoloSeg.Add(picSeg);
|
||||
picsCamSoloOverlay.Add(picOverlay);
|
||||
}
|
||||
|
||||
Mapa = new MapasModel()
|
||||
|
|
@ -1117,6 +1139,10 @@ namespace AgroBase.Forms.Operacoes
|
|||
{
|
||||
picsCamSolo[i].Image = bitmapSolo;
|
||||
}
|
||||
Bitmap bitmapSoloSeg = CarregarImagemCamera(pathImagensSolo[i], "_segmentacao");
|
||||
picsCamSoloSeg[i].Image = bitmapSoloSeg;
|
||||
Bitmap bitmapSoloOverlay = FuncoesGlobais.FazerOverlay(bitmapSolo, bitmapSoloSeg);
|
||||
picsCamSoloOverlay[i].Image = bitmapSoloOverlay;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -766,6 +766,61 @@ namespace AgroBase.Models
|
|||
}
|
||||
}
|
||||
|
||||
public static Bitmap FazerOverlay(Bitmap rgb, Bitmap segmentada, float alpha = 0.35f, bool useNearest = true)
|
||||
{
|
||||
// 1) Garantir mesma resolução
|
||||
Bitmap segSameSize = segmentada;
|
||||
if (segmentada.Width != rgb.Width || segmentada.Height != rgb.Height)
|
||||
{
|
||||
segSameSize = new Bitmap(rgb.Width, rgb.Height, PixelFormat.Format24bppRgb);
|
||||
using (var g = Graphics.FromImage(segSameSize))
|
||||
{
|
||||
g.InterpolationMode = useNearest ? InterpolationMode.NearestNeighbor : InterpolationMode.HighQualityBilinear;
|
||||
g.PixelOffsetMode = PixelOffsetMode.Half;
|
||||
g.DrawImage(segmentada, new Rectangle(0, 0, rgb.Width, rgb.Height));
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Compor overlay (rgb + alpha*segmentada)
|
||||
var output = new Bitmap(rgb.Width, rgb.Height, PixelFormat.Format24bppRgb);
|
||||
using (var g = Graphics.FromImage(output))
|
||||
using (var ia = new ImageAttributes())
|
||||
{
|
||||
// fundo (RGB)
|
||||
g.DrawImage(rgb, 0, 0, rgb.Width, rgb.Height);
|
||||
|
||||
// matriz de cor com alpha global
|
||||
var cm = new ColorMatrix
|
||||
{
|
||||
Matrix00 = 1f,
|
||||
Matrix11 = 1f,
|
||||
Matrix22 = 1f, // R,G,B inalterados
|
||||
Matrix33 = alpha, // A (transparência da segmentação)
|
||||
Matrix44 = 1f
|
||||
};
|
||||
ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
|
||||
|
||||
// overlay = 0.65*rgb + 0.35*seg (se quiser pesar o fundo, desenhe rgb antes, como já fizemos)
|
||||
g.CompositingMode = CompositingMode.SourceOver;
|
||||
g.CompositingQuality = CompositingQuality.HighSpeed;
|
||||
g.InterpolationMode = useNearest ? InterpolationMode.NearestNeighbor : InterpolationMode.HighQualityBilinear;
|
||||
g.PixelOffsetMode = PixelOffsetMode.Half;
|
||||
|
||||
g.DrawImage(
|
||||
segSameSize,
|
||||
new Rectangle(0, 0, rgb.Width, rgb.Height),
|
||||
0, 0, segSameSize.Width, segSameSize.Height,
|
||||
GraphicsUnit.Pixel,
|
||||
ia
|
||||
);
|
||||
}
|
||||
|
||||
if (!ReferenceEquals(segSameSize, segmentada))
|
||||
segSameSize.Dispose();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static string ConverterSegundosParaHHmmss(int totalSeconds)
|
||||
{
|
||||
TimeSpan timeSpan = TimeSpan.FromSeconds(totalSeconds);
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -1,12 +1,5 @@
|
|||
{
|
||||
"epochs": [ {
|
||||
"calculation_time": "13396617367014463",
|
||||
"config_version": 0,
|
||||
"model_version": "0",
|
||||
"padded_top_topics_start_index": 0,
|
||||
"taxonomy_version": 0,
|
||||
"top_topics_and_observing_domains": [ ]
|
||||
}, {
|
||||
"calculation_time": "13397225327956881",
|
||||
"config_version": 0,
|
||||
"model_version": "0",
|
||||
|
|
@ -27,7 +20,14 @@
|
|||
"padded_top_topics_start_index": 0,
|
||||
"taxonomy_version": 0,
|
||||
"top_topics_and_observing_domains": [ ]
|
||||
}, {
|
||||
"calculation_time": "13399153521058315",
|
||||
"config_version": 0,
|
||||
"model_version": "0",
|
||||
"padded_top_topics_start_index": 0,
|
||||
"taxonomy_version": 0,
|
||||
"top_topics_and_observing_domains": [ ]
|
||||
} ],
|
||||
"hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642",
|
||||
"next_scheduled_calculation_time": "13399060328666305"
|
||||
"next_scheduled_calculation_time": "13399758321058597"
|
||||
}
|
||||
|
|
|
|||
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.
Binary file not shown.
|
|
@ -1,3 +1,3 @@
|
|||
2025/08/06-10:48:40.356 1270 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
|
||||
2025/08/06-10:48:40.362 1270 Recovering log #3
|
||||
2025/08/06-10:48:40.365 1270 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log
|
||||
2025/08/08-17:00:08.334 7670 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
|
||||
2025/08/08-17:00:08.341 7670 Recovering log #3
|
||||
2025/08/08-17:00:08.345 7670 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
2025/08/06-10:45:42.987 5bbc Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
|
||||
2025/08/06-10:45:42.993 5bbc Recovering log #3
|
||||
2025/08/06-10:45:42.995 5bbc Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log
|
||||
2025/08/08-16:53:28.084 55f8 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
|
||||
2025/08/08-16:53:28.091 55f8 Recovering log #3
|
||||
2025/08/08-16:53:28.094 55f8 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"net":{"http_server_properties":{"servers":[{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13399048123791313","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":33724},"server":"https://tile.openstreetmap.org","supports_spdy":true}],"supports_quic":{"address":"192.168.26.32","used_quic":true},"version":5},"network_qualities":{"CAASABiAgICA+P////8B":"4G","CAESABiAgICA+P////8B":"4G","CAISABiAgICA+P////8B":"4G","CAYSABiAgICA+P////8B":"Offline"}}}
|
||||
{"net":{"http_server_properties":{"servers":[{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13399243208733068","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":10995},"server":"https://tile.openstreetmap.org","supports_spdy":true}],"supports_quic":{"address":"2804:d78:627:be00:194b:9f:4c67:e845","used_quic":true},"version":5},"network_qualities":{"CAASABiAgICA+P////8B":"4G","CAESABiAgICA+P////8B":"4G","CAISABiAgICA+P////8B":"4G","CAYSABiAgICA+P////8B":"Offline"}}}
|
||||
|
|
@ -1 +1 @@
|
|||
{"sts":[{"expiry":1786019258.180145,"host":"bWGAftl61rqoc0YzqPncsLvQQh/iC2Bdp3ejUeGC83w=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1754483258.180153}],"version":2}
|
||||
{"sts":[{"expiry":1786217642.613677,"host":"bWGAftl61rqoc0YzqPncsLvQQh/iC2Bdp3ejUeGC83w=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1754681642.613684}],"version":2}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,3 @@
|
|||
2025/08/06-14:23:57.026 1270 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
|
||||
2025/08/06-14:23:57.027 1270 Recovering log #3
|
||||
2025/08/06-14:23:57.030 1270 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
||||
2025/08/08-17:00:46.661 7670 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
|
||||
2025/08/08-17:00:46.662 7670 Recovering log #3
|
||||
2025/08/08-17:00:46.666 7670 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
2025/08/06-10:48:33.159 5bbc Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
|
||||
2025/08/06-10:48:33.160 5bbc Recovering log #3
|
||||
2025/08/06-10:48:33.163 5bbc Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
||||
2025/08/08-16:58:34.324 55f8 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
|
||||
2025/08/08-16:58:34.326 55f8 Recovering log #3
|
||||
2025/08/08-16:58:34.329 55f8 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
2025/08/06-10:48:40.290 5f3c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
|
||||
2025/08/06-10:48:40.291 5f3c Recovering log #7
|
||||
2025/08/06-10:48:40.292 5f3c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log
|
||||
2025/08/08-17:00:08.246 a298 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
|
||||
2025/08/08-17:00:08.248 a298 Recovering log #7
|
||||
2025/08/08-17:00:08.249 a298 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
2025/08/06-10:45:42.911 3124 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
|
||||
2025/08/06-10:45:42.912 3124 Recovering log #7
|
||||
2025/08/06-10:45:42.912 3124 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log
|
||||
2025/08/08-16:53:28.002 4c34 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
|
||||
2025/08/08-16:53:28.003 4c34 Recovering log #7
|
||||
2025/08/08-16:53:28.004 4c34 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
{"user_experience_metrics.stability.exited_cleanly":true,"variations_crash_streak":1}
|
||||
{"user_experience_metrics.stability.exited_cleanly":true,"variations_crash_streak":0}
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -1,221 +1 @@
|
|||
{
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"Id": "1",
|
||||
"Name": "CidadeJardimTerreno2",
|
||||
"Length": 14.558011415731592,
|
||||
"Dist1": 14.558011415731592,
|
||||
"Dist2": 0.0
|
||||
},
|
||||
"geometry": {
|
||||
"id": null,
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
-47.395205344,
|
||||
-22.172559531333334
|
||||
],
|
||||
[
|
||||
-47.395212610166666,
|
||||
-22.172614638833334
|
||||
],
|
||||
[
|
||||
-47.395219157,
|
||||
-22.172656417833334
|
||||
],
|
||||
[
|
||||
-47.395223544833335,
|
||||
-22.1726892105
|
||||
],
|
||||
[
|
||||
-47.39522414098443,
|
||||
-22.17269369161165
|
||||
],
|
||||
[
|
||||
-47.395225326538004,
|
||||
-22.172702654611555
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"Id": "2",
|
||||
"Name": "CidadeJardimTerreno2",
|
||||
"Length": 14.771318274761821,
|
||||
"Dist1": 14.558011415731592,
|
||||
"Dist2": 0.0
|
||||
},
|
||||
"geometry": {
|
||||
"id": null,
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
-47.39521090233333,
|
||||
-22.172708800833334
|
||||
],
|
||||
[
|
||||
-47.395206943666665,
|
||||
-22.172679811
|
||||
],
|
||||
[
|
||||
-47.395202420666664,
|
||||
-22.1726491015
|
||||
],
|
||||
[
|
||||
-47.395198865666664,
|
||||
-22.172615782833333
|
||||
],
|
||||
[
|
||||
-47.395193255833334,
|
||||
-22.172577132833332
|
||||
],
|
||||
[
|
||||
-47.395192610024395,
|
||||
-22.17257265769541
|
||||
],
|
||||
[
|
||||
-47.395191325698136,
|
||||
-22.172563706509337
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"Id": "3",
|
||||
"Name": "CidadeJardimTerreno2",
|
||||
"Length": 14.827915524386164,
|
||||
"Dist1": 14.558011415731592,
|
||||
"Dist2": 0.0
|
||||
},
|
||||
"geometry": {
|
||||
"id": null,
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
-47.3951762925,
|
||||
-22.172561603
|
||||
],
|
||||
[
|
||||
-47.395180824166665,
|
||||
-22.172591686166665
|
||||
],
|
||||
[
|
||||
-47.395185745333336,
|
||||
-22.172623080166666
|
||||
],
|
||||
[
|
||||
-47.395190433,
|
||||
-22.172656367166667
|
||||
],
|
||||
[
|
||||
-47.395195199,
|
||||
-22.172693641833334
|
||||
],
|
||||
[
|
||||
-47.39519576904436,
|
||||
-22.172698125891035
|
||||
],
|
||||
[
|
||||
-47.39519690267159,
|
||||
-22.172707094716973
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"Id": "4",
|
||||
"Name": "CidadeJardimTerreno2",
|
||||
"Length": 14.785159385903514,
|
||||
"Dist1": 14.558011415731592,
|
||||
"Dist2": 0.0
|
||||
},
|
||||
"geometry": {
|
||||
"id": null,
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
-47.39518293216667,
|
||||
-22.1727127985
|
||||
],
|
||||
[
|
||||
-47.39517819266667,
|
||||
-22.172680514833335
|
||||
],
|
||||
[
|
||||
-47.3951732595,
|
||||
-22.172646752833334
|
||||
],
|
||||
[
|
||||
-47.39516880516667,
|
||||
-22.1726149155
|
||||
],
|
||||
[
|
||||
-47.39516381233334,
|
||||
-22.172581167166665
|
||||
],
|
||||
[
|
||||
-47.39516315429932,
|
||||
-22.172576693573966
|
||||
],
|
||||
[
|
||||
-47.39516184565584,
|
||||
-22.172567745443878
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"Id": "5",
|
||||
"Name": "CidadeJardimTerreno2",
|
||||
"Length": 14.80117692191233,
|
||||
"Dist1": 14.558011415731592,
|
||||
"Dist2": 0.0
|
||||
},
|
||||
"geometry": {
|
||||
"id": null,
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
-47.395147317833334,
|
||||
-22.172566031
|
||||
],
|
||||
[
|
||||
-47.395151503166666,
|
||||
-22.172595452333333
|
||||
],
|
||||
[
|
||||
-47.3951561855,
|
||||
-22.172628011166665
|
||||
],
|
||||
[
|
||||
-47.39516159866667,
|
||||
-22.172663757333332
|
||||
],
|
||||
[
|
||||
-47.39516672716667,
|
||||
-22.172697770833334
|
||||
],
|
||||
[
|
||||
-47.395167397572706,
|
||||
-22.172702242832194
|
||||
],
|
||||
[
|
||||
-47.39516873082615,
|
||||
-22.17271118781009
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"Id":"1","Name":"08_08_2025_16_42_31_Manual","Length":0.0,"Dist1":0.0,"Dist2":0.0},"geometry":{"id":null,"type":"LineString","coordinates":[[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]}}]}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_71c541743f0c8faef84495db1742d718 {
|
||||
#map_7166a4145b3904abbc4abfe2859cddb9 {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
|
|
@ -54,14 +54,14 @@
|
|||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_71c541743f0c8faef84495db1742d718" ></div>
|
||||
<div class="folium-map" id="map_7166a4145b3904abbc4abfe2859cddb9" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_71c541743f0c8faef84495db1742d718 = L.map(
|
||||
"map_71c541743f0c8faef84495db1742d718",
|
||||
var map_7166a4145b3904abbc4abfe2859cddb9 = L.map(
|
||||
"map_7166a4145b3904abbc4abfe2859cddb9",
|
||||
{
|
||||
center: [0.0, 0.0],
|
||||
crs: L.CRS.EPSG3857,
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
|
||||
|
||||
|
||||
var tile_layer_44cd6f7e825210da22334b9c0a7d73f9 = L.tileLayer(
|
||||
var tile_layer_9627b4b85e8fb117b7e79c7fddf9872d = L.tileLayer(
|
||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
"minZoom": 0,
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
);
|
||||
|
||||
|
||||
tile_layer_44cd6f7e825210da22334b9c0a7d73f9.addTo(map_71c541743f0c8faef84495db1742d718);
|
||||
tile_layer_9627b4b85e8fb117b7e79c7fddf9872d.addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -116,7 +116,7 @@
|
|||
}
|
||||
trajeto_json_add({"features": []});
|
||||
|
||||
trajeto_json.addTo(map_71c541743f0c8faef84495db1742d718);
|
||||
trajeto_json.addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
||||
|
||||
function adicionarGeometria(novaGeometria) {
|
||||
trajeto_json.addData(novaGeometria);
|
||||
|
|
@ -179,9 +179,9 @@
|
|||
|
||||
var marcadorEquipamento = L.marker([0, 0], {
|
||||
icon: customIcon
|
||||
}).addTo(map_71c541743f0c8faef84495db1742d718);
|
||||
}).addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
||||
|
||||
var marcadorBase = L.marker([0, 0], {}).addTo(map_71c541743f0c8faef84495db1742d718);
|
||||
var marcadorBase = L.marker([0, 0], {}).addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
||||
var icon = L.AwesomeMarkers.icon(
|
||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||
);
|
||||
|
|
@ -246,7 +246,7 @@
|
|||
}
|
||||
|
||||
if (foco) {
|
||||
map_71c541743f0c8faef84495db1742d718.setView(novaPosicao, map_71c541743f0c8faef84495db1742d718.getZoom());
|
||||
map_7166a4145b3904abbc4abfe2859cddb9.setView(novaPosicao, map_7166a4145b3904abbc4abfe2859cddb9.getZoom());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -268,7 +268,7 @@
|
|||
marcadorDinamico.setRotationAngle(angulo);
|
||||
|
||||
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
|
||||
map_71c541743f0c8faef84495db1742d718.setView(novaPosicao, map_71c541743f0c8faef84495db1742d718.getZoom());*/
|
||||
map_7166a4145b3904abbc4abfe2859cddb9.setView(novaPosicao, map_7166a4145b3904abbc4abfe2859cddb9.getZoom());*/
|
||||
});
|
||||
|
||||
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_ca7dc8cbe647335c250a74caa3289064 {
|
||||
#map_f21522cbe2b071ceb21cf0d5c95f5a21 {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
|
|
@ -54,16 +54,16 @@
|
|||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_ca7dc8cbe647335c250a74caa3289064" ></div>
|
||||
<div class="folium-map" id="map_f21522cbe2b071ceb21cf0d5c95f5a21" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_ca7dc8cbe647335c250a74caa3289064 = L.map(
|
||||
"map_ca7dc8cbe647335c250a74caa3289064",
|
||||
var map_f21522cbe2b071ceb21cf0d5c95f5a21 = L.map(
|
||||
"map_f21522cbe2b071ceb21cf0d5c95f5a21",
|
||||
{
|
||||
center: [-22.172636164916668, -47.395186322185666],
|
||||
center: [0.0, 0.0],
|
||||
crs: L.CRS.EPSG3857,
|
||||
...{
|
||||
"zoom": 12,
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
|
||||
|
||||
|
||||
var tile_layer_79063dfeb1319c85aa667607a51dd28b = L.tileLayer(
|
||||
var tile_layer_1ca71298385dd222bb7746161b7dbce5 = L.tileLayer(
|
||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
{
|
||||
"minZoom": 0,
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
);
|
||||
|
||||
|
||||
tile_layer_79063dfeb1319c85aa667607a51dd28b.addTo(map_ca7dc8cbe647335c250a74caa3289064);
|
||||
tile_layer_1ca71298385dd222bb7746161b7dbce5.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
|
||||
|
||||
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
}*/
|
||||
});
|
||||
}
|
||||
function geo_json_e3dc8721488569c264b865754d696ffb_onEachFeature(feature, layer) {
|
||||
function geo_json_b0da161aca2ab03761c9445da3d471fe_onEachFeature(feature, layer) {
|
||||
|
||||
layer.on({
|
||||
|
||||
|
|
@ -148,23 +148,23 @@
|
|||
}*/
|
||||
});
|
||||
};
|
||||
var geo_json_e3dc8721488569c264b865754d696ffb = L.geoJson(null, {
|
||||
onEachFeature: geo_json_e3dc8721488569c264b865754d696ffb_onEachFeature,
|
||||
var geo_json_b0da161aca2ab03761c9445da3d471fe = L.geoJson(null, {
|
||||
onEachFeature: geo_json_b0da161aca2ab03761c9445da3d471fe_onEachFeature,
|
||||
|
||||
...{
|
||||
}
|
||||
});
|
||||
|
||||
function geo_json_e3dc8721488569c264b865754d696ffb_add (data) {
|
||||
geo_json_e3dc8721488569c264b865754d696ffb
|
||||
function geo_json_b0da161aca2ab03761c9445da3d471fe_add (data) {
|
||||
geo_json_b0da161aca2ab03761c9445da3d471fe
|
||||
.addData(data);
|
||||
}
|
||||
geo_json_e3dc8721488569c264b865754d696ffb_add({"features": [{"geometry": {"coordinates": [[-47.395205344, -22.172559531333334], [-47.395212610166666, -22.172614638833334], [-47.395219157, -22.172656417833334], [-47.395223544833335, -22.1726892105], [-47.39522414098443, -22.17269369161165], [-47.395225326538004, -22.172702654611555]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "1", "Length": 14.558011415731592, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.39521090233333, -22.172708800833334], [-47.395206943666665, -22.172679811], [-47.395202420666664, -22.1726491015], [-47.395198865666664, -22.172615782833333], [-47.395193255833334, -22.172577132833332], [-47.395192610024395, -22.17257265769541], [-47.395191325698136, -22.172563706509337]], "id": null, "type": "LineString"}, "id": 1, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "2", "Length": 14.771318274761821, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.3951762925, -22.172561603], [-47.395180824166665, -22.172591686166665], [-47.395185745333336, -22.172623080166666], [-47.395190433, -22.172656367166667], [-47.395195199, -22.172693641833334], [-47.39519576904436, -22.172698125891035], [-47.39519690267159, -22.172707094716973]], "id": null, "type": "LineString"}, "id": 2, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "3", "Length": 14.827915524386164, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.39518293216667, -22.1727127985], [-47.39517819266667, -22.172680514833335], [-47.3951732595, -22.172646752833334], [-47.39516880516667, -22.1726149155], [-47.39516381233334, -22.172581167166665], [-47.39516315429932, -22.172576693573966], [-47.39516184565584, -22.172567745443878]], "id": null, "type": "LineString"}, "id": 3, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "4", "Length": 14.785159385903514, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.395147317833334, -22.172566031], [-47.395151503166666, -22.172595452333333], [-47.3951561855, -22.172628011166665], [-47.39516159866667, -22.172663757333332], [-47.39516672716667, -22.172697770833334], [-47.395167397572706, -22.172702242832194], [-47.39516873082615, -22.17271118781009]], "id": null, "type": "LineString"}, "id": 4, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "5", "Length": 14.80117692191233, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}], "type": "FeatureCollection"});
|
||||
geo_json_e3dc8721488569c264b865754d696ffb.setStyle(function(feature) {return feature.properties.style;});
|
||||
geo_json_b0da161aca2ab03761c9445da3d471fe_add({"features": [{"geometry": {"coordinates": [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 0.0, "Dist2": 0.0, "Id": "1", "Length": 0.0, "Name": "08_08_2025_16_42_31_Manual"}, "type": "Feature"}], "type": "FeatureCollection"});
|
||||
geo_json_b0da161aca2ab03761c9445da3d471fe.setStyle(function(feature) {return feature.properties.style;});
|
||||
|
||||
|
||||
|
||||
geo_json_e3dc8721488569c264b865754d696ffb.addTo(map_ca7dc8cbe647335c250a74caa3289064);
|
||||
geo_json_b0da161aca2ab03761c9445da3d471fe.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -185,7 +185,7 @@
|
|||
}
|
||||
trajeto_json_add({"features": []});
|
||||
|
||||
trajeto_json.addTo(map_ca7dc8cbe647335c250a74caa3289064);
|
||||
trajeto_json.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
|
||||
function adicionarGeometria(novaGeometria) {
|
||||
trajeto_json.addData(novaGeometria);
|
||||
|
|
@ -243,7 +243,7 @@
|
|||
}
|
||||
trajeto_dinamico_json_add({"features": []});
|
||||
|
||||
trajeto_dinamico_json.addTo(map_ca7dc8cbe647335c250a74caa3289064);
|
||||
trajeto_dinamico_json.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
|
||||
function adicionarGeometriaDinamica(novaGeometria) {
|
||||
trajeto_dinamico_json.addData(novaGeometria);
|
||||
|
|
@ -296,9 +296,9 @@
|
|||
|
||||
var marcadorEquipamento = L.marker([0, 0], {
|
||||
icon: customIcon
|
||||
}).addTo(map_ca7dc8cbe647335c250a74caa3289064);
|
||||
}).addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
|
||||
var marcadorBase = L.marker([0, 0], {}).addTo(map_ca7dc8cbe647335c250a74caa3289064);
|
||||
var marcadorBase = L.marker([0, 0], {}).addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
var icon = L.AwesomeMarkers.icon(
|
||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||
);
|
||||
|
|
@ -380,7 +380,7 @@
|
|||
}
|
||||
|
||||
if (foco) {
|
||||
map_ca7dc8cbe647335c250a74caa3289064.setView(novaPosicao, map_ca7dc8cbe647335c250a74caa3289064.getZoom());
|
||||
map_f21522cbe2b071ceb21cf0d5c95f5a21.setView(novaPosicao, map_f21522cbe2b071ceb21cf0d5c95f5a21.getZoom());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -397,7 +397,7 @@
|
|||
function atualizarSelecaoRuas(selecionadas) {
|
||||
selecionadas = JSON.parse(selecionadas);
|
||||
RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : [];
|
||||
geo_json_e3dc8721488569c264b865754d696ffb.eachLayer(function (layer) {
|
||||
geo_json_b0da161aca2ab03761c9445da3d471fe.eachLayer(function (layer) {
|
||||
if (RuasSelecionadas.includes(parseInt(layer.feature.id))) {
|
||||
layer.setStyle({ color: 'blue' });
|
||||
} else {
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -71,7 +71,7 @@ class CameraOak:
|
|||
try:
|
||||
self.pipeline = self._criar_pipeline()
|
||||
self.device = dai.Device(self.pipeline, self.dev_info)
|
||||
self.q_video = self.device.getOutputQueue(name="video", maxSize=1, blocking=False)
|
||||
self.q_video = self.device.getOutputQueue(name="rgb", maxSize=1, blocking=False)
|
||||
|
||||
if self.tem_depth:
|
||||
self.q_depth = self.device.getOutputQueue(name="depth", maxSize=1, blocking=False)
|
||||
|
|
@ -139,12 +139,15 @@ class CameraOak:
|
|||
pipeline = dai.Pipeline()
|
||||
|
||||
# RGB
|
||||
cam = pipeline.create(dai.node.ColorCamera)
|
||||
cam = pipeline.createColorCamera()
|
||||
cam.setBoardSocket(dai.CameraBoardSocket.CAM_A)
|
||||
cam.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
|
||||
cam.setInterleaved(False)
|
||||
cam.setBoardSocket(dai.CameraBoardSocket.CAM_A)
|
||||
xout = pipeline.create(dai.node.XLinkOut)
|
||||
xout.setStreamName("video")
|
||||
cam.setColorOrder(dai.ColorCameraProperties.ColorOrder.RGB)
|
||||
cam.setFps(30)
|
||||
|
||||
xout = pipeline.createXLinkOut()
|
||||
xout.setStreamName("rgb")
|
||||
cam.video.link(xout.input)
|
||||
self.mostrar_log(f"Pipeline rgb criado")
|
||||
|
||||
|
|
@ -203,18 +206,18 @@ class CameraOak:
|
|||
y1 = 1.0 - (ROI_INICIO + ROI_TAMANHO)
|
||||
y2 = 1.0 - ROI_INICIO
|
||||
|
||||
manip = pipeline.create(dai.node.ImageManip)
|
||||
manip = pipeline.createImageManip()
|
||||
manip.initialConfig.setCropRect(0.0, y1, 1.0, y2)
|
||||
manip.initialConfig.setResize(RESOLUCAO[1], RESOLUCAO[0])
|
||||
manip.initialConfig.setResize(RESOLUCAO[0], RESOLUCAO[1])
|
||||
manip.initialConfig.setFrameType(dai.RawImgFrame.Type.RGB888p)
|
||||
manip.initialConfig.setKeepAspectRatio(False)
|
||||
cam.video.link(manip.inputImage)
|
||||
|
||||
cam.preview.link(manip.inputImage)
|
||||
|
||||
nn = pipeline.create(dai.node.NeuralNetwork)
|
||||
nn = pipeline.createNeuralNetwork()
|
||||
nn.setBlobPath(blob_path)
|
||||
manip.out.link(nn.input)
|
||||
|
||||
xout_nn = pipeline.create(dai.node.XLinkOut)
|
||||
xout_nn = pipeline.createXLinkOut()
|
||||
xout_nn.setStreamName("nn")
|
||||
nn.out.link(xout_nn.input)
|
||||
|
||||
|
|
@ -291,11 +294,12 @@ class CameraOak:
|
|||
return None, {"erro": "Segmentação não disponível", "duracao": 0, "frame_valido": False}
|
||||
start = time.time()
|
||||
try:
|
||||
w, h = self.modelo_ia_onboard["ia_resolution"]
|
||||
in_nn = self.q_nn.get()
|
||||
out = in_nn.getFirstLayerFp16()
|
||||
h, w = self.modelo_ia_onboard["ia_resolution"]
|
||||
out_np = np.array(out, dtype=np.float32).reshape((len(self.classes), h, w))
|
||||
pred_ids = np.argmax(out_np, axis=0).astype(np.uint8)
|
||||
out_raw = in_nn.getFirstLayerFp16()
|
||||
arr16 = np.frombuffer(np.asarray(out_raw, dtype=np.float16), dtype=np.float16)
|
||||
arr16 = arr16.reshape(len(self.classes), h, w)
|
||||
pred_ids = arr16.argmax(axis=0).astype(np.uint8, copy=False)
|
||||
dur = time.time() - start
|
||||
self.timestamp_ultima_segmentacao = time.time()
|
||||
return pred_ids, {"erro": None, "duracao": dur, "frame_valido": True}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -35,6 +35,40 @@ def decode_image_base64(base64_str):
|
|||
frame_decodificado = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
|
||||
return frame_decodificado
|
||||
|
||||
def fazer_overlay(rgb_bgr, seg_color, alpha=0.35, out_size=None, seg_is_rgb=False):
|
||||
"""
|
||||
rgb_bgr: np.uint8 HxWx3 (BGR)
|
||||
seg_color: np.uint8 HxWx3 (BGR) ou RGB (defina seg_is_rgb=True)
|
||||
alpha: peso da segmentação (0..1)
|
||||
out_size: (W, H) opcional pra forçar dimensão final
|
||||
"""
|
||||
# converte seg pra BGR se veio em RGB
|
||||
if seg_is_rgb:
|
||||
seg_color = cv2.cvtColor(seg_color, cv2.COLOR_RGB2BGR)
|
||||
|
||||
# garante mesmo tamanho (usa INTER_AREA pro RGB e NEAREST pra máscara)
|
||||
if out_size is not None:
|
||||
W, H = out_size
|
||||
else:
|
||||
H, W = rgb_bgr.shape[:2]
|
||||
|
||||
if (rgb_bgr.shape[1], rgb_bgr.shape[0]) != (W, H):
|
||||
rgb_res = np.empty((H, W, 3), np.uint8)
|
||||
cv2.resize(rgb_bgr, (W, H), dst=rgb_res, interpolation=cv2.INTER_AREA)
|
||||
else:
|
||||
rgb_res = rgb_bgr
|
||||
|
||||
if (seg_color.shape[1], seg_color.shape[0]) != (W, H):
|
||||
seg_res = np.empty((H, W, 3), np.uint8)
|
||||
cv2.resize(seg_color, (W, H), dst=seg_res, interpolation=cv2.INTER_NEAREST)
|
||||
else:
|
||||
seg_res = seg_color
|
||||
|
||||
# blend (0.65*RGB + 0.35*SEG por padrão)
|
||||
out = np.empty_like(rgb_res)
|
||||
cv2.addWeighted(rgb_res, 1.0 - alpha, seg_res, alpha, 0, dst=out)
|
||||
return out
|
||||
|
||||
def analisar_linhas_por_profundidade(matriz, campo, fov_h):
|
||||
try:
|
||||
linhas_info = {}
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -5,7 +5,7 @@ import time
|
|||
|
||||
import cv2
|
||||
from shared.enums import StatusModulo, CameraFrameType, StatusOperacao, WeedWorkerCommandType
|
||||
from shared.utils import decode_image_base64, encode_image_base64
|
||||
from shared.utils import decode_image_base64, encode_image_base64, fazer_overlay
|
||||
from camera_worker.camera_oak import CameraOak
|
||||
from shared.contexto_global_redis import CmdKey, ContextoGlobalRedis, CtxKey
|
||||
from weed_worker.weed_detector import WeedDetector
|
||||
|
|
@ -63,7 +63,7 @@ class CameraManager:
|
|||
|
||||
self.weed_detector = WeedDetector(self.camera.colormap_rgb, self.camera.classes)
|
||||
|
||||
self._iniciar_loop_analise_continua(10.0)
|
||||
self._iniciar_loop_analise_continua(20.0)
|
||||
self.iniciando = False
|
||||
self.atualizar_saude_camera()
|
||||
|
||||
|
|
@ -114,12 +114,21 @@ class CameraManager:
|
|||
f = None
|
||||
t = None
|
||||
if tipo == CameraFrameType.Rgb:
|
||||
f, t, _ = self.get_rgb_frame()
|
||||
f = self._ultimo_rgb_frame
|
||||
if f is not None:
|
||||
f = encode_image_base64(f)
|
||||
t = self.camera.timestamp_ultimo_frame_rgb
|
||||
elif tipo == CameraFrameType.Segmentacao:
|
||||
f = self._ultima_analise.get("frame", {}).get("frame")
|
||||
t = self._ultima_analise.get("timestamp")
|
||||
elif tipo == CameraFrameType.Debug:
|
||||
frame_seg = self._ultima_analise.get("mask_color")
|
||||
frame_rgb = self._ultimo_rgb_frame
|
||||
if frame_seg is not None and frame_rgb is not None:
|
||||
f = fazer_overlay(frame_rgb, frame_seg, alpha=0.35, out_size=(640, 360), seg_is_rgb=False)
|
||||
if f is not None:
|
||||
f = encode_image_base64(f)
|
||||
t = self.camera.timestamp_ultimo_frame_rgb
|
||||
return f, t
|
||||
|
||||
def _iniciar_loop_analise_continua(self, freq):
|
||||
|
|
@ -156,9 +165,11 @@ class CameraManager:
|
|||
predictions, ts, res = self.get_segmentation_predictions()
|
||||
if ts == self._ts_segmentacao_anterior:
|
||||
return # já analisado
|
||||
fps = 1.0 / (ts - self._ts_segmentacao_anterior)
|
||||
self._ts_segmentacao_anterior = ts
|
||||
if predictions is not None:
|
||||
analise_completa = self.detectar_ervas(predictions)
|
||||
rgb_frame, ts_frame, res_frame = self.get_rgb_frame()
|
||||
analise_completa = self.detectar_ervas(predictions, rgb_frame)
|
||||
analise = analise_completa.get("dados_visuais", {})
|
||||
|
||||
#self.mostrar_log(f"Deteccoes no radar: {len(analise.get('deteccoes', []))}")
|
||||
|
|
@ -166,6 +177,7 @@ class CameraManager:
|
|||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
CtxKey.DadosWeedWorker,
|
||||
ts_analise=ts,
|
||||
fps_model=fps,
|
||||
analise=converter_valores_numpy(analise)
|
||||
)
|
||||
|
||||
|
|
@ -185,12 +197,12 @@ class CameraManager:
|
|||
|
||||
self._ultima_analise = analise_completa.copy()
|
||||
|
||||
def detectar_ervas(self, predictions):
|
||||
def detectar_ervas(self, predictions, rgb_frame):
|
||||
if self.weed_detector is None:
|
||||
self.mostrar_log("WeedDetector não inicializado!")
|
||||
return []
|
||||
try:
|
||||
return self.weed_detector.detectar(predictions)
|
||||
return self.weed_detector.detectar(predictions, rgb_frame)
|
||||
except Exception as e:
|
||||
self.mostrar_log(f"Erro na detecção de ervas: {e}")
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
"frames_histerese": 2,
|
||||
"min_area_px": 400,
|
||||
"max_area_frac": 0.2,
|
||||
"ia_roi_size": 0.2,
|
||||
"ia_resolution": [384,384],
|
||||
"area_atuacao_bicos": 0.1,
|
||||
"ia_roi_begin": 0.0,
|
||||
"ia_roi_size": 1.0,
|
||||
"ia_resolution": [512,288],
|
||||
|
||||
"erva_top_band_frac": 0.30,
|
||||
"erva_frac_ema": 0.3,
|
||||
|
|
|
|||
|
|
@ -42,36 +42,58 @@ _CONFIG_LOCK = threading.Lock()
|
|||
def load_config(force_reload=False):
|
||||
global _CONFIG_CACHE, _CONFIG_MTIME
|
||||
with _CONFIG_LOCK:
|
||||
try:
|
||||
mtime = os.path.getmtime(_CONFIG_PATH)
|
||||
if force_reload or _CONFIG_CACHE is None or mtime != _CONFIG_MTIME:
|
||||
with open(_CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
_CONFIG_CACHE = json.load(f)
|
||||
_CONFIG_MTIME = mtime
|
||||
except Exception as e:
|
||||
mostrar_log(f"Erro ao ler config: {e}")
|
||||
if _CONFIG_CACHE is None:
|
||||
# Valores default se der ruim no primeiro load
|
||||
_CONFIG_CACHE = {
|
||||
"debug_visual": True,
|
||||
"frames_consecutivos": 3,
|
||||
"frames_histerese": 2,
|
||||
"min_area_px": 400,
|
||||
"max_area_frac": 0.2,
|
||||
"ia_roi_size": 0.2,
|
||||
"ia_resolution": [384,384],
|
||||
|
||||
"erva_top_band_frac": 0.30,
|
||||
"erva_frac_ema": 0.3,
|
||||
"erva_thresh_vel_gain": 0.4,
|
||||
"min_frac_erva_global_on": 0.0020,
|
||||
"min_frac_erva_global_off": 0.0015,
|
||||
"min_frac_erva_top_on": 0.0015,
|
||||
"min_frac_erva_top_off": 0.0010,
|
||||
"min_frac_erva_por_bico": 0.02,
|
||||
"usar_morfologia": True,
|
||||
"kernel_morf": 3
|
||||
}
|
||||
#try:
|
||||
# mtime = os.path.getmtime(_CONFIG_PATH)
|
||||
# if force_reload or _CONFIG_CACHE is None or mtime != _CONFIG_MTIME:
|
||||
# with open(_CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
# _CONFIG_CACHE = json.load(f)
|
||||
# _CONFIG_MTIME = mtime
|
||||
#except Exception as e:
|
||||
# mostrar_log(f"Erro ao ler config: {e}")
|
||||
# if _CONFIG_CACHE is None:
|
||||
# # Valores default se der ruim no primeiro load
|
||||
# _CONFIG_CACHE = {
|
||||
# "debug_visual": True,
|
||||
# "frames_consecutivos": 3,
|
||||
# "frames_histerese": 2,
|
||||
# "min_area_px": 400,
|
||||
# "max_area_frac": 0.2,
|
||||
# "area_atuacao_bicos": 0.1,
|
||||
# "ia_roi_begin": 0.0,
|
||||
# "ia_roi_size": 1.0,
|
||||
# "ia_resolution": [512,288],
|
||||
# "erva_top_band_frac": 0.30,
|
||||
# "erva_frac_ema": 0.3,
|
||||
# "erva_thresh_vel_gain": 0.4,
|
||||
# "min_frac_erva_global_on": 0.0020,
|
||||
# "min_frac_erva_global_off": 0.0015,
|
||||
# "min_frac_erva_top_on": 0.0015,
|
||||
# "min_frac_erva_top_off": 0.0010,
|
||||
# "min_frac_erva_por_bico": 0.02,
|
||||
# "usar_morfologia": True,
|
||||
# "kernel_morf": 3
|
||||
# }
|
||||
_CONFIG_CACHE = {
|
||||
"debug_visual": True,
|
||||
"frames_consecutivos": 3,
|
||||
"frames_histerese": 2,
|
||||
"min_area_px": 400,
|
||||
"max_area_frac": 0.2,
|
||||
"area_atuacao_bicos": 0.1,
|
||||
"ia_roi_begin": 0.0,
|
||||
"ia_roi_size": 1.0,
|
||||
"ia_resolution": [512,288],
|
||||
"erva_top_band_frac": 0.30,
|
||||
"erva_frac_ema": 0.3,
|
||||
"erva_thresh_vel_gain": 0.4,
|
||||
"min_frac_erva_global_on": 0.0020,
|
||||
"min_frac_erva_global_off": 0.0015,
|
||||
"min_frac_erva_top_on": 0.0015,
|
||||
"min_frac_erva_top_off": 0.0010,
|
||||
"min_frac_erva_por_bico": 0.02,
|
||||
"usar_morfologia": True,
|
||||
"kernel_morf": 3
|
||||
}
|
||||
dadosAtu = ContextoGlobalRedis.get_operacao().get("Atu", {})
|
||||
contexto = ContextoGlobalRedis.get_contexto()
|
||||
_CONFIG_CACHE["qtd_bicos"] = dadosAtu.get("qtd_bicos", 4)
|
||||
|
|
@ -79,7 +101,7 @@ def load_config(force_reload=False):
|
|||
|
||||
_CONFIG_CACHE["ia_model_path"] = ContextoGlobalRedis.get_equipamento().get("path_ia_model_ervas", "C:/AgroBaseModels/Ervas/model-2_1.blob")
|
||||
_CONFIG_CACHE["ia_labelmap_path"] = ContextoGlobalRedis.get_equipamento().get("path_ia_labelmap_ervas", "C:/AgroBaseModels/Ervas/model-2_1.txt")
|
||||
_CONFIG_CACHE["ia_roi_begin"] = dadosAtu.get("percent_vertical_deteccao", 0.3)
|
||||
_CONFIG_CACHE["faixa_atuacao_bicos"] = dadosAtu.get("percent_vertical_deteccao", 0.3)
|
||||
return _CONFIG_CACHE
|
||||
|
||||
def reload_config():
|
||||
|
|
|
|||
|
|
@ -10,20 +10,29 @@ class ClassesSegmentacao(IntEnum):
|
|||
CANA = 1
|
||||
CHAO = 2
|
||||
|
||||
|
||||
class WeedDetector:
|
||||
def __init__(self, color_map, classes):
|
||||
from weed_worker.config import load_config
|
||||
config = load_config()
|
||||
resolucao = config.get("ia_resolution")
|
||||
self._reiniciar_deteccoes()
|
||||
self.color_map = color_map
|
||||
self.classes = classes
|
||||
self.resolucao = (resolucao[0], resolucao[1])
|
||||
self.color_lut = np.array(self.color_map, np.uint8)
|
||||
self.lut = np.zeros((256, 3), dtype=np.uint8)
|
||||
for i, color in enumerate(color_map):
|
||||
#self.lut[i] = color
|
||||
self.lut[i] = (color[2], color[1], color[0]) # converte pra (B, G, R)
|
||||
IGNORE_ID = 255
|
||||
self.lut[IGNORE_ID] = (255, 255, 255)
|
||||
|
||||
self._reiniciar_deteccoes()
|
||||
self.use_mock = False
|
||||
self.img_mock = "C:\\ZendionInc\\agrobot_base\\AgroBase\\AgroBase\\bin\\x64\\Debug\\Operacoes\\25_07_2025_14_39_14\\Cam0\\85_rgb.jpeg"
|
||||
self.resolucao = (resolucao[0], resolucao[1])
|
||||
self.predictions = None
|
||||
self.dados_visuais = {}
|
||||
self._dbg_img_shape = (640, 360)
|
||||
self._mostrar_debug = False
|
||||
|
||||
self._dbg_last_ts = None
|
||||
self._dbg_fps_ema = None # fps do "ciclo de debug" (pós-segmentação)
|
||||
|
|
@ -41,6 +50,17 @@ class WeedDetector:
|
|||
self.ervas_identificadas = [dict() for _ in range(qtd_bicos)]
|
||||
self.ultimo_status_bicos = {i: False for i in range(qtd_bicos)}
|
||||
self.ervas_registradas_bico = [set() for _ in range(qtd_bicos)]
|
||||
self.pred_rgb = np.empty((self.resolucao[1], self.resolucao[0], 3), dtype=np.uint8)
|
||||
|
||||
self._is_weed = np.zeros(256, dtype=bool)
|
||||
self._is_weed[int(ClassesSegmentacao.ERVA.value)] = True
|
||||
self._inv_total = 1.0 / (self.resolucao[1] * self.resolucao[0])
|
||||
|
||||
self._erva_frac_global_ema = 0.0
|
||||
self._ervas_no_radar = False
|
||||
self._ervas_no_radar_percent = 0.0
|
||||
self.debug_estat = False
|
||||
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
CtxKey.DadosWeedWorker,
|
||||
analise__ervas_identificadas=self.ervas_identificadas
|
||||
|
|
@ -48,14 +68,9 @@ class WeedDetector:
|
|||
|
||||
def _segmentar_predictions(self, predictions):
|
||||
try:
|
||||
# 🔸 Redimensiona para RGB bonitão (overlay, debug ou exportar)
|
||||
mask_resized = cv2.resize(predictions.astype(np.uint8), self.resolucao, interpolation=cv2.INTER_NEAREST)
|
||||
self.pred_rgb[:] = self.lut[predictions]
|
||||
|
||||
lut = np.zeros((256, 3), dtype=np.uint8)
|
||||
for i, color in enumerate(self.color_map):
|
||||
lut[i] = color
|
||||
|
||||
mask_color = lut[mask_resized]
|
||||
mask_color = self.pred_rgb
|
||||
frame_color = encode_image_base64(mask_color)
|
||||
|
||||
return {
|
||||
|
|
@ -65,14 +80,14 @@ class WeedDetector:
|
|||
"frame": frame_color
|
||||
},
|
||||
"mask_color": mask_color,
|
||||
"classes": mask_resized
|
||||
"classes": predictions
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Erro ao processar predictions: {e}")
|
||||
return None
|
||||
|
||||
def detectar(self, predictions):
|
||||
def detectar(self, predictions, rgb_frame=None):
|
||||
try:
|
||||
# 🔸 Constrói a máscara colorida e outras saídas com base na predictions já pronta
|
||||
resultado = self._segmentar_predictions(predictions)
|
||||
|
|
@ -80,59 +95,35 @@ class WeedDetector:
|
|||
print("[Erro] Segmentação vazia ou falhou")
|
||||
return None
|
||||
|
||||
classes_mask = resultado.get("classes")
|
||||
if classes_mask is None:
|
||||
if predictions is None:
|
||||
print("[Erro] Máscara de classes não encontrada no resultado")
|
||||
return None
|
||||
|
||||
# 🔸 Extrai blobs da classe ERVA (classe_id = 0)
|
||||
#mask_erva = (classes_mask == ClassesSegmentacao.ERVA.value).astype(np.uint8)
|
||||
#num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(mask_erva, connectivity=8)
|
||||
|
||||
#deteccoes = []
|
||||
#for i in range(1, num_labels): # ignora fundo
|
||||
# x, y, w, h, area = stats[i]
|
||||
# deteccoes.append({
|
||||
# 'id': 0,
|
||||
# 'x': x,
|
||||
# 'y': y,
|
||||
# 'largura': w,
|
||||
# 'altura': h,
|
||||
# 'confianca': 1.0,
|
||||
# 'descricao': self.classes[ClassesSegmentacao.ERVA.value]
|
||||
# })
|
||||
|
||||
# 🔸 Calcula controle de bicos com base na segmentação
|
||||
#controle_bicos, deteccoes_filtro = self._calcular_atuacao_bicos(deteccoes, classes_mask.shape)
|
||||
|
||||
# 🔸 Decisão por máscara (sem bbox)
|
||||
#controle_bicos, estat, ervas_no_radar = self._atuacao_por_mascara(classes_mask)
|
||||
#self.frame_idx += 1
|
||||
|
||||
from weed_worker.config import load_config
|
||||
config = load_config()
|
||||
|
||||
# vel_norm pode vir do contexto (0..1 da sua Vmax). Se não tiver, manda 0.0
|
||||
vel_norm = float(config.get("velocidade_robo", 0.0))
|
||||
ervas_no_radar, estat_erva = self._decidir_ervas_no_radar(classes_mask, config, vel_norm=vel_norm)
|
||||
controle_bicos, estat_bicos = self._atuacao_por_mascara(classes_mask, config) # sua lógica de CANA por setor
|
||||
ervas_no_radar, estat_erva = self._decidir_ervas_no_radar(predictions, config, vel_norm=vel_norm)
|
||||
controle_bicos, estat_bicos = self._atuacao_por_mascara(predictions, config) # sua lógica de CANA por setor
|
||||
|
||||
|
||||
resultado["dados_visuais"] = {
|
||||
"timestamp": time.time(),
|
||||
"height": classes_mask.shape[0],
|
||||
"width": classes_mask.shape[1],
|
||||
"height": predictions.shape[0],
|
||||
"width": predictions.shape[1],
|
||||
"deteccoes": [], # deteccoes_filtro,
|
||||
"controle": controle_bicos,
|
||||
"ervas_identificadas": self.ervas_identificadas,
|
||||
"ervas_no_radar": ervas_no_radar, # <- NOVO sinal
|
||||
"ervas_no_radar": ervas_no_radar,
|
||||
"estatisticas": {
|
||||
"erva": estat_erva,
|
||||
"bicos": estat_bicos
|
||||
}
|
||||
}
|
||||
|
||||
self._mostrar_debug_bicos(resultado["mask_color"], [], controle_bicos, ervas_no_radar)
|
||||
frame = rgb_frame if rgb_frame is not None else resultado["mask_color"]
|
||||
self._mostrar_debug_bicos(frame, resultado["classes"], [], controle_bicos, config)
|
||||
|
||||
return resultado
|
||||
|
||||
|
|
@ -140,250 +131,218 @@ class WeedDetector:
|
|||
print(f"Erro ao detectar ervas com predictions: {e}")
|
||||
return None
|
||||
|
||||
def _calcular_iou(self, bbox1, bbox2):
|
||||
x1, y1, w1, h1 = bbox1
|
||||
x2, y2, w2, h2 = bbox2
|
||||
def _atuacao_por_mascara(self, predictions, config):
|
||||
"""
|
||||
Decide atuação dos bicos por máscara (sem bbox), otimizado:
|
||||
- LUT booleana pra "é erva?"
|
||||
- soma por colunas + binning com np.add.reduceat (sem loop por bico)
|
||||
- kernel morfológico cacheado
|
||||
- retorna dict só se precisar (debug); senão usa arrays
|
||||
"""
|
||||
qtd_bicos = int(config.get("qtd_bicos"))
|
||||
zona_inicio = float(config.get("faixa_atuacao_bicos"))
|
||||
zona_altura = float(config.get("area_atuacao_bicos"))
|
||||
usar_morf = bool(config.get("usar_morfologia", False))
|
||||
kernel_morf = int(config.get("kernel_morf", 3))
|
||||
|
||||
xi1 = max(x1, x2)
|
||||
yi1 = max(y1, y2)
|
||||
xi2 = min(x1 + w1, x2 + w2)
|
||||
yi2 = min(y1 + h1, y2 + h2)
|
||||
inter_width = max(0, xi2 - xi1)
|
||||
inter_height = max(0, yi2 - yi1)
|
||||
inter_area = inter_width * inter_height
|
||||
# threshold: fração (<1) ou px absolutos (>=1)
|
||||
thr_cfg = float(config.get("min_frac_erva_por_bico", 0.02))
|
||||
|
||||
area1 = w1 * h1
|
||||
area2 = w2 * h2
|
||||
union_area = area1 + area2 - inter_area
|
||||
H, W = predictions.shape[:2]
|
||||
|
||||
if union_area == 0:
|
||||
return 0
|
||||
return inter_area / union_area
|
||||
# recorte vertical (banda de atuação)
|
||||
y_inicio = int((1.0 - zona_inicio) * H)
|
||||
y_fim = int((1.0 - (zona_inicio + zona_altura)) * H)
|
||||
y_top = min(y_inicio, y_fim)
|
||||
y_bot = max(y_inicio, y_fim)
|
||||
if y_bot <= y_top:
|
||||
# nada a fazer
|
||||
zeros = np.zeros(qtd_bicos, dtype=bool)
|
||||
estat = {"contagem_cana_px_por_bico": np.zeros(qtd_bicos, int),
|
||||
"contagem_cana_frac_por_bico": np.zeros(qtd_bicos, float),
|
||||
"faixa": {"y_top": y_top, "y_bot": y_bot}}
|
||||
self.ultimo_status_bicos = {i: False for i in range(qtd_bicos)}
|
||||
return {i: False for i in range(qtd_bicos)}, estat
|
||||
|
||||
def _track_ervas(self, detections, histerese, velocidade_robo):
|
||||
band = predictions[y_top:y_bot, :]
|
||||
|
||||
# --- LUT booleana (inicialize uma vez no __init__):
|
||||
# self._is_weed = np.zeros(256, bool); self._is_weed[ClassesSegmentacao.ERVA.value] = True
|
||||
mask_erva = self._is_weed[band] # bool view HxW da banda
|
||||
|
||||
# --- morfologia opcional (kernel cacheado):
|
||||
if usar_morf and kernel_morf >= 3 and (kernel_morf & 1):
|
||||
# cacheia por tamanho pra não recriar
|
||||
if getattr(self, "_morf_cache_k", None) != kernel_morf:
|
||||
self._morf_cache_k = kernel_morf
|
||||
self._morf_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_morf, kernel_morf))
|
||||
mask_erva = cv2.morphologyEx(mask_erva.astype(np.uint8), cv2.MORPH_OPEN, self._morf_kernel).astype(bool)
|
||||
|
||||
# --- soma por colunas e binning por bicos (sem loop):
|
||||
# soma de 'True' por coluna
|
||||
col_sums = mask_erva.sum(axis=0).astype(np.int32) # shape (W,)
|
||||
|
||||
# bordas dos bicos (inteiros, de 0 a W):
|
||||
# exato e sem acumulador manual
|
||||
edges = np.linspace(0, W, qtd_bicos + 1, dtype=np.int32)
|
||||
|
||||
# soma por bico: reduceat soma colunas entre edges[i]:edges[i+1]
|
||||
px_erva_por_bico = np.add.reduceat(col_sums, edges[:-1])
|
||||
# áreas por bico (altura da banda * largura de cada setor)
|
||||
alturas = (y_bot - y_top)
|
||||
larguras = np.diff(edges)
|
||||
area_por_bico = alturas * larguras
|
||||
|
||||
# fração por bico (evita divisão por zero)
|
||||
with np.errstate(divide="ignore", invalid="ignore"):
|
||||
frac_por_bico = np.where(area_por_bico > 0, px_erva_por_bico / area_por_bico, 0.0)
|
||||
|
||||
# decisão vetorizada
|
||||
if thr_cfg < 1.0:
|
||||
ativa_arr = (frac_por_bico >= thr_cfg)
|
||||
else:
|
||||
ativa_arr = (px_erva_por_bico >= int(thr_cfg))
|
||||
|
||||
# salva status (se você precisa em dict em outro lugar)
|
||||
atuacao_bicos = {i: bool(ativa_arr[i]) for i in range(qtd_bicos)}
|
||||
self.ultimo_status_bicos = atuacao_bicos.copy()
|
||||
|
||||
estatisticas = {
|
||||
"contagem_cana_px_por_bico": px_erva_por_bico, # array
|
||||
"contagem_cana_frac_por_bico": frac_por_bico, # array
|
||||
"faixa": {"y_top": y_top, "y_bot": y_bot},
|
||||
"larguras": larguras,
|
||||
}
|
||||
return atuacao_bicos, estatisticas
|
||||
|
||||
def _decidir_ervas_no_radar(self, predictions, cfg, vel_norm=0.0):
|
||||
# thresholds base
|
||||
on_global = float(cfg.get("min_frac_erva_global_on", 0.0020))
|
||||
off_global = float(cfg.get("min_frac_erva_global_off", 0.0015))
|
||||
|
||||
# ajuste por velocidade
|
||||
k = float(cfg.get("erva_thresh_vel_gain", 0.0))
|
||||
if k:
|
||||
adj = 1.0 - k * float(vel_norm)
|
||||
if adj < 0.5: adj = 0.5
|
||||
elif adj > 1.0: adj = 1.0
|
||||
on_global *= adj
|
||||
else:
|
||||
adj = 1.0
|
||||
|
||||
# fração global (sem ==)
|
||||
weed_sum = int(self._is_weed[predictions].sum())
|
||||
frac_global = weed_sum * self._inv_total
|
||||
|
||||
# EMA
|
||||
alpha = float(cfg.get("erva_frac_ema", 0.3))
|
||||
ema_g = self._erva_frac_global_ema = (1 - alpha) * self._erva_frac_global_ema + alpha * frac_global
|
||||
|
||||
# histerese
|
||||
prev = self._ervas_no_radar
|
||||
thr = off_global if prev else on_global
|
||||
ervas_no_radar = ema_g >= thr
|
||||
self._ervas_no_radar = ervas_no_radar
|
||||
self._ervas_no_radar_percent = frac_global
|
||||
|
||||
if self.debug_estat:
|
||||
return ervas_no_radar, {
|
||||
"frac_global": frac_global,
|
||||
"ema_global": ema_g,
|
||||
"thr_on_global": on_global,
|
||||
"thr_off_global": off_global,
|
||||
"vel_adj": adj,
|
||||
}
|
||||
return ervas_no_radar, None
|
||||
|
||||
def _mostrar_debug_bicos(self, rgb_frame, classes_mask, detections, atuacao_bicos, config=None):
|
||||
try:
|
||||
max_dist_base = 60 # px (parado)
|
||||
fator_velocidade = 100 # px por m/s (ajuste conforme calibragem real!)
|
||||
max_dist = max_dist_base + fator_velocidade * velocidade_robo
|
||||
|
||||
_ervas_filtradas = []
|
||||
_ervas_radar = []
|
||||
|
||||
#print(f"Deteccoes: {detections}")
|
||||
|
||||
for det in detections:
|
||||
cx = det["x"] + det["largura"] // 2
|
||||
cy = det["y"] + det["altura"] // 2
|
||||
class_id = det["id"]
|
||||
|
||||
found = False
|
||||
for erva in self.ervas_ativas_filtradas:
|
||||
if erva["class_id"] == class_id:
|
||||
ecx, ecy = erva["centro"]
|
||||
dist = np.hypot(cx - ecx, cy - ecy)
|
||||
iou = self._calcular_iou((det["x"], det["y"], det["largura"], det["altura"]), erva["bbox"])
|
||||
if dist < max_dist or iou > 0.3:
|
||||
# Atualiza erva
|
||||
erva["centro"] = (cx, cy)
|
||||
erva["bbox"] = (det["x"], det["y"], det["largura"], det["altura"])
|
||||
erva["ultimo_frame"] = self.frame_idx
|
||||
erva["frames_detectada"] = erva.get("frames_detectada", 0) + 1
|
||||
_ervas_filtradas.append(erva)
|
||||
_ervas_radar.append(erva)
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
erva = {
|
||||
"id": self.next_erva_id,
|
||||
"centro": (cx, cy),
|
||||
"bbox": (det["x"], det["y"], det["largura"], det["altura"]),
|
||||
"class_id": class_id,
|
||||
"ultimo_frame": self.frame_idx,
|
||||
"frames_detectada": 1, # Primeira vez detectada
|
||||
"status": "ativa",
|
||||
"descricao": det["descricao"],
|
||||
"confianca": det["confianca"]
|
||||
}
|
||||
self.next_erva_id += 1
|
||||
_ervas_filtradas.append(erva)
|
||||
_ervas_radar.append(erva)
|
||||
|
||||
# Remove ervas sumidas há mais de N frames
|
||||
self.ervas_ativas_filtradas = [
|
||||
erva for erva in _ervas_filtradas
|
||||
if self.frame_idx - erva["ultimo_frame"] < histerese
|
||||
]
|
||||
self.ervas_ativas_radar = [
|
||||
erva for erva in _ervas_radar
|
||||
if self.frame_idx - erva["ultimo_frame"] < histerese
|
||||
]
|
||||
|
||||
#print(f"Ervas ativas: {self.ervas_ativas}")
|
||||
|
||||
return self.ervas_ativas_filtradas, self.ervas_ativas_radar
|
||||
except Exception as e:
|
||||
print(f"Erro ao trackear ervas: {e}")
|
||||
|
||||
def _calcular_atuacao_bicos(self, detections, frame_shape):
|
||||
try:
|
||||
from weed_worker.config import load_config
|
||||
config = load_config()
|
||||
qtd_bicos = config.get("qtd_bicos")
|
||||
zona_inicio = config.get("ia_roi_begin")
|
||||
zona_altura = config.get("ia_roi_size")
|
||||
frames_consecutivos = config.get("frames_consecutivos")
|
||||
histerese = config.get("frames_histerese")
|
||||
velocidade_robo = config.get("velocidade_robo")
|
||||
min_area = config.get("min_area_px")
|
||||
max_area = config.get("max_area_frac")
|
||||
|
||||
H, W = frame_shape[:2]
|
||||
largura_bico = W / qtd_bicos
|
||||
|
||||
y_inicio = int((1.0 - zona_inicio) * H)
|
||||
y_fim = int((1.0 - (zona_inicio + zona_altura)) * H)
|
||||
|
||||
atuacao_bicos = {i: False for i in range(qtd_bicos)}
|
||||
ervas_filtradas, ervas_radar = self._track_ervas(detections, histerese, velocidade_robo)
|
||||
|
||||
deteccoes = []
|
||||
|
||||
for det in ervas_radar:
|
||||
if not self._filtro_bbox(det, frame_shape, min_area, max_area):
|
||||
continue
|
||||
if det.get("frames_detectada", 0) < frames_consecutivos:
|
||||
continue
|
||||
deteccoes.append(det)
|
||||
x, y, w, h = det["bbox"][0], det["bbox"][1], det["bbox"][2], det["bbox"][3]
|
||||
y_faixa_top = min(y_inicio, y_fim)
|
||||
y_faixa_bot = max(y_inicio, y_fim)
|
||||
y_base = y + h
|
||||
y_top = y
|
||||
|
||||
# Só atua se base da erva está dentro da faixa fina de atuação!
|
||||
if y_base >= y_faixa_top and y_top <= y_faixa_bot:
|
||||
x1 = x
|
||||
x2 = x + w - 1
|
||||
idx_ini = int(x1 / largura_bico)
|
||||
idx_fim = int(x2 / largura_bico)
|
||||
idx_ini = max(0, min(idx_ini, qtd_bicos - 1))
|
||||
idx_fim = max(0, min(idx_fim, qtd_bicos - 1))
|
||||
for i in range(idx_ini, idx_fim + 1):
|
||||
atuacao_bicos[i] = True
|
||||
self._registrar_erva_identificada(i, det["descricao"], det["id"])
|
||||
|
||||
self.ultimo_status_bicos = atuacao_bicos.copy()
|
||||
|
||||
return atuacao_bicos, deteccoes
|
||||
except Exception as e:
|
||||
print(f"Erro ao calcular atuacaoi dos bicos: {e}")
|
||||
|
||||
def _filtro_bbox(self, det, frame_shape, min_area, max_area_frac):
|
||||
"""Filtra bbox por área mínima e máxima (anti ruído/falsos positivos)"""
|
||||
try:
|
||||
H, W = frame_shape[:2]
|
||||
max_area = max_area_frac * (W * H)
|
||||
w, h = det["bbox"][2], det["bbox"][3]
|
||||
area = w * h
|
||||
if area < min_area or area > max_area:
|
||||
return False
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Erro ao filtrar as bboxes: {e}")
|
||||
|
||||
def _registrar_erva_identificada(self, idx_bico, class_erva, id_erva):
|
||||
if id_erva in self.ervas_registradas_bico[idx_bico]:
|
||||
return
|
||||
d = self.ervas_identificadas[idx_bico]
|
||||
d[class_erva] = d.get(class_erva, 0) + 1
|
||||
self.ervas_registradas_bico[idx_bico].add(id_erva)
|
||||
|
||||
|
||||
def _mostrar_debug_bicos(self, frame, detections, atuacao_bicos, ervas_no_radar):
|
||||
try:
|
||||
# mede FPS do "ciclo de debug" (render + imshow)
|
||||
dbg_fps = self._fps_update('_dbg_last_ts', '_dbg_fps_ema')
|
||||
|
||||
#original = cv2.resize(frame, self.resolucao)
|
||||
#seg_color = np.zeros_like(original)
|
||||
#for class_id, color in enumerate(self.color_map):
|
||||
# seg_color[self.predictions == class_id] = color
|
||||
#overlay = cv2.addWeighted(original, 0.5, seg_color, 0.5, 0)
|
||||
|
||||
#debug_img = frame.copy()
|
||||
debug_img = cv2.resize(frame.copy(), (1920,1080), interpolation=cv2.INTER_NEAREST)
|
||||
H, W = debug_img.shape[:2]
|
||||
if not self._mostrar_debug:
|
||||
#print(f"FPS {dbg_fps:.2f}")
|
||||
return
|
||||
|
||||
from weed_worker.config import load_config
|
||||
config = load_config()
|
||||
qtd_bicos = config.get("qtd_bicos")
|
||||
zona_inicio = config.get("ia_roi_begin")
|
||||
faixa_atuacao = config.get("ia_roi_size")
|
||||
#H, W = overlay.shape[:2]
|
||||
largura_bico = W / qtd_bicos
|
||||
# --- cache/config ---
|
||||
if config is None:
|
||||
config = self._cached_cfg # já carregado fora do loop, atualize quando mudar
|
||||
qtd_bicos = int(config.get("qtd_bicos"))
|
||||
zona_inicio = float(config.get("faixa_atuacao_bicos"))
|
||||
faixa_atuacao = float(config.get("area_atuacao_bicos"))
|
||||
|
||||
# Limites ajustados da faixa de atuação (do topo para baixo!)
|
||||
# --- resize sem alocar ---
|
||||
if not hasattr(self, "_dbg_img") or self._dbg_img.shape[:2] != self._dbg_img_shape:
|
||||
self._dbg_img = np.empty((self._dbg_img_shape[1], self._dbg_img_shape[0], 3), dtype=np.uint8)
|
||||
self._seg_color = np.empty((self._dbg_img_shape[1], self._dbg_img_shape[0], 3), dtype=np.uint8)
|
||||
self._layer = np.zeros_like(self._dbg_img)
|
||||
|
||||
cv2.resize(rgb_frame, self._dbg_img_shape, dst=self._dbg_img, interpolation=cv2.INTER_AREA)
|
||||
cm_resized = cv2.resize(classes_mask, self._dbg_img_shape, interpolation=cv2.INTER_NEAREST)
|
||||
|
||||
# --- colore segmentação por LUT (BGR) ---
|
||||
# self.color_lut: (256,3) uint8 BGR (prepare uma vez no __init__)
|
||||
self._seg_color[:] = self.color_lut[cm_resized]
|
||||
|
||||
# --- overlay da segmentação (um addWeighted) ---
|
||||
cv2.addWeighted(self._seg_color, 0.35, self._dbg_img, 0.65, 0, dst=self._dbg_img)
|
||||
|
||||
W, H = self._dbg_img_shape
|
||||
largura_bico = W / float(qtd_bicos)
|
||||
|
||||
# --- faixa de atuação: desenha em layer e blend uma vez ---
|
||||
y_inicio = int((1.0 - zona_inicio) * H)
|
||||
y_fim = int((1.0 - (zona_inicio + faixa_atuacao)) * H)
|
||||
y_fim = int((1.0 - (zona_inicio + faixa_atuacao)) * H)
|
||||
y0, y1 = min(y_inicio, y_fim), max(y_inicio, y_fim)
|
||||
|
||||
#debug_img = overlay.copy()
|
||||
self._layer.fill(0) # zera layer (sem realocar)
|
||||
cv2.rectangle(self._layer, (0, y0), (W, y1), (220, 220, 100), thickness=-1)
|
||||
cv2.addWeighted(self._layer, 0.18, self._dbg_img, 0.82, 0, dst=self._dbg_img)
|
||||
cv2.rectangle(self._dbg_img, (0, y0), (W, y1), (180, 180, 80), 2)
|
||||
cv2.putText(self._dbg_img, "Zona de Atuacao", (10, max(0, y0 - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (180, 180, 80), 2)
|
||||
|
||||
# --- bicos: desenha todos os retângulos ativos na layer e blend uma vez ---
|
||||
self._layer.fill(0)
|
||||
CORES = [(0,255,0), (255,0,0), (0,255,255), (255,128,0), (255,0,255), (0,128,255), (128,255,0), (0,0,255)]
|
||||
|
||||
# Faixa de atuação (transparente)
|
||||
overlay_tmp = debug_img.copy()
|
||||
cv2.rectangle(overlay_tmp, (0, y_fim), (W, y_inicio), (220,220,100), -1)
|
||||
cv2.addWeighted(overlay_tmp, 0.18, debug_img, 0.82, 0, debug_img)
|
||||
|
||||
# Borda + texto
|
||||
cv2.rectangle(debug_img, (0, y_fim), (W, y_inicio), (180,180,80), 2)
|
||||
cv2.putText(debug_img, "Zona de Atuacao", (10, y_inicio-10),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (180,180,80), 2)
|
||||
|
||||
controle_bicos = ContextoGlobalRedis.get_controle().get("controle_bicos", {})
|
||||
|
||||
# Zonas dos bicos
|
||||
for i in range(qtd_bicos):
|
||||
x0 = int(i * largura_bico)
|
||||
x1 = int((i+1) * largura_bico)
|
||||
x1 = int((i + 1) * largura_bico)
|
||||
cor = CORES[i % len(CORES)]
|
||||
if atuacao_bicos.get(i, False):
|
||||
overlay2 = debug_img.copy()
|
||||
cv2.rectangle(overlay2, (x0, y_fim), (x1, y_inicio), cor, -1)
|
||||
cv2.addWeighted(overlay2, 0.15, debug_img, 0.85, 0, debug_img)
|
||||
cv2.rectangle(debug_img, (x0, y_fim), (x1, y_inicio), cor, 1)
|
||||
cv2.rectangle(self._layer, (x0, y0), (x1, y1), cor, thickness=-1)
|
||||
# um blend para todos os bicos ligados
|
||||
cv2.addWeighted(self._layer, 0.15, self._dbg_img, 0.85, 0, dst=self._dbg_img)
|
||||
|
||||
# bordas + texto (rápido, mantém no loop)
|
||||
for i in range(qtd_bicos):
|
||||
x0 = int(i * largura_bico)
|
||||
x1 = int((i + 1) * largura_bico)
|
||||
cor = CORES[i % len(CORES)]
|
||||
cv2.rectangle(self._dbg_img, (x0, y0), (x1, y1), cor, 1)
|
||||
status = "ON" if atuacao_bicos.get(i, False) else "OFF"
|
||||
status_controle = "ON" if controle_bicos.get(i, False) else "OFF"
|
||||
cv2.putText(debug_img, f"Bico {i} {status} ({status_controle})", (x0+5, y_inicio+25), cv2.FONT_HERSHEY_SIMPLEX, 0.6, cor, 2)
|
||||
cv2.putText(self._dbg_img, f"Bico {i} {status}", (x0 + 5, min(H-5, y1 + 20)),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.6, cor, 2)
|
||||
|
||||
escala_x = self.resolucao[0] / frame.shape[1]
|
||||
escala_y = self.resolucao[1] / frame.shape[0]
|
||||
# BBoxes
|
||||
# --- bboxes (escala pro debug HxW) ---
|
||||
sx = W / float(self.resolucao[0])
|
||||
sy = H / float(self.resolucao[1])
|
||||
for det in detections:
|
||||
x = int(det["bbox"][0] * escala_x)
|
||||
y = int(det["bbox"][1] * escala_y)
|
||||
w = int(det["bbox"][2] * escala_x)
|
||||
h = int(det["bbox"][3] * escala_y)
|
||||
_id = det.get("id")
|
||||
class_name = det.get("descricao", "erva")
|
||||
conf = det.get("confianca", 0)
|
||||
bbox_cor = (0,0,255)
|
||||
cv2.rectangle(debug_img, (x, y), (x+w, y+h), bbox_cor, 2)
|
||||
cv2.putText(debug_img, f"ID: {_id} | {class_name} {conf:.2f}", (x, y-5),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, bbox_cor, 2)
|
||||
x = int(det["bbox"][0] * sx); y = int(det["bbox"][1] * sy)
|
||||
w = int(det["bbox"][2] * sx); h = int(det["bbox"][3] * sy)
|
||||
bbox_cor = (0, 0, 255)
|
||||
cv2.rectangle(self._dbg_img, (x, y), (x + w, y + h), bbox_cor, 2)
|
||||
cv2.putText(self._dbg_img, f'ID:{det.get("id")} {det.get("descricao","erva")} {det.get("confianca",0):.2f}',
|
||||
(x, max(0, y - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, bbox_cor, 2)
|
||||
|
||||
# --- HUD de performance ---
|
||||
cv2.putText(debug_img, f"Ervas no radar: {'Sim' if ervas_no_radar else 'Nao'}", (10, 30),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
|
||||
cv2.putText(debug_img, f"Dbg FPS: {dbg_fps:.1f}", (10, 60),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
|
||||
# --- HUD ---
|
||||
cv2.putText(self._dbg_img, f"Ervas no radar: {'Sim' if self._ervas_no_radar else 'Nao'} ({(self._ervas_no_radar_percent * 100.0):.2f}%)", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
|
||||
cv2.putText(self._dbg_img, f"Dbg FPS: {dbg_fps:.1f}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
|
||||
|
||||
cv2.imshow("Debug Weed Worker", cv2.cvtColor(debug_img, cv2.COLOR_RGB2BGR))
|
||||
# Dica: para nao travar pipeline, mantenha 1ms; evite valores maiores
|
||||
# já está em BGR
|
||||
cv2.imshow("Debug Weed Worker", self._dbg_img)
|
||||
cv2.waitKey(1)
|
||||
except Exception as e:
|
||||
print(f"Erro ao mostrar debug: {e}")
|
||||
|
||||
|
||||
def _fps_update(self, last_ts_attr: str, ema_attr: str, alpha: float = 0.2):
|
||||
"""Atualiza e retorna FPS (EMA) baseado no timestamp anterior salvo em self"""
|
||||
import time
|
||||
|
|
@ -396,145 +355,3 @@ class WeedDetector:
|
|||
setattr(self, last_ts_attr, now)
|
||||
setattr(self, ema_attr, fps_ema)
|
||||
return fps_ema if fps_ema is not None else 0.0
|
||||
|
||||
|
||||
def _atuacao_por_mascara(self, classes_mask, config):
|
||||
"""
|
||||
Decide atuação dos bicos por máscara (sem bbox):
|
||||
- Para cada bico: ativa se a fração de CANA no setor >= limiar.
|
||||
- Limiar aceita dois formatos:
|
||||
• < 1.0 => fração (recomendado, independente da resolução)
|
||||
• >= 1 => pixels absolutos (retrocompat)
|
||||
Retorna: (atuacao_bicos: dict, estatisticas: dict)
|
||||
"""
|
||||
qtd_bicos = int(config.get("qtd_bicos"))
|
||||
zona_inicio = float(config.get("ia_roi_begin"))
|
||||
zona_altura = float(config.get("ia_roi_size"))
|
||||
usar_morf = bool(config.get("usar_morfologia", False))
|
||||
kernel_morf = int(config.get("kernel_morf", 3))
|
||||
|
||||
# Limiar “dinâmico”: fração (<1) ou absoluto (>=1)
|
||||
thr_erva_cfg = config.get("min_frac_erva_por_bico", 0.02) # 2% do setor por padrão
|
||||
try:
|
||||
thr_erva_cfg = float(thr_erva_cfg)
|
||||
except:
|
||||
thr_erva_cfg = 0.02
|
||||
|
||||
H, W = classes_mask.shape[:2]
|
||||
y_inicio = int((1.0 - zona_inicio) * H)
|
||||
y_fim = int((1.0 - (zona_inicio + zona_altura)) * H)
|
||||
|
||||
y_top = min(y_inicio, y_fim)
|
||||
y_bot = max(y_inicio, y_fim)
|
||||
band = classes_mask[y_top:y_bot, :]
|
||||
|
||||
# Máscara binária de CANA na banda
|
||||
mask_erva = (band == ClassesSegmentacao.ERVA.value).astype(np.uint8)
|
||||
|
||||
# Anti-ruído opcional
|
||||
if usar_morf and kernel_morf >= 3 and kernel_morf % 2 == 1:
|
||||
k = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_morf, kernel_morf))
|
||||
mask_erva = cv2.morphologyEx(mask_erva, cv2.MORPH_OPEN, k)
|
||||
|
||||
largura_bico = W / float(qtd_bicos)
|
||||
atuacao_bicos = {i: False for i in range(qtd_bicos)}
|
||||
cont_erva_px = {}
|
||||
cont_erva_frac = {}
|
||||
|
||||
for i in range(qtd_bicos):
|
||||
x0 = int(i * largura_bico)
|
||||
x1 = int((i + 1) * largura_bico)
|
||||
x0 = max(0, min(W, x0))
|
||||
x1 = max(0, min(W, x1))
|
||||
if x1 <= x0:
|
||||
cont_erva_px[i] = 0
|
||||
cont_erva_frac[i] = 0.0
|
||||
continue
|
||||
|
||||
region = mask_erva[:, x0:x1]
|
||||
px_erva = int(region.sum())
|
||||
area_setor = float(region.size)
|
||||
frac_erva = (px_erva / area_setor) if area_setor > 0 else 0.0
|
||||
|
||||
cont_erva_px[i] = px_erva
|
||||
cont_erva_frac[i] = frac_erva
|
||||
|
||||
# Se threshold <1: comparar por fração; se >=1: comparar por pixels
|
||||
if thr_erva_cfg < 1.0:
|
||||
ativa = (frac_erva >= thr_erva_cfg)
|
||||
else:
|
||||
ativa = (px_erva >= int(thr_erva_cfg))
|
||||
|
||||
atuacao_bicos[i] = bool(ativa)
|
||||
|
||||
estatisticas = {
|
||||
"contagem_cana_px_por_bico": cont_erva_px,
|
||||
"contagem_cana_frac_por_bico": cont_erva_frac,
|
||||
"faixa": {"y_top": y_top, "y_bot": y_bot}
|
||||
}
|
||||
self.ultimo_status_bicos = atuacao_bicos.copy()
|
||||
return atuacao_bicos, estatisticas
|
||||
|
||||
def _fractions_erva(self, classes_mask):
|
||||
H, W = classes_mask.shape[:2]
|
||||
total_px = H * W
|
||||
|
||||
# global
|
||||
frac_global = np.count_nonzero(classes_mask == ClassesSegmentacao.ERVA.value) / float(total_px)
|
||||
|
||||
# lookahead (top band p/ antecipar redução)
|
||||
from weed_worker.config import load_config
|
||||
cfg = load_config()
|
||||
top_frac = float(cfg.get("erva_top_band_frac", 0.30)) # 30% do topo
|
||||
top_h = max(1, int(H * top_frac))
|
||||
band_top = classes_mask[0:top_h, :]
|
||||
frac_top = np.count_nonzero(band_top == ClassesSegmentacao.ERVA.value) / float(band_top.size)
|
||||
|
||||
return frac_global, frac_top
|
||||
|
||||
def _decidir_ervas_no_radar(self, classes_mask, cfg, vel_norm=0.0):
|
||||
"""
|
||||
Decide ErvasNoRadar por fração (global e lookahead).
|
||||
Aplica histerese e opcionalmente ajusta threshold pela velocidade.
|
||||
"""
|
||||
|
||||
# thresholds base (frações)
|
||||
on_global = float(cfg.get("min_frac_erva_global_on", 0.0020)) # 0,20%
|
||||
off_global = float(cfg.get("min_frac_erva_global_off", 0.0015)) # 0,15%
|
||||
on_top = float(cfg.get("min_frac_erva_top_on", 0.0015))
|
||||
off_top = float(cfg.get("min_frac_erva_top_off", 0.0010))
|
||||
|
||||
# ajuste por velocidade (opcional)
|
||||
k = float(cfg.get("erva_thresh_vel_gain", 0.0)) # 0.0 desliga
|
||||
adj = max(0.5, min(1.0, 1.0 - k * float(vel_norm))) # clamp [0.5, 1.0]
|
||||
on_global *= adj; on_top *= adj
|
||||
# (tipicamente só mexe no ON; OFF pode ficar fixo)
|
||||
|
||||
frac_global, frac_top = self._fractions_erva(classes_mask)
|
||||
|
||||
# EMA (suavização) opcional
|
||||
alpha = float(cfg.get("erva_frac_ema", 0.3))
|
||||
self._erva_frac_global_ema = (1-alpha)*getattr(self, "_erva_frac_global_ema", frac_global) + alpha*frac_global
|
||||
self._erva_frac_top_ema = (1-alpha)*getattr(self, "_erva_frac_top_ema", frac_top) + alpha*frac_top
|
||||
|
||||
# Histerese global
|
||||
prev = getattr(self, "_ervas_no_radar", False)
|
||||
hit_global = self._erva_frac_global_ema >= (on_global if not prev else off_global)
|
||||
hit_top = self._erva_frac_top_ema >= (on_top if not prev else off_top)
|
||||
|
||||
ervas_no_radar = bool(hit_global or hit_top)
|
||||
self._ervas_no_radar = ervas_no_radar
|
||||
|
||||
estat = {
|
||||
"frac_global": frac_global,
|
||||
"frac_top": frac_top,
|
||||
"ema_global": self._erva_frac_global_ema,
|
||||
"ema_top": self._erva_frac_top_ema,
|
||||
"thr_on_global": on_global,
|
||||
"thr_off_global": off_global,
|
||||
"thr_on_top": on_top,
|
||||
"thr_off_top": off_top,
|
||||
"vel_adj": adj
|
||||
}
|
||||
return ervas_no_radar, estat
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -6,42 +6,54 @@ import numpy as np
|
|||
MODELO = "oak-1"
|
||||
pasta_mascaras = os.path.join(MODELO, "dataset", "original", "masks")
|
||||
|
||||
# Lista de substituições (em RGB)
|
||||
substituicoes = [
|
||||
{"target_rgb": (128, 0, 0), "tolerancia": 10}, # chão
|
||||
{"target_rgb": (0, 128, 0), "tolerancia": 10}, # erva
|
||||
{"target_rgb": (0, 0, 128), "tolerancia": 10}, # cana
|
||||
# Regras de substituição (cores em RGB)
|
||||
# Exemplo: trocar (255, 0, 0) por branco (255,255,255) com tolerância 10
|
||||
SUBSTITUICOES = [
|
||||
#{"target_rgb": (255, 0, 0), "tolerancia": 10, "replace_rgb": (255, 255, 255)}, # vermelho -> branco
|
||||
{"target_rgb": (128, 0, 0), "tolerancia": 50, "replace_rgb": (128, 0, 0)}, # chao
|
||||
{"target_rgb": (0, 128, 0), "tolerancia": 50, "replace_rgb": (0, 128, 0)}, # erva
|
||||
{"target_rgb": (0, 0, 128), "tolerancia": 50, "replace_rgb": (0, 0, 128)}, # cana
|
||||
]
|
||||
|
||||
def dentro_da_tolerancia(pixel, target, tol):
|
||||
return all(abs(int(pixel[i]) - target[i]) <= tol for i in range(3))
|
||||
def aplicar_substituicoes(img_bgr):
|
||||
"""Recebe imagem BGR (OpenCV) e aplica regras RGB com tolerância, de forma vetorizada."""
|
||||
# Converte uma vez pra RGB só para fazer o match nas cores “humanas”
|
||||
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
|
||||
|
||||
for rule in SUBSTITUICOES:
|
||||
(tr, tg, tb) = rule["target_rgb"]
|
||||
tol = int(rule.get("tolerancia", 0))
|
||||
(rr, rg, rb) = rule["replace_rgb"]
|
||||
|
||||
# Faixa inferior/superior com tolerância (clamp 0..255)
|
||||
lower = np.array([max(tr - tol, 0), max(tg - tol, 0), max(tb - tol, 0)], dtype=np.uint8)
|
||||
upper = np.array([min(tr + tol, 255), min(tg + tol, 255), min(tb + tol, 255)], dtype=np.uint8)
|
||||
|
||||
# Máscara booleana dos pixels a substituir
|
||||
mask = cv2.inRange(img_rgb, lower, upper) # 255 onde bate
|
||||
|
||||
if np.any(mask):
|
||||
# Cria uma imagem de destino RGB com a cor de replace
|
||||
replace_rgb = np.zeros_like(img_rgb)
|
||||
replace_rgb[:] = (rr, rg, rb)
|
||||
# Faz o blend: onde mask==255, põe replace; onde não, mantém original
|
||||
img_rgb = np.where(mask[..., None] == 255, replace_rgb, img_rgb)
|
||||
|
||||
# Volta pra BGR pro OpenCV salvar
|
||||
return cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
|
||||
|
||||
def corrigir_mascara(caminho_img):
|
||||
img = cv2.imread(caminho_img)
|
||||
if img is None:
|
||||
print(f"Erro ao carregar: {caminho_img}")
|
||||
img_bgr = cv2.imread(caminho_img, cv2.IMREAD_COLOR)
|
||||
if img_bgr is None:
|
||||
print(f"[ERRO] Não abriu: {caminho_img}")
|
||||
return
|
||||
|
||||
alterado = False
|
||||
|
||||
# Percorre pixel por pixel (sim, é necessário se for por range)
|
||||
for y in range(img.shape[0]):
|
||||
for x in range(img.shape[1]):
|
||||
b, g, r = img[y, x] # OpenCV usa BGR
|
||||
for s in substituicoes:
|
||||
target_r, target_g, target_b = s["target_rgb"]
|
||||
tol = s["tolerancia"]
|
||||
if dentro_da_tolerancia((r, g, b), (target_r, target_g, target_b), tol):
|
||||
img[y, x] = (target_b, target_g, target_r) # volta pra BGR
|
||||
alterado = True
|
||||
break
|
||||
|
||||
if alterado:
|
||||
cv2.imwrite(caminho_img, img)
|
||||
out_bgr = aplicar_substituicoes(img_bgr)
|
||||
if not np.array_equal(out_bgr, img_bgr):
|
||||
cv2.imwrite(caminho_img, out_bgr)
|
||||
print(f"Ajustado: {os.path.basename(caminho_img)}")
|
||||
|
||||
# Roda para todas as máscaras
|
||||
for nome_arquivo in os.listdir(pasta_mascaras):
|
||||
if nome_arquivo.lower().endswith(".png"):
|
||||
caminho = os.path.join(pasta_mascaras, nome_arquivo)
|
||||
corrigir_mascara(caminho)
|
||||
if __name__ == "__main__":
|
||||
for nome in os.listdir(pasta_mascaras):
|
||||
if nome.lower().endswith((".png", ".jpg", ".jpeg")):
|
||||
corrigir_mascara(os.path.join(pasta_mascaras, nome))
|
||||
|
|
|
|||
|
|
@ -1,141 +1,109 @@
|
|||
import os
|
||||
from torchvision import transforms
|
||||
from PIL import ImageOps, Image, ImageEnhance, ImageFilter
|
||||
import torchvision.transforms.functional as TF
|
||||
from torchvision.transforms.functional import to_pil_image
|
||||
import torchvision.transforms as T
|
||||
import cv2
|
||||
import numpy as np
|
||||
import json, os, cv2, numpy as np
|
||||
from PIL import Image
|
||||
import albumentations as A
|
||||
|
||||
# ⚙️ Configurações
|
||||
MODELO = "oak-1"
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
MODELO = config["camera"]
|
||||
|
||||
# Caminhos para os diretórios onde suas imagens e máscaras originais estão armazenadas
|
||||
# Pastas
|
||||
dataset_path = os.path.join(MODELO, "dataset", "original", "images")
|
||||
masks_path = os.path.join(MODELO, "dataset", "original", "masks")
|
||||
masks_path = os.path.join(MODELO, "dataset", "original", "masks")
|
||||
aug_img_out = os.path.join(MODELO, "dataset", "augmented", "images")
|
||||
aug_msk_out = os.path.join(MODELO, "dataset", "augmented", "masks")
|
||||
os.makedirs(aug_img_out, exist_ok=True)
|
||||
os.makedirs(aug_msk_out, exist_ok=True)
|
||||
|
||||
# Caminhos para os diretórios onde as imagens e máscaras aumentadas serão salvas
|
||||
augmented_images_path = os.path.join(MODELO, "dataset", "augmented", "images")
|
||||
augmented_masks_path = os.path.join(MODELO, "dataset", "augmented", "masks")
|
||||
# Pipeline de augmentations
|
||||
train_tf = A.Compose([
|
||||
A.HorizontalFlip(p=0.5),
|
||||
|
||||
class ComposeWithSeed(object):
|
||||
def __init__(self, transforms):
|
||||
self.transforms = transforms
|
||||
# Geométricas (aplicam em imagem e máscara)
|
||||
A.ShiftScaleRotate(
|
||||
shift_limit=0.05,
|
||||
scale_limit=0.15,
|
||||
rotate_limit=8,
|
||||
border_mode=cv2.BORDER_CONSTANT,
|
||||
value=(255,255,255),
|
||||
mask_value=(255,255,255),
|
||||
interpolation=cv2.INTER_LINEAR,
|
||||
p=0.5
|
||||
),
|
||||
|
||||
def __call__(self, i, img, mask):
|
||||
transforms.RandomHorizontalFlip(p=0.5)
|
||||
apply_mask, t = self.transforms[i]
|
||||
img = t(img)
|
||||
if apply_mask:
|
||||
mask = t(mask)
|
||||
return img, mask
|
||||
|
||||
# Fotométricas (somente imagem)
|
||||
A.OneOf([
|
||||
A.RandomBrightnessContrast(0.2, 0.2, p=1),
|
||||
A.HueSaturationValue(hue_shift_limit=5, sat_shift_limit=25, val_shift_limit=20, p=1),
|
||||
A.RandomGamma(gamma_limit=(80,120), p=1),
|
||||
], p=0.7),
|
||||
|
||||
def gaussian_blur(img, radius=1):
|
||||
return img.filter(ImageFilter.GaussianBlur(radius))
|
||||
A.OneOf([
|
||||
A.MotionBlur(blur_limit=3, p=1),
|
||||
A.GaussianBlur(blur_limit=3, p=1),
|
||||
], p=0.25),
|
||||
|
||||
def add_gaussian_noise(img, mean=0, std=10):
|
||||
arr = np.array(img).astype(np.float32)
|
||||
noise = np.random.normal(mean, std, arr.shape)
|
||||
arr_noisy = np.clip(arr + noise, 0, 255).astype(np.uint8)
|
||||
return Image.fromarray(arr_noisy)
|
||||
A.OneOf([
|
||||
A.GaussNoise(var_limit=(5.0, 15.0), p=1),
|
||||
A.ImageCompression(quality_lower=40, quality_upper=80, p=1),
|
||||
], p=0.25),
|
||||
|
||||
def lighting_more_sun(img):
|
||||
t = T.ColorJitter(brightness=0.25, contrast=0.25, saturation=0.15, hue=0.02)
|
||||
return t(img)
|
||||
A.RandomShadow(p=0.2),
|
||||
A.RandomSunFlare(p=0.1),
|
||||
A.ChannelShuffle(p=0.05),
|
||||
A.CoarseDropout(max_holes=8, max_height=20, max_width=20, p=0.2)
|
||||
|
||||
def lighting_less_sun(img):
|
||||
t = T.ColorJitter(brightness=0.25, contrast=0.25, saturation=0.05, hue=0.02)
|
||||
img = t(img)
|
||||
# Inverter o efeito de "mais sol" — clareia reduzindo brilho/contraste
|
||||
enhancer_b = ImageEnhance.Brightness(img)
|
||||
img = enhancer_b.enhance(0.75) # < 1.0 escurece
|
||||
enhancer_c = ImageEnhance.Contrast(img)
|
||||
img = enhancer_c.enhance(0.85) # < 1.0 reduz contraste
|
||||
return img
|
||||
# Resize final (img=LINEAR, mask=NEAREST)
|
||||
#A.Resize(height=H, width=W, interpolation=cv2.INTER_LINEAR, mask_interpolation=cv2.INTER_NEAREST),
|
||||
], additional_targets={'mask':'mask'})
|
||||
|
||||
def flip_horizontal(img):
|
||||
return ImageOps.mirror(img)
|
||||
def load_rgb(path):
|
||||
# cv2 lê BGR → converte pra RGB (Albumentations usa RGB por padrão)
|
||||
im = cv2.imread(path, cv2.IMREAD_COLOR)
|
||||
if im is None:
|
||||
raise FileNotFoundError(path)
|
||||
return cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
|
||||
|
||||
def flip_vertical(img):
|
||||
return ImageOps.flip(img) # ou TF.vflip(img)
|
||||
def save_rgb(path, arr_rgb):
|
||||
# Salva em RGB mantendo cores corretas
|
||||
Image.fromarray(arr_rgb).save(path)
|
||||
|
||||
def rotate_image(img, angle):
|
||||
return TF.rotate(img, angle, fill=(255,255,255))
|
||||
def augment_images_and_masks(n_copies=6):
|
||||
# Faz pareamento por nome base (sem extensão)
|
||||
imgs = sorted([f for f in os.listdir(dataset_path) if os.path.isfile(os.path.join(dataset_path,f))])
|
||||
msks = sorted([f for f in os.listdir(masks_path) if os.path.isfile(os.path.join(masks_path,f))])
|
||||
|
||||
def perspective_image(img, magnitude=0.5):
|
||||
width, height = img.size
|
||||
|
||||
# Pontos de origem
|
||||
points_orig = np.float32([
|
||||
[0, 0],
|
||||
[width, 0],
|
||||
[0, height],
|
||||
[width, height]
|
||||
])
|
||||
|
||||
# Pontos de destino, deslocados com base na magnitude
|
||||
points_dest = np.float32([
|
||||
[int(magnitude * width), int(magnitude * height)],
|
||||
[int((1 - magnitude) * width), 0],
|
||||
[0, int((1 - magnitude) * height)],
|
||||
[width, height]
|
||||
])
|
||||
|
||||
# Calcula a matriz de transformação e aplica a transformação de perspectiva
|
||||
matrix = cv2.getPerspectiveTransform(points_orig, points_dest)
|
||||
img_transformed = cv2.warpPerspective(np.array(img), matrix, (width, height), borderValue=(255,255,255))
|
||||
|
||||
return Image.fromarray(img_transformed)
|
||||
# Mapeia máscaras por nome-base
|
||||
msk_map = {os.path.splitext(m)[0]: m for m in msks}
|
||||
|
||||
# Definindo as transformações
|
||||
transform_list = [
|
||||
(True, T.Lambda(lambda img: flip_horizontal(img))), # Aplica flip na horizontal
|
||||
#(True, T.Lambda(lambda img: flip_vertical(img))), # Aplica flip na vertical
|
||||
#(True, T.Lambda(lambda img: rotate_image(img, 90))), # Rotação de 90 graus
|
||||
#(True, T.Lambda(lambda img: rotate_image(img, -90))), # Rotação de -90 graus
|
||||
#(True, T.Lambda(lambda img: perspective_image(img, magnitude=0.2))),
|
||||
#(True, T.Lambda(lambda img: perspective_image(img, magnitude=0.1))),
|
||||
(False, T.Lambda(lambda img: lighting_more_sun(img))),
|
||||
(False, T.Lambda(lambda img: lighting_less_sun(img))),
|
||||
(False, T.Lambda(lambda img: gaussian_blur(img, radius=1))),
|
||||
(False, T.Lambda(lambda img: add_gaussian_noise(img, std=8))),
|
||||
#T.ToTensor(), # Converte as imagens PIL para tensores PyTorch
|
||||
]
|
||||
total = 0
|
||||
for img_file in imgs:
|
||||
base, ext = os.path.splitext(img_file)
|
||||
if base not in msk_map:
|
||||
print(f"[WARN] Máscara não encontrada para {img_file}, pulando.")
|
||||
continue
|
||||
|
||||
# Agora, definimos a transformação composta com a classe personalizada
|
||||
transform = ComposeWithSeed(transform_list)
|
||||
img_path = os.path.join(dataset_path, img_file)
|
||||
msk_path = os.path.join(masks_path, msk_map[base])
|
||||
|
||||
# Verifica se os diretórios de destino existem, caso contrário, cria os diretórios
|
||||
os.makedirs(augmented_images_path, exist_ok=True)
|
||||
os.makedirs(augmented_masks_path, exist_ok=True)
|
||||
# Carrega RGB (máscara como RGB também — mantemos as cores exatas)
|
||||
img = load_rgb(img_path)
|
||||
msk = load_rgb(msk_path)
|
||||
|
||||
# Função para aplicar a transformação e salvar as imagens e máscaras transformadas
|
||||
def augment_images_and_masks(dataset_path, masks_path, augmented_images_path, augmented_masks_path, transform, num_copies):
|
||||
# Lista todos os arquivos nos diretórios do dataset de imagens e máscaras
|
||||
image_files = [f for f in os.listdir(dataset_path) if os.path.isfile(os.path.join(dataset_path, f))]
|
||||
mask_files = [f for f in os.listdir(masks_path) if os.path.isfile(os.path.join(masks_path, f))]
|
||||
for i in range(n_copies):
|
||||
# Aplica aug; máscara recebe só geométricas
|
||||
aug = train_tf(image=img, mask=msk)
|
||||
img_aug = aug["image"]
|
||||
msk_aug = aug["mask"]
|
||||
|
||||
for image_file, mask_file in zip(image_files, mask_files):
|
||||
image_path = os.path.join(dataset_path, image_file)
|
||||
mask_path = os.path.join(masks_path, mask_file)
|
||||
image = Image.open(image_path).convert('RGB')
|
||||
mask = Image.open(mask_path).convert('RGB')
|
||||
# Salva
|
||||
out_img = os.path.join(aug_img_out, f"{base}_aug_{i:02d}{ext}")
|
||||
out_msk = os.path.join(aug_msk_out, f"{base}_aug_{i:02d}{os.path.splitext(msk_map[base])[1]}")
|
||||
save_rgb(out_img, img_aug)
|
||||
save_rgb(out_msk, msk_aug)
|
||||
total += 1
|
||||
|
||||
for i in range(num_copies):
|
||||
# Aplica a transformação de maneira consistente em ambos, imagem e máscara
|
||||
transformed_image, transformed_mask = transform(i, image, mask)
|
||||
print(f"Augmentation completed! {total} pares gerados.")
|
||||
|
||||
# Salva a imagem e a máscara transformadas
|
||||
image_save_path = os.path.join(augmented_images_path, f"{os.path.splitext(image_file)[0]}_aug_{i}{os.path.splitext(image_file)[1]}")
|
||||
mask_save_path = os.path.join(augmented_masks_path, f"{os.path.splitext(mask_file)[0]}_aug_{i}{os.path.splitext(mask_file)[1]}")
|
||||
|
||||
#transformed_image_pil = to_pil_image(transformed_image)
|
||||
transformed_image.save(image_save_path)
|
||||
#transformed_mask_pil = to_pil_image(transformed_mask)
|
||||
transformed_mask.save(mask_save_path)
|
||||
|
||||
# Chama a função para iniciar o processo de aumento de dados
|
||||
augment_images_and_masks(dataset_path, masks_path, augmented_images_path, augmented_masks_path, transform, num_copies=len(transform_list))
|
||||
|
||||
print("Augmentation completed!")
|
||||
if __name__ == "__main__":
|
||||
augment_images_and_masks(n_copies=8)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
import json
|
||||
import os
|
||||
import cv2
|
||||
from utils import carregar_labelmap_completo, converter_mask_rgb_para_ids
|
||||
|
||||
# ⚙️ Configurações
|
||||
MODELO = "oak-1"
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
MODELO = config["camera"]
|
||||
RESOLUCAO = config["resolucao"]
|
||||
pasta_base = os.path.join(MODELO, "dataset")
|
||||
fonte_dados = ["original", "augmented"]
|
||||
labelmap_path = os.path.join(pasta_base, "labelmap.txt")
|
||||
|
||||
RESOLUCOES = {
|
||||
#"512x512": (512, 512),
|
||||
#"768x768": (768, 768),
|
||||
"384x384": (384, 384)
|
||||
f"{RESOLUCAO[0]}x{RESOLUCAO[1]}": (RESOLUCAO[0], RESOLUCAO[1]),
|
||||
}
|
||||
|
||||
# === Início do processamento ===
|
||||
|
|
@ -44,7 +46,7 @@ for fonte in fonte_dados:
|
|||
|
||||
# Tenta carregar a máscara RGB (se existir)
|
||||
if os.path.exists(caminho_mask):
|
||||
img_mask_rgb = cv2.imread(caminho_mask).convert("RGB")
|
||||
img_mask_rgb = cv2.cvtColor(cv2.imread(caminho_mask), cv2.COLOR_BGR2RGB)
|
||||
#img_mask_rgb = cv2.cvtColor(img_mask_rgb, cv2.COLOR_BGR2RGB) # ← CORRIGE isso!
|
||||
if img_mask_rgb is not None:
|
||||
mask_ids = converter_mask_rgb_para_ids(img_mask_rgb, cor_para_id, ignore_id)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
import json
|
||||
import os
|
||||
import shutil
|
||||
import random
|
||||
|
||||
# ⚙️ Configurações
|
||||
MODELO = "oak-1"
|
||||
RESOLUCAO = (384, 384)
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
MODELO = config["camera"]
|
||||
RESOLUCAO = config["resolucao"]
|
||||
pasta_origem = os.path.join(MODELO, "dataset", f"{RESOLUCAO[0]}x{RESOLUCAO[1]}")
|
||||
pasta_destino = os.path.join(MODELO, "dataset", "split")
|
||||
|
||||
percent_train = 0.7
|
||||
percent_val = 0.2
|
||||
percent_test = 0.1
|
||||
percent_train = 0.9
|
||||
percent_val = 0.09
|
||||
percent_test = 0.01
|
||||
|
||||
seed = 42
|
||||
random.seed(seed)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
import os
|
||||
import time
|
||||
import argparse
|
||||
|
|
@ -10,12 +11,14 @@ from roi_seg_dataset import ROISegDataset
|
|||
import matplotlib.pyplot as plt
|
||||
|
||||
# ⚙️ Configurações
|
||||
MODELO = "oak-1"
|
||||
MODEL_NAME = "ervas_full"
|
||||
RESOLUCAO = (384, 384)
|
||||
ROI_INICIO = 0.0
|
||||
ROI_TAMANHO = 1.0
|
||||
save_path = os.path.join(MODELO, "backup", "fast_scnn", MODEL_NAME)
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
MODELO = config["camera"]
|
||||
MODEL_NAME = config["model_name"]
|
||||
RESOLUCAO = config["resolucao"]
|
||||
ROI_INICIO = config["roi_inicio"]
|
||||
ROI_TAMANHO = config["roi_tamanho"]
|
||||
save_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME)
|
||||
dataset_path = os.path.join(MODELO, "dataset")
|
||||
split_folder = "train"
|
||||
labelmap_path = os.path.join(dataset_path, "labelmap.txt")
|
||||
|
|
@ -27,12 +30,13 @@ def train(args):
|
|||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||
print(f"Device: {device}")
|
||||
|
||||
ds_train = ROISegDataset(os.path.join(dataset_path, "split", split_folder), save_path, ROI_INICIO, ROI_TAMANHO, RESOLUCAO[1], RESOLUCAO[0], labelmap_path)
|
||||
ds_train = ROISegDataset(os.path.join(dataset_path, "split", split_folder), save_path, ROI_INICIO, ROI_TAMANHO, RESOLUCAO[0], RESOLUCAO[1], labelmap_path)
|
||||
dl_train = DataLoader(ds_train, batch_size=batch_size, shuffle=True, num_workers=num_workers, pin_memory=True)
|
||||
|
||||
model = FastSCNN(num_classes=len(ds_train.classes)).to(device)
|
||||
criterion = nn.CrossEntropyLoss(ignore_index=ds_train.ignore_id)
|
||||
optimizer = optim.AdamW(model.parameters(), lr=args.lr, weight_decay=1e-4)
|
||||
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=args.epochs)
|
||||
scaler = torch.cuda.amp.GradScaler(enabled=args.amp)
|
||||
start_epoch = 1
|
||||
best_loss = 1e9
|
||||
|
|
@ -52,7 +56,7 @@ def train(args):
|
|||
# Caso seja apenas um .pth com model.state_dict() direto
|
||||
model.load_state_dict(checkpoint)
|
||||
|
||||
for epoch in range(1, args.epochs + 1):
|
||||
for epoch in range(start_epoch, args.epochs + 1):
|
||||
model.train()
|
||||
total_loss = 0
|
||||
t0 = time.time()
|
||||
|
|
@ -86,8 +90,9 @@ def train(args):
|
|||
|
||||
# Plot da curva de perda
|
||||
if epoch % 5 == 0 or epoch == args.epochs:
|
||||
x_epochs = list(range(start_epoch, start_epoch + len(loss_history)))
|
||||
plt.figure()
|
||||
plt.plot(range(start_epoch, epoch + 1), loss_history, marker="o", label="Loss de Treinamento")
|
||||
plt.plot(x_epochs, loss_history, marker="o", label="Loss de Treinamento")
|
||||
plt.xlabel("Época")
|
||||
plt.ylabel("Loss")
|
||||
plt.grid(True)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
import os
|
||||
import time
|
||||
import cv2
|
||||
|
|
@ -11,15 +12,17 @@ from fast_scnn import FastSCNN
|
|||
from utils import carregar_labelmap_completo, compute_roi_indices, converter_mask_ids_para_rgb, desenhar_legenda_horizontal, desenhar_legenda_vertical, resize_keep_width
|
||||
|
||||
# ⚙️ Configurações
|
||||
MODELO = "oak-1"
|
||||
MODEL_NAME = "ervas_full"
|
||||
RESOLUCAO = (384, 384)
|
||||
ROI_INICIO = 0.0
|
||||
ROI_TAMANHO = 1.0
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
MODELO = config["camera"]
|
||||
MODEL_NAME = config["model_name"]
|
||||
RESOLUCAO = config["resolucao"]
|
||||
ROI_INICIO = config["roi_inicio"]
|
||||
ROI_TAMANHO = config["roi_tamanho"]
|
||||
dataset_path = os.path.join(MODELO, "dataset")
|
||||
split_folder = "test"
|
||||
labelmap_path = os.path.join(dataset_path, "labelmap.txt")
|
||||
model_path = os.path.join(MODELO, "backup", "fast_scnn", MODEL_NAME, MODEL_NAME + "_best.pth")
|
||||
model_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME, MODEL_NAME + "_best.pth")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
|
@ -65,7 +68,7 @@ def main():
|
|||
y_fim, y_inicio = compute_roi_indices(H, ROI_INICIO, ROI_TAMANHO)
|
||||
|
||||
roi = frame[y_fim:y_inicio, 0:W]
|
||||
roi_resized = resize_keep_width(roi, RESOLUCAO[1], RESOLUCAO[0])
|
||||
roi_resized = resize_keep_width(roi, RESOLUCAO[0], RESOLUCAO[1], cv2.INTER_AREA)
|
||||
roi_norm = roi_resized.astype(np.float32) / 255.0
|
||||
|
||||
roi_tensor = torch.from_numpy(roi_norm).permute(2, 0, 1).unsqueeze(0).to(device)
|
||||
|
|
@ -123,8 +126,8 @@ def main():
|
|||
|
||||
img_roi = img_rgb[y_fim:y_inicio, 0:W]
|
||||
mask_roi = mask_gt[y_fim:y_inicio, 0:W]
|
||||
img_resized = resize_keep_width(img_roi, RESOLUCAO[1], RESOLUCAO[0])
|
||||
mask_resized = resize_keep_width(mask_roi, RESOLUCAO[1], RESOLUCAO[0])
|
||||
img_resized = resize_keep_width(img_roi, RESOLUCAO[0], RESOLUCAO[1], cv2.INTER_AREA)
|
||||
mask_resized = resize_keep_width(mask_roi, RESOLUCAO[0], RESOLUCAO[1], cv2.INTER_NEAREST)
|
||||
|
||||
img_norm = img_resized.astype(np.float32) / 255.0
|
||||
img_tensor = torch.from_numpy(img_norm).permute(2, 0, 1).unsqueeze(0).to(device)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
import json
|
||||
import os
|
||||
import torch
|
||||
from fast_scnn import FastSCNN
|
||||
from utils import carregar_labelmap_completo
|
||||
|
||||
# ⚙️ Configurações
|
||||
MODELO = "oak-1"
|
||||
MODEL_NAME = "ervas_full"
|
||||
RESOLUCAO = (384, 384)
|
||||
model_path = os.path.join(MODELO, "backup", "fast_scnn", MODEL_NAME)
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
MODELO = config["camera"]
|
||||
MODEL_NAME = config["model_name"]
|
||||
RESOLUCAO = config["resolucao"]
|
||||
model_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME)
|
||||
labelmap_path = os.path.join(MODELO, "dataset", "labelmap.txt")
|
||||
model_name = MODEL_NAME + "_best"
|
||||
|
||||
dummy_input = torch.randn(1, 3, RESOLUCAO[0], RESOLUCAO[1]) # (batch, channels, height, width)
|
||||
dummy_input = torch.randn(1, 3, RESOLUCAO[1], RESOLUCAO[0]) # (batch, channels, height, width)
|
||||
|
||||
_, _, classes, _ = carregar_labelmap_completo(labelmap_path)
|
||||
NUM_CLASSES = len(classes)
|
||||
|
|
@ -35,7 +38,8 @@ from openvino.tools.mo import convert_model
|
|||
from openvino.runtime import serialize
|
||||
ov_model = convert_model(
|
||||
input_model=os.path.join(model_path, model_name + ".onnx"),
|
||||
input_shape=[1, 3, RESOLUCAO[0], RESOLUCAO[1]],
|
||||
input_shape=[1, 3, RESOLUCAO[1], RESOLUCAO[0]],
|
||||
layout="NCHW",
|
||||
)
|
||||
serialize(
|
||||
ov_model,
|
||||
|
|
@ -50,6 +54,12 @@ blob_path = blobconverter.from_openvino(
|
|||
bin=os.path.join(model_path, model_name + ".bin"),
|
||||
data_type="FP16",
|
||||
shaves=6,
|
||||
output_dir=model_path
|
||||
output_dir=model_path,
|
||||
compile_params=[
|
||||
"-ip U8", # entrada em bytes; compila a conversão interna p/ FP16
|
||||
"--mean_values=[123.675,116.28,103.53]",
|
||||
"--scale_values=[58.395,57.12,57.375]",
|
||||
#"--reverse_input_channels" # pq você treinou em RGB
|
||||
],
|
||||
)
|
||||
print(f"Blob salvo em: {blob_path}")
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import json
|
||||
import os
|
||||
import depthai as dai
|
||||
import numpy as np
|
||||
|
|
@ -6,12 +7,14 @@ import time
|
|||
from utils import converter_mask_ids_para_rgb, carregar_labelmap_completo
|
||||
|
||||
# ⚙️ Configurações
|
||||
MODELO = "oak-1"
|
||||
MODEL_NAME = "ervas_full"
|
||||
RESOLUCAO = (384, 384)
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
MODELO = config["camera"]
|
||||
MODEL_NAME = "ervas_medium" #config["model_name"]
|
||||
RESOLUCAO = config["resolucao"]
|
||||
ROI_INICIO = 0.0
|
||||
ROI_TAMANHO = 1.0
|
||||
blob_path = os.path.join(MODELO, "backup", "fast_scnn", MODEL_NAME, MODEL_NAME + "_best_openvino_2022.1_6shave.blob")
|
||||
blob_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME, MODEL_NAME + "_best_openvino_2022.1_6shave.blob")
|
||||
labelmap_path = os.path.join(MODELO, "dataset", "labelmap.txt")
|
||||
model_name = MODEL_NAME + "_best"
|
||||
|
||||
|
|
@ -38,10 +41,10 @@ y1 = 1.0 - (ROI_INICIO + ROI_TAMANHO)
|
|||
y2 = 1.0 - ROI_INICIO
|
||||
|
||||
manip.initialConfig.setCropRect(0.0, y1, 1.0, y2)
|
||||
manip.initialConfig.setResize(RESOLUCAO[1], RESOLUCAO[0])
|
||||
|
||||
manip.initialConfig.setResize(RESOLUCAO[0], RESOLUCAO[1])
|
||||
manip.initialConfig.setFrameType(dai.RawImgFrame.Type.RGB888p)
|
||||
cam.preview.link(manip.inputImage)
|
||||
manip.initialConfig.setKeepAspectRatio(False)
|
||||
cam.video.link(manip.inputImage)
|
||||
|
||||
# Neural network
|
||||
nn = pipeline.createNeuralNetwork()
|
||||
|
|
@ -51,13 +54,16 @@ manip.out.link(nn.input)
|
|||
# Saída RGB para overlay (sem redimensionar)
|
||||
xout_rgb = pipeline.createXLinkOut()
|
||||
xout_rgb.setStreamName("rgb")
|
||||
cam.preview.link(xout_rgb.input)
|
||||
cam.video.link(xout_rgb.input)
|
||||
|
||||
# Saída NN
|
||||
xout_nn = pipeline.createXLinkOut()
|
||||
xout_nn.setStreamName("nn")
|
||||
nn.out.link(xout_nn.input)
|
||||
|
||||
# E garanta que o video stream é 16:9
|
||||
#cam.setVideoSize(384, 384) # ou 1280x720
|
||||
|
||||
# Rodar pipeline
|
||||
with dai.Device(pipeline) as device:
|
||||
rgb_queue = device.getOutputQueue("rgb", maxSize=1, blocking=False)
|
||||
|
|
@ -66,45 +72,51 @@ with dai.Device(pipeline) as device:
|
|||
print("Rodando inferência na OAK... Pressione 'q' para sair.")
|
||||
prev_time = time.time()
|
||||
|
||||
H, W = RESOLUCAO[1], RESOLUCAO[0]
|
||||
|
||||
in_rgb = rgb_queue.get()
|
||||
frame = in_rgb.getCvFrame()
|
||||
frame_h, frame_w = frame.shape[:2]
|
||||
|
||||
y_start = int(y1 * frame_h)
|
||||
y_end = int(y2 * frame_h)
|
||||
roi_h = y_end - y_start
|
||||
roi_w = frame_w
|
||||
|
||||
pred_ids = np.empty((H, W), dtype=np.uint8)
|
||||
pred_rgb = np.empty((H, W, 3), dtype=np.uint8)
|
||||
overlay = np.empty((H, W, 3), dtype=np.uint8)
|
||||
|
||||
lut = np.zeros((256, 3), dtype=np.uint8)
|
||||
for i, color in enumerate(colormap_rgb):
|
||||
lut[i] = color
|
||||
lut[IGNORE_ID] = (255, 255, 255)
|
||||
|
||||
while True:
|
||||
in_rgb = rgb_queue.get()
|
||||
in_nn = nn_queue.get()
|
||||
|
||||
# RGB frame da câmera
|
||||
frame = in_rgb.getCvFrame()
|
||||
|
||||
# Inferência - saída é um vetor flat [num_classes * H * W]
|
||||
out = in_nn.getFirstLayerFp16()
|
||||
out_np = np.array(out, dtype=np.float32).reshape((NUM_CLASSES, RESOLUCAO[1], RESOLUCAO[0]))
|
||||
out_raw = in_nn.getFirstLayerFp16()
|
||||
arr16 = np.frombuffer(np.asarray(out_raw, dtype=np.float16), dtype=np.float16)
|
||||
arr16 = arr16.reshape(NUM_CLASSES, H, W)
|
||||
pred_ids = arr16.argmax(axis=0).astype(np.uint8, copy=False)
|
||||
|
||||
# Pega o índice da classe com maior probabilidade por pixel
|
||||
pred_ids = np.argmax(out_np, axis=0).astype(np.uint8)
|
||||
|
||||
# Converter para RGB bonitão
|
||||
pred_rgb = converter_mask_ids_para_rgb(pred_ids, colormap_rgb, IGNORE_ID)
|
||||
roi_h = int((y2 - y1) * frame.shape[0])
|
||||
roi_w = frame.shape[1]
|
||||
pred_rgb_resized = cv2.resize(pred_rgb, (roi_w, roi_h), interpolation=cv2.INTER_NEAREST)
|
||||
y_start = int(y1 * frame.shape[0])
|
||||
y_end = y_start + roi_h
|
||||
y_start = max(0, min(frame.shape[0], y_start))
|
||||
y_end = max(0, min(frame.shape[0], y_end))
|
||||
overlay = frame.copy()
|
||||
overlay[y_start:y_end, 0:roi_w] = cv2.addWeighted(
|
||||
frame[y_start:y_end, 0:roi_w], 0.4, pred_rgb_resized, 0.6, 0
|
||||
)
|
||||
pred_rgb[:] = lut[pred_ids]
|
||||
|
||||
frame = in_rgb.getCvFrame()
|
||||
cv2.resize(frame, (W, H), interpolation=cv2.INTER_AREA, dst=overlay)
|
||||
cv2.addWeighted(overlay, 0.4, pred_rgb, 0.6, 0, dst=overlay)
|
||||
|
||||
# FPS
|
||||
now = time.time()
|
||||
fps = 1.0 / (now - prev_time)
|
||||
fps_inst = 1.0 / (now - prev_time)
|
||||
prev_time = now
|
||||
cv2.putText(overlay, f"FPS: {fps:.1f}", (10, 30),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
|
||||
|
||||
# Redimensiona para tela cheia (por exemplo 1280x720 ou tela do usuário)
|
||||
overlay_display = cv2.resize(overlay, (1280, 720))
|
||||
cv2.imshow("Segmentação - OAK (on-board)", cv2.cvtColor(overlay_display, cv2.COLOR_RGB2BGR))
|
||||
fps = 0.9 * fps + 0.1 * fps_inst if 'fps' in locals() else fps_inst
|
||||
|
||||
cv2.putText(overlay, f"FPS: {fps:.1f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
|
||||
#cv2.imshow("Segmentacao - OAK (on-board)", cv2.cvtColor(overlay, cv2.COLOR_RGB2BGR))
|
||||
cv2.imshow("Segmentacao - OAK (on-board)", overlay)
|
||||
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||
break
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"camera": "oak-1",
|
||||
"modelo": "fast_scnn",
|
||||
"model_name": "ervas_medium_new",
|
||||
"resolucao": [512, 288],
|
||||
"roi_inicio": 0.0,
|
||||
"roi_tamanho": 1.0
|
||||
}
|
||||
Binary file not shown.
|
|
@ -44,8 +44,8 @@ class ROISegDataset(Dataset):
|
|||
img_roi = img_rgb[y_fim:y_inicio, 0:W]
|
||||
msk_roi = msk_grayscale[y_fim:y_inicio, 0:W]
|
||||
|
||||
img_in = resize_keep_width(img_roi, self.input_w, self.min_input_h)
|
||||
msk_ids = resize_keep_width(msk_roi, self.input_w, self.min_input_h)
|
||||
img_in = resize_keep_width(img_roi, self.input_w, self.min_input_h, cv2.INTER_AREA)
|
||||
msk_ids = resize_keep_width(msk_roi, self.input_w, self.min_input_h, cv2.INTER_NEAREST)
|
||||
|
||||
valores_validos = list(range(len(self.classes))) + [255]
|
||||
msk_ids[np.isin(msk_ids, valores_validos, invert=True)] = self.ignore_id # converte inválidos em ignore
|
||||
|
|
|
|||
|
|
@ -37,22 +37,30 @@ def carregar_labelmap_completo(caminho):
|
|||
return cor_para_id, cores_rgb, id_para_nome, ignore_rgb
|
||||
|
||||
def converter_mask_rgb_para_ids(img_rgb, mapa_rgb, ignore_id):
|
||||
h, w, _ = img_rgb.shape
|
||||
mask = np.ones((h, w), dtype=np.uint8) * ignore_id # Inicializa como ignore
|
||||
# Cria um mapa 256^3 para IDs (usa int32 para indexar)
|
||||
lut = np.full((256**3,), ignore_id, dtype=np.uint8)
|
||||
|
||||
for cor, classe_id in mapa_rgb.items():
|
||||
r, g, b = cor
|
||||
cond = (img_rgb[:,:,0]==r) & (img_rgb[:,:,1]==g) & (img_rgb[:,:,2]==b)
|
||||
mask[cond] = classe_id
|
||||
# Pixels brancos (ou ignore_bgr) continuam como 255
|
||||
return mask
|
||||
lut[(r << 16) + (g << 8) + b] = classe_id
|
||||
|
||||
def converter_mask_ids_para_rgb(mask_ids: np.ndarray, mapa_rgb: dict, ignore_id: int = 255) -> np.ndarray:
|
||||
h, w = mask_ids.shape
|
||||
rgb = np.zeros((h, w, 3), dtype=np.uint8)
|
||||
for class_id, color in enumerate(mapa_rgb):
|
||||
rgb[mask_ids == class_id] = color
|
||||
rgb[mask_ids == ignore_id] = [255, 255, 255]
|
||||
return rgb
|
||||
# Converte RGB para índice único
|
||||
flat_idx = (img_rgb[:,:,0].astype(np.int32) << 16) + \
|
||||
(img_rgb[:,:,1].astype(np.int32) << 8) + \
|
||||
img_rgb[:,:,2].astype(np.int32)
|
||||
|
||||
# Aplica LUT vetorizada
|
||||
return lut[flat_idx]
|
||||
|
||||
def converter_mask_ids_para_rgb(mask_ids: np.ndarray, colormap_rgb: list, ignore_id: int = 255) -> np.ndarray:
|
||||
# Criar lookup table (256 cores possíveis)
|
||||
lut = np.zeros((256, 3), dtype=np.uint8)
|
||||
for i, color in enumerate(colormap_rgb):
|
||||
lut[i] = color
|
||||
lut[ignore_id] = (255, 255, 255)
|
||||
|
||||
# Aplicar LUT direto (vetorizado)
|
||||
return lut[mask_ids]
|
||||
|
||||
def desenhar_legenda_vertical(colormap_rgb, classes, largura=200):
|
||||
"""
|
||||
|
|
@ -103,9 +111,9 @@ def compute_roi_indices(H: int, zona_inicio: float, faixa_atuacao: float):
|
|||
y_fim = max(0, y_inicio - 1)
|
||||
return y_fim, y_inicio
|
||||
|
||||
def resize_keep_width(img: np.ndarray, new_w: int, min_h: int) -> np.ndarray:
|
||||
def resize_keep_width(img: np.ndarray, new_w: int, min_h: int, interpolation: int) -> np.ndarray:
|
||||
h, w = img.shape[:2]
|
||||
new_h = int(round(new_w * (h / w)))
|
||||
if min_h is not None and new_h < min_h:
|
||||
new_h = min_h
|
||||
return cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
|
||||
return cv2.resize(img, (new_w, new_h), interpolation=interpolation)
|
||||
|
|
|
|||
Loading…
Reference in New Issue