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 = {
|
self.rgb_processing_config = {
|
||||||
"mode": "linear_demosaic", # "linear_demosaic", "linear_demosaic_half" ou "bayer_planes"
|
"mode": "linear_demosaic", # "linear_demosaic", "linear_demosaic_half" ou "bayer_planes"
|
||||||
"demosaic_algorithm": "ea", # "ea" ou "bilinear"
|
"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 = {
|
self.fusion_config = {
|
||||||
|
|
@ -389,6 +423,7 @@ class RawProcessorCore:
|
||||||
|
|
||||||
self.last_decode_perf = {}
|
self.last_decode_perf = {}
|
||||||
self._last_decode_perf_log_ts = 0.0
|
self._last_decode_perf_log_ts = 0.0
|
||||||
|
self.last_rgb_enhancement_result = None
|
||||||
|
|
||||||
if calibration_json_path:
|
if calibration_json_path:
|
||||||
self.load_config_json(calibration_json_path)
|
self.load_config_json(calibration_json_path)
|
||||||
|
|
@ -553,6 +588,301 @@ class RawProcessorCore:
|
||||||
|
|
||||||
return rgb[:, :, 0], rgb[:, :, 1], rgb[:, :, 2]
|
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(
|
def build_training_rgb(
|
||||||
self,
|
self,
|
||||||
raw16: np.ndarray,
|
raw16: np.ndarray,
|
||||||
|
|
@ -585,8 +915,19 @@ class RawProcessorCore:
|
||||||
g = g * float(gains.get("G", 1.0))
|
g = g * float(gains.get("G", 1.0))
|
||||||
b = b * float(gains.get("B", 1.0))
|
b = b * float(gains.get("B", 1.0))
|
||||||
|
|
||||||
chw = np.stack([r, g, b], axis=0).astype(np.float32)
|
rgb_hwc = np.stack([r, g, b], axis=2).astype(np.float32)
|
||||||
chw = np.clip(chw, 0.0, 1.0)
|
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":
|
if output_dtype == "float32":
|
||||||
return chw
|
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
|
cam_id = meta.get("cam_id") or meta.get("camera_id") or meta.get("id") or role
|
||||||
|
|
||||||
if role == "rgb":
|
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] = {
|
decoded[cam_id] = {
|
||||||
"name": "RGB",
|
"name": "RGB",
|
||||||
"image": data.astype(np.float32) / max_val,
|
"image": rgb_img,
|
||||||
"meta": meta,
|
"meta": meta,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -798,6 +1164,14 @@ class RawProcessorCore:
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"rgb_processing.mode inválido: {rgb_mode}")
|
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] = {
|
decoded[cam_id] = {
|
||||||
"name": "RGB",
|
"name": "RGB",
|
||||||
"role": "rgb",
|
"role": "rgb",
|
||||||
|
|
@ -812,10 +1186,21 @@ class RawProcessorCore:
|
||||||
|
|
||||||
rgb = data[:, :, ::-1].astype(np.float32) / 255.0
|
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] = {
|
decoded[cam_id] = {
|
||||||
"name": "RGB",
|
"name": "RGB",
|
||||||
"role": "rgb",
|
"role": "rgb",
|
||||||
"image": np.clip(rgb, 0.0, 1.0),
|
"image": rgb,
|
||||||
"meta": cam_meta,
|
"meta": cam_meta,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,18 @@ class MultiSpecSegformerService:
|
||||||
sess_options = ort.SessionOptions()
|
sess_options = ort.SessionOptions()
|
||||||
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
|
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_cache_dir = str(self.config.get("trt_cache_dir", "trt_engine_cache"))
|
||||||
trt_fp16 = bool(self.config.get("trt_fp16", True))
|
trt_fp16 = bool(self.config.get("trt_fp16", True))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,27 @@ import sys
|
||||||
import os
|
import os
|
||||||
import time
|
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():
|
def main():
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
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")
|
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.enums import VisualWorkerCommandType, TipoFrameCamera
|
||||||
from shared.utils import encode_image_base64
|
from shared.utils import encode_image_base64
|
||||||
from visual_worker.config import mostrar_log, get_camera_manager, iniciar_camera_manager
|
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 _iniciar_loop_inferencia(self, freq=25.0):
|
||||||
def loop():
|
def loop():
|
||||||
|
periodo = 1.0 / max(float(freq), 0.1)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
t0_wall = time.time()
|
|
||||||
t_loop0 = time.perf_counter()
|
t_loop0 = time.perf_counter()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.iniciando:
|
if self.iniciando:
|
||||||
time.sleep(0.05)
|
time.sleep(0.02)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not self.operante or self.camera is None:
|
if not self.operante or self.camera is None:
|
||||||
|
|
@ -882,15 +883,24 @@ class CameraManager:
|
||||||
|
|
||||||
if tensor5 is None or tensor_ts is None:
|
if tensor5 is None or tensor_ts is None:
|
||||||
self.perf.inc("infer_sem_tensor_novo")
|
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
|
continue
|
||||||
|
|
||||||
t_inf0 = time.perf_counter()
|
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()
|
t_inf1 = time.perf_counter()
|
||||||
|
|
||||||
infer_ms = (t_inf1 - t_inf0) * 1000.0
|
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(
|
infer_forward_ms = infer_full.get(
|
||||||
"forward_ms",
|
"forward_ms",
|
||||||
|
|
@ -941,15 +951,24 @@ class CameraManager:
|
||||||
infer_gpu_ms=float(infer_forward_ms or 0.0),
|
infer_gpu_ms=float(infer_forward_ms or 0.0),
|
||||||
tensor_ts=tensor_ts,
|
tensor_ts=tensor_ts,
|
||||||
frame_ts=pred_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:
|
# Só limita depois de uma inferência realmente executada.
|
||||||
self.mostrar_log(f"❌ Erro no loop_inferencia weed: {e}")
|
gasto = time.perf_counter() - t_loop0
|
||||||
|
restante = periodo - gasto
|
||||||
|
|
||||||
finally:
|
if restante > 0:
|
||||||
lat = time.time() - t0_wall
|
time.sleep(restante)
|
||||||
time.sleep(max(0.0, (1.0 / freq) - lat))
|
|
||||||
|
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()
|
threading.Thread(target=loop, daemon=True).start()
|
||||||
|
|
||||||
|
|
@ -976,7 +995,7 @@ class CameraManager:
|
||||||
|
|
||||||
if pred_cache is None:
|
if pred_cache is None:
|
||||||
self.perf.inc("det_sem_prediction_nova")
|
self.perf.inc("det_sem_prediction_nova")
|
||||||
time.sleep(0.005)
|
time.sleep(0.001)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
predictions = pred_cache.get("predictions")
|
predictions = pred_cache.get("predictions")
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ WEED_DEFAULT_CONFIG = {
|
||||||
"deteccao_fps": 25.0,
|
"deteccao_fps": 25.0,
|
||||||
|
|
||||||
# Supervisor/performance. Não precisa ser igual ao pipeline.
|
# 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.
|
# Publicação Redis. Mantém baixo para não virar ruído.
|
||||||
"publicacao_fps": 5.0,
|
"publicacao_fps": 5.0,
|
||||||
|
|
@ -73,10 +73,10 @@ WEED_DEFAULT_CONFIG = {
|
||||||
"camera_height": 800,
|
"camera_height": 800,
|
||||||
|
|
||||||
# FPS solicitado na câmera/OAK. Pode ser maior que o pipeline.
|
# 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].
|
# Tamanho final do tensor entregue ao modelo: [W, H].
|
||||||
"ia_resolution": [1024, 640],
|
"ia_resolution": [640, 400],
|
||||||
|
|
||||||
# Ordem oficial do tensor multiespectral.
|
# Ordem oficial do tensor multiespectral.
|
||||||
# Deve bater com o modelo ONNX exportado.
|
# Deve bater com o modelo ONNX exportado.
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,28 @@ import sys
|
||||||
import os
|
import os
|
||||||
import time
|
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():
|
def main():
|
||||||
|
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
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")
|
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 weed_worker.config import mostrar_log, get_camera_manager, iniciar_camera_manager
|
||||||
from shared.enums import WeedWorkerCommandType, TipoFrameCamera
|
from shared.enums import WeedWorkerCommandType, TipoFrameCamera
|
||||||
from shared.utils import encode_image_base64
|
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:
|
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
|
from __future__ import annotations
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue