Coverage for collectra/tasks/object_detection/adaptation_commands.py: 94.23%
52 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 09:56 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-16 09:56 +0000
1"""CLI commands for adapting detector checkpoints to aligned RGB views."""
3from pathlib import Path
5from collectra.cli import command
8def _output_path(input_model, output_model, num_views, suffix, overwrite):
9 input_model = Path(input_model)
10 if not input_model.is_file():
11 raise ValueError(f"Input checkpoint does not exist: {input_model}")
12 if num_views < 1:
13 raise ValueError("num_views must be >= 1")
14 output_model = (
15 Path(output_model)
16 if output_model
17 else input_model.with_name(
18 f"{input_model.stem}-{num_views}views-{suffix}{input_model.suffix or '.pth'}"
19 )
20 )
21 if input_model.resolve() == output_model.resolve():
22 raise ValueError("Output must differ from the input checkpoint")
23 if output_model.exists() and not overwrite:
24 raise ValueError("Output already exists; use --overwrite")
25 return output_model
28def _check_fusion(num_views, reference_view, hidden_dim):
29 if not 0 <= reference_view < num_views:
30 raise ValueError("reference_view must be between 0 and num_views - 1")
31 if hidden_dim < 1:
32 raise ValueError("hidden_dim must be >= 1")
35class RFDETRAdaptationCommands:
36 @command
37 def adapt_input_channels(
38 self,
39 input_model: Path,
40 num_views: int,
41 output: Path | None = None,
42 variant: str | None = None,
43 overwrite: bool = False,
44 validate: bool = True,
45 ) -> Path:
46 """Expand RGB input filters across aligned views and save a checkpoint."""
47 destination = _output_path(
48 input_model, output, num_views, f"{3 * num_views}ch", overwrite
49 )
50 from .rfdetr_input_adaptation import (
51 expand_rfdetr_input_channels,
52 validate_saved_model,
53 )
55 expand_rfdetr_input_channels(input_model, destination, num_views, variant)
56 if validate:
57 validate_saved_model(destination, 3 * num_views)
58 print(f"Saved: {destination}")
59 return destination
61 @command
62 def adapt_feature_fusion(
63 self,
64 input_model: Path,
65 num_views: int,
66 output: Path | None = None,
67 variant: str | None = None,
68 reference_view: int = 0,
69 hidden_dim: int = 64,
70 overwrite: bool = False,
71 validate: bool = True,
72 ) -> Path:
73 """Add shared RGB encoding and attention across aligned views."""
74 destination = _output_path(input_model, output, num_views, "fusion", overwrite)
75 _check_fusion(num_views, reference_view, hidden_dim)
76 from .rfdetr_feature_fusion import (
77 expand_rfdetr_feature_fusion,
78 validate_saved_model,
79 )
81 expand_rfdetr_feature_fusion(
82 input_model, destination, num_views, variant, reference_view, hidden_dim
83 )
84 if validate:
85 validate_saved_model(destination, num_views)
86 print(f"Saved: {destination}")
87 return destination
90class YOLOAdaptationCommands:
91 @command
92 def adapt_feature_fusion(
93 self,
94 input_model: Path,
95 num_views: int,
96 output: Path | None = None,
97 reference_view: int = 0,
98 hidden_dim: int = 64,
99 overwrite: bool = False,
100 validate: bool = True,
101 ) -> Path:
102 """Add a shared RGB backbone and attention across aligned views."""
103 destination = _output_path(input_model, output, num_views, "fusion", overwrite)
104 if Path(input_model).suffix.lower() != ".pt":
105 raise ValueError("Input must be an RGB YOLO .pt checkpoint")
106 _check_fusion(num_views, reference_view, hidden_dim)
107 from .yolo_feature_fusion import (
108 expand_yolo_feature_fusion,
109 validate_saved_model,
110 )
112 expand_yolo_feature_fusion(
113 input_model, destination, num_views, reference_view, hidden_dim
114 )
115 if validate:
116 validate_saved_model(destination, num_views)
117 print(f"Saved: {destination}")
118 return destination