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>();
|
||||
picsCamSoloOverlay = new List<PictureBox>();
|
||||
flwCameras.Controls.Clear();
|
||||
LogsCam_Solo = new List<List<CameraWorkerItemModel>>();
|
||||
foreach (string cam in data.cam_solo)
|
||||
{
|
||||
var log = JsonConvert.DeserializeObject<List<CameraWorkerItemModel>>(OperacaoModel.DeserializarDadosOperacao(ofd.FileName, cam)).ToList();
|
||||
|
|
|
|||
|
|
@ -136,8 +136,11 @@ namespace AgroBase.Models
|
|||
{
|
||||
// Para gráficos de linha
|
||||
var _momentos = Momentos.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);
|
||||
if (Serie.Valores.Count() > 0)
|
||||
{
|
||||
var _valores = Serie.Valores.GetRange(startIdx, endIdx - startIdx + 1).ToList();
|
||||
PopularSerieGraficoLinha(chart, Serie.Titulo, _momentos, _valores, Serie.Visivel, Serie.MostrarValor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ namespace AgroBase.Services.Operadores
|
|||
("camera_caminho_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CameraCaminho?.Id ?? ""),
|
||||
("camera_solo_id", Variaveis.OperacaoEmAndamento.DispSen?.Dados?.CamerasSolo?.FirstOrDefault()?.Id ?? ""),
|
||||
("path_ia_model_ruas", VersionamentoService.ArquivoModeloStreetDetector.CaminhoCompleto),
|
||||
("path_ia_labelmap_ruas", VersionamentoService.ArquivoLabelmapStreetDetector.CaminhoCompleto),
|
||||
("path_ia_model_ervas", VersionamentoService.ArquivoModeloWeedDetector.CaminhoCompleto),
|
||||
("path_ia_labelmap_ervas", VersionamentoService.ArquivoModeloLabelmapWeedDetector.CaminhoCompleto),
|
||||
("angulo_roll_max", VariaveisEquipamento.AnguloInclinacaoRollMax),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,17 @@ namespace AgroBase.Services
|
|||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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": [ ]
|
||||
} ],
|
||||
"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/08-17:00:08.341 7670 Recovering log #3
|
||||
2025/08/08-17:00:08.345 7670 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log
|
||||
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/11-17:15:07.459 43b4 Recovering log #3
|
||||
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/08-16:53:28.091 55f8 Recovering log #3
|
||||
2025/08/08-16:53:28.094 55f8 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log
|
||||
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/11-16:38:48.540 8fe4 Recovering log #3
|
||||
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/08-17:00:46.662 7670 Recovering log #3
|
||||
2025/08/08-17:00:46.666 7670 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
||||
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/11-17:17:21.485 43b4 Recovering log #3
|
||||
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/08-16:58:34.326 55f8 Recovering log #3
|
||||
2025/08/08-16:58:34.329 55f8 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log
|
||||
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/11-16:44:31.242 8fe4 Recovering log #3
|
||||
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/08-17:00:08.248 a298 Recovering log #7
|
||||
2025/08/08-17:00:08.249 a298 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log
|
||||
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/11-17:15:07.354 5f14 Recovering log #7
|
||||
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/08-16:53:28.003 4c34 Recovering log #7
|
||||
2025/08/08-16:53:28.004 4c34 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000007.log
|
||||
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/11-16:38:48.453 418c Recovering log #7
|
||||
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,
|
||||
"Arquivo": "model",
|
||||
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
|
||||
"Extensao": ".onnx",
|
||||
"Extensao": ".blob",
|
||||
"Versao": "1_1",
|
||||
"TipoArquivo": 0,
|
||||
"ArquivoDownload": "street_detector_model-1_1.onnx",
|
||||
"ArquivoDownload": "street_detector_model-1_1.blob",
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"Arquivo": "labelmap",
|
||||
"Arquivo": "model",
|
||||
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
|
||||
"Extensao": ".txt",
|
||||
"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,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_7166a4145b3904abbc4abfe2859cddb9 {
|
||||
#map_7792e3f9a50d24693529ab60471511c5 {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
|
|
@ -54,14 +54,14 @@
|
|||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_7166a4145b3904abbc4abfe2859cddb9" ></div>
|
||||
<div class="folium-map" id="map_7792e3f9a50d24693529ab60471511c5" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_7166a4145b3904abbc4abfe2859cddb9 = L.map(
|
||||
"map_7166a4145b3904abbc4abfe2859cddb9",
|
||||
var map_7792e3f9a50d24693529ab60471511c5 = L.map(
|
||||
"map_7792e3f9a50d24693529ab60471511c5",
|
||||
{
|
||||
center: [0.0, 0.0],
|
||||
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",
|
||||
{
|
||||
"minZoom": 0,
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
);
|
||||
|
||||
|
||||
tile_layer_9627b4b85e8fb117b7e79c7fddf9872d.addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
||||
tile_layer_a04e5e8300b6eacc885e71a463b29158.addTo(map_7792e3f9a50d24693529ab60471511c5);
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -116,7 +116,7 @@
|
|||
}
|
||||
trajeto_json_add({"features": []});
|
||||
|
||||
trajeto_json.addTo(map_7166a4145b3904abbc4abfe2859cddb9);
|
||||
trajeto_json.addTo(map_7792e3f9a50d24693529ab60471511c5);
|
||||
|
||||
function adicionarGeometria(novaGeometria) {
|
||||
trajeto_json.addData(novaGeometria);
|
||||
|
|
@ -179,9 +179,9 @@
|
|||
|
||||
var marcadorEquipamento = L.marker([0, 0], {
|
||||
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(
|
||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||
);
|
||||
|
|
@ -246,7 +246,7 @@
|
|||
}
|
||||
|
||||
if (foco) {
|
||||
map_7166a4145b3904abbc4abfe2859cddb9.setView(novaPosicao, map_7166a4145b3904abbc4abfe2859cddb9.getZoom());
|
||||
map_7792e3f9a50d24693529ab60471511c5.setView(novaPosicao, map_7792e3f9a50d24693529ab60471511c5.getZoom());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -268,7 +268,7 @@
|
|||
marcadorDinamico.setRotationAngle(angulo);
|
||||
|
||||
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
|
||||
map_7166a4145b3904abbc4abfe2859cddb9.setView(novaPosicao, map_7166a4145b3904abbc4abfe2859cddb9.getZoom());*/
|
||||
map_7792e3f9a50d24693529ab60471511c5.setView(novaPosicao, map_7792e3f9a50d24693529ab60471511c5.getZoom());*/
|
||||
});
|
||||
|
||||
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<meta name="viewport" content="width=device-width,
|
||||
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<style>
|
||||
#map_f21522cbe2b071ceb21cf0d5c95f5a21 {
|
||||
#map_b86da2c0cf19440a67c00659ca7d57e9 {
|
||||
position: relative;
|
||||
width: 100.0%;
|
||||
height: 100.0%;
|
||||
|
|
@ -54,14 +54,14 @@
|
|||
<body>
|
||||
|
||||
|
||||
<div class="folium-map" id="map_f21522cbe2b071ceb21cf0d5c95f5a21" ></div>
|
||||
<div class="folium-map" id="map_b86da2c0cf19440a67c00659ca7d57e9" ></div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
|
||||
var map_f21522cbe2b071ceb21cf0d5c95f5a21 = L.map(
|
||||
"map_f21522cbe2b071ceb21cf0d5c95f5a21",
|
||||
var map_b86da2c0cf19440a67c00659ca7d57e9 = L.map(
|
||||
"map_b86da2c0cf19440a67c00659ca7d57e9",
|
||||
{
|
||||
center: [0.0, 0.0],
|
||||
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",
|
||||
{
|
||||
"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({
|
||||
|
||||
|
|
@ -148,23 +148,23 @@
|
|||
}*/
|
||||
});
|
||||
};
|
||||
var geo_json_b0da161aca2ab03761c9445da3d471fe = L.geoJson(null, {
|
||||
onEachFeature: geo_json_b0da161aca2ab03761c9445da3d471fe_onEachFeature,
|
||||
var geo_json_92428d205e1566c4dea13c3b7eb4954d = L.geoJson(null, {
|
||||
onEachFeature: geo_json_92428d205e1566c4dea13c3b7eb4954d_onEachFeature,
|
||||
|
||||
...{
|
||||
}
|
||||
});
|
||||
|
||||
function geo_json_b0da161aca2ab03761c9445da3d471fe_add (data) {
|
||||
geo_json_b0da161aca2ab03761c9445da3d471fe
|
||||
function geo_json_92428d205e1566c4dea13c3b7eb4954d_add (data) {
|
||||
geo_json_92428d205e1566c4dea13c3b7eb4954d
|
||||
.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_b0da161aca2ab03761c9445da3d471fe.setStyle(function(feature) {return feature.properties.style;});
|
||||
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_92428d205e1566c4dea13c3b7eb4954d.setStyle(function(feature) {return feature.properties.style;});
|
||||
|
||||
|
||||
|
||||
geo_json_b0da161aca2ab03761c9445da3d471fe.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
geo_json_92428d205e1566c4dea13c3b7eb4954d.addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||
|
||||
</script>
|
||||
|
||||
|
|
@ -185,7 +185,7 @@
|
|||
}
|
||||
trajeto_json_add({"features": []});
|
||||
|
||||
trajeto_json.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
trajeto_json.addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||
|
||||
function adicionarGeometria(novaGeometria) {
|
||||
trajeto_json.addData(novaGeometria);
|
||||
|
|
@ -243,7 +243,7 @@
|
|||
}
|
||||
trajeto_dinamico_json_add({"features": []});
|
||||
|
||||
trajeto_dinamico_json.addTo(map_f21522cbe2b071ceb21cf0d5c95f5a21);
|
||||
trajeto_dinamico_json.addTo(map_b86da2c0cf19440a67c00659ca7d57e9);
|
||||
|
||||
function adicionarGeometriaDinamica(novaGeometria) {
|
||||
trajeto_dinamico_json.addData(novaGeometria);
|
||||
|
|
@ -296,9 +296,9 @@
|
|||
|
||||
var marcadorEquipamento = L.marker([0, 0], {
|
||||
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(
|
||||
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
|
||||
);
|
||||
|
|
@ -380,7 +380,7 @@
|
|||
}
|
||||
|
||||
if (foco) {
|
||||
map_f21522cbe2b071ceb21cf0d5c95f5a21.setView(novaPosicao, map_f21522cbe2b071ceb21cf0d5c95f5a21.getZoom());
|
||||
map_b86da2c0cf19440a67c00659ca7d57e9.setView(novaPosicao, map_b86da2c0cf19440a67c00659ca7d57e9.getZoom());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -397,7 +397,7 @@
|
|||
function atualizarSelecaoRuas(selecionadas) {
|
||||
selecionadas = JSON.parse(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))) {
|
||||
layer.setStyle({ color: 'blue' });
|
||||
} else {
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -192,36 +192,39 @@ class CameraOak:
|
|||
self.mostrar_log(f"[WARN] Falha ao montar pipeline imu: {e}")
|
||||
|
||||
if self.modelo_ia_onboard is not None:
|
||||
from shared.utils import carregar_labelmap_completo
|
||||
try:
|
||||
from shared.utils import carregar_labelmap_completo
|
||||
|
||||
# Carregar mapa de cores
|
||||
labelmap_path = self.modelo_ia_onboard["ia_labelmap_path"]
|
||||
self.cor_para_id, self.colormap_rgb, self.classes, self.ignore_rgb = carregar_labelmap_completo(labelmap_path)
|
||||
# Carregar mapa de cores
|
||||
labelmap_path = self.modelo_ia_onboard["ia_labelmap_path"]
|
||||
self.cor_para_id, self.colormap_rgb, self.classes, self.ignore_rgb = carregar_labelmap_completo(labelmap_path)
|
||||
|
||||
RESOLUCAO = self.modelo_ia_onboard["ia_resolution"]
|
||||
ROI_INICIO = self.modelo_ia_onboard["ia_roi_begin"]
|
||||
ROI_TAMANHO = self.modelo_ia_onboard["ia_roi_size"]
|
||||
blob_path = self.modelo_ia_onboard["ia_model_path"]
|
||||
RESOLUCAO = self.modelo_ia_onboard["ia_resolution"]
|
||||
ROI_INICIO = self.modelo_ia_onboard["ia_roi_begin"]
|
||||
ROI_TAMANHO = self.modelo_ia_onboard["ia_roi_size"]
|
||||
blob_path = self.modelo_ia_onboard["ia_model_path"]
|
||||
|
||||
y1 = 1.0 - (ROI_INICIO + ROI_TAMANHO)
|
||||
y2 = 1.0 - ROI_INICIO
|
||||
y1 = 1.0 - (ROI_INICIO + ROI_TAMANHO)
|
||||
y2 = 1.0 - ROI_INICIO
|
||||
|
||||
manip = pipeline.createImageManip()
|
||||
manip.initialConfig.setCropRect(0.0, y1, 1.0, y2)
|
||||
manip.initialConfig.setResize(RESOLUCAO[0], RESOLUCAO[1])
|
||||
manip.initialConfig.setFrameType(dai.RawImgFrame.Type.RGB888p)
|
||||
manip.initialConfig.setKeepAspectRatio(False)
|
||||
cam.video.link(manip.inputImage)
|
||||
manip = pipeline.createImageManip()
|
||||
manip.initialConfig.setCropRect(0.0, y1, 1.0, y2)
|
||||
manip.initialConfig.setResize(RESOLUCAO[0], RESOLUCAO[1])
|
||||
manip.initialConfig.setFrameType(dai.RawImgFrame.Type.RGB888p)
|
||||
manip.initialConfig.setKeepAspectRatio(False)
|
||||
cam.video.link(manip.inputImage)
|
||||
|
||||
nn = pipeline.createNeuralNetwork()
|
||||
nn.setBlobPath(blob_path)
|
||||
manip.out.link(nn.input)
|
||||
nn = pipeline.createNeuralNetwork()
|
||||
nn.setBlobPath(blob_path)
|
||||
manip.out.link(nn.input)
|
||||
|
||||
xout_nn = pipeline.createXLinkOut()
|
||||
xout_nn.setStreamName("nn")
|
||||
nn.out.link(xout_nn.input)
|
||||
xout_nn = pipeline.createXLinkOut()
|
||||
xout_nn.setStreamName("nn")
|
||||
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
|
||||
|
||||
|
|
|
|||
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.radar_top_down import Radar2DManager
|
||||
from visual_worker.processamento.segmentacao_semantica import ClassesSegmentacao, SegmentacaoManager
|
||||
from shared.enums import ManagerWorkerCommandType, ModoOperacao, StatusModulo, T_Code, CameraFrameType
|
||||
from shared.utils import analisar_linhas_por_profundidade, decode_image_base64, encode_image_base64
|
||||
from shared.enums import StatusModulo, T_Code, CameraFrameType
|
||||
from shared.utils import analisar_linhas_por_profundidade, decode_image_base64, encode_image_base64, fazer_overlay
|
||||
from shared.gps_handler import GPSHandler
|
||||
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:
|
||||
def __init__(self, mostrar_log):
|
||||
self.mostrar_log = mostrar_log
|
||||
self.mx_id = None
|
||||
self.tempo_saude = 10
|
||||
self.reiniciar_status()
|
||||
|
||||
def reiniciar_status(self):
|
||||
|
|
@ -34,6 +35,11 @@ class CameraManager:
|
|||
self._ultima_analise_segmentacao = {}
|
||||
self._ultima_analise_matriz_confianca = {}
|
||||
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):
|
||||
if self.iniciando:
|
||||
|
|
@ -51,8 +57,11 @@ class CameraManager:
|
|||
|
||||
self.mx_id = mx_id
|
||||
|
||||
from visual_worker.config import load_config
|
||||
camera_config = load_config()
|
||||
|
||||
try:
|
||||
nova = CameraOak(self.mostrar_log, mx_id)
|
||||
nova = CameraOak(self.mostrar_log, mx_id, modelo_ia_onboard=camera_config)
|
||||
if nova.iniciado:
|
||||
self.camera = nova
|
||||
except Exception as e:
|
||||
|
|
@ -63,14 +72,13 @@ class CameraManager:
|
|||
self.mostrar_log(f"❌ Camera com ID {mx_id} não iniciada.")
|
||||
else:
|
||||
self.mostrar_log(f"📷 Camera visual selecionada: {self.camera.modelo} - {self.camera.mx_id}")
|
||||
self.grid_ref = None
|
||||
self.grid_ref_shape = (20, 20)
|
||||
self.grid_ref_shape = (15, 10)
|
||||
self.grid_ref = self._gerar_grid_referencia_geometrico()
|
||||
self.depth_referencia = None
|
||||
self.setores_referencia = None
|
||||
self.anomalias_manager = AnaliseAnomaliasManager()
|
||||
self.solo_manager = AnaliseSoloManager()
|
||||
arquivo_modelo = ContextoGlobalRedis.get_equipamento().get("path_ia_model_ruas")
|
||||
self.segmentacao_manager = SegmentacaoManager(arquivo_modelo)
|
||||
self.segmentacao_manager = SegmentacaoManager(self.camera.colormap_rgb, self.camera.classes)
|
||||
self.radar_manager = Radar2DManager()
|
||||
self.operante = True
|
||||
self._timestamp_analise = None
|
||||
|
|
@ -94,16 +102,32 @@ class CameraManager:
|
|||
self._analisando_matriz_confianca = False
|
||||
self._analisando_segmentacao = False
|
||||
|
||||
self._iniciar_loop_analise_continua(4.0)
|
||||
self._iniciar_loop_analise_continua(15.0)
|
||||
self.iniciando = False
|
||||
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):
|
||||
if self.camera is not None:
|
||||
self.camera.atualizar_saude()
|
||||
elif self.mx_id is not None:
|
||||
from camera_worker.manager import definir_saude_camera
|
||||
definir_saude_camera(self.mx_id, StatusModulo.DESCONECTADO, 0, ["desconectado"], False, {})
|
||||
self._ultima_saude_ts = time.time()
|
||||
#self.mostrar_log("Atualizando saude da camera...")
|
||||
try:
|
||||
if self.camera is not None:
|
||||
self.camera.atualizar_saude()
|
||||
elif self.mx_id is not None:
|
||||
from camera_worker.manager import definir_saude_camera
|
||||
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):
|
||||
if self.camera is None:
|
||||
|
|
@ -130,6 +154,7 @@ class CameraManager:
|
|||
try:
|
||||
frame, res = self.camera.requisitar_frame_depth()
|
||||
if frame is not None:
|
||||
self._ultimo_depth_frame = frame
|
||||
return frame, self.camera.timestamp_ultimo_frame_depth, res
|
||||
elif "X_LINK_ERROR" in res["erro"]:
|
||||
self.reiniciar_status()
|
||||
|
|
@ -146,6 +171,24 @@ class CameraManager:
|
|||
return gerar_heatmap(frame, self.camera.parametros["distancia_maxima"]), timestamp, res
|
||||
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):
|
||||
f = None
|
||||
t = None
|
||||
|
|
@ -163,20 +206,31 @@ class CameraManager:
|
|||
elif tipo == CameraFrameType.Segmentacao:
|
||||
f = self._ultima_analise_segmentacao.get("frame", {}).get("frame")
|
||||
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
|
||||
|
||||
|
||||
|
||||
def _iniciar_loop_analise_continua(self, freq):
|
||||
def loop():
|
||||
ultima_atualizacao_saude = 0
|
||||
ultima_atualizacao = 0
|
||||
self._ultima_saude_ts = 0
|
||||
while True:
|
||||
if self.camera is None:
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
t0 = time.time()
|
||||
if (t0 - ultima_atualizacao_saude) >= 5:
|
||||
self.camera.atualizar_saude()
|
||||
ultima_atualizacao_saude = time.time()
|
||||
if (t0 - self._ultima_saude_ts) >= self.tempo_saude:
|
||||
self._ultima_saude_ts = time.time()
|
||||
self.atualizar_saude_camera()
|
||||
try:
|
||||
status = StatusModulo((self.camera.ultima_saude or {}).get("status", StatusModulo.DESCONECTADO.value))
|
||||
if status == StatusModulo.DESCONECTADO:
|
||||
|
|
@ -197,11 +251,35 @@ class CameraManager:
|
|||
except Exception as e:
|
||||
self.mostrar_log(f"Erro no loop de analise continua: {e}")
|
||||
finally:
|
||||
latencia = time.time() - t0
|
||||
time.sleep(max(0, (1.0 / freq) - latencia))
|
||||
latencia, fps, _freq = self._calcular_performance(t0, time.time(), {"ultima_chamada": ultima_atualizacao})
|
||||
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()
|
||||
|
||||
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)):
|
||||
"""
|
||||
Mostra o frame RGB com linhas horizontais do grid de referência
|
||||
|
|
@ -231,104 +309,101 @@ class CameraManager:
|
|||
|
||||
|
||||
def _realizar_analises(self):
|
||||
if self._depth_frame_necessario and self.camera.tem_depth:
|
||||
try:
|
||||
# 🔸 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
|
||||
executor = self._pool
|
||||
tarefas = []
|
||||
|
||||
parametros_camera = self.camera.parametros
|
||||
fov_h = parametros_camera["fov_h"]
|
||||
distancia_max_m = parametros_camera["distancia_maxima"] / 1000.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:
|
||||
tarefas = []
|
||||
depth_frame_np, depth_timestamp, depth_res = self.get_depth_frame()
|
||||
|
||||
#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 not self._analisando_segmentacao:
|
||||
tarefas.append(executor.submit(self._analise_segmentacao))
|
||||
if True or not self._nova_segmentacao_disponivel:
|
||||
if not self._analisando_segmentacao:
|
||||
tarefas.append(executor.submit(self._analise_segmentacao))
|
||||
|
||||
if False and self._nova_segmentacao_disponivel:
|
||||
if not self._analisando_matriz_confianca:
|
||||
self._nova_segmentacao_disponivel = False
|
||||
tarefas.append(executor.submit(self._analise_matriz_confianca, depth_frame_np, distancia_max_m, fov_h))
|
||||
if self._nova_segmentacao_disponivel:
|
||||
if not self._analisando_matriz_confianca:
|
||||
self._nova_segmentacao_disponivel = False
|
||||
tarefas.append(executor.submit(self._analise_matriz_confianca, depth_frame_np, distancia_max_m, fov_h))
|
||||
|
||||
if False and self._nova_grid_conf_disponivel:
|
||||
matriz_conf = self._ultima_analise_matriz_confianca["matriz"]
|
||||
if not self._analisando_anomalias:
|
||||
self._nova_grid_conf_disponivel = False
|
||||
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:
|
||||
self._nova_grid_conf_disponivel = False
|
||||
tarefas.append(executor.submit(self._analise_solo, matriz_conf, percentual_solo, limiar_conf, fov_h))
|
||||
if not self._analisando_matriz_custo:
|
||||
self._nova_grid_conf_disponivel = False
|
||||
tarefas.append(executor.submit(self._analise_matriz_custo, matriz_conf, fov_h))
|
||||
if self._nova_grid_conf_disponivel:
|
||||
limiar_conf: float = 0.4
|
||||
matriz_conf = self._ultima_analise_matriz_confianca["matriz"]
|
||||
if not self._analisando_anomalias:
|
||||
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))
|
||||
if not self._analisando_solo:
|
||||
self._nova_grid_conf_disponivel = False
|
||||
tarefas.append(executor.submit(self._analise_solo, matriz_conf, percentual_solo, limiar_conf, fov_h))
|
||||
if not self._analisando_matriz_custo:
|
||||
self._nova_grid_conf_disponivel = False
|
||||
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):
|
||||
if self._analisando_segmentacao:
|
||||
return
|
||||
self._analisando_segmentacao = True
|
||||
t0 = time.time()
|
||||
try:
|
||||
rgb_frame, rgb_timestamp, res_frame = self.get_rgb_frame()
|
||||
if rgb_frame is None or rgb_frame.size == 0:
|
||||
self._analisando_segmentacao = False
|
||||
return
|
||||
analise_segmentacao, log = self.segmentacao_manager.segmentar(rgb_frame)
|
||||
if analise_segmentacao == None:
|
||||
self.mostrar_log(log)
|
||||
t1 = time.time()
|
||||
analise_segmentacao["latencia"] = t1 - t0
|
||||
self._ultima_analise_segmentacao = analise_segmentacao
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
CtxKey.DadosVisualWorker,
|
||||
ts_analise=t1,
|
||||
segmentacao=converter_valores_numpy(self._ultima_analise_segmentacao["dados_visuais"])
|
||||
)
|
||||
self._nova_segmentacao_disponivel = True
|
||||
t0 = time.time()
|
||||
predictions, ts, res = self.get_segmentation_predictions()
|
||||
if ts == self._ts_segmentacao_anterior:
|
||||
return # já analisado
|
||||
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:
|
||||
self.mostrar_log(log)
|
||||
analise_segmentacao["ultima_chamada"] = self._ultima_analise_segmentacao.get("ultima_chamada", t0)
|
||||
self._calcular_performance(t0, t1, analise_segmentacao)
|
||||
self._ultima_analise_segmentacao = analise_segmentacao
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
CtxKey.DadosVisualWorker,
|
||||
ts_analise=t1,
|
||||
segmentacao=converter_valores_numpy(self._ultima_analise_segmentacao["dados_visuais"])
|
||||
)
|
||||
self._nova_segmentacao_disponivel = True
|
||||
|
||||
# Enviar comando para atualizar os dados de controle sempre que um novo dado de segmentacao seja processado e a operacao seja do tipo MapeamentoVisual
|
||||
#op_modo = ContextoGlobalRedis.get_operacao().get("modo", ModoOperacao.NaoDefinido.value)
|
||||
#movimento_automatico = ContextoGlobalRedis.get_controle().get("movimento_automatico", False)
|
||||
#if ModoOperacao(op_modo) == ModoOperacao.MapeamentoVisual and movimento_automatico and analise_segmentacao is not None:
|
||||
# ContextoGlobalRedis.publicar_comando(CmdKey.ManagerWorkerRx, { "cmd": ManagerWorkerCommandType.AtualizarDadosControle.value })
|
||||
# Enviar comando para atualizar os dados de controle sempre que um novo dado de segmentacao seja processado e a operacao seja do tipo MapeamentoVisual
|
||||
#op_modo = ContextoGlobalRedis.get_operacao().get("modo", ModoOperacao.NaoDefinido.value)
|
||||
#movimento_automatico = ContextoGlobalRedis.get_controle().get("movimento_automatico", False)
|
||||
#if ModoOperacao(op_modo) == ModoOperacao.MapeamentoVisual and movimento_automatico and analise_segmentacao is not None:
|
||||
# ContextoGlobalRedis.publicar_comando(CmdKey.ManagerWorkerRx, { "cmd": ManagerWorkerCommandType.AtualizarDadosControle.value })
|
||||
|
||||
except Exception as e:
|
||||
self.mostrar_log(f"❌ Erro na segmentacao semantica: {e}")
|
||||
finally:
|
||||
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):
|
||||
if self._analisando_matriz_confianca:
|
||||
return
|
||||
self._analisando_matriz_confianca = True
|
||||
t0 = time.time()
|
||||
try:
|
||||
if depth_frame_np is None or depth_frame_np.size == 0:
|
||||
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)
|
||||
|
||||
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.")
|
||||
else:
|
||||
t0 = time.time()
|
||||
grid_conf = self._gerar_grid_confianca(depth_frame_np, segmentacao, dist_max)
|
||||
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
|
||||
matriz = self._ultima_analise_matriz_confianca["matriz"]
|
||||
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", []))
|
||||
)
|
||||
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:
|
||||
self.mostrar_log(f"❌ Erro na geracao da matriz de confianca: {e}")
|
||||
finally:
|
||||
|
|
@ -351,13 +426,14 @@ class CameraManager:
|
|||
if self._analisando_anomalias:
|
||||
return
|
||||
self._analisando_anomalias = True
|
||||
t0 = time.time()
|
||||
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.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)
|
||||
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
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
CtxKey.DadosVisualWorker,
|
||||
|
|
@ -374,11 +450,12 @@ class CameraManager:
|
|||
if self._analisando_solo:
|
||||
return
|
||||
self._analisando_solo = True
|
||||
t0 = time.time()
|
||||
try:
|
||||
t0 = time.time()
|
||||
analise_solo = self.solo_manager.analisar_solo(grid_conf, percentual_solo, limiar_conf, fov_h)
|
||||
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
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
CtxKey.DadosVisualWorker,
|
||||
|
|
@ -395,14 +472,15 @@ class CameraManager:
|
|||
if self._analisando_radar:
|
||||
return
|
||||
self._analisando_radar = True
|
||||
t0 = time.time()
|
||||
try:
|
||||
if depth_frame_np is None or depth_frame_np.size == 0:
|
||||
return
|
||||
t0 = time.time()
|
||||
depth_frame = cp.asarray(depth_frame_np)
|
||||
analise_radar = self.radar_manager.analisar_radar_2d(depth_frame, fov_h, dist_max)
|
||||
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
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
CtxKey.DadosVisualWorker,
|
||||
|
|
@ -421,12 +499,13 @@ class CameraManager:
|
|||
if self._analisando_matriz_custo:
|
||||
return
|
||||
self._analisando_matriz_custo = True
|
||||
t0 = time.time()
|
||||
try:
|
||||
t0 = time.time()
|
||||
largura_robo = ContextoGlobalRedis.get(CtxKey.DadosEquipamento, {}).get("largura", 0.85)
|
||||
matriz_custo = self._gerar_matriz_custo_fundida(grid_conf, largura_robo, fov_h)
|
||||
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
|
||||
matriz = matriz_custo["matriz"]
|
||||
ContextoGlobalRedis.atualizar_ctx_dict(
|
||||
|
|
@ -493,81 +572,133 @@ class CameraManager:
|
|||
return caminho_analise
|
||||
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:
|
||||
grid_h, grid_w = self.grid_ref_shape
|
||||
altura, largura = depth_frame.shape
|
||||
# --- força ndarray ---
|
||||
depth = self._as_ndarray(depth_frame) # mm, 2D
|
||||
seg = self._as_ndarray(segmentacao_frame) # IDs, 2D (NEAREST)
|
||||
|
||||
h_step = altura // grid_h
|
||||
w_step = largura // grid_w
|
||||
if depth.ndim != 2:
|
||||
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):
|
||||
prof_ref = self.grid_ref[i]
|
||||
for j in range(grid_w):
|
||||
y0, y1 = i * h_step, (i + 1) * h_step
|
||||
x0, x1 = j * w_step, (j + 1) * w_step
|
||||
# calcula tamanho mínimo de célula (>=1px) e ajusta grid efetivo ao frame
|
||||
h = max(1, H // gh_cfg)
|
||||
w = max(1, W // gw_cfg)
|
||||
gh_eff = max(1, H // h)
|
||||
gw_eff = max(1, W // w)
|
||||
|
||||
cel_depth = depth_frame[y0:y1, x0:x1]
|
||||
cel_seg = segmentacao_frame[y0:y1, x0:x1]
|
||||
total_pix = cel_depth.size
|
||||
# crop exato para poder reshape
|
||||
H2 = gh_eff * h
|
||||
W2 = gw_eff * w
|
||||
depth = self._as_ndarray(depth[:H2, :W2])
|
||||
seg = self._as_ndarray(seg[:H2, :W2])
|
||||
|
||||
# Frequência das classes
|
||||
valores, contagens = np.unique(cel_seg, return_counts=True)
|
||||
freq_classes = {int(v): int(c) / total_pix for v, c in zip(valores, contagens)}
|
||||
# ---------- reshape em blocos ----------
|
||||
depth_b = depth.reshape(gh_eff, h, gw_eff, w)
|
||||
seg_b = seg.reshape(gh_eff, h, gw_eff, w)
|
||||
|
||||
# Índice de Segmentação (presença de chão)
|
||||
IS = freq_classes.get(ClassesSegmentacao.RUA.value, 0.0)
|
||||
# ---------- métricas vetorizadas ----------
|
||||
valid = (depth_b > 0)
|
||||
ignore_id = getattr(self, "ignore_id", 255)
|
||||
not_ign = (seg_b != ignore_id)
|
||||
|
||||
# Í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
|
||||
IP = 1.0 - min(abs(delta) / (limiar_prof * 1000.0), 1.0)
|
||||
conf_profundidade = celula_valida.size / total_pix
|
||||
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
|
||||
|
||||
# Índice Caminho Livre
|
||||
PESO_SEG, PESO_PROF = 0.6, 0.4
|
||||
ICL = PESO_SEG * IS + PESO_PROF * IP
|
||||
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))
|
||||
|
||||
num_mapeados = total_pix - np.count_nonzero(cel_seg == 0)
|
||||
conf_segmentacao = num_mapeados / total_pix
|
||||
# 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)
|
||||
|
||||
indice_confiabilidade = (conf_profundidade + conf_segmentacao) / 2.0
|
||||
# 🔒 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
|
||||
|
||||
grid_resultado[i][j] = {
|
||||
"linha": i,
|
||||
"coluna": j,
|
||||
"pix_x": x1 - x0,
|
||||
"pix_y": y1 - y0,
|
||||
prof_ref = np.repeat(grid_ref_arr.reshape(gh_eff, 1), gw_eff, axis=1) # (gh, gw) mm
|
||||
|
||||
delta = prof_median - prof_ref
|
||||
limiar_mm = float(limiar_prof) * 1000.0
|
||||
IP = 1.0 - np.minimum(np.abs(delta) / max(limiar_mm, 1e-6), 1.0)
|
||||
IP = np.nan_to_num(IP, nan=0.0)
|
||||
|
||||
ICL = 0.6 * IS + 0.4 * IP
|
||||
conf_geral = 0.5 * (conf_profundidade + conf_segmentacao)
|
||||
|
||||
# opcional: frequências por classe (se realmente precisar)
|
||||
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}
|
||||
|
||||
# ---------- 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[i][j] = {
|
||||
"linha": i, "coluna": j,
|
||||
"pix_x": w, "pix_y": h,
|
||||
"segmentacao": freq_classes,
|
||||
"prof_ref": prof_ref / 1000.0,
|
||||
"prof_median": prof_median / 1000.0,
|
||||
"prof_delta": delta / 1000.0,
|
||||
"indice_seg_chao": IS,
|
||||
"indice_prof_delta": IP,
|
||||
"indice_caminho_livre": ICL,
|
||||
"conf_profundidade": conf_profundidade,
|
||||
"conf_segmentacao": conf_segmentacao,
|
||||
"indice_confiabilidade": indice_confiabilidade
|
||||
"prof_ref": float(prof_ref[i, j]) / 1000.0,
|
||||
"prof_median": float(prof_median[i, j]) / 1000.0,
|
||||
"prof_delta": float(delta[i, j]) / 1000.0,
|
||||
"indice_seg_chao": float(IS[i, j]),
|
||||
"indice_prof_delta": float(IP[i, j]),
|
||||
"indice_caminho_livre": float(ICL[i, j]),
|
||||
"conf_profundidade": float(conf_profundidade[i, j]),
|
||||
"conf_segmentacao": float(conf_segmentacao[i, j]),
|
||||
"indice_confiabilidade": float(conf_geral[i, j]),
|
||||
}
|
||||
|
||||
return {
|
||||
"matriz": grid_resultado
|
||||
}
|
||||
# (opcional) guardar grid efetivo, útil pra debug
|
||||
self.grid_ref_shape_eff = (gh_eff, gw_eff)
|
||||
|
||||
return {"timestamp": time.time(), "matriz": grid}
|
||||
|
||||
except Exception as e:
|
||||
self.mostrar_log(f"❌ Erro ao gerar grid de confianca: {e}")
|
||||
return {
|
||||
"matriz": [[]]
|
||||
}
|
||||
return {"timestamp": time.time(), "matriz": [[]]}
|
||||
|
||||
def _mostrar_debug_grid_confianca(self, rgb_frame: np.ndarray, grid_conf: list, exibir_debug: bool = False, segmentacao_colorida: np.ndarray = None):
|
||||
try:
|
||||
|
|
@ -575,7 +706,7 @@ class CameraManager:
|
|||
return
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
return {
|
||||
"timestamp": time.time(),
|
||||
"matriz": matriz_custo
|
||||
}
|
||||
except Exception as e:
|
||||
self.mostrar_log(f"❌ Erro ao gerar matriz de custo: {e}")
|
||||
return {
|
||||
"timestamp": time.time(),
|
||||
"matriz": [[]]
|
||||
}
|
||||
|
||||
|
|
@ -1018,7 +1151,7 @@ class CameraManager:
|
|||
if not exibir_debug:
|
||||
return
|
||||
|
||||
img_debug = cv2.resize(rgb_frame.copy(), (1600, 900))
|
||||
img_debug = cv2.resize(rgb_frame.copy(), (1280, 720))
|
||||
overlay = img_debug.copy()
|
||||
|
||||
altura, largura, _ = img_debug.shape
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import os
|
||||
import threading
|
||||
import time
|
||||
|
||||
from visual_worker.camera_manager import CameraManager
|
||||
from shared.contexto_global_redis import ContextoGlobalRedis
|
||||
|
||||
module_id = "visual"
|
||||
topico_tx = f"operador/{module_id}/tx"
|
||||
|
|
@ -28,3 +31,57 @@ def iniciar_camera_manager(mx_id):
|
|||
manager.inicializar(mx_id=mx_id)
|
||||
if manager.camera is not None and manager.operante:
|
||||
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
|
||||
|
||||
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:
|
||||
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)
|
||||
indices_x = cp.linspace(-0.5, 0.5, width)
|
||||
theta_x = indices_x * fov_horizontal
|
||||
# Cacheia tan(theta_x) por largura+FOV para evitar recomputar a cada frame
|
||||
key = (W, float(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
|
||||
theta_matrix = cp.tile(theta_x, (height, 1))
|
||||
# Depth em metros
|
||||
Z = d * 0.001 # m
|
||||
|
||||
# 🔥 Converte depth de mm para metros
|
||||
Z = depth_frame / 1000.0
|
||||
# Filtro válido (agora em metros)
|
||||
mask = (Z > 0) & (Z >= min_dist_m) & (Z <= max_dist_m)
|
||||
Z = cp.where(mask, Z, cp.nan)
|
||||
|
||||
# 🔥 Calcula X no plano
|
||||
X = Z * cp.tan(theta_matrix)
|
||||
# X por broadcasting, sem tile
|
||||
X = Z * self._tan_theta_x # (H,W) * (1,W) -> (H,W)
|
||||
|
||||
# 🔥 Filtro de distância válida
|
||||
mask = (Z >= min_dist) & (Z <= max_dist) & (Z > 0)
|
||||
# Compacta removendo NaNs sem copiar desnecessariamente
|
||||
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:
|
||||
print(f"Erro ao gerar radar topdown: {e}")
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from enum import IntEnum
|
|||
import time
|
||||
import cv2
|
||||
import numpy as np
|
||||
import onnxruntime as ort
|
||||
|
||||
from shared.utils import encode_image_base64
|
||||
from shared.enums import StatusCarroMapa
|
||||
|
|
@ -14,117 +13,33 @@ class ClassesSegmentacao(IntEnum):
|
|||
|
||||
|
||||
class SegmentacaoManager:
|
||||
def __init__(self, path_model: str):
|
||||
self.path_modelo = path_model
|
||||
self.path_classes = path_model.replace("onnx", "txt")
|
||||
self.modelo = None
|
||||
self.input_name = None
|
||||
self.output_name = None
|
||||
self.classes = {}
|
||||
self.color_map = []
|
||||
self.carregado = False
|
||||
self.use_mock = False
|
||||
self.img_mock = "C:\\ZendionInc\\agrobot_base\\AgroBase\\AgroBase\\bin\\x64\\Debug\\Operacoes\\28_07_2025_14_47_15\\Snr\\137_rgb.jpeg"
|
||||
self.resolucao = (512, 512)
|
||||
def __init__(self, color_map, classes):
|
||||
from visual_worker.config import load_config
|
||||
config = load_config()
|
||||
resolucao = config.get("ia_resolution")
|
||||
self.color_map = color_map
|
||||
self.classes = classes
|
||||
self.resolucao = (resolucao[0], resolucao[1])
|
||||
self.pred_rgb = np.empty((self.resolucao[1], self.resolucao[0], 3), dtype=np.uint8)
|
||||
self.color_lut = np.array(self.color_map, np.uint8)
|
||||
self.lut = np.zeros((256, 3), dtype=np.uint8)
|
||||
for i, color in enumerate(color_map):
|
||||
#self.lut[i] = color
|
||||
self.lut[i] = (color[2], color[1], color[0]) # converte pra (B, G, R)
|
||||
IGNORE_ID = 255
|
||||
self.lut[IGNORE_ID] = (255, 255, 255)
|
||||
|
||||
self.predictions = None
|
||||
self.log = None
|
||||
self.dados_visuais = {}
|
||||
self._carregar_modelo()
|
||||
|
||||
def _carregar_labelmap_completo(self, caminho):
|
||||
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):
|
||||
def _segmentar_predictions(self, predictions):
|
||||
try:
|
||||
self.modelo = ort.InferenceSession(
|
||||
self.path_modelo,
|
||||
providers=["CUDAExecutionProvider", "CPUExecutionProvider"]
|
||||
)
|
||||
self.input_name = self.modelo.get_inputs()[0].name
|
||||
self.output_name = self.modelo.get_outputs()[0].name
|
||||
self.pred_rgb[:] = self.lut[predictions]
|
||||
|
||||
# Carrega as classes e cores
|
||||
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]
|
||||
mask_color = self.pred_rgb
|
||||
frame_color = encode_image_base64(mask_color)
|
||||
|
||||
#perfil_corredor = self._calcular_perfil_corredor(mask_resized, depth_frame, fov_h, 640, 24)
|
||||
|
||||
return {
|
||||
"timestamp": time.time(),
|
||||
"frame": {
|
||||
|
|
@ -132,9 +47,29 @@ class SegmentacaoManager:
|
|||
"frame": frame_color
|
||||
},
|
||||
"mask_color": mask_color,
|
||||
"classes": mask_resized,
|
||||
"dados_visuais": self.dados_visuais
|
||||
}, self.log
|
||||
"classes": predictions
|
||||
}
|
||||
|
||||
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:
|
||||
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):
|
||||
self.mostrar_log = mostrar_log
|
||||
self.mx_id = None
|
||||
self.tempo_saude = 10
|
||||
self.reiniciar_status()
|
||||
|
||||
def reiniciar_status(self):
|
||||
|
|
@ -22,8 +23,10 @@ class CameraManager:
|
|||
self.operante = False
|
||||
self.iniciando = False
|
||||
self.weed_detector = None
|
||||
self._ultimo_rgb_frame = None
|
||||
self._ultima_analise = {}
|
||||
self._ts_segmentacao_anterior = 0
|
||||
self._ultima_saude_ts = 0
|
||||
|
||||
def inicializar(self, mx_id):
|
||||
if self.iniciando:
|
||||
|
|
@ -68,11 +71,16 @@ class CameraManager:
|
|||
self.atualizar_saude_camera()
|
||||
|
||||
def atualizar_saude_camera(self):
|
||||
if self.camera is not None:
|
||||
self.camera.atualizar_saude()
|
||||
elif self.mx_id is not None:
|
||||
from camera_worker.manager import definir_saude_camera
|
||||
definir_saude_camera(self.mx_id, StatusModulo.DESCONECTADO, 0, ["desconectado"], False, {})
|
||||
self._ultima_saude_ts = time.time()
|
||||
#self.mostrar_log("Atualizando saude da camera...")
|
||||
try:
|
||||
if self.camera is not None:
|
||||
self.camera.atualizar_saude()
|
||||
elif self.mx_id is not None:
|
||||
from camera_worker.manager import definir_saude_camera
|
||||
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):
|
||||
if self.camera is None:
|
||||
|
|
@ -133,16 +141,16 @@ class CameraManager:
|
|||
|
||||
def _iniciar_loop_analise_continua(self, freq):
|
||||
def loop():
|
||||
ultima_atualizacao_saude = 0
|
||||
self._ultima_saude_ts = 0
|
||||
while True:
|
||||
if self.camera is None:
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
t0 = time.time()
|
||||
if (t0 - ultima_atualizacao_saude) >= 5:
|
||||
self.camera.atualizar_saude()
|
||||
ultima_atualizacao_saude = time.time()
|
||||
if (t0 - self._ultima_saude_ts) >= self.tempo_saude:
|
||||
self._ultima_saude_ts = time.time()
|
||||
self.atualizar_saude_camera()
|
||||
try:
|
||||
status = StatusModulo((self.camera.ultima_saude or {}).get("status", StatusModulo.DESCONECTADO.value))
|
||||
if status == StatusModulo.DESCONECTADO:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pasta_mascaras = os.path.join(MODELO, "dataset", "original", "masks")
|
|||
# Regras de substituição (cores em RGB)
|
||||
# Exemplo: trocar (255, 0, 0) por branco (255,255,255) com tolerância 10
|
||||
SUBSTITUICOES = [
|
||||
#{"target_rgb": (255, 0, 0), "tolerancia": 10, "replace_rgb": (255, 255, 255)}, # vermelho -> branco
|
||||
#{"target_rgb": (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": (0, 128, 0), "tolerancia": 50, "replace_rgb": (0, 128, 0)}, # erva
|
||||
{"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
|
||||
import albumentations as A
|
||||
|
||||
|
|
|
|||
|
|
@ -18,89 +18,290 @@ MODEL_NAME = config["model_name"]
|
|||
RESOLUCAO = config["resolucao"]
|
||||
ROI_INICIO = config["roi_inicio"]
|
||||
ROI_TAMANHO = config["roi_tamanho"]
|
||||
MAIN_CLASS_NAME = config["main_class_name"]
|
||||
save_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME)
|
||||
dataset_path = os.path.join(MODELO, "dataset")
|
||||
split_folder = "train"
|
||||
labelmap_path = os.path.join(dataset_path, "labelmap.txt")
|
||||
batch_size = 8
|
||||
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):
|
||||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||
print(f"Device: {device}")
|
||||
|
||||
ds_train = ROISegDataset(os.path.join(dataset_path, "split", split_folder), save_path, ROI_INICIO, ROI_TAMANHO, RESOLUCAO[0], RESOLUCAO[1], labelmap_path)
|
||||
dl_train = DataLoader(ds_train, batch_size=batch_size, shuffle=True, num_workers=num_workers, pin_memory=True)
|
||||
# --- Dataset ---
|
||||
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)
|
||||
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):
|
||||
print(f"🔁 Carregando modelo salvo: {args.checkpoint}")
|
||||
checkpoint = torch.load(args.checkpoint, map_location=device)
|
||||
|
||||
if "model" in checkpoint:
|
||||
model.load_state_dict(checkpoint["model"])
|
||||
optimizer.load_state_dict(checkpoint["optimizer"])
|
||||
scaler.load_state_dict(checkpoint["scaler"])
|
||||
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:
|
||||
# Caso seja apenas um .pth com model.state_dict() direto
|
||||
model.load_state_dict(checkpoint)
|
||||
|
||||
# --- Loop de treino ---
|
||||
for epoch in range(start_epoch, args.epochs + 1):
|
||||
model.train()
|
||||
total_loss = 0
|
||||
t0 = time.time()
|
||||
|
||||
# ----- Treino -----
|
||||
model.train()
|
||||
running_train_loss = 0
|
||||
for x, y in dl_train:
|
||||
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):
|
||||
logits = model(x)
|
||||
loss = criterion(logits, y)
|
||||
scaler.scale(loss).backward()
|
||||
scaler.step(optimizer)
|
||||
scaler.update()
|
||||
total_loss += loss.item() * x.size(0)
|
||||
running_train_loss += loss.item() * x.size(0)
|
||||
|
||||
avg_loss = total_loss / len(ds_train)
|
||||
loss_history.append(avg_loss)
|
||||
print(f"[{epoch}/{args.epochs}] loss={avg_loss:.4f} time={time.time()-t0:.1f}s")
|
||||
avg_train_loss = running_train_loss / len(ds_train)
|
||||
train_loss_history.append(avg_train_loss)
|
||||
|
||||
if avg_loss < best_loss:
|
||||
best_loss = avg_loss
|
||||
# ----- Validação + métricas -----
|
||||
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": model.state_dict(),
|
||||
"optimizer": optimizer.state_dict(),
|
||||
"scaler": scaler.state_dict(),
|
||||
"epoch": epoch,
|
||||
"best_loss": best_loss
|
||||
"best_val_loss": best_val_loss
|
||||
}, 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:
|
||||
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.plot(x_epochs, loss_history, marker="o", label="Loss de Treinamento")
|
||||
plt.xlabel("Época")
|
||||
plt.ylabel("Loss")
|
||||
plt.grid(True)
|
||||
plt.legend()
|
||||
plt.title("Curva de Loss")
|
||||
plt.plot(x_epochs, train_loss_history, marker="o", label="Train Loss")
|
||||
plt.plot(x_epochs, val_loss_history, marker="s", label="Val Loss")
|
||||
plt.xlabel("Época"); plt.ylabel("Loss"); plt.grid(True); plt.legend(); plt.title("Curva de Loss")
|
||||
plt.tight_layout()
|
||||
plt.savefig(os.path.join(save_path, "loss_curve.png"))
|
||||
plt.close()
|
||||
plt.savefig(os.path.join(save_path, "loss_curve.png")); 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():
|
||||
ap = argparse.ArgumentParser()
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ MODEL_NAME = config["model_name"]
|
|||
RESOLUCAO = config["resolucao"]
|
||||
ROI_INICIO = config["roi_inicio"]
|
||||
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")
|
||||
split_folder = "test"
|
||||
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():
|
||||
parser = argparse.ArgumentParser()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
import os
|
||||
import torch
|
||||
from fast_scnn import FastSCNN
|
||||
from fast_scnn import FastSCNN, FastSCNNWithNorm
|
||||
from utils import carregar_labelmap_completo
|
||||
|
||||
# ⚙️ Configurações
|
||||
|
|
@ -10,21 +10,23 @@ with open("config.json", "r") as f:
|
|||
MODELO = config["camera"]
|
||||
MODEL_NAME = config["model_name"]
|
||||
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)
|
||||
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)
|
||||
|
||||
_, _, classes, _ = carregar_labelmap_completo(labelmap_path)
|
||||
NUM_CLASSES = len(classes)
|
||||
|
||||
model = FastSCNN(num_classes=NUM_CLASSES) # ajuste num_classes conforme seu labelmap
|
||||
model.load_state_dict(torch.load(os.path.join(model_path, model_name + ".pth")))
|
||||
model.eval()
|
||||
base = FastSCNNWithNorm(num_classes=NUM_CLASSES, to_rgb=True) # ajuste num_classes conforme seu labelmap
|
||||
base.backbone.load_state_dict(torch.load(os.path.join(model_path, f"{MODEL_NAME}_best.pth"), map_location="cpu"))
|
||||
base.eval()
|
||||
|
||||
torch.onnx.export(
|
||||
model,
|
||||
base,
|
||||
dummy_input,
|
||||
os.path.join(model_path, model_name + ".onnx"),
|
||||
input_names=["input"],
|
||||
|
|
@ -55,11 +57,11 @@ blob_path = blobconverter.from_openvino(
|
|||
data_type="FP16",
|
||||
shaves=6,
|
||||
output_dir=model_path,
|
||||
compile_params=[
|
||||
"-ip U8", # entrada em bytes; compila a conversão interna p/ FP16
|
||||
"--mean_values=[123.675,116.28,103.53]",
|
||||
"--scale_values=[58.395,57.12,57.375]",
|
||||
#compile_params=[
|
||||
# "-ip U8", # entrada em bytes; compila a conversão interna p/ FP16
|
||||
#"--mean_values=[123.675,116.28,103.53]",
|
||||
#"--scale_values=[58.395,57.12,57.375]",
|
||||
#"--reverse_input_channels" # pq você treinou em RGB
|
||||
],
|
||||
#],
|
||||
)
|
||||
print(f"Blob salvo em: {blob_path}")
|
||||
|
|
@ -10,13 +10,14 @@ from utils import converter_mask_ids_para_rgb, carregar_labelmap_completo
|
|||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
MODELO = config["camera"]
|
||||
MODEL_NAME = "ervas_medium" #config["model_name"]
|
||||
MODEL_NAME = config["model_name"]
|
||||
RESOLUCAO = config["resolucao"]
|
||||
ROI_INICIO = 0.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")
|
||||
model_name = MODEL_NAME + "_best"
|
||||
|
||||
# Carregar mapa de cores
|
||||
_, colormap_rgb, classes, ignore_rgb = carregar_labelmap_completo(labelmap_path)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
"camera": "oak-1",
|
||||
"modelo": "fast_scnn",
|
||||
"model_name": "ervas_medium_new",
|
||||
"main_class_name": "erva",
|
||||
"use_main_class": true,
|
||||
"resolucao": [512, 288],
|
||||
"roi_inicio": 0.0,
|
||||
"roi_tamanho": 1.0
|
||||
|
|
|
|||
|
|
@ -1,5 +1,26 @@
|
|||
import torch
|
||||
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):
|
||||
def __init__(self, num_classes):
|
||||
super().__init__()
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue