API Reference
This section provides detailed documentation for all Planet Ruler modules and functions.
Core Modules
Geometry Module
- planet_ruler.geometry.horizon_distance(r, h)[source]
Estimate the distance to the horizon (limb) given a height and radius.
- planet_ruler.geometry.limb_camera_angle(r, h)[source]
The angle the camera must tilt in theta_x or theta_y to center the limb. Complement of theta (angle of limb down from the x-y plane).
- planet_ruler.geometry.focal_length(w, fov)[source]
The size of the CCD (inferred) based on focal length and field of view.
- planet_ruler.geometry.detector_size(f, fov)[source]
The size of the CCD (inferred) based on focal length and field of view.
- planet_ruler.geometry.field_of_view(f, w)[source]
The size of the CCD (inferred) based on focal length and field of view.
- planet_ruler.geometry.intrinsic_transform(camera_coords, f=1, px=1, py=1, x0=0, y0=0)[source]
Transform from camera coordinates into image coordinates.
- Parameters:
camera_coords (np.ndarray) – Coordinates of the limb in camera space. Array has Nx4 shape where N is the number of x-axis pixels in the image.
f (float) – Focal length of the camera (m).
px (float) – The scale of x pixels.
py (float) – The scale of y pixels.
x0 (float) – The x-axis principle point (should be center of image in pixel coordinates).
y0 (float) – The y-axis principle point. (should be center of image in pixel coordinates).
- Returns:
Coordinates in image space.
- Return type:
pixel_coords (np.ndarray)
- planet_ruler.geometry.extrinsic_transform(world_coords, theta_x=0, theta_y=0, theta_z=0, origin_x=0, origin_y=0, origin_z=0)[source]
Transform from world coordinates into camera coordinates. Note that for a limb calculation we will define origin_x/y/z as the camera position – these should all be set to zero.
- Parameters:
world_coords (np.ndarray) – Coordinates of the limb in the world. Array has Nx4 shape where N is the number of x-axis pixels in the image.
theta_x (float) – Rotation around the x (horizontal lateral) axis, AKA pitch — tilts the camera up/down. (radians)
theta_y (float) – Rotation around the y (toward-limb) axis, AKA roll. When theta_z=0, acts as a pure phase shift in φ with no effect on the projected arc shape. When theta_z≠0, the z-rotation breaks that symmetry and theta_y has a genuine effect on the arc. (radians)
theta_z (float) – Rotation around the z (vertical) axis, AKA yaw. Use theta_z=π for the physically correct ∪-shaped horizon arc (near limb visible, more planet at image center). (radians)
origin_x (float) – Horizontal offset from the object in question to the camera (m).
origin_y (float) – Distance from the object in question to the camera (m).
origin_z (float) – Height difference from the object in question to the camera (m).
- Returns:
Coordinates in camera space.
- Return type:
camera_coords (np.ndarray)
- planet_ruler.geometry.limb_arc_sample(r, n_pix_x, n_pix_y, h=1, f=None, fov=None, w=None, x0=0, y0=0, theta_x=0, theta_y=0, theta_z=0, origin_x=0, origin_y=0, origin_z=0, return_full=False, num_sample=5000)[source]
Calculate the limb orientation in an image given the physical parameters of the system.
- Parameters:
n_pix_x (int) – Width of image (pixels).
n_pix_y (int) – Height of image (pixels).
r (float) – Radius of the body in question.
h (float) – Height above surface (units should match radius).
f (float) – Focal length of the camera (m).
fov (float) – Field of view, assuming square (degrees).
w (float) – detector size (float): Width of CCD (m).
x0 (float) – The x-axis principle point.
y0 (float) – The y-axis principle point.
theta_x (float) – Rotation around the x (horizontal lateral) axis, AKA pitch — tilts the camera up/down. (radians)
theta_y (float) – Rotation around the y (toward-limb) axis, AKA roll. When theta_z=0, acts as a pure phase shift in φ with no effect on the projected arc shape. When theta_z≠0, the z-rotation breaks that symmetry and theta_y has a genuine effect on the arc. (radians)
theta_z (float) – Rotation around the z (vertical) axis, AKA yaw. Use theta_z=π for the physically correct ∪-shaped horizon arc (near limb visible, more planet at image center). (radians)
origin_x (float) – Horizontal offset from the object in question to the camera (m).
origin_y (float) – Distance from the object in question to the camera (m).
origin_z (float) – Height difference from the object in question to the camera (m).
return_full (bool) – Return both the x and y coordinates of the limb in camera space. Note these will not be interpolated back on to the pixel grid.
num_sample (
int) – The number of points sampled from the simulated limb – will be interpolated onto pixel grid. [default 1000]
- Return type:
- planet_ruler.geometry.get_rotation_matrix(theta_x, theta_y, theta_z)[source]
Compute combined rotation matrix from Euler angles. Extracted from extrinsic_transform for reuse.
- Returns:
3x3 rotation matrix
- Return type:
R
- planet_ruler.geometry.limb_arc(r, n_pix_x, n_pix_y, h=1, f=None, fov=None, w=None, x0=0, y0=0, theta_x=0, theta_y=0, theta_z=0, origin_x=0, origin_y=0, origin_z=0, return_full=False, x_coords=None, **kwargs)[source]
Calculate limb position analytically at each pixel x-coordinate.
No sampling or interpolation - directly solves for phi at each column. This eliminates edge artifacts and is sometimes faster than sampling methods.
Mathematical approach: 1. Limb is parameterized by angle phi around circle 2. For each x_pixel, solve: x_pixel = f(phi) for phi 3. This reduces to: a·cos(phi) + b·sin(phi) = c 4. Standard analytical solution exists!
- Parameters:
n_pix_x (int) – Width of image (pixels).
n_pix_y (int) – Height of image (pixels).
r (float) – Radius of the body in question.
h (float) – Height above surface (units should match radius).
f (float) – Focal length of the camera (m).
fov (float) – Field of view, assuming square (degrees).
w (float) – detector size (float): Width of CCD (m).
x0 (float) – The x-axis principle point.
y0 (float) – The y-axis principle point.
theta_x (float) – Rotation around the x (horizontal lateral) axis, AKA pitch — tilts the camera up/down. (radians)
theta_y (float) – Rotation around the y (toward-limb) axis, AKA roll. When theta_z=0, acts as a pure phase shift in φ with no effect on the projected arc shape. When theta_z≠0, the z-rotation breaks that symmetry and theta_y has a genuine effect on the arc. (radians)
theta_z (float) – Rotation around the z (vertical) axis, AKA yaw. Use theta_z=π for the physically correct ∪-shaped horizon arc (near limb visible, more planet at image center). (radians)
origin_x (float) – Horizontal offset from the object in question to the camera (m).
origin_y (float) – Distance from the object in question to the camera (m).
origin_z (float) – Height difference from the object in question to the camera (m).
return_full (bool) – Return both the x and y coordinates of the limb in camera space. Note these will not be interpolated back on to the pixel grid.
x_coords (
Optional[ndarray]) – Optional array of x-coordinates to compute (default: all pixels). For sparse computation (e.g., manual annotation), pass only the x-coordinates where you have data. Dramatically speeds up fitting when only a few points are annotated.
- Returns:
- Array of y-coordinates for each x-pixel column
Length matches len(x_coords) if provided, else n_pix_x
- Return type:
y_pixel
- planet_ruler.geometry.limb_arc_sagitta(u, theta_x, f_px, r, h)[source]
Exact projected sagitta of the planetary limb arc at pixel offset u.
- Derivation (perspective geometry, theta_y=0, theta_z=pi for ∪-arc):
The camera is at altitude h above a sphere of radius r. In camera coordinates the visible horizon is the set of tangent points satisfying X·r̂ = r²/(r+h). After rotation R_x(theta_x)·R_z(pi) the horizon circle projects as a conic; solving the x-pixel equation for the azimuth angle phi and back-substituting for the y-pixel yields the closed form below.
The formula is verified to machine precision against limb_arc() for theta_x ∈ {-alpha, 0, alpha, 2*alpha} and is invariant to theta_y (verified to 2.7e-7 px residuals for theta_y ∈ {0, 0.01, 0.05, 0.1}).
- At theta_x=0 this reduces exactly to
s(u) = kappa * (sqrt(f_px**2 + u**2) - f_px)
where kappa = sqrt(h*(2r+h)) / r = 1/K. Consequently, the OLS fit s = s0 - c*A(u) (A = sqrt(f²+u²) - f) recovers K = 1/|c| with zero residual — the fundamental reason the hyperbola model is exact at theta_x=0.
- Parameters:
u (
ndarray) – Horizontal pixel offsets from the image centre (u = x - x0).theta_x (
float) – Camera tilt in radians (0 = horizontal, positive = looking down toward the horizon).f_px (
float) – Focal length in pixels (f_mm / sensor_width_mm * n_pix_x).r (
float) – Planetary radius [m].h (
float) – Camera altitude above surface [m].
- Return type:
- Returns:
Sagitta at each u [pixels]. Positive values are below the arc apex for ∪-arcs (theta_z=pi).
- Exact formulas:
kappa = sqrt(h * (2*r + h)) / r g(u) = sqrt(f_px**2 + u**2 * (cos(theta_x)**2 - kappa**2*sin(theta_x)**2)) s(u) = (f_px**2 + u**2*cos(theta_x)*(cos(theta_x) + kappa*sin(theta_x)) - f_px*g(u))
/ ((f_px*sin(theta_x) + cos(theta_x)*g(u)/kappa) * (kappa*sin(theta_x) + cos(theta_x)))
The geometry module contains fundamental mathematical functions for planetary calculations:
Horizon calculations: horizon_distance, limb_camera_angle
Camera optics: focal_length, detector_size, field_of_view
Coordinate transforms: intrinsic_transform, extrinsic_transform
Limb arc modeling: limb_arc, limb_arc_sample - generates theoretical limb curves
Rotation matrices: get_rotation_matrix - Euler angle to rotation matrix conversion
Image Processing Module
- planet_ruler.image.load_image(filepath)[source]
Load a 3 or 4-channel image from filepath into an array.
- planet_ruler.image.directional_gradient_blur(image, sigma_base=2.0, streak_length=20, decay_rate=0.2, normalize_gradients=True)[source]
Blur along gradient directions to create smooth ‘streaks’ toward edges.
For each pixel, follow its gradient direction outward, averaging pixels along that ray with exponential decay. This: - Smooths noisy/weak gradients (striations with inconsistent directions) - Strengthens coherent gradients (limb where all gradients align) - Creates smooth ‘valleys’ guiding optimizer toward strong edges
- Parameters:
image (np.ndarray) – Input image (H x W or H x W x 3)
sigma_base (float) – Initial gradient smoothing for direction estimation
streak_length (int) – How far to follow gradient direction (pixels)
decay_rate (float) – Exponential decay rate (higher = faster decay)
normalize_gradients (bool) –
True: Use unit vectors (direction only) - RECOMMENDED All pixels sample same distance regardless of gradient strength. Consistent, predictable behavior.
False: Use full gradient magnitude as direction Strong gradients sample further. Can create artifacts.
- Returns:
Blurred gradient magnitude field grad_angle (np.ndarray): Gradient angle field
- Return type:
blurred_grad_mag (np.ndarray)
- Note: This is UNI-directional (samples in one direction only).
Use bidirectional_gradient_blur() to preserve peak locations.
- planet_ruler.image.bilinear_interpolate(array, y, x)[source]
Bilinear interpolation for 2D array at non-integer coordinates.
- Parameters:
array – 2D array to sample from
y – Arrays of y and x coordinates (can be non-integer)
x – Arrays of y and x coordinates (can be non-integer)
- Returns:
Interpolated values at (y, x) positions
- planet_ruler.image.bidirectional_gradient_blur(image, sigma_base=2.0, streak_length=20, decay_rate=0.2, normalize_gradients=True)[source]
Blur in BOTH directions along gradient (forward and backward).
This creates symmetric streaks on both sides of edges, preserving the location of gradient maxima while smoothing the field.
- Parameters:
image – Input image
sigma_base – Initial smoothing for gradient estimation
streak_length – How far to sample in each direction
decay_rate – Exponential decay (higher = faster falloff)
normalize_gradients – Use unit vectors (direction only) vs full magnitude
- Returns:
Smoothed gradient magnitude field grad_angle: Original gradient angle field
- Return type:
blurred_mag
- planet_ruler.image.gradient_break(im_arr, log=False, y_min=0, y_max=-1, window_length=None, polyorder=1, deriv=0, delta=1)[source]
Scan each vertical line of an image for the maximum change in brightness gradient – usually corresponds to a horizon.
- Parameters:
im_arr (np.ndarray) – Image array.
log (bool) – Use the log(gradient). Sometimes good for smoothing.
y_min (int) – Minimum y-position to consider.
y_max (int) – Maximum y-position to consider.
window_length (int) – Width of window to apply smoothing for each vertical. Larger means less noise but less sensitivity.
polyorder (int) – Polynomial order for smoothing.
deriv (int) – Derivative level for smoothing.
delta (int) – Delta for smoothing.
- Returns:
image array (np.ndarray)
- class planet_ruler.image.MaskSegmenter(image, method='sam', downsample_factor=1, interactive=True, **backend_kwargs)[source]
Bases:
objectMethod-agnostic mask-based segmentation.
Supports pluggable backends (SAM, custom algorithms, etc.) with optional downsampling and interactive classification.
- class planet_ruler.image.SegmentationBackend[source]
Bases:
objectBase class for segmentation backends.
- class planet_ruler.image.SAMBackend(model_size='vit_b')[source]
Bases:
SegmentationBackendSegment Anything Model backend.
- class planet_ruler.image.CustomBackend(segment_fn)[source]
Bases:
SegmentationBackendCustom user-provided segmentation backend.
- planet_ruler.image.smooth_limb(y, method='rolling-median', window_length=50, polyorder=1, deriv=0, delta=1)[source]
Smooth the limb position values.
- Parameters:
y (np.ndarray) – Y-locations of the string for each column.
method (str) – Smoothing method. Must be one of [‘bin-interpolate’, ‘savgol’, ‘rolling-mean’, ‘rolling-median’].
window_length (int) – The length of the filter window (i.e., the number of coefficients). If mode is ‘interp’, window_length must be less than or equal to the size of x.
polyorder (float) – The order of the polynomial used to fit the samples. polyorder must be less than window_length.
deriv (int) – The order of the derivative to compute. This must be a non-negative integer. The default is 0, which means to filter the data without differentiating.
delta (int) – The spacing of the samples to which the filter will be applied. This is only used if deriv > 0. Default is 1.0.
- Returns:
Y-locations of the smoothed string for each column.
- Return type:
position (np.ndarray)
- planet_ruler.image.fill_nans(limb)[source]
Fill NaNs for the limb position values.
- Parameters:
limb (np.ndarray) – Y-locations of the limb on the image.
- Returns:
Y-locations of the limb on the image.
- Return type:
limb (np.ndarray)
- planet_ruler.image.gradient_field(image, kernel_smoothing=5.0, directional_smoothing=50, directional_decay_rate=0.15)[source]
Pre-compute gradient field AND its derivatives for fast sub-pixel interpolation. Uses directional blur for enhanced edge detection.
- Parameters:
image (
ndarray) – Input imagekernel_smoothing (
float) – Sigma for initial gradient direction estimationdirectional_smoothing (
int) – Distance to sample in each direction (±pixels). Set to 0 to bypass directional smoothing entirely.directional_decay_rate (
float) – Exponential decay rate for directional blur
- Returns:
- Dictionary containing gradient field components:
grad_mag: Gradient magnitude array
grad_angle: Gradient angle array
grad_sin: Sin of gradient angle (for interpolation)
grad_cos: Cos of gradient angle (for interpolation)
grad_mag_dy, grad_mag_dx: Derivatives of gradient magnitude
grad_sin_dy, grad_sin_dx: Derivatives of sine component
grad_cos_dy, grad_cos_dx: Derivatives of cosine component
image_height, image_width: Dimensions
- Return type:
The image module handles computer vision tasks:
Image loading: load_image - loads and validates image files
Gradient-field detection: gradient_field - automated limb detection using directional blur and flux analysis
Legacy detection: gradient_break - simpler gradient-based detection
Image segmentation: MaskSegmenter class with pluggable backends including Segment Anything integration (optional)
Limb processing: smooth_limb, fill_nans - post-processing operations
Interpolation: bilinear_interpolate - sub-pixel image sampling
Crop Module
Image Cropping Tool for Planet Ruler
Allows users to select a rectangular region to crop from a horizon image, automatically adjusting camera parameters (detector size, principal point, dimensions).
- class planet_ruler.crop.TkImageCropper(image_path, initial_parameters=None, initial_zoom=None)[source]
Bases:
objectTkinter-based interactive tool for cropping images with automatic parameter scaling.
Features: - Drag to select rectangular crop region - Zoom with scroll wheel - Scrollable canvas for navigation - Automatic parameter scaling for detector_size (w), principal point (x0, y0), and dimensions - Save cropped image and adjusted parameters
- calculate_scaled_parameters()[source]
Calculate scaled parameters after cropping.
- Return type:
- Returns:
Dictionary of scaled parameters
- planet_ruler.crop.crop_observation_image(image_path, initial_parameters)[source]
Interactively crop an observation image and get scaled parameters.
- Parameters:
- Return type:
- Returns:
Tuple of (cropped_image, scaled_parameters, crop_bounds) crop_bounds is (x1, y1, x2, y2) in original coordinates
The crop module provides interactive image cropping with automatic camera parameter scaling:
Interactive cropping: TkImageCropper - GUI tool for selecting crop regions
Parameter scaling: Automatic adjustment of detector size, principal point, and FOV
Convenience function: crop_observation_image - high-level interface
Validation: Ensures geometric consistency after cropping
Key features:
Drag-to-select rectangular crop regions
Zoom/pan for precise selection
Automatic scaling of physical sensor dimensions
Handles out-of-bounds principal points correctly
Preserves pixel physical dimensions
The crop operation scales camera parameters using the following rules:
detector_width_new = detector_width_old × (crop_width / image_width)
detector_height_new = detector_height_old × (crop_height / image_height)
focal_length_new = focal_length_old (unchanged)
principal_point_x_new = principal_point_x_old - crop_x_offset
principal_point_y_new = principal_point_y_old - crop_y_offset
field_of_view_new = 2 × arctan(detector_width_new / (2 × focal_length))
Physical pixel size remains constant: px = detector_width / n_pixels_x
Principal points may be outside image bounds after cropping; this is geometrically valid and represents the camera pointing outside the cropped region.
Observation Module
- class planet_ruler.observation.PlanetObservation(image_filepath)[source]
Bases:
objectBase class for planet observations.
- Parameters:
image_filepath (str) – Path to image file.
- crop_image(update_parameters=True)[source]
Interactively crop the observation image with automatic parameter scaling.
Opens a GUI tool allowing the user to select a rectangular region to crop. If the observation has camera parameters (e.g., LimbObservation), they are automatically scaled to match the cropped region.
Note: The principal point may end up outside the cropped region bounds (negative coordinates). This is geometrically valid - it means the camera’s optical axis was pointing at a location not visible in the cropped image.
- Parameters:
update_parameters (
bool) – If True and parameters exist, scale them for the crop- Returns:
For method chaining
- Return type:
self
Example
>>> obs = LimbObservation("airplane.jpg", "config.yaml") >>> obs.crop_image() # Opens interactive crop tool >>> obs.detect_limb() >>> obs.fit_arc()
Notes
Works for any PlanetObservation subclass
For LimbObservation: automatically scales detector_size, principal_point, etc.
Principal point can be outside cropped bounds (mathematically valid)
- class planet_ruler.observation.LimbObservation(image_filepath, fit_config, limb_detection='manual', minimizer='differential-evolution')[source]
Bases:
PlanetObservationObservation of a planet’s limb (horizon).
- Parameters:
- __init__(image_filepath, fit_config, limb_detection='manual', minimizer='differential-evolution')[source]
- analyze(detect_limb_kwargs=None, fit_method='arc', fit_method_kwargs=None, fit_stages=None)[source]
Perform complete limb analysis: detection + fitting in one call.
- Parameters:
detect_limb_kwargs (
Optional[dict]) – Optional kwargs for detect_limb().fit_method (
str) – Single-stage method – “arc” (default), “gradient”, or “sagitta”. Ignored when fit_stages is provided.fit_method_kwargs (
Optional[dict]) – Optional kwargs for the chosen fit method.fit_stages (
Optional[List[dict]]) – If provided, run fit_limb(fit_stages) (multi-stage orchestrator).
- Returns:
For method chaining.
- Return type:
self
- property radius_km: float
Get the fitted planetary radius in kilometers.
- Returns:
Planetary radius in km, or 0 if not fitted
- Return type:
- property altitude_km: float
Get the observer altitude in kilometers.
- Returns:
Observer altitude in km, or 0 if not fitted
- Return type:
- property focal_length_mm: float
Get the camera focal length in millimeters.
- Returns:
Focal length in mm, or 0 if not fitted
- Return type:
- property radius_uncertainty: float
Get parameter uncertainty for radius.
Automatically selects best method based on minimizer: - differential_evolution: Uses population spread (fast, exact) - dual_annealing/basinhopping: Uses Hessian approximation (fast, approximate)
- Returns:
Radius uncertainty in km (1-sigma), or 0 if not available
- Return type:
- parameter_uncertainty(parameter, method='auto', scale_factor=1.0, confidence_level=0.68, **kwargs)[source]
Get uncertainty for any fitted parameter.
- Parameters:
parameter (
str) – Parameter name (e.g., ‘r’, ‘h’, ‘f’, ‘theta_x’)method (
Literal['auto','hessian','profile','bootstrap']) – Uncertainty method - ‘auto’: Choose based on minimizer (recommended) - ‘hessian’: Fast Hessian approximation - ‘profile’: Slow but accurate profile likelihood - ‘bootstrap’: Multiple fits (very slow)scale_factor (
float) – Scale result (e.g., 1000.0 for m→km)confidence_level (
float) – Confidence level (0.68=1σ, 0.95=2σ)**kwargs – Additional arguments passed to uncertainty calculator
- Return type:
- Returns:
dict with ‘uncertainty’, ‘method’, ‘confidence_level’, ‘additional_info’
Examples
# Radius uncertainty in km (1-sigma) obs.parameter_uncertainty(‘r’, scale_factor=1000.0)
# Altitude uncertainty in km (2-sigma / 95% CI) obs.parameter_uncertainty(‘h’, scale_factor=1000.0, confidence_level=0.95)
# Focal length uncertainty in mm (using profile likelihood) obs.parameter_uncertainty(‘f’, scale_factor=1000.0, method=’profile’)
- reset_params()[source]
Restore init_parameter_values to the original loaded values (cold start).
- Return type:
- plot_3d(**kwargs)[source]
Create 3D visualization of the planetary geometry.
- Parameters:
**kwargs – Arguments passed to plot_3d_solution
- Return type:
- load_fit_config(fit_config)[source]
Load the fit configuration from file, setting all parameters to their initial values. Missing values are filled with defaults.
- register_limb(limb)[source]
Register a detected limb.
- Parameters:
limb (np.ndarray) – Limb vector (y pixel coordinates).
- Return type:
- detect_limb(detection_method=None, log=False, y_min=0, y_max=-1, window_length=501, polyorder=1, deriv=0, delta=1, segmentation_method='sam', downsample_factor=1, interactive=True, **segmentation_kwargs)[source]
Use the instance-defined method to find the limb in our observation. Kwargs are passed to the method.
- Parameters:
detection_method (literal) –
- Detection method. Must be one of
manual
gradient-break
gradient-field
segmentation
Default (None) uses the class attribute self.limb_detection.
log (bool) – Use the log(gradient). Sometimes good for smoothing.
y_min (int) – Minimum y-position to consider.
y_max (int) – Maximum y-position to consider.
window_length (int) – Width of window to apply smoothing for each vertical. Larger means less noise but less sensitivity.
polyorder (int) – Polynomial order for smoothing.
deriv (int) – Derivative level for smoothing.
delta (int) – Delta for smoothing.
segmentation_method (str) – Model used for segmentation. Must be one of [‘sam’].
downsample_factor (int) – Downsampling used for segmentation.
interactive (bool) – Prompts user to verify segmentation via annotation tool.
segmentation_kwargs (dict) – Kwargs passed to segmentation engine.
- Return type:
- smooth_limb(fill_nan=True, **kwargs)[source]
Apply the smooth_limb function to current observation.
- fit_sagitta(n_sigma=2.0, bias_correct=False, uncertainty='both')[source]
Estimate planetary radius using the sagitta (arc-height) method.
Runs SagittaFitter: a 2-D L-BFGS-B optimizer over (kappa, theta_x) using the exact projected-sagitta formula. Updates self.init_parameter_values and self.parameter_limits so that a subsequent fit_arc/fit_gradient stage warm-starts from the result automatically.
- Parameters:
n_sigma (
float) – Radius bound width in sigma units.bias_correct (
bool) – If True and y0 is provided, correct for camera tilt bias using the apex y-offset to estimate theta_x, then refine kappa via 1-D minimization.uncertainty (
str) – Bound-width method — “ols” (cheap, single pass), “jackknife” (leave-one-out), or “both” (quadrature sum, default).
- Returns:
For method chaining.
- Return type:
self
- fit_arc(loss_function='l2', minimizer=None, minimizer_preset='balanced', minimizer_kwargs=None, max_iter=15000, seed=0, verbose=False, n_jobs=1, _dashboard=None)[source]
Fit the planet limb arc using a pixel-space cost function.
Requires a detected limb (call detect_limb() first, or register_limb()). Reads self.init_parameter_values as the initial guess and writes fitted free-parameter values back so a chained call warm-starts automatically.
- Parameters:
loss_function (
Literal['l2','l1','log-l1']) – “l2” (default), “l1”, or “log-l1”.minimizer (
Optional[Literal['differential-evolution','dual-annealing','basinhopping','scipy-minimize','shgo']]) – Minimizer name. Defaults to self.minimizer.minimizer_preset (
Literal['fast','balanced','robust','super_robust','scipy-default']) – Preset config.minimizer_kwargs (
Optional[Dict]) – Override specific minimizer kwargs.max_iter (
int) – Maximum iterations.seed (
int) – Random seed.verbose (
bool) – Print progress.n_jobs (
int) – Parallel workers. Effective only for differential-evolution and shgo; emits a UserWarning for other minimizers._dashboard – Internal — FitDashboard instance passed by fit_limb.
- Returns:
For method chaining.
- Return type:
self
- fit_gradient(resolution_stages=None, minimizer=None, minimizer_preset='balanced', minimizer_kwargs=None, image_smoothing=None, kernel_smoothing=5.0, directional_smoothing=50, directional_decay_rate=0.15, prefer_direction=None, max_iter=15000, max_iter_per_stage=None, seed=0, verbose=False, n_jobs=1, _dashboard=None)[source]
Fit using gradient field alignment, optionally with coarse-to-fine stages.
Does not require a pre-detected limb; operates directly on the image. Multi-resolution stages warm-start from the previous stage automatically.
- Parameters:
resolution_stages (
Union[List[int],Literal['auto'],None]) – Downsampling factors, e.g. [4, 2, 1] (coarse→fine). None → single full-resolution stage. “auto” → inferred from image size.minimizer (
Optional[Literal['differential-evolution','dual-annealing','basinhopping','scipy-minimize','shgo']]) – Minimizer name. Defaults to self.minimizer.minimizer_preset (
Literal['fast','balanced','robust','super_robust','scipy-default']) – Preset config.minimizer_kwargs (
Optional[Dict]) – Override specific minimizer kwargs.image_smoothing (
Optional[float]) – Gaussian blur sigma applied before optimisation (removes high-frequency artifacts; original image restored after).kernel_smoothing (
float) – Gradient field kernel size.directional_smoothing (
int) – Directional sampling distance along gradients.directional_decay_rate (
float) – Exponential decay for directional samples.prefer_direction (
Optional[Literal['up','down']]) – “up” (dark sky / bright planet), “down”, or None.max_iter (
int) – Total maximum iterations (split across stages if multi-res).max_iter_per_stage (
Optional[List[int]]) – Explicit per-stage iteration budget.seed (
int) – Random seed.verbose (
bool) – Print progress.n_jobs (
int) – Parallel workers. Effective only for differential-evolution and shgo; emits a UserWarning for other minimizers.
- Returns:
For method chaining.
- Return type:
self
- fit_limb(stages, dashboard=False, dashboard_kwargs=None, target_planet='earth', n_jobs=1)[source]
Run a multi-stage fit by sequentially calling fit_sagitta / fit_arc / fit_gradient.
Resets self.stage_results before running so the list reflects only this call. Individual fit_* calls accumulate into stage_results without resetting.
- Parameters:
stages (
List[dict]) – Ordered list of stage dicts, each with a “method” key (“sagitta”, “arc”, or “gradient”) plus kwargs for that method.dashboard (
bool) – Show a live FitDashboard during optimization.dashboard_kwargs (
Optional[Dict]) – Extra kwargs forwarded to FitDashboard.__init__.target_planet (
str) – Planet name for dashboard reference radius.n_jobs (
int) – Parallel workers forwarded to each arc/gradient stage. Effective only for differential-evolution and shgo.
- Returns:
For method chaining.
- Return type:
self
Example:
obs.fit_limb([ {"method": "sagitta", "n_sigma": 2.0}, {"method": "arc", "minimizer": "basinhopping", "minimizer_preset": "robust"}, ])
- planet_ruler.observation.package_results(observation)[source]
Consolidate the results of a fit to see final vs. initial values.
- Parameters:
observation (object) – Instance of LimbObservation (must have used differential evolution minimizer).
- Returns:
- DataFrame of results including
fit value
initial value
parameter
- Return type:
results (pd.DataFrame)
The observation module provides high-level interfaces:
PlanetObservation: Base class for planetary observations
LimbObservation: Complete workflow for limb-based radius fitting (default: manual annotation)
Results processing: unpack_diff_evol_posteriors, package_results
Annotation Module
Manual Limb Annotation Tool for Planet Ruler
Allows users to click points on a horizon image and generate a sparse target for fitting with the existing planet_ruler pipeline.
- class planet_ruler.annotate.ToolTip(widget, text)[source]
Bases:
objectSimple tooltip that appears on hover.
- planet_ruler.annotate.create_tooltip(widget, text)[source]
Helper function to create tooltip for a widget.
- class planet_ruler.annotate.TkLimbAnnotator(image_path, image=None, initial_stretch=1.0, initial_zoom=None, output_dir=None)[source]
Bases:
objectTkinter-based interactive tool for manually annotating planet limbs.
Features: - Zoom with scroll wheel (fit large images in window) - Vertical stretch buttons (stretch pixels vertically for precision) - Scrollable canvas for navigating - Click to add points, right-click to undo - Save/load points to JSON - Generate sparse target array for CostFunction
- __init__(image_path, image=None, initial_stretch=1.0, initial_zoom=None, output_dir=None)[source]
Initialize the annotation tool.
- Parameters:
image_path (str) – Path to the image to annotate
image (np.ndarray) – Optionally the already loaded image
initial_stretch (float) – Initial vertical stretch factor
initial_zoom (float) – Initial zoom level (None = auto-fit)
output_dir (str or Path, optional) – Directory for saved JSON files. Defaults to the same directory as image_path.
- class planet_ruler.annotate.TkMaskSelector(image, masks, initial_zoom=None)[source]
Bases:
objectInteractive mask classification tool for segmentation results.
Works with ANY segmentation method - completely backend-agnostic. Allows users to classify masks as ‘planet’, ‘sky’, or ‘exclude’ for horizon detection. Aligns with Planet Ruler’s educational philosophy of transparency over black-box automation.
- Parameters:
image (
ndarray) – Original image array (H x W x 3)masks (
list) – List of masks in any of these formats: - List of np.ndarray (boolean H x W arrays) - List of dicts with ‘mask’ or ‘segmentation’ key - Mixed formats are OKinitial_zoom (
Optional[float]) – Initial zoom level (None = auto-fit to window)
- planet_ruler.annotate.load_limb_points_from_json(json_path, return_metadata=False)[source]
Load limb points from JSON and convert to sparse target array.
- Parameters:
- Returns:
Sparse target array with limb y-coordinates tuple: (target, metadata) if return_metadata=True
- Return type:
np.ndarray
Example
>>> # Simple usage - just get the target array >>> target = load_limb_points_from_json("image_limb_points.json") >>> >>> # Use with LimbObservation >>> from planet_ruler.observation import LimbObservation >>> obs = LimbObservation("image.jpg", "config.yaml") >>> target = load_limb_points_from_json("image_limb_points.json") >>> obs.register_limb(target) >>> obs.fit_limb() >>> >>> # Get metadata too (for validation) >>> target, metadata = load_limb_points_from_json( ... "image_limb_points.json", ... return_metadata=True ... ) >>> print(f"Loaded {metadata['n_points']} points from {metadata['image_path']}")
The annotation module provides interactive manual limb detection:
TkLimbAnnotator: Interactive GUI for manual horizon point selection
Point management: Add, remove, and edit limb points with mouse interaction
Image controls: Zoom, pan, and contrast adjustment for precise annotation
File I/O: Save and load annotation sessions to/from JSON files
Target generation: Convert point annotations to dense limb arrays
Camera Module
Automatic extraction of camera parameters from smartphone images. Uses EXIF data and a phone camera database.
- planet_ruler.camera.match_camera_module(camera_data, focal_length_mm, aperture=None)[source]
Match specific camera module for multi-camera phones.
Uses focal length as primary matcher, aperture as secondary confirmation. Falls back to main camera if no match found.
- planet_ruler.camera.get_focal_length_35mm_equiv(exif_data)[source]
Get 35mm equivalent focal length (more reliable for phones).
- planet_ruler.camera.get_image_orientation_from_exif(exif_data=None)[source]
Determine orientation from EXIF Orientation tag only.
- planet_ruler.camera.get_image_orientation_from_dimensions(width, height)[source]
Determine orientation from image dimensions (heuristic).
- planet_ruler.camera.apply_orientation_correction(params, exif_data=None)[source]
Apply orientation correction to camera parameters.
This modifies params in-place and returns it. Call this RIGHT BEFORE each return statement in extract_camera_parameters().
- planet_ruler.camera.calculate_sensor_dimensions(focal_length_mm, focal_length_35mm)[source]
Calculate sensor dimensions from focal length ratio. Uses the relationship: focal_length_mm / sensor_width = focal_length_35mm / 36mm
- planet_ruler.camera.get_sensor_statistics_by_type(camera_type)[source]
Calculate sensor dimension statistics for a given camera type. Returns median, min, and max for sensor width and height.
- planet_ruler.camera.infer_camera_type(exif_data)[source]
Try to infer camera type from EXIF data even if exact model unknown.
- planet_ruler.camera.extract_camera_parameters(image_path)[source]
Automatically extract all camera parameters from any camera image. Handles phones, point-and-shoot cameras, DSLRs, mirrorless, etc.
- Returns:
- Camera parameters including:
focal_length_mm: focal length in millimeters
sensor_width_mm: sensor width in millimeters
sensor_height_mm: sensor height in millimeters
sensor_width_min: minimum sensor width (if using type statistics)
sensor_width_max: maximum sensor width (if using type statistics)
image_width_px: image width in pixels
image_height_px: image height in pixels
camera_model: detected camera model
camera_type: ‘phone’, ‘compact’, ‘dslr’, ‘mirrorless’, or ‘unknown’
confidence: ‘high’, ‘medium’, or ‘low’
- Return type:
- planet_ruler.camera.get_gps_altitude(image_path)[source]
Extract GPS altitude from EXIF data if available.
- Returns:
Altitude in meters, or None if not available
- Return type:
- planet_ruler.camera.sample_param_from_bounds(lo, hi, ref=None, perturbation_factor=1.0, seed=None)[source]
Sample a parameter initial value uniformly from a sub-interval of [lo, hi].
Each side of the interval is scaled proportionally toward
refby(1 - perturbation_factor), preserving asymmetry in the original bounds. Withperturbation_factor=1.0the full [lo, hi] range is used; with 0.0 onlyrefis returned.- Example: lo=0.7, hi=1.2, ref=1.0, perturbation_factor=0.5
new_lo = 1.0 - (1.0 - 0.7) * 0.5 = 0.85 new_hi = 1.0 + (1.2 - 1.0) * 0.5 = 1.10
Uses a local Random instance to avoid polluting global random state.
- Parameters:
lo (
float) – Lower bound of search interval (inclusive)hi (
float) – Upper bound of search interval (inclusive)ref (
Optional[float]) – Reference value toward which bounds are scaled. Defaults to the midpoint (lo + hi) / 2, which is exact for symmetric bounds.perturbation_factor (
float) – 1.0 = full [lo, hi]; 0.0 = ref only. (default: 1.0)
- Return type:
- planet_ruler.camera.init_params_from_bounds(param_limits, perturbation_factor=1.0, seed=None, ref_values=None, params=None)[source]
Sample initial values for parameters uniformly from their search bounds.
Each parameter is sampled via
sample_param_from_bounds(). A single seeded RNG advances sequentially through a fixed parameter ordering so that each parameter’s draw is independent of which other parameters are included, and results are fully reproducible given the same seed.- Parameters:
param_limits (
Dict[str,List[float]]) – Dict mapping parameter name → [lo, hi].perturbation_factor (
float) – Passed tosample_param_from_bounds(). 1.0 (default) samples the full [lo, hi] range; 0.0 returns ref only.ref_values (
Optional[Dict[str,float]]) – Optional dict of reference values used as the proportional centre for each parameter. Defaults to the midpoint of each parameter’s bounds when not provided.params (
Optional[List[str]]) – Optional list of parameter names to sample. When None, all keys present inparam_limitsare sampled in the fixed order below (unknown names are appended at the end).
- Return type:
- Returns:
Dict mapping parameter name → sampled initial value.
- planet_ruler.camera.get_initial_radius(planet='earth')[source]
Return the known radius for a planet (metres), or 10,000 km for unknown planets.
- Return type:
- planet_ruler.camera.check_planet_ruler_crop_metadata(image_path)[source]
Check for crop metadata in sidecar JSON file.
- planet_ruler.camera.create_config_from_image(image_path, altitude_m=None, planet='earth', limits_preset='balanced', param_tolerances=None, param_tolerance=None)[source]
Create a complete planet_ruler configuration from an image. Works with any camera - phones, point-and-shoot, DSLRs, etc.
- Parameters:
image_path (
str) – Path to the imagealtitude_m (
Optional[float]) – Altitude in meters (REQUIRED if not in GPS data)planet (
str) – Planet name for initial radius guess (default: ‘earth’)limits_preset (
str) – How tightly to constrain camera/altitude parameters."tight"— trust EXIF and GPS data; small search windows."balanced"— default; moderate windows for most photos."loose"— wide windows; use when metadata reliability is uncertain. Altitude (h) bounds are automatically tightened when GPS altitude is detected, regardless of preset.param_tolerances (
Optional[Dict[str,float]]) – Per-parameter fractional tolerance overrides, e.g.{"h": 0.30}to widen the altitude window without changing other parameters. Keys:"f","w","h".param_tolerance (
Optional[float]) – Deprecated. Passlimits_presetinstead. A deprecation warning is raised; the value is ignored and"balanced"is used.
- Returns:
Configuration ready for planet_ruler
- Return type:
- Raises:
ValueError – If altitude cannot be determined from GPS and not provided manually
ValueError – If
limits_presetis not a recognised preset name.
Notes
For uncertainty quantification, consider running multiple times (multi-start optimization)
Theta parameters (orientation) always span ±π; they have no prior
r bounds scale with the preset around the planet init radius
The camera module provides automatic camera parameter extraction from image EXIF data:
EXIF processing: extract_exif, get_camera_model, get_focal_length_mm
Camera database: Comprehensive database of sensor dimensions for phones, DSLRs, mirrorless cameras
Parameter extraction: extract_camera_parameters - automatic detection of focal length and sensor size
GPS integration: get_gps_altitude - extract altitude from GPS EXIF data
Configuration generation: create_config_from_image - complete auto-config from any camera image
Planet radii: get_initial_radius - perturbed initial radius guesses for robust optimization
Fitting and Optimization
- planet_ruler.fit.unpack_parameters(params, template)[source]
Turn a list of parameters back into a dict.
- planet_ruler.fit.pack_parameters(params, template)[source]
Turn a dict of parameters (or defaults) into a list.
- class planet_ruler.fit.BaseFitter[source]
Bases:
ABCCommon interface for planet-ruler fitting strategies.
Each subclass encapsulates one strategy and returns a stage-result dict from .fit(). The dict always contains:
updated_init – {param: value} updates to warm-start the next stage updated_limits – {param: [lo, hi]} tighter bounds for the next stage status – “ok” | “flat_arc” | “too_few_points” | “error” warnings – list[str]
- class planet_ruler.fit.BaseCostFunction(target, function, free_parameters, init_parameter_values)[source]
Bases:
objectShared evaluation logic for all cost function variants.
- class planet_ruler.fit.L2CostFunction(target, function, free_parameters, init_parameter_values, loss_function='l2')[source]
Bases:
BaseCostFunctionCost function for l2, l1, and log-l1 loss against a detected limb.
- class planet_ruler.fit.GradientFieldCostFunction(target, function, free_parameters, init_parameter_values, loss_function='gradient_field', kernel_smoothing=5.0, directional_smoothing=30, directional_decay_rate=0.15, prefer_direction=None)[source]
Bases:
BaseCostFunctionCost function using gradient field flux alignment.
- class planet_ruler.fit.LimbFitter(target, free_parameters, init_parameter_values, parameter_limits, loss_function, minimizer, minimizer_kwargs, max_iter=15000, seed=0, verbose=False, kernel_smoothing=5.0, directional_smoothing=50, directional_decay_rate=0.15, prefer_direction=None, n_jobs=1)[source]
Bases:
BaseFitterImage-space fitter using a pixel-level cost function and a scipy minimizer.
Accepts one target (image for gradient_field, limb array for l2/l1/log-l1) at a fixed resolution. The caller is responsible for downsampling and parameter scaling before instantiation.
minimizer_kwargs must already have preset values resolved by the caller.
- planet_ruler.fit.estimate_radius_via_sagitta(limb, h, f_px, x0=None, y0=None, sigma_px='auto', n_sigma=1.0, bias_correct=False, uncertainty='ols')[source]
Estimate planetary radius from a sparse limb arc using OLS on the sagitta.
Fits K = r / sqrt(h*(2r+h)) by regressing observed sagittae against the exact hyperbola model A(u) = sqrt(f_px^2 + u^2) - f_px.
- Parameters:
limb (
ndarray) – 1-D array of length n_pix_x; NaN where not annotated.h (
float) – Observer altitude in metres.f_px (
float) – Focal length in pixels.x0 (
Optional[float]) – Optional override for apex x-coordinate (auto-detected if None).y0 (
Optional[float]) – Image vertical centre in pixels, required for bias_correct=True.sigma_px (
float|str) – Annotation noise in pixels, or “auto” to derive from RMS residuals.n_sigma (
float) – Bound width in sigma units.bias_correct (
bool) – If True and y0 is provided, correct for camera tilt bias using the apex y-offset to estimate theta_x, then refine kappa via 1-D minimization.uncertainty (
str) – “ols” | “jackknife” | “both”. Controls which K_sigma drives bounds.
- Returns:
- r, r_low, r_high, r_sigma, K, K_sigma, K_sigma_jack,
n_points, residual_rms, arc_angle_deg, x_apex, y_apex, theta_x_est, status, warnings.
- Return type:
dict with keys
- class planet_ruler.fit.SagittaFitter(limb, h, f_px, y0, free_parameters, init_parameter_values, parameter_limits, sigma_px='auto', n_sigma=1.0, uncertainty='jackknife', bias_correct=False)[source]
Bases:
BaseFitter2-D optimizer over (kappa, theta_x) using the exact projected-sagitta formula.
Accurate to < 0.05 km for all tested camera tilts. Fits only r and theta_x; other free_parameters are handled by a subsequent LimbFitter stage.
- planet_ruler.fit.calculate_parameter_uncertainty(observation, parameter='r', method='auto', uncertainty_type='std', scale_factor=1.0)[source]
Calculate parameter uncertainty from fitting results.
Provides a flexible interface for uncertainty estimation that works with different optimization methods and uncertainty metrics.
- Parameters:
observation – LimbObservation object with completed fit
parameter (str) – Parameter name to calculate uncertainty for (default: “r”)
method (str) – Uncertainty calculation method: - “auto”: Automatically detect method from fit results - “differential_evolution”: Use DE population posteriors - “bootstrap”: Use bootstrap resampling (future implementation) - “hessian”: Use Hessian-based uncertainty (future implementation)
uncertainty_type (str) – Type of uncertainty measure: - “std”: Standard deviation of parameter distribution - “ptp”: Peak-to-peak range (max - min) - “iqr”: Interquartile range (75th - 25th percentile) - “ci”: Confidence interval (returns dict with bounds)
scale_factor (float) – Scale factor to apply to results (e.g., 1000 for km units)
- Returns:
- Dictionary containing uncertainty information:
”value”: Fitted parameter value (scaled)
”uncertainty”: Uncertainty estimate (scaled)
”method”: Method used for uncertainty calculation
”type”: Type of uncertainty measure used
”raw_data”: Raw parameter samples if available
- Return type:
- Raises:
ValueError – If uncertainty method is not supported or data is insufficient
AttributeError – If observation doesn’t have required fit results
- planet_ruler.fit.format_parameter_result(uncertainty_result, units='')[source]
Format parameter uncertainty results for display.
- planet_ruler.fit.unpack_diff_evol_posteriors(observation)[source]
Extract the final state population of a differential evolution minimization and organize as a DataFrame.
- Parameters:
observation (object) – Instance of LimbObservation (must have used differential evolution minimizer).
- Returns:
Population (rows) and properties (columns).
- Return type:
population (pd.DataFrame)
The fit module handles parameter optimization:
CostFunction: Configurable cost function for parameter fitting
Parameter handling: pack_parameters, unpack_parameters
Uncertainty analysis: calculate_parameter_uncertainty, format_parameter_result
Uncertainty Module
- planet_ruler.uncertainty.calculate_parameter_uncertainty(observation, parameter, method='auto', scale_factor=1.0, confidence_level=0.68, n_bootstrap=20, **kwargs)[source]
Calculate uncertainty for a fitted parameter using appropriate method.
Automatically selects best method based on minimizer used: - differential_evolution: Population spread (fast, exact) - dual_annealing/basinhopping: Hessian approximation (fast, approximate) - any: Profile likelihood or bootstrap (slow, accurate)
- Parameters:
observation – LimbObservation instance with completed fit
parameter (
str) – Parameter name (e.g., ‘r’, ‘h’, ‘f’)method (
Literal['auto','hessian','profile','bootstrap']) – Uncertainty estimation method - ‘auto’: Choose based on minimizer and available data - ‘hessian’: Inverse Hessian at optimum (fast) - ‘profile’: Profile likelihood (slow but accurate) - ‘bootstrap’: Multiple fits with different seeds (slow)scale_factor (
float) – Scale result (e.g., 1000.0 to convert m→km)confidence_level (
float) – Confidence level (0.68=1σ, 0.95=2σ)n_bootstrap (
int) – Number of bootstrap iterations
- Returns:
‘uncertainty’: The uncertainty value
’method’: Method used
’confidence_level’: Confidence level
’additional_info’: Method-specific details
- Return type:
dict with keys
The uncertainty module provides comprehensive parameter uncertainty estimation:
Multiple methods: Population spread, Hessian approximation, profile likelihood, bootstrap
Auto-selection: Automatically chooses best method based on minimizer used
Confidence intervals: Configurable confidence levels (68%, 95%, 99%, etc.)
Method-specific details: Additional diagnostics for each uncertainty estimation method
Available Uncertainty Methods:
population: Uses parameter spread from differential-evolution population (fast, exact for DE)
hessian: Inverse Hessian approximation at optimum (fast, approximate, works with all minimizers)
profile: Profile likelihood re-optimization (slow, most accurate, works with all minimizers)
bootstrap: Multiple fits with different seeds (slow, robust, partially implemented)
auto: Automatically selects population for DE, hessian for others
Plotting and Visualization
- planet_ruler.plot.plot_image(im_arr, gradient=False, show=True)[source]
Display an image using matplotlib.pyplot.imshow.
- planet_ruler.plot.plot_limb(y, show=True, c='y', s=10, alpha=0.2)[source]
Display the limb (usually on top of an image).
- planet_ruler.plot.plot_3d_solution(r, h=1, zoom=1, savefile=None, legend=True, vertical_axis='z', azim=None, roll=None, x_axis=False, y_axis=True, z_axis=False, **kwargs)[source]
Plot a limb solution in 3D.
- Parameters:
r (float) – Radius of the body in question.
h (float) – Height above surface (units should match radius).
zoom (float) – Shrink the height according to a zoom factor to make viewing easier.
savefile (str) – Path to optionally save figure.
legend (bool) – Display the legend.
vertical_axis (str) – Which axis will be used as the vertical (x, y, or z).
azim (float) – Viewing azimuth.
roll (float) – Viewing roll.
x_axis (bool) – Plot the x-axis.
y_axis (bool) – Plot the y-axis.
z_axis (bool) – Plot the z-axis.
kwargs – Absorbs other solution kwargs that don’t matter for physical space.
- Return type:
- planet_ruler.plot.plot_topography(image)[source]
Display the full limb, including the section not seen in the image.
- Parameters:
image (np.ndarray) – Image array.
- Return type:
- planet_ruler.plot.plot_gradient_field_at_limb(y_pixels, image, image_smoothing=None, kernel_smoothing=5.0, directional_smoothing=50, directional_decay_rate=0.15, sample_spacing=50, arrow_scale=20)[source]
Visualize gradient field along a proposed limb curve.
- Parameters:
y_pixels (np.ndarray) – Y-coordinates of limb at each x-position
image (np.ndarray) – Input image (H x W x 3 or H x W)
image_smoothing (int) – For gradient_field - Gaussian blur sigma applied to image before gradient computation. Removes high-frequency artifacts (crater rims, striations) that could mislead optimization. Different from kernel_smoothing.
kernel_smoothing (float) – Initial smoothing for gradient direction estimation
directional_smoothing (int) – How far to sample along gradients
directional_decay_rate (float) – Exponential decay rate for sampling
sample_spacing (int) – Sample every N pixels along x-axis
arrow_scale (float) – Scale factor for arrow lengths
- Returns:
Matplotlib figure and axis objects
- Return type:
fig, ax
- planet_ruler.plot.compare_blur_methods(image, y_pixels=None)[source]
Compare gradient magnitude with different blur methods.
- Parameters:
image – Input image
y_pixels – Optional limb curve to overlay
- planet_ruler.plot.compare_gradient_fields(y_pixels_list, labels, image, image_smoothing=None, kernel_smoothing=5.0, directional_smoothing=30, directional_decay_rate=0.15)[source]
Compare gradient alignment for multiple proposed limbs.
- Parameters:
y_pixels_list (list) – List of y-coordinate arrays (different limb proposals)
labels (list) – Labels for each limb
image (np.ndarray) – Input image
kernel_smoothing (float) – Initial smoothing for gradient direction estimation
directional_smoothing (int) – How far to sample along gradients
directional_decay_rate (float) – Exponential decay rate for sampling
- planet_ruler.plot.plot_diff_evol_posteriors(observation, show_points=False, log=True)[source]
Extract and display the final state population of a differential evolution minimization.
- planet_ruler.plot.plot_full_limb(observation, x_min=None, x_max=None, y_min=None, y_max=None)[source]
Display the full limb, including the section not seen in the image.
- planet_ruler.plot.plot_residuals(observation, show_sparse_markers=True, marker_size=10, alpha=0.6, figsize=(16, 6), show_image=False, image_alpha=0.4, band_size=30)[source]
Plot residuals between the fitted limb and the detected target limb.
- Parameters:
observation – Instance of LimbObservation that has been fitted.
show_sparse_markers (
bool) – Use larger markers for sparse data.marker_size (
int) – Size of markers for sparse data.alpha (
float) – Transparency of residual markers/line.figsize (
tuple) – Figure size (width, height).show_image (
bool) – Show straightened image strip as background.image_alpha (
float) – Transparency of background image.band_size (
int) – Size of band around residuals to plot (in pixels)
- Return type:
- planet_ruler.plot.plot_gradient_field_quiver(image, step=2, scale=0.15, limb_y=None, roi_height=None, image_smoothing=None, kernel_smoothing=5.0, directional_smoothing=50, directional_decay_rate=0.15, downsample_factor=32, figsize=(16, 10), cmap='hot')[source]
Plot gradient field as quiver (arrow) plot.
- Parameters:
image (
ndarray) – Input image array.step (
int) – Spacing between arrows (every Nth pixel).limb_y (
Optional[ndarray]) – Optional limb curve to overlay (y-coordinates).roi_height (
Optional[int]) – If provided, only show ±roi_height pixels around limb.image_smoothing (
Optional[int]) – For gradient_field - Gaussian blur sigma applied to image before gradient computation. Removes high-frequency artifacts (crater rims, striations) that could mislead optimization. Different from kernel_smoothing.kernel_smoothing (
float) – Sigma for initial gradient direction estimation.directional_smoothing (
int) – Distance for directional blur sampling.directional_decay_rate (
float) – Exponential decay for directional blur.downsample_factor (
int) – Downsample image by this factor before computing gradients. Values > 1 reduce resolution (e.g., 2 = half resolution, 4 = quarter). Useful for visualizing gradient field at different optimization stages.figsize (
tuple) – Figure size (width, height).cmap (
str) – Colormap for gradient magnitude.
- Return type:
- planet_ruler.plot.plot_segmentation_masks(observation)[source]
Display all the classes/masks generated by the segmentation.
- planet_ruler.plot.plot_sam_masks(masks, labels=None, colors=None, alpha=0.5, image=None, figsize=(16, 10), title=None, show=True)[source]
Plot multiple SAM segmentation masks as separate labeled objects with legend.
- Parameters:
masks (list) – List of SAM mask dictionaries with ‘segmentation’ keys containing boolean arrays. Can also be a list of boolean arrays directly.
labels (list) – Labels for each mask (for legend). If None, uses “Mask 0”, “Mask 1”, etc.
colors (list) – Colors for each mask. If None, uses a default color cycle.
alpha (float) – Transparency of mask overlays (0=transparent, 1=opaque).
image (np.ndarray) – Optional background image to display under masks.
figsize (tuple) – Figure size in inches.
title (str) – Optional title for the plot.
show (bool) – Whether to display the plot immediately.
- Returns:
(fig, ax) matplotlib figure and axis objects.
- Return type:
Example
>>> masks = [{'segmentation': planet_mask}, {'segmentation': sky_mask}] >>> fig, ax = plot_sam_masks( ... masks, ... labels=['Planet', 'Sky'], ... colors=['yellow', 'cyan'], ... image=observation.image ... )
The plot module provides visualization functions:
Image display: plot_image - displays images with optional gradient overlay
Limb visualization: plot_limb - plots detected horizon curves
3D solutions: plot_3d_solution - 3D parameter space visualization
Topography: plot_topography - terrain height visualization
Analysis plots: plot_diff_evol_posteriors - differential evolution posterior distributions
Full limb plots: plot_full_limb - complete limb visualization with uncertainty
Segmentation plots: plot_segmentation_masks - image segmentation mask visualization
Validation Module
Validation functions for planet_ruler.
- planet_ruler.validation.validate_limb_config(config, strict=True)[source]
Validate that a planet_ruler configuration is internally consistent.
Checks: 1. Initial parameter values are within their specified limits 2. Theta parameter limits are wide enough (avoid r-h coupling issues) 3. Radius limits span reasonable range for robust optimization
- Parameters:
- Raises:
AssertionError – If strict=True and validation fails
- Return type:
Example
>>> config = { ... 'init_parameter_values': {'r': 6371000, 'theta_x': 0.1}, ... 'parameter_limits': {'r': [1e6, 1e8], 'theta_x': [-3.14, 3.14]} ... } >>> validate_config(config) # Passes
The validation module provides configuration validation:
Config validation: validate_limb_config - checks parameter limits, theta ranges, and consistency
Parameter bounds: Validates initial values are within specified limits
Optimization warnings: Identifies potential issues with tight constraints that could affect convergence
Command-Line Interface
Command-line interface for planet_ruler
This module provides a simple CLI for measuring planetary radii from horizon photographs.
The CLI module provides command-line access to planet_ruler:
Main interface: main - primary CLI entry point
Measurement: measure_command - automated radius measurement from images
Demo functionality: demo_command - run example scenarios
Configuration: load_config, list_command - manage configuration files
Dashboard Module
Live progress dashboard for parameter optimization.
Displays real-time updates during fit_limb() optimization, showing: - Current parameter estimates (radius, altitude, focal length) - Optimization progress and convergence - Loss function evolution - Smart warnings and educational hints
Works in: terminal, Jupyter notebooks, IPython Updates in-place (no scrolling spam)
Example
>>> obs = LimbObservation("photo.jpg", "config.yaml")
>>> obs.detect_limb(method="manual")
>>> obs.fit_limb(dashboard=True) # Shows live dashboard during fitting
- class planet_ruler.dashboard.OutputCapture(max_lines=5, line_width=55, capture_stderr=True)[source]
Bases:
objectCapture stdout/stderr for display in dashboard.
Thread-safe ring buffer that stores recent output lines. Designed to integrate with FitDashboard for live output display.
- Parameters:
Examples
>>> capture = OutputCapture(max_lines=5) >>> with capture: ... print("Iteration 100: loss = 123.45") ... print("Warning: parameter drift detected") >>> lines = capture.get_lines()
- class planet_ruler.dashboard.FitDashboard(initial_params, target_planet='earth', max_iter=None, free_params=None, total_stages=1, cumulative_max_iter=None, min_refresh_delay=0.0, refresh_frequency=1, output_capture=None, show_output=True, max_output_lines=3, max_warnings=3, max_hints=3, min_message_display_time=3.0, width=63)[source]
Bases:
objectLive dashboard for optimization progress.
Shows real-time parameter estimates, convergence status, and helpful warnings during fit_limb() optimization.
- Parameters:
initial_params (dict) – Initial parameter values (r, h, f, w, etc.)
target_planet (str, default 'earth') – Reference planet for comparison (‘earth’, ‘mars’, etc.)
max_iter (int, optional) – Maximum iterations (if known)
free_params (list of str, optional) – Which parameters are being optimized
total_stages (int, default 1) – Number of optimization stages (for multi-resolution)
cumulative_max_iter (int, optional) – Total iterations across all stages
min_refresh_delay (float, default 0.0) – Minimum time (seconds) between refreshes (0.0 enables adaptive)
refresh_frequency (int, default 1) – Refresh every N iterations
output_capture (OutputCapture, optional) – Output capture instance for displaying print/log statements
show_output (bool, default True) – Show output section in dashboard (requires output_capture)
max_output_lines (int, default 3) – Maximum number of output lines to display
max_warnings (int, default 3) – Number of warning message slots
max_hints (int, default 3) – Number of hint message slots
min_message_display_time (float, default 3.0) – Minimum time (seconds) to display warnings/hints before removal
width (int, default 63) – Dashboard width in characters (includes borders)
- __init__(initial_params, target_planet='earth', max_iter=None, free_params=None, total_stages=1, cumulative_max_iter=None, min_refresh_delay=0.0, refresh_frequency=1, output_capture=None, show_output=True, max_output_lines=3, max_warnings=3, max_hints=3, min_message_display_time=3.0, width=63)[source]
- update(current_params, loss)[source]
Update dashboard with current optimization state.
Called by optimizer callback at each iteration.
The dashboard module provides real-time optimization monitoring:
FitDashboard: Live progress dashboard with adaptive refresh rate
OutputCapture: Capture stdout/stderr for display in dashboard
Adaptive refresh: Automatically adjusts update frequency (20Hz → 2Hz) based on optimization activity
Configurable display: Adjust width, message slots, and display time
Demo and Configuration
The demo module manages example scenarios:
Parameter loading: load_demo_parameters - loads predefined planetary scenarios
Interactive widgets: make_dropdown - creates UI elements for Jupyter notebooks
Function Categories
Mathematical Functions
Core geometric calculations:
Estimate the distance to the horizon (limb) given a height and radius. |
|
The angle the camera must tilt in theta_x or theta_y to center the limb. |
|
The size of the CCD (inferred) based on focal length and field of view. |
|
The size of the CCD (inferred) based on focal length and field of view. |
|
The size of the CCD (inferred) based on focal length and field of view. |
|
Compute combined rotation matrix from Euler angles. |
Image Processing Functions
Computer vision and image analysis:
|
Load a 3 or 4-channel image from filepath into an array. |
|
Scan each vertical line of an image for the maximum change in brightness gradient -- usually corresponds to a horizon. |
|
Smooth the limb position values. |
Fill NaNs for the limb position values. |
|
Bilinear interpolation for 2D array at non-integer coordinates. |
|
|
Pre-compute gradient field AND its derivatives for fast sub-pixel interpolation. |
Manual Annotation Functions
Interactive limb detection and annotation:
|
Tkinter-based interactive tool for manually annotating planet limbs. |
Start the application. |
|
Get the current sparse target array. |
|
Generate sparse target array. |
|
Save points to JSON, prompting user to confirm or edit the save path. |
|
Load points from JSON. |
Camera Parameter Functions
Automatic camera parameter extraction:
|
Extract EXIF data from image. |
|
Extract camera model from EXIF data. |
Extract focal length in mm from EXIF. |
|
Get 35mm equivalent focal length (more reliable for phones). |
|
Automatically extract all camera parameters from any camera image. |
|
|
Extract GPS altitude from EXIF data if available. |
Create a complete planet_ruler configuration from an image. |
|
|
Return the known radius for a planet (metres), or 10,000 km for unknown planets. |
Coordinate Transform Functions
Camera geometry and projections:
Transform from camera coordinates into image coordinates. |
|
Transform from world coordinates into camera coordinates. |
|
|
Calculate limb position analytically at each pixel x-coordinate. |
Calculate the limb orientation in an image given the physical parameters of the system. |
Optimization Functions
Parameter fitting and uncertainty:
|
Turn a dict of parameters (or defaults) into a list. |
|
Turn a list of parameters back into a dict. |
Calculate parameter uncertainty from fitting results. |
|
Format parameter uncertainty results for display. |
|
|
Calculate uncertainty for a fitted parameter using appropriate method. |
|
Uncertainty from differential evolution population. |
|
Uncertainty from inverse Hessian (covariance) at optimum. |
|
Uncertainty from profile likelihood. |
|
Uncertainty from bootstrap: run optimizer multiple times with different seeds. |
Validation Functions
Configuration and parameter validation:
Validate that a planet_ruler configuration is internally consistent. |
Command-Line Functions
CLI interface and commands:
Main CLI entry point. |
|
Handle the measure command. |
|
Handle the demo command. |
|
Handle the list command. |
|
|
Load configuration from YAML or JSON file. |
Visualization Functions
Plotting and display:
|
Display an image using matplotlib.pyplot.imshow. |
|
Display the limb (usually on top of an image). |
|
Plot a limb solution in 3D. |
Display the full limb, including the section not seen in the image. |
|
Extract and display the final state population of a differential evolution minimization. |
|
|
Display the full limb, including the section not seen in the image. |
Display all the classes/masks generated by the segmentation. |
Classes
CostFunction
Cost function wrapper for optimization problems. Supports multiple loss functions (L1, L2, log-L1) and flexible parameter handling.
PlanetObservation
- class planet_ruler.observation.PlanetObservation(image_filepath)[source]
Bases:
objectBase class for planet observations.
- Parameters:
image_filepath (str) – Path to image file.
- crop_image(update_parameters=True)[source]
Interactively crop the observation image with automatic parameter scaling.
Opens a GUI tool allowing the user to select a rectangular region to crop. If the observation has camera parameters (e.g., LimbObservation), they are automatically scaled to match the cropped region.
Note: The principal point may end up outside the cropped region bounds (negative coordinates). This is geometrically valid - it means the camera’s optical axis was pointing at a location not visible in the cropped image.
- Parameters:
update_parameters (
bool) – If True and parameters exist, scale them for the crop- Returns:
For method chaining
- Return type:
self
Example
>>> obs = LimbObservation("airplane.jpg", "config.yaml") >>> obs.crop_image() # Opens interactive crop tool >>> obs.detect_limb() >>> obs.fit_arc()
Notes
Works for any PlanetObservation subclass
For LimbObservation: automatically scales detector_size, principal_point, etc.
Principal point can be outside cropped bounds (mathematically valid)
Base class for planetary observations. Handles image loading and basic plotting functionality.
LimbObservation
- class planet_ruler.observation.LimbObservation(image_filepath, fit_config, limb_detection='manual', minimizer='differential-evolution')[source]
Bases:
PlanetObservationObservation of a planet’s limb (horizon).
- Parameters:
- __init__(image_filepath, fit_config, limb_detection='manual', minimizer='differential-evolution')[source]
- analyze(detect_limb_kwargs=None, fit_method='arc', fit_method_kwargs=None, fit_stages=None)[source]
Perform complete limb analysis: detection + fitting in one call.
- Parameters:
detect_limb_kwargs (
Optional[dict]) – Optional kwargs for detect_limb().fit_method (
str) – Single-stage method – “arc” (default), “gradient”, or “sagitta”. Ignored when fit_stages is provided.fit_method_kwargs (
Optional[dict]) – Optional kwargs for the chosen fit method.fit_stages (
Optional[List[dict]]) – If provided, run fit_limb(fit_stages) (multi-stage orchestrator).
- Returns:
For method chaining.
- Return type:
self
- property radius_km: float
Get the fitted planetary radius in kilometers.
- Returns:
Planetary radius in km, or 0 if not fitted
- Return type:
- property altitude_km: float
Get the observer altitude in kilometers.
- Returns:
Observer altitude in km, or 0 if not fitted
- Return type:
- property focal_length_mm: float
Get the camera focal length in millimeters.
- Returns:
Focal length in mm, or 0 if not fitted
- Return type:
- property radius_uncertainty: float
Get parameter uncertainty for radius.
Automatically selects best method based on minimizer: - differential_evolution: Uses population spread (fast, exact) - dual_annealing/basinhopping: Uses Hessian approximation (fast, approximate)
- Returns:
Radius uncertainty in km (1-sigma), or 0 if not available
- Return type:
- parameter_uncertainty(parameter, method='auto', scale_factor=1.0, confidence_level=0.68, **kwargs)[source]
Get uncertainty for any fitted parameter.
- Parameters:
parameter (
str) – Parameter name (e.g., ‘r’, ‘h’, ‘f’, ‘theta_x’)method (
Literal['auto','hessian','profile','bootstrap']) – Uncertainty method - ‘auto’: Choose based on minimizer (recommended) - ‘hessian’: Fast Hessian approximation - ‘profile’: Slow but accurate profile likelihood - ‘bootstrap’: Multiple fits (very slow)scale_factor (
float) – Scale result (e.g., 1000.0 for m→km)confidence_level (
float) – Confidence level (0.68=1σ, 0.95=2σ)**kwargs – Additional arguments passed to uncertainty calculator
- Return type:
- Returns:
dict with ‘uncertainty’, ‘method’, ‘confidence_level’, ‘additional_info’
Examples
# Radius uncertainty in km (1-sigma) obs.parameter_uncertainty(‘r’, scale_factor=1000.0)
# Altitude uncertainty in km (2-sigma / 95% CI) obs.parameter_uncertainty(‘h’, scale_factor=1000.0, confidence_level=0.95)
# Focal length uncertainty in mm (using profile likelihood) obs.parameter_uncertainty(‘f’, scale_factor=1000.0, method=’profile’)
- reset_params()[source]
Restore init_parameter_values to the original loaded values (cold start).
- Return type:
- plot_3d(**kwargs)[source]
Create 3D visualization of the planetary geometry.
- Parameters:
**kwargs – Arguments passed to plot_3d_solution
- Return type:
- load_fit_config(fit_config)[source]
Load the fit configuration from file, setting all parameters to their initial values. Missing values are filled with defaults.
- register_limb(limb)[source]
Register a detected limb.
- Parameters:
limb (np.ndarray) – Limb vector (y pixel coordinates).
- Return type:
- detect_limb(detection_method=None, log=False, y_min=0, y_max=-1, window_length=501, polyorder=1, deriv=0, delta=1, segmentation_method='sam', downsample_factor=1, interactive=True, **segmentation_kwargs)[source]
Use the instance-defined method to find the limb in our observation. Kwargs are passed to the method.
- Parameters:
detection_method (literal) –
- Detection method. Must be one of
manual
gradient-break
gradient-field
segmentation
Default (None) uses the class attribute self.limb_detection.
log (bool) – Use the log(gradient). Sometimes good for smoothing.
y_min (int) – Minimum y-position to consider.
y_max (int) – Maximum y-position to consider.
window_length (int) – Width of window to apply smoothing for each vertical. Larger means less noise but less sensitivity.
polyorder (int) – Polynomial order for smoothing.
deriv (int) – Derivative level for smoothing.
delta (int) – Delta for smoothing.
segmentation_method (str) – Model used for segmentation. Must be one of [‘sam’].
downsample_factor (int) – Downsampling used for segmentation.
interactive (bool) – Prompts user to verify segmentation via annotation tool.
segmentation_kwargs (dict) – Kwargs passed to segmentation engine.
- Return type:
- smooth_limb(fill_nan=True, **kwargs)[source]
Apply the smooth_limb function to current observation.
- fit_sagitta(n_sigma=2.0, bias_correct=False, uncertainty='both')[source]
Estimate planetary radius using the sagitta (arc-height) method.
Runs SagittaFitter: a 2-D L-BFGS-B optimizer over (kappa, theta_x) using the exact projected-sagitta formula. Updates self.init_parameter_values and self.parameter_limits so that a subsequent fit_arc/fit_gradient stage warm-starts from the result automatically.
- Parameters:
n_sigma (
float) – Radius bound width in sigma units.bias_correct (
bool) – If True and y0 is provided, correct for camera tilt bias using the apex y-offset to estimate theta_x, then refine kappa via 1-D minimization.uncertainty (
str) – Bound-width method — “ols” (cheap, single pass), “jackknife” (leave-one-out), or “both” (quadrature sum, default).
- Returns:
For method chaining.
- Return type:
self
- fit_arc(loss_function='l2', minimizer=None, minimizer_preset='balanced', minimizer_kwargs=None, max_iter=15000, seed=0, verbose=False, n_jobs=1, _dashboard=None)[source]
Fit the planet limb arc using a pixel-space cost function.
Requires a detected limb (call detect_limb() first, or register_limb()). Reads self.init_parameter_values as the initial guess and writes fitted free-parameter values back so a chained call warm-starts automatically.
- Parameters:
loss_function (
Literal['l2','l1','log-l1']) – “l2” (default), “l1”, or “log-l1”.minimizer (
Optional[Literal['differential-evolution','dual-annealing','basinhopping','scipy-minimize','shgo']]) – Minimizer name. Defaults to self.minimizer.minimizer_preset (
Literal['fast','balanced','robust','super_robust','scipy-default']) – Preset config.minimizer_kwargs (
Optional[Dict]) – Override specific minimizer kwargs.max_iter (
int) – Maximum iterations.seed (
int) – Random seed.verbose (
bool) – Print progress.n_jobs (
int) – Parallel workers. Effective only for differential-evolution and shgo; emits a UserWarning for other minimizers._dashboard – Internal — FitDashboard instance passed by fit_limb.
- Returns:
For method chaining.
- Return type:
self
- fit_gradient(resolution_stages=None, minimizer=None, minimizer_preset='balanced', minimizer_kwargs=None, image_smoothing=None, kernel_smoothing=5.0, directional_smoothing=50, directional_decay_rate=0.15, prefer_direction=None, max_iter=15000, max_iter_per_stage=None, seed=0, verbose=False, n_jobs=1, _dashboard=None)[source]
Fit using gradient field alignment, optionally with coarse-to-fine stages.
Does not require a pre-detected limb; operates directly on the image. Multi-resolution stages warm-start from the previous stage automatically.
- Parameters:
resolution_stages (
Union[List[int],Literal['auto'],None]) – Downsampling factors, e.g. [4, 2, 1] (coarse→fine). None → single full-resolution stage. “auto” → inferred from image size.minimizer (
Optional[Literal['differential-evolution','dual-annealing','basinhopping','scipy-minimize','shgo']]) – Minimizer name. Defaults to self.minimizer.minimizer_preset (
Literal['fast','balanced','robust','super_robust','scipy-default']) – Preset config.minimizer_kwargs (
Optional[Dict]) – Override specific minimizer kwargs.image_smoothing (
Optional[float]) – Gaussian blur sigma applied before optimisation (removes high-frequency artifacts; original image restored after).kernel_smoothing (
float) – Gradient field kernel size.directional_smoothing (
int) – Directional sampling distance along gradients.directional_decay_rate (
float) – Exponential decay for directional samples.prefer_direction (
Optional[Literal['up','down']]) – “up” (dark sky / bright planet), “down”, or None.max_iter (
int) – Total maximum iterations (split across stages if multi-res).max_iter_per_stage (
Optional[List[int]]) – Explicit per-stage iteration budget.seed (
int) – Random seed.verbose (
bool) – Print progress.n_jobs (
int) – Parallel workers. Effective only for differential-evolution and shgo; emits a UserWarning for other minimizers.
- Returns:
For method chaining.
- Return type:
self
- fit_limb(stages, dashboard=False, dashboard_kwargs=None, target_planet='earth', n_jobs=1)[source]
Run a multi-stage fit by sequentially calling fit_sagitta / fit_arc / fit_gradient.
Resets self.stage_results before running so the list reflects only this call. Individual fit_* calls accumulate into stage_results without resetting.
- Parameters:
stages (
List[dict]) – Ordered list of stage dicts, each with a “method” key (“sagitta”, “arc”, or “gradient”) plus kwargs for that method.dashboard (
bool) – Show a live FitDashboard during optimization.dashboard_kwargs (
Optional[Dict]) – Extra kwargs forwarded to FitDashboard.__init__.target_planet (
str) – Planet name for dashboard reference radius.n_jobs (
int) – Parallel workers forwarded to each arc/gradient stage. Effective only for differential-evolution and shgo.
- Returns:
For method chaining.
- Return type:
self
Example:
obs.fit_limb([ {"method": "sagitta", "n_sigma": 2.0}, {"method": "arc", "minimizer": "basinhopping", "minimizer_preset": "robust"}, ])
Complete workflow class for limb-based planetary radius determination. Default detection method is interactive manual annotation. Includes horizon detection, limb fitting with multi-resolution optimization, and comprehensive uncertainty analysis.
Detection Methods Available:
manual (default): Interactive GUI for precise point selection
gradient-break: Simple gradient-based detection
gradient-field: Automated detection using gradient flow analysis with directional blur
segmentation: AI-powered automatic detection (requires PyTorch + Segment Anything)
Key Features:
Multi-resolution optimization with coarse-to-fine refinement
Multiple uncertainty estimation methods (population spread, Hessian, profile likelihood)
Flexible cost functions (gradient-field flux, L1, L2, log-L1)
Support for multiple minimizers (differential-evolution, dual-annealing, basinhopping)
MaskSegmenter
- class planet_ruler.image.MaskSegmenter(image, method='sam', downsample_factor=1, interactive=True, **backend_kwargs)[source]
Bases:
objectMethod-agnostic mask-based segmentation.
Supports pluggable backends (SAM, custom algorithms, etc.) with optional downsampling and interactive classification.
Advanced image segmentation with pluggable backends. Supports Segment Anything model (optional dependency) and custom segmentation functions. Provides automated mask generation, interactive classification, and limb extraction from complex images.
TkLimbAnnotator
- class planet_ruler.annotate.TkLimbAnnotator(image_path, image=None, initial_stretch=1.0, initial_zoom=None, output_dir=None)[source]
Bases:
objectTkinter-based interactive tool for manually annotating planet limbs.
Features: - Zoom with scroll wheel (fit large images in window) - Vertical stretch buttons (stretch pixels vertically for precision) - Scrollable canvas for navigating - Click to add points, right-click to undo - Save/load points to JSON - Generate sparse target array for CostFunction
- __init__(image_path, image=None, initial_stretch=1.0, initial_zoom=None, output_dir=None)[source]
Initialize the annotation tool.
- Parameters:
image_path (str) – Path to the image to annotate
image (np.ndarray) – Optionally the already loaded image
initial_stretch (float) – Initial vertical stretch factor
initial_zoom (float) – Initial zoom level (None = auto-fit)
output_dir (str or Path, optional) – Directory for saved JSON files. Defaults to the same directory as image_path.
Interactive GUI for manual limb annotation. Provides precise user control over horizon detection with no additional dependencies required.
Key Features:
Interactive point selection: Left click to add points, right click to remove
Zoom and pan: Mouse wheel zoom, drag to pan for precise annotation
Contrast adjustment: Arrow keys to adjust image stretch/brightness
Session management: Save (‘s’) and load (‘l’) annotation sessions
Target generation: Convert sparse points to dense limb arrays
Constants and Configuration
Camera Database
The camera module includes a comprehensive database of sensor dimensions for automatic parameter extraction:
Smartphones: iPhone models (iPhone 11-14 series), Samsung Galaxy, Google Pixel, etc.
Point-and-shoot: Canon PowerShot series, Nikon Coolpix, Sony Cyber-shot
DSLRs: Canon EOS series, Nikon D series, Sony Alpha
Mirrorless: Sony ILCE series, Canon EOS R, Nikon Z series
Generic sensors: Common sensor sizes (1/2.3”, 1/1.7”, APS-C, Full Frame)
Planet Radius Database
Built-in planetary radii for initial optimization guesses (automatically perturbed):
Earth: 6,371,000 m
Mars: 3,389,500 m
Jupiter: 69,911,000 m
Saturn: 58,232,000 m
Moon: 1,737,400 m
Pluto: 1,188,300 m
And others…
Default Parameters
The following default values are used throughout Planet Ruler:
ISS altitude: 418,000 m
Image processing window: 21 pixels
Optimization tolerance: 1e-6
Maximum iterations: 1000
Perturbation factor: 50% (for initial radius guessing)
File Formats
Supported file formats:
Images: JPEG, PNG, TIFF, BMP (with EXIF support)
Configuration: YAML (.yaml, .yml), JSON (.json)
Data output: CSV, JSON, pickle
Annotation sessions: JSON
Error Handling
Planet Ruler raises specific exceptions for different error conditions:
ValueError: Invalid parameter values or configurations
FileNotFoundError: Missing image or configuration files
ImportError: Missing optional dependencies (e.g., PyTorch for Segment Anything)
RuntimeError: Optimization convergence failures
AssertionError: Configuration validation failures (when strict=True)
See individual function documentation for specific error conditions and handling recommendations.