Ajustes operação
This commit is contained in:
parent
053bd9994f
commit
778af21ebf
|
|
@ -47,6 +47,14 @@ Thumbs.db
|
||||||
*.mov
|
*.mov
|
||||||
*.wmv
|
*.wmv
|
||||||
|
|
||||||
# Ignorar a pasta Operacoes dentro da hierarquia completa
|
# Ignorar arquivos de build
|
||||||
AgroBase/AgroBase/obj/
|
AgroBase/AgroBase/obj/
|
||||||
AgroBase/AgroBase/bin/Debug/
|
AgroBase/AgroBase/bin/
|
||||||
|
|
||||||
|
# Ignorar a pasta Operacoes e outras dentro da pasta Debug
|
||||||
|
AgroBase/AgroBase/bin/x64/Debug/Operacoes/
|
||||||
|
AgroBase/AgroBase/bin/x64/Debug/Logs/
|
||||||
|
AgroBase/AgroBase/bin/x64/Debug/Python/venv/
|
||||||
|
Python/OAK/datasets/venv/
|
||||||
|
Python/OAK/datasets/oak-1/dataset/
|
||||||
|
Python/OAK/datasets/oak-d/dataset/
|
||||||
|
|
|
||||||
|
|
@ -797,11 +797,19 @@ namespace AgroBase.Models.Modules
|
||||||
return BombaOk;
|
return BombaOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RealizarCalibragemAsync()
|
public List<Task> RealizarCalibragemAsync()
|
||||||
{
|
{
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
var DispAtu = Variaveis.OperacaoEmAndamento.DispAtu;
|
var DispAtu = Variaveis.OperacaoEmAndamento.DispAtu;
|
||||||
if (DispAtu != null)
|
if (DispAtu != null)
|
||||||
DispAtu.Dados.AtuPronto = await DispAtu.Dados.RealizarTestesIniciais();
|
tasks.Add(DispAtu.Dados.RealizarTestesIniciais().ContinueWith(t => {
|
||||||
|
if (t.IsFaulted)
|
||||||
|
Console.WriteLine($"Erro no referenciamento do módulo {Modulo_ID}: {t.Exception?.InnerException?.Message}");
|
||||||
|
if (t.IsCompleted)
|
||||||
|
Console.WriteLine($"Referenciamento do módulo {Modulo_ID} concluído com sucesso!");
|
||||||
|
|
||||||
|
}));
|
||||||
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,6 @@ namespace AgroBase.Models.Modules
|
||||||
{
|
{
|
||||||
return Modulos.Any(x => x.Conectado);
|
return Modulos.Any(x => x.Conectado);
|
||||||
}
|
}
|
||||||
set
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public bool DirPronto { get; set; } = false;
|
public bool DirPronto { get; set; } = false;
|
||||||
public bool MovPronto { get; set; } = false;
|
public bool MovPronto { get; set; } = false;
|
||||||
|
|
@ -398,16 +394,30 @@ namespace AgroBase.Models.Modules
|
||||||
return Modulos.All(x => x.DirMotor.Referenciado);
|
return Modulos.All(x => x.DirMotor.Referenciado);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RealizarCalibragemAsync()
|
public List<Task> RealizarCalibragemAsync()
|
||||||
{
|
{
|
||||||
|
List<Task> tasks = new List<Task>();
|
||||||
var DispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
|
var DispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
|
||||||
if (DispMvd != null)
|
if (DispMvd != null)
|
||||||
{
|
{
|
||||||
DispMvd.Dados.MovPronto = DispMvd.Dados.Modulos.All(x => x.MovMotor.Inicializado);
|
DispMvd.Dados.MovPronto = DispMvd.Dados.Modulos.All(x => x.MovMotor.Inicializado);
|
||||||
DispMvd.Dados.DirPronto = DispMvd.Dados.Modulos.All(x => x.DirMotor.Inicializado);
|
DispMvd.Dados.DirPronto = DispMvd.Dados.Modulos.All(x => x.DirMotor.Inicializado);
|
||||||
if (DispMvd.Dados.Modulos.Any(x => x.DirMotor.Inicializado))
|
if (DispMvd.Dados.Modulos.Any(x => x.DirMotor.Inicializado))
|
||||||
DispMvd.Dados.DirPronto &= await DispMvd.Dados.RealizarReferenciamentoDirecional();
|
{
|
||||||
|
Variaveis.OperacaoEmAndamento.Controle.TipoMovimento = TipoMovimentoDirecional.Diagnostico;
|
||||||
|
var ModsRef = Modulos.Where(x => x.DirMotor.Comandar).ToList(); // && x.DirMotor.Inicializado
|
||||||
|
foreach (var Modulo in ModsRef)
|
||||||
|
{
|
||||||
|
tasks.Add(Modulo.DirMotor.ReferenciaMotor().ContinueWith(t => {
|
||||||
|
if (t.IsFaulted)
|
||||||
|
Console.WriteLine($"Erro no referenciamento do módulo {Modulo.Modulo_ID}: {t.Exception?.InnerException?.Message}");
|
||||||
|
if (t.IsCompleted)
|
||||||
|
Console.WriteLine($"Referenciamento do módulo {Modulo.Modulo_ID} concluído com sucesso!");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,13 @@ namespace AgroBase.Models.Modules
|
||||||
public byte _EnderecoCAN_Tx { get; set; } = 0x11;
|
public byte _EnderecoCAN_Tx { get; set; } = 0x11;
|
||||||
public byte _EnderecoCAN_Rx { get; set; } = 0x12;
|
public byte _EnderecoCAN_Rx { get; set; } = 0x12;
|
||||||
public int _TaxaAmostragem { get; set; } = 1000;
|
public int _TaxaAmostragem { get; set; } = 1000;
|
||||||
|
public double FrequenciaBase
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return 1.0 / (_TaxaAmostragem / 1000.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
public bool RequisitarDados { get; set; } = true;
|
public bool RequisitarDados { get; set; } = true;
|
||||||
public static int PingsConsiderarConexao { get; set; } = 5;
|
public static int PingsConsiderarConexao { get; set; } = 5;
|
||||||
public static int TempoLimiteConexao
|
public static int TempoLimiteConexao
|
||||||
|
|
@ -1997,8 +2004,8 @@ namespace AgroBase.Models.Modules
|
||||||
|
|
||||||
private void AtualizaComponentesIniciados()
|
private void AtualizaComponentesIniciados()
|
||||||
{
|
{
|
||||||
AtualizarStatusComponentes();
|
//AtualizarStatusComponentes();
|
||||||
return;
|
//return;
|
||||||
|
|
||||||
DateTime Agora = DateTime.Now;
|
DateTime Agora = DateTime.Now;
|
||||||
bool modConectado = DadosLeitura.UltimoComandoRespondido.AddMilliseconds(TempoLimiteConexao) > Agora;
|
bool modConectado = DadosLeitura.UltimoComandoRespondido.AddMilliseconds(TempoLimiteConexao) > Agora;
|
||||||
|
|
|
||||||
|
|
@ -695,8 +695,8 @@ namespace AgroBase.Models
|
||||||
var DispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
|
var DispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
|
||||||
|
|
||||||
var tasks = new List<Task>();
|
var tasks = new List<Task>();
|
||||||
if (DispAtu?.Dados != null) tasks.Add(DispAtu.Dados.RealizarCalibragemAsync());
|
if (DispAtu?.Dados != null) tasks.AddRange(DispAtu.Dados.RealizarCalibragemAsync());
|
||||||
if (DispMvd?.Dados != null) tasks.Add(DispMvd.Dados.RealizarCalibragemAsync());
|
if (DispMvd?.Dados != null) tasks.AddRange(DispMvd.Dados.RealizarCalibragemAsync());
|
||||||
|
|
||||||
if (tasks.Count == 0)
|
if (tasks.Count == 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -321,8 +321,14 @@ namespace AgroBase.Services.Operadores
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DadosSen = Variaveis.OperacaoEmAndamento.DispSen.Dados;
|
||||||
|
RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen),
|
||||||
|
("timestamp", agora),
|
||||||
|
("conectado", DadosSen.Conectado),
|
||||||
|
("freq_base", DadosSen.FrequenciaBase)
|
||||||
|
);
|
||||||
var sensoresAtualizados = new List<(string caminho, object valor)>();
|
var sensoresAtualizados = new List<(string caminho, object valor)>();
|
||||||
foreach (var sensor in Variaveis.OperacaoEmAndamento.DispSen.Dados.Sensores)
|
foreach (var sensor in DadosSen.Sensores)
|
||||||
{
|
{
|
||||||
int tipo = (int)sensor.Componente;
|
int tipo = (int)sensor.Componente;
|
||||||
int id = sensor.ID_Num;
|
int id = sensor.ID_Num;
|
||||||
|
|
@ -361,7 +367,7 @@ namespace AgroBase.Services.Operadores
|
||||||
RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), sensoresAtualizados.ToArray());
|
RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), sensoresAtualizados.ToArray());
|
||||||
|
|
||||||
var servosAtualizados = new List<(string caminho, object valor)>();
|
var servosAtualizados = new List<(string caminho, object valor)>();
|
||||||
foreach (var servo in Variaveis.OperacaoEmAndamento.DispSen.Dados.Servos)
|
foreach (var servo in DadosSen.Servos)
|
||||||
{
|
{
|
||||||
int id = servo.ID_Num;
|
int id = servo.ID_Num;
|
||||||
string basePath = $"servos.{id}";
|
string basePath = $"servos.{id}";
|
||||||
|
|
@ -394,10 +400,12 @@ namespace AgroBase.Services.Operadores
|
||||||
servosAtualizados.Add(($"{basePath}", servoObj));
|
servosAtualizados.Add(($"{basePath}", servoObj));
|
||||||
}
|
}
|
||||||
RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), servosAtualizados.ToArray());
|
RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), servosAtualizados.ToArray());
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,36 +138,43 @@ namespace AgroBase.Services.Operadores
|
||||||
|
|
||||||
public static void AtualizarAnalise()
|
public static void AtualizarAnalise()
|
||||||
{
|
{
|
||||||
DadosLeitura.Imu = JsonConvert.DeserializeObject<VisualWorkerMessageIMUModel>(RedisService.Get(RedisService.ModKey(Enums.T_Code.Imu)));
|
string imuJson = RedisService.Get(RedisService.ModKey(Enums.T_Code.Imu));
|
||||||
|
if (!string.IsNullOrEmpty(imuJson))
|
||||||
|
{
|
||||||
|
DadosLeitura.Imu = JsonConvert.DeserializeObject<VisualWorkerMessageIMUModel>(imuJson);
|
||||||
|
}
|
||||||
|
|
||||||
var json = RedisService.Get(CtxKey.DadosVisualWorker);
|
var json = RedisService.Get(CtxKey.DadosVisualWorker);
|
||||||
var dados = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
|
var dados = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
|
||||||
|
|
||||||
DateTime timestamp = FuncoesGlobais.UnixToDateTime(dados["ts_analise"].ToString());
|
if (dados.ContainsKey("ts_analise"))
|
||||||
|
|
||||||
//var matrizes = JsonConvert.DeserializeObject<Dictionary<string, object>>(dados["matrizes"].ToString());
|
|
||||||
//var confianca = JsonConvert.DeserializeObject<List<List<double>>>(JsonConvert.SerializeObject(matrizes["confianca"]));
|
|
||||||
//var custo = JsonConvert.DeserializeObject<List<List<double>>>(JsonConvert.SerializeObject(matrizes["custo"]));
|
|
||||||
|
|
||||||
var anomalias = JsonConvert.DeserializeObject<Dictionary<string, object>>(dados["anomalias"].ToString());
|
|
||||||
var deteccoes = JsonConvert.DeserializeObject<List<VisualWorkerMessageAnaliseBBoxModel>>(JsonConvert.SerializeObject(anomalias["deteccoes"]));
|
|
||||||
var sombras = JsonConvert.DeserializeObject<List<VisualWorkerMessageAnaliseBBoxModel>>(JsonConvert.SerializeObject(anomalias["sombras"]));
|
|
||||||
|
|
||||||
var corredor = JsonConvert.DeserializeObject<Dictionary<string, object>>(dados["perfil_corredor"].ToString());
|
|
||||||
var segmentacao = JsonConvert.DeserializeObject<List<VisualWorkerMessageRadar2DPerfilModel>>(JsonConvert.SerializeObject(corredor["segmentacao"]));
|
|
||||||
var profundidade = JsonConvert.DeserializeObject<List<VisualWorkerMessageRadar2DPerfilModel>>(JsonConvert.SerializeObject(corredor["profundidade"]));
|
|
||||||
|
|
||||||
var solo = JsonConvert.DeserializeObject<VisualWorkerMessageAnaliseSoloModel>(JsonConvert.SerializeObject(dados["solo"]));
|
|
||||||
|
|
||||||
DadosLeitura.Analises = new VisualWorkerMessageAnaliseModel()
|
|
||||||
{
|
{
|
||||||
timestamp = timestamp,
|
DateTime timestamp = FuncoesGlobais.UnixToDateTime(dados["ts_analise"].ToString());
|
||||||
solo = solo,
|
|
||||||
anomalias = deteccoes,
|
//var matrizes = JsonConvert.DeserializeObject<Dictionary<string, object>>(dados["matrizes"].ToString());
|
||||||
sombras = sombras,
|
//var confianca = JsonConvert.DeserializeObject<List<List<double>>>(JsonConvert.SerializeObject(matrizes["confianca"]));
|
||||||
corredor_prof = profundidade,
|
//var custo = JsonConvert.DeserializeObject<List<List<double>>>(JsonConvert.SerializeObject(matrizes["custo"]));
|
||||||
corredor_seg = segmentacao,
|
|
||||||
};
|
var anomalias = JsonConvert.DeserializeObject<Dictionary<string, object>>(dados["anomalias"].ToString());
|
||||||
|
var deteccoes = JsonConvert.DeserializeObject<List<VisualWorkerMessageAnaliseBBoxModel>>(JsonConvert.SerializeObject(anomalias["deteccoes"]));
|
||||||
|
var sombras = JsonConvert.DeserializeObject<List<VisualWorkerMessageAnaliseBBoxModel>>(JsonConvert.SerializeObject(anomalias["sombras"]));
|
||||||
|
|
||||||
|
var corredor = JsonConvert.DeserializeObject<Dictionary<string, object>>(dados["perfil_corredor"].ToString());
|
||||||
|
var segmentacao = JsonConvert.DeserializeObject<List<VisualWorkerMessageRadar2DPerfilModel>>(JsonConvert.SerializeObject(corredor["segmentacao"]));
|
||||||
|
var profundidade = JsonConvert.DeserializeObject<List<VisualWorkerMessageRadar2DPerfilModel>>(JsonConvert.SerializeObject(corredor["profundidade"]));
|
||||||
|
|
||||||
|
var solo = JsonConvert.DeserializeObject<VisualWorkerMessageAnaliseSoloModel>(JsonConvert.SerializeObject(dados["solo"]));
|
||||||
|
|
||||||
|
DadosLeitura.Analises = new VisualWorkerMessageAnaliseModel()
|
||||||
|
{
|
||||||
|
timestamp = timestamp,
|
||||||
|
solo = solo,
|
||||||
|
anomalias = deteccoes,
|
||||||
|
sombras = sombras,
|
||||||
|
corredor_prof = profundidade,
|
||||||
|
corredor_seg = segmentacao,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
# capture_unificado.py com thread de status e IMU da OAK-D
|
||||||
|
import depthai as dai
|
||||||
|
import cv2
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
from datetime import datetime
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
from ahrs.filters import Madgwick
|
||||||
|
from scipy.spatial.transform import Rotation as R
|
||||||
|
|
||||||
|
DIMENSAO_FINAL = (1280, 720)
|
||||||
|
QUALIDADE_JPEG = 95
|
||||||
|
TECLA_SALVAR = ord('s')
|
||||||
|
ALTURA_JANELA = 1080
|
||||||
|
LARGURA_JANELA = 1920
|
||||||
|
RECONNECT_INTERVAL = 5
|
||||||
|
AUTO_INTERVAL = 2.0
|
||||||
|
|
||||||
|
CAMERAS = {
|
||||||
|
"oak-d": None,
|
||||||
|
"oak-1": None
|
||||||
|
}
|
||||||
|
|
||||||
|
CAMERAS_IDS = {
|
||||||
|
"oak-d": "14442C10C143E2D600",
|
||||||
|
"oak-1": "14442C1011AD1ED000"
|
||||||
|
}
|
||||||
|
|
||||||
|
ULTIMA_TENTATIVA = {nome: 0 for nome in CAMERAS}
|
||||||
|
ULTIMA_CAPTURA = {nome: None for nome in CAMERAS}
|
||||||
|
STATUS_INFO = {nome: None for nome in CAMERAS}
|
||||||
|
IMU_INFO = {"oak-d": None}
|
||||||
|
AUTO_MODE = {nome: False for nome in CAMERAS}
|
||||||
|
ULTIMO_AUTO_SNAPSHOT = {nome: time.time() for nome in CAMERAS}
|
||||||
|
|
||||||
|
madgwick = Madgwick()
|
||||||
|
q_imu = np.array([1.0, 0.0, 0.0, 0.0])
|
||||||
|
|
||||||
|
PASTAS_SAIDA = {
|
||||||
|
"oak-d": os.path.join("oak-d", "dataset", "original", "images"),
|
||||||
|
"oak-1": os.path.join("oak-1", "dataset", "original", "images")
|
||||||
|
}
|
||||||
|
|
||||||
|
ISO_ATUAL = {
|
||||||
|
"oak-d": 400,
|
||||||
|
"oak-1": 400
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPOSICAO_ATUAL = {
|
||||||
|
"oak-d": 6000,
|
||||||
|
"oak-1": 6000
|
||||||
|
}
|
||||||
|
|
||||||
|
ultimo_frame_salvo = {
|
||||||
|
"oak-d": np.zeros((360, 480, 3), dtype=np.uint8),
|
||||||
|
"oak-1": np.zeros((360, 480, 3), dtype=np.uint8)
|
||||||
|
}
|
||||||
|
|
||||||
|
camera_ativa = "oak-d"
|
||||||
|
|
||||||
|
for pasta in PASTAS_SAIDA.values():
|
||||||
|
os.makedirs(pasta, exist_ok=True)
|
||||||
|
|
||||||
|
def monitorar_status(nome):
|
||||||
|
while True:
|
||||||
|
time.sleep(2)
|
||||||
|
if CAMERAS[nome] is None:
|
||||||
|
STATUS_INFO[nome] = None
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
dev = CAMERAS[nome]["device"]
|
||||||
|
temp = dev.getChipTemperature().average
|
||||||
|
ddr = dev.getDdrMemoryUsage().used / 1024 / 1024
|
||||||
|
running = dev.isPipelineRunning()
|
||||||
|
speed = dev.getUsbSpeed().name
|
||||||
|
STATUS_INFO[nome] = {
|
||||||
|
"temp": temp,
|
||||||
|
"ddr": ddr,
|
||||||
|
"pipeline": running,
|
||||||
|
"usb": speed
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
STATUS_INFO[nome] = None
|
||||||
|
|
||||||
|
def iniciar_dispositivo(nome):
|
||||||
|
try:
|
||||||
|
pipeline = dai.Pipeline()
|
||||||
|
|
||||||
|
camRgb = pipeline.create(dai.node.ColorCamera)
|
||||||
|
camRgb.setPreviewSize(DIMENSAO_FINAL[0], DIMENSAO_FINAL[1])
|
||||||
|
camRgb.setInterleaved(False)
|
||||||
|
camRgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
|
||||||
|
camRgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
|
||||||
|
xoutRgb = pipeline.create(dai.node.XLinkOut)
|
||||||
|
xoutRgb.setStreamName("rgb")
|
||||||
|
camRgb.preview.link(xoutRgb.input)
|
||||||
|
|
||||||
|
control_in = pipeline.create(dai.node.XLinkIn)
|
||||||
|
control_in.setStreamName("control")
|
||||||
|
control_in.out.link(camRgb.inputControl)
|
||||||
|
|
||||||
|
if nome == "oak-d":
|
||||||
|
imu = pipeline.create(dai.node.IMU)
|
||||||
|
imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, 500)
|
||||||
|
imu.enableIMUSensor(dai.IMUSensor.GYROSCOPE_RAW, 500)
|
||||||
|
imu.setBatchReportThreshold(1)
|
||||||
|
imu.setMaxBatchReports(20)
|
||||||
|
xoutImu = pipeline.create(dai.node.XLinkOut)
|
||||||
|
xoutImu.setStreamName("imu")
|
||||||
|
imu.out.link(xoutImu.input)
|
||||||
|
|
||||||
|
mx_id = CAMERAS_IDS[nome]
|
||||||
|
dev_info = dai.DeviceInfo(mx_id)
|
||||||
|
device = dai.Device(pipeline, dev_info)
|
||||||
|
rgbQueue = device.getOutputQueue(name="rgb", maxSize=4, blocking=False)
|
||||||
|
controlQueue = device.getInputQueue("control")
|
||||||
|
|
||||||
|
cam = {
|
||||||
|
"device": device,
|
||||||
|
"queue": rgbQueue,
|
||||||
|
"control": controlQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
if nome == "oak-d":
|
||||||
|
imuQueue = device.getOutputQueue(name="imu", maxSize=50, blocking=False)
|
||||||
|
cam["imu"] = imuQueue
|
||||||
|
|
||||||
|
CAMERAS[nome] = cam
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[{nome.upper()} ❌] Erro ao iniciar: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def aplicar_controle_manual(nome):
|
||||||
|
if CAMERAS[nome] is None:
|
||||||
|
return
|
||||||
|
ctrl = dai.CameraControl()
|
||||||
|
ctrl.setManualExposure(EXPOSICAO_ATUAL[nome], ISO_ATUAL[nome])
|
||||||
|
ctrl.setManualFocus(130)
|
||||||
|
CAMERAS[nome]["control"].send(ctrl)
|
||||||
|
|
||||||
|
def monitorar_imu():
|
||||||
|
global q_imu
|
||||||
|
while True:
|
||||||
|
time.sleep(0.01)
|
||||||
|
cam = CAMERAS["oak-d"]
|
||||||
|
if cam is None or "imu" not in cam:
|
||||||
|
continue
|
||||||
|
imuQueue = cam["imu"]
|
||||||
|
imuData = imuQueue.tryGet()
|
||||||
|
if imuData is not None:
|
||||||
|
for packet in imuData.packets:
|
||||||
|
accel = packet.acceleroMeter
|
||||||
|
gyro = packet.gyroscope
|
||||||
|
ax, ay, az = accel.x, accel.y, accel.z
|
||||||
|
gx = np.deg2rad(gyro.x)
|
||||||
|
gy = np.deg2rad(gyro.y)
|
||||||
|
gz = np.deg2rad(gyro.z)
|
||||||
|
q_imu = madgwick.updateIMU(q=q_imu, gyr=np.array([gx, gy, gz]), acc=np.array([ax, ay, az]))
|
||||||
|
r = R.from_quat([q_imu[1], q_imu[2], q_imu[3], q_imu[0]])
|
||||||
|
roll, pitch, yaw = r.as_euler('xyz', degrees=True)
|
||||||
|
IMU_INFO["oak-d"] = {
|
||||||
|
"roll": roll,
|
||||||
|
"pitch": pitch,
|
||||||
|
"yaw": yaw
|
||||||
|
}
|
||||||
|
|
||||||
|
for nome in CAMERAS:
|
||||||
|
threading.Thread(target=monitorar_status, args=(nome,), daemon=True).start()
|
||||||
|
th_IMU = threading.Thread(target=monitorar_imu, daemon=True)
|
||||||
|
th_IMU.start()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
canvas = np.zeros((ALTURA_JANELA, LARGURA_JANELA, 3), dtype=np.uint8)
|
||||||
|
agora = datetime.now()
|
||||||
|
timestamp_atual = agora.strftime("%d/%m/%Y %H:%M:%S")
|
||||||
|
agora_sec = time.time()
|
||||||
|
|
||||||
|
for nome in CAMERAS:
|
||||||
|
if CAMERAS[nome] is None and agora_sec - ULTIMA_TENTATIVA[nome] >= RECONNECT_INTERVAL:
|
||||||
|
print(f"[{nome.upper()} 🔄] Tentando reconectar...")
|
||||||
|
sucesso = iniciar_dispositivo(nome)
|
||||||
|
if sucesso:
|
||||||
|
aplicar_controle_manual(nome)
|
||||||
|
ULTIMA_TENTATIVA[nome] = agora_sec
|
||||||
|
|
||||||
|
for nome in CAMERAS:
|
||||||
|
if AUTO_MODE[nome] and (time.time() - ULTIMO_AUTO_SNAPSHOT[nome]) >= AUTO_INTERVAL:
|
||||||
|
if CAMERAS[nome] is not None:
|
||||||
|
try:
|
||||||
|
frame = CAMERAS[nome]["queue"].get().getCvFrame()
|
||||||
|
now = datetime.now()
|
||||||
|
timestamp = now.strftime("%Y%m%d_%H%M%S_%f")[:-3]
|
||||||
|
nome_base = f"img_{timestamp}.jpg"
|
||||||
|
caminho_final = os.path.join(PASTAS_SAIDA[nome], nome_base)
|
||||||
|
cv2.imwrite(caminho_final, frame, [cv2.IMWRITE_JPEG_QUALITY, QUALIDADE_JPEG])
|
||||||
|
ultimo_frame_salvo[nome] = cv2.resize(frame, (480, 360))
|
||||||
|
ULTIMA_CAPTURA[nome] = now
|
||||||
|
print(f"[{nome.upper()} 🕒] Auto-snapshot salvo: {caminho_final}")
|
||||||
|
ULTIMO_AUTO_SNAPSHOT[nome] = time.time()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[{nome.upper()} ❌] Erro no auto-snapshot: {e}")
|
||||||
|
|
||||||
|
for idx, nome in enumerate(CAMERAS):
|
||||||
|
x_base = idx * 960
|
||||||
|
|
||||||
|
if CAMERAS[nome] is None:
|
||||||
|
cv2.putText(canvas, f"{nome.upper()} DESCONECTADA - Reconectando...", (x_base + 20, 50),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
in_rgb = CAMERAS[nome]["queue"].get()
|
||||||
|
frame = in_rgb.getCvFrame()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[{nome.upper()} ⚠️] Erro ao capturar frame: {e}")
|
||||||
|
CAMERAS[nome] = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
frame_vivo = cv2.resize(frame, (960, 720))
|
||||||
|
captura_redimensionada = cv2.resize(ultimo_frame_salvo[nome], (480, 360))
|
||||||
|
|
||||||
|
canvas[0:720, x_base:x_base+960] = frame_vivo
|
||||||
|
canvas[720:1080, x_base:x_base+480] = captura_redimensionada
|
||||||
|
|
||||||
|
cor = (0,255,0) if camera_ativa == nome else (255,255,255)
|
||||||
|
cv2.putText(canvas, f"{nome.upper()} (Ativa: {'SIM' if camera_ativa==nome else 'NAO'}) | Modo: {'AUTO' if AUTO_MODE[nome] else 'MANUAL'}",
|
||||||
|
(x_base + 10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.8, cor, 2)
|
||||||
|
cv2.putText(canvas, f"ISO: {ISO_ATUAL[nome]} | EXP: {EXPOSICAO_ATUAL[nome]}", (x_base + 10, 680), cv2.FONT_HERSHEY_SIMPLEX, 0.7, cor, 2)
|
||||||
|
cv2.putText(canvas, f"AO VIVO: {timestamp_atual}", (x_base + 10, 700), cv2.FONT_HERSHEY_SIMPLEX, 0.6, cor, 1)
|
||||||
|
|
||||||
|
if ULTIMA_CAPTURA[nome] is not None:
|
||||||
|
stamp = ULTIMA_CAPTURA[nome].strftime("%d/%m/%Y %H:%M:%S")
|
||||||
|
cv2.putText(canvas, f"ULTIMA CAPTURA: {stamp}", (x_base + 10, 740), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255,255,255), 1)
|
||||||
|
|
||||||
|
# Mostrar status cacheado se existir
|
||||||
|
info = STATUS_INFO.get(nome)
|
||||||
|
if info:
|
||||||
|
info_x = x_base + 500
|
||||||
|
y_base = 740
|
||||||
|
cv2.putText(canvas, f"Temp: {info['temp']:.2f} C", (info_x, y_base), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (200,200,255), 1)
|
||||||
|
cv2.putText(canvas, f"DDR: {info['ddr']:.2f} MB", (info_x, y_base+30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (200,200,255), 1)
|
||||||
|
cv2.putText(canvas, f"Pipeline: {'ON' if info['pipeline'] else 'OFF'}", (info_x, y_base+60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (200,200,255), 1)
|
||||||
|
cv2.putText(canvas, f"USB: {info['usb']}", (info_x, y_base+90), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (200,200,255), 1)
|
||||||
|
|
||||||
|
if nome == "oak-d" and IMU_INFO["oak-d"]:
|
||||||
|
imu = IMU_INFO["oak-d"]
|
||||||
|
cv2.putText(canvas, f"Roll: {imu['roll']:.2f}", (info_x, y_base+130), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (180,255,180), 1)
|
||||||
|
cv2.putText(canvas, f"Pitch: {imu['pitch']:.2f}", (info_x, y_base+160), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (180,255,180), 1)
|
||||||
|
cv2.putText(canvas, f"Yaw: {imu['yaw']:.2f}", (info_x, y_base+190), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (180,255,180), 1)
|
||||||
|
|
||||||
|
if AUTO_MODE:
|
||||||
|
cv2.putText(canvas, f"AUTO SNAP: ON ({AUTO_INTERVAL:.1f}s)", (30, 1035), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,255), 2)
|
||||||
|
else:
|
||||||
|
cv2.putText(canvas, "AUTO SNAP: OFF", (30, 1035), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (100,100,100), 2)
|
||||||
|
|
||||||
|
cv2.putText(canvas, "Teclas: [1] OAK-D | [2] OAK-1 | s/+/-/m/n = camera ativa | q para sair",
|
||||||
|
(30, 1070), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (200, 200, 255), 2)
|
||||||
|
|
||||||
|
cv2.imshow("Captura Dual", canvas)
|
||||||
|
key = cv2.waitKey(1) & 0xFF
|
||||||
|
|
||||||
|
if key == ord('1'):
|
||||||
|
camera_ativa = "oak-d"
|
||||||
|
elif key == ord('2'):
|
||||||
|
camera_ativa = "oak-1"
|
||||||
|
elif key == TECLA_SALVAR:
|
||||||
|
if CAMERAS[camera_ativa] is not None:
|
||||||
|
try:
|
||||||
|
frame = CAMERAS[camera_ativa]["queue"].get().getCvFrame()
|
||||||
|
now = datetime.now()
|
||||||
|
timestamp = now.strftime("%Y%m%d_%H%M%S_%f")[:-3]
|
||||||
|
nome_base = f"img_{timestamp}.jpg"
|
||||||
|
caminho_final = os.path.join(PASTAS_SAIDA[camera_ativa], nome_base)
|
||||||
|
cv2.imwrite(caminho_final, frame, [cv2.IMWRITE_JPEG_QUALITY, QUALIDADE_JPEG])
|
||||||
|
ultimo_frame_salvo[camera_ativa] = cv2.resize(frame, (480, 360))
|
||||||
|
ULTIMA_CAPTURA[camera_ativa] = now
|
||||||
|
print(f"[{camera_ativa.upper()} ✔] Imagem salva: {caminho_final}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[{camera_ativa.upper()} ❌] Erro ao salvar imagem: {e}")
|
||||||
|
elif key == ord('+') or key == ord('='):
|
||||||
|
ISO_ATUAL[camera_ativa] = min(1600, ISO_ATUAL[camera_ativa] + 50)
|
||||||
|
aplicar_controle_manual(camera_ativa)
|
||||||
|
elif key == ord('-'):
|
||||||
|
ISO_ATUAL[camera_ativa] = max(100, ISO_ATUAL[camera_ativa] - 50)
|
||||||
|
aplicar_controle_manual(camera_ativa)
|
||||||
|
elif key == ord('m'):
|
||||||
|
EXPOSICAO_ATUAL[camera_ativa] = min(33000, EXPOSICAO_ATUAL[camera_ativa] + 1000)
|
||||||
|
aplicar_controle_manual(camera_ativa)
|
||||||
|
elif key == ord('n'):
|
||||||
|
EXPOSICAO_ATUAL[camera_ativa] = max(100, EXPOSICAO_ATUAL[camera_ativa] - 1000)
|
||||||
|
aplicar_controle_manual(camera_ativa)
|
||||||
|
elif key == ord('a'):
|
||||||
|
AUTO_MODE[camera_ativa] = not AUTO_MODE[camera_ativa]
|
||||||
|
print(f"[AUTO] {camera_ativa.upper()} modo automático {'ativado' if AUTO_MODE[camera_ativa] else 'desativado'}")
|
||||||
|
ULTIMO_AUTO_SNAPSHOT[camera_ativa] = time.time()
|
||||||
|
elif key == ord('q'):
|
||||||
|
print("[INFO] Encerrando...")
|
||||||
|
break
|
||||||
|
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
import depthai as dai
|
||||||
|
import cv2
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# === CONFIGURAÇÕES ===
|
||||||
|
PASTA_SAIDA = "dataset"
|
||||||
|
DIMENSAO_FINAL = (1280, 720)
|
||||||
|
QUALIDADE_JPEG = 95
|
||||||
|
TECLA_SALVAR = ord('s')
|
||||||
|
|
||||||
|
# Área de exibição
|
||||||
|
ALTURA_JANELA = 1080
|
||||||
|
LARGURA_JANELA = 1920
|
||||||
|
|
||||||
|
EXPOSICAO_ATUAL = [6000]
|
||||||
|
ISO_ATUAL = [400]
|
||||||
|
|
||||||
|
os.makedirs(PASTA_SAIDA, exist_ok=True)
|
||||||
|
|
||||||
|
# === PIPELINE ===
|
||||||
|
pipeline = dai.Pipeline()
|
||||||
|
|
||||||
|
cam_rgb = pipeline.createColorCamera()
|
||||||
|
cam_rgb.setPreviewSize(DIMENSAO_FINAL)
|
||||||
|
cam_rgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
|
||||||
|
cam_rgb.setInterleaved(False)
|
||||||
|
cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
|
||||||
|
|
||||||
|
xout = pipeline.createXLinkOut()
|
||||||
|
xout.setStreamName("rgb")
|
||||||
|
cam_rgb.preview.link(xout.input)
|
||||||
|
|
||||||
|
control_in = pipeline.createXLinkIn()
|
||||||
|
control_in.setStreamName("control")
|
||||||
|
control_in.out.link(cam_rgb.inputControl)
|
||||||
|
|
||||||
|
# === EXECUÇÃO ===
|
||||||
|
with dai.Device(pipeline) as device:
|
||||||
|
print("[INFO] Pressione 's' para salvar | '+' e '-' para ISO | 'q' para sair.")
|
||||||
|
|
||||||
|
controlQueue = device.getInputQueue("control")
|
||||||
|
rgbQueue = device.getOutputQueue("rgb", maxSize=4, blocking=False)
|
||||||
|
|
||||||
|
def aplicar_controle_manual(exposicao, iso):
|
||||||
|
ctrl = dai.CameraControl()
|
||||||
|
ctrl.setManualExposure(exposicao, iso)
|
||||||
|
ctrl.setManualFocus(130)
|
||||||
|
controlQueue.send(ctrl)
|
||||||
|
print(f"[⚙️] Exposicao: {exposicao} µs | ISO: {iso}")
|
||||||
|
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
ultimo_frame_salvo = np.zeros((DIMENSAO_FINAL[1], DIMENSAO_FINAL[0], 3), dtype=np.uint8)
|
||||||
|
|
||||||
|
# Fullscreen
|
||||||
|
cv2.namedWindow("Coletor de Dataset", cv2.WINDOW_NORMAL)
|
||||||
|
cv2.setWindowProperty("Coletor de Dataset", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
in_rgb = rgbQueue.get()
|
||||||
|
frame = in_rgb.getCvFrame()
|
||||||
|
|
||||||
|
# Redimensiona o frame ao vivo
|
||||||
|
frame_vivo = cv2.resize(frame, (1280, 960))
|
||||||
|
|
||||||
|
# Redimensiona a última captura
|
||||||
|
captura_redimensionada = cv2.resize(ultimo_frame_salvo, (512, 512))
|
||||||
|
|
||||||
|
# Cria o canvas vazio
|
||||||
|
canvas = np.zeros((ALTURA_JANELA, LARGURA_JANELA, 3), dtype=np.uint8)
|
||||||
|
|
||||||
|
# Posiciona o ao vivo à esquerda
|
||||||
|
canvas[60:1020, 30:1310] = frame_vivo
|
||||||
|
|
||||||
|
# Posiciona a última imagem salva à direita
|
||||||
|
canvas[60:572, 1400:1912] = captura_redimensionada
|
||||||
|
|
||||||
|
# Adiciona textos no lado direito
|
||||||
|
cv2.putText(canvas, f"ISO: {ISO_ATUAL[0]} | Exposicao: {EXPOSICAO_ATUAL[0]}", (1350, 620), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 0), 2)
|
||||||
|
cv2.putText(canvas, "Pressione 's' para salvar", (1350, 680), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1)
|
||||||
|
cv2.putText(canvas, "'+' ou '-' para ajustar ISO", (1350, 720), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1)
|
||||||
|
cv2.putText(canvas, "'m' ou 'n' para ajustar a exposicao", (1350, 760), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1)
|
||||||
|
cv2.putText(canvas, "'q' para sair", (1350, 800), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1)
|
||||||
|
|
||||||
|
cv2.imshow("Coletor de Dataset", canvas)
|
||||||
|
|
||||||
|
key = cv2.waitKey(1) & 0xFF
|
||||||
|
|
||||||
|
if key == TECLA_SALVAR:
|
||||||
|
now = datetime.now()
|
||||||
|
timestamp = now.strftime("%Y%m%d_%H%M%S_%f")[:-3]
|
||||||
|
caminho = os.path.join(PASTA_SAIDA, f"img_{timestamp}.jpg")
|
||||||
|
|
||||||
|
# Gera timestamp e nome-base
|
||||||
|
now = datetime.now()
|
||||||
|
timestamp = now.strftime("%Y%m%d_%H%M%S_%f")[:-3]
|
||||||
|
nome_base = f"img_{timestamp}.jpg"
|
||||||
|
|
||||||
|
# Original (sem resize)
|
||||||
|
os.makedirs(os.path.join("dataset", "original", "images"), exist_ok=True)
|
||||||
|
cv2.imwrite(os.path.join("dataset", "original", "images", nome_base), frame, [cv2.IMWRITE_JPEG_QUALITY, QUALIDADE_JPEG])
|
||||||
|
|
||||||
|
# Demais resoluções
|
||||||
|
#resolucoes = {
|
||||||
|
# "512x512": (512, 512),
|
||||||
|
# "768x768": (768, 768),
|
||||||
|
# "384x384": (384, 384),
|
||||||
|
#}
|
||||||
|
#for nome_pasta, dim in resolucoes.items():
|
||||||
|
# path_pasta = os.path.join("dataset", nome_pasta)
|
||||||
|
# os.makedirs(path_pasta, exist_ok=True)
|
||||||
|
# imagem = cv2.resize(frame, dim, interpolation=cv2.INTER_AREA)
|
||||||
|
# cv2.imwrite(os.path.join(path_pasta, nome_base), imagem, [cv2.IMWRITE_JPEG_QUALITY, QUALIDADE_JPEG])
|
||||||
|
|
||||||
|
print(f"[✔] Imagem salva: {caminho}")
|
||||||
|
ultimo_frame_salvo = frame.copy()
|
||||||
|
|
||||||
|
elif key == ord('+') or key == ord('='):
|
||||||
|
ISO_ATUAL[0] = min(1600, ISO_ATUAL[0] + 50)
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
|
||||||
|
elif key == ord('-'):
|
||||||
|
ISO_ATUAL[0] = max(100, ISO_ATUAL[0] - 50)
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
|
||||||
|
elif key == ord('m'):
|
||||||
|
EXPOSICAO_ATUAL[0] = min(10000, EXPOSICAO_ATUAL[0] + 500)
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
|
||||||
|
elif key == ord('n'):
|
||||||
|
EXPOSICAO_ATUAL[0] = max(1000, EXPOSICAO_ATUAL[0] - 500)
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
|
||||||
|
elif key == ord('q'):
|
||||||
|
print("[INFO] Encerrando...")
|
||||||
|
break
|
||||||
|
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import albumentations as A
|
||||||
|
from albumentations.pytorch import ToTensorV2
|
||||||
|
import numpy as np
|
||||||
|
import random
|
||||||
|
|
||||||
|
# === CONFIG ===
|
||||||
|
PASTA_ORIGINAL = "dataset/original"
|
||||||
|
PASTA_AUGMENTED = "dataset/augmented"
|
||||||
|
NUM_AUGMENTACOES = 3 # quantas imagens gerar por imagem original
|
||||||
|
|
||||||
|
os.makedirs(os.path.join(PASTA_AUGMENTED, "images"), exist_ok=True)
|
||||||
|
os.makedirs(os.path.join(PASTA_AUGMENTED, "labels"), exist_ok=True)
|
||||||
|
|
||||||
|
transform = A.Compose([
|
||||||
|
A.HorizontalFlip(p=0.5),
|
||||||
|
A.VerticalFlip(p=0.1),
|
||||||
|
A.RandomBrightnessContrast(p=0.3),
|
||||||
|
A.Rotate(limit=10, p=0.4),
|
||||||
|
A.RandomScale(scale_limit=0.1, p=0.3),
|
||||||
|
], bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels']))
|
||||||
|
|
||||||
|
imagens = [f for f in os.listdir(os.path.join(PASTA_ORIGINAL, "images")) if f.endswith(('.jpg', '.jpeg', '.png'))]
|
||||||
|
|
||||||
|
for nome_img in imagens:
|
||||||
|
caminho_img = os.path.join(PASTA_ORIGINAL, "images", nome_img)
|
||||||
|
caminho_lbl = os.path.join(PASTA_ORIGINAL, "labels", nome_img.replace(".jpg", ".txt").replace(".jpeg", ".txt").replace(".png", ".txt"))
|
||||||
|
|
||||||
|
if not os.path.exists(caminho_lbl):
|
||||||
|
print(f"[!] Label ausente: {nome_img}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Carrega imagem e label
|
||||||
|
image = cv2.imread(caminho_img)
|
||||||
|
height, width = image.shape[:2]
|
||||||
|
|
||||||
|
with open(caminho_lbl, 'r') as f:
|
||||||
|
linhas = f.readlines()
|
||||||
|
|
||||||
|
bboxes = []
|
||||||
|
class_labels = []
|
||||||
|
for linha in linhas:
|
||||||
|
parts = linha.strip().split()
|
||||||
|
if len(parts) != 5:
|
||||||
|
continue
|
||||||
|
cls, x, y, w, h = map(float, parts)
|
||||||
|
bboxes.append([x, y, w, h])
|
||||||
|
class_labels.append(int(cls))
|
||||||
|
|
||||||
|
for i in range(NUM_AUGMENTACOES):
|
||||||
|
augmented = transform(image=image, bboxes=bboxes, class_labels=class_labels)
|
||||||
|
img_aug = augmented['image']
|
||||||
|
bboxes_aug = augmented['bboxes']
|
||||||
|
labels_aug = augmented['class_labels']
|
||||||
|
|
||||||
|
nome_base = os.path.splitext(nome_img)[0]
|
||||||
|
nome_img_out = f"{nome_base}_aug{i}.jpg"
|
||||||
|
nome_lbl_out = f"{nome_base}_aug{i}.txt"
|
||||||
|
|
||||||
|
cv2.imwrite(os.path.join(PASTA_AUGMENTED, "images", nome_img_out), img_aug)
|
||||||
|
|
||||||
|
with open(os.path.join(PASTA_AUGMENTED, "labels", nome_lbl_out), 'w') as f:
|
||||||
|
for cls, bbox in zip(labels_aug, bboxes_aug):
|
||||||
|
x, y, w, h = bbox
|
||||||
|
f.write(f"{cls} {x:.6f} {y:.6f} {w:.6f} {h:.6f}\n")
|
||||||
|
|
||||||
|
print(f"[+] Augmentado: {nome_img_out}")
|
||||||
|
|
||||||
|
print("\n✅ Augmentation finalizado!")
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import glob
|
||||||
|
|
||||||
|
# === CONFIGURACAO ===
|
||||||
|
RESOLUCOES = {
|
||||||
|
"640x640": (640, 640),
|
||||||
|
"1280x720": (1280, 720),
|
||||||
|
}
|
||||||
|
|
||||||
|
FONTES = ["original", "augmented"]
|
||||||
|
PASTA_BASE = "dataset"
|
||||||
|
|
||||||
|
for fonte in FONTES:
|
||||||
|
for nome_res, (larg, alt) in RESOLUCOES.items():
|
||||||
|
pasta_imgs_saida = os.path.join(PASTA_BASE, nome_res, "images")
|
||||||
|
pasta_lbls_saida = os.path.join(PASTA_BASE, nome_res, "labels")
|
||||||
|
os.makedirs(pasta_imgs_saida, exist_ok=True)
|
||||||
|
os.makedirs(pasta_lbls_saida, exist_ok=True)
|
||||||
|
|
||||||
|
pasta_imgs_origem = os.path.join(PASTA_BASE, fonte, "images")
|
||||||
|
pasta_lbls_origem = os.path.join(PASTA_BASE, fonte, "labels")
|
||||||
|
|
||||||
|
imagens = sorted(glob.glob(os.path.join(pasta_imgs_origem, "*.jpg")))
|
||||||
|
|
||||||
|
for i, caminho_img in enumerate(imagens, 1):
|
||||||
|
nome_base = os.path.splitext(os.path.basename(caminho_img))[0]
|
||||||
|
caminho_lbl = os.path.join(pasta_lbls_origem, f"{nome_base}.txt")
|
||||||
|
if not os.path.exists(caminho_lbl):
|
||||||
|
print(f"[!] Label ausente para {nome_base}, pulando...")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Carrega imagem
|
||||||
|
img = cv2.imread(caminho_img)
|
||||||
|
if img is None:
|
||||||
|
print(f"[!] Erro ao ler imagem {caminho_img}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
h_orig, w_orig = img.shape[:2]
|
||||||
|
img_resized = cv2.resize(img, (larg, alt), interpolation=cv2.INTER_AREA)
|
||||||
|
|
||||||
|
# Salva imagem redimensionada
|
||||||
|
caminho_saida_img = os.path.join(pasta_imgs_saida, f"{nome_base}.jpg")
|
||||||
|
cv2.imwrite(caminho_saida_img, img_resized)
|
||||||
|
|
||||||
|
# Recalcula labels
|
||||||
|
with open(caminho_lbl, 'r') as f:
|
||||||
|
linhas = f.readlines()
|
||||||
|
|
||||||
|
with open(os.path.join(pasta_lbls_saida, f"{nome_base}.txt"), 'w') as saida_lbl:
|
||||||
|
for linha in linhas:
|
||||||
|
partes = linha.strip().split()
|
||||||
|
if len(partes) != 5:
|
||||||
|
continue
|
||||||
|
cls, x, y, w, h = map(float, partes)
|
||||||
|
# Conversao relativa antiga -> relativa nova
|
||||||
|
x_pix = x * w_orig
|
||||||
|
y_pix = y * h_orig
|
||||||
|
w_pix = w * w_orig
|
||||||
|
h_pix = h * h_orig
|
||||||
|
|
||||||
|
x_novo = x_pix / larg
|
||||||
|
y_novo = y_pix / alt
|
||||||
|
w_novo = w_pix / larg
|
||||||
|
h_novo = h_pix / alt
|
||||||
|
|
||||||
|
saida_lbl.write(f"{int(cls)} {x_novo:.6f} {y_novo:.6f} {w_novo:.6f} {h_novo:.6f}\n")
|
||||||
|
|
||||||
|
print(f"[{fonte}] [{nome_res}] {i}/{len(imagens)} normalizado: {nome_base}")
|
||||||
|
|
||||||
|
print("\n✅ Normalizacao YOLO concluida!")
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
Passo 1: Clonar repositório do YoloV7
|
||||||
|
git clone https://github.com/WongKinYiu/yolov7.git
|
||||||
|
|
||||||
|
Passo 2: Navegar para yolov7
|
||||||
|
cd yolov7
|
||||||
|
|
||||||
|
Passo 3: Instalar requirements
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
Passo 4: Ajustar arquivo em "models/experimental.py", linha 252, adicionando a flag weights_only=False
|
||||||
|
ckpt = torch.load(w, map_location=map_location, weights_only=False) # load
|
||||||
|
|
||||||
|
Passo 6: Retornar a pasta anterior
|
||||||
|
cd ..
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
# === CONFIG ===
|
||||||
|
PASTA_ORIGEM_IMG = "dataset/1280x720/images"
|
||||||
|
PASTA_ORIGEM_LBL = "dataset/1280x720/labels"
|
||||||
|
PASTA_SAIDA = "yolov7/train"
|
||||||
|
|
||||||
|
SPLIT = {
|
||||||
|
"train": 0.7,
|
||||||
|
"val": 0.2,
|
||||||
|
"test": 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
# === PREPARA PASTAS ===
|
||||||
|
for tipo in SPLIT.keys():
|
||||||
|
os.makedirs(os.path.join(PASTA_SAIDA, tipo, "images"), exist_ok=True)
|
||||||
|
os.makedirs(os.path.join(PASTA_SAIDA, tipo, "labels"), exist_ok=True)
|
||||||
|
|
||||||
|
# === LISTA E EMBARALHA ===
|
||||||
|
arquivos = [f for f in os.listdir(PASTA_ORIGEM_IMG) if f.endswith(".jpg")]
|
||||||
|
random.shuffle(arquivos)
|
||||||
|
|
||||||
|
total = len(arquivos)
|
||||||
|
qt_train = int(SPLIT["train"] * total)
|
||||||
|
qt_val = int(SPLIT["val"] * total)
|
||||||
|
|
||||||
|
splits = {
|
||||||
|
"train": arquivos[:qt_train],
|
||||||
|
"val": arquivos[qt_train:qt_train+qt_val],
|
||||||
|
"test": arquivos[qt_train+qt_val:]
|
||||||
|
}
|
||||||
|
|
||||||
|
# === COPIA ===
|
||||||
|
for tipo, lista in splits.items():
|
||||||
|
for nome_img in lista:
|
||||||
|
nome_lbl = nome_img.replace(".jpg", ".txt")
|
||||||
|
|
||||||
|
origem_img = os.path.join(PASTA_ORIGEM_IMG, nome_img)
|
||||||
|
origem_lbl = os.path.join(PASTA_ORIGEM_LBL, nome_lbl)
|
||||||
|
|
||||||
|
destino_img = os.path.join(PASTA_SAIDA, tipo, "images", nome_img)
|
||||||
|
destino_lbl = os.path.join(PASTA_SAIDA, tipo, "labels", nome_lbl)
|
||||||
|
|
||||||
|
shutil.copy2(origem_img, destino_img)
|
||||||
|
|
||||||
|
if os.path.exists(origem_lbl):
|
||||||
|
shutil.copy2(origem_lbl, destino_lbl)
|
||||||
|
else:
|
||||||
|
print(f"[!] Label ausente: {nome_lbl}")
|
||||||
|
|
||||||
|
print(f"[✔] {tipo.upper()} - {len(lista)} imagens")
|
||||||
|
|
||||||
|
print("\n✅ Split do dataset para YOLOv7 concluído!")
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
Passo 1: Navegar para yolov7
|
||||||
|
cd yolov7
|
||||||
|
|
||||||
|
Passo 2: Criar arquivo de configuração do Treinamento, em "train/data.yaml", com o conteúdo
|
||||||
|
|
||||||
|
train: train/train/images
|
||||||
|
val: train/val/images
|
||||||
|
test: train/test/images
|
||||||
|
|
||||||
|
nc: 2 # número de classes
|
||||||
|
names: ["weed", "crop"]
|
||||||
|
|
||||||
|
Passo 3: Baixar recursos e testar, onde --device 0 para cuda, e cpu para CPU
|
||||||
|
python test.py --data train/data.yaml --img 640 --batch 32 --conf 0.001 --iou 0.65 --device cpu --weights yolov7.pt --name ervas_v7
|
||||||
|
|
||||||
|
Passo 4: Ajustar arquivo "train.py" na linha 71, adicionando a flag weights_only=False
|
||||||
|
run_id = torch.load(weights, map_location=device, weights_only=False).get('wandb_id') if weights.endswith('.pt') and os.path.isfile(weights) else None
|
||||||
|
|
||||||
|
E na linha 87
|
||||||
|
ckpt = torch.load(weights, map_location=device, weights_only=False) # load checkpoint
|
||||||
|
|
||||||
|
Passo 5: Remover cache dos labels antes do treinamento
|
||||||
|
del train\val\labels.cache
|
||||||
|
del train\train\labels.cache
|
||||||
|
del train\test\labels.cache
|
||||||
|
|
||||||
|
Passo 6: Treinamento, onde --device 0 para cuda, e cpu para CPU
|
||||||
|
python train.py --data train/data.yaml --weights yolov7.pt --batch 16 --epochs 50 --img 412 --device cpu --workers 8 --name ervas_v7
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
import depthai as dai
|
||||||
|
import cv2
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# === CONFIGURAÇÕES ===
|
||||||
|
PASTA_SAIDA = "dataset"
|
||||||
|
DIMENSAO_FINAL = (1920, 1080)
|
||||||
|
QUALIDADE_JPEG = 95
|
||||||
|
TECLA_SALVAR = ord('s')
|
||||||
|
|
||||||
|
# Área de exibição
|
||||||
|
ALTURA_JANELA = 1080
|
||||||
|
LARGURA_JANELA = 1920
|
||||||
|
|
||||||
|
EXPOSICAO_ATUAL = [6000]
|
||||||
|
ISO_ATUAL = [400]
|
||||||
|
|
||||||
|
os.makedirs(PASTA_SAIDA, exist_ok=True)
|
||||||
|
|
||||||
|
# === PIPELINE ===
|
||||||
|
pipeline = dai.Pipeline()
|
||||||
|
|
||||||
|
cam_rgb = pipeline.createColorCamera()
|
||||||
|
cam_rgb.setPreviewSize(DIMENSAO_FINAL)
|
||||||
|
cam_rgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
|
||||||
|
cam_rgb.setInterleaved(False)
|
||||||
|
cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
|
||||||
|
|
||||||
|
xout = pipeline.createXLinkOut()
|
||||||
|
xout.setStreamName("rgb")
|
||||||
|
cam_rgb.preview.link(xout.input)
|
||||||
|
|
||||||
|
control_in = pipeline.createXLinkIn()
|
||||||
|
control_in.setStreamName("control")
|
||||||
|
control_in.out.link(cam_rgb.inputControl)
|
||||||
|
|
||||||
|
# === EXECUÇÃO ===
|
||||||
|
with dai.Device(pipeline) as device:
|
||||||
|
print("[INFO] Pressione 's' para salvar | '+' e '-' para ISO | 'q' para sair.")
|
||||||
|
|
||||||
|
controlQueue = device.getInputQueue("control")
|
||||||
|
rgbQueue = device.getOutputQueue("rgb", maxSize=4, blocking=False)
|
||||||
|
|
||||||
|
def aplicar_controle_manual(exposicao, iso):
|
||||||
|
ctrl = dai.CameraControl()
|
||||||
|
ctrl.setManualExposure(exposicao, iso)
|
||||||
|
ctrl.setManualFocus(130)
|
||||||
|
controlQueue.send(ctrl)
|
||||||
|
print(f"[⚙️] Exposicao: {exposicao} µs | ISO: {iso}")
|
||||||
|
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
ultimo_frame_salvo = np.zeros((DIMENSAO_FINAL[1], DIMENSAO_FINAL[0], 3), dtype=np.uint8)
|
||||||
|
|
||||||
|
# Fullscreen
|
||||||
|
cv2.namedWindow("Coletor de Dataset", cv2.WINDOW_NORMAL)
|
||||||
|
cv2.setWindowProperty("Coletor de Dataset", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
in_rgb = rgbQueue.get()
|
||||||
|
frame = in_rgb.getCvFrame()
|
||||||
|
|
||||||
|
# Redimensiona o frame ao vivo
|
||||||
|
frame_vivo = cv2.resize(frame, (1280, 960))
|
||||||
|
|
||||||
|
# Redimensiona a última captura
|
||||||
|
captura_redimensionada = cv2.resize(ultimo_frame_salvo, (512, 512))
|
||||||
|
|
||||||
|
# Cria o canvas vazio
|
||||||
|
canvas = np.zeros((ALTURA_JANELA, LARGURA_JANELA, 3), dtype=np.uint8)
|
||||||
|
|
||||||
|
# Posiciona o ao vivo à esquerda
|
||||||
|
canvas[60:1020, 30:1310] = frame_vivo
|
||||||
|
|
||||||
|
# Posiciona a última imagem salva à direita
|
||||||
|
canvas[60:572, 1400:1912] = captura_redimensionada
|
||||||
|
|
||||||
|
# Adiciona textos no lado direito
|
||||||
|
cv2.putText(canvas, f"ISO: {ISO_ATUAL[0]} | Exposicao: {EXPOSICAO_ATUAL[0]}", (1350, 620), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 0), 2)
|
||||||
|
cv2.putText(canvas, "Pressione 's' para salvar", (1350, 680), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1)
|
||||||
|
cv2.putText(canvas, "'+' ou '-' para ajustar ISO", (1350, 720), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1)
|
||||||
|
cv2.putText(canvas, "'m' ou 'n' para ajustar a exposicao", (1350, 760), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1)
|
||||||
|
cv2.putText(canvas, "'q' para sair", (1350, 800), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1)
|
||||||
|
|
||||||
|
cv2.imshow("Coletor de Dataset", canvas)
|
||||||
|
|
||||||
|
key = cv2.waitKey(1) & 0xFF
|
||||||
|
|
||||||
|
if key == TECLA_SALVAR:
|
||||||
|
now = datetime.now()
|
||||||
|
timestamp = now.strftime("%Y%m%d_%H%M%S_%f")[:-3]
|
||||||
|
caminho = os.path.join(PASTA_SAIDA, f"img_{timestamp}.jpg")
|
||||||
|
|
||||||
|
# Gera timestamp e nome-base
|
||||||
|
now = datetime.now()
|
||||||
|
timestamp = now.strftime("%Y%m%d_%H%M%S_%f")[:-3]
|
||||||
|
nome_base = f"img_{timestamp}.jpg"
|
||||||
|
|
||||||
|
# Original (sem resize)
|
||||||
|
os.makedirs(os.path.join("dataset", "original", "images"), exist_ok=True)
|
||||||
|
cv2.imwrite(os.path.join("dataset", "original", "images", nome_base), frame, [cv2.IMWRITE_JPEG_QUALITY, QUALIDADE_JPEG])
|
||||||
|
|
||||||
|
# Demais resoluções
|
||||||
|
#resolucoes = {
|
||||||
|
# "512x512": (512, 512),
|
||||||
|
# "768x768": (768, 768),
|
||||||
|
# "384x384": (384, 384),
|
||||||
|
#}
|
||||||
|
#for nome_pasta, dim in resolucoes.items():
|
||||||
|
# path_pasta = os.path.join("dataset", nome_pasta)
|
||||||
|
# os.makedirs(path_pasta, exist_ok=True)
|
||||||
|
# imagem = cv2.resize(frame, dim, interpolation=cv2.INTER_AREA)
|
||||||
|
# cv2.imwrite(os.path.join(path_pasta, nome_base), imagem, [cv2.IMWRITE_JPEG_QUALITY, QUALIDADE_JPEG])
|
||||||
|
|
||||||
|
print(f"[✔] Imagem salva: {caminho}")
|
||||||
|
ultimo_frame_salvo = frame.copy()
|
||||||
|
|
||||||
|
elif key == ord('+') or key == ord('='):
|
||||||
|
ISO_ATUAL[0] = min(1600, ISO_ATUAL[0] + 50)
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
|
||||||
|
elif key == ord('-'):
|
||||||
|
ISO_ATUAL[0] = max(100, ISO_ATUAL[0] - 50)
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
|
||||||
|
elif key == ord('m'):
|
||||||
|
EXPOSICAO_ATUAL[0] = min(10000, EXPOSICAO_ATUAL[0] + 500)
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
|
||||||
|
elif key == ord('n'):
|
||||||
|
EXPOSICAO_ATUAL[0] = max(1000, EXPOSICAO_ATUAL[0] - 500)
|
||||||
|
aplicar_controle_manual(EXPOSICAO_ATUAL[0], ISO_ATUAL[0])
|
||||||
|
|
||||||
|
elif key == ord('q'):
|
||||||
|
print("[INFO] Encerrando...")
|
||||||
|
break
|
||||||
|
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
import os
|
||||||
|
from torchvision import transforms
|
||||||
|
from PIL import ImageOps, Image
|
||||||
|
import torchvision.transforms.functional as TF
|
||||||
|
from torchvision.transforms.functional import to_pil_image
|
||||||
|
import torchvision.transforms as T
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class ComposeWithSeed(object):
|
||||||
|
def __init__(self, transforms):
|
||||||
|
self.transforms = transforms
|
||||||
|
|
||||||
|
def __call__(self, i, img, mask):
|
||||||
|
transforms.RandomHorizontalFlip(p=0.5)
|
||||||
|
t = self.transforms[i]
|
||||||
|
img = t(img)
|
||||||
|
mask = t(mask)
|
||||||
|
return img, mask
|
||||||
|
|
||||||
|
# Função para aplicar flip horizontal e vertical
|
||||||
|
def flip_image(img):
|
||||||
|
return ImageOps.mirror(ImageOps.flip(img))
|
||||||
|
|
||||||
|
# Função para rotacionar a imagem em ângulos fixos
|
||||||
|
def rotate_image(img, angle):
|
||||||
|
return TF.rotate(img, angle)
|
||||||
|
|
||||||
|
def perspective_image(img, magnitude=0.5):
|
||||||
|
width, height = img.size
|
||||||
|
|
||||||
|
# Pontos de origem
|
||||||
|
points_orig = np.float32([
|
||||||
|
[0, 0],
|
||||||
|
[width, 0],
|
||||||
|
[0, height],
|
||||||
|
[width, height]
|
||||||
|
])
|
||||||
|
|
||||||
|
# Pontos de destino, deslocados com base na magnitude
|
||||||
|
points_dest = np.float32([
|
||||||
|
[int(magnitude * width), int(magnitude * height)],
|
||||||
|
[int((1 - magnitude) * width), 0],
|
||||||
|
[0, int((1 - magnitude) * height)],
|
||||||
|
[width, height]
|
||||||
|
])
|
||||||
|
|
||||||
|
# Calcula a matriz de transformação e aplica a transformação de perspectiva
|
||||||
|
matrix = cv2.getPerspectiveTransform(points_orig, points_dest)
|
||||||
|
img_transformed = cv2.warpPerspective(np.array(img), matrix, (width, height))
|
||||||
|
|
||||||
|
return Image.fromarray(img_transformed)
|
||||||
|
|
||||||
|
# Definindo as transformações
|
||||||
|
transform_list = [
|
||||||
|
T.Lambda(lambda img: flip_image(img)), # Aplica flip nos dois eixos
|
||||||
|
T.Lambda(lambda img: rotate_image(img, 90)), # Rotação de 90 graus
|
||||||
|
T.Lambda(lambda img: rotate_image(img, -90)), # Rotação de -90 graus
|
||||||
|
T.Lambda(lambda img: perspective_image(img, magnitude=0.2)),
|
||||||
|
T.Lambda(lambda img: perspective_image(img, magnitude=0.1)),
|
||||||
|
# Aqui, você pode adicionar outras transformações fixas conforme necessário
|
||||||
|
#T.Resize((256, 256)), # Redimensionamento para o tamanho desejado
|
||||||
|
T.ToTensor(), # Converte as imagens PIL para tensores PyTorch
|
||||||
|
# Normalização pode ser adicionada aqui, se necessário
|
||||||
|
]
|
||||||
|
|
||||||
|
# Agora, definimos a transformação composta com a classe personalizada
|
||||||
|
transform = ComposeWithSeed(transform_list)
|
||||||
|
|
||||||
|
# Caminhos para os diretórios onde suas imagens e máscaras originais estão armazenadas
|
||||||
|
dataset_path = 'dataset/original/images'
|
||||||
|
masks_path = 'dataset/original/masks'
|
||||||
|
|
||||||
|
# Caminhos para os diretórios onde as imagens e máscaras aumentadas serão salvas
|
||||||
|
augmented_images_path = 'dataset/augmented/images'
|
||||||
|
augmented_masks_path = 'dataset/augmented/masks'
|
||||||
|
|
||||||
|
# Verifica se os diretórios de destino existem, caso contrário, cria os diretórios
|
||||||
|
os.makedirs(augmented_images_path, exist_ok=True)
|
||||||
|
os.makedirs(augmented_masks_path, exist_ok=True)
|
||||||
|
|
||||||
|
# Função para aplicar a transformação e salvar as imagens e máscaras transformadas
|
||||||
|
def augment_images_and_masks(dataset_path, masks_path, augmented_images_path, augmented_masks_path, transform, num_copies=5):
|
||||||
|
# Lista todos os arquivos nos diretórios do dataset de imagens e máscaras
|
||||||
|
image_files = [f for f in os.listdir(dataset_path) if os.path.isfile(os.path.join(dataset_path, f))]
|
||||||
|
mask_files = [f for f in os.listdir(masks_path) if os.path.isfile(os.path.join(masks_path, f))]
|
||||||
|
|
||||||
|
for image_file, mask_file in zip(image_files, mask_files):
|
||||||
|
image_path = os.path.join(dataset_path, image_file)
|
||||||
|
mask_path = os.path.join(masks_path, mask_file)
|
||||||
|
image = Image.open(image_path).convert('RGB')
|
||||||
|
mask = Image.open(mask_path).convert('RGB')
|
||||||
|
|
||||||
|
for i in range(num_copies):
|
||||||
|
# Aplica a transformação de maneira consistente em ambos, imagem e máscara
|
||||||
|
transformed_image, transformed_mask = transform(i, image, mask)
|
||||||
|
|
||||||
|
# Salva a imagem e a máscara transformadas
|
||||||
|
image_save_path = os.path.join(augmented_images_path, f"{os.path.splitext(image_file)[0]}_aug_{i}{os.path.splitext(image_file)[1]}")
|
||||||
|
mask_save_path = os.path.join(augmented_masks_path, f"{os.path.splitext(mask_file)[0]}_aug_{i}{os.path.splitext(mask_file)[1]}")
|
||||||
|
|
||||||
|
#transformed_image_pil = to_pil_image(transformed_image)
|
||||||
|
transformed_image.save(image_save_path)
|
||||||
|
#transformed_mask_pil = to_pil_image(transformed_mask)
|
||||||
|
transformed_mask.save(mask_save_path)
|
||||||
|
|
||||||
|
# Chama a função para iniciar o processo de aumento de dados
|
||||||
|
augment_images_and_masks(dataset_path, masks_path, augmented_images_path, augmented_masks_path, transform, num_copies=5)
|
||||||
|
|
||||||
|
print("Augmentation completed!")
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# === CONFIGURAÇÕES ===
|
||||||
|
PASTA_BASE = "dataset"
|
||||||
|
FONTE_DADOS = ["original", "augmented"] # <<-- novas fontes
|
||||||
|
LABELMAP_PATH = os.path.join(PASTA_BASE, "labelmap.txt")
|
||||||
|
|
||||||
|
RESOLUCOES = {
|
||||||
|
"512x512": (512, 512),
|
||||||
|
"768x768": (768, 768),
|
||||||
|
"384x384": (384, 384)
|
||||||
|
}
|
||||||
|
|
||||||
|
# === Função para ler o labelmap ===
|
||||||
|
def carregar_labelmap_completo(caminho):
|
||||||
|
cor_para_id = {}
|
||||||
|
id_para_nome = {}
|
||||||
|
cores_bgr = []
|
||||||
|
|
||||||
|
with open(caminho, 'r') as arquivo:
|
||||||
|
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
|
||||||
|
|
||||||
|
classe_id = len(cor_para_id)
|
||||||
|
cor_para_id[cor_bgr] = classe_id
|
||||||
|
cores_bgr.append(cor_bgr)
|
||||||
|
id_para_nome[classe_id] = nome_classe
|
||||||
|
|
||||||
|
return cor_para_id, cores_bgr, id_para_nome
|
||||||
|
|
||||||
|
|
||||||
|
# === Converte uma máscara RGB para índice de classes ===
|
||||||
|
def converter_mask_rgb_para_ids(img_rgb, mapa):
|
||||||
|
h, w, _ = img_rgb.shape
|
||||||
|
mask = np.zeros((h, w), dtype=np.uint8)
|
||||||
|
for cor, classe_id in mapa.items():
|
||||||
|
r, g, b = cor
|
||||||
|
cond = (img_rgb[:,:,0] == b) & (img_rgb[:,:,1] == g) & (img_rgb[:,:,2] == r)
|
||||||
|
mask[cond] = classe_id
|
||||||
|
return mask
|
||||||
|
|
||||||
|
# === Início do processamento ===
|
||||||
|
cor_para_id, _, _ = carregar_labelmap_completo(LABELMAP_PATH)
|
||||||
|
|
||||||
|
# Cria pastas de saída
|
||||||
|
for nome_res, dim in RESOLUCOES.items():
|
||||||
|
os.makedirs(os.path.join(PASTA_BASE, nome_res, "images"), exist_ok=True)
|
||||||
|
os.makedirs(os.path.join(PASTA_BASE, nome_res, "masks"), exist_ok=True)
|
||||||
|
|
||||||
|
# Processa cada fonte de dados (original + augmented)
|
||||||
|
for fonte in FONTE_DADOS:
|
||||||
|
if (not os.path.exists(os.path.join(PASTA_BASE, fonte))):
|
||||||
|
continue
|
||||||
|
pasta_rgb = os.path.join(PASTA_BASE, fonte, "images")
|
||||||
|
pasta_masks = os.path.join(PASTA_BASE, fonte, "masks")
|
||||||
|
|
||||||
|
nomes_arquivos = sorted([f for f in os.listdir(pasta_rgb) if f.endswith(".jpg") or f.endswith(".jpeg")])
|
||||||
|
total = len(nomes_arquivos)
|
||||||
|
|
||||||
|
for i, nome in enumerate(nomes_arquivos, 1):
|
||||||
|
caminho_rgb = os.path.join(pasta_rgb, nome)
|
||||||
|
caminho_mask = os.path.join(pasta_masks, nome.replace(".jpg", ".png").replace(".jpeg", ".png"))
|
||||||
|
|
||||||
|
img_rgb = cv2.imread(caminho_rgb)
|
||||||
|
if img_rgb is None:
|
||||||
|
print(f"[!] Erro ao ler imagem {nome}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Tenta carregar a máscara RGB (se existir)
|
||||||
|
if os.path.exists(caminho_mask):
|
||||||
|
img_mask_rgb = cv2.imread(caminho_mask)
|
||||||
|
img_mask_rgb = cv2.cvtColor(img_mask_rgb, cv2.COLOR_BGR2RGB) # ← CORRIGE isso!
|
||||||
|
if img_mask_rgb is not None:
|
||||||
|
mask_ids = converter_mask_rgb_para_ids(img_mask_rgb, cor_para_id)
|
||||||
|
else:
|
||||||
|
print(f"[!] Erro ao ler máscara {caminho_mask}, ignorando.")
|
||||||
|
mask_ids = None
|
||||||
|
else:
|
||||||
|
mask_ids = None
|
||||||
|
|
||||||
|
for nome_res, dim in RESOLUCOES.items():
|
||||||
|
# Cria nomes únicos baseados na fonte
|
||||||
|
nome_saida_img = f"{fonte}_{nome}"
|
||||||
|
nome_saida_mask = nome_saida_img.replace(".jpg", ".png").replace(".jpeg", ".png")
|
||||||
|
|
||||||
|
# Redimensiona e salva imagem
|
||||||
|
img_resized = cv2.resize(img_rgb, dim, interpolation=cv2.INTER_AREA)
|
||||||
|
path_img_saida = os.path.join(PASTA_BASE, nome_res, "images", nome_saida_img)
|
||||||
|
cv2.imwrite(path_img_saida, img_resized)
|
||||||
|
|
||||||
|
# Redimensiona e salva máscara (se existir)
|
||||||
|
if mask_ids is not None:
|
||||||
|
mask_resized = cv2.resize(mask_ids, dim, interpolation=cv2.INTER_NEAREST)
|
||||||
|
path_mask_saida = os.path.join(PASTA_BASE, nome_res, "masks", nome_saida_mask)
|
||||||
|
cv2.imwrite(path_mask_saida, mask_resized)
|
||||||
|
|
||||||
|
print(f"[{fonte}] [{i}/{total}] Redimensionado: {nome}")
|
||||||
|
|
||||||
|
print("\n✅ Concluído com sucesso! Todas as fontes foram processadas.")
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import random
|
||||||
|
|
||||||
|
# === CONFIGURAÇÕES ===
|
||||||
|
RESOLUCAO_BASE = "512x512"
|
||||||
|
PASTA_ORIGEM = os.path.join("dataset", RESOLUCAO_BASE)
|
||||||
|
PASTA_DESTINO = os.path.join("dataset", "split")
|
||||||
|
|
||||||
|
PERCENT_TRAIN = 0.7
|
||||||
|
PERCENT_VAL = 0.2
|
||||||
|
PERCENT_TEST = 0.1
|
||||||
|
|
||||||
|
SEED = 42
|
||||||
|
random.seed(SEED)
|
||||||
|
|
||||||
|
# === Coleta imagens ===
|
||||||
|
pasta_rgb = os.path.join(PASTA_ORIGEM, "images")
|
||||||
|
pasta_masks = os.path.join(PASTA_ORIGEM, "masks")
|
||||||
|
|
||||||
|
arquivos = sorted([f for f in os.listdir(pasta_rgb) if f.endswith(".jpg") or f.endswith(".jpeg")])
|
||||||
|
|
||||||
|
# Embaralha
|
||||||
|
random.shuffle(arquivos)
|
||||||
|
|
||||||
|
# Divide
|
||||||
|
total = len(arquivos)
|
||||||
|
n_train = int(total * PERCENT_TRAIN)
|
||||||
|
n_val = int(total * PERCENT_VAL)
|
||||||
|
|
||||||
|
arquivos_train = arquivos[:n_train]
|
||||||
|
arquivos_val = arquivos[n_train:n_train+n_val]
|
||||||
|
arquivos_test = arquivos[n_train+n_val:]
|
||||||
|
|
||||||
|
conjuntos = {
|
||||||
|
"train": arquivos_train,
|
||||||
|
"val": arquivos_val,
|
||||||
|
"test": arquivos_test
|
||||||
|
}
|
||||||
|
|
||||||
|
# === Função auxiliar ===
|
||||||
|
def copiar(imagens, conjunto):
|
||||||
|
path_img_dest = os.path.join(PASTA_DESTINO, conjunto, "images")
|
||||||
|
path_mask_dest = os.path.join(PASTA_DESTINO, conjunto, "masks")
|
||||||
|
os.makedirs(path_img_dest, exist_ok=True)
|
||||||
|
os.makedirs(path_mask_dest, exist_ok=True)
|
||||||
|
|
||||||
|
for nome in imagens:
|
||||||
|
nome_mask = nome.replace(".jpg", ".png").replace(".jpeg", ".png")
|
||||||
|
if not os.path.exists(nome_mask):
|
||||||
|
continue
|
||||||
|
shutil.copy2(os.path.join(pasta_rgb, nome), os.path.join(path_img_dest, nome))
|
||||||
|
shutil.copy2(os.path.join(pasta_masks, nome_mask), os.path.join(path_mask_dest, nome_mask))
|
||||||
|
|
||||||
|
# === Executa cópia ===
|
||||||
|
for conjunto, lista in conjuntos.items():
|
||||||
|
print(f"[{conjunto}] {len(lista)} arquivos")
|
||||||
|
copiar(lista, conjunto)
|
||||||
|
|
||||||
|
print("\n✅ Dataset dividido com sucesso!")
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
import torch
|
||||||
|
import torch.optim as optim
|
||||||
|
#import network.modeling as models
|
||||||
|
import numpy as np
|
||||||
|
from torch.utils.data import Dataset, DataLoader
|
||||||
|
from torchvision import transforms as T
|
||||||
|
from PIL import Image
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# Configuração do parsing de argumentos
|
||||||
|
parser = argparse.ArgumentParser(description='Treinamento do modelo DeepLabV3+')
|
||||||
|
parser.add_argument('--checkpoint', type=str, help='Caminho para o checkpoint de onde continuar o treinamento', default=None)
|
||||||
|
parser.add_argument('--n_epoch', type=str, help='Número de épocas para o treinamento', default='10')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Transformação personalizada para máscaras
|
||||||
|
def mask_transform(mask):
|
||||||
|
return torch.tensor(np.array(mask), dtype=torch.long)
|
||||||
|
|
||||||
|
# Transformações para as imagens
|
||||||
|
image_transforms = T.Compose([
|
||||||
|
T.Resize((512, 512)),
|
||||||
|
T.ToTensor(),
|
||||||
|
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Dataset personalizado
|
||||||
|
class CustomVOC(Dataset):
|
||||||
|
def __init__(self, image_dirs, mask_dirs, transform=None, mask_transform=None):
|
||||||
|
self.transform = transform
|
||||||
|
self.mask_transform = mask_transform
|
||||||
|
self.images = []
|
||||||
|
self.masks = []
|
||||||
|
|
||||||
|
# Combine todos os arquivos de imagem dos diretórios fornecidos
|
||||||
|
for image_dir in image_dirs:
|
||||||
|
for img in os.listdir(image_dir):
|
||||||
|
if not img.endswith(".jpg") and not img.endswith(".jpeg"):
|
||||||
|
continue
|
||||||
|
self.images.append(os.path.join(image_dir, img))
|
||||||
|
mask_name = img.replace(".jpg", ".png").replace(".jpeg", ".png")
|
||||||
|
self.masks.append(os.path.join(mask_dirs[0], mask_name))
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.images)
|
||||||
|
|
||||||
|
def __getitem__(self, idx):
|
||||||
|
img_path = self.images[idx]
|
||||||
|
mask_path = self.masks[idx] # Já ajustado na inicialização
|
||||||
|
image = Image.open(img_path).convert("RGB")
|
||||||
|
mask = Image.open(mask_path).convert("L") # 1 canal
|
||||||
|
|
||||||
|
if self.transform:
|
||||||
|
image = self.transform(image)
|
||||||
|
if self.mask_transform:
|
||||||
|
mask = self.mask_transform(mask)
|
||||||
|
|
||||||
|
return image, mask
|
||||||
|
|
||||||
|
# Instanciando o Dataset
|
||||||
|
save_dir = "backup/"
|
||||||
|
model_name = "ruasModel"
|
||||||
|
|
||||||
|
train_dataset = CustomVOC(
|
||||||
|
image_dirs=["dataset/split/train/images"],
|
||||||
|
mask_dirs=["dataset/split/train/masks"],
|
||||||
|
transform=image_transforms,
|
||||||
|
mask_transform=mask_transform
|
||||||
|
)
|
||||||
|
train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True, num_workers=0)
|
||||||
|
|
||||||
|
val_dataset = CustomVOC(
|
||||||
|
image_dirs=["dataset/split/val/images"],
|
||||||
|
mask_dirs=["dataset/split/val/masks"],
|
||||||
|
transform=image_transforms,
|
||||||
|
mask_transform=mask_transform
|
||||||
|
)
|
||||||
|
val_loader = DataLoader(val_dataset, batch_size=4, shuffle=False, num_workers=0)
|
||||||
|
|
||||||
|
# Definindo o modelo DeepLabV3+
|
||||||
|
def create_deeplabv3plus(_num_classes, _output_stride):
|
||||||
|
#model = models.deeplabv3plus_resnet50(num_classes=_num_classes, output_stride=_output_stride)
|
||||||
|
from torchvision.models.segmentation import deeplabv3_resnet50
|
||||||
|
model = deeplabv3_resnet50(weights=None, num_classes=_num_classes, output_stride=_output_stride)
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
# Configuração do modelo, perda e otimizador
|
||||||
|
num_classes = 4
|
||||||
|
output_stride = 16
|
||||||
|
model = create_deeplabv3plus(num_classes, output_stride)
|
||||||
|
criterion = torch.nn.CrossEntropyLoss()
|
||||||
|
optimizer = optim.Adam(model.parameters(), lr=0.001)
|
||||||
|
|
||||||
|
# Preparando o modelo para treinamento
|
||||||
|
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||||
|
|
||||||
|
# Carregar checkpoint se fornecido
|
||||||
|
start_epoch = 0 # Iniciar do começo se nenhum checkpoint for fornecido
|
||||||
|
if args.checkpoint:
|
||||||
|
checkpoint = torch.load(args.checkpoint)
|
||||||
|
model.load_state_dict(checkpoint['model_state_dict'])
|
||||||
|
|
||||||
|
# Carrega o estado do otimizador
|
||||||
|
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
|
||||||
|
|
||||||
|
# Garante que todos os tensores no otimizador estejam no dispositivo correto
|
||||||
|
for state in optimizer.state.values():
|
||||||
|
for k, v in state.items():
|
||||||
|
if isinstance(v, torch.Tensor):
|
||||||
|
state[k] = v.to(device)
|
||||||
|
|
||||||
|
start_epoch = checkpoint['epoch']
|
||||||
|
print(f"Continuando o treinamento do checkpoint {args.checkpoint}, a partir da época {start_epoch+1}")
|
||||||
|
|
||||||
|
model.to(device)
|
||||||
|
# Força os BatchNorms internos (como do ASPP) para modo eval
|
||||||
|
for m in model.modules():
|
||||||
|
if isinstance(m, torch.nn.BatchNorm2d):
|
||||||
|
m.eval()
|
||||||
|
|
||||||
|
epoch_losses = []
|
||||||
|
epoch_accuracies = []
|
||||||
|
|
||||||
|
plt.ion() # Ativa o modo interativo
|
||||||
|
fig, ax1 = plt.subplots()
|
||||||
|
|
||||||
|
color_loss = 'tab:red'
|
||||||
|
ax1.set_xlabel('Época')
|
||||||
|
ax1.set_ylabel('Loss', color=color_loss)
|
||||||
|
ax1.tick_params(axis='y', labelcolor=color_loss)
|
||||||
|
|
||||||
|
ax2 = ax1.twinx() # Instancia um segundo eixo que compartilha o mesmo eixo x
|
||||||
|
color_accuracy = 'tab:blue'
|
||||||
|
ax2.set_ylabel('Accuracy', color=color_accuracy) # Definimos o label do eixo y
|
||||||
|
ax2.tick_params(axis='y', labelcolor=color_accuracy)
|
||||||
|
|
||||||
|
fig.tight_layout() # Ajusta o layout para evitar sobreposições
|
||||||
|
|
||||||
|
# Treinamento do modelo
|
||||||
|
num_epochs = int(args.n_epoch)
|
||||||
|
# Loop de treinamento, ajustado para continuar de onde parou
|
||||||
|
best_val_loss = float('inf')
|
||||||
|
|
||||||
|
for epoch in range(start_epoch, num_epochs):
|
||||||
|
print(f"Epoca {epoch}")
|
||||||
|
# === TREINO ===
|
||||||
|
model.train()
|
||||||
|
running_loss = 0.0
|
||||||
|
correct = 0
|
||||||
|
total = 0
|
||||||
|
|
||||||
|
for images, masks in train_loader:
|
||||||
|
if images.size(0) == 1:
|
||||||
|
print("[⚠️] Pulando batch com 1 imagem (evita erro no BatchNorm)")
|
||||||
|
continue
|
||||||
|
images = images.to(device)
|
||||||
|
masks = masks.to(device)
|
||||||
|
|
||||||
|
optimizer.zero_grad()
|
||||||
|
print(f"Batch shape: {images.shape}")
|
||||||
|
outputs = model(images)
|
||||||
|
loss = criterion(outputs['out'], masks)
|
||||||
|
loss.backward()
|
||||||
|
optimizer.step()
|
||||||
|
|
||||||
|
running_loss += loss.item()
|
||||||
|
_, predicted = torch.max(outputs['out'].data, 1)
|
||||||
|
correct += (predicted == masks).sum().item()
|
||||||
|
total += masks.numel()
|
||||||
|
|
||||||
|
train_loss = running_loss / len(train_loader)
|
||||||
|
train_accuracy = 100 * correct / total
|
||||||
|
|
||||||
|
# === VALIDAÇÃO ===
|
||||||
|
model.eval()
|
||||||
|
val_loss = 0.0
|
||||||
|
val_correct = 0
|
||||||
|
val_total = 0
|
||||||
|
|
||||||
|
with torch.no_grad():
|
||||||
|
for images, masks in val_loader:
|
||||||
|
images = images.to(device)
|
||||||
|
masks = masks.to(device)
|
||||||
|
|
||||||
|
outputs = model(images)
|
||||||
|
loss = criterion(outputs['out'], masks)
|
||||||
|
val_loss += loss.item()
|
||||||
|
|
||||||
|
_, predicted = torch.max(outputs['out'].data, 1)
|
||||||
|
val_correct += (predicted == masks).sum().item()
|
||||||
|
val_total += masks.numel()
|
||||||
|
|
||||||
|
val_loss /= len(val_loader)
|
||||||
|
val_accuracy = 100 * val_correct / val_total
|
||||||
|
|
||||||
|
# === Checkpoint da época ===
|
||||||
|
checkpoint_path = os.path.join(save_dir, f"{model_name}_checkpoint_epoch_{epoch+1}.pth")
|
||||||
|
torch.save({
|
||||||
|
'epoch': epoch+1,
|
||||||
|
'model_state_dict': model.state_dict(),
|
||||||
|
'optimizer_state_dict': optimizer.state_dict(),
|
||||||
|
'loss': train_loss,
|
||||||
|
'val_loss': val_loss,
|
||||||
|
}, checkpoint_path)
|
||||||
|
print(f"[✔] Checkpoint salvo: {checkpoint_path}")
|
||||||
|
|
||||||
|
# === Melhor modelo (baseado no val_loss) ===
|
||||||
|
if val_loss < best_val_loss:
|
||||||
|
best_val_loss = val_loss
|
||||||
|
best_model_path = os.path.join(save_dir, f"{model_name}_best.pth")
|
||||||
|
torch.save(model.state_dict(), best_model_path)
|
||||||
|
print(f"[🌟] Novo melhor modelo salvo: {best_model_path}")
|
||||||
|
|
||||||
|
# === Plot dados ===
|
||||||
|
epoch_losses.append(train_loss)
|
||||||
|
epoch_accuracies.append(train_accuracy)
|
||||||
|
ax1.plot(epoch_losses, color='tab:red')
|
||||||
|
ax2.plot(epoch_accuracies, color='tab:blue')
|
||||||
|
fig.canvas.draw()
|
||||||
|
fig.canvas.flush_events()
|
||||||
|
|
||||||
|
print(f"[📊] Época {epoch+1} | Train Loss: {train_loss:.4f} | Train Acc: {train_accuracy:.2f}% | Val Loss: {val_loss:.4f} | Val Acc: {val_accuracy:.2f}%")
|
||||||
|
|
||||||
|
|
||||||
|
# Salvar o modelo final
|
||||||
|
save_path = f'{save_dir}{model_name}_final.pth'
|
||||||
|
torch.save(model.state_dict(), save_path)
|
||||||
|
print(f"Modelo salvo em {save_path}")
|
||||||
|
|
||||||
|
plt.ioff() # Desativa o modo interativo
|
||||||
|
plt.show()
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import torch
|
||||||
|
import torchvision.transforms as T
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
import cv2
|
||||||
|
import depthai as dai
|
||||||
|
|
||||||
|
# === CONFIGURAÇÕES ===
|
||||||
|
model_path = 'backup/ruasModel_best.pth'
|
||||||
|
num_classes = 4
|
||||||
|
labelmap_path = 'dataset/labelmap.txt'
|
||||||
|
image_path = 'dataset/split/test/images/'
|
||||||
|
USE_CAMERA = False
|
||||||
|
|
||||||
|
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||||
|
|
||||||
|
# === FUNÇÕES AUXILIARES ===
|
||||||
|
|
||||||
|
def carregar_labelmap_completo(caminho):
|
||||||
|
cor_para_id = {}
|
||||||
|
id_para_nome = {}
|
||||||
|
cores_bgr = []
|
||||||
|
|
||||||
|
with open(caminho, 'r') as arquivo:
|
||||||
|
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
|
||||||
|
|
||||||
|
classe_id = len(cor_para_id)
|
||||||
|
cor_para_id[cor_bgr] = classe_id
|
||||||
|
cores_bgr.append(cor_bgr)
|
||||||
|
id_para_nome[classe_id] = nome_classe
|
||||||
|
|
||||||
|
return cor_para_id, cores_bgr, id_para_nome
|
||||||
|
|
||||||
|
def segment_image(image_path, model):
|
||||||
|
image = Image.open(image_path).convert("RGB")
|
||||||
|
transform = T.Compose([
|
||||||
|
T.Resize((512, 512)),
|
||||||
|
T.ToTensor(),
|
||||||
|
T.Normalize(mean=[0.485, 0.456, 0.406],
|
||||||
|
std=[0.229, 0.224, 0.225])
|
||||||
|
])
|
||||||
|
input_tensor = transform(image).unsqueeze(0).to(device)
|
||||||
|
|
||||||
|
model.eval()
|
||||||
|
with torch.no_grad():
|
||||||
|
output = model(input_tensor)['out']
|
||||||
|
prediction = torch.argmax(output.squeeze(), dim=0).cpu().numpy()
|
||||||
|
return prediction
|
||||||
|
|
||||||
|
def display_segmentation(original_path, prediction):
|
||||||
|
original = cv2.imread(original_path)
|
||||||
|
original = cv2.resize(original, (512, 512))
|
||||||
|
|
||||||
|
_, cores_bgr, id_para_nome = carregar_labelmap_completo(labelmap_path)
|
||||||
|
|
||||||
|
seg_color = np.zeros_like(original)
|
||||||
|
for class_id, color in enumerate(cores_bgr):
|
||||||
|
seg_color[prediction == class_id] = color
|
||||||
|
|
||||||
|
overlay = cv2.addWeighted(original, 0.5, seg_color, 0.5, 0)
|
||||||
|
|
||||||
|
# Legenda
|
||||||
|
legenda_inicio_y = 20
|
||||||
|
for i, cor in enumerate(cores_bgr):
|
||||||
|
nome = id_para_nome.get(i, f"Classe {i}")
|
||||||
|
pos_y = legenda_inicio_y + i * 30
|
||||||
|
cv2.rectangle(overlay, (10, pos_y - 15), (30, pos_y + 5), cor, -1)
|
||||||
|
cv2.putText(overlay, nome, (40, pos_y + 2), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255,255,255), 1, cv2.LINE_AA)
|
||||||
|
|
||||||
|
cv2.imshow("Segmentacao - Overlay", overlay)
|
||||||
|
# Remove: cv2.waitKey() e destroyAllWindows
|
||||||
|
|
||||||
|
|
||||||
|
# === CARREGA MODELO ===
|
||||||
|
import torchvision.models.segmentation as models
|
||||||
|
model = models.deeplabv3_resnet50(weights=None, num_classes=num_classes)
|
||||||
|
model.load_state_dict(torch.load(model_path, map_location=device))
|
||||||
|
model.to(device)
|
||||||
|
|
||||||
|
# === EXECUÇÃO EM TEMPO REAL COM OAK-D ===
|
||||||
|
if USE_CAMERA:
|
||||||
|
print("[📷] Iniciando segmentação em tempo real com a OAK-D Lite...")
|
||||||
|
|
||||||
|
# Setup da câmera
|
||||||
|
pipeline = dai.Pipeline()
|
||||||
|
cam_rgb = pipeline.createColorCamera()
|
||||||
|
cam_rgb.setPreviewSize(640, 640)
|
||||||
|
cam_rgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
|
||||||
|
cam_rgb.setInterleaved(False)
|
||||||
|
cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
|
||||||
|
|
||||||
|
xout = pipeline.createXLinkOut()
|
||||||
|
xout.setStreamName("rgb")
|
||||||
|
cam_rgb.preview.link(xout.input)
|
||||||
|
|
||||||
|
_, cores_bgr, id_para_nome = carregar_labelmap_completo(labelmap_path)
|
||||||
|
|
||||||
|
with dai.Device(pipeline) as oak_device:
|
||||||
|
queue = oak_device.getOutputQueue(name="rgb", maxSize=1, blocking=False)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
in_rgb = queue.get()
|
||||||
|
frame = in_rgb.getCvFrame()
|
||||||
|
frame_resized = cv2.resize(frame, (512, 512))
|
||||||
|
|
||||||
|
# Pré-processa o frame
|
||||||
|
image_pil = Image.fromarray(cv2.cvtColor(frame_resized, cv2.COLOR_BGR2RGB))
|
||||||
|
transform = T.Compose([
|
||||||
|
T.ToTensor(),
|
||||||
|
T.Normalize(mean=[0.485, 0.456, 0.406],
|
||||||
|
std=[0.229, 0.224, 0.225])
|
||||||
|
])
|
||||||
|
input_tensor = transform(image_pil).unsqueeze(0).to(device)
|
||||||
|
|
||||||
|
# Segmentação
|
||||||
|
model.eval()
|
||||||
|
with torch.no_grad():
|
||||||
|
output = model(input_tensor)['out']
|
||||||
|
prediction = torch.argmax(output.squeeze(), dim=0).cpu().numpy()
|
||||||
|
|
||||||
|
# Cria imagem colorida da segmentação
|
||||||
|
seg_color = np.zeros_like(frame_resized)
|
||||||
|
for class_id, color in enumerate(cores_bgr):
|
||||||
|
seg_color[prediction == class_id] = color
|
||||||
|
|
||||||
|
overlay = cv2.addWeighted(frame_resized, 0.5, seg_color, 0.5, 0)
|
||||||
|
|
||||||
|
# Legenda
|
||||||
|
legenda_inicio_y = 20
|
||||||
|
for i, cor in enumerate(cores_bgr):
|
||||||
|
nome = id_para_nome.get(i, f"Classe {i}")
|
||||||
|
pos_y = legenda_inicio_y + i * 30
|
||||||
|
cv2.rectangle(overlay, (10, pos_y - 15), (30, pos_y + 5), cor, -1)
|
||||||
|
cv2.putText(overlay, nome, (40, pos_y + 2), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255,255,255), 1, cv2.LINE_AA)
|
||||||
|
|
||||||
|
# Mostra
|
||||||
|
cv2.imshow("Segmentacao em Tempo Real", overlay)
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
else:
|
||||||
|
# === SEGMENTAÇÃO POR NAVEGAÇÃO ENTRE IMAGENS ===
|
||||||
|
if image_path and os.path.isdir(image_path):
|
||||||
|
print(f"[📁] Navegando imagens em: {image_path}")
|
||||||
|
|
||||||
|
# Lista de imagens
|
||||||
|
extensoes = ("*.jpg", "*.png", "*.jpeg")
|
||||||
|
arquivos = []
|
||||||
|
for ext in extensoes:
|
||||||
|
arquivos.extend(glob.glob(os.path.join(image_path, ext)))
|
||||||
|
arquivos.sort()
|
||||||
|
|
||||||
|
if not arquivos:
|
||||||
|
print("[!] Nenhuma imagem encontrada na pasta de teste.")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
indice = 0
|
||||||
|
while True:
|
||||||
|
caminho_img = arquivos[indice]
|
||||||
|
output_predictions = segment_image(caminho_img, model)
|
||||||
|
display_segmentation(caminho_img, output_predictions)
|
||||||
|
|
||||||
|
print(f"[{indice+1}/{len(arquivos)}] {os.path.basename(caminho_img)}")
|
||||||
|
|
||||||
|
key = cv2.waitKey(0) & 0xFF
|
||||||
|
cv2.destroyAllWindows() # ← fecha imagem ao mudar
|
||||||
|
|
||||||
|
if key == ord('d'):
|
||||||
|
if indice < len(arquivos) - 1:
|
||||||
|
indice += 1
|
||||||
|
else:
|
||||||
|
indice = 0
|
||||||
|
elif key == ord('a'):
|
||||||
|
if indice > 0:
|
||||||
|
indice -= 1
|
||||||
|
else:
|
||||||
|
indice = len(arquivos) - 1
|
||||||
|
elif key == ord('q'):
|
||||||
|
print("[👋] Saindo da visualização.")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print(f"[!] Tecla inválida ({key}). Use A, D ou Q.")
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
absl-py==2.3.0
|
||||||
|
AHRS==0.3.1
|
||||||
|
albucore==0.0.24
|
||||||
|
albumentations==2.0.8
|
||||||
|
annotated-types==0.7.0
|
||||||
|
certifi==2025.6.15
|
||||||
|
charset-normalizer==3.4.2
|
||||||
|
colorama==0.4.6
|
||||||
|
contourpy==1.3.2
|
||||||
|
cycler==0.12.1
|
||||||
|
depthai==2.30.0.0
|
||||||
|
filelock==3.13.1
|
||||||
|
fonttools==4.58.4
|
||||||
|
fsspec==2024.6.1
|
||||||
|
grpcio==1.73.1
|
||||||
|
idna==3.10
|
||||||
|
Jinja2==3.1.4
|
||||||
|
kiwisolver==1.4.8
|
||||||
|
labelImg==1.8.6
|
||||||
|
lxml==6.0.0
|
||||||
|
Markdown==3.8.2
|
||||||
|
MarkupSafe==2.1.5
|
||||||
|
matplotlib==3.10.3
|
||||||
|
mpmath==1.3.0
|
||||||
|
networkx==3.3
|
||||||
|
numpy==2.2.6
|
||||||
|
opencv-python==4.11.0.86
|
||||||
|
opencv-python-headless==4.11.0.86
|
||||||
|
packaging==25.0
|
||||||
|
pandas==2.3.0
|
||||||
|
pillow==11.0.0
|
||||||
|
protobuf==6.31.1
|
||||||
|
pydantic==2.11.7
|
||||||
|
pydantic_core==2.33.2
|
||||||
|
pyparsing==3.2.3
|
||||||
|
PyQt5==5.15.11
|
||||||
|
PyQt5-Qt5==5.15.2
|
||||||
|
PyQt5_sip==12.17.0
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
pytz==2025.2
|
||||||
|
PyYAML==6.0.2
|
||||||
|
requests==2.32.4
|
||||||
|
scipy==1.15.3
|
||||||
|
seaborn==0.13.2
|
||||||
|
simsimd==6.4.9
|
||||||
|
six==1.17.0
|
||||||
|
stringzilla==3.12.5
|
||||||
|
sympy==1.13.3
|
||||||
|
tensorboard==2.19.0
|
||||||
|
tensorboard-data-server==0.7.2
|
||||||
|
torch==2.7.1+cu118
|
||||||
|
torchaudio==2.7.1+cu118
|
||||||
|
torchvision==0.22.1+cu118
|
||||||
|
tqdm==4.67.1
|
||||||
|
typing-inspection==0.4.1
|
||||||
|
typing_extensions==4.14.0
|
||||||
|
tzdata==2025.2
|
||||||
|
urllib3==2.5.0
|
||||||
|
Werkzeug==3.1.3
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import depthai as dai
|
||||||
|
import cv2
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# === CONFIGURAÇÕES ===
|
||||||
|
PASTA_SAIDA = os.path.join("dataset", "oak-d-lite")
|
||||||
|
DIMENSAO_FINAL = (512, 512)
|
||||||
|
QUALIDADE_JPEG = 95 # 0 a 100
|
||||||
|
TECLA_SALVAR = ord('s') # pressione 's' para salvar
|
||||||
|
|
||||||
|
# Cria pasta de saída se não existir
|
||||||
|
os.makedirs(PASTA_SAIDA, exist_ok=True)
|
||||||
|
|
||||||
|
# === PIPELINE OAK ===
|
||||||
|
pipeline = dai.Pipeline()
|
||||||
|
cam_rgb = pipeline.createColorCamera()
|
||||||
|
cam_rgb.setPreviewSize(640, 640)
|
||||||
|
cam_rgb.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
|
||||||
|
cam_rgb.setInterleaved(False)
|
||||||
|
cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
|
||||||
|
|
||||||
|
xout = pipeline.createXLinkOut()
|
||||||
|
xout.setStreamName("rgb")
|
||||||
|
cam_rgb.preview.link(xout.input)
|
||||||
|
|
||||||
|
# === EXECUÇÃO ===
|
||||||
|
with dai.Device(pipeline) as device:
|
||||||
|
print("[INFO] Pressione 's' para salvar imagem, 'q' para sair.")
|
||||||
|
queue = device.getOutputQueue(name="rgb", maxSize=4, blocking=False)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
frame = queue.get().getCvFrame()
|
||||||
|
frame_resized = cv2.resize(frame, DIMENSAO_FINAL, interpolation=cv2.INTER_AREA)
|
||||||
|
|
||||||
|
cv2.imshow("OAK-D RGB", frame_resized)
|
||||||
|
key = cv2.waitKey(1) & 0xFF
|
||||||
|
|
||||||
|
if key == TECLA_SALVAR:
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
caminho = os.path.join(PASTA_SAIDA, f"img_{timestamp}.jpg")
|
||||||
|
cv2.imwrite(caminho, frame_resized, [cv2.IMWRITE_JPEG_QUALITY, QUALIDADE_JPEG])
|
||||||
|
print(f"[✔] Imagem salva: {caminho}")
|
||||||
|
|
||||||
|
elif key == ord('q'):
|
||||||
|
print("[INFO] Encerrando...")
|
||||||
|
break
|
||||||
|
|
||||||
|
cv2.destroyAllWindows()
|
||||||
Loading…
Reference in New Issue