User Guide: Architecture & Registry System¶
CoreCV is engineered with a modular, decoupled architecture. Vision models are constructed from three distinct architectural building blocks—Backbones, Necks, and Heads—registered and assembled using a type-safe static registry system (CoreRegistry).
1. Built-in Registry Catalogue¶
All components in CoreCV are registered in global registries (BACKBONE_REGISTRY, NECK_REGISTRY, HEAD_REGISTRY) and can be selected by passing their string registration keys in configuration dicts or YAML files (head_type="...", neck_type="...").
Built-in Backbones (BACKBONE_REGISTRY)¶
| Model Family | Registry Keys (model_name) |
Feature Strides | Default Channels |
|---|---|---|---|
| ResNet | resnet18, resnet34, resnet50, resnet101 |
[4, 8, 16, 32] |
[64, 128, 256, 512] / [256, 512, 1024, 2048] |
| MobileNetV3 | mobilenet_v3_small, mobilenet_v3_large |
[4, 8, 16, 32] |
[16, 24, 48, 96] / [24, 40, 112, 160] |
| ConvNeXt | convnext_tiny, convnext_small, convnext_base, convnext_large |
[4, 8, 16, 32] |
[96, 192, 384, 768]+ |
| ViT | vit_tiny, vit_small, vit_base |
[8, 16, 32] |
SimplePyramidAdapter outputs [192/384/768] |
Built-in Necks (NECK_REGISTRY)¶
Registry Key (neck_type) |
Class | Architecture Description |
|---|---|---|
fpn |
FPN |
Feature Pyramid Network (Lin et al., 2017) with top-down pathway and lateral 1x1 convs. |
panet |
PANet |
Path Aggregation Network (Liu et al., 2018) adding bottom-up path augmentation to FPN. |
Built-in Heads (HEAD_REGISTRY)¶
| Task Category | Registry Key (head_type) |
Class | Architecture Description |
|---|---|---|---|
| Classification | linear_classification |
LinearClassificationHead |
Global average pooling followed by linear classification layer. |
| Segmentation | aspp_decoder |
ASPPDecoder |
DeepLabV3+ style decoder with Atrous Spatial Pyramid Pooling and C2 low-level feature fusion. |
| Segmentation | resunet_decoder |
ResUNetDecoder |
U-Net style decoder with residual blocks and progressive multi-scale upsampling. |
| Detection | decoupled_anchor_free |
DecoupledAnchorFreeHead |
YOLOX/FCOS style decoupled head with separate classification and box regression branches per level. |
| Detection | query_detection |
QueryDetectionHead |
RT-DETR/D-FINE style transformer decoder head with learnable query embeddings. |
2. Modularity & Dynamic Component Resolution¶
CoreCV does NOT hardcode head and neck choices to specific backbone architectures or fixed channel dimensions. Component resolution and parameter passing are fully dynamic and decoupled.
Internal Resolution Logic (CoreModel._build_model_from_config)¶
Located in src/corecv/api/model.py:
# 1. Resolve Backbone via BACKBONE_REGISTRY
backbone_cls = get_backbone(config["model_name"]) # e.g., "resnet50"
backbone = backbone_cls(pretrained=config.get("pretrained", True))
# 2. Resolve Task-Specific Components via HEAD_REGISTRY / NECK_REGISTRY
if task == "classification":
head_type = config.get("head_type", "linear_classification")
head_cls = get_head(head_type)
# Dynamic signature filtering prevents unexpected keyword argument errors
head_kwargs = CoreModel._filter_kwargs_for_signature(
head_cls, config, extra_keys={"feature_info": backbone.feature_info, "num_classes": num_classes}
)
head = head_cls(**head_kwargs)
model = nn.Sequential(OrderedDict([("backbone", backbone), ("head", head)]))
elif task == "segmentation":
head_type = config.get("head_type", "aspp_decoder")
head_cls = get_head(head_type)
# Dynamic decoder channels override (default 256)
decoder_channels = config.get("decoder_channels", 256)
head_kwargs = CoreModel._filter_kwargs_for_signature(
head_cls, config, extra_keys={"feature_info": backbone.feature_info, "out_channels": decoder_channels, "num_classes": num_classes}
)
head = head_cls(**head_kwargs)
model = nn.Sequential(OrderedDict([("backbone", backbone), ("head", head)]))
elif task == "detection":
neck_type = config.get("neck_type", "fpn")
head_type = config.get("head_type", "decoupled_anchor_free")
# Dynamic neck channels override (default 256)
neck_channels = config.get("neck_channels", config.get("out_channels", 256))
neck_cls = get_neck(neck_type)
neck_kwargs = CoreModel._filter_kwargs_for_signature(
neck_cls, config, extra_keys={"feature_info": backbone.feature_info, "out_channels": neck_channels}
)
neck = neck_cls(**neck_kwargs)
head_cls = get_head(head_type)
head_kwargs = CoreModel._filter_kwargs_for_signature(
head_cls, config, extra_keys={"feature_info": backbone.feature_info, "num_classes": num_classes}
)
head = head_cls(**head_kwargs)
model = CoreObjectDetector(backbone=backbone, neck=neck, head=head)
Flexible Model Initialization Syntaxes¶
CoreModel supports multiple polymorphic initialization styles:
from corecv.api import CoreModel
# Style 1: Plain backbone string name with default components
model = CoreModel("resnet18", task="classification", num_classes=10)
# Style 2: Plain backbone string with direct neck, head, and channel overrides
model_det = CoreModel(
"mobilenet_v3_large",
task="detection",
neck="panet", # Direct registered neck selection
head="query_detection", # Direct registered head selection
neck_channels=128, # Dynamically forwarded keyword argument
num_classes=80,
)
# Style 3: Raw Python configuration dictionary
config_seg = {
"model_name": "convnext_tiny",
"head_type": "resunet_decoder", # Swapped built-in segmentation decoder
"decoder_channels": 128, # Custom decoder channel dimension override
"num_classes": 19,
}
model_seg = CoreModel(config_seg, task="segmentation")
3. Feature Flow: Detection vs. Segmentation¶
Object Detection Feature Flow¶
Input (B, 3, H, W)
│
├─> Backbone (e.g., ResNet50) ──> C2 (stride 4), C3 (stride 8), C4 (stride 16), C5 (stride 32)
│
├─> Neck (FPN or PANet) ──────> Top-down & bottom-up fusion ──> P3, P4, P5 (each 256 channels)
│
└─> Detection Head ────────────> Per feature level (P3, P4, P5):
├── Decoupled Head: cls_logits (B, num_classes, H_i, W_i) & reg_pred (B, 4, H_i, W_i)
└── Query Head: Cross-attention over queries ──> (B, N_queries, 4 + num_classes)
Segmentation Feature Flow¶
Input (B, 3, H, W)
│
├─> Backbone ──> High-res low-level features (C2) + Deep semantic features (C5)
│
└─> Decoder Head:
├── ASPPDecoder: ASPP dilated convs on C5 (dilations 1,6,12,18) + C2 fusion ──> Mask (B, num_classes, H, W)
└── ResUNetDecoder: Progressive upsampling with residual skip connections ──> Mask (B, num_classes, H, W)
4. Extending CoreCV via CoreRegistry¶
CoreCV uses CoreRegistry to provide generic, type-safe decorators with pre-instantiation signature checking.
Registering Custom Backbones¶
import torch
import torch.nn as nn
from corecv.core.contract import BaseBackbone, FeatureInfo
from corecv.core.registry import register_backbone
@register_backbone("CustomResNet")
class CustomResNet(BaseBackbone):
def __init__(self, pretrained: bool = False):
super().__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)
self.feature_info = FeatureInfo(
channels=[64, 128, 256, 512],
strides=[4, 8, 16, 32],
)
def forward(self, x: torch.Tensor) -> list[torch.Tensor]:
return [...]
Registering Custom Necks¶
from corecv.core.registry import register_neck
@register_neck("CustomFPN")
class CustomFPN(nn.Module):
def __init__(self, feature_info, out_channels: int = 256):
super().__init__()
self.laterals = nn.ModuleList([
nn.Conv2d(c, out_channels, 1) for c in feature_info.channels
])
def forward(self, features: list[torch.Tensor]) -> list[torch.Tensor]:
return [lat(f) for lat, f in zip(self.laterals, features)]
Registering Custom Heads¶
from corecv.core.registry import register_head
@register_head("CustomSegHead")
class CustomSegHead(nn.Module):
def __init__(self, feature_info, out_channels: int = 256, num_classes: int = 19):
super().__init__()
self.conv = nn.Conv2d(out_channels, num_classes, kernel_size=1)
def forward(self, features: list[torch.Tensor]) -> torch.Tensor:
return self.conv(features[0])
Static Signature Validation¶
Before building models, CoreRegistry verifies constructor signatures statically: