spatial-maker 0.3.2

Convert 2D images and videos to stereoscopic 3D spatial content for Apple Vision Pro using AI depth estimation
Documentation
pub mod depth;
pub mod error;
pub mod image_loader;
pub mod model;
pub mod output;
pub mod stereo;
pub mod video;

#[cfg(all(target_os = "macos", feature = "coreml"))]
pub mod depth_coreml;

pub use error::{SpatialError, SpatialResult};
pub use image_loader::load_image;
pub use model::{find_model, get_checkpoint_dir, model_exists};
pub use output::{create_sbs_image, save_stereo_image, ImageEncoding, MVHEVCConfig, OutputFormat, OutputOptions};
pub use stereo::generate_stereo_pair;
pub use video::{get_video_metadata, process_video, ProgressCallback, VideoMetadata, VideoProgress};

#[cfg(all(target_os = "macos", feature = "coreml"))]
pub use depth_coreml::CoreMLDepthEstimator;

#[cfg(feature = "onnx")]
pub use depth::OnnxDepthEstimator;

use std::path::Path;

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct SpatialConfig {
	pub encoder_size: String,
	pub max_disparity: u32,
	pub target_depth_size: u32,
}

pub type StereoOutputFormat = OutputFormat;

impl Default for SpatialConfig {
	fn default() -> Self {
		Self {
			encoder_size: "s".to_string(),
			max_disparity: 30,
			target_depth_size: 518,
		}
	}
}

pub async fn process_photo(
	input_path: &Path,
	output_path: &Path,
	config: SpatialConfig,
	output_options: OutputOptions,
) -> SpatialResult<()> {
	let input_image = load_image(input_path).await?;

	model::ensure_model_exists::<fn(u64, u64)>(&config.encoder_size, None).await?;

	#[cfg(all(target_os = "macos", feature = "coreml"))]
	let depth_map = {
		let model_path = model::find_model(&config.encoder_size)?;
		let model_str = model_path.to_str().ok_or_else(|| {
			SpatialError::ModelError("Invalid model path encoding".to_string())
		})?;
		let estimator = CoreMLDepthEstimator::new(model_str)?;
		estimator.estimate(&input_image)?
	};

	#[cfg(not(all(target_os = "macos", feature = "coreml")))]
	let depth_map = {
		#[cfg(feature = "onnx")]
		{
			let model_path = model::find_model(&config.encoder_size)?;
			let estimator = OnnxDepthEstimator::new(model_path.to_str().unwrap())?;
			estimator.estimate(&input_image)?
		}
		#[cfg(not(feature = "onnx"))]
		{
			return Err(SpatialError::ConfigError(
				"No depth backend enabled. Enable 'coreml' (macOS) or 'onnx' feature.".to_string(),
			));
		}
	};

	let (left, right) = generate_stereo_pair(&input_image, &depth_map, config.max_disparity)?;

	save_stereo_image(&left, &right, output_path, output_options)?;

	Ok(())
}

pub async fn process_video_sbs(
	input_path: &Path,
	output_path: &Path,
	config: SpatialConfig,
	progress_cb: Option<ProgressCallback>,
) -> SpatialResult<()> {
	video::process_video(input_path, output_path, config, progress_cb).await
}