Ajustes operação

This commit is contained in:
Diego Freitas 2025-07-17 08:19:42 -03:00
parent 3ac137920b
commit 486d05eaeb
22 changed files with 1721 additions and 43 deletions

12
.gitignore vendored
View File

@ -47,6 +47,14 @@ Thumbs.db
*.mov
*.wmv
# Ignorar a pasta Operacoes dentro da hierarquia completa
# Ignorar arquivos de build
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/

View File

@ -797,11 +797,19 @@ namespace AgroBase.Models.Modules
return BombaOk;
}
public async Task RealizarCalibragemAsync()
public List<Task> RealizarCalibragemAsync()
{
List<Task> tasks = new List<Task>();
var DispAtu = Variaveis.OperacaoEmAndamento.DispAtu;
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;
}
}

View File

@ -20,10 +20,6 @@ namespace AgroBase.Models.Modules
{
return Modulos.Any(x => x.Conectado);
}
set
{
}
}
public bool DirPronto { get; set; } = false;
public bool MovPronto { get; set; } = false;
@ -398,16 +394,30 @@ namespace AgroBase.Models.Modules
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;
if (DispMvd != null)
{
DispMvd.Dados.MovPronto = DispMvd.Dados.Modulos.All(x => x.MovMotor.Inicializado);
DispMvd.Dados.DirPronto = DispMvd.Dados.Modulos.All(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;
}

View File

@ -20,6 +20,13 @@ namespace AgroBase.Models.Modules
public byte _EnderecoCAN_Tx { get; set; } = 0x11;
public byte _EnderecoCAN_Rx { get; set; } = 0x12;
public int _TaxaAmostragem { get; set; } = 1000;
public double FrequenciaBase
{
get
{
return 1.0 / (_TaxaAmostragem / 1000.0);
}
}
public bool RequisitarDados { get; set; } = true;
public static int PingsConsiderarConexao { get; set; } = 5;
public static int TempoLimiteConexao
@ -1997,8 +2004,8 @@ namespace AgroBase.Models.Modules
private void AtualizaComponentesIniciados()
{
AtualizarStatusComponentes();
return;
//AtualizarStatusComponentes();
//return;
DateTime Agora = DateTime.Now;
bool modConectado = DadosLeitura.UltimoComandoRespondido.AddMilliseconds(TempoLimiteConexao) > Agora;

View File

@ -695,8 +695,8 @@ namespace AgroBase.Models
var DispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
var tasks = new List<Task>();
if (DispAtu?.Dados != null) tasks.Add(DispAtu.Dados.RealizarCalibragemAsync());
if (DispMvd?.Dados != null) tasks.Add(DispMvd.Dados.RealizarCalibragemAsync());
if (DispAtu?.Dados != null) tasks.AddRange(DispAtu.Dados.RealizarCalibragemAsync());
if (DispMvd?.Dados != null) tasks.AddRange(DispMvd.Dados.RealizarCalibragemAsync());
if (tasks.Count == 0)
{

View File

@ -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)>();
foreach (var sensor in Variaveis.OperacaoEmAndamento.DispSen.Dados.Sensores)
foreach (var sensor in DadosSen.Sensores)
{
int tipo = (int)sensor.Componente;
int id = sensor.ID_Num;
@ -361,7 +367,7 @@ namespace AgroBase.Services.Operadores
RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), sensoresAtualizados.ToArray());
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;
string basePath = $"servos.{id}";
@ -394,10 +400,12 @@ namespace AgroBase.Services.Operadores
servosAtualizados.Add(($"{basePath}", servoObj));
}
RedisService.AtualizarCampos(RedisService.ModKey(Enums.T_Code.Sen), servosAtualizados.ToArray());
}
}
}

View File

@ -138,36 +138,43 @@ namespace AgroBase.Services.Operadores
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 dados = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
DateTime timestamp = FuncoesGlobais.UnixToDateTime(dados["ts_analise"].ToString());
//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()
if (dados.ContainsKey("ts_analise"))
{
timestamp = timestamp,
solo = solo,
anomalias = deteccoes,
sombras = sombras,
corredor_prof = profundidade,
corredor_seg = segmentacao,
};
DateTime timestamp = FuncoesGlobais.UnixToDateTime(dados["ts_analise"].ToString());
//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,
solo = solo,
anomalias = deteccoes,
sombras = sombras,
corredor_prof = profundidade,
corredor_seg = segmentacao,
};
}
}
}

View File

@ -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()

View File

@ -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()

View File

@ -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!")

View File

@ -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!")

View File

@ -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 ..

View File

@ -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!")

View File

@ -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

View File

@ -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()

View File

@ -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!")

View File

@ -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.")

View File

@ -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!")

View File

@ -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()

View File

@ -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.")

View File

@ -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

View File

@ -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()