Single Camera ExampleΒΆ
This example demonstrates a basic camera-only calibration workflow.
use libcalibgeom::{
find_checkerboard, subpix_saddle_point_polynomium, validate_license_with_env,
Calibration, Camera, CameraModelType, ImageDetection, InitializationSettings,
OptimizationSettings, ParameterState, PoseRt, Target,
};
use std::fs;
use std::path::PathBuf;
fn checkerboard_object_points(rows: u32, columns: u32, spacing: f64) -> Vec<f64> {
let mut xyz = Vec::with_capacity((rows * columns * 3) as usize);
for r in 0..rows {
for c in 0..columns {
xyz.push(c as f64 * spacing);
xyz.push(r as f64 * spacing);
xyz.push(0.0);
}
}
xyz
}
fn main() {
let mut calibration = Calibration::new().expect("failed to create calibration");
let licensed = validate_license_with_env();
println!("license_valid: {licensed}");
if !licensed {
return;
}
let rows = 6;
let columns = 7;
let images_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../data/pensylvania_c1");
println!("loading_images_from: {}", images_dir.display());
let mut image_paths: Vec<PathBuf> = match fs::read_dir(&images_dir) {
Ok(entries) => entries
.filter_map(Result::ok)
.map(|entry| entry.path())
.filter(|path| {
path.extension()
.and_then(|ext| ext.to_str())
.is_some_and(|ext| ext.eq_ignore_ascii_case("png"))
})
.collect(),
Err(error) => {
println!("failed_to_read_images_dir: {error}");
return;
}
};
image_paths.sort();
println!("image_file_count: {}", image_paths.len());
if image_paths.is_empty() {
return;
}
let first_image = match image::open(&image_paths[0]) {
Ok(image) => image.to_luma8(),
Err(error) => {
println!("failed_to_open_first_image: {error}");
return;
}
};
let image_width = first_image.width();
let image_height = first_image.height();
let mut camera =
Camera::new(CameraModelType::OpenCV, image_width, image_height, "cam0")
.expect("failed to create camera");
let model_name = camera.model_name().unwrap_or_default();
println!("camera_model: {model_name}");
println!("camera_parameters: {}", camera.parameter_specs().len());
let _ = camera.set_parameter_value("f", 1000.0)
&& camera.set_parameter_value("ar", 1.0)
&& camera.set_parameter_value("cx", 640.0)
&& camera.set_parameter_value("cy", 480.0)
&& camera.set_parameter_value("k1", 0.0)
&& camera.set_parameter_value("k2", 0.0)
&& camera.set_parameter_value("k3", 0.0)
&& camera.set_parameter_value("p1", 0.0)
&& camera.set_parameter_value("p2", 0.0);
let _ = camera.set_parameter_state("f", ParameterState::Free)
&& camera.set_parameter_state("ar", ParameterState::Free)
&& camera.set_parameter_state("cx", ParameterState::Free)
&& camera.set_parameter_state("cy", ParameterState::Free)
&& camera.set_parameter_state("k1", ParameterState::Free)
&& camera.set_parameter_state("k2", ParameterState::Free)
&& camera.set_parameter_state("k3", ParameterState::Free)
&& camera.set_parameter_state("p1", ParameterState::Free)
&& camera.set_parameter_state("p2", ParameterState::Free);
let object_points_xyz = checkerboard_object_points(rows, columns, 0.05);
let mut target = Target::new().expect("failed to create target");
let _ = target.set_object_points_xyz(&object_points_xyz);
calibration.reset();
let target_added = calibration.add_target(&target).is_some();
let camera_added = calibration.add_camera(&camera).is_some();
let configured = target_added && camera_added;
println!("configured: {configured}");
if !configured {
return;
}
println!(
"counts cameras={} targets={} poses={}",
calibration.cameras().len(),
calibration.targets().len(),
calibration.poses().len()
);
let mut image_detections = Vec::new();
let mut pose_id = 0u32;
for image_path in image_paths {
let image = match image::open(&image_path) {
Ok(image) => image.to_luma8(),
Err(error) => {
println!("failed_to_open_image {}: {error}", image_path.display());
continue;
}
};
if image.width() != image_width || image.height() != image_height {
println!(
"skipping_size_mismatch {}: {}x{}",
image_path.display(),
image.width(),
image.height()
);
continue;
}
let Some(points) = find_checkerboard(&image, rows, columns) else {
println!("checkerboard_not_found: {}", image_path.display());
continue;
};
let Some(points) = subpix_saddle_point_polynomium(&image, 5, &points, 50) else {
println!("checkerboard_subpix_failed: {}", image_path.display());
continue;
};
if points.len() != (rows * columns) as usize {
println!(
"checkerboard_size_mismatch {}: got {}",
image_path.display(),
points.len()
);
continue;
}
let Some(added_pose_id) = calibration.add_pose(PoseRt {
rvec: [0.0, 0.0, 0.0],
tvec: [0.0, 0.0, 0.0],
}) else {
println!("failed_to_add_pose");
continue;
};
image_detections.push(ImageDetection {
camera_id: 0,
pose_id: added_pose_id,
target_id: 0,
points,
});
println!("detection[{pose_id}] added: {}", image_path.display());
pose_id += 1;
}
println!("detection_count: {}", image_detections.len());
if image_detections.is_empty() {
return;
}
println!("pose_count: {}", calibration.poses().len());
let initialized = calibration.initialize(
&image_detections,
None,
None,
InitializationSettings::default(),
);
println!(
"initialize_succeeded: {}, status: {:?}",
initialized.succeeded,
initialized.status
);
let optimized = calibration.optimize(
&image_detections,
None,
None,
OptimizationSettings {
max_iterations: 50,
..OptimizationSettings::default()
},
);
println!(
"optimize_succeeded: {}, converged: {}, rpe_rms: {}, status: {:?}",
optimized.succeeded,
optimized.converged,
optimized.rpe_rms,
optimized.status
);
println!(
"reprojection detections={}",
optimized.reprojection_errors.len()
);
println!("{}", calibration.to_string().unwrap_or_default());
}
Use this as the starting point for custom camera model setup and feature ingestion.