ajuste no uso de cpu a 100% no weed worker
This commit is contained in:
parent
afc91db881
commit
632f672ce1
|
|
@ -237,6 +237,40 @@ class RawProcessorCore:
|
|||
self.rgb_processing_config = {
|
||||
"mode": "linear_demosaic", # "linear_demosaic", "linear_demosaic_half" ou "bayer_planes"
|
||||
"demosaic_algorithm": "ea", # "ea" ou "bilinear"
|
||||
|
||||
# Pós-processamento RGB opcional, aplicado logo após o decode/debayer
|
||||
# e antes da fusão com RE/NIR.
|
||||
#
|
||||
# backend aceitos:
|
||||
# "none" / None / false
|
||||
# "hybrid" / "hybrid:balanced" / "hybrid_balanced"
|
||||
# "hybrid_soft", "hybrid_strong"
|
||||
#
|
||||
# Observação:
|
||||
# - Por padrão fica desligado para manter compatibilidade total.
|
||||
# - Para o experimento atual, use backend="hybrid" e preset="balanced".
|
||||
"enhancement": {
|
||||
"enabled": False,
|
||||
"backend": "none",
|
||||
"preset": "balanced", # "soft", "balanced", "strong"
|
||||
"apply_to_preview_input": False,
|
||||
|
||||
# Mantém o pipeline parecido com o script de preview/regens:
|
||||
# float01 -> uint8 -> OpenCV -> float01.
|
||||
"use_u8_pipeline": True,
|
||||
|
||||
# Desligado por padrão para preservar escala radiométrica.
|
||||
# Se quiser reproduzir exatamente o visual do script de previews,
|
||||
# pode ligar este auto_stretch.
|
||||
"auto_stretch": {
|
||||
"enabled": False,
|
||||
"low_pct": 0.2,
|
||||
"high_pct": 99.8
|
||||
},
|
||||
|
||||
"clip_output": True,
|
||||
"save_debug": True
|
||||
}
|
||||
}
|
||||
|
||||
self.fusion_config = {
|
||||
|
|
@ -389,6 +423,7 @@ class RawProcessorCore:
|
|||
|
||||
self.last_decode_perf = {}
|
||||
self._last_decode_perf_log_ts = 0.0
|
||||
self.last_rgb_enhancement_result = None
|
||||
|
||||
if calibration_json_path:
|
||||
self.load_config_json(calibration_json_path)
|
||||
|
|
@ -553,6 +588,301 @@ class RawProcessorCore:
|
|||
|
||||
return rgb[:, :, 0], rgb[:, :, 1], rgb[:, :, 2]
|
||||
|
||||
|
||||
# ============================================================
|
||||
# RGB ENHANCEMENT / REGEN BACKENDS
|
||||
# ============================================================
|
||||
|
||||
def _get_rgb_enhancement_config(self) -> dict:
|
||||
"""
|
||||
Resolve a configuração de pós-processamento RGB dentro de rgb_processing.
|
||||
|
||||
Contrato recomendado no module_params.json:
|
||||
|
||||
"rgb_processing": {
|
||||
"mode": "bayer_planes",
|
||||
"demosaic_algorithm": "ea",
|
||||
"enhancement": {
|
||||
"enabled": true,
|
||||
"backend": "hybrid",
|
||||
"preset": "balanced",
|
||||
"use_u8_pipeline": true,
|
||||
"auto_stretch": {
|
||||
"enabled": false,
|
||||
"low_pct": 0.2,
|
||||
"high_pct": 99.8
|
||||
},
|
||||
"clip_output": true
|
||||
}
|
||||
}
|
||||
|
||||
Compatibilidade:
|
||||
- também aceita rgb_processing.enhancement_backend = "hybrid:balanced"
|
||||
- também aceita rgb_processing.enhancement_preset = "balanced"
|
||||
"""
|
||||
rgb_cfg = getattr(self, "rgb_processing_config", {}) or {}
|
||||
enh = rgb_cfg.get("enhancement", {}) or {}
|
||||
|
||||
if not isinstance(enh, dict):
|
||||
enh = {}
|
||||
|
||||
# Atalhos opcionais no nível de rgb_processing.
|
||||
if "enhancement_backend" in rgb_cfg and "backend" not in enh:
|
||||
enh["backend"] = rgb_cfg.get("enhancement_backend")
|
||||
if "enhancement_preset" in rgb_cfg and "preset" not in enh:
|
||||
enh["preset"] = rgb_cfg.get("enhancement_preset")
|
||||
if "enhancement_enabled" in rgb_cfg and "enabled" not in enh:
|
||||
enh["enabled"] = bool(rgb_cfg.get("enhancement_enabled"))
|
||||
|
||||
backend = enh.get("backend", "none")
|
||||
backend_norm, preset_norm = self._resolve_rgb_enhancement_backend_and_preset(
|
||||
backend=backend,
|
||||
preset=enh.get("preset", "balanced"),
|
||||
)
|
||||
|
||||
enabled = bool(enh.get("enabled", False))
|
||||
if backend_norm in ("none", "", "off", "disabled"):
|
||||
enabled = False
|
||||
|
||||
auto_stretch = enh.get("auto_stretch", {}) or {}
|
||||
if not isinstance(auto_stretch, dict):
|
||||
auto_stretch = {}
|
||||
|
||||
return {
|
||||
"enabled": enabled,
|
||||
"backend": backend_norm,
|
||||
"preset": preset_norm,
|
||||
"apply_to_preview_input": bool(enh.get("apply_to_preview_input", False)),
|
||||
"use_u8_pipeline": bool(enh.get("use_u8_pipeline", True)),
|
||||
"auto_stretch": {
|
||||
"enabled": bool(auto_stretch.get("enabled", False)),
|
||||
"low_pct": float(auto_stretch.get("low_pct", 0.2)),
|
||||
"high_pct": float(auto_stretch.get("high_pct", 99.8)),
|
||||
},
|
||||
"clip_output": bool(enh.get("clip_output", True)),
|
||||
"save_debug": bool(enh.get("save_debug", True)),
|
||||
}
|
||||
|
||||
def _resolve_rgb_enhancement_backend_and_preset(self, backend, preset="balanced") -> tuple[str, str]:
|
||||
"""
|
||||
Aceita strings amigáveis:
|
||||
none
|
||||
hybrid
|
||||
hybrid:balanced
|
||||
hybrid_balanced
|
||||
hybrid-soft
|
||||
"""
|
||||
if backend is None or backend is False:
|
||||
return "none", "balanced"
|
||||
|
||||
b = str(backend).strip().lower()
|
||||
p = str(preset or "balanced").strip().lower()
|
||||
|
||||
if b in ("", "none", "off", "false", "disabled", "raw"):
|
||||
return "none", "balanced"
|
||||
|
||||
# hybrid:balanced
|
||||
if ":" in b:
|
||||
parts = [x.strip() for x in b.split(":") if x.strip()]
|
||||
if len(parts) >= 1:
|
||||
b = parts[0]
|
||||
if len(parts) >= 2:
|
||||
p = parts[1]
|
||||
|
||||
# hybrid_balanced / hybrid-balanced
|
||||
for sep in ("_", "-"):
|
||||
if b.startswith(f"hybrid{sep}"):
|
||||
p = b.split(sep, 1)[1]
|
||||
b = "hybrid"
|
||||
|
||||
if p not in ("soft", "balanced", "strong"):
|
||||
p = "balanced"
|
||||
|
||||
if b not in ("hybrid",):
|
||||
raise ValueError(
|
||||
f"rgb_processing.enhancement.backend inválido: {backend}. "
|
||||
"Use 'none' ou 'hybrid'."
|
||||
)
|
||||
|
||||
return b, p
|
||||
|
||||
def _rgb_float01_to_u8_for_enhancement(self, rgb: np.ndarray, cfg: dict) -> np.ndarray:
|
||||
"""
|
||||
Converte RGB float01 para uint8.
|
||||
Opcionalmente aplica auto_stretch por percentil para reproduzir melhor
|
||||
o visual dos scripts de preview/regens.
|
||||
|
||||
Por padrão auto_stretch fica desligado, porque preservar a escala do tensor
|
||||
tende a ser mais seguro para treino/inferência.
|
||||
"""
|
||||
x = np.asarray(rgb, dtype=np.float32)
|
||||
|
||||
auto = (cfg or {}).get("auto_stretch", {}) or {}
|
||||
if bool(auto.get("enabled", False)):
|
||||
low_pct = float(auto.get("low_pct", 0.2))
|
||||
high_pct = float(auto.get("high_pct", 99.8))
|
||||
|
||||
lo = np.percentile(x, low_pct)
|
||||
hi = np.percentile(x, high_pct)
|
||||
|
||||
if hi <= lo + 1e-6:
|
||||
lo = float(np.min(x))
|
||||
hi = float(np.max(x))
|
||||
|
||||
x = (x - float(lo)) / max(float(hi - lo), 1e-6)
|
||||
|
||||
x = np.clip(x, 0.0, 1.0)
|
||||
return np.clip(x * 255.0 + 0.5, 0, 255).astype(np.uint8)
|
||||
|
||||
def _gray_world_wb_u8(self, rgb_u8: np.ndarray, strength: float = 0.55) -> np.ndarray:
|
||||
img = rgb_u8.astype(np.float32)
|
||||
means = img.reshape(-1, 3).mean(axis=0)
|
||||
target = float(means.mean())
|
||||
gains = target / np.maximum(means, 1e-6)
|
||||
gains = np.clip(gains, 0.60, 1.70)
|
||||
gains = 1.0 + (gains - 1.0) * float(strength)
|
||||
return np.clip(img * gains[None, None, :], 0, 255).astype(np.uint8)
|
||||
|
||||
def _apply_gamma_u8(self, rgb_u8: np.ndarray, gamma: float = 0.94) -> np.ndarray:
|
||||
x = rgb_u8.astype(np.float32) / 255.0
|
||||
y = np.power(np.clip(x, 0.0, 1.0), float(gamma))
|
||||
return np.clip(y * 255.0 + 0.5, 0, 255).astype(np.uint8)
|
||||
|
||||
def _clahe_luminance_u8(self, rgb_u8: np.ndarray, clip_limit: float = 1.7, tile_grid_size: int = 8) -> np.ndarray:
|
||||
lab = cv2.cvtColor(rgb_u8, cv2.COLOR_RGB2LAB)
|
||||
l, a, b = cv2.split(lab)
|
||||
clahe = cv2.createCLAHE(
|
||||
clipLimit=float(clip_limit),
|
||||
tileGridSize=(int(tile_grid_size), int(tile_grid_size)),
|
||||
)
|
||||
l2 = clahe.apply(l)
|
||||
return cv2.cvtColor(cv2.merge([l2, a, b]), cv2.COLOR_LAB2RGB)
|
||||
|
||||
def _denoise_fast_u8(self, rgb_u8: np.ndarray, strength: float = 3.0) -> np.ndarray:
|
||||
bgr = cv2.cvtColor(rgb_u8, cv2.COLOR_RGB2BGR)
|
||||
out = cv2.bilateralFilter(
|
||||
bgr,
|
||||
d=5,
|
||||
sigmaColor=float(strength) * 12.0,
|
||||
sigmaSpace=3.0,
|
||||
)
|
||||
return cv2.cvtColor(out, cv2.COLOR_BGR2RGB)
|
||||
|
||||
def _unsharp_u8(self, rgb_u8: np.ndarray, sigma: float = 0.9, amount: float = 0.85, threshold: int = 2) -> np.ndarray:
|
||||
img = rgb_u8.astype(np.float32)
|
||||
blur = cv2.GaussianBlur(img, (0, 0), sigmaX=float(sigma), sigmaY=float(sigma))
|
||||
sharp = img + float(amount) * (img - blur)
|
||||
|
||||
if int(threshold) > 0:
|
||||
diff = np.max(np.abs(img - blur), axis=2)
|
||||
mask = diff >= int(threshold)
|
||||
out = img.copy()
|
||||
out[mask] = sharp[mask]
|
||||
else:
|
||||
out = sharp
|
||||
|
||||
return np.clip(out, 0, 255).astype(np.uint8)
|
||||
|
||||
def _local_contrast_u8(self, rgb_u8: np.ndarray, sigma: float = 9.0, amount: float = 0.14) -> np.ndarray:
|
||||
img = rgb_u8.astype(np.float32)
|
||||
blur = cv2.GaussianBlur(img, (0, 0), sigmaX=float(sigma), sigmaY=float(sigma))
|
||||
return np.clip(img + float(amount) * (img - blur), 0, 255).astype(np.uint8)
|
||||
|
||||
def _hybrid_enhance_u8(self, rgb_u8: np.ndarray, preset: str = "balanced") -> np.ndarray:
|
||||
preset = str(preset or "balanced").lower()
|
||||
|
||||
if preset == "soft":
|
||||
wb, gamma, clahe, den, lc, us = 0.35, 0.96, 1.35, 1.8, 0.08, 0.55
|
||||
elif preset == "strong":
|
||||
wb, gamma, clahe, den, lc, us = 0.65, 0.90, 2.25, 3.2, 0.22, 1.25
|
||||
else:
|
||||
wb, gamma, clahe, den, lc, us = 0.50, 0.94, 1.75, 2.5, 0.14, 0.85
|
||||
|
||||
out = self._gray_world_wb_u8(rgb_u8, strength=wb)
|
||||
out = self._apply_gamma_u8(out, gamma=gamma)
|
||||
out = self._clahe_luminance_u8(out, clip_limit=clahe, tile_grid_size=8)
|
||||
out = self._denoise_fast_u8(out, strength=den)
|
||||
out = self._local_contrast_u8(out, sigma=9.0, amount=lc)
|
||||
out = self._unsharp_u8(out, sigma=0.85, amount=us, threshold=2)
|
||||
return out
|
||||
|
||||
def apply_rgb_enhancement_to_hwc_float01(
|
||||
self,
|
||||
rgb: np.ndarray,
|
||||
stage: str = "after_decode",
|
||||
source_kind: str = "raw",
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
Aplica pós-processamento RGB opcional em imagem HWC float32 0..1.
|
||||
|
||||
Local correto no pipeline:
|
||||
- depois do debayer/decode RGB;
|
||||
- depois do rgb_calibration;
|
||||
- antes de flatfield native/fusão/crop/resize final.
|
||||
|
||||
Isso garante que treinamento e inferência usem exatamente a mesma transformação
|
||||
quando ambos usam o mesmo module_params.json.
|
||||
"""
|
||||
cfg = self._get_rgb_enhancement_config()
|
||||
self.last_rgb_enhancement_result = {
|
||||
"enabled": bool(cfg.get("enabled", False)),
|
||||
"applied": False,
|
||||
"stage": stage,
|
||||
"source_kind": source_kind,
|
||||
"config": cfg,
|
||||
"warnings": [],
|
||||
}
|
||||
|
||||
if not cfg.get("enabled", False):
|
||||
return rgb
|
||||
|
||||
if source_kind == "preview" and not cfg.get("apply_to_preview_input", False):
|
||||
self.last_rgb_enhancement_result["warnings"].append("skipped_preview_input")
|
||||
return rgb
|
||||
|
||||
if rgb is None or not isinstance(rgb, np.ndarray) or rgb.ndim != 3 or rgb.shape[2] != 3:
|
||||
self.last_rgb_enhancement_result["warnings"].append(
|
||||
f"invalid_rgb_shape:{None if rgb is None else rgb.shape}"
|
||||
)
|
||||
return rgb
|
||||
|
||||
t0 = time.perf_counter()
|
||||
|
||||
backend = cfg.get("backend", "none")
|
||||
preset = cfg.get("preset", "balanced")
|
||||
|
||||
if backend == "hybrid":
|
||||
if bool(cfg.get("use_u8_pipeline", True)):
|
||||
rgb_u8 = self._rgb_float01_to_u8_for_enhancement(rgb, cfg)
|
||||
out_u8 = self._hybrid_enhance_u8(rgb_u8, preset=preset)
|
||||
out = out_u8.astype(np.float32) / 255.0
|
||||
else:
|
||||
# Caminho defensivo. Hoje mantemos o u8 como padrão porque é
|
||||
# exatamente o mesmo tipo de operação usado no script visual.
|
||||
rgb_u8 = self._rgb_float01_to_u8_for_enhancement(rgb, cfg)
|
||||
out_u8 = self._hybrid_enhance_u8(rgb_u8, preset=preset)
|
||||
out = out_u8.astype(np.float32) / 255.0
|
||||
else:
|
||||
raise ValueError(f"RGB enhancement backend não suportado: {backend}")
|
||||
|
||||
if bool(cfg.get("clip_output", True)):
|
||||
np.clip(out, 0.0, 1.0, out=out)
|
||||
|
||||
dt_ms = (time.perf_counter() - t0) * 1000.0
|
||||
|
||||
self.last_rgb_enhancement_result.update({
|
||||
"applied": True,
|
||||
"backend": backend,
|
||||
"preset": preset,
|
||||
"time_ms": float(dt_ms),
|
||||
"input_shape": list(rgb.shape),
|
||||
"output_shape": list(out.shape),
|
||||
"output_dtype": str(out.dtype),
|
||||
})
|
||||
|
||||
return out.astype(np.float32, copy=False)
|
||||
|
||||
|
||||
def build_training_rgb(
|
||||
self,
|
||||
raw16: np.ndarray,
|
||||
|
|
@ -585,8 +915,19 @@ class RawProcessorCore:
|
|||
g = g * float(gains.get("G", 1.0))
|
||||
b = b * float(gains.get("B", 1.0))
|
||||
|
||||
chw = np.stack([r, g, b], axis=0).astype(np.float32)
|
||||
chw = np.clip(chw, 0.0, 1.0)
|
||||
rgb_hwc = np.stack([r, g, b], axis=2).astype(np.float32)
|
||||
np.clip(rgb_hwc, 0.0, 1.0, out=rgb_hwc)
|
||||
|
||||
# Pós-processamento RGB opcional.
|
||||
# Aplicado aqui para o caminho de treino/offline que chama build_training_rgb().
|
||||
rgb_hwc = self.apply_rgb_enhancement_to_hwc_float01(
|
||||
rgb_hwc,
|
||||
stage="build_training_rgb.after_decode",
|
||||
source_kind="raw",
|
||||
)
|
||||
|
||||
chw = np.transpose(rgb_hwc, (2, 0, 1)).astype(np.float32, copy=False)
|
||||
np.clip(chw, 0.0, 1.0, out=chw)
|
||||
|
||||
if output_dtype == "float32":
|
||||
return chw
|
||||
|
|
@ -639,9 +980,34 @@ class RawProcessorCore:
|
|||
cam_id = meta.get("cam_id") or meta.get("camera_id") or meta.get("id") or role
|
||||
|
||||
if role == "rgb":
|
||||
# Caminho offline: se o RGB veio como RAW16 Bayer 2D,
|
||||
# monta RGB de treino usando a mesma configuração de rgb_processing.
|
||||
if isinstance(data, np.ndarray) and data.ndim == 2:
|
||||
rgb_chw = self.build_training_rgb(
|
||||
data,
|
||||
output_dtype="float32",
|
||||
bit_depth=bit_depth,
|
||||
)
|
||||
rgb_img = np.transpose(rgb_chw, (1, 2, 0)).astype(np.float32, copy=False)
|
||||
|
||||
# Compatibilidade: se já veio HWC RGB processado.
|
||||
elif isinstance(data, np.ndarray) and data.ndim == 3 and data.shape[2] == 3:
|
||||
rgb_img = data.astype(np.float32)
|
||||
if rgb_img.max() > 1.5:
|
||||
rgb_img /= 255.0
|
||||
rgb_img = np.clip(rgb_img, 0.0, 1.0)
|
||||
|
||||
rgb_img = self.apply_rgb_enhancement_to_hwc_float01(
|
||||
rgb_img,
|
||||
stage="decode_bins_cameras.preview_or_processed_input",
|
||||
source_kind="preview",
|
||||
)
|
||||
else:
|
||||
raise RuntimeError(f"RGB offline inválido em {cam_id}: shape={getattr(data, 'shape', None)}")
|
||||
|
||||
decoded[cam_id] = {
|
||||
"name": "RGB",
|
||||
"image": data.astype(np.float32) / max_val,
|
||||
"image": rgb_img,
|
||||
"meta": meta,
|
||||
}
|
||||
|
||||
|
|
@ -798,6 +1164,14 @@ class RawProcessorCore:
|
|||
else:
|
||||
raise ValueError(f"rgb_processing.mode inválido: {rgb_mode}")
|
||||
|
||||
# Pós-processamento RGB opcional.
|
||||
# Este é o caminho principal de inferência RAW_BRUTO.
|
||||
rgb_hwc = self.apply_rgb_enhancement_to_hwc_float01(
|
||||
rgb_hwc,
|
||||
stage="decode_stream_cameras.after_raw_decode",
|
||||
source_kind="raw",
|
||||
)
|
||||
|
||||
decoded[cam_id] = {
|
||||
"name": "RGB",
|
||||
"role": "rgb",
|
||||
|
|
@ -812,10 +1186,21 @@ class RawProcessorCore:
|
|||
|
||||
rgb = data[:, :, ::-1].astype(np.float32) / 255.0
|
||||
|
||||
rgb = np.clip(rgb, 0.0, 1.0)
|
||||
|
||||
# Por padrão não mexe em preview/RGB já processado.
|
||||
# Se quiser aplicar também neste caminho, use:
|
||||
# rgb_processing.enhancement.apply_to_preview_input=true
|
||||
rgb = self.apply_rgb_enhancement_to_hwc_float01(
|
||||
rgb,
|
||||
stage="decode_stream_cameras.preview_input",
|
||||
source_kind="preview",
|
||||
)
|
||||
|
||||
decoded[cam_id] = {
|
||||
"name": "RGB",
|
||||
"role": "rgb",
|
||||
"image": np.clip(rgb, 0.0, 1.0),
|
||||
"image": rgb,
|
||||
"meta": cam_meta,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -190,6 +190,18 @@ class MultiSpecSegformerService:
|
|||
sess_options = ort.SessionOptions()
|
||||
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
|
||||
|
||||
#sess_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL
|
||||
#sess_options.intra_op_num_threads = 1
|
||||
#sess_options.inter_op_num_threads = 1
|
||||
#sess_options.add_session_config_entry(
|
||||
# "session.intra_op.allow_spinning",
|
||||
# "0",
|
||||
#)
|
||||
#sess_options.add_session_config_entry(
|
||||
# "session.inter_op.allow_spinning",
|
||||
# "0",
|
||||
#)
|
||||
|
||||
trt_cache_dir = str(self.config.get("trt_cache_dir", "trt_engine_cache"))
|
||||
trt_fp16 = bool(self.config.get("trt_fp16", True))
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,27 @@ import sys
|
|||
import os
|
||||
import time
|
||||
|
||||
# ============================================================
|
||||
# Controle de paralelismo nativo
|
||||
# Precisa ser definido antes de importar NumPy, OpenCV,
|
||||
# Numba, ONNX Runtime ou módulos que os importem internamente.
|
||||
# ============================================================
|
||||
os.environ.setdefault("OMP_WAIT_POLICY", "PASSIVE")
|
||||
os.environ.setdefault("KMP_BLOCKTIME", "0")
|
||||
# Limita kernels Numba, caso o Visual Worker utilize Numba
|
||||
# direta ou indiretamente.
|
||||
os.environ.setdefault("NUMBA_NUM_THREADS", "4")
|
||||
|
||||
def main():
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "VisualWorker")
|
||||
|
||||
# Importar e configurar OpenCV antes dos módulos do Visual Worker,
|
||||
# pois eles podem carregar OpenCV internamente.
|
||||
import cv2
|
||||
cv2.setNumThreads(2)
|
||||
cv2.ocl.setUseOpenCL(False)
|
||||
|
||||
from shared.enums import VisualWorkerCommandType, TipoFrameCamera
|
||||
from shared.utils import encode_image_base64
|
||||
from visual_worker.config import mostrar_log, get_camera_manager, iniciar_camera_manager
|
||||
|
|
|
|||
|
|
@ -863,13 +863,14 @@ class CameraManager:
|
|||
|
||||
def _iniciar_loop_inferencia(self, freq=25.0):
|
||||
def loop():
|
||||
periodo = 1.0 / max(float(freq), 0.1)
|
||||
|
||||
while True:
|
||||
t0_wall = time.time()
|
||||
t_loop0 = time.perf_counter()
|
||||
|
||||
try:
|
||||
if self.iniciando:
|
||||
time.sleep(0.05)
|
||||
time.sleep(0.02)
|
||||
continue
|
||||
|
||||
if not self.operante or self.camera is None:
|
||||
|
|
@ -882,15 +883,24 @@ class CameraManager:
|
|||
|
||||
if tensor5 is None or tensor_ts is None:
|
||||
self.perf.inc("infer_sem_tensor_novo")
|
||||
time.sleep(0.005)
|
||||
|
||||
# Espera curta, sem aplicar novamente um período inteiro.
|
||||
time.sleep(0.001)
|
||||
continue
|
||||
|
||||
t_inf0 = time.perf_counter()
|
||||
predictions = self.model_svc.infer_tensor_fast(tensor5, keep_probs=False)
|
||||
predictions = self.model_svc.infer_tensor_fast(
|
||||
tensor5,
|
||||
keep_probs=False,
|
||||
)
|
||||
t_inf1 = time.perf_counter()
|
||||
|
||||
infer_ms = (t_inf1 - t_inf0) * 1000.0
|
||||
infer_full = getattr(self.model_svc, "_ultimo_predictions_full", {}) or {}
|
||||
infer_full = getattr(
|
||||
self.model_svc,
|
||||
"_ultimo_predictions_full",
|
||||
{},
|
||||
) or {}
|
||||
|
||||
infer_forward_ms = infer_full.get(
|
||||
"forward_ms",
|
||||
|
|
@ -941,15 +951,24 @@ class CameraManager:
|
|||
infer_gpu_ms=float(infer_forward_ms or 0.0),
|
||||
tensor_ts=tensor_ts,
|
||||
frame_ts=pred_ts,
|
||||
idade_tensor_ms=(time.time() - tensor_ts) * 1000.0 if tensor_ts else None,
|
||||
idade_tensor_ms=(
|
||||
(time.time() - tensor_ts) * 1000.0
|
||||
if tensor_ts else None
|
||||
),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self.mostrar_log(f"❌ Erro no loop_inferencia weed: {e}")
|
||||
# Só limita depois de uma inferência realmente executada.
|
||||
gasto = time.perf_counter() - t_loop0
|
||||
restante = periodo - gasto
|
||||
|
||||
finally:
|
||||
lat = time.time() - t0_wall
|
||||
time.sleep(max(0.0, (1.0 / freq) - lat))
|
||||
if restante > 0:
|
||||
time.sleep(restante)
|
||||
|
||||
except Exception as e:
|
||||
self.mostrar_log(
|
||||
f"❌ Erro no loop_inferencia weed: {e}"
|
||||
)
|
||||
time.sleep(0.02)
|
||||
|
||||
threading.Thread(target=loop, daemon=True).start()
|
||||
|
||||
|
|
@ -976,7 +995,7 @@ class CameraManager:
|
|||
|
||||
if pred_cache is None:
|
||||
self.perf.inc("det_sem_prediction_nova")
|
||||
time.sleep(0.005)
|
||||
time.sleep(0.001)
|
||||
continue
|
||||
|
||||
predictions = pred_cache.get("predictions")
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ WEED_DEFAULT_CONFIG = {
|
|||
"deteccao_fps": 25.0,
|
||||
|
||||
# Supervisor/performance. Não precisa ser igual ao pipeline.
|
||||
"analise_fps": 15.0,
|
||||
"analise_fps": 10.0,
|
||||
|
||||
# Publicação Redis. Mantém baixo para não virar ruído.
|
||||
"publicacao_fps": 5.0,
|
||||
|
|
@ -73,10 +73,10 @@ WEED_DEFAULT_CONFIG = {
|
|||
"camera_height": 800,
|
||||
|
||||
# FPS solicitado na câmera/OAK. Pode ser maior que o pipeline.
|
||||
"camera_fps": 40,
|
||||
"camera_fps": 25,
|
||||
|
||||
# Tamanho final do tensor entregue ao modelo: [W, H].
|
||||
"ia_resolution": [1024, 640],
|
||||
"ia_resolution": [640, 400],
|
||||
|
||||
# Ordem oficial do tensor multiespectral.
|
||||
# Deve bater com o modelo ONNX exportado.
|
||||
|
|
|
|||
|
|
@ -2,11 +2,28 @@ import sys
|
|||
import os
|
||||
import time
|
||||
|
||||
# ============================================================
|
||||
# Controle de paralelismo nativo
|
||||
# Precisa ser definido antes de importar NumPy, OpenCV,
|
||||
# Numba, ONNX Runtime ou módulos que os importem internamente.
|
||||
# ============================================================
|
||||
os.environ.setdefault("OMP_WAIT_POLICY", "PASSIVE")
|
||||
os.environ.setdefault("KMP_BLOCKTIME", "0")
|
||||
# Limita kernels Numba, caso o Visual Worker utilize Numba
|
||||
# direta ou indiretamente.
|
||||
os.environ.setdefault("NUMBA_NUM_THREADS", "4")
|
||||
|
||||
def main():
|
||||
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "WeedWorker")
|
||||
|
||||
# Importar e configurar OpenCV antes dos módulos do Visual Worker,
|
||||
# pois eles podem carregar OpenCV internamente.
|
||||
import cv2
|
||||
cv2.setNumThreads(2)
|
||||
cv2.ocl.setUseOpenCL(False)
|
||||
|
||||
from weed_worker.config import mostrar_log, get_camera_manager, iniciar_camera_manager
|
||||
from shared.enums import WeedWorkerCommandType, TipoFrameCamera
|
||||
from shared.utils import encode_image_base64
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Exporta o checkpoint PyTorch do SegFormer Multi-Head OAK-FCC-3 para ONNX.
|
|||
|
||||
Exemplo:
|
||||
|
||||
python _10_export_onnx.py --config config.json --checkpoint backup/segformer_b1/target_teached/stacked_raw5/best_score.pt --out backup/segformer_b1/target_teached/stacked_raw5/best_score.onnx --train-script _8_train_multihead.py --opset 17 --device cuda
|
||||
python _10_export_onnx.py --config config.json --checkpoint backup/segformer_b1/target_teached/stacked_raw5/best_score.pt --out backup/segformer_b1/target_teached/stacked_raw5/best_score.onnx --train-script _8_train_multihead.py --opset 17 --device cuda --include-norm --postprocess argmax_fullres
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
|
|||
Loading…
Reference in New Issue