diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/camera_oak.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/camera_oak.cpython-311.pyc index 90fe9a083..2226b50b6 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/camera_oak.cpython-311.pyc and b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/camera_oak.cpython-311.pyc differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/camera_oak.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/camera_oak.py index 863b9f9fa..0f104a1a6 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/camera_oak.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/camera_oak.py @@ -7,9 +7,10 @@ from shared.enums import StatusModulo, T_Code from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey class CameraOak: - def __init__(self, mostrar_log, mx_id, modelo_ia_onboard=None): + def __init__(self, mostrar_log, mx_id, modelo_ia_seg=None, modelo_ia_det=None): self.mostrar_log = mostrar_log - self.modelo_ia_onboard = modelo_ia_onboard + self.modelo_ia_seg = modelo_ia_seg + self.modelo_ia_det = modelo_ia_det disp_list = dai.Device.getAllAvailableDevices() disp_info = next((d for d in disp_list if d.getMxId() == mx_id), None) @@ -81,8 +82,11 @@ class CameraOak: self.q_imu = self.device.getOutputQueue(name="imu", maxSize=50, blocking=False) self.imu = IMUCamera(self.q_imu, freq=100, angulo_inicial=26.3) - if self.modelo_ia_onboard is not None: - self.q_nn = self.device.getOutputQueue(name="nn", maxSize=1, blocking=False) + if self.modelo_ia_seg is not None: + self.q_seg = self.device.getOutputQueue(name="seg", maxSize=1, blocking=False) + + if self.modelo_ia_det is not None: + self.q_det = self.device.getOutputQueue(name="det", maxSize=4, blocking=False) if self.dispositivo == T_Code.Snr: dadosSnr = ContextoGlobalRedis.get_operacao().get("Snr", {}) @@ -195,18 +199,36 @@ class CameraOak: except Exception as e: self.mostrar_log(f"[WARN] Falha ao montar pipeline imu: {e}") - if self.modelo_ia_onboard is not None: + if self.modelo_ia_seg is not None or self.modelo_ia_det is not None: + N_seg = self.modelo_ia_seg["det_every_n"] + N_det = self.modelo_ia_det["det_every_n"] + script = pipeline.create(dai.node.Script) + script.setProcessor(dai.ProcessorType.LEON_CSS) + script.setScript(f""" + from time import monotonic + i = 0 + while True: + f = node.io['in'].get() + if i % {N_seg} == 0: + node.io['toSeg'].send(f) + if i % {N_det} == 0: + node.io['toDet'].send(f) + i += 1 + """) + cam.video.link(script.inputs['in']) + + if self.modelo_ia_seg is not None: try: from shared.utils import carregar_labelmap_completo # Carregar mapa de cores - labelmap_path = self.modelo_ia_onboard["ia_labelmap_path"] - self.cor_para_id, self.colormap_rgb, self.classes, self.ignore_rgb = carregar_labelmap_completo(labelmap_path) + labelmap_path = self.modelo_ia_seg["ia_labelmap_path"] + self.modelo_ia_seg["cor_para_id"], self.modelo_ia_seg["colormap_rgb"], self.modelo_ia_seg["classes"], self.modelo_ia_seg["ignore_rgb"] = carregar_labelmap_completo(labelmap_path) - RESOLUCAO = self.modelo_ia_onboard["ia_resolution"] - ROI_INICIO = self.modelo_ia_onboard["ia_roi_begin"] - ROI_TAMANHO = self.modelo_ia_onboard["ia_roi_size"] - blob_path = self.modelo_ia_onboard["ia_model_path"] + RESOLUCAO = self.modelo_ia_seg["ia_resolution"] + ROI_INICIO = self.modelo_ia_seg["ia_roi_begin"] + ROI_TAMANHO = self.modelo_ia_seg["ia_roi_size"] + blob_path = self.modelo_ia_seg["ia_model_path"] y1 = 1.0 - (ROI_INICIO + ROI_TAMANHO) y2 = 1.0 - ROI_INICIO @@ -216,20 +238,66 @@ class CameraOak: manip.initialConfig.setResize(RESOLUCAO[0], RESOLUCAO[1]) manip.initialConfig.setFrameType(dai.RawImgFrame.Type.RGB888p) manip.initialConfig.setKeepAspectRatio(False) - cam.video.link(manip.inputImage) + #cam.video.link(manip.inputImage) nn = pipeline.createNeuralNetwork() nn.setBlobPath(blob_path) manip.out.link(nn.input) xout_nn = pipeline.createXLinkOut() - xout_nn.setStreamName("nn") + xout_nn.setStreamName("seg") nn.out.link(xout_nn.input) + script.outputs['toSeg'].link(manip.inputImage) + self.mostrar_log("Pipeline de segmentação onboard criado") except Exception as e: self.mostrar_log(f"[WARN] Falha ao montar pipeline IA Onboard: {e}") + if self.modelo_ia_det is not None: + try: + RESOLUCAO = self.modelo_ia_det["ia_resolution"] + ROI_INICIO = self.modelo_ia_det["ia_roi_begin"] + ROI_TAMANHO = self.modelo_ia_det["ia_roi_size"] + CONF = self.modelo_ia_det["ia_conf"] + blob_path = self.modelo_ia_det["ia_model_path"] + + y1 = 1.0 - (ROI_INICIO + ROI_TAMANHO) + y2 = 1.0 - ROI_INICIO + + if blob_path: + manip_det = pipeline.createImageManip() + manip_det.initialConfig.setCropRect(0.0, y1, 1.0, y2) + manip_det.initialConfig.setResize(RESOLUCAO[0], RESOLUCAO[1]) + manip_det.initialConfig.setKeepAspectRatio(True) + manip_det.initialConfig.setFrameType(dai.RawImgFrame.Type.RGB888p) + + det = pipeline.createMobileNetDetectionNetwork() + det.setBlobPath(blob_path) + det.setConfidenceThreshold(CONF) + det.setNumInferenceThreads(2) + det.input.setBlocking(False) + det.input.setQueueSize(2) + manip_det.out.link(det.input) + + xout_det = pipeline.createXLinkOut() + xout_det.setStreamName("det") + det.out.link(xout_det.input) + + script.outputs['toDet'].link(manip_det.inputImage) + + # (opcional) passthrough para sincronizar timestamp/frame com a detecção + # xout_det_img = pipeline.createXLinkOut() + # xout_det_img.setStreamName("det_img") + # det.passthrough.link(xout_det_img.input) + + self.mostrar_log("Pipeline de detecção leve (MobileNet-SSD) criado") + else: + self.mostrar_log("[INFO] Detector não configurado (detector_blob_path ausente). Pulando.") + except Exception as e: + self.mostrar_log(f"[WARN] Falha ao montar pipeline detecção: {e}") + + return pipeline def _set_calib(self): @@ -297,15 +365,15 @@ class CameraOak: } def requisitar_segmentacao(self): - if not hasattr(self, "q_nn"): - return None, {"erro": "Segmentação não disponível", "duracao": 0, "frame_valido": False} + if not hasattr(self, "q_seg"): + return None, {"erro": "Segmentacao nao disponivel", "duracao": 0, "frame_valido": False} start = time.time() try: - w, h = self.modelo_ia_onboard["ia_resolution"] - in_nn = self.q_nn.get() - out_raw = in_nn.getFirstLayerFp16() + w, h = self.modelo_ia_seg["ia_resolution"] + in_seg = self.q_seg.get() + out_raw = in_seg.getFirstLayerFp16() arr16 = np.frombuffer(np.asarray(out_raw, dtype=np.float16), dtype=np.float16) - arr16 = arr16.reshape(len(self.classes), h, w) + arr16 = arr16.reshape(len(self.modelo_ia_seg["classes"]), h, w) pred_ids = arr16.argmax(axis=0).astype(np.uint8, copy=False) dur = time.time() - start self.timestamp_ultima_segmentacao = time.time() @@ -313,6 +381,103 @@ class CameraOak: except Exception as e: dur = time.time() - start return None, {"erro": str(e), "duracao": dur, "frame_valido": False} + + def requisitar_deteccao(self, mapear_para_fullframe: bool = False): + """ + Lê uma predição do detector leve (MobileNet-SSD/YOLO). + Retorna (lista_de_deteccoes, meta). + + Cada detecção: + { + "label_id": int, + "label": str|None, + "conf": float, + "bbox_norm": [x0, y0, x1, y1], # 0..1, relativo ao input do detector (ROI) + "bbox_px": [x0, y0, x1, y1], # em pixels do input do detector (ex.: 300x300) + "bbox_full": [x0, y0, x1, y1], # opcional, só se mapear_para_fullframe=True e houver ROI/size salvos + "xyz_m": [x,y,z] # opcional, se for SpatialDetectionNetwork + } + """ + if not hasattr(self, "q_det"): + return [], {"erro": "Deteccao não disponivel", "duracao": 0, "frame_valido": False} + + # defaults caso você não tenha setado antes + det_W, det_H = self.modelo_ia_det["ia_resolution"] + + def _clamp01(v): + return float(min(1.0, max(0.0, v))) + + def _map_bbox_to_full(bn): + # precisa: self.det_roi_frac = (rx1, ry1, rx2, ry2) em 0..1 no frame FULL + # self.frame_size = (W_full, H_full) + if not (hasattr(self, "det_roi_frac") and hasattr(self, "frame_size")): + return None + rx1, ry1, rx2, ry2 = self.det_roi_frac + Wf, Hf = self.frame_size + sx = (rx2 - rx1) + sy = (ry2 - ry1) + x0n, y0n, x1n, y1n = bn + fx0 = (rx1 + x0n * sx) * Wf + fy0 = (ry1 + y0n * sy) * Hf + fx1 = (rx1 + x1n * sx) * Wf + fy1 = (ry1 + y1n * sy) * Hf + return [int(round(fx0)), int(round(fy0)), int(round(fx1)), int(round(fy1))] + + start = time.time() + try: + pkt = self.q_det.get() # ImgDetections + raw_dets = getattr(pkt, "detections", []) + dets = [] + + for d in raw_dets: + # normalizados 0..1 (clamp por segurança) + x0n = _clamp01(getattr(d, "xmin", 0.0)) + y0n = _clamp01(getattr(d, "ymin", 0.0)) + x1n = _clamp01(getattr(d, "xmax", 0.0)) + y1n = _clamp01(getattr(d, "ymax", 0.0)) + + # em pixels do input do detector (ex.: 300x300) + x0p = int(round(x0n * det_W)); y0p = int(round(y0n * det_H)) + x1p = int(round(x1n * det_W)); y1p = int(round(y1n * det_H)) + + label_id = int(getattr(d, "label", -1)) + conf = float(getattr(d, "confidence", 0.0)) + label = None + labels = self.modelo_ia_det.get("classes", []) + if 0 <= label_id < len(labels): + if label_id != 0: # 0 = background + label = labels[label_id] + else: + label = None + + item = { + "label_id": label_id, + "label": label, + "conf": conf, + "bbox_norm": [x0n, y0n, x1n, y1n], + "bbox_px": [x0p, y0p, x1p, y1p], + } + + # Se for SpatialDetectionNetwork, adiciona XYZ (em metros) + if hasattr(d, "spatialCoordinates"): + sc = d.spatialCoordinates + item["xyz_m"] = [float(sc.x) / 1000.0, float(sc.y) / 1000.0, float(sc.z) / 1000.0] + + # Opcional: mapear para o frame completo (leva em conta ROI da detecção) + if mapear_para_fullframe: + bf = _map_bbox_to_full(item["bbox_norm"]) + if bf is not None: + item["bbox_full"] = bf + + dets.append(item) + + dur = time.time() - start + self.timestamp_ultima_deteccao = time.time() + return dets, {"erro": None, "duracao": dur, "frame_valido": True} + + except Exception as e: + dur = time.time() - start + return [], {"erro": str(e), "duracao": dur, "frame_valido": False} def atualizar_saude(self): #self.mostrar_log(f"[{self.mx_id}] Atualizando saude {self.dispositivo.name}...") diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/camera_manager.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/camera_manager.cpython-311.pyc index f8111fc89..296fbd169 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/camera_manager.cpython-311.pyc and b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/camera_manager.cpython-311.pyc differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/config.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/config.cpython-311.pyc index 9ce2ba711..112f93d3b 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/config.cpython-311.pyc and b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/config.cpython-311.pyc differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/camera_manager.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/camera_manager.py index 03796397d..e296c016f 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/camera_manager.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/camera_manager.py @@ -33,11 +33,13 @@ class CameraManager: self._ultima_analise_solo = {} self._ultima_analise_radar = {} self._ultima_analise_segmentacao = {} + self._ultima_analise_deteccao = {} self._ultima_analise_matriz_confianca = {} self._ultima_analise_matriz_custo = {} self._ultimo_rgb_frame = None self._ultimo_depth_frame = None self._ts_segmentacao_anterior = 0 + self._ts_deteccao_anterior = 0 self._pool = ThreadPoolExecutor(max_workers=6) def inicializar(self, mx_id): @@ -56,11 +58,12 @@ class CameraManager: self.mx_id = mx_id - from visual_worker.config import load_config - camera_config = load_config() + from visual_worker.config import load_seg_config, load_det_config + seg_config = load_seg_config() + det_config = load_det_config() try: - nova = CameraOak(self.mostrar_log, mx_id, modelo_ia_onboard=camera_config) + nova = CameraOak(self.mostrar_log, mx_id, modelo_ia_seg=seg_config, modelo_ia_det=det_config) if nova.iniciado: self.camera = nova except Exception as e: @@ -78,7 +81,7 @@ class CameraManager: self.setores_referencia = None self.anomalias_manager = AnaliseAnomaliasManager() self.solo_manager = AnaliseSoloManager() - self.segmentacao_manager = SegmentacaoManager(self.camera.colormap_rgb, self.camera.classes) + self.segmentacao_manager = SegmentacaoManager(self.camera.modelo_ia_seg.get("colormap_rgb"), self.camera.modelo_ia_seg.get("classes")) self.radar_manager = Radar2DManager() self.data_fuser = CostmapFuser(grid_shape=self.grid_ref_shape, K=3, M=2, fuse_method="q0.7", block_thr=0.7, central_cols=None, y_range_m=(0.5,5.0), near_is_bottom=True, fov_h_rad=np.radians(self.camera.parametros["fov_h"]), robot_width=self.largura_robo_m) self.operante = True @@ -88,12 +91,16 @@ class CameraManager: self._ultima_analise_solo = {} self._ultima_analise_radar = {} self._ultima_analise_segmentacao = {} + self._ultima_analise_deteccao = {} self._ultima_analise_matriz_confianca = {} self._ultima_analise_matriz_custo = {} + self._ts_segmentacao_anterior = 0 + self._ts_deteccao_anterior = 0 self._ultimo_rgb_frame = None self._depth_frame_necessario = True self._rgb_frame_necessario = True self._nova_segmentacao_disponivel = False + self._nova_deteccao_disponivel = False self._nova_grid_conf_disponivel = False self._analisando_anomalias = False @@ -102,6 +109,7 @@ class CameraManager: self._analisando_matriz_custo = False self._analisando_matriz_confianca = False self._analisando_segmentacao = False + self._analisando_deteccao = False self._iniciar_loop_analise_continua(15.0) self.iniciando = False @@ -178,7 +186,6 @@ class CameraManager: try: predictions, res = self.camera.requisitar_segmentacao() if predictions is not None: - self._ultimo_predictions = predictions return predictions, self.camera.timestamp_ultima_segmentacao, res elif "X_LINK_ERROR" in res["erro"]: self.reiniciar_status() @@ -189,6 +196,23 @@ class CameraManager: return None, None, None + def get_detections(self): + if self.camera is None: + return None, None, None + + try: + detections, res = self.camera.requisitar_deteccao() + if detections is not None: + return detections, self.camera.timestamp_ultima_deteccao, res + elif "X_LINK_ERROR" in res["erro"]: + self.reiniciar_status() + except Exception as e: + self.mostrar_log("Erro ao requisitar detections:", e) + if "X_LINK_ERROR" in str(e): + self.reiniciar_status() + + return None, None, None + def get_select_frame(self, tipo: CameraFrameType): f = None t = None @@ -273,18 +297,18 @@ class CameraManager: def _realizar_analises(self): + self._analise_segmentacao() + if self._depth_frame_necessario: depth_frame_np, depth_timestamp, depth_res = self.get_depth_frame() else: depth_frame_np = self._ultimo_depth_frame - self._analise_segmentacao() parametros_camera = self.camera.parametros fov_h = parametros_camera["fov_h"] distancia_max_m = parametros_camera["distancia_maxima"] / 1000.0 self._analise_matriz_confianca(depth_frame_np, distancia_max_m, fov_h) - #key, vis = self.debug_show_costmap(rgb_frame=self._ultimo_rgb_frame, grid_dict=self._ultima_analise_matriz_confianca, grid_shape=self.grid_ref_shape, window_name="viz MPC", wait=1, text_mode="mini") - #key, vis = self.debug_show_visualworker(frame_bgr=self._ultimo_rgb_frame, grid=self._ultima_analise_matriz_confianca, wait=1, text_mode="full", draw_grid=True, draw_cells=True, draw_legend=True) - self.segmentacao_manager.display_segmentation_debug(self._ultimo_rgb_frame, 150) + + self._analise_deteccao() def _realizar_analises_async(self): executor = self._pool @@ -351,6 +375,12 @@ class CameraManager: ) self._nova_segmentacao_disponivel = True + from visual_worker.config import load_seg_config + if load_seg_config().get("debug_visual", False): + #key, vis = self.debug_show_costmap(rgb_frame=self._ultimo_rgb_frame, grid_dict=self._ultima_analise_matriz_confianca, grid_shape=self.grid_ref_shape, window_name="viz MPC", wait=1, text_mode="mini") + #key, vis = self.debug_show_visualworker(frame_bgr=self._ultimo_rgb_frame, grid=self._ultima_analise_matriz_confianca, wait=1, text_mode="full", draw_grid=True, draw_cells=True, draw_legend=True) + self.segmentacao_manager.display_segmentation_debug(self._ultimo_rgb_frame, 150) + # Enviar comando para atualizar os dados de controle sempre que um novo dado de segmentacao seja processado e a operacao seja do tipo MapeamentoVisual #op_modo = ContextoGlobalRedis.get_operacao().get("modo", ModoOperacao.NaoDefinido.value) #movimento_automatico = ContextoGlobalRedis.get_controle().get("movimento_automatico", False) @@ -375,7 +405,7 @@ class CameraManager: t0 = time.time() #grid_conf = self._gerar_grid_confianca(depth_frame_np, segmentacao, dist_max) - grid_conf = self._construir_grid_confianca(depth_frame_np, segmentacao, self.grid_ref, self.grid_ref_shape, self.camera.classes) + grid_conf = self._construir_grid_confianca(depth_frame_np, segmentacao, self.grid_ref, self.grid_ref_shape, self.camera.modelo_ia_seg.get("classes")) #self.mostrar_log(grid_conf) t1 = time.time() grid_conf["ultima_chamada"] = self._ultima_analise_matriz_confianca.get("ultima_chamada", t0) @@ -401,6 +431,38 @@ class CameraManager: self._analisando_matriz_confianca = False #self.mostrar_log("Matriz de confianca concluida") + def _analise_deteccao(self): + if self._analisando_deteccao or self.camera.modelo_ia_det is None: return + self._analisando_deteccao = True + try: + t0 = time.time() + dets, ts, meta = self.get_detections() + if ts == self._ts_deteccao_anterior: return + self._ts_deteccao_anterior = ts + if dets is not None: + t1 = time.time() + analise_deteccoes = { + "bboxes": dets + } + self._calcular_performance(t0, t1, analise_deteccoes) + self._ultima_analise_deteccao = analise_deteccoes + ContextoGlobalRedis.atualizar_ctx_dict( + CtxKey.DadosVisualWorker, + ts_analise=t1, + deteccao=converter_valores_numpy(dets) + ) + self._nova_deteccao_disponivel = True + + from visual_worker.config import load_det_config + if load_det_config().get("debug_visual", False): + self._overlay_deteccoes(self._ultimo_rgb_frame, dets) + + except Exception as e: + self.mostrar_log(f"❌ Erro na deteccao de objetos: {e}") + finally: + self._analisando_deteccao = False + #self.mostrar_log(f"Deteccao concluida em {self._ultima_analise_deteccao['latencia']:.4f} s, a {fps:.4f} FPS") + def _analise_anomalias(self, grid_conf, limiar_delta, limiar_conf, dist_max, largura_min, altura_min): if self._analisando_anomalias: return @@ -1399,4 +1461,122 @@ class CameraManager: cv2.imshow(win_name, vis) cv2.waitKey(1) return vis, metrics + + + def _overlay_deteccoes( + self, + rgb_frame, + dets, + conf_thr=0.5, # limiar de confiança pra desenhar + roi_frac=None, # (rx1,ry1,rx2,ry2) normalizado da ROI usada no detector (ex.: (0.0,y1,1.0,y2)) + show=True, # se True, faz cv2.imshow + janela="det", # nome da janela + fps_state=None, # dict estado do FPS (persistido fora), ex.: {} + ): + """ + dets: lista de dicts no formato: + { + "label_id": int, + "label": str|None, + "conf": float, + "bbox_norm": [x0,y0,x1,y1] # 0..1 relativo ao input do detector (na ROI) + # opcional: "bbox_full": [x0,y0,x1,y1] em px do frame completo + } + Retorna: (frame_com_overlay, fps_state, keep_loop_bool) + """ + img = cv2.resize(rgb_frame.copy(), (1280, 720)) + H, W = img.shape[:2] + + # paleta simples por classe + palette = [ + (255, 56, 56), (255, 157, 151), (72, 249, 10), (0, 255, 0), (0, 0, 255), + (255, 0, 255), (0, 255, 255), (255, 191, 0), (52, 148, 230), (147, 112, 219) + ] + + def _map_bbox_norm_to_full(bn): + # bn é [x0n,y0n,x1n,y1n] relativo ao input da ROI (0..1) + x0n, y0n, x1n, y1n = bn + if roi_frac is not None: + rx1, ry1, rx2, ry2 = roi_frac + sx, sy = (rx2 - rx1), (ry2 - ry1) + x0 = int(round((rx1 + x0n * sx) * W)) + y0 = int(round((ry1 + y0n * sy) * H)) + x1 = int(round((rx1 + x1n * sx) * W)) + y1 = int(round((ry1 + y1n * sy) * H)) + else: + x0 = int(round(x0n * W)) + y0 = int(round(y0n * H)) + x1 = int(round(x1n * W)) + y1 = int(round(y1n * H)) + # clamp + x0 = max(0, min(W - 1, x0)); x1 = max(0, min(W - 1, x1)) + y0 = max(0, min(H - 1, y0)); y1 = max(0, min(H - 1, y1)) + return x0, y0, x1, y1 + + def _put_label(img, text, x, y, bg): + (tw, th), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1) + cv2.rectangle(img, (x, max(0, y - th - 6)), (x + tw + 6, y), bg, -1) + cv2.putText(img, text, (x + 3, y - 4), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1, cv2.LINE_AA) + + # desenhar ROI (opcional, ajuda debug) + if roi_frac is not None: + rx1, ry1, rx2, ry2 = roi_frac + x0r, y0r = int(rx1 * W), int(ry1 * H) + x1r, y1r = int(rx2 * W), int(ry2 * H) + cv2.rectangle(img, (x0r, y0r), (x1r, y1r), (60, 60, 60), 1) + + # desenhar detecções + for d in dets: + if d.get("conf", 0.0) < conf_thr: + continue + + # bbox em px do frame + if "bbox_full" in d and d["bbox_full"]: + x0, y0, x1, y1 = d["bbox_full"] + # clamp se necessário + x0 = max(0, min(W - 1, int(x0))); x1 = max(0, min(W - 1, int(x1))) + y0 = max(0, min(H - 1, int(y0))); y1 = max(0, min(H - 1, int(y1))) + else: + bn = d.get("bbox_norm", None) + if not bn: + continue + x0, y0, x1, y1 = _map_bbox_norm_to_full(bn) + + if x1 <= x0 or y1 <= y0: + continue + + lid = int(d.get("label_id", -1)) + color = palette[lid % len(palette)] if lid >= 0 else (0, 255, 0) + + cv2.rectangle(img, (x0, y0), (x1, y1), color, 2) + + name = d.get("label", None) + txt = f"{name or f'id:{lid}'} {d.get('conf', 0.0):.2f}" + _put_label(img, txt, x0, y0, color) + + # FPS (EMA) + now = time.monotonic() + if fps_state is None: + fps_state = {} + t_prev = fps_state.get("t_prev") + fps_ema = fps_state.get("fps_ema") + if t_prev is not None: + dt = now - t_prev + if dt > 0: + fps_inst = 1.0 / dt + alpha = 0.90 + fps_ema = fps_inst if fps_ema is None else (alpha * fps_ema + (1 - alpha) * fps_inst) + fps_state["t_prev"] = now + fps_state["fps_ema"] = fps_ema + + if fps_ema: + cv2.putText(img, f"FPS: {fps_ema:.1f}", (10, 22), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (50, 220, 50), 2, cv2.LINE_AA) + + keep = True + if show: + cv2.imshow(janela, img) + k = cv2.waitKey(1) & 0xFF + keep = (k != 27) # ESC para sair + + return img, fps_state, keep \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/config.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/config.py index 993d80c7b..5bd3ee81b 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/config.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/config.py @@ -37,7 +37,7 @@ _CONFIG_CACHE = None _CONFIG_MTIME = None _CONFIG_LOCK = threading.Lock() -def load_config(force_reload=False): +def load_seg_config(force_reload=False): global _CONFIG_CACHE, _CONFIG_MTIME with _CONFIG_LOCK: #try: @@ -72,15 +72,36 @@ def load_config(force_reload=False): # "kernel_morf": 3 # } _CONFIG_CACHE = { - "debug_visual": True, + "debug_visual": False, "ia_roi_begin": 0.0, "ia_roi_size": 1.0, - "ia_resolution": [512,288] + "ia_resolution": [512,288], + "det_every_n": 1, } _CONFIG_CACHE["ia_model_path"] = ContextoGlobalRedis.get_equipamento().get("path_ia_model_ruas", "C:/AgroBaseModels/Ruas/model-1_1.blob") _CONFIG_CACHE["ia_labelmap_path"] = ContextoGlobalRedis.get_equipamento().get("path_ia_labelmap_ruas", "C:/AgroBaseModels/Ruas/model-1_1.txt") return _CONFIG_CACHE -def reload_config(): - return load_config(force_reload=True) +def reload_seg_config(): + return load_seg_config(force_reload=True) + +def load_det_config(): + _CONFIG_DET = { + "debug_visual": False, + "ia_roi_begin": 0.0, + "ia_roi_size": 1.0, + "ia_resolution": [300,300], + "det_every_n": 3, + "ia_conf": 0.5, + "ia_model_path": "C:\\AgroBaseModels\\Ruas\\det_3.blob", + "classes": [ + "background", + "aeroplane","bicycle","bird","boat","bottle", + "bus","car","cat","chair","cow", + "diningtable","dog","horse","motorbike","person", + "pottedplant","sheep","sofa","train","tvmonitor", + ] + } + return _CONFIG_DET + diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/segmentacao_semantica.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/segmentacao_semantica.cpython-311.pyc index 42d47b665..30a638402 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/segmentacao_semantica.cpython-311.pyc and b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/segmentacao_semantica.cpython-311.pyc differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/segmentacao_semantica.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/segmentacao_semantica.py index 11bee7be4..3a9ae7253 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/segmentacao_semantica.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/segmentacao_semantica.py @@ -15,8 +15,8 @@ class ClassesSegmentacao(IntEnum): class SegmentacaoManager: def __init__(self, color_map, classes): - from visual_worker.config import load_config - config = load_config() + from visual_worker.config import load_seg_config + config = load_seg_config() resolucao = config.get("ia_resolution") self.color_map = color_map self.classes = classes diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/camera_manager.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/camera_manager.cpython-311.pyc index 51f03c889..e2e502669 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/camera_manager.cpython-311.pyc and b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/camera_manager.cpython-311.pyc differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/config.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/config.cpython-311.pyc index 1eae5a0c7..49e567b88 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/config.cpython-311.pyc and b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/config.cpython-311.pyc differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/weed_detector.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/weed_detector.cpython-311.pyc index d76e4a0cd..63d4f8f50 100644 Binary files a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/weed_detector.cpython-311.pyc and b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/weed_detector.cpython-311.pyc differ diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/camera_manager.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/camera_manager.py index b6a9efd23..aef139f7f 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/camera_manager.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/camera_manager.py @@ -42,8 +42,8 @@ class CameraManager: self.mx_id = mx_id - from weed_worker.config import load_config - camera_config = load_config() + from weed_worker.config import load_seg_config + camera_config = load_seg_config() try: nova = CameraOak(self.mostrar_log, mx_id, modelo_ia_onboard=camera_config) @@ -62,7 +62,7 @@ class CameraManager: self._ultimo_rgb_frame = None self._ultimo_predictions = None - self.weed_detector = WeedDetector(self.camera.colormap_rgb, self.camera.classes) + self.weed_detector = WeedDetector(self.camera.modelo_ia_seg.get("colormap_rgb"), self.camera.modelo_ia_seg.get("classes")) self._iniciar_loop_analise_continua(20.0) self.iniciando = False diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py index 7e57e9faa..5d25fc37c 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py @@ -39,7 +39,7 @@ _CONFIG_CACHE = None _CONFIG_MTIME = None _CONFIG_LOCK = threading.Lock() -def load_config(force_reload=False): +def load_seg_config(force_reload=False): global _CONFIG_CACHE, _CONFIG_MTIME with _CONFIG_LOCK: #try: @@ -104,5 +104,5 @@ def load_config(force_reload=False): _CONFIG_CACHE["faixa_atuacao_bicos"] = dadosAtu.get("percent_vertical_deteccao", 0.3) return _CONFIG_CACHE -def reload_config(): - return load_config(force_reload=True) +def reload_seg_config(): + return load_seg_config(force_reload=True) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py index 0f1629b69..9c587515c 100644 --- a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py @@ -12,8 +12,8 @@ class ClassesSegmentacao(IntEnum): class WeedDetector: def __init__(self, color_map, classes): - from weed_worker.config import load_config - config = load_config() + from weed_worker.config import load_seg_config + config = load_seg_config() resolucao = config.get("ia_resolution") self.color_map = color_map self.classes = classes @@ -40,8 +40,8 @@ class WeedDetector: self._seg_fps_ema = None # opcional: chame quando terminar a segmentação def _reiniciar_deteccoes(self): - from weed_worker.config import load_config - config = load_config() + from weed_worker.config import load_seg_config + config = load_seg_config() qtd_bicos = config.get("qtd_bicos") self.ervas_ativas_radar = [] self.ervas_ativas_filtradas = [] @@ -99,8 +99,8 @@ class WeedDetector: print("[Erro] Máscara de classes não encontrada no resultado") return None - from weed_worker.config import load_config - config = load_config() + from weed_worker.config import load_seg_config + config = load_seg_config() # vel_norm pode vir do contexto (0..1 da sua Vmax). Se não tiver, manda 0.0 vel_norm = float(config.get("velocidade_robo", 0.0)) diff --git a/Python/OAK/datasets/_7_convert_fastscnn.py b/Python/OAK/datasets/_7_convert_fastscnn.py index d36a8c874..c92aef692 100644 --- a/Python/OAK/datasets/_7_convert_fastscnn.py +++ b/Python/OAK/datasets/_7_convert_fastscnn.py @@ -11,6 +11,7 @@ MODELO = config["camera"] MODEL_NAME = config["model_name"] RESOLUCAO = config["resolucao"] MAIN_CLASS_NAME = config["main_class_name"] +N_SHAVES = config["shaves"] use_main_class = config["use_main_class"] model_path = os.path.join(MODELO, "backup", config["modelo"], MODEL_NAME) labelmap_path = os.path.join(MODELO, "dataset", "labelmap.txt") @@ -55,7 +56,7 @@ blob_path = blobconverter.from_openvino( xml=os.path.join(model_path, model_name + ".xml"), bin=os.path.join(model_path, model_name + ".bin"), data_type="FP16", - shaves=6, + shaves=N_SHAVES, output_dir=model_path, #compile_params=[ # "-ip U8", # entrada em bytes; compila a conversão interna p/ FP16 diff --git a/Python/OAK/datasets/config.json b/Python/OAK/datasets/config.json index bcbb8da7b..d629f3fc6 100644 --- a/Python/OAK/datasets/config.json +++ b/Python/OAK/datasets/config.json @@ -6,5 +6,6 @@ "use_main_class": false, "resolucao": [512, 288], "roi_inicio": 0.0, - "roi_tamanho": 1.0 + "roi_tamanho": 1.0, + "shaves": 3 } \ No newline at end of file