Targetless lidar-camera calibration from a rosbag you already have
No checkerboards. No scripted drive. No site visit. Upload a recording of normal operation and get extrinsics back with a confidence score per sensor. Four API calls; a job typically takes 15–25 minutes.
Every field named here is in the result payload. See the quickstart.
Three steps, one afternoon
Check the bag locally
Run the free deepen-bag-check CLI on your machine. It reports which calibration types the recording qualifies for, and what is missing when it does not. Nothing leaves your network.
Send it to the API
Upload the recording and start a job. It runs async, so you poll instead of holding a connection. In-VPC deployment via AWS Marketplace is coming.
Read the extrinsics
Roll, pitch, yaw and translation per sensor pair, each with a confidence value, quality flags, and a link to a before/after overlay report.
Why targetless, and how it compares
Every route to lidar-camera extrinsic calibration asks something of you. Here is what each one asks.
| Deepen Calibrate | Choreographed-capture services | Target-based tools | DIY open-source toolboxes | |
|---|---|---|---|---|
| Capture required | Existing recording of normal operation | Prescribed maneuver (published guides specify a figure-8 near textured structure) | A capture session with the board in view of every sensor | Depends on the module; usually a target session |
| Physical targets | None | None | Checkerboard, ChArUco or AprilGrid required | Usually required |
| Turnaround | 15–25 minutes, async job | Vendor-published cloud path is human-reviewed, about one business day | Local run, plus target setup time | Local run, plus integration time |
| Delivery | REST API, 4 calls, JSON result | File package after a contact-form intake | CLI on your machine | Source you build and wire yourself |
| Failure handling | Structured error, credit refunded automatically | Not published | Credits deducted on success only (per public pricing) | Open an issue |
| What the result reports | Per-sensor confidence, quality flags, error stats, before/after overlay report | Not published | Per vendor docs | Per tool |
Columns describe categories of multi-sensor calibration software, not specific vendors. Verified against public vendor documentation at time of writing.
Can you calibrate lidar and camera without a target?
Yes. Deepen Calibrate estimates lidar-camera extrinsics from a recording of normal driving or operation. No checkerboard, no AprilGrid, no calibration bay. Run the free CLI first to confirm the recording has enough motion and coverage.
How do I calibrate sensors from a rosbag?
Four calls. POST the .bag, .db3 or .mcap to /calibration/bags, POST to /validate with a calibration type, POST to /calibrations to start the job, then GET /result. The job is async; poll until it reports succeeded.
What happens if the calibration fails?
You get a structured error naming the reason, and the credit for that run is refunded automatically. Running deepen-bag-check locally beforehand catches most causes before you spend anything.
Which sensors and formats are supported?
ROS1 .bag, ROS2 .db3 and .mcap. Velodyne, Ouster, Hesai and RoboSense lidar, including raw Hesai packets. Raw and compressed camera topics, standard IMU messages, CAN vehicle speed. LiDAR-Camera and Multi-LiDAR run through the API today.
Two ways to run it, one engine
Both paths run the same validator and return the same result schema. Only the location changes.
Hosted API
Upload once. We run the job and return extrinsics plus a quality report. Nothing to provision.
- One key, no infrastructure on your side
- Same validated pipeline as the in-VPC path
- First result the same session you sign up
AWS Marketplace — runs in your VPC
The engine runs in your AWS account against your own S3 bucket and compute. Built for data-residency and export-control reviews.
- Runs entirely inside your own AWS account
- Fits data-residency and export-control requirements
- Same calibration engine, same result format
Works with the rosbags you already record
Same reader on both sides: the CLI and the API parse identical formats, and neither needs ROS installed.
Container formats
ROS1 .bag · ROS2 .db3 · ROS2 .mcap
Lidar
Velodyne, Ouster, Hesai, RoboSense point clouds — plus native support for raw Hesai packet data
Camera
Raw and compressed (JPEG/PNG) image topics
IMU
Standard IMU messages, used for motion excitation and initial extrinsics
Vehicle
CAN / vehicle-speed data for lidar–vehicle calibration
Calibration types
LiDAR–Camera and Multi-LiDAR run through the API today. The CLI also validates LiDAR–IMU and LiDAR–Vehicle bags. Ask us to run those.
Topics are classified by message type, not by name, so your existing driver or stack naming convention works as-is.
From upload to extrinsics: four API calls
The same checks deepen-bag-check runs locally are what validate your bag server-side — nothing is re-checked differently.
Multipart upload. The file is stored, not inspected, until you validate it.
curl -X POST https://selfserve.calibrate.deepen.ai/calibration/bags \
-H "X-Api-Key: $DEEPEN_API_KEY" \
-F "file=@drive.mcap"
# -> 201 Created
{
"bag_id": "bag_8f2c1a",
"status": "uploaded",
"size_bytes": 4831201280,
"container_format": null,
"created_at": "2026-07-28T18:04:11Z"
}
Returns the calibration types this bag is eligible for, and the reason each ineligible type failed.
curl -X POST "https://selfserve.calibrate.deepen.ai/calibration/bags/bag_8f2c1a/validate?calibration_type=lidar_camera" \
-H "X-Api-Key: $DEEPEN_API_KEY"
# -> 200 OK (trimmed)
{
"bag_id": "bag_8f2c1a",
"status": "warnings",
"container_format": "ros2_mcap",
"eligible_calibration_types": ["lidar_camera", "multi_lidar"],
"ineligible_calibration_types": [
{ "type": "lidar_vehicle", "reason": "no CAN/vehicle-speed topic found" }
]
}
Start a job for an eligible type. It runs async and returns a job id.
curl -X POST https://selfserve.calibrate.deepen.ai/calibration/bags/bag_8f2c1a/calibrations \
-H "X-Api-Key: $DEEPEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"calibration_type": "lidar_camera"}'
# -> 202 Accepted
{
"calibration_id": "calib_5b19",
"status": "queued",
"calibration_type": "lidar_camera",
"created_at": "2026-07-28T18:06:02Z"
}
Poll until succeeded, then read extrinsics, confidence, quality flags, error stats and a report link per sensor.
curl https://selfserve.calibrate.deepen.ai/calibration/calibrations/calib_5b19/result \
-H "X-Api-Key: $DEEPEN_API_KEY"
# -> 200 OK (trimmed)
{
"schema_version": "1.0",
"calibration_id": "calib_5b19",
"status": "succeeded",
"extrinsic_camera_coordinate_system": "ROS_REP_103",
"results": {
"camera_front_wide": {
"extrinsic_parameters": {
"roll": 0.31, "pitch": -1.02, "yaw": 89.87,
"px": 0.14, "py": -0.02, "pz": 1.31
},
"reference_frame": "lidar_roof",
"confidence": 0.94
}
},
"quality_flags": []
}
These calls run against the live API. Swap in the key you get at checkout. A failed job returns a structured error and refunds the credit automatically — you do not pay for a run that produced no extrinsics.
Check eligibility before you pay
deepen-bag-check reads your .bag, .db3 or .mcap on your own machine. No upload, no account, no ROS install. It is the same validator that gates every paid job, so a pass here is a pass there.
# install (pip install works too, inside a venv) pipx install deepen-bag-check # check a bag against a calibration type deepen-bag-check drive.mcap --for lidar-camera
Open source, Apache-2.0 — view the source on GitHub.
deepen-bag-check 1.0.0 — drive.mcap container: ros2_mcap status: WARNINGS (exit code 1) Topics: /sensor/camera/front_wide/image/compressed [camera_compressed] 20.0 Hz /sensor/lidar/roof/points [lidar] 10.1 Hz vendor=hesai Checks: [WARN] camera_info_present: no paired CameraInfo topic [PASS] pointcloud_field_schema: fields map cleanly (vendor=hesai) [PASS] motion_excitation: sufficient rotational excitation Eligible calibration types: lidar_camera, multi_lidar
What it checks
- Container format & schema — catches a corrupt or unsupported file before it becomes a confusing failure downstream
- Topic classification by message type, never by name
- Per-vendor point cloud field mapping, so mismatched intensity or ring fields don't surprise you later
- Camera intrinsics presence, sensor sync, coverage, and motion excitation
- Machine-readable JSON output for CI, or a human-readable report for a quick look
Start with a bag you already have
Check it free with the CLI. If it qualifies, one API key gets you extrinsics the same session.
Pay per calibration, or per month
Card checkout issues your API key immediately. Failed jobs are refunded as credits.
Standard calibration
Up to 2 sensors, recordings up to 10 GB. No commitment.
- Pay per completed job, no monthly commitment
- Re-run after every sensor swap or knock
- Same hosted API, same result schema
Complex calibration
Multi-lidar and multi-camera rigs, larger recordings. No commitment.
- Pay per completed job, no monthly commitment
- Covers larger sensor counts & recording sizes
- Same hosted API, same result schema
Unlimited monthly
Flat rate, priority processing, direct line to the team. Built for bring-up and integration months.
- Flat monthly rate, no per-job billing
- No per-job accounting during bring-up
- Priority processing and a direct line to the team
Enterprise & AWS Marketplace
Private offers, volume terms, and in-VPC deployment billed through your existing AWS account.
- Private offers & volume pricing
- In-your-VPC deployment (AWS Marketplace)Coming soon
- Procurement through your existing AWS bill
Standard vs. complex is based on sensor count and recording size at submission time — exact thresholds are confirmed at checkout.
Payments via Stripe. Checkout is handled by Stripe — Deepen never sees or stores your card details.
Per-customer API keys. API access is scoped to a key issued to your account, not shared credentials.
Data handling by path. Hosted API: your recording is uploaded to Deepen for processing. AWS Marketplace (in-VPC): your data never leaves your AWS account.
Tell us about your rig
Sensors, rig type, recording format, and which path you want. We reply with access details.