Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/core/src/ar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
//! | [`math`] | `m*.c` (16 files), `v*.c` (6 files) | `ARMat` / `ARVec` linear algebra: inversion, QR, PCA, quaternion conversion |
//! | [`matrix`] | `arGetMatrixCode.c` | Matrix-code (barcode) marker decoding and ECC |
//! | [`param`] | `param*.c` (10 files) | Camera intrinsic parameters: `ARParam`, `ARParamLTf`, lens-distortion helpers |
//! | [`param_gl`] | `paramGL.c` | OpenGL projection helpers: converts `ARParam` to a right-handed frustum matrix |
//! | [`pattern`] | `arPatt*.c` | Pattern template loading, normalisation, and ID matching |
//! | [`pose`] | `ar3DCreateHandle.c`, `arGetTransMat.c`, `arGetTransMatStereo.c` | 3-D pose estimation from marker corners; wraps into `AR3DHandle` |
//!
Expand All @@ -73,5 +74,6 @@ pub mod marker;
pub mod math;
pub mod matrix;
pub mod param;
pub mod param_gl;
pub mod pattern;
pub mod pose;
141 changes: 141 additions & 0 deletions crates/core/src/ar/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,75 @@ use crate::types::ARParam;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{self, Read};

/// Resize camera parameters to a new image size **in place**.
///
/// C equivalent: `arParamChangeSize`
///
/// Scales rows 0 and 1 of `param.mat` by `xsize / param.xsize` and
/// `ysize / param.ysize` respectively, then updates `param.xsize` and
/// `param.ysize`. This preserves the principal-point and focal-length
/// ratios when the camera is used with an image of a different resolution.
///
/// This matches the C calling convention where source and destination are
/// the same pointer:
/// ```c
/// arParamChangeSize(&cparam, width, height, &cparam);
/// ```
///
/// # Arguments
///
/// * `param` — Camera parameters to resize (mutated in place).
/// * `xsize` — New image width in pixels.
/// * `ysize` — New image height in pixels.
///
/// # Errors
///
/// Returns `Err` if `param.xsize` or `param.ysize` is zero before the
/// resize (i.e. the parameter was never initialised).
///
/// # Example
///
/// ```rust,no_run
/// use webarkitlib_rs::ar::param::ar_param_change_size;
/// use webarkitlib_rs::types::ARParam;
///
/// let mut cparam = ARParam::default();
/// cparam.xsize = 640;
/// cparam.ysize = 480;
/// cparam.mat[0][0] = 700.0; // fx
/// cparam.mat[1][1] = 700.0; // fy
///
/// ar_param_change_size(&mut cparam, 1280, 960).unwrap();
/// assert_eq!(cparam.xsize, 1280);
/// assert_eq!(cparam.ysize, 960);
/// ```
pub fn ar_param_change_size(
param: &mut ARParam,
xsize: i32,
ysize: i32,
) -> Result<(), &'static str> {
if param.xsize == 0 || param.ysize == 0 {
arlog_e!(
"ar_param_change_size: source ARParam has zero image dimensions ({}x{})",
param.xsize,
param.ysize
);
return Err("ar_param_change_size: source ARParam has zero image dimensions");
}

let sx = xsize as f64 / param.xsize as f64;
let sy = ysize as f64 / param.ysize as f64;

for col in 0..4 {
param.mat[0][col] *= sx;
param.mat[1][col] *= sy;
}
param.xsize = xsize;
param.ysize = ysize;

Ok(())
}

impl ARParam {
/// Load ARParam from a byte stream (Endian-safe cross-platform BigEndian deserialization)
#[allow(clippy::field_reassign_with_default)]
Expand Down Expand Up @@ -174,4 +243,76 @@ mod tests {
assert_eq!(param.dist_factor[2], 20.0);
assert_eq!(param.dist_factor[3], 10.0);
}

#[test]
fn test_ar_param_change_size_doubles_resolution() {
let mut src = ARParam::default();
src.xsize = 640;
src.ysize = 480;
// fx, skew=0, cx, tx
src.mat[0] = [700.0, 0.0, 320.0, 0.0];
// 0, fy, cy, ty
src.mat[1] = [0.0, 700.0, 240.0, 0.0];
// 0, 0, 1, 0
src.mat[2] = [0.0, 0.0, 1.0, 0.0];

let original_mat2 = src.mat[2];
let original_dist = src.dist_factor;

ar_param_change_size(&mut src, 1280, 960).unwrap();

assert_eq!(src.xsize, 1280);
assert_eq!(src.ysize, 960);
// Row 0 scaled by 2x
assert!((src.mat[0][0] - 1400.0).abs() < 1e-9, "fx should double");
assert!((src.mat[0][2] - 640.0).abs() < 1e-9, "cx should double");
// Row 1 scaled by 2x
assert!((src.mat[1][1] - 1400.0).abs() < 1e-9, "fy should double");
assert!((src.mat[1][2] - 480.0).abs() < 1e-9, "cy should double");
// Row 2 unchanged
assert_eq!(src.mat[2], original_mat2);
// Distortion factors unchanged
assert_eq!(src.dist_factor, original_dist);
}

#[test]
fn test_ar_param_change_size_halves_resolution() {
let mut src = ARParam::default();
src.xsize = 1280;
src.ysize = 960;
src.mat[0] = [1400.0, 0.0, 640.0, 0.0];
src.mat[1] = [0.0, 1400.0, 480.0, 0.0];
src.mat[2] = [0.0, 0.0, 1.0, 0.0];

ar_param_change_size(&mut src, 640, 480).unwrap();

assert_eq!(src.xsize, 640);
assert_eq!(src.ysize, 480);
assert!((src.mat[0][0] - 700.0).abs() < 1e-9);
assert!((src.mat[1][1] - 700.0).abs() < 1e-9);
}

#[test]
fn test_ar_param_change_size_identity() {
let mut src = ARParam::default();
src.xsize = 640;
src.ysize = 480;
src.mat[0] = [700.0, 0.0, 320.0, 0.0];
src.mat[1] = [0.0, 700.0, 240.0, 0.0];
src.mat[2] = [0.0, 0.0, 1.0, 0.0];

let original_mat = src.mat;

ar_param_change_size(&mut src, 640, 480).unwrap();

assert_eq!(src.mat, original_mat);
assert_eq!(src.xsize, 640);
assert_eq!(src.ysize, 480);
}

#[test]
fn test_ar_param_change_size_zero_src_dims_returns_err() {
let mut src = ARParam::default(); // xsize = ysize = 0
assert!(ar_param_change_size(&mut src, 640, 480).is_err());
}
}
Loading
Loading