Train models for native camera formats supported by edge platforms
Project description
EdgeFirst CameraAdaptor
Train deep learning models that consume camera formats natively supported by target edge platforms, avoiding costly runtime conversions.
Why CameraAdaptor?
When deploying computer vision models to edge devices, there's often a mismatch between:
- Training data format: RGB images from standard datasets (ImageNet, COCO, etc.)
- Inference input format: Native camera/hardware formats (YUV, Bayer, BGR, RGBA)
Traditional approach: Convert camera output → RGB → Model inference
Problem: This conversion requires hardware (ISP, GPU, 2D accelerator) and adds latency.
Solution: Train the model to expect the native camera format directly.
How It Works
┌─────────────────────────────────────────────────────────────────┐
│ TRAINING TIME │
├─────────────────────────────────────────────────────────────────┤
│ RGB Dataset ─── CameraAdaptorTransform ──→ Target Format │
│ (e.g., YUYV, BGR) │
│ ↓ │
│ Model with CameraAdaptor │
│ as first layer │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ INFERENCE TIME │
├─────────────────────────────────────────────────────────────────┤
│ Camera/Hardware ────────────────────────→ Model (native format)│
│ No conversion needed! │
└─────────────────────────────────────────────────────────────────┘
Key Components
| Component | Purpose |
|---|---|
CameraAdaptorTransform |
Preprocessing: Convert RGB training data to target format |
CameraAdaptor (PyTorch) |
Model layer: Handle format-specific input processing |
CameraAdaptor (TensorFlow) |
Model layer: Handle format-specific input processing |
CameraAdaptorConfig |
Configuration and metadata for model export |
Important Design Note
The CameraAdaptor layer does NOT perform color space conversion. Color conversion
is handled by CameraAdaptorTransform during training data loading:
- Training:
CameraAdaptorTransformconverts RGB images → target format (e.g., YUYV, BGR) - Inference: Camera/ISP provides data directly in target format → no conversion needed
The CameraAdaptor layer only performs:
- Layout permutation (NHWC ↔ NCHW) when
channels_last/channels_firstis enabled - Alpha channel dropping for RGBA/BGRA inputs
EdgeFirst Ecosystem
EdgeFirst CameraAdaptor is part of the EdgeFirst AI ecosystem:
- EdgeFirst HAL: Runtime library with optimized pre-processing pipelines for edge deployment. Use HAL for on-target inference and benchmarking of models trained with CameraAdaptor.
- EdgeFirst CameraAdaptor: Training library (this project) for creating models that accept native camera formats.
On-target benchmarks use edgefirst-hal to benchmark pre-processing pipelines with various CameraAdaptor configurations.
Installation
# Core library (numpy only)
pip install edgefirst-cameraadaptor
# With preprocessing support (OpenCV)
pip install edgefirst-cameraadaptor[transform]
# With PyTorch support
pip install edgefirst-cameraadaptor[torch]
# With TensorFlow support
pip install edgefirst-cameraadaptor[tensorflow]
# With PyTorch Lightning support
pip install edgefirst-cameraadaptor[lightning]
# Everything
pip install edgefirst-cameraadaptor[all]
Quick Start
Preprocessing Transform
Convert training images to your target camera format:
from edgefirst.cameraadaptor import CameraAdaptorTransform
# Create transform for BGR format (RGB source by default)
transform = CameraAdaptorTransform("bgr")
bgr_frame = transform(rgb_frame)
# If using OpenCV's default BGR loading
transform = CameraAdaptorTransform("yuyv", source_format="bgr")
yuyv_frame = transform(bgr_frame) # cv2.imread() returns BGR
PyTorch Model
Add the adaptor as the first layer of your model:
from edgefirst.cameraadaptor.pytorch import CameraAdaptor
import torch.nn as nn
class MyModel(nn.Module):
def __init__(self, adaptor="rgb"):
super().__init__()
self.adaptor = CameraAdaptor(adaptor)
self.backbone = nn.Sequential(
nn.Conv2d(CameraAdaptor.compute_output_channels(adaptor), 64, 3),
# ... rest of your model
)
def forward(self, x):
x = self.adaptor(x)
return self.backbone(x)
# Model for RGBA input (4 channels -> 3 channels after adaptor)
model = MyModel(adaptor="rgba")
TensorFlow/Keras Model
from edgefirst.cameraadaptor.tensorflow import CameraAdaptor
import tensorflow as tf
inputs = tf.keras.Input(shape=(224, 224, 4)) # RGBA input
x = CameraAdaptor("rgba")(inputs) # Drops alpha -> 3 channels
x = tf.keras.layers.Conv2D(64, 3, padding="same")(x)
# ... rest of your model
Channels-Last Input (Camera Pipeline Direct)
For models receiving data directly from camera pipelines in NHWC format:
# PyTorch: accept channels-last input, convert to channels-first internally
adaptor = CameraAdaptor("yuyv", channels_last=True)
x = torch.randn(1, 224, 224, 2) # NHWC from camera
y = adaptor(x) # Output: (1, 2, 224, 224) in NCHW
# TensorFlow: accept channels-first input if needed
from edgefirst.cameraadaptor.tensorflow import CameraAdaptor
layer = CameraAdaptor("yuyv", channels_first=True)
x = tf.random.normal((1, 2, 224, 224)) # NCHW
y = layer(x) # Output: (1, 224, 224, 2) in NHWC
Ultralytics YAML Configuration
# YOLOv8 model with RGBA input
backbone:
- [-1, 1, CameraAdaptor, [rgba]] # First layer
- [-1, 1, Conv, [64, 3, 2]]
# ... rest of backbone
Source Format (Data Loader Compatibility)
Different image loading libraries return different formats:
| Library | Default Format | Transform Setup |
|---|---|---|
| PIL/Pillow | RGB | source_format="rgb" (default) |
| torchvision | RGB | source_format="rgb" (default) |
| OpenCV cv2.imread() | BGR | source_format="bgr" |
| OpenCV cv2.IMREAD_UNCHANGED | BGRA | source_format="bgra" |
| imageio | RGB | source_format="rgb" (default) |
| skimage | RGB | source_format="rgb" (default) |
Important: OpenCV loads images as BGR by default. If you're using cv2.imread() without explicit conversion, set source_format="bgr":
import cv2
from edgefirst.cameraadaptor import CameraAdaptorTransform
# CORRECT: Tell the transform your source is BGR
img = cv2.imread("image.jpg")
transform = CameraAdaptorTransform("yuyv", source_format="bgr")
yuyv = transform(img)
Supported Color Spaces
Currently Supported
| Format | Input Channels | Output Channels | Description |
|---|---|---|---|
| RGB | 3 | 3 | Standard RGB |
| BGR | 3 | 3 | OpenCV native |
| RGBA | 4 | 3 | RGB + alpha (dropped) |
| BGRA | 4 | 3 | BGR + alpha (dropped) |
| YUYV | 2 | 2 | YUV 4:2:2, ch0=Y, ch1=UV |
Planned
- Roadmap: NV12, NV21 (semi-planar YUV 4:2:0)
- Roadmap: Bayer patterns (RGGB, BGGR, GRBG, GBRG)
See FORMATS.md for detailed format documentation.
Platform-Specific Guidance
See PLATFORMS.md for i.MX platform-specific recommendations:
- i.MX 93: PXP outputs BGR - train models with BGR format
- i.MX 8M Plus: G2D outputs RGBA - use RGBA to auto-slice alpha
- i.MX 95: ISI/ISP pipeline considerations
Configuration
Use CameraAdaptorConfig for model metadata:
from edgefirst.cameraadaptor import CameraAdaptorConfig
config = CameraAdaptorConfig(
adaptor="yuyv",
input_dtype="uint8", # For quantized models
output_dtype="uint8",
)
# Embed in model metadata
metadata = config.to_metadata()
PyTorch Lightning Integration
from pytorch_lightning import Trainer
from edgefirst.cameraadaptor.pytorch.lightning import create_callback
callback = create_callback("yuyv")
trainer = Trainer(callbacks=[callback])
Migration from Existing Code
From ultralytics/edgefirst
# Before
from ultralytics.edgefirst.camera.adaptor import CameraAdaptorTransform
from ultralytics.edgefirst.nn.modules import CameraAdaptor
# After
from edgefirst.cameraadaptor import CameraAdaptorTransform
from edgefirst.cameraadaptor.pytorch import CameraAdaptor
From modelpack
# Before
from deepview.modelpack.datasets.color import ColorAdaptor
from deepview.modelpack.layers.conv2d import ColorAdaptor as TFColorAdaptor
# After
from edgefirst.cameraadaptor import CameraAdaptorTransform
from edgefirst.cameraadaptor.tensorflow import CameraAdaptor
API Reference
CameraAdaptorTransform
Preprocessing transform for converting images to target formats.
transform = CameraAdaptorTransform(
adaptor="yuyv", # Target format
source_format="rgb", # Source format (default: "rgb")
)
output = transform(image) # or transform.convert(image)
Parameters:
adaptor: Target color space (str or ColorSpace enum)source_format: Source color space from data loader (str or ColorSpace enum, default: "rgb")
Properties:
adaptor: Target adaptor name (str)source_format: Source format name (str)channels: Output channel countinput_channels: Source format channel countoutput_channels: Channels model backbone receives
CameraAdaptor (PyTorch)
from edgefirst.cameraadaptor.pytorch import CameraAdaptor
adaptor = CameraAdaptor(
adaptor="yuyv", # Target format
channels_last=False, # True for NHWC input
)
output = adaptor(input_tensor)
Parameters:
adaptor: Target color space (str or ColorSpace enum)channels_last: If True, input is NHWC, permuted to NCHW (default: False)
Static Methods:
compute_input_channels(args): Get input channels from YAML argscompute_output_channels(args): Get output channels from YAML args
CameraAdaptor (TensorFlow)
from edgefirst.cameraadaptor.tensorflow import CameraAdaptor
layer = CameraAdaptor(
adaptor="yuyv", # Target format (None for auto-detect)
channels_first=False, # True for NCHW input
)
output = layer(input_tensor)
Parameters:
adaptor: Target color space (str, None for auto-detect)channels_first: If True, input is NCHW, permuted to NHWC (default: False)
CameraAdaptorConfig
from edgefirst.cameraadaptor import CameraAdaptorConfig
config = CameraAdaptorConfig(
adaptor="yuyv",
input_dtype="float32",
output_dtype="float32",
)
Properties:
input_channels: Input channel countoutput_channels: Output channel countis_quantized: Whether config uses quantized dtypes
Methods:
to_dict(): Convert to dictionaryto_metadata(): Convert to model metadata formatfrom_dict(data): Create from dictionaryfrom_metadata(metadata): Create from model metadata
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file edgefirst_cameraadaptor-0.1.0.tar.gz.
File metadata
- Download URL: edgefirst_cameraadaptor-0.1.0.tar.gz
- Upload date:
- Size: 34.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
278ca5e21acc67fe201e78a938a45ebad4076003784e8aa4a2141e942c37a525
|
|
| MD5 |
cfacb1b4a3efe409f1fff5a2421aa169
|
|
| BLAKE2b-256 |
f67298d9f58234f883d5f711f2a04ff76fa64d83e28fa6ea6e82211a2078072e
|
File details
Details for the file edgefirst_cameraadaptor-0.1.0-py3-none-any.whl.
File metadata
- Download URL: edgefirst_cameraadaptor-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd4171726bfc40fbff29e4a48f8a3cade1bf661efd54ab68faf4fc6cc1ddd867
|
|
| MD5 |
2f20cb043a256fae89f41f2c1b734be2
|
|
| BLAKE2b-256 |
df69e07cf1e8c9948b765ee4eedc74533db2e5ec7e93834eb4112428a0638dcd
|