# Требования к адаптации кода под gin-config стиль Референсный проект: `test_bb_uav` --- ## 1. Структура директорий Создать директорию для конфигурационных файлов: ``` depth_edges_annotate_worlduav/ ├── in/ │ └── config_files/ │ ├── pipeline.gin # Стадии пайплайна, пути, форматы вывода │ ├── hardware.gin # Профили GPU, batch size, FP16, workers │ ├── models.gin # Идентификаторы моделей, fallback-стратегии │ ├── input.gin # Размер изображений, нормализация, аугментации │ └── segmentation.gin # Текстовые промпты, параметры сегментации ├── src/ │ ├── conf/ │ │ ├── __init__.py │ │ ├── pipeline_conf.py # PipelineConfig (пути, стадии, resume, вывод) │ │ ├── hardware_conf.py # HardwareConfig (GPU, batch, FP16, workers) │ │ ├── models_conf.py # ModelsConfig (model IDs, fallback) │ │ ├── input_conf.py # InputConfig (размер, нормализация) │ │ └── seg_conf.py # SegConfig (промпты, параметры) │ ├── augmentor/ │ │ ├── ... # Существующие модули (без изменения структуры) │ └── augment_dataset.py # Точка входа (адаптировать) ``` --- ## 2. Паттерн конфигурационных классов Каждый конфиг-класс следует единому шаблону из `test_bb_uav`: ```python import gin @gin.configurable class HardwareConfig: def __init__(self, profile_name='rtx4090', use_fp16=True, batch_size=None, num_workers=4, reserve_gb=2.0): self.profile_name = profile_name self.use_fp16 = use_fp16 self.batch_size = batch_size self.num_workers = num_workers self.reserve_gb = reserve_gb ``` ### Правила: - Декоратор `@gin.configurable` только на классах (не на функциях) - Все параметры имеют значения по умолчанию в `__init__` - Класс хранит параметры как атрибуты экземпляра (`self.param = param`) - Не использовать dataclass совместно с gin — заменить dataclass на обычные классы - Допустима пост-обработка в `__init__` (вычисление производных значений) --- ## 3. Загрузка конфигов ### 3.1 Центральный загрузчик (ПРОДАКШЕН) Один файл `src/conf/config_loader.py` содержит единственную функцию, которая парсит **все** .gin файлы за один вызов: ```python def load_all_configs(path2cfg: str) -> dict[str, Any]: """Parse ALL .gin files at once and return all config objects.""" gin.clear_config() # ОБЯЗАТЕЛЬНО: сброс глобального состояния gin gin_files = sorted(Path(path2cfg).glob("*.gin")) gin.parse_config_files_and_bindings( config_files=[str(f) for f in gin_files], bindings=[], ) return { "pipeline": PipelineConfig(), "hardware": HardwareConfig(), "models": ModelsConfig(), "input": InputConfig(), "seg": SegConfig(), } ``` ### Правила: - `gin.clear_config()` **обязателен** перед каждой загрузкой — без него параметры от предыдущих вызовов накапливаются (gin имеет глобальное состояние) - Все .gin файлы парсятся **одним вызовом** `gin.parse_config_files_and_bindings()` - Конфиг-объекты создаются **после** полной загрузки всех .gin - В main() — **один вызов** `load_all_configs()`, не 5 отдельных `get_*_cfg()` ### 3.2 Индивидуальные загрузчики (ТОЛЬКО ДЛЯ ТЕСТИРОВАНИЯ) Для unit-тестов и отладки — отдельные `get__cfg()` функции в том же файле: ```python def get_hardware_cfg(path2cfg: str) -> HardwareConfig: """Load ONLY hardware config (for isolated testing).""" gin.clear_config() # Обязательно! gin.parse_config_file(f"{path2cfg}hardware.gin") return HardwareConfig() ``` ### Правила: - Имя функции: `get__cfg(path2cfg: str) -> ConfigClass` - **Обязательно** `gin.clear_config()` перед `gin.parse_config_file()` - Используются **только** в тестах и notebooks, не в main() ### Референс: `develop_style_prompt/config_loader_reference.py` --- ## 4. Формат .gin файлов Плоский формат `ClassName.param = value`: ```gin # hardware.gin HardwareConfig.profile_name = 'rtx4090' HardwareConfig.use_fp16 = True HardwareConfig.batch_size = 8 HardwareConfig.num_workers = 4 HardwareConfig.reserve_gb = 2.0 ``` ### Правила: - Одна строка — один параметр - Комментарии через `#` - Строки в одинарных или двойных кавычках - Списки в квадратных скобках: `[1, 2, 3]` - Не использовать макросы, ссылки, `gin.constant()` или другие продвинутые фичи gin - Каждый `.gin` файл соответствует одному конфиг-классу --- ## 5. Точка входа (main) Адаптировать `augment_dataset.py` — использовать центральный загрузчик: ```python from src.conf.config_loader import load_all_configs from src.utils.utils_file_dir import get_proj_dir def main() -> None: proj_dir = get_proj_dir() path2cfg = f"{proj_dir}in/config_files/" # ОДИН вызов загружает ВСЕ конфиги: configs = load_all_configs(path2cfg) # Явная передача в пайплайн: run_pipeline( configs["pipeline"], configs["hardware"], configs["models"], configs["input"], configs["seg"], ) ``` ```python # Ищем pyproject.toml или .git как корень проекта: MARKERS = ("pyproject.toml", ".git", "in") def get_proj_dir() -> str: current = Path(__file__).resolve().parent for _ in range(10): if any((current / m).exists() for m in MARKERS): return str(current) + "/" current = current.parent raise RuntimeError("Project root not found") ``` ### Правила: - Убрать `argparse` — все параметры из gin-файлов - **Один вызов** `load_all_configs()` вместо 5 отдельных `get_*_cfg()` - `gin.clear_config()` вызывается **внутри** `load_all_configs()` автоматически - Конфиг-объекты передаются **явно** через аргументы (не через глобальный gin) - Функция `get_proj_dir()` определяет корень проекта автоматически --- ## 6. Распределение параметров по конфиг-классам ### PipelineConfig (`pipeline.gin`) | Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | `input_root` | str | — | Путь к входным данным | | `output_root` | str | — | Путь для выходных данных | | `stages` | list | `['depth', 'edges', 'segmentation']` | Стадии пайплайна | | `save_npy` | bool | `True` | Сохранять .npy файлы | | `save_vis` | bool | `True` | Сохранять визуализации | | `save_concat` | bool | `True` | Сохранять 6-канальный результат | | `resume` | bool | `True` | Пропускать уже обработанные | | `subset` | str | `None` | Фильтр по подмножеству | | `source` | str | `None` | Фильтр по источнику (query/db) | | `log_level` | str | `'INFO'` | Уровень логирования | ### HardwareConfig (`hardware.gin`) | Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | `profile_name` | str | `'rtx4090'` | Имя профиля GPU | | `use_fp16` | bool | `True` | Использовать half precision | | `batch_size` | int | `None` | Размер батча (None = авто) | | `num_workers` | int | `4` | Число DataLoader workers | | `reserve_gb` | float | `2.0` | Резерв VRAM в ГБ | ### ModelsConfig (`models.gin`) | Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | `depth_model_id` | str | `'depth-anything/...'` | HuggingFace ID модели глубины | | `depth_fallback_id` | str | `'...'` | Fallback модель глубины | | `seg_model_id` | str | `'...'` | ID модели сегментации | | `seg_fallback_id` | str | `'...'` | Fallback модель сегментации | | `depth_model_size` | str | `'Large'` | Размер модели глубины | | `seg_model_size` | str | `'b5'` | Размер модели сегментации | ### InputConfig (`input.gin`) | Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | `image_size` | int | `256` | Размер изображения | | `sobel_blur_ksize` | int | `3` | Ядро Sobel для edges | | `edge_normalize` | bool | `True` | Нормализация карты краев | ### SegConfig (`segmentation.gin`) | Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | `prompts` | list | `[...]` | Текстовые промпты для OV-сегментации | | `threshold` | float | `0.3` | Порог сегментации | --- ## 7. Миграция существующих модулей ### Что удалить: - `argparse` из `augment_dataset.py` - `@dataclass` из `AugmentConfig` и `HardwareProfile` в `config.py` - Словарь `PROFILES` с хардкодом профилей (перенести в gin) - `DEFAULT_GEO_PROMPTS` из `models.py` (перенести в gin) ### Что сохранить: - Структуру `src/augmentor/` (dataset, inference, models, io_utils) - Логику fallback при загрузке моделей (но model ID из конфига) - Логику discover_images, AugmentDataset - Функции сохранения (save_depth, save_edges, save_segmentation) ### Что адаптировать: - Все функции, принимающие `AugmentConfig`, должны принимать отдельные конфиг-объекты - Захардкоженные значения (model IDs, промпты, размер 256) — в gin-файлы - `pipeline_256.py` — удалить или адаптировать под новый стиль --- ## 8. Ограничения по использованию gin Использовать **минимальный** набор возможностей gin: - **Да**: `@gin.configurable`, `gin.parse_config_file()` - **Нет**: `gin.register()`, `gin.constant()`, `gin.query_parameter()`, макросы, ссылки между конфигами - Значения конфигов можно менять в runtime после загрузки (`config.param = new_value`) --- ## 9. Порядок выполнения миграции 1. Создать `src/conf/` с конфиг-классами и функциями загрузки 2. Создать `in/config_files/` с `.gin` файлами 3. Адаптировать `augment_dataset.py` — заменить argparse на gin-загрузку 4. Адаптировать `augmentor/models.py` — model IDs из конфига 5. Адаптировать `augmentor/inference.py` — параметры из конфига 6. Адаптировать `augmentor/dataset.py` — принимать конфиг-объекты 7. Удалить старый `config.py` (dataclass-версию) 8. Проверить, что `depth_edges_gen.py` и `depth_anything_v2_generate.py` используют новые конфиги 9. Удалить или адаптировать `pipeline_256.py`