ajustes ia sonar
This commit is contained in:
parent
2266ddc84d
commit
d1e8828e0f
Binary file not shown.
Binary file not shown.
|
|
@ -136,6 +136,7 @@ namespace AgroBase.Forms.Operacoes
|
||||||
picsCamSoloSeg = new List<PictureBox>();
|
picsCamSoloSeg = new List<PictureBox>();
|
||||||
picsCamSoloOverlay = new List<PictureBox>();
|
picsCamSoloOverlay = new List<PictureBox>();
|
||||||
flwCameras.Controls.Clear();
|
flwCameras.Controls.Clear();
|
||||||
|
LogsCam_Solo = new List<List<CameraWorkerItemModel>>();
|
||||||
foreach (string cam in data.cam_solo)
|
foreach (string cam in data.cam_solo)
|
||||||
{
|
{
|
||||||
var log = JsonConvert.DeserializeObject<List<CameraWorkerItemModel>>(OperacaoModel.DeserializarDadosOperacao(ofd.FileName, cam)).ToList();
|
var log = JsonConvert.DeserializeObject<List<CameraWorkerItemModel>>(OperacaoModel.DeserializarDadosOperacao(ofd.FileName, cam)).ToList();
|
||||||
|
|
|
||||||
|
|
@ -136,11 +136,14 @@ namespace AgroBase.Models
|
||||||
{
|
{
|
||||||
// Para gráficos de linha
|
// Para gráficos de linha
|
||||||
var _momentos = Momentos.GetRange(startIdx, endIdx - startIdx + 1).ToList();
|
var _momentos = Momentos.GetRange(startIdx, endIdx - startIdx + 1).ToList();
|
||||||
|
if (Serie.Valores.Count() > 0)
|
||||||
|
{
|
||||||
var _valores = Serie.Valores.GetRange(startIdx, endIdx - startIdx + 1).ToList();
|
var _valores = Serie.Valores.GetRange(startIdx, endIdx - startIdx + 1).ToList();
|
||||||
PopularSerieGraficoLinha(chart, Serie.Titulo, _momentos, _valores, Serie.Visivel, Serie.MostrarValor);
|
PopularSerieGraficoLinha(chart, Serie.Titulo, _momentos, _valores, Serie.Visivel, Serie.MostrarValor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Erro ao atualizar dados do grafico: {ex.Message}");
|
Console.WriteLine($"Erro ao atualizar dados do grafico: {ex.Message}");
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@ namespace AgroBase.Services.Operadores
|
||||||
("camera_caminho_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CameraCaminho?.Id ?? ""),
|
("camera_caminho_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CameraCaminho?.Id ?? ""),
|
||||||
("camera_solo_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CamerasSolo?.FirstOrDefault()?.Id ?? ""),
|
("camera_solo_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CamerasSolo?.FirstOrDefault()?.Id ?? ""),
|
||||||
("path_ia_model_ruas", VersionamentoService.ArquivoModeloStreetDetector.CaminhoCompleto),
|
("path_ia_model_ruas", VersionamentoService.ArquivoModeloStreetDetector.CaminhoCompleto),
|
||||||
|
("path_ia_labelmap_ruas", VersionamentoService.ArquivoLabelmapStreetDetector.CaminhoCompleto),
|
||||||
("path_ia_model_ervas", VersionamentoService.ArquivoModeloWeedDetector.CaminhoCompleto),
|
("path_ia_model_ervas", VersionamentoService.ArquivoModeloWeedDetector.CaminhoCompleto),
|
||||||
("path_ia_labelmap_ervas", VersionamentoService.ArquivoModeloLabelmapWeedDetector.CaminhoCompleto),
|
("path_ia_labelmap_ervas", VersionamentoService.ArquivoModeloLabelmapWeedDetector.CaminhoCompleto),
|
||||||
("angulo_roll_max", VariaveisEquipamento.AnguloInclinacaoRollMax),
|
("angulo_roll_max", VariaveisEquipamento.AnguloInclinacaoRollMax),
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,17 @@ namespace AgroBase.Services
|
||||||
{
|
{
|
||||||
lock (_ArquivoLock)
|
lock (_ArquivoLock)
|
||||||
{
|
{
|
||||||
return _ArquivosVersionados.FirstOrDefault(x => x.TipoArquivo == TipoArquivoVersionado.ModeloIA_StreetDetector);
|
return _ArquivosVersionados.Where(x => x.TipoArquivo == TipoArquivoVersionado.ModeloIA_StreetDetector).FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static VersaoArquivoModel ArquivoLabelmapStreetDetector
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_ArquivoLock)
|
||||||
|
{
|
||||||
|
return _ArquivosVersionados.Where(x => x.TipoArquivo == TipoArquivoVersionado.ModeloIA_StreetDetector).Skip(1).FirstOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +48,7 @@ namespace AgroBase.Services
|
||||||
{
|
{
|
||||||
lock (_ArquivoLock)
|
lock (_ArquivoLock)
|
||||||
{
|
{
|
||||||
return _ArquivosVersionados.FirstOrDefault(x => x.TipoArquivo == TipoArquivoVersionado.ModeloIA_WeedDetector);
|
return _ArquivosVersionados.Where(x => x.TipoArquivo == TipoArquivoVersionado.ModeloIA_WeedDetector).FirstOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +58,7 @@ namespace AgroBase.Services
|
||||||
{
|
{
|
||||||
lock (_ArquivoLock)
|
lock (_ArquivoLock)
|
||||||
{
|
{
|
||||||
return _ArquivosVersionados.Skip(1).FirstOrDefault(x => x.TipoArquivo == TipoArquivoVersionado.ModeloIA_WeedDetector);
|
return _ArquivosVersionados.Where(x => x.TipoArquivo == TipoArquivoVersionado.ModeloIA_WeedDetector).Skip(1).FirstOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -29,5 +29,5 @@
|
||||||
"top_topics_and_observing_domains": [ ]
|
"top_topics_and_observing_domains": [ ]
|
||||||
} ],
|
} ],
|
||||||
"hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642",
|
"hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642",
|
||||||
"next_scheduled_calculation_time": "13399758321058597"
|
"next_scheduled_calculation_time": "13399758321058748"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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/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/11-17:15:07.450 43b4 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/11-17:15:07.459 43b4 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
|
2025/08/11-17:15:07.463 43b4 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/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/11-16:38:48.533 8fe4 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/11-16:38:48.540 8fe4 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
|
2025/08/11-16:38:48.543 8fe4 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":"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"}}}
|
{"net":{"http_server_properties":{"servers":[{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13399499518686465","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":95712},"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"}}}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,3 @@
|
||||||
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/11-17:17:21.484 43b4 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/11-17:17:21.485 43b4 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
|
2025/08/11-17:17:21.489 43b4 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/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/11-16:44:31.240 8fe4 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/11-16:44:31.242 8fe4 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
|
2025/08/11-16:44:31.246 8fe4 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/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/11-17:15:07.352 5f14 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/11-17:15:07.354 5f14 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
|
2025/08/11-17:15:07.354 5f14 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/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/11-16:38:48.452 418c 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/11-16:38:48.453 418c 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
|
2025/08/11-16:38:48.454 418c 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.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -21,14 +21,14 @@
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"Arquivo": "model",
|
"Arquivo": "model",
|
||||||
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
|
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
|
||||||
"Extensao": ".onnx",
|
"Extensao": ".blob",
|
||||||
"Versao": "1_1",
|
"Versao": "1_1",
|
||||||
"TipoArquivo": 0,
|
"TipoArquivo": 0,
|
||||||
"ArquivoDownload": "street_detector_model-1_1.onnx",
|
"ArquivoDownload": "street_detector_model-1_1.blob",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"Arquivo": "labelmap",
|
"Arquivo": "model",
|
||||||
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
|
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
|
||||||
"Extensao": ".txt",
|
"Extensao": ".txt",
|
||||||
"Versao": "1_1",
|
"Versao": "1_1",
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"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]]}}]}
|
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"Id":"1","Name":"11_08_2025_16_37_30_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],[0.0,0.0]]}}]}
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
<meta name="viewport" content="width=device-width,
|
<meta name="viewport" content="width=device-width,
|
||||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<style>
|
<style>
|
||||||
#map_7166a4145b3904abbc4abfe2859cddb9 {
|
#map_7792e3f9a50d24693529ab60471511c5 {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100.0%;
|
width: 100.0%;
|
||||||
height: 100.0%;
|
height: 100.0%;
|
||||||
|
|
@ -54,14 +54,14 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
||||||
<div class="folium-map" id="map_7166a4145b3904abbc4abfe2859cddb9" ></div>
|
<div class="folium-map" id="map_7792e3f9a50d24693529ab60471511c5" ></div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
var map_7166a4145b3904abbc4abfe2859cddb9 = L.map(
|
var map_7792e3f9a50d24693529ab60471511c5 = L.map(
|
||||||
"map_7166a4145b3904abbc4abfe2859cddb9",
|
"map_7792e3f9a50d24693529ab60471511c5",
|
||||||
{
|
{
|
||||||
center: [0.0, 0.0],
|
center: [0.0, 0.0],
|
||||||
crs: L.CRS.EPSG3857,
|
crs: L.CRS.EPSG3857,
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var tile_layer_9627b4b85e8fb117b7e79c7fddf9872d = L.tileLayer(
|
var tile_layer_a04e5e8300b6eacc885e71a463b29158 = L.tileLayer(
|
||||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||||
{
|
{
|
||||||
"minZoom": 0,
|
"minZoom": 0,
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
tile_layer_9627b4b85e8fb117b7e79c7fddf9872d.addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
tile_layer_a04e5e8300b6eacc885e71a463b29158.addTo(map_7792e3f9a50d24693529ab60471511c5);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@
|
||||||
}
|
}
|
||||||
trajeto_json_add({"features": []});
|
trajeto_json_add({"features": []});
|
||||||
|
|
||||||
trajeto_json.addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
trajeto_json.addTo(map_7792e3f9a50d24693529ab60471511c5);
|
||||||
|
|
||||||
function adicionarGeometria(novaGeometria) {
|
function adicionarGeometria(novaGeometria) {
|
||||||
trajeto_json.addData(novaGeometria);
|
trajeto_json.addData(novaGeometria);
|
||||||
|
|
@ -179,9 +179,9 @@
|
||||||
|
|
||||||
var marcadorEquipamento = L.marker([0, 0], {
|
var marcadorEquipamento = L.marker([0, 0], {
|
||||||
icon: customIcon
|
icon: customIcon
|
||||||
}).addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
}).addTo(map_7792e3f9a50d24693529ab60471511c5);
|
||||||
|
|
||||||
var marcadorBase = L.marker([0, 0], {}).addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
var marcadorBase = L.marker([0, 0], {}).addTo(map_7792e3f9a50d24693529ab60471511c5);
|
||||||
var icon = L.AwesomeMarkers.icon(
|
var icon = L.AwesomeMarkers.icon(
|
||||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||||
);
|
);
|
||||||
|
|
@ -246,7 +246,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foco) {
|
if (foco) {
|
||||||
map_7166a4145b3904abbc4abfe2859cddb9.setView(novaPosicao, map_7166a4145b3904abbc4abfe2859cddb9.getZoom());
|
map_7792e3f9a50d24693529ab60471511c5.setView(novaPosicao, map_7792e3f9a50d24693529ab60471511c5.getZoom());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +268,7 @@
|
||||||
marcadorDinamico.setRotationAngle(angulo);
|
marcadorDinamico.setRotationAngle(angulo);
|
||||||
|
|
||||||
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
|
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
|
||||||
map_7166a4145b3904abbc4abfe2859cddb9.setView(novaPosicao, map_7166a4145b3904abbc4abfe2859cddb9.getZoom());*/
|
map_7792e3f9a50d24693529ab60471511c5.setView(novaPosicao, map_7792e3f9a50d24693529ab60471511c5.getZoom());*/
|
||||||
});
|
});
|
||||||
|
|
||||||
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {
|
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
<meta name="viewport" content="width=device-width,
|
<meta name="viewport" content="width=device-width,
|
||||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<style>
|
<style>
|
||||||
#map_f21522cbe2b071ceb21cf0d5c95f5a21 {
|
#map_b86da2c0cf19440a67c00659ca7d57e9 {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100.0%;
|
width: 100.0%;
|
||||||
height: 100.0%;
|
height: 100.0%;
|
||||||
|
|
@ -54,14 +54,14 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
||||||
<div class="folium-map" id="map_f21522cbe2b071ceb21cf0d5c95f5a21" ></div>
|
<div class="folium-map" id="map_b86da2c0cf19440a67c00659ca7d57e9" ></div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
var map_f21522cbe2b071ceb21cf0d5c95f5a21 = L.map(
|
var map_b86da2c0cf19440a67c00659ca7d57e9 = L.map(
|
||||||
"map_f21522cbe2b071ceb21cf0d5c95f5a21",
|
"map_b86da2c0cf19440a67c00659ca7d57e9",
|
||||||
{
|
{
|
||||||
center: [0.0, 0.0],
|
center: [0.0, 0.0],
|
||||||
crs: L.CRS.EPSG3857,
|
crs: L.CRS.EPSG3857,
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var tile_layer_1ca71298385dd222bb7746161b7dbce5 = L.tileLayer(
|
var tile_layer_b64ed6ac3009000233329a32f2decf43 = L.tileLayer(
|
||||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||||
{
|
{
|
||||||
"minZoom": 0,
|
"minZoom": 0,
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
tile_layer_1ca71298385dd222bb7746161b7dbce5.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
tile_layer_b64ed6ac3009000233329a32f2decf43.addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
}*/
|
}*/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function geo_json_b0da161aca2ab03761c9445da3d471fe_onEachFeature(feature, layer) {
|
function geo_json_92428d205e1566c4dea13c3b7eb4954d_onEachFeature(feature, layer) {
|
||||||
|
|
||||||
layer.on({
|
layer.on({
|
||||||
|
|
||||||
|
|
@ -148,23 +148,23 @@
|
||||||
}*/
|
}*/
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
var geo_json_b0da161aca2ab03761c9445da3d471fe = L.geoJson(null, {
|
var geo_json_92428d205e1566c4dea13c3b7eb4954d = L.geoJson(null, {
|
||||||
onEachFeature: geo_json_b0da161aca2ab03761c9445da3d471fe_onEachFeature,
|
onEachFeature: geo_json_92428d205e1566c4dea13c3b7eb4954d_onEachFeature,
|
||||||
|
|
||||||
...{
|
...{
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function geo_json_b0da161aca2ab03761c9445da3d471fe_add (data) {
|
function geo_json_92428d205e1566c4dea13c3b7eb4954d_add (data) {
|
||||||
geo_json_b0da161aca2ab03761c9445da3d471fe
|
geo_json_92428d205e1566c4dea13c3b7eb4954d
|
||||||
.addData(data);
|
.addData(data);
|
||||||
}
|
}
|
||||||
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_92428d205e1566c4dea13c3b7eb4954d_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], [0.0, 0.0]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 0.0, "Dist2": 0.0, "Id": "1", "Length": 0.0, "Name": "11_08_2025_16_37_30_Manual"}, "type": "Feature"}], "type": "FeatureCollection"});
|
||||||
geo_json_b0da161aca2ab03761c9445da3d471fe.setStyle(function(feature) {return feature.properties.style;});
|
geo_json_92428d205e1566c4dea13c3b7eb4954d.setStyle(function(feature) {return feature.properties.style;});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
geo_json_b0da161aca2ab03761c9445da3d471fe.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
geo_json_92428d205e1566c4dea13c3b7eb4954d.addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -185,7 +185,7 @@
|
||||||
}
|
}
|
||||||
trajeto_json_add({"features": []});
|
trajeto_json_add({"features": []});
|
||||||
|
|
||||||
trajeto_json.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
trajeto_json.addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||||
|
|
||||||
function adicionarGeometria(novaGeometria) {
|
function adicionarGeometria(novaGeometria) {
|
||||||
trajeto_json.addData(novaGeometria);
|
trajeto_json.addData(novaGeometria);
|
||||||
|
|
@ -243,7 +243,7 @@
|
||||||
}
|
}
|
||||||
trajeto_dinamico_json_add({"features": []});
|
trajeto_dinamico_json_add({"features": []});
|
||||||
|
|
||||||
trajeto_dinamico_json.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
trajeto_dinamico_json.addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||||
|
|
||||||
function adicionarGeometriaDinamica(novaGeometria) {
|
function adicionarGeometriaDinamica(novaGeometria) {
|
||||||
trajeto_dinamico_json.addData(novaGeometria);
|
trajeto_dinamico_json.addData(novaGeometria);
|
||||||
|
|
@ -296,9 +296,9 @@
|
||||||
|
|
||||||
var marcadorEquipamento = L.marker([0, 0], {
|
var marcadorEquipamento = L.marker([0, 0], {
|
||||||
icon: customIcon
|
icon: customIcon
|
||||||
}).addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
}).addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||||
|
|
||||||
var marcadorBase = L.marker([0, 0], {}).addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
var marcadorBase = L.marker([0, 0], {}).addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||||
var icon = L.AwesomeMarkers.icon(
|
var icon = L.AwesomeMarkers.icon(
|
||||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||||
);
|
);
|
||||||
|
|
@ -380,7 +380,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foco) {
|
if (foco) {
|
||||||
map_f21522cbe2b071ceb21cf0d5c95f5a21.setView(novaPosicao, map_f21522cbe2b071ceb21cf0d5c95f5a21.getZoom());
|
map_b86da2c0cf19440a67c00659ca7d57e9.setView(novaPosicao, map_b86da2c0cf19440a67c00659ca7d57e9.getZoom());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +397,7 @@
|
||||||
function atualizarSelecaoRuas(selecionadas) {
|
function atualizarSelecaoRuas(selecionadas) {
|
||||||
selecionadas = JSON.parse(selecionadas);
|
selecionadas = JSON.parse(selecionadas);
|
||||||
RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : [];
|
RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : [];
|
||||||
geo_json_b0da161aca2ab03761c9445da3d471fe.eachLayer(function (layer) {
|
geo_json_92428d205e1566c4dea13c3b7eb4954d.eachLayer(function (layer) {
|
||||||
if (RuasSelecionadas.includes(parseInt(layer.feature.id))) {
|
if (RuasSelecionadas.includes(parseInt(layer.feature.id))) {
|
||||||
layer.setStyle({ color: 'blue' });
|
layer.setStyle({ color: 'blue' });
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -192,6 +192,7 @@ class CameraOak:
|
||||||
self.mostrar_log(f"[WARN] Falha ao montar pipeline imu: {e}")
|
self.mostrar_log(f"[WARN] Falha ao montar pipeline imu: {e}")
|
||||||
|
|
||||||
if self.modelo_ia_onboard is not None:
|
if self.modelo_ia_onboard is not None:
|
||||||
|
try:
|
||||||
from shared.utils import carregar_labelmap_completo
|
from shared.utils import carregar_labelmap_completo
|
||||||
|
|
||||||
# Carregar mapa de cores
|
# Carregar mapa de cores
|
||||||
|
|
@ -222,6 +223,8 @@ class CameraOak:
|
||||||
nn.out.link(xout_nn.input)
|
nn.out.link(xout_nn.input)
|
||||||
|
|
||||||
self.mostrar_log("Pipeline de segmentação onboard criado")
|
self.mostrar_log("Pipeline de segmentação onboard criado")
|
||||||
|
except Exception as e:
|
||||||
|
self.mostrar_log(f"[WARN] Falha ao montar pipeline IA Onboard: {e}")
|
||||||
|
|
||||||
return pipeline
|
return pipeline
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -12,16 +12,17 @@ from visual_worker.processamento.analise_solo import AnaliseSoloManager
|
||||||
from visual_worker.processamento.analise_anomalias import AnaliseAnomaliasManager
|
from visual_worker.processamento.analise_anomalias import AnaliseAnomaliasManager
|
||||||
from visual_worker.processamento.radar_top_down import Radar2DManager
|
from visual_worker.processamento.radar_top_down import Radar2DManager
|
||||||
from visual_worker.processamento.segmentacao_semantica import ClassesSegmentacao, SegmentacaoManager
|
from visual_worker.processamento.segmentacao_semantica import ClassesSegmentacao, SegmentacaoManager
|
||||||
from shared.enums import ManagerWorkerCommandType, ModoOperacao, StatusModulo, T_Code, CameraFrameType
|
from shared.enums import StatusModulo, T_Code, CameraFrameType
|
||||||
from shared.utils import analisar_linhas_por_profundidade, decode_image_base64, encode_image_base64
|
from shared.utils import analisar_linhas_por_profundidade, decode_image_base64, encode_image_base64, fazer_overlay
|
||||||
from shared.gps_handler import GPSHandler
|
from shared.gps_handler import GPSHandler
|
||||||
from camera_worker.camera_oak import CameraOak
|
from camera_worker.camera_oak import CameraOak
|
||||||
from shared.contexto_global_redis import CmdKey, ContextoGlobalRedis, CtxKey
|
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
|
||||||
|
|
||||||
class CameraManager:
|
class CameraManager:
|
||||||
def __init__(self, mostrar_log):
|
def __init__(self, mostrar_log):
|
||||||
self.mostrar_log = mostrar_log
|
self.mostrar_log = mostrar_log
|
||||||
self.mx_id = None
|
self.mx_id = None
|
||||||
|
self.tempo_saude = 10
|
||||||
self.reiniciar_status()
|
self.reiniciar_status()
|
||||||
|
|
||||||
def reiniciar_status(self):
|
def reiniciar_status(self):
|
||||||
|
|
@ -34,6 +35,11 @@ class CameraManager:
|
||||||
self._ultima_analise_segmentacao = {}
|
self._ultima_analise_segmentacao = {}
|
||||||
self._ultima_analise_matriz_confianca = {}
|
self._ultima_analise_matriz_confianca = {}
|
||||||
self._ultima_analise_matriz_custo = {}
|
self._ultima_analise_matriz_custo = {}
|
||||||
|
self._ultimo_rgb_frame = None
|
||||||
|
self._ultimo_depth_frame = None
|
||||||
|
self._ts_segmentacao_anterior = 0
|
||||||
|
self._pool = ThreadPoolExecutor(max_workers=6)
|
||||||
|
self._ultima_saude_ts = 0
|
||||||
|
|
||||||
def inicializar(self, mx_id):
|
def inicializar(self, mx_id):
|
||||||
if self.iniciando:
|
if self.iniciando:
|
||||||
|
|
@ -51,8 +57,11 @@ class CameraManager:
|
||||||
|
|
||||||
self.mx_id = mx_id
|
self.mx_id = mx_id
|
||||||
|
|
||||||
|
from visual_worker.config import load_config
|
||||||
|
camera_config = load_config()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
nova = CameraOak(self.mostrar_log, mx_id)
|
nova = CameraOak(self.mostrar_log, mx_id, modelo_ia_onboard=camera_config)
|
||||||
if nova.iniciado:
|
if nova.iniciado:
|
||||||
self.camera = nova
|
self.camera = nova
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -63,14 +72,13 @@ class CameraManager:
|
||||||
self.mostrar_log(f"❌ Camera com ID {mx_id} não iniciada.")
|
self.mostrar_log(f"❌ Camera com ID {mx_id} não iniciada.")
|
||||||
else:
|
else:
|
||||||
self.mostrar_log(f"📷 Camera visual selecionada: {self.camera.modelo} - {self.camera.mx_id}")
|
self.mostrar_log(f"📷 Camera visual selecionada: {self.camera.modelo} - {self.camera.mx_id}")
|
||||||
self.grid_ref = None
|
self.grid_ref_shape = (15, 10)
|
||||||
self.grid_ref_shape = (20, 20)
|
self.grid_ref = self._gerar_grid_referencia_geometrico()
|
||||||
self.depth_referencia = None
|
self.depth_referencia = None
|
||||||
self.setores_referencia = None
|
self.setores_referencia = None
|
||||||
self.anomalias_manager = AnaliseAnomaliasManager()
|
self.anomalias_manager = AnaliseAnomaliasManager()
|
||||||
self.solo_manager = AnaliseSoloManager()
|
self.solo_manager = AnaliseSoloManager()
|
||||||
arquivo_modelo = ContextoGlobalRedis.get_equipamento().get("path_ia_model_ruas")
|
self.segmentacao_manager = SegmentacaoManager(self.camera.colormap_rgb, self.camera.classes)
|
||||||
self.segmentacao_manager = SegmentacaoManager(arquivo_modelo)
|
|
||||||
self.radar_manager = Radar2DManager()
|
self.radar_manager = Radar2DManager()
|
||||||
self.operante = True
|
self.operante = True
|
||||||
self._timestamp_analise = None
|
self._timestamp_analise = None
|
||||||
|
|
@ -94,16 +102,32 @@ class CameraManager:
|
||||||
self._analisando_matriz_confianca = False
|
self._analisando_matriz_confianca = False
|
||||||
self._analisando_segmentacao = False
|
self._analisando_segmentacao = False
|
||||||
|
|
||||||
self._iniciar_loop_analise_continua(4.0)
|
self._iniciar_loop_analise_continua(15.0)
|
||||||
self.iniciando = False
|
self.iniciando = False
|
||||||
self.atualizar_saude_camera()
|
self.atualizar_saude_camera()
|
||||||
|
|
||||||
|
def _gerar_grid_referencia_geometrico(self, angulo_inclinacao_graus=26, altura_camera_m=0.74):
|
||||||
|
grid_h = self.grid_ref_shape[0]
|
||||||
|
def dist_grid_calibrado(grid_h, i, fov, incl, altura):
|
||||||
|
alpha_v = ((i + 0.5) / grid_h - 0.5) * np.radians(fov)
|
||||||
|
gamma = np.radians(incl) + alpha_v
|
||||||
|
d = (altura / np.tan(gamma)) * 1000.0
|
||||||
|
return d
|
||||||
|
d = np.array([dist_grid_calibrado(grid_h, i, -43.28, 28.91, 0.74) for i in range(grid_h)], dtype=np.float32)
|
||||||
|
d = d[::-1] # ordena de baixo->cima como você queria
|
||||||
|
return d # <-- ndarray, não list
|
||||||
|
|
||||||
def atualizar_saude_camera(self):
|
def atualizar_saude_camera(self):
|
||||||
|
self._ultima_saude_ts = time.time()
|
||||||
|
#self.mostrar_log("Atualizando saude da camera...")
|
||||||
|
try:
|
||||||
if self.camera is not None:
|
if self.camera is not None:
|
||||||
self.camera.atualizar_saude()
|
self.camera.atualizar_saude()
|
||||||
elif self.mx_id is not None:
|
elif self.mx_id is not None:
|
||||||
from camera_worker.manager import definir_saude_camera
|
from camera_worker.manager import definir_saude_camera
|
||||||
definir_saude_camera(self.mx_id, StatusModulo.DESCONECTADO, 0, ["desconectado"], False, {})
|
definir_saude_camera(self.mx_id, StatusModulo.DESCONECTADO, 0, ["desconectado"], False, {})
|
||||||
|
except Exception as e:
|
||||||
|
self.mostrar_log(f"[saude] erro: {e}")
|
||||||
|
|
||||||
def get_rgb_frame(self):
|
def get_rgb_frame(self):
|
||||||
if self.camera is None:
|
if self.camera is None:
|
||||||
|
|
@ -130,6 +154,7 @@ class CameraManager:
|
||||||
try:
|
try:
|
||||||
frame, res = self.camera.requisitar_frame_depth()
|
frame, res = self.camera.requisitar_frame_depth()
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
|
self._ultimo_depth_frame = frame
|
||||||
return frame, self.camera.timestamp_ultimo_frame_depth, res
|
return frame, self.camera.timestamp_ultimo_frame_depth, res
|
||||||
elif "X_LINK_ERROR" in res["erro"]:
|
elif "X_LINK_ERROR" in res["erro"]:
|
||||||
self.reiniciar_status()
|
self.reiniciar_status()
|
||||||
|
|
@ -146,6 +171,24 @@ class CameraManager:
|
||||||
return gerar_heatmap(frame, self.camera.parametros["distancia_maxima"]), timestamp, res
|
return gerar_heatmap(frame, self.camera.parametros["distancia_maxima"]), timestamp, res
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
def get_segmentation_predictions(self):
|
||||||
|
if self.camera is None:
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
|
try:
|
||||||
|
predictions, res = self.camera.requisitar_segmentacao()
|
||||||
|
if predictions is not None:
|
||||||
|
self._ultimo_predictions = predictions
|
||||||
|
return predictions, self.camera.timestamp_ultima_segmentacao, res
|
||||||
|
elif "X_LINK_ERROR" in res["erro"]:
|
||||||
|
self.reiniciar_status()
|
||||||
|
except Exception as e:
|
||||||
|
self.mostrar_log("Erro ao requisitar predictions:", e)
|
||||||
|
if "X_LINK_ERROR" in str(e):
|
||||||
|
self.reiniciar_status()
|
||||||
|
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
def get_select_frame(self, tipo: CameraFrameType):
|
def get_select_frame(self, tipo: CameraFrameType):
|
||||||
f = None
|
f = None
|
||||||
t = None
|
t = None
|
||||||
|
|
@ -163,20 +206,31 @@ class CameraManager:
|
||||||
elif tipo == CameraFrameType.Segmentacao:
|
elif tipo == CameraFrameType.Segmentacao:
|
||||||
f = self._ultima_analise_segmentacao.get("frame", {}).get("frame")
|
f = self._ultima_analise_segmentacao.get("frame", {}).get("frame")
|
||||||
t = self._ultima_analise_segmentacao.get("timestamp")
|
t = self._ultima_analise_segmentacao.get("timestamp")
|
||||||
|
elif tipo == CameraFrameType.Debug:
|
||||||
|
frame_seg = self._ultima_analise_segmentacao.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
|
return f, t
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _iniciar_loop_analise_continua(self, freq):
|
def _iniciar_loop_analise_continua(self, freq):
|
||||||
def loop():
|
def loop():
|
||||||
ultima_atualizacao_saude = 0
|
ultima_atualizacao = 0
|
||||||
|
self._ultima_saude_ts = 0
|
||||||
while True:
|
while True:
|
||||||
if self.camera is None:
|
if self.camera is None:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
if (t0 - ultima_atualizacao_saude) >= 5:
|
if (t0 - self._ultima_saude_ts) >= self.tempo_saude:
|
||||||
self.camera.atualizar_saude()
|
self._ultima_saude_ts = time.time()
|
||||||
ultima_atualizacao_saude = time.time()
|
self.atualizar_saude_camera()
|
||||||
try:
|
try:
|
||||||
status = StatusModulo((self.camera.ultima_saude or {}).get("status", StatusModulo.DESCONECTADO.value))
|
status = StatusModulo((self.camera.ultima_saude or {}).get("status", StatusModulo.DESCONECTADO.value))
|
||||||
if status == StatusModulo.DESCONECTADO:
|
if status == StatusModulo.DESCONECTADO:
|
||||||
|
|
@ -197,11 +251,35 @@ class CameraManager:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.mostrar_log(f"Erro no loop de analise continua: {e}")
|
self.mostrar_log(f"Erro no loop de analise continua: {e}")
|
||||||
finally:
|
finally:
|
||||||
latencia = time.time() - t0
|
latencia, fps, _freq = self._calcular_performance(t0, time.time(), {"ultima_chamada": ultima_atualizacao})
|
||||||
time.sleep(max(0, (1.0 / freq) - latencia))
|
novo_delay = max(0, (1.0 / freq) - latencia)
|
||||||
|
#self.mostrar_log(
|
||||||
|
# self._log_performance("Loop", { "freq": _freq, "fps": fps, "latencia": latencia }) +
|
||||||
|
# self._log_performance("Radar", self._ultima_analise_radar) +
|
||||||
|
# self._log_performance("Segmentacao", self._ultima_analise_segmentacao) +
|
||||||
|
# self._log_performance("Matriz Confianca", self._ultima_analise_matriz_confianca) +
|
||||||
|
# self._log_performance("Anomalias", self._ultima_analise_anomalias) +
|
||||||
|
# self._log_performance("Solo", self._ultima_analise_solo) +
|
||||||
|
# self._log_performance("Matriz Custo", self._ultima_analise_matriz_custo)
|
||||||
|
#)
|
||||||
|
ultima_atualizacao = t0
|
||||||
|
time.sleep(novo_delay)
|
||||||
|
|
||||||
threading.Thread(target=loop, daemon=True).start()
|
threading.Thread(target=loop, daemon=True).start()
|
||||||
|
|
||||||
|
def _calcular_performance(self, t0, t1, analise):
|
||||||
|
latencia = t1 - t0
|
||||||
|
freq = 1.0 / max(latencia, 1e-6)
|
||||||
|
fps = 1.0 / max((t0 - analise.get("ultima_chamada", t0)), 1e-6)
|
||||||
|
analise["latencia"] = latencia
|
||||||
|
analise["fps"] = fps
|
||||||
|
analise["freq"] = freq
|
||||||
|
analise["ultima_chamada"] = t0
|
||||||
|
return latencia, fps, freq
|
||||||
|
|
||||||
|
def _log_performance(self, titulo, analise):
|
||||||
|
return f"{titulo}: {analise.get('latencia', 0):.3f} s, {analise.get('fps', 0):.2f} FPS, {analise.get('freq', 0):.2f} Hz; "
|
||||||
|
|
||||||
def _mostrar_grid_distancias_sobre_rgb(self, rgb_frame, distancias_grid, cor_linha=(0,255,0)):
|
def _mostrar_grid_distancias_sobre_rgb(self, rgb_frame, distancias_grid, cor_linha=(0,255,0)):
|
||||||
"""
|
"""
|
||||||
Mostra o frame RGB com linhas horizontais do grid de referência
|
Mostra o frame RGB com linhas horizontais do grid de referência
|
||||||
|
|
@ -231,41 +309,35 @@ class CameraManager:
|
||||||
|
|
||||||
|
|
||||||
def _realizar_analises(self):
|
def _realizar_analises(self):
|
||||||
if self._depth_frame_necessario and self.camera.tem_depth:
|
executor = self._pool
|
||||||
try:
|
tarefas = []
|
||||||
# 🔸 Captura frame
|
|
||||||
depth_frame_np, depth_timestamp, depth_res = self.get_depth_frame()
|
|
||||||
except Exception as e:
|
|
||||||
self.mostrar_log(f"❌ Erro ao capturar frame: {e}")
|
|
||||||
return
|
|
||||||
|
|
||||||
parametros_camera = self.camera.parametros
|
parametros_camera = self.camera.parametros
|
||||||
fov_h = parametros_camera["fov_h"]
|
fov_h = parametros_camera["fov_h"]
|
||||||
distancia_max_m = parametros_camera["distancia_maxima"] / 1000.0
|
distancia_max_m = parametros_camera["distancia_maxima"] / 1000.0
|
||||||
percentual_solo = parametros_camera["percentual_altura_solo"] / 100.0
|
percentual_solo = parametros_camera["percentual_altura_solo"] / 100.0
|
||||||
limiar_delta = calcular_threshold_anomalias()
|
|
||||||
limiar_conf: float = 0.4
|
|
||||||
largura_min: float = 0.15
|
|
||||||
altura_min: float = 0.15
|
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=6) as executor:
|
depth_frame_np, depth_timestamp, depth_res = self.get_depth_frame()
|
||||||
tarefas = []
|
|
||||||
|
|
||||||
#tarefas.append(executor.submit(self._analise_radar, depth_frame_np, fov_h, distancia_max_m))
|
tarefas.append(executor.submit(self._analise_radar, depth_frame_np, fov_h, distancia_max_m))
|
||||||
|
|
||||||
if True or not self._nova_segmentacao_disponivel:
|
if True or not self._nova_segmentacao_disponivel:
|
||||||
if not self._analisando_segmentacao:
|
if not self._analisando_segmentacao:
|
||||||
tarefas.append(executor.submit(self._analise_segmentacao))
|
tarefas.append(executor.submit(self._analise_segmentacao))
|
||||||
|
|
||||||
if False and self._nova_segmentacao_disponivel:
|
if self._nova_segmentacao_disponivel:
|
||||||
if not self._analisando_matriz_confianca:
|
if not self._analisando_matriz_confianca:
|
||||||
self._nova_segmentacao_disponivel = False
|
self._nova_segmentacao_disponivel = False
|
||||||
tarefas.append(executor.submit(self._analise_matriz_confianca, depth_frame_np, distancia_max_m, fov_h))
|
tarefas.append(executor.submit(self._analise_matriz_confianca, depth_frame_np, distancia_max_m, fov_h))
|
||||||
|
|
||||||
if False and self._nova_grid_conf_disponivel:
|
if self._nova_grid_conf_disponivel:
|
||||||
|
limiar_conf: float = 0.4
|
||||||
matriz_conf = self._ultima_analise_matriz_confianca["matriz"]
|
matriz_conf = self._ultima_analise_matriz_confianca["matriz"]
|
||||||
if not self._analisando_anomalias:
|
if not self._analisando_anomalias:
|
||||||
self._nova_grid_conf_disponivel = False
|
self._nova_grid_conf_disponivel = False
|
||||||
|
limiar_delta = calcular_threshold_anomalias()
|
||||||
|
largura_min: float = 0.15
|
||||||
|
altura_min: float = 0.15
|
||||||
tarefas.append(executor.submit(self._analise_anomalias, matriz_conf, limiar_delta, limiar_conf, distancia_max_m, largura_min, altura_min))
|
tarefas.append(executor.submit(self._analise_anomalias, matriz_conf, limiar_delta, limiar_conf, distancia_max_m, largura_min, altura_min))
|
||||||
if not self._analisando_solo:
|
if not self._analisando_solo:
|
||||||
self._nova_grid_conf_disponivel = False
|
self._nova_grid_conf_disponivel = False
|
||||||
|
|
@ -274,24 +346,25 @@ class CameraManager:
|
||||||
self._nova_grid_conf_disponivel = False
|
self._nova_grid_conf_disponivel = False
|
||||||
tarefas.append(executor.submit(self._analise_matriz_custo, matriz_conf, fov_h))
|
tarefas.append(executor.submit(self._analise_matriz_custo, matriz_conf, fov_h))
|
||||||
|
|
||||||
#for t in tarefas:
|
|
||||||
# t.result() # Espera cada uma terminar
|
|
||||||
|
|
||||||
def _analise_segmentacao(self):
|
def _analise_segmentacao(self):
|
||||||
if self._analisando_segmentacao:
|
if self._analisando_segmentacao:
|
||||||
return
|
return
|
||||||
self._analisando_segmentacao = True
|
self._analisando_segmentacao = True
|
||||||
t0 = time.time()
|
|
||||||
try:
|
try:
|
||||||
rgb_frame, rgb_timestamp, res_frame = self.get_rgb_frame()
|
t0 = time.time()
|
||||||
if rgb_frame is None or rgb_frame.size == 0:
|
predictions, ts, res = self.get_segmentation_predictions()
|
||||||
self._analisando_segmentacao = False
|
if ts == self._ts_segmentacao_anterior:
|
||||||
return
|
return # já analisado
|
||||||
analise_segmentacao, log = self.segmentacao_manager.segmentar(rgb_frame)
|
self._ts_segmentacao_anterior = ts
|
||||||
|
if predictions is not None:
|
||||||
|
rgb_frame, _ts, res = self.get_rgb_frame()
|
||||||
|
analise_segmentacao, log = self.segmentacao_manager.segmentar(predictions)
|
||||||
|
t1 = time.time()
|
||||||
if analise_segmentacao == None:
|
if analise_segmentacao == None:
|
||||||
self.mostrar_log(log)
|
self.mostrar_log(log)
|
||||||
t1 = time.time()
|
analise_segmentacao["ultima_chamada"] = self._ultima_analise_segmentacao.get("ultima_chamada", t0)
|
||||||
analise_segmentacao["latencia"] = t1 - t0
|
self._calcular_performance(t0, t1, analise_segmentacao)
|
||||||
self._ultima_analise_segmentacao = analise_segmentacao
|
self._ultima_analise_segmentacao = analise_segmentacao
|
||||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||||
CtxKey.DadosVisualWorker,
|
CtxKey.DadosVisualWorker,
|
||||||
|
|
@ -310,25 +383,27 @@ class CameraManager:
|
||||||
self.mostrar_log(f"❌ Erro na segmentacao semantica: {e}")
|
self.mostrar_log(f"❌ Erro na segmentacao semantica: {e}")
|
||||||
finally:
|
finally:
|
||||||
self._analisando_segmentacao = False
|
self._analisando_segmentacao = False
|
||||||
#self.mostrar_log("Segmentacao concluida")
|
#self.mostrar_log(f"Segmentacao concluida em {self._ultima_analise_segmentacao['latencia']:.4f} s, a {fps:.4f} FPS")
|
||||||
|
|
||||||
def _analise_matriz_confianca(self, depth_frame_np, dist_max, fov_h):
|
def _analise_matriz_confianca(self, depth_frame_np, dist_max, fov_h):
|
||||||
if self._analisando_matriz_confianca:
|
if self._analisando_matriz_confianca:
|
||||||
return
|
return
|
||||||
self._analisando_matriz_confianca = True
|
self._analisando_matriz_confianca = True
|
||||||
t0 = time.time()
|
|
||||||
try:
|
try:
|
||||||
if depth_frame_np is None or depth_frame_np.size == 0:
|
if depth_frame_np is None or depth_frame_np.size == 0:
|
||||||
return
|
return
|
||||||
segmentacao = self._ultima_analise_segmentacao.get("classes", [])
|
segmentacao = self._ultima_analise_segmentacao.get("classes")
|
||||||
|
if segmentacao is None: return
|
||||||
#segmentacao_vis = self._ultima_analise_segmentacao.get("mask_color", None)
|
#segmentacao_vis = self._ultima_analise_segmentacao.get("mask_color", None)
|
||||||
|
|
||||||
if depth_frame_np is None or segmentacao is None:
|
if depth_frame_np is None or segmentacao is None:
|
||||||
self.mostrar_log("❌ Depth frame ou segmentação inválidos para gerar matriz de confiança.")
|
self.mostrar_log("❌ Depth frame ou segmentação inválidos para gerar matriz de confiança.")
|
||||||
else:
|
else:
|
||||||
|
t0 = time.time()
|
||||||
grid_conf = self._gerar_grid_confianca(depth_frame_np, segmentacao, dist_max)
|
grid_conf = self._gerar_grid_confianca(depth_frame_np, segmentacao, dist_max)
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
grid_conf["latencia"] = t1 - t0
|
grid_conf["ultima_chamada"] = self._ultima_analise_matriz_confianca.get("ultima_chamada", t0)
|
||||||
|
self._calcular_performance(t0, t1, grid_conf)
|
||||||
self._ultima_analise_matriz_confianca = grid_conf
|
self._ultima_analise_matriz_confianca = grid_conf
|
||||||
matriz = self._ultima_analise_matriz_confianca["matriz"]
|
matriz = self._ultima_analise_matriz_confianca["matriz"]
|
||||||
self._ultima_analise_segmentacao["corredor_perfil"] = self.segmentacao_manager.calcular_perfil_corredor(matriz, fov_h)
|
self._ultima_analise_segmentacao["corredor_perfil"] = self.segmentacao_manager.calcular_perfil_corredor(matriz, fov_h)
|
||||||
|
|
@ -339,7 +414,7 @@ class CameraManager:
|
||||||
perfil_corredor__segmentacao=converter_valores_numpy(self._ultima_analise_segmentacao.get("corredor_perfil", []))
|
perfil_corredor__segmentacao=converter_valores_numpy(self._ultima_analise_segmentacao.get("corredor_perfil", []))
|
||||||
)
|
)
|
||||||
self._nova_grid_conf_disponivel = True
|
self._nova_grid_conf_disponivel = True
|
||||||
#self._mostrar_debug_grid_confianca(rgb_frame, grid_conf, True, segmentacao_vis)
|
#self._mostrar_debug_grid_confianca(self._ultimo_rgb_frame, grid_conf["matriz"], True, self._ultima_analise_segmentacao["mask_color"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.mostrar_log(f"❌ Erro na geracao da matriz de confianca: {e}")
|
self.mostrar_log(f"❌ Erro na geracao da matriz de confianca: {e}")
|
||||||
finally:
|
finally:
|
||||||
|
|
@ -351,13 +426,14 @@ class CameraManager:
|
||||||
if self._analisando_anomalias:
|
if self._analisando_anomalias:
|
||||||
return
|
return
|
||||||
self._analisando_anomalias = True
|
self._analisando_anomalias = True
|
||||||
t0 = time.time()
|
|
||||||
try:
|
try:
|
||||||
|
t0 = time.time()
|
||||||
#analise_anomalias = self.anomalias_manager.detectar_anomalias(depth_frame, self.depth_referencia, limiar, distancia_max, largura_min, altura_min, parametros_camera)
|
#analise_anomalias = self.anomalias_manager.detectar_anomalias(depth_frame, self.depth_referencia, limiar, distancia_max, largura_min, altura_min, parametros_camera)
|
||||||
#analise_anomalias = self.anomalias_manager.analisar_anomalias(depth_frame_np, self.depth_referencia, limiar, distancia_max_m, largura_min, altura_min)
|
#analise_anomalias = self.anomalias_manager.analisar_anomalias(depth_frame_np, self.depth_referencia, limiar, distancia_max_m, largura_min, altura_min)
|
||||||
analise_anomalias = self.anomalias_manager.analisar_anomalias_grid(grid_conf, limiar_delta, limiar_conf, (640, 480), dist_max, largura_min, altura_min)
|
analise_anomalias = self.anomalias_manager.analisar_anomalias_grid(grid_conf, limiar_delta, limiar_conf, (640, 480), dist_max, largura_min, altura_min)
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
analise_anomalias["latencia"] = t1 - t0
|
analise_anomalias["ultima_chamada"] = self._ultima_analise_anomalias.get("ultima_chamada", t0)
|
||||||
|
self._calcular_performance(t0, t1, analise_anomalias)
|
||||||
self._ultima_analise_anomalias = analise_anomalias
|
self._ultima_analise_anomalias = analise_anomalias
|
||||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||||
CtxKey.DadosVisualWorker,
|
CtxKey.DadosVisualWorker,
|
||||||
|
|
@ -374,11 +450,12 @@ class CameraManager:
|
||||||
if self._analisando_solo:
|
if self._analisando_solo:
|
||||||
return
|
return
|
||||||
self._analisando_solo = True
|
self._analisando_solo = True
|
||||||
t0 = time.time()
|
|
||||||
try:
|
try:
|
||||||
|
t0 = time.time()
|
||||||
analise_solo = self.solo_manager.analisar_solo(grid_conf, percentual_solo, limiar_conf, fov_h)
|
analise_solo = self.solo_manager.analisar_solo(grid_conf, percentual_solo, limiar_conf, fov_h)
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
analise_solo["latencia"] = t1 - t0
|
analise_solo["ultima_chamada"] = self._ultima_analise_solo.get("ultima_chamada", t0)
|
||||||
|
self._calcular_performance(t0, t1, analise_solo)
|
||||||
self._ultima_analise_solo = analise_solo
|
self._ultima_analise_solo = analise_solo
|
||||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||||
CtxKey.DadosVisualWorker,
|
CtxKey.DadosVisualWorker,
|
||||||
|
|
@ -395,14 +472,15 @@ class CameraManager:
|
||||||
if self._analisando_radar:
|
if self._analisando_radar:
|
||||||
return
|
return
|
||||||
self._analisando_radar = True
|
self._analisando_radar = True
|
||||||
t0 = time.time()
|
|
||||||
try:
|
try:
|
||||||
if depth_frame_np is None or depth_frame_np.size == 0:
|
if depth_frame_np is None or depth_frame_np.size == 0:
|
||||||
return
|
return
|
||||||
|
t0 = time.time()
|
||||||
depth_frame = cp.asarray(depth_frame_np)
|
depth_frame = cp.asarray(depth_frame_np)
|
||||||
analise_radar = self.radar_manager.analisar_radar_2d(depth_frame, fov_h, dist_max)
|
analise_radar = self.radar_manager.analisar_radar_2d(depth_frame, fov_h, dist_max)
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
analise_radar["latencia"] = t1 - t0
|
analise_radar["ultima_chamada"] = self._ultima_analise_radar.get("ultima_chamada", t0)
|
||||||
|
self._calcular_performance(t0, t1, analise_radar)
|
||||||
self._ultima_analise_radar = analise_radar
|
self._ultima_analise_radar = analise_radar
|
||||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||||
CtxKey.DadosVisualWorker,
|
CtxKey.DadosVisualWorker,
|
||||||
|
|
@ -421,12 +499,13 @@ class CameraManager:
|
||||||
if self._analisando_matriz_custo:
|
if self._analisando_matriz_custo:
|
||||||
return
|
return
|
||||||
self._analisando_matriz_custo = True
|
self._analisando_matriz_custo = True
|
||||||
t0 = time.time()
|
|
||||||
try:
|
try:
|
||||||
|
t0 = time.time()
|
||||||
largura_robo = ContextoGlobalRedis.get(CtxKey.DadosEquipamento, {}).get("largura", 0.85)
|
largura_robo = ContextoGlobalRedis.get(CtxKey.DadosEquipamento, {}).get("largura", 0.85)
|
||||||
matriz_custo = self._gerar_matriz_custo_fundida(grid_conf, largura_robo, fov_h)
|
matriz_custo = self._gerar_matriz_custo_fundida(grid_conf, largura_robo, fov_h)
|
||||||
t1 = time.time()
|
t1 = time.time()
|
||||||
matriz_custo["latencia"] = t1 - t0
|
matriz_custo["ultima_chamada"] = self._ultima_analise_matriz_custo.get("ultima_chamada", t0)
|
||||||
|
self._calcular_performance(t0, t1, matriz_custo)
|
||||||
self._ultima_analise_matriz_custo = matriz_custo
|
self._ultima_analise_matriz_custo = matriz_custo
|
||||||
matriz = matriz_custo["matriz"]
|
matriz = matriz_custo["matriz"]
|
||||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||||
|
|
@ -493,81 +572,133 @@ class CameraManager:
|
||||||
return caminho_analise
|
return caminho_analise
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _gerar_grid_confianca(self, depth_frame, segmentacao_frame, limiar_prof):
|
def _as_ndarray(self, x, dtype=None):
|
||||||
|
if isinstance(x, np.ndarray):
|
||||||
|
return x.astype(dtype, copy=False) if dtype is not None else x
|
||||||
|
return np.array(x, dtype=dtype, copy=False)
|
||||||
|
|
||||||
|
def _gerar_grid_confianca(self, depth_frame, segmentacao_frame, limiar_prof, incluir_hist=False):
|
||||||
|
"""
|
||||||
|
depth_frame: np.ndarray HxW (milímetros; 0 = inválido)
|
||||||
|
segmentacao_frame: np.ndarray HxW (IDs de classe)
|
||||||
|
limiar_prof: limiar em METROS (ex.: 0.5) para índice de profundidade
|
||||||
|
incluir_hist: se True, retorna freq por classe em cada célula (custa alguns ms)
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
grid_h, grid_w = self.grid_ref_shape
|
# --- força ndarray ---
|
||||||
altura, largura = depth_frame.shape
|
depth = self._as_ndarray(depth_frame) # mm, 2D
|
||||||
|
seg = self._as_ndarray(segmentacao_frame) # IDs, 2D (NEAREST)
|
||||||
|
|
||||||
h_step = altura // grid_h
|
if depth.ndim != 2:
|
||||||
w_step = largura // grid_w
|
raise ValueError(f"depth_frame deve ser 2D, veio {depth.shape}")
|
||||||
|
if seg.ndim == 3 and seg.shape[2] == 3:
|
||||||
|
# se vier máscara colorida por engano, precisa converter antes (RGB->IDs)
|
||||||
|
raise ValueError("segmentacao_frame veio RGB; converta para IDs antes de chamar.")
|
||||||
|
if seg.shape != depth.shape:
|
||||||
|
seg = cv2.resize(seg, (depth.shape[1], depth.shape[0]), interpolation=cv2.INTER_NEAREST)
|
||||||
|
|
||||||
grid_resultado = [[{} for _ in range(grid_w)] for _ in range(grid_h)]
|
H, W = depth.shape
|
||||||
|
gh_cfg, gw_cfg = self.grid_ref_shape # alvo "ideal" (ex.: 20x20)
|
||||||
|
|
||||||
for i in range(grid_h):
|
# calcula tamanho mínimo de célula (>=1px) e ajusta grid efetivo ao frame
|
||||||
prof_ref = self.grid_ref[i]
|
h = max(1, H // gh_cfg)
|
||||||
for j in range(grid_w):
|
w = max(1, W // gw_cfg)
|
||||||
y0, y1 = i * h_step, (i + 1) * h_step
|
gh_eff = max(1, H // h)
|
||||||
x0, x1 = j * w_step, (j + 1) * w_step
|
gw_eff = max(1, W // w)
|
||||||
|
|
||||||
cel_depth = depth_frame[y0:y1, x0:x1]
|
# crop exato para poder reshape
|
||||||
cel_seg = segmentacao_frame[y0:y1, x0:x1]
|
H2 = gh_eff * h
|
||||||
total_pix = cel_depth.size
|
W2 = gw_eff * w
|
||||||
|
depth = self._as_ndarray(depth[:H2, :W2])
|
||||||
|
seg = self._as_ndarray(seg[:H2, :W2])
|
||||||
|
|
||||||
# Frequência das classes
|
# ---------- reshape em blocos ----------
|
||||||
valores, contagens = np.unique(cel_seg, return_counts=True)
|
depth_b = depth.reshape(gh_eff, h, gw_eff, w)
|
||||||
freq_classes = {int(v): int(c) / total_pix for v, c in zip(valores, contagens)}
|
seg_b = seg.reshape(gh_eff, h, gw_eff, w)
|
||||||
|
|
||||||
# Índice de Segmentação (presença de chão)
|
# ---------- métricas vetorizadas ----------
|
||||||
IS = freq_classes.get(ClassesSegmentacao.RUA.value, 0.0)
|
valid = (depth_b > 0)
|
||||||
|
ignore_id = getattr(self, "ignore_id", 255)
|
||||||
|
not_ign = (seg_b != ignore_id)
|
||||||
|
|
||||||
|
rua_id = int(ClassesSegmentacao.RUA.value)
|
||||||
|
IS = (seg_b == rua_id).mean(axis=(1,3)) # presença de "rua"/chão [0..1] por célula
|
||||||
|
conf_segmentacao = not_ign.mean(axis=(1,3)) # fração de pixels não-ignorados
|
||||||
|
|
||||||
|
depth_f = np.where(valid, depth_b.astype(np.float32), np.nan)
|
||||||
|
prof_median = np.nanmedian(depth_f, axis=(1,3)) # mm
|
||||||
|
conf_profundidade = valid.mean(axis=(1,3))
|
||||||
|
|
||||||
|
# baseline por linha (grid_ref) em mm
|
||||||
|
if getattr(self, "grid_ref", None) is None or len(self.grid_ref) != gh_eff:
|
||||||
|
# mediana por faixa horizontal ao longo de toda a largura
|
||||||
|
ref = np.nanmedian(
|
||||||
|
np.where(depth > 0, depth, np.nan).reshape(gh_eff, h, W2),
|
||||||
|
axis=(1, 2)
|
||||||
|
)
|
||||||
|
self.grid_ref = np.nan_to_num(ref, nan=0.0).astype(np.float32)
|
||||||
|
|
||||||
|
# 🔒 garante ndarray e tamanho correto, mesmo se veio de sorted(...)
|
||||||
|
grid_ref_arr = np.asarray(self.grid_ref, dtype=np.float32)
|
||||||
|
if grid_ref_arr.ndim != 1 or grid_ref_arr.shape[0] != gh_eff:
|
||||||
|
# fallback: recalcula para o gh_eff atual
|
||||||
|
ref = np.nanmedian(
|
||||||
|
np.where(depth > 0, depth, np.nan).reshape(gh_eff, h, W2),
|
||||||
|
axis=(1, 2)
|
||||||
|
)
|
||||||
|
grid_ref_arr = np.nan_to_num(ref, nan=0.0).astype(np.float32)
|
||||||
|
self.grid_ref = grid_ref_arr # mantém coerente no objeto
|
||||||
|
|
||||||
|
prof_ref = np.repeat(grid_ref_arr.reshape(gh_eff, 1), gw_eff, axis=1) # (gh, gw) mm
|
||||||
|
|
||||||
# Índice de Profundidade
|
|
||||||
celula_valida = cel_depth[cel_depth > 0.0]
|
|
||||||
if celula_valida.size == 0:
|
|
||||||
#print(f"⚠️ Célula ({i}, {j}) sem profundidade válida")
|
|
||||||
prof_median = -99999.0
|
|
||||||
delta = -99999.0
|
|
||||||
IP = 0.0
|
|
||||||
conf_profundidade = 0.0
|
|
||||||
else:
|
|
||||||
prof_median = np.median(celula_valida)
|
|
||||||
delta = prof_median - prof_ref
|
delta = prof_median - prof_ref
|
||||||
IP = 1.0 - min(abs(delta) / (limiar_prof * 1000.0), 1.0)
|
limiar_mm = float(limiar_prof) * 1000.0
|
||||||
conf_profundidade = celula_valida.size / total_pix
|
IP = 1.0 - np.minimum(np.abs(delta) / max(limiar_mm, 1e-6), 1.0)
|
||||||
|
IP = np.nan_to_num(IP, nan=0.0)
|
||||||
|
|
||||||
# Índice Caminho Livre
|
ICL = 0.6 * IS + 0.4 * IP
|
||||||
PESO_SEG, PESO_PROF = 0.6, 0.4
|
conf_geral = 0.5 * (conf_profundidade + conf_segmentacao)
|
||||||
ICL = PESO_SEG * IS + PESO_PROF * IP
|
|
||||||
|
|
||||||
num_mapeados = total_pix - np.count_nonzero(cel_seg == 0)
|
# opcional: frequências por classe (se realmente precisar)
|
||||||
conf_segmentacao = num_mapeados / total_pix
|
freq_por_classe = None
|
||||||
|
if incluir_hist:
|
||||||
|
# defina os IDs que interessam (evita iterar 0..254)
|
||||||
|
ids_classes = sorted({rua_id} | set(getattr(self, "ids_classes", [])) or {0,1,2})
|
||||||
|
ids_classes = [c for c in ids_classes if 0 <= c < 255]
|
||||||
|
inv_area = 1.0 / (h * w)
|
||||||
|
freq_por_classe = {c: (seg_b == c).sum(axis=(1,3)) * inv_area for c in ids_classes}
|
||||||
|
|
||||||
indice_confiabilidade = (conf_profundidade + conf_segmentacao) / 2.0
|
# ---------- monta saída (loop leve só pra dicionários) ----------
|
||||||
|
grid = [[None for _ in range(gw_eff)] for _ in range(gh_eff)]
|
||||||
|
for i in range(gh_eff):
|
||||||
|
for j in range(gw_eff):
|
||||||
|
freq_classes = {rua_id: float(IS[i, j])}
|
||||||
|
if incluir_hist:
|
||||||
|
freq_classes = {int(c): float(freq_por_classe[c][i, j]) for c in freq_por_classe}
|
||||||
|
|
||||||
grid_resultado[i][j] = {
|
grid[i][j] = {
|
||||||
"linha": i,
|
"linha": i, "coluna": j,
|
||||||
"coluna": j,
|
"pix_x": w, "pix_y": h,
|
||||||
"pix_x": x1 - x0,
|
|
||||||
"pix_y": y1 - y0,
|
|
||||||
"segmentacao": freq_classes,
|
"segmentacao": freq_classes,
|
||||||
"prof_ref": prof_ref / 1000.0,
|
"prof_ref": float(prof_ref[i, j]) / 1000.0,
|
||||||
"prof_median": prof_median / 1000.0,
|
"prof_median": float(prof_median[i, j]) / 1000.0,
|
||||||
"prof_delta": delta / 1000.0,
|
"prof_delta": float(delta[i, j]) / 1000.0,
|
||||||
"indice_seg_chao": IS,
|
"indice_seg_chao": float(IS[i, j]),
|
||||||
"indice_prof_delta": IP,
|
"indice_prof_delta": float(IP[i, j]),
|
||||||
"indice_caminho_livre": ICL,
|
"indice_caminho_livre": float(ICL[i, j]),
|
||||||
"conf_profundidade": conf_profundidade,
|
"conf_profundidade": float(conf_profundidade[i, j]),
|
||||||
"conf_segmentacao": conf_segmentacao,
|
"conf_segmentacao": float(conf_segmentacao[i, j]),
|
||||||
"indice_confiabilidade": indice_confiabilidade
|
"indice_confiabilidade": float(conf_geral[i, j]),
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
# (opcional) guardar grid efetivo, útil pra debug
|
||||||
"matriz": grid_resultado
|
self.grid_ref_shape_eff = (gh_eff, gw_eff)
|
||||||
}
|
|
||||||
|
return {"timestamp": time.time(), "matriz": grid}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.mostrar_log(f"❌ Erro ao gerar grid de confianca: {e}")
|
self.mostrar_log(f"❌ Erro ao gerar grid de confianca: {e}")
|
||||||
return {
|
return {"timestamp": time.time(), "matriz": [[]]}
|
||||||
"matriz": [[]]
|
|
||||||
}
|
|
||||||
|
|
||||||
def _mostrar_debug_grid_confianca(self, rgb_frame: np.ndarray, grid_conf: list, exibir_debug: bool = False, segmentacao_colorida: np.ndarray = None):
|
def _mostrar_debug_grid_confianca(self, rgb_frame: np.ndarray, grid_conf: list, exibir_debug: bool = False, segmentacao_colorida: np.ndarray = None):
|
||||||
try:
|
try:
|
||||||
|
|
@ -575,7 +706,7 @@ class CameraManager:
|
||||||
return
|
return
|
||||||
|
|
||||||
img_debug = rgb_frame.copy()
|
img_debug = rgb_frame.copy()
|
||||||
img_debug = cv2.resize(img_debug, (1600, 900))
|
img_debug = cv2.resize(img_debug, (1280, 720))
|
||||||
|
|
||||||
# Se tiver segmentação colorida, aplica direto no img_debug (fundo)
|
# Se tiver segmentação colorida, aplica direto no img_debug (fundo)
|
||||||
if segmentacao_colorida is not None:
|
if segmentacao_colorida is not None:
|
||||||
|
|
@ -979,11 +1110,13 @@ class CameraManager:
|
||||||
matriz_custo = self._aplicar_bonus_caminho(matriz_custo, pontos_plotar, largura_robo_m)
|
matriz_custo = self._aplicar_bonus_caminho(matriz_custo, pontos_plotar, largura_robo_m)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
"timestamp": time.time(),
|
||||||
"matriz": matriz_custo
|
"matriz": matriz_custo
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.mostrar_log(f"❌ Erro ao gerar matriz de custo: {e}")
|
self.mostrar_log(f"❌ Erro ao gerar matriz de custo: {e}")
|
||||||
return {
|
return {
|
||||||
|
"timestamp": time.time(),
|
||||||
"matriz": [[]]
|
"matriz": [[]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1018,7 +1151,7 @@ class CameraManager:
|
||||||
if not exibir_debug:
|
if not exibir_debug:
|
||||||
return
|
return
|
||||||
|
|
||||||
img_debug = cv2.resize(rgb_frame.copy(), (1600, 900))
|
img_debug = cv2.resize(rgb_frame.copy(), (1280, 720))
|
||||||
overlay = img_debug.copy()
|
overlay = img_debug.copy()
|
||||||
|
|
||||||
altura, largura, _ = img_debug.shape
|
altura, largura, _ = img_debug.shape
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from visual_worker.camera_manager import CameraManager
|
from visual_worker.camera_manager import CameraManager
|
||||||
|
from shared.contexto_global_redis import ContextoGlobalRedis
|
||||||
|
|
||||||
module_id = "visual"
|
module_id = "visual"
|
||||||
topico_tx = f"operador/{module_id}/tx"
|
topico_tx = f"operador/{module_id}/tx"
|
||||||
|
|
@ -28,3 +31,57 @@ def iniciar_camera_manager(mx_id):
|
||||||
manager.inicializar(mx_id=mx_id)
|
manager.inicializar(mx_id=mx_id)
|
||||||
if manager.camera is not None and manager.operante:
|
if manager.camera is not None and manager.operante:
|
||||||
mostrar_log(f"✅ Camera manager iniciado, com MX_ID: {mx_id}")
|
mostrar_log(f"✅ Camera manager iniciado, com MX_ID: {mx_id}")
|
||||||
|
|
||||||
|
|
||||||
|
_CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json")
|
||||||
|
_CONFIG_CACHE = None
|
||||||
|
_CONFIG_MTIME = None
|
||||||
|
_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,
|
||||||
|
# "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,
|
||||||
|
"ia_roi_begin": 0.0,
|
||||||
|
"ia_roi_size": 1.0,
|
||||||
|
"ia_resolution": [512,288]
|
||||||
|
}
|
||||||
|
_CONFIG_CACHE["ia_model_path"] = ContextoGlobalRedis.get_equipamento().get("path_ia_model_ruas", "C:/AgroBaseModels/Ruas/model-1_1.blob")
|
||||||
|
_CONFIG_CACHE["ia_labelmap_path"] = ContextoGlobalRedis.get_equipamento().get("path_ia_labelmap_ruas", "C:/AgroBaseModels/Ruas/model-1_1.txt")
|
||||||
|
|
||||||
|
return _CONFIG_CACHE
|
||||||
|
|
||||||
|
def reload_config():
|
||||||
|
return load_config(force_reload=True)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -59,30 +59,37 @@ class Radar2DManager:
|
||||||
|
|
||||||
return resultado
|
return resultado
|
||||||
|
|
||||||
def gerar_radar_topdown(self, depth_frame, fov_horizontal, min_dist, max_dist):
|
def gerar_radar_topdown(self, depth_frame, fov_horizontal, min_dist_m, max_dist_m):
|
||||||
|
"""
|
||||||
|
depth_frame: CuPy array (mm), 2D
|
||||||
|
fov_horizontal: em radianos
|
||||||
|
min_dist_m / max_dist_m: metros
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
height, width = depth_frame.shape
|
d = cp.asarray(depth_frame, dtype=cp.float32) # mm
|
||||||
|
H, W = d.shape
|
||||||
|
|
||||||
# 🔥 Índices de pixels na horizontal (normalizado de -0.5 a +0.5)
|
# Cacheia tan(theta_x) por largura+FOV para evitar recomputar a cada frame
|
||||||
indices_x = cp.linspace(-0.5, 0.5, width)
|
key = (W, float(fov_horizontal))
|
||||||
theta_x = indices_x * fov_horizontal
|
if getattr(self, "_tan_cache_key", None) != key:
|
||||||
|
theta_x = (cp.linspace(-0.5, 0.5, W, dtype=cp.float32) * fov_horizontal)[cp.newaxis, :] # (1, W)
|
||||||
|
self._tan_theta_x = cp.tan(theta_x) # (1, W)
|
||||||
|
self._tan_cache_key = key
|
||||||
|
|
||||||
# 🔥 Matriz com ângulo lateral para cada coluna
|
# Depth em metros
|
||||||
theta_matrix = cp.tile(theta_x, (height, 1))
|
Z = d * 0.001 # m
|
||||||
|
|
||||||
# 🔥 Converte depth de mm para metros
|
# Filtro válido (agora em metros)
|
||||||
Z = depth_frame / 1000.0
|
mask = (Z > 0) & (Z >= min_dist_m) & (Z <= max_dist_m)
|
||||||
|
Z = cp.where(mask, Z, cp.nan)
|
||||||
|
|
||||||
# 🔥 Calcula X no plano
|
# X por broadcasting, sem tile
|
||||||
X = Z * cp.tan(theta_matrix)
|
X = Z * self._tan_theta_x # (H,W) * (1,W) -> (H,W)
|
||||||
|
|
||||||
# 🔥 Filtro de distância válida
|
# Compacta removendo NaNs sem copiar desnecessariamente
|
||||||
mask = (Z >= min_dist) & (Z <= max_dist) & (Z > 0)
|
m = cp.isfinite(Z)
|
||||||
|
return X[m].ravel(), Z[m].ravel()
|
||||||
|
|
||||||
X = X[mask]
|
|
||||||
Z = Z[mask]
|
|
||||||
|
|
||||||
return X.flatten(), Z.flatten()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Erro ao gerar radar topdown: {e}")
|
print(f"Erro ao gerar radar topdown: {e}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ from enum import IntEnum
|
||||||
import time
|
import time
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import onnxruntime as ort
|
|
||||||
|
|
||||||
from shared.utils import encode_image_base64
|
from shared.utils import encode_image_base64
|
||||||
from shared.enums import StatusCarroMapa
|
from shared.enums import StatusCarroMapa
|
||||||
|
|
@ -14,117 +13,33 @@ class ClassesSegmentacao(IntEnum):
|
||||||
|
|
||||||
|
|
||||||
class SegmentacaoManager:
|
class SegmentacaoManager:
|
||||||
def __init__(self, path_model: str):
|
def __init__(self, color_map, classes):
|
||||||
self.path_modelo = path_model
|
from visual_worker.config import load_config
|
||||||
self.path_classes = path_model.replace("onnx", "txt")
|
config = load_config()
|
||||||
self.modelo = None
|
resolucao = config.get("ia_resolution")
|
||||||
self.input_name = None
|
self.color_map = color_map
|
||||||
self.output_name = None
|
self.classes = classes
|
||||||
self.classes = {}
|
self.resolucao = (resolucao[0], resolucao[1])
|
||||||
self.color_map = []
|
self.pred_rgb = np.empty((self.resolucao[1], self.resolucao[0], 3), dtype=np.uint8)
|
||||||
self.carregado = False
|
self.color_lut = np.array(self.color_map, np.uint8)
|
||||||
self.use_mock = False
|
self.lut = np.zeros((256, 3), dtype=np.uint8)
|
||||||
self.img_mock = "C:\\ZendionInc\\agrobot_base\\AgroBase\\AgroBase\\bin\\x64\\Debug\\Operacoes\\28_07_2025_14_47_15\\Snr\\137_rgb.jpeg"
|
for i, color in enumerate(color_map):
|
||||||
self.resolucao = (512, 512)
|
#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.predictions = None
|
self.predictions = None
|
||||||
|
self.log = None
|
||||||
self.dados_visuais = {}
|
self.dados_visuais = {}
|
||||||
self._carregar_modelo()
|
|
||||||
|
|
||||||
def _carregar_labelmap_completo(self, caminho):
|
def _segmentar_predictions(self, predictions):
|
||||||
cor_para_id = {}
|
|
||||||
id_para_nome = {}
|
|
||||||
cores_bgr = []
|
|
||||||
|
|
||||||
with open(caminho, 'r') as arquivo:
|
|
||||||
idx = 0
|
|
||||||
for linha in arquivo:
|
|
||||||
if linha.startswith("#") or not linha.strip():
|
|
||||||
continue
|
|
||||||
partes = linha.strip().split(':')
|
|
||||||
if len(partes) >= 2:
|
|
||||||
nome_classe, cor_rgb_str = partes[0], partes[1]
|
|
||||||
r, g, b = map(int, cor_rgb_str.split(','))
|
|
||||||
cor_bgr = (b, g, r) # Corrige para BGR
|
|
||||||
|
|
||||||
if nome_classe.lower() == "ignore":
|
|
||||||
ignore_bgr = cor_bgr
|
|
||||||
continue # NÃO adiciona ignore no LUT de classes
|
|
||||||
|
|
||||||
cor_para_id[cor_bgr] = idx
|
|
||||||
cores_bgr.append(cor_bgr)
|
|
||||||
id_para_nome[idx] = nome_classe
|
|
||||||
idx += 1
|
|
||||||
|
|
||||||
return cor_para_id, cores_bgr, id_para_nome, ignore_bgr
|
|
||||||
|
|
||||||
def _carregar_modelo(self):
|
|
||||||
try:
|
try:
|
||||||
self.modelo = ort.InferenceSession(
|
self.pred_rgb[:] = self.lut[predictions]
|
||||||
self.path_modelo,
|
|
||||||
providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
|
|
||||||
)
|
|
||||||
self.input_name = self.modelo.get_inputs()[0].name
|
|
||||||
self.output_name = self.modelo.get_outputs()[0].name
|
|
||||||
|
|
||||||
# Carrega as classes e cores
|
mask_color = self.pred_rgb
|
||||||
cor_para_id, self.color_map, self.classes, ignore_bgr = self._carregar_labelmap_completo(self.path_classes)
|
|
||||||
|
|
||||||
self.carregado = True
|
|
||||||
self.log = f"✅ Modelo de segmentação carregado com sucesso. Classes: {self.classes}, Color Map: {self.color_map}"
|
|
||||||
print(self.log)
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.log = f"❌ Erro ao carregar modelo: {e}"
|
|
||||||
self.carregado = False
|
|
||||||
return False
|
|
||||||
|
|
||||||
def segmentar(self, rgb_frame):
|
|
||||||
if not self.carregado:
|
|
||||||
if not self._carregar_modelo():
|
|
||||||
return None, self.log
|
|
||||||
|
|
||||||
# 🔧 MOCK PARA TESTE: sobrescreve o frame com imagem local
|
|
||||||
if self.use_mock:
|
|
||||||
img_mock = cv2.imread(self.img_mock)
|
|
||||||
if img_mock is None:
|
|
||||||
self.log = "❌ Imagem de teste não encontrada!"
|
|
||||||
return None, self.log
|
|
||||||
# Redimensiona o mock para o mesmo shape do RGB real (ex: da câmera ou do sistema)
|
|
||||||
rgb_frame = cv2.resize(img_mock, (rgb_frame.shape[1], rgb_frame.shape[0]))
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 🔸 Preprocessamento
|
|
||||||
mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
|
|
||||||
std = np.array([0.229, 0.224, 0.225], dtype=np.float32)
|
|
||||||
img_resized = cv2.resize(rgb_frame, self.resolucao).astype(np.float32) / 255.0
|
|
||||||
img_resized = (img_resized - mean) / std
|
|
||||||
input_blob = img_resized.transpose(2, 0, 1)
|
|
||||||
input_blob = np.expand_dims(input_blob, axis=0).astype(np.float32)
|
|
||||||
|
|
||||||
# 🔸 Inferência
|
|
||||||
|
|
||||||
# Executa a inferência
|
|
||||||
outputs = self.modelo.run(None, {self.input_name: input_blob})
|
|
||||||
prediction = outputs[0] # shape: (1, num_classes, H, W)
|
|
||||||
# Seleciona a classe com maior score
|
|
||||||
prediction = prediction.squeeze(0).argmax(axis=0)
|
|
||||||
|
|
||||||
self.dados_visuais = self._analisar_corredor_visual(prediction)
|
|
||||||
|
|
||||||
# 🔸 Redimensiona máscara para o tamanho original da imagem
|
|
||||||
mask_resized = cv2.resize(prediction.astype(np.uint8), (rgb_frame.shape[1], rgb_frame.shape[0]), interpolation=cv2.INTER_NEAREST)
|
|
||||||
|
|
||||||
# 🔸 Cria máscara colorida com LUT vetorizada
|
|
||||||
lut = np.zeros((256, 3), dtype=np.uint8)
|
|
||||||
for i, color in enumerate(self.color_map):
|
|
||||||
lut[i] = color
|
|
||||||
|
|
||||||
mask_color = lut[mask_resized]
|
|
||||||
frame_color = encode_image_base64(mask_color)
|
frame_color = encode_image_base64(mask_color)
|
||||||
|
|
||||||
#perfil_corredor = self._calcular_perfil_corredor(mask_resized, depth_frame, fov_h, 640, 24)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"timestamp": time.time(),
|
"timestamp": time.time(),
|
||||||
"frame": {
|
"frame": {
|
||||||
|
|
@ -132,9 +47,29 @@ class SegmentacaoManager:
|
||||||
"frame": frame_color
|
"frame": frame_color
|
||||||
},
|
},
|
||||||
"mask_color": mask_color,
|
"mask_color": mask_color,
|
||||||
"classes": mask_resized,
|
"classes": predictions
|
||||||
"dados_visuais": self.dados_visuais
|
}
|
||||||
}, self.log
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro ao processar predictions: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def segmentar(self, predictions):
|
||||||
|
try:
|
||||||
|
# 🔸 Constrói a máscara colorida e outras saídas com base na predictions já pronta
|
||||||
|
resultado = self._segmentar_predictions(predictions)
|
||||||
|
if resultado is None:
|
||||||
|
print("[Erro] Segmentação vazia ou falhou")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if predictions is None:
|
||||||
|
print("[Erro] Máscara de classes não encontrada no resultado")
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.dados_visuais = self._analisar_corredor_visual(predictions)
|
||||||
|
resultado["dados_visuais"] = self.dados_visuais
|
||||||
|
|
||||||
|
return resultado, self.log
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log = f"❌ Erro na segmentação: {e}"
|
self.log = f"❌ Erro na segmentação: {e}"
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -15,6 +15,7 @@ class CameraManager:
|
||||||
def __init__(self, mostrar_log):
|
def __init__(self, mostrar_log):
|
||||||
self.mostrar_log = mostrar_log
|
self.mostrar_log = mostrar_log
|
||||||
self.mx_id = None
|
self.mx_id = None
|
||||||
|
self.tempo_saude = 10
|
||||||
self.reiniciar_status()
|
self.reiniciar_status()
|
||||||
|
|
||||||
def reiniciar_status(self):
|
def reiniciar_status(self):
|
||||||
|
|
@ -22,8 +23,10 @@ class CameraManager:
|
||||||
self.operante = False
|
self.operante = False
|
||||||
self.iniciando = False
|
self.iniciando = False
|
||||||
self.weed_detector = None
|
self.weed_detector = None
|
||||||
|
self._ultimo_rgb_frame = None
|
||||||
self._ultima_analise = {}
|
self._ultima_analise = {}
|
||||||
self._ts_segmentacao_anterior = 0
|
self._ts_segmentacao_anterior = 0
|
||||||
|
self._ultima_saude_ts = 0
|
||||||
|
|
||||||
def inicializar(self, mx_id):
|
def inicializar(self, mx_id):
|
||||||
if self.iniciando:
|
if self.iniciando:
|
||||||
|
|
@ -68,11 +71,16 @@ class CameraManager:
|
||||||
self.atualizar_saude_camera()
|
self.atualizar_saude_camera()
|
||||||
|
|
||||||
def atualizar_saude_camera(self):
|
def atualizar_saude_camera(self):
|
||||||
|
self._ultima_saude_ts = time.time()
|
||||||
|
#self.mostrar_log("Atualizando saude da camera...")
|
||||||
|
try:
|
||||||
if self.camera is not None:
|
if self.camera is not None:
|
||||||
self.camera.atualizar_saude()
|
self.camera.atualizar_saude()
|
||||||
elif self.mx_id is not None:
|
elif self.mx_id is not None:
|
||||||
from camera_worker.manager import definir_saude_camera
|
from camera_worker.manager import definir_saude_camera
|
||||||
definir_saude_camera(self.mx_id, StatusModulo.DESCONECTADO, 0, ["desconectado"], False, {})
|
definir_saude_camera(self.mx_id, StatusModulo.DESCONECTADO, 0, ["desconectado"], False, {})
|
||||||
|
except Exception as e:
|
||||||
|
self.mostrar_log(f"[saude] erro: {e}")
|
||||||
|
|
||||||
def get_rgb_frame(self):
|
def get_rgb_frame(self):
|
||||||
if self.camera is None:
|
if self.camera is None:
|
||||||
|
|
@ -133,16 +141,16 @@ class CameraManager:
|
||||||
|
|
||||||
def _iniciar_loop_analise_continua(self, freq):
|
def _iniciar_loop_analise_continua(self, freq):
|
||||||
def loop():
|
def loop():
|
||||||
ultima_atualizacao_saude = 0
|
self._ultima_saude_ts = 0
|
||||||
while True:
|
while True:
|
||||||
if self.camera is None:
|
if self.camera is None:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
if (t0 - ultima_atualizacao_saude) >= 5:
|
if (t0 - self._ultima_saude_ts) >= self.tempo_saude:
|
||||||
self.camera.atualizar_saude()
|
self._ultima_saude_ts = time.time()
|
||||||
ultima_atualizacao_saude = time.time()
|
self.atualizar_saude_camera()
|
||||||
try:
|
try:
|
||||||
status = StatusModulo((self.camera.ultima_saude or {}).get("status", StatusModulo.DESCONECTADO.value))
|
status = StatusModulo((self.camera.ultima_saude or {}).get("status", StatusModulo.DESCONECTADO.value))
|
||||||
if status == StatusModulo.DESCONECTADO:
|
if status == StatusModulo.DESCONECTADO:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pasta_mascaras = os.path.join(MODELO, "dataset", "original", "masks")
|
||||||
# Regras de substituição (cores em RGB)
|
# Regras de substituição (cores em RGB)
|
||||||
# Exemplo: trocar (255, 0, 0) por branco (255,255,255) com tolerância 10
|
# Exemplo: trocar (255, 0, 0) por branco (255,255,255) com tolerância 10
|
||||||
SUBSTITUICOES = [
|
SUBSTITUICOES = [
|
||||||
#{"target_rgb": (255, 0, 0), "tolerancia": 10, "replace_rgb": (255, 255, 255)}, # vermelho -> branco
|
#{"target_rgb": (255, 255, 255), "tolerancia": 10, "replace_rgb": (128, 0, 0)}, # branco -> vermelho
|
||||||
{"target_rgb": (128, 0, 0), "tolerancia": 50, "replace_rgb": (128, 0, 0)}, # chao
|
{"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, 128, 0), "tolerancia": 50, "replace_rgb": (0, 128, 0)}, # erva
|
||||||
{"target_rgb": (0, 0, 128), "tolerancia": 50, "replace_rgb": (0, 0, 128)}, # cana
|
{"target_rgb": (0, 0, 128), "tolerancia": 50, "replace_rgb": (0, 0, 128)}, # cana
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import json, os, cv2, numpy as np
|
import json, os, cv2
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import albumentations as A
|
import albumentations as A
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,89 +18,290 @@ MODEL_NAME = config["model_name"]
|
||||||
RESOLUCAO = config["resolucao"]
|
RESOLUCAO = config["resolucao"]
|
||||||
ROI_INICIO = config["roi_inicio"]
|
ROI_INICIO = config["roi_inicio"]
|
||||||
ROI_TAMANHO = config["roi_tamanho"]
|
ROI_TAMANHO = config["roi_tamanho"]
|
||||||
|
MAIN_CLASS_NAME = config["main_class_name"]
|
||||||
save_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME)
|
save_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME)
|
||||||
dataset_path = os.path.join(MODELO, "dataset")
|
dataset_path = os.path.join(MODELO, "dataset")
|
||||||
split_folder = "train"
|
|
||||||
labelmap_path = os.path.join(dataset_path, "labelmap.txt")
|
labelmap_path = os.path.join(dataset_path, "labelmap.txt")
|
||||||
batch_size = 8
|
batch_size = 8
|
||||||
num_workers = 4
|
num_workers = 4
|
||||||
|
|
||||||
|
# ---- Helpers de métricas ----
|
||||||
|
@torch.no_grad()
|
||||||
|
def confmat_update(confmat, pred, target, num_classes, ignore_index=None):
|
||||||
|
# pred, target: (B,H,W)
|
||||||
|
if ignore_index is not None:
|
||||||
|
mask = target != ignore_index
|
||||||
|
target = target[mask]
|
||||||
|
pred = pred[mask]
|
||||||
|
k = (target * num_classes + pred).to(torch.int64)
|
||||||
|
binc = torch.bincount(k, minlength=num_classes**2)
|
||||||
|
confmat += binc.reshape(num_classes, num_classes)
|
||||||
|
return confmat
|
||||||
|
|
||||||
|
def metrics_from_confmat(confmat, main_class_id=None):
|
||||||
|
# confmat: CxC
|
||||||
|
cm = confmat.float()
|
||||||
|
tp = torch.diag(cm)
|
||||||
|
fp = cm.sum(0) - tp
|
||||||
|
fn = cm.sum(1) - tp
|
||||||
|
denom_iou = tp + fp + fn + 1e-7
|
||||||
|
iou_per_class = tp / denom_iou
|
||||||
|
miou = iou_per_class.mean().item()
|
||||||
|
pix_acc = tp.sum() / (cm.sum() + 1e-7)
|
||||||
|
|
||||||
|
main_class_metrics = None
|
||||||
|
if main_class_id is not None and 0 <= main_class_id < cm.shape[0]:
|
||||||
|
p = tp[main_class_id] / (tp[main_class_id] + fp[main_class_id] + 1e-7)
|
||||||
|
r = tp[main_class_id] / (tp[main_class_id] + fn[main_class_id] + 1e-7)
|
||||||
|
f1 = 2 * p * r / (p + r + 1e-7)
|
||||||
|
main_class_metrics = {
|
||||||
|
"precision": p.item(),
|
||||||
|
"recall": r.item(),
|
||||||
|
"f1": f1.item(),
|
||||||
|
"iou": iou_per_class[main_class_id].item(),
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"miou": miou,
|
||||||
|
"pixel_acc": pix_acc.item(),
|
||||||
|
"iou_per_class": iou_per_class.cpu().tolist(),
|
||||||
|
"main_class": main_class_metrics
|
||||||
|
}
|
||||||
|
|
||||||
def train(args):
|
def train(args):
|
||||||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||||
print(f"Device: {device}")
|
print(f"Device: {device}")
|
||||||
|
|
||||||
ds_train = ROISegDataset(os.path.join(dataset_path, "split", split_folder), save_path, ROI_INICIO, ROI_TAMANHO, RESOLUCAO[0], RESOLUCAO[1], labelmap_path)
|
# --- Dataset ---
|
||||||
dl_train = DataLoader(ds_train, batch_size=batch_size, shuffle=True, num_workers=num_workers, pin_memory=True)
|
ds_train = ROISegDataset(
|
||||||
|
os.path.join(dataset_path, "split", "train"),
|
||||||
|
save_path, ROI_INICIO, ROI_TAMANHO,
|
||||||
|
RESOLUCAO[0], RESOLUCAO[1], labelmap_path
|
||||||
|
)
|
||||||
|
ds_val = ROISegDataset(
|
||||||
|
os.path.join(dataset_path, "split", "val"),
|
||||||
|
save_path, ROI_INICIO, ROI_TAMANHO,
|
||||||
|
RESOLUCAO[0], RESOLUCAO[1], labelmap_path
|
||||||
|
)
|
||||||
|
|
||||||
model = FastSCNN(num_classes=len(ds_train.classes)).to(device)
|
dl_train = DataLoader(ds_train, batch_size=batch_size, shuffle=True, num_workers=num_workers, pin_memory=True)
|
||||||
|
dl_val = DataLoader(ds_val, batch_size=batch_size, shuffle=False, num_workers=num_workers, pin_memory=True)
|
||||||
|
|
||||||
|
# Detecta automaticamente o ID da classe ERVA
|
||||||
|
main_class_id = None
|
||||||
|
try:
|
||||||
|
if hasattr(ds_train, "classes") and isinstance(ds_train.classes, dict):
|
||||||
|
for k, v in ds_train.classes.items():
|
||||||
|
if isinstance(v, str) and MAIN_CLASS_NAME in v.lower():
|
||||||
|
main_class_id = k
|
||||||
|
break
|
||||||
|
elif isinstance(ds_train.classes, (list, tuple)):
|
||||||
|
main_class_id = next((i for i, c in enumerate(ds_train.classes) if isinstance(c, str) and MAIN_CLASS_NAME in c.lower()), None)
|
||||||
|
|
||||||
|
if main_class_id is not None:
|
||||||
|
print(f"🌿 Classe PRIMARIA detectada: id={main_class_id}, nome='{ds_train.classes[main_class_id]}'")
|
||||||
|
else:
|
||||||
|
print("⚠️ Classe PRIMARIA não encontrada; métricas específicas da classe primaria serão puladas.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Erro ao detectar classe PRIMARIA: {e}")
|
||||||
|
|
||||||
|
num_classes = len(ds_train.classes)
|
||||||
|
|
||||||
|
# --- Modelo / Otimizador / Schedulers ---
|
||||||
|
model = FastSCNN(num_classes=num_classes).to(device)
|
||||||
criterion = nn.CrossEntropyLoss(ignore_index=ds_train.ignore_id)
|
criterion = nn.CrossEntropyLoss(ignore_index=ds_train.ignore_id)
|
||||||
optimizer = optim.AdamW(model.parameters(), lr=args.lr, weight_decay=1e-4)
|
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
|
|
||||||
loss_history = []
|
|
||||||
|
|
||||||
|
# Scheduler inteligente: começa em Cosine, muda pra Plateau se travar
|
||||||
|
min_lr = getattr(args, "min_lr", 1e-6)
|
||||||
|
plateau_factor = getattr(args, "plateau_factor", 0.5)
|
||||||
|
plateau_patience = getattr(args, "plateau_patience", 6) # épocas sem melhora antes de trocar
|
||||||
|
plateau_cooldown = getattr(args, "plateau_cooldown", 1)
|
||||||
|
|
||||||
|
cosine = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=args.epochs, eta_min=min_lr)
|
||||||
|
plateau = torch.optim.lr_scheduler.ReduceLROnPlateau(
|
||||||
|
optimizer, mode="min", factor=plateau_factor,
|
||||||
|
patience=plateau_patience, cooldown=plateau_cooldown,
|
||||||
|
min_lr=min_lr, verbose=True
|
||||||
|
)
|
||||||
|
active_sched = "cosine"
|
||||||
|
|
||||||
|
scaler = torch.cuda.amp.GradScaler(enabled=args.amp)
|
||||||
|
|
||||||
|
start_epoch = 1
|
||||||
|
best_val_loss = float("inf")
|
||||||
|
best_main_class_f1 = -1.0
|
||||||
|
train_loss_history, val_loss_history, lr_history = [], [], []
|
||||||
|
f1_history, miou_history = [], []
|
||||||
|
|
||||||
|
# --- no topo (config) ---
|
||||||
|
patience_loss = 12 # ligeiramente > plateau_patience + 2
|
||||||
|
patience_f1 = 6 # deixa o F1 respirar
|
||||||
|
delta_f1_min = 0.0015 # ignora ruído
|
||||||
|
grace_after_switch = 4 # épocas de graça após mudar pro Plateau
|
||||||
|
|
||||||
|
no_imp_loss = 0
|
||||||
|
no_imp_f1 = 0
|
||||||
|
epochs_since_switch = 0
|
||||||
|
active_sched = "cosine" # como já está
|
||||||
|
|
||||||
|
# --- Checkpoint ---
|
||||||
if args.checkpoint and os.path.exists(args.checkpoint):
|
if args.checkpoint and os.path.exists(args.checkpoint):
|
||||||
print(f"🔁 Carregando modelo salvo: {args.checkpoint}")
|
print(f"🔁 Carregando modelo salvo: {args.checkpoint}")
|
||||||
checkpoint = torch.load(args.checkpoint, map_location=device)
|
checkpoint = torch.load(args.checkpoint, map_location=device)
|
||||||
|
|
||||||
if "model" in checkpoint:
|
if "model" in checkpoint:
|
||||||
model.load_state_dict(checkpoint["model"])
|
model.load_state_dict(checkpoint["model"])
|
||||||
optimizer.load_state_dict(checkpoint["optimizer"])
|
optimizer.load_state_dict(checkpoint["optimizer"])
|
||||||
scaler.load_state_dict(checkpoint["scaler"])
|
scaler.load_state_dict(checkpoint["scaler"])
|
||||||
start_epoch = checkpoint.get("epoch", 1) + 1
|
start_epoch = checkpoint.get("epoch", 1) + 1
|
||||||
best_loss = checkpoint.get("best_loss", 1e9)
|
best_val_loss = checkpoint.get("best_val_loss", float("inf"))
|
||||||
else:
|
else:
|
||||||
# Caso seja apenas um .pth com model.state_dict() direto
|
|
||||||
model.load_state_dict(checkpoint)
|
model.load_state_dict(checkpoint)
|
||||||
|
|
||||||
|
# --- Loop de treino ---
|
||||||
for epoch in range(start_epoch, args.epochs + 1):
|
for epoch in range(start_epoch, args.epochs + 1):
|
||||||
model.train()
|
|
||||||
total_loss = 0
|
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
|
|
||||||
|
# ----- Treino -----
|
||||||
|
model.train()
|
||||||
|
running_train_loss = 0
|
||||||
for x, y in dl_train:
|
for x, y in dl_train:
|
||||||
x, y = x.to(device), y.to(device)
|
x, y = x.to(device), y.to(device)
|
||||||
optimizer.zero_grad()
|
optimizer.zero_grad(set_to_none=True)
|
||||||
with torch.cuda.amp.autocast(enabled=args.amp):
|
with torch.cuda.amp.autocast(enabled=args.amp):
|
||||||
logits = model(x)
|
logits = model(x)
|
||||||
loss = criterion(logits, y)
|
loss = criterion(logits, y)
|
||||||
scaler.scale(loss).backward()
|
scaler.scale(loss).backward()
|
||||||
scaler.step(optimizer)
|
scaler.step(optimizer)
|
||||||
scaler.update()
|
scaler.update()
|
||||||
total_loss += loss.item() * x.size(0)
|
running_train_loss += loss.item() * x.size(0)
|
||||||
|
|
||||||
avg_loss = total_loss / len(ds_train)
|
avg_train_loss = running_train_loss / len(ds_train)
|
||||||
loss_history.append(avg_loss)
|
train_loss_history.append(avg_train_loss)
|
||||||
print(f"[{epoch}/{args.epochs}] loss={avg_loss:.4f} time={time.time()-t0:.1f}s")
|
|
||||||
|
|
||||||
if avg_loss < best_loss:
|
# ----- Validação + métricas -----
|
||||||
best_loss = avg_loss
|
model.eval()
|
||||||
|
running_val_loss = 0
|
||||||
|
confmat = torch.zeros((num_classes, num_classes), dtype=torch.int64, device=device)
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
for x, y in dl_val:
|
||||||
|
x, y = x.to(device), y.to(device)
|
||||||
|
with torch.cuda.amp.autocast(enabled=args.amp):
|
||||||
|
logits = model(x)
|
||||||
|
loss = criterion(logits, y)
|
||||||
|
running_val_loss += loss.item() * x.size(0)
|
||||||
|
|
||||||
|
pred = logits.argmax(1)
|
||||||
|
confmat = confmat_update(confmat, pred, y, num_classes, ignore_index=ds_train.ignore_id)
|
||||||
|
|
||||||
|
avg_val_loss = running_val_loss / len(ds_val)
|
||||||
|
val_loss_history.append(avg_val_loss)
|
||||||
|
|
||||||
|
m = metrics_from_confmat(confmat, main_class_id=main_class_id)
|
||||||
|
miou_history.append(m["miou"])
|
||||||
|
main_class_f1 = m["main_class"]["f1"] if (m["main_class"] is not None) else None
|
||||||
|
if main_class_f1 is not None:
|
||||||
|
f1_history.append(main_class_f1)
|
||||||
|
cur_lr = optimizer.param_groups[0]["lr"]
|
||||||
|
lr_history.append(cur_lr)
|
||||||
|
|
||||||
|
elapsed = time.time() - t0
|
||||||
|
msg = (f"[{epoch}/{args.epochs}] "
|
||||||
|
f"train_loss={avg_train_loss:.4f} "
|
||||||
|
f"val_loss={avg_val_loss:.4f} "
|
||||||
|
f"mIoU={m['miou']:.4f} "
|
||||||
|
f"pixAcc={m['pixel_acc']:.4f} "
|
||||||
|
f"lr={cur_lr:.2e} "
|
||||||
|
f"time={elapsed:.1f}s")
|
||||||
|
if main_class_f1 is not None:
|
||||||
|
msg += f" | {MAIN_CLASS_NAME}: F1={main_class_f1:.4f} IoU={m['main_class']['iou']:.4f}"
|
||||||
|
print(msg)
|
||||||
|
|
||||||
|
# ----- Tracking de melhora por LOSS -----
|
||||||
|
improved_loss = avg_val_loss < best_val_loss - 1e-6
|
||||||
|
if improved_loss:
|
||||||
|
best_val_loss = avg_val_loss
|
||||||
|
no_imp_loss = 0
|
||||||
|
# checkpoint por loss
|
||||||
torch.save(model.state_dict(), os.path.join(save_path, f"{MODEL_NAME}_best.pth"))
|
torch.save(model.state_dict(), os.path.join(save_path, f"{MODEL_NAME}_best.pth"))
|
||||||
torch.save({
|
torch.save({
|
||||||
"model": model.state_dict(),
|
"model": model.state_dict(),
|
||||||
"optimizer": optimizer.state_dict(),
|
"optimizer": optimizer.state_dict(),
|
||||||
"scaler": scaler.state_dict(),
|
"scaler": scaler.state_dict(),
|
||||||
"epoch": epoch,
|
"epoch": epoch,
|
||||||
"best_loss": best_loss
|
"best_val_loss": best_val_loss
|
||||||
}, os.path.join(save_path, f"{MODEL_NAME}_best_checkpoint.pth"))
|
}, os.path.join(save_path, f"{MODEL_NAME}_best_checkpoint.pth"))
|
||||||
print("✅ Novo melhor modelo salvo!")
|
print("✅ Novo melhor modelo salvo (val_loss).")
|
||||||
|
else:
|
||||||
|
no_imp_loss += 1
|
||||||
|
|
||||||
# Plot da curva de perda
|
# ----- Tracking + checkpoint por F1 da classe principal -----
|
||||||
|
if main_class_f1 is not None:
|
||||||
|
if main_class_f1 > best_main_class_f1 + delta_f1_min:
|
||||||
|
best_main_class_f1 = main_class_f1
|
||||||
|
no_imp_f1 = 0
|
||||||
|
torch.save(model.state_dict(), os.path.join(save_path, f"{MODEL_NAME}_best_f1_{MAIN_CLASS_NAME}.pth"))
|
||||||
|
print(f"🌿💾 Checkpoint salvo (melhor F1 da {MAIN_CLASS_NAME}).")
|
||||||
|
else:
|
||||||
|
no_imp_f1 += 1
|
||||||
|
else:
|
||||||
|
# se não houver F1 (ex: id não definido), ignora o critério
|
||||||
|
no_imp_f1 = 0
|
||||||
|
|
||||||
|
# ----- Scheduler inteligente -----
|
||||||
|
if active_sched == "cosine":
|
||||||
|
# se travar por plateau_patience, troca pra ReduceLROnPlateau
|
||||||
|
if no_imp_loss >= plateau_patience:
|
||||||
|
active_sched = "plateau"
|
||||||
|
print("🔁 Mudando scheduler: Cosine → ReduceLROnPlateau (platô detectado).")
|
||||||
|
# resets ao trocar
|
||||||
|
no_imp_loss = 0
|
||||||
|
no_imp_f1 = 0
|
||||||
|
epochs_since_switch = 0
|
||||||
|
plateau.step(avg_val_loss) # primeiro passo do plateau
|
||||||
|
# (opcional) “adiantar” a queda do LR:
|
||||||
|
for g in optimizer.param_groups:
|
||||||
|
g['lr'] = max(g['lr'] * plateau_factor, min_lr)
|
||||||
|
else:
|
||||||
|
cosine.step()
|
||||||
|
else:
|
||||||
|
plateau.step(avg_val_loss)
|
||||||
|
epochs_since_switch += 1
|
||||||
|
|
||||||
|
# ----- Log de estagnação -----
|
||||||
|
print(f"⏳ Sem melhora — loss: {no_imp_loss}/{patience_loss}, {MAIN_CLASS_NAME}: {no_imp_f1}/{patience_f1}")
|
||||||
|
|
||||||
|
# ----- Early stopping bi-critério (com 'graça' após switch) -----
|
||||||
|
if (no_imp_loss >= patience_loss and
|
||||||
|
(main_class_f1 is None or no_imp_f1 >= patience_f1) and
|
||||||
|
(active_sched == "cosine" or epochs_since_switch >= grace_after_switch)):
|
||||||
|
print("⏹ Early stopping: loss e F1 sem melhora (com período de graça respeitado).")
|
||||||
|
break
|
||||||
|
|
||||||
|
# ----- Plots periódicos -----
|
||||||
if epoch % 5 == 0 or epoch == args.epochs:
|
if epoch % 5 == 0 or epoch == args.epochs:
|
||||||
x_epochs = list(range(start_epoch, start_epoch + len(loss_history)))
|
x_epochs = list(range(start_epoch, start_epoch + len(train_loss_history)))
|
||||||
|
# Loss
|
||||||
plt.figure()
|
plt.figure()
|
||||||
plt.plot(x_epochs, loss_history, marker="o", label="Loss de Treinamento")
|
plt.plot(x_epochs, train_loss_history, marker="o", label="Train Loss")
|
||||||
plt.xlabel("Época")
|
plt.plot(x_epochs, val_loss_history, marker="s", label="Val Loss")
|
||||||
plt.ylabel("Loss")
|
plt.xlabel("Época"); plt.ylabel("Loss"); plt.grid(True); plt.legend(); plt.title("Curva de Loss")
|
||||||
plt.grid(True)
|
|
||||||
plt.legend()
|
|
||||||
plt.title("Curva de Loss")
|
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
plt.savefig(os.path.join(save_path, "loss_curve.png"))
|
plt.savefig(os.path.join(save_path, "loss_curve.png")); plt.close()
|
||||||
plt.close()
|
# LR
|
||||||
|
plt.figure()
|
||||||
|
plt.plot(x_epochs, lr_history, marker=".")
|
||||||
|
plt.xlabel("Época"); plt.ylabel("LR"); plt.grid(True); plt.title("Learning Rate")
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(os.path.join(save_path, "lr_curve.png")); plt.close()
|
||||||
|
# mIoU e F1(erva)
|
||||||
|
plt.figure()
|
||||||
|
plt.plot(x_epochs, miou_history, marker="^", label="mIoU")
|
||||||
|
if len(f1_history) == len(miou_history):
|
||||||
|
plt.plot(x_epochs, f1_history, marker="*", label=f"F1 {MAIN_CLASS_NAME}")
|
||||||
|
plt.xlabel("Época"); plt.ylabel("Score"); plt.grid(True); plt.legend(); plt.title(f"mIoU / F1({MAIN_CLASS_NAME})")
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(os.path.join(save_path, "metrics_curve.png")); plt.close()
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
ap = argparse.ArgumentParser()
|
ap = argparse.ArgumentParser()
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,12 @@ MODEL_NAME = config["model_name"]
|
||||||
RESOLUCAO = config["resolucao"]
|
RESOLUCAO = config["resolucao"]
|
||||||
ROI_INICIO = config["roi_inicio"]
|
ROI_INICIO = config["roi_inicio"]
|
||||||
ROI_TAMANHO = config["roi_tamanho"]
|
ROI_TAMANHO = config["roi_tamanho"]
|
||||||
|
MAIN_CLASS_NAME = config["main_class_name"]
|
||||||
|
use_main_class = config["use_main_class"]
|
||||||
dataset_path = os.path.join(MODELO, "dataset")
|
dataset_path = os.path.join(MODELO, "dataset")
|
||||||
split_folder = "test"
|
split_folder = "test"
|
||||||
labelmap_path = os.path.join(dataset_path, "labelmap.txt")
|
labelmap_path = os.path.join(dataset_path, "labelmap.txt")
|
||||||
model_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME, MODEL_NAME + "_best.pth")
|
model_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME, f"{MODEL_NAME}_best{f'_f1_{MAIN_CLASS_NAME}' if use_main_class else ''}.pth")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import torch
|
import torch
|
||||||
from fast_scnn import FastSCNN
|
from fast_scnn import FastSCNN, FastSCNNWithNorm
|
||||||
from utils import carregar_labelmap_completo
|
from utils import carregar_labelmap_completo
|
||||||
|
|
||||||
# ⚙️ Configurações
|
# ⚙️ Configurações
|
||||||
|
|
@ -10,21 +10,23 @@ with open("config.json", "r") as f:
|
||||||
MODELO = config["camera"]
|
MODELO = config["camera"]
|
||||||
MODEL_NAME = config["model_name"]
|
MODEL_NAME = config["model_name"]
|
||||||
RESOLUCAO = config["resolucao"]
|
RESOLUCAO = config["resolucao"]
|
||||||
|
MAIN_CLASS_NAME = config["main_class_name"]
|
||||||
|
use_main_class = config["use_main_class"]
|
||||||
model_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME)
|
model_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME)
|
||||||
labelmap_path = os.path.join(MODELO, "dataset", "labelmap.txt")
|
labelmap_path = os.path.join(MODELO, "dataset", "labelmap.txt")
|
||||||
model_name = MODEL_NAME + "_best"
|
model_name = f"{MODEL_NAME}_best{f'_f1_{MAIN_CLASS_NAME}' if use_main_class else ''}"
|
||||||
|
|
||||||
dummy_input = torch.randn(1, 3, RESOLUCAO[1], RESOLUCAO[0]) # (batch, channels, height, width)
|
dummy_input = torch.randn(1, 3, RESOLUCAO[1], RESOLUCAO[0]) # (batch, channels, height, width)
|
||||||
|
|
||||||
_, _, classes, _ = carregar_labelmap_completo(labelmap_path)
|
_, _, classes, _ = carregar_labelmap_completo(labelmap_path)
|
||||||
NUM_CLASSES = len(classes)
|
NUM_CLASSES = len(classes)
|
||||||
|
|
||||||
model = FastSCNN(num_classes=NUM_CLASSES) # ajuste num_classes conforme seu labelmap
|
base = FastSCNNWithNorm(num_classes=NUM_CLASSES, to_rgb=True) # ajuste num_classes conforme seu labelmap
|
||||||
model.load_state_dict(torch.load(os.path.join(model_path, model_name + ".pth")))
|
base.backbone.load_state_dict(torch.load(os.path.join(model_path, f"{MODEL_NAME}_best.pth"), map_location="cpu"))
|
||||||
model.eval()
|
base.eval()
|
||||||
|
|
||||||
torch.onnx.export(
|
torch.onnx.export(
|
||||||
model,
|
base,
|
||||||
dummy_input,
|
dummy_input,
|
||||||
os.path.join(model_path, model_name + ".onnx"),
|
os.path.join(model_path, model_name + ".onnx"),
|
||||||
input_names=["input"],
|
input_names=["input"],
|
||||||
|
|
@ -55,11 +57,11 @@ blob_path = blobconverter.from_openvino(
|
||||||
data_type="FP16",
|
data_type="FP16",
|
||||||
shaves=6,
|
shaves=6,
|
||||||
output_dir=model_path,
|
output_dir=model_path,
|
||||||
compile_params=[
|
#compile_params=[
|
||||||
"-ip U8", # entrada em bytes; compila a conversão interna p/ FP16
|
# "-ip U8", # entrada em bytes; compila a conversão interna p/ FP16
|
||||||
"--mean_values=[123.675,116.28,103.53]",
|
#"--mean_values=[123.675,116.28,103.53]",
|
||||||
"--scale_values=[58.395,57.12,57.375]",
|
#"--scale_values=[58.395,57.12,57.375]",
|
||||||
#"--reverse_input_channels" # pq você treinou em RGB
|
#"--reverse_input_channels" # pq você treinou em RGB
|
||||||
],
|
#],
|
||||||
)
|
)
|
||||||
print(f"Blob salvo em: {blob_path}")
|
print(f"Blob salvo em: {blob_path}")
|
||||||
|
|
@ -10,13 +10,14 @@ from utils import converter_mask_ids_para_rgb, carregar_labelmap_completo
|
||||||
with open("config.json", "r") as f:
|
with open("config.json", "r") as f:
|
||||||
config = json.load(f)
|
config = json.load(f)
|
||||||
MODELO = config["camera"]
|
MODELO = config["camera"]
|
||||||
MODEL_NAME = "ervas_medium" #config["model_name"]
|
MODEL_NAME = config["model_name"]
|
||||||
RESOLUCAO = config["resolucao"]
|
RESOLUCAO = config["resolucao"]
|
||||||
ROI_INICIO = 0.0
|
ROI_INICIO = 0.0
|
||||||
ROI_TAMANHO = 1.0
|
ROI_TAMANHO = 1.0
|
||||||
blob_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME, MODEL_NAME + "_best_openvino_2022.1_6shave.blob")
|
MAIN_CLASS_NAME = config["main_class_name"]
|
||||||
|
use_main_class = config["use_main_class"]
|
||||||
|
blob_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME, f"{MODEL_NAME}_best{f'_f1_{MAIN_CLASS_NAME}' if use_main_class else ''}_openvino_2022.1_6shave.blob")
|
||||||
labelmap_path = os.path.join(MODELO, "dataset", "labelmap.txt")
|
labelmap_path = os.path.join(MODELO, "dataset", "labelmap.txt")
|
||||||
model_name = MODEL_NAME + "_best"
|
|
||||||
|
|
||||||
# Carregar mapa de cores
|
# Carregar mapa de cores
|
||||||
_, colormap_rgb, classes, ignore_rgb = carregar_labelmap_completo(labelmap_path)
|
_, colormap_rgb, classes, ignore_rgb = carregar_labelmap_completo(labelmap_path)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
"camera": "oak-1",
|
"camera": "oak-1",
|
||||||
"modelo": "fast_scnn",
|
"modelo": "fast_scnn",
|
||||||
"model_name": "ervas_medium_new",
|
"model_name": "ervas_medium_new",
|
||||||
|
"main_class_name": "erva",
|
||||||
|
"use_main_class": true,
|
||||||
"resolucao": [512, 288],
|
"resolucao": [512, 288],
|
||||||
"roi_inicio": 0.0,
|
"roi_inicio": 0.0,
|
||||||
"roi_tamanho": 1.0
|
"roi_tamanho": 1.0
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,26 @@
|
||||||
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
|
|
||||||
|
class FastSCNNWithNorm(nn.Module):
|
||||||
|
def __init__(self, num_classes, mean=(123.675,116.28,103.53), std=(58.395,57.12,57.375), to_rgb=True):
|
||||||
|
super().__init__()
|
||||||
|
self.backbone = FastSCNN(num_classes=num_classes)
|
||||||
|
# registra constantes como buffers (vão pro ONNX)
|
||||||
|
m = torch.tensor(mean).view(1,3,1,1)
|
||||||
|
s = torch.tensor(std).view(1,3,1,1)
|
||||||
|
self.register_buffer("mean", m, persistent=False)
|
||||||
|
self.register_buffer("std", s, persistent=False)
|
||||||
|
self.to_rgb = to_rgb # se tua câmera entregar BGR, podemos inverter canais aqui
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
# x chega como FP16 (convertido pelo NN node), mas garantimos float32 pra estabilidade das consts
|
||||||
|
x = x.float()
|
||||||
|
if self.to_rgb:
|
||||||
|
# se a ColorCamera estiver em BGR, inverte canais aqui (BGR->RGB)
|
||||||
|
x = x[:, [2,1,0], :, :]
|
||||||
|
x = (x - self.mean) / self.std
|
||||||
|
return self.backbone(x)
|
||||||
|
|
||||||
class FastSCNN(nn.Module):
|
class FastSCNN(nn.Module):
|
||||||
def __init__(self, num_classes):
|
def __init__(self, num_classes):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Reference in New Issue