initial commit
This commit is contained in:
commit
0bafbee31f
54 files changed
+6317
No files matched your search
+494
@@ -0,0 +1,494 @@
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
# =========================
|
||||
# 项目根目录
|
||||
# =========================
|
||||
# 例如:
|
||||
# 宿主机:
|
||||
# /data2/zhangw_data/Bird_detect_cascade_service
|
||||
#
|
||||
# Docker:
|
||||
# /Bird_detect_cascade_service
|
||||
#
|
||||
# 会自动适配,不需要写死绝对路径
|
||||
# =========================
|
||||
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
MODELS_DIR = os.path.join(BASE_DIR, "models")
|
||||
RUNS_DIR = os.path.join(BASE_DIR, "runs")
|
||||
|
||||
|
||||
def _get_str(name: str, default: str) -> str:
|
||||
return os.getenv(name, default)
|
||||
|
||||
|
||||
def _get_int(name: str, default: int) -> int:
|
||||
value = os.getenv(name)
|
||||
if value is None or value == "":
|
||||
return default
|
||||
return int(value)
|
||||
|
||||
|
||||
def _get_float(name: str, default: float) -> float:
|
||||
value = os.getenv(name)
|
||||
if value is None or value == "":
|
||||
return default
|
||||
return float(value)
|
||||
|
||||
|
||||
def _get_bool(name: str, default: bool) -> bool:
|
||||
value = os.getenv(name)
|
||||
if value is None or value == "":
|
||||
return default
|
||||
return value.lower() in ["1", "true", "yes", "y"]
|
||||
|
||||
|
||||
def _get_int_list(name: str, default: str) -> list[int]:
|
||||
value = os.getenv(name, default)
|
||||
|
||||
if value is None or value.strip() == "":
|
||||
return []
|
||||
|
||||
return [
|
||||
int(x.strip())
|
||||
for x in value.split(",")
|
||||
if x.strip() != ""
|
||||
]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Settings:
|
||||
|
||||
# =========================
|
||||
# 模型开关
|
||||
# =========================
|
||||
enable_detection: bool = _get_bool(
|
||||
"ENABLE_DETECTION",
|
||||
True,
|
||||
)
|
||||
|
||||
enable_classification: bool = _get_bool(
|
||||
"ENABLE_CLASSIFICATION",
|
||||
True,
|
||||
)
|
||||
|
||||
# =========================
|
||||
# YOLOv5 模型
|
||||
# =========================
|
||||
det_model_path: str = _get_str(
|
||||
"DET_MODEL_PATH",
|
||||
os.path.join(
|
||||
MODELS_DIR,
|
||||
"YNDC_20251010_yolov5_optimize.onnx",
|
||||
),
|
||||
)
|
||||
|
||||
det_classes_path: str = _get_str(
|
||||
"DET_CLASSES_PATH",
|
||||
os.path.join(
|
||||
MODELS_DIR,
|
||||
"YNDC_20251010_yolov5_optimize.txt",
|
||||
),
|
||||
)
|
||||
|
||||
# =========================
|
||||
# ConvNeXt 分类模型
|
||||
# =========================
|
||||
cls_model_path: str = _get_str(
|
||||
"CLS_MODEL_PATH",
|
||||
os.path.join(
|
||||
MODELS_DIR,
|
||||
"JXPYH_20260204_mmcls_optimize.onnx",
|
||||
),
|
||||
)
|
||||
|
||||
cls_classes_path: str = _get_str(
|
||||
"CLS_CLASSES_PATH",
|
||||
os.path.join(
|
||||
MODELS_DIR,
|
||||
"JXPYH_20260204_mmcls_optimize.txt",
|
||||
),
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 密度估计模型
|
||||
# =========================
|
||||
enable_density: bool = _get_bool(
|
||||
"ENABLE_DENSITY",
|
||||
True,
|
||||
)
|
||||
|
||||
density_model_path: str = _get_str(
|
||||
"DENSITY_MODEL_PATH",
|
||||
os.path.join(
|
||||
MODELS_DIR,
|
||||
"bird_density.onnx",
|
||||
),
|
||||
)
|
||||
|
||||
# 如果模型输入固定,例如 400×400
|
||||
# 可以设置 DENSITY_IMG_SIZE=400
|
||||
# 默认 0 表示自动读取 ONNX 输入尺寸
|
||||
density_img_size: int = _get_int(
|
||||
"DENSITY_IMG_SIZE",
|
||||
0,
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 输入尺寸
|
||||
# =========================
|
||||
det_img_size: int = _get_int(
|
||||
"DET_IMG_SIZE",
|
||||
640,
|
||||
)
|
||||
|
||||
cls_img_size: int = _get_int(
|
||||
"CLS_IMG_SIZE",
|
||||
224,
|
||||
)
|
||||
|
||||
# =========================
|
||||
# YOLO 阈值
|
||||
# =========================
|
||||
det_conf_thres: float = _get_float(
|
||||
"DET_CONF_THRES",
|
||||
0.25,
|
||||
)
|
||||
|
||||
det_iou_thres: float = _get_float(
|
||||
"DET_IOU_THRES",
|
||||
0.45,
|
||||
)
|
||||
|
||||
# 二次过滤 YOLO 检测框,避免把明显错误的框传给 ConvNeXt
|
||||
det_second_conf_thres: float = _get_float(
|
||||
"DET_SECOND_CONF_THRES",
|
||||
0.35,
|
||||
)
|
||||
|
||||
# 最小框面积占整图比例,太小的噪声框过滤
|
||||
min_box_area_ratio: float = _get_float(
|
||||
"MIN_BOX_AREA_RATIO",
|
||||
0.00002,
|
||||
)
|
||||
|
||||
# 最大框面积占整图比例,太大的误检框过滤
|
||||
max_box_area_ratio: float = _get_float(
|
||||
"MAX_BOX_AREA_RATIO",
|
||||
1.0,
|
||||
)
|
||||
|
||||
# 最大长宽比,过细过长的框过滤
|
||||
max_box_aspect_ratio: float = _get_float(
|
||||
"MAX_BOX_ASPECT_RATIO",
|
||||
6.0,
|
||||
)
|
||||
|
||||
# 可选检测区域:按检测框中心点的纵向位置过滤。
|
||||
# 默认 0~1 表示整幅画面;可排除固定字幕、时间戳或岸边噪声区域。
|
||||
detection_roi_y_min_ratio: float = _get_float(
|
||||
"DETECTION_ROI_Y_MIN_RATIO",
|
||||
0.0,
|
||||
)
|
||||
|
||||
detection_roi_y_max_ratio: float = _get_float(
|
||||
"DETECTION_ROI_Y_MAX_RATIO",
|
||||
1.0,
|
||||
)
|
||||
|
||||
# 分类置信度低于该值时,不显示物种名称
|
||||
cls_show_conf_thres: float = _get_float(
|
||||
"CLS_SHOW_CONF_THRES",
|
||||
0.50,
|
||||
)
|
||||
|
||||
# 大目标判断阈值:框面积占整图比例超过该值,认为是近景大目标
|
||||
large_box_area_ratio: float = _get_float(
|
||||
"LARGE_BOX_AREA_RATIO",
|
||||
0.002,
|
||||
)
|
||||
|
||||
# 大目标分类显示阈值,近景大目标即使分类置信度稍低,也显示物种名
|
||||
large_box_cls_show_conf_thres: float = _get_float(
|
||||
"LARGE_BOX_CLS_SHOW_CONF_THRES",
|
||||
0.25,
|
||||
)
|
||||
|
||||
# 数量达到该阈值时,认为是密集场景,不显示物种名
|
||||
dense_scene_count_thres: int = _get_int(
|
||||
"DENSE_SCENE_COUNT_THRES",
|
||||
20,
|
||||
)
|
||||
|
||||
# 大目标强制显示物种名,只要分类结果非空即可
|
||||
show_large_box_name_always: bool = _get_bool(
|
||||
"SHOW_LARGE_BOX_NAME_ALWAYS",
|
||||
True,
|
||||
)
|
||||
|
||||
# 是否在密集场景隐藏所有物种名
|
||||
hide_cls_name_in_dense_scene: bool = _get_bool(
|
||||
"HIDE_CLS_NAME_IN_DENSE_SCENE",
|
||||
True,
|
||||
)
|
||||
|
||||
# 所有物种是否统一使用同一种标签和检测框颜色
|
||||
uniform_species_color: bool = _get_bool(
|
||||
"UNIFORM_SPECIES_COLOR",
|
||||
False,
|
||||
)
|
||||
|
||||
# 可选:整段素材已人工确认只有一个物种时,覆盖细粒度模型标签。
|
||||
# 留空表示使用模型输出。
|
||||
forced_species_name: str = _get_str(
|
||||
"FORCED_SPECIES_NAME",
|
||||
"",
|
||||
).strip()
|
||||
|
||||
forced_species_class_id: int = _get_int(
|
||||
"FORCED_SPECIES_CLASS_ID",
|
||||
-1,
|
||||
)
|
||||
|
||||
# 只对达到该框面积占比的目标应用人工物种覆盖。
|
||||
# 可用来统一主要大型鸟类,同时保留画面中的小型伴生鸟类。
|
||||
forced_species_min_box_area_ratio: float = _get_float(
|
||||
"FORCED_SPECIES_MIN_BOX_AREA_RATIO",
|
||||
0.0,
|
||||
)
|
||||
|
||||
# 视频级人工复核映射,格式示例:
|
||||
# 东方白鹳=白鹤,白鹭=白鹤
|
||||
species_name_remap: str = _get_str(
|
||||
"SPECIES_NAME_REMAP",
|
||||
"",
|
||||
).strip()
|
||||
|
||||
# 只有原始细粒度分类置信度达到该阈值时才应用标签映射。
|
||||
# 默认 0 保持原有行为;混合物种场景可提高该值以避免低置信度误映射。
|
||||
species_name_remap_min_confidence: float = _get_float(
|
||||
"SPECIES_NAME_REMAP_MIN_CONFIDENCE",
|
||||
0.0,
|
||||
)
|
||||
|
||||
# 留空表示允许全部;非空时,只有白名单中的物种显示名称和物种色。
|
||||
allowed_species_names: str = _get_str(
|
||||
"ALLOWED_SPECIES_NAMES",
|
||||
"",
|
||||
).strip()
|
||||
|
||||
# 可选双物种明暗规则。两个名称都非空时,不运行细粒度分类模型,
|
||||
# 而是根据检测框内暗色像素比例区分两个外观差异明显的物种。
|
||||
binary_dark_species_name: str = _get_str(
|
||||
"BINARY_DARK_SPECIES_NAME",
|
||||
"",
|
||||
).strip()
|
||||
|
||||
binary_light_species_name: str = _get_str(
|
||||
"BINARY_LIGHT_SPECIES_NAME",
|
||||
"",
|
||||
).strip()
|
||||
|
||||
binary_dark_pixel_threshold: int = _get_int(
|
||||
"BINARY_DARK_PIXEL_THRESHOLD",
|
||||
85,
|
||||
)
|
||||
|
||||
binary_dark_ratio_threshold: float = _get_float(
|
||||
"BINARY_DARK_RATIO_THRESHOLD",
|
||||
0.12,
|
||||
)
|
||||
|
||||
# 可选:对画面中的深色目标做人工标签覆盖。
|
||||
# 适合模型把黑色雁鸭类误判成白骨顶等细粒度类别的场景。
|
||||
dark_override_species_name: str = _get_str(
|
||||
"DARK_OVERRIDE_SPECIES_NAME",
|
||||
"",
|
||||
).strip()
|
||||
|
||||
dark_override_species_class_id: int = _get_int(
|
||||
"DARK_OVERRIDE_SPECIES_CLASS_ID",
|
||||
-1,
|
||||
)
|
||||
|
||||
dark_override_pixel_threshold: int = _get_int(
|
||||
"DARK_OVERRIDE_PIXEL_THRESHOLD",
|
||||
80,
|
||||
)
|
||||
|
||||
dark_override_ratio_threshold: float = _get_float(
|
||||
"DARK_OVERRIDE_RATIO_THRESHOLD",
|
||||
0.35,
|
||||
)
|
||||
|
||||
dark_override_min_box_area_ratio: float = _get_float(
|
||||
"DARK_OVERRIDE_MIN_BOX_AREA_RATIO",
|
||||
0.0,
|
||||
)
|
||||
|
||||
dark_override_max_box_area_ratio: float = _get_float(
|
||||
"DARK_OVERRIDE_MAX_BOX_AREA_RATIO",
|
||||
1.0,
|
||||
)
|
||||
|
||||
dark_override_roi_y_min_ratio: float = _get_float(
|
||||
"DARK_OVERRIDE_ROI_Y_MIN_RATIO",
|
||||
0.0,
|
||||
)
|
||||
|
||||
dark_override_roi_y_max_ratio: float = _get_float(
|
||||
"DARK_OVERRIDE_ROI_Y_MAX_RATIO",
|
||||
1.0,
|
||||
)
|
||||
|
||||
# 可选:按画面纵向区域做人工标签覆盖。
|
||||
# 用于同一视频中上/中/下区域物种分布稳定的场景。
|
||||
region_override_species_name: str = _get_str(
|
||||
"REGION_OVERRIDE_SPECIES_NAME",
|
||||
"",
|
||||
).strip()
|
||||
|
||||
region_override_species_class_id: int = _get_int(
|
||||
"REGION_OVERRIDE_SPECIES_CLASS_ID",
|
||||
-1,
|
||||
)
|
||||
|
||||
region_override_source_species_names: str = _get_str(
|
||||
"REGION_OVERRIDE_SOURCE_SPECIES_NAMES",
|
||||
"",
|
||||
).strip()
|
||||
|
||||
region_override_roi_y_min_ratio: float = _get_float(
|
||||
"REGION_OVERRIDE_ROI_Y_MIN_RATIO",
|
||||
0.0,
|
||||
)
|
||||
|
||||
region_override_roi_y_max_ratio: float = _get_float(
|
||||
"REGION_OVERRIDE_ROI_Y_MAX_RATIO",
|
||||
1.0,
|
||||
)
|
||||
|
||||
# 可选:鹤类混淆纠正。针对白鹤/白头鹤/白枕鹤相互误判,
|
||||
# 根据检测框内亮白像素和暗色像素比例做保守二次校验。
|
||||
enable_crane_appearance_correction: bool = _get_bool(
|
||||
"ENABLE_CRANE_APPEARANCE_CORRECTION",
|
||||
False,
|
||||
)
|
||||
|
||||
crane_white_ratio_threshold: float = _get_float(
|
||||
"CRANE_WHITE_RATIO_THRESHOLD",
|
||||
0.30,
|
||||
)
|
||||
|
||||
crane_dark_ratio_threshold: float = _get_float(
|
||||
"CRANE_DARK_RATIO_THRESHOLD",
|
||||
0.28,
|
||||
)
|
||||
|
||||
crane_tail_white_ratio_threshold: float = _get_float(
|
||||
"CRANE_TAIL_WHITE_RATIO_THRESHOLD",
|
||||
0.16,
|
||||
)
|
||||
|
||||
crane_uncertain_hide_label: bool = _get_bool(
|
||||
"CRANE_UNCERTAIN_HIDE_LABEL",
|
||||
True,
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 裁剪外扩比例
|
||||
# =========================
|
||||
crop_padding_ratio: float = _get_float(
|
||||
"CROP_PADDING_RATIO",
|
||||
0.08,
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 最多检测目标数
|
||||
# =========================
|
||||
max_detections: int = _get_int(
|
||||
"MAX_DETECTIONS",
|
||||
50,
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 推理设备
|
||||
# cpu / cuda
|
||||
# =========================
|
||||
device: str = _get_str(
|
||||
"DEVICE",
|
||||
"cpu",
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 输出目录
|
||||
# =========================
|
||||
output_dir: str = _get_str(
|
||||
"OUTPUT_DIR",
|
||||
RUNS_DIR,
|
||||
)
|
||||
|
||||
image_output_subdir: str = _get_str(
|
||||
"IMAGE_OUTPUT_SUBDIR",
|
||||
"images",
|
||||
)
|
||||
|
||||
video_output_subdir: str = _get_str(
|
||||
"VIDEO_OUTPUT_SUBDIR",
|
||||
"video",
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 视频抽帧间隔
|
||||
# =========================
|
||||
video_sample_interval: int = _get_int(
|
||||
"VIDEO_SAMPLE_INTERVAL",
|
||||
2,
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 是否保存画框视频
|
||||
# =========================
|
||||
save_annotated_video: bool = _get_bool(
|
||||
"SAVE_ANNOTATED_VIDEO",
|
||||
True,
|
||||
)
|
||||
|
||||
# =========================
|
||||
# 只保留 YOLO 类别
|
||||
# 默认只保留 bird
|
||||
# =========================
|
||||
target_det_class_ids: list[int] = None
|
||||
|
||||
# =========================
|
||||
# 视频框平滑
|
||||
# =========================
|
||||
smooth_video_boxes: bool = _get_bool(
|
||||
"SMOOTH_VIDEO_BOXES",
|
||||
True,
|
||||
)
|
||||
|
||||
smooth_hold_frames: int = _get_int(
|
||||
"SMOOTH_HOLD_FRAMES",
|
||||
3,
|
||||
)
|
||||
|
||||
box_smooth_alpha: float = _get_float(
|
||||
"BOX_SMOOTH_ALPHA",
|
||||
0.25,
|
||||
)
|
||||
|
||||
def __post_init__(self):
|
||||
object.__setattr__(
|
||||
self,
|
||||
"target_det_class_ids",
|
||||
_get_int_list(
|
||||
"TARGET_DET_CLASS_IDS",
|
||||
"0",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
settings = Settings()
|
||||
Reference in new issue
Block a user