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.

Parameters:
  • r (float) – Radius of the body in question.

  • h (float) – Height above surface (units should match radius).

Returns:

Distance in same units as inputs.

Return type:

d (float)

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).

Parameters:
  • r (float) – Radius of the body in question.

  • h (float) – Height above surface (units should match radius).

Returns:

Angle of camera (radians).

Return type:

theta_c (float)

planet_ruler.geometry.focal_length(w, fov)[source]

The size of the CCD (inferred) based on focal length and field of view.

Parameters:
  • w (float) – detector size (float): Width of CCD (m).

  • fov (float) – Field of view, assuming square (degrees).

Returns:

Focal length of the camera (m).

Return type:

f (float)

planet_ruler.geometry.detector_size(f, fov)[source]

The size of the CCD (inferred) based on focal length and field of view.

Parameters:
  • f (float) – Focal length of the camera (m).

  • radians (# todo really need to pick either degrees or)

  • fov (float) – Field of view, assuming square (degrees).

Returns:

Width of CCD (m).

Return type:

detector size (float)

planet_ruler.geometry.field_of_view(f, w)[source]

The size of the CCD (inferred) based on focal length and field of view.

Parameters:
  • f (float) – Focal length of the camera (m).

  • w (float) – Width of detector (m).

Returns:

Field of view, assuming square (degrees).

Return type:

fov (float)

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) axis, AKA pitch. (radians)

  • theta_y (float) – Rotation around the y (toward the limb) axis, AKA roll. (radians)

  • theta_z (float) – Rotation around the z (vertical) axis, AKA yaw. (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) axis, AKA pitch. (radians)

  • theta_y (float) – Rotation around the y (toward the limb) axis, AKA roll. (radians)

  • theta_z (float) – Rotation around the z (vertical) axis, AKA yaw. (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:

ndarray

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, **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!

Args: (same as original limb_arc)

Returns:

Array of y-coordinates for each x-pixel column

Return type:

y_pixel

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.

Parameters:

filepath (str) – Path to image file.

Return type:

ndarray

Returns:

image array (np.ndarray)

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: object

Method-agnostic mask-based segmentation.

Supports pluggable backends (SAM, custom algorithms, etc.) with optional downsampling and interactive classification.

__init__(image, method='sam', downsample_factor=1, interactive=True, **backend_kwargs)[source]

Initialize mask segmenter.

Parameters:
  • image (ndarray) – Input image (H x W x 3)

  • method (str) – Backend method (‘sam’ or ‘custom’)

  • downsample_factor (int) – Downsample factor for speed (1 = no downsampling)

  • interactive (bool) – Use interactive GUI for classification

  • **backend_kwargs – Backend-specific arguments

segment()[source]

Run segmentation pipeline.

Return type:

ndarray

Returns:

Limb coordinates (1D array of y-values)

class planet_ruler.image.SegmentationBackend[source]

Bases: object

Base class for segmentation backends.

segment(image)[source]

Segment image into masks.

Parameters:

image (ndarray) – Input image (H x W x 3)

Return type:

list

Returns:

List of mask objects (format can vary by backend)

class planet_ruler.image.SAMBackend(model_size='vit_b')[source]

Bases: SegmentationBackend

Segment Anything Model backend.

__init__(model_size='vit_b')[source]

Initialize SAM backend.

Parameters:

model_size (str) – SAM model variant (‘vit_b’, ‘vit_l’, ‘vit_h’)

segment(image)[source]

Run SAM on image.

Return type:

list

class planet_ruler.image.CustomBackend(segment_fn)[source]

Bases: SegmentationBackend

Custom user-provided segmentation backend.

__init__(segment_fn)[source]

Initialize with custom segmentation function.

Parameters:

segment_fn – Function that takes image and returns list of masks

segment(image)[source]

Run custom segmentation function.

Return type:

list

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 image

  • kernel_smoothing (float) – Sigma for initial gradient direction estimation

  • directional_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:

dict

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

Observation Module

class planet_ruler.observation.PlanetObservation(image_filepath)[source]

Bases: object

Base class for planet observations.

Parameters:

image_filepath (str) – Path to image file.

__init__(image_filepath)[source]
plot(gradient=False, show=True)[source]

Display the observation and all current features.

Parameters:
  • gradient (bool) – Show the image gradient instead of the raw image.

  • show (bool) – Show – useful as False if intending to add more to the plot before showing.

Return type:

None

class planet_ruler.observation.LimbObservation(image_filepath, fit_config, limb_detection='manual', minimizer='differential-evolution')[source]

Bases: PlanetObservation

Observation of a planet’s limb (horizon).

Parameters:
  • image_filepath (str) – Path to image file.

  • fit_config (str) – Path to fit config file.

  • limb_detection (str) – Method to locate the limb in the image.

  • minimizer (str) – Choice of minimizer. Supports ‘differential-evolution’, ‘dual-annealing’, and ‘basinhopping’.

__init__(image_filepath, fit_config, limb_detection='manual', minimizer='differential-evolution')[source]
analyze(detect_limb_kwargs=None, fit_limb_kwargs=None)[source]

Perform complete limb analysis: detection + fitting in one call.

Parameters:
  • detect_limb_kwargs (dict) – Optional arguments for detect_limb()

  • fit_limb_kwargs (dict) – Optional arguments for fit_limb()

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:

float

property altitude_km: float

Get the observer altitude in kilometers.

Returns:

Observer altitude in km, or 0 if not fitted

Return type:

float

property focal_length_mm: float

Get the camera focal length in millimeters.

Returns:

Focal length in mm, or 0 if not fitted

Return type:

float

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:

float

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:

Dict

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’)

plot_3d(**kwargs)[source]

Create 3D visualization of the planetary geometry.

Parameters:

**kwargs – Arguments passed to plot_3d_solution

Return type:

None

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.

Parameters:

fit_config (str or dict) – Path to configuration file.

Return type:

None

register_limb(limb)[source]

Register a detected limb.

Parameters:

limb (np.ndarray) – Limb vector (y pixel coordinates).

Return type:

LimbObservation

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:

LimbObservation

smooth_limb(fill_nan=True, **kwargs)[source]

Apply the smooth_limb function to current observation.

Parameters:

fill_nan (bool) – Fill any NaNs in the limb.

Return type:

None

fit_limb(loss_function='l2', max_iter=15000, resolution_stages=None, max_iter_per_stage=None, n_jobs=1, seed=0, image_smoothing=None, kernel_smoothing=5.0, directional_smoothing=50, directional_decay_rate=0.15, prefer_direction=None, minimizer=None, minimizer_preset='balanced', minimizer_kwargs=None, warm_start=False, dashboard=False, dashboard_kwargs=None, target_planet='earth', verbose=False)[source]

Fit the limb to determine planetary parameters.

Supports single-resolution or multi-resolution (coarse-to-fine) optimization. Multi-resolution is recommended for gradient_field loss to avoid local minima.

Parameters:
  • loss_function (Literal['l2', 'l1', 'log-l1', 'gradient_field']) – Loss function type - ‘l2’, ‘l1’, ‘log-l1’: Traditional (requires detected limb) - ‘gradient_field’: Direct gradient alignment (no detection needed)

  • max_iter (int) – Maximum iterations (for single-resolution or total if multires)

  • resolution_stages (Union[List[int], Literal['auto'], None]) –

    Resolution strategy - None: Single resolution (original behavior) - ‘auto’: Auto-determine stages based on image size - List[int]: Custom stages, e.g., [4, 2, 1] = 1/4 → 1/2 → full NOTE: Multi-resolution only works with gradient_field loss functions.

    Traditional loss functions (l2, l1, log-l1) require single resolution.

  • max_iter_per_stage (Optional[List[int]]) – Iterations per stage (auto if None)

  • n_jobs (int) – Number of parallel workers

  • seed (int) – Random seed for reproducibility

  • image_smoothing (Optional[float]) – 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) – For gradient_field - initial blur for gradient direction estimation. Makes the gradient field smoother for directional sampling.

  • directional_smoothing (int) – For gradient_field - sampling distance along gradients

  • directional_decay_rate (float) – For gradient_field - exponential decay for samples

  • prefer_direction (Optional[Literal['up', 'down']]) – For gradient_field - prefer ‘up’ or ‘down’ gradients where ‘up’ means dark-sky/bright-planet and v.v. (None = no preference, choose best gradient regardless of direction)

  • minimizer (str) – Choice of minimizer. Supports ‘differential-evolution’, ‘dual-annealing’, and ‘basinhopping’.

  • minimizer_preset (Literal['fast', 'balanced', 'robust', 'scipy-default']) – Optimization strategy - ‘fast’: Quick convergence, may miss global minimum - ‘balanced’: Good trade-off (default) - ‘robust’: Thorough exploration, slower

  • minimizer_kwargs (Optional[Dict]) – Override specific minimizer parameters (advanced)

  • warm_start (bool) – If True, use previous fit’s results as starting point (useful for iterative refinement). If False (default), use original init_parameter_values. Note: Multi-resolution stages always warm-start from previous stages automatically. This parameter is for warm-starting across separate fit_limb() calls.

  • dashboard (bool) – Show live progress dashboard during optimization

  • dashboard_kwargs (Optional[Dict]) – Additional kwargs for FitDashboard - output_capture: OutputCapture instance for print/log display - show_output: Show output section (default True if capture provided) - max_output_lines: Number of output lines to show (default 3) - min_refresh_delay: Fixed refresh delay (0.0 for adaptive, default) - refresh_frequency: Refresh every N iterations (default 1)

  • target_planet (str) – Reference planet for dashboard comparisons (‘earth’, ‘mars’, ‘jupiter’, ‘saturn’, ‘moon’, ‘pluto’)

  • verbose (bool) – Print detailed progress

Returns:

For method chaining

Return type:

self

Examples

# Simple single-resolution fit obs.fit_limb()

# Auto multi-resolution for gradient field obs.fit_limb(loss_function=’gradient_field’, resolution_stages=’auto’)

# Remove image artifacts before optimization obs.fit_limb(

loss_function=’gradient_field’, resolution_stages=’auto’, image_smoothing=2.0, # Remove crater rims, striations kernel_smoothing=5.0 # Smooth gradient field

)

# Custom stages with robust optimization obs.fit_limb(

loss_function=’gradient_field’, resolution_stages=[8, 4, 2, 1], minimizer_preset=’robust’

)

# Override specific minimizer parameters obs.fit_limb(

loss_function=’gradient_field’, minimizer_kwargs={‘popsize’: 25, ‘atol’: 0.5}

)

# Dashboard with output capture from planet_ruler.dashboard import OutputCapture capture = OutputCapture() with capture:

obs.fit_limb(

loss_function=’gradient_field’, dashboard=True, dashboard_kwargs={‘output_capture’: capture}

)

# Iterative refinement with warm start obs.fit_limb(loss_function=’gradient_field’, resolution_stages=’auto’) obs.fit_limb(loss_function=’gradient_field’, warm_start=True,

minimizer_preset=’robust’) # Refine with more thorough search

save_limb(filepath)[source]

Save the detected limb position as a numpy array.

Parameters:

filepath (str) – Path to save file.

Return type:

None

load_limb(filepath)[source]

Load the detected limb position from a numpy array.

Parameters:

filepath (str) – Path to save file.

Return type:

None

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: object

Simple tooltip that appears on hover.

__init__(widget, text)[source]
show_tip(event=None)[source]
hide_tip(event=None)[source]
planet_ruler.annotate.create_tooltip(widget, text)[source]

Helper function to create tooltip for a widget.

class planet_ruler.annotate.TkLimbAnnotator(image_path, initial_stretch=1.0, initial_zoom=None)[source]

Bases: object

Tkinter-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, initial_stretch=1.0, initial_zoom=None)[source]

Initialize the annotation tool.

Parameters:
  • image_path (str) – Path to the image to annotate

  • initial_stretch (float) – Initial vertical stretch factor

  • initial_zoom (float) – Initial zoom level (None = auto-fit)

create_widgets()[source]

Create all UI widgets.

auto_fit_zoom()[source]

Automatically set zoom to fit image in window.

set_zoom(zoom)[source]

Set absolute zoom level.

adjust_zoom(factor)[source]

Adjust zoom by a multiplicative factor.

on_scroll_zoom(event)[source]

Handle scroll wheel for zooming.

set_stretch(stretch)[source]

Set absolute stretch level.

adjust_stretch(delta)[source]

Adjust stretch by an additive amount.

update_stretched_image()[source]

Update the displayed image with current zoom and stretch.

redraw_points()[source]

Redraw all annotation points at current zoom and stretch.

on_left_click(event)[source]

Add a point at click location.

on_right_click(event)[source]

Undo last point.

clear_all()[source]

Clear all points.

update_status()[source]

Update status text.

get_status_text()[source]

Generate status text.

generate_target()[source]

Generate sparse target array.

save_points()[source]

Save points to JSON.

load_points()[source]

Load points from JSON.

get_target()[source]

Get the current sparse target array.

run()[source]

Start the application.

class planet_ruler.annotate.TkMaskSelector(image, masks, initial_zoom=None)[source]

Bases: object

Interactive 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 OK

  • initial_zoom (Optional[float]) – Initial zoom level (None = auto-fit to window)

__init__(image, masks, initial_zoom=None)[source]
update_canvas()[source]

Update canvas with current overlay at current zoom level.

update_mask_list()[source]

Update the mask listbox with text labels.

on_listbox_select(event)[source]

Handle mask selection from listbox.

on_canvas_click(event)[source]

Handle click on canvas to select mask.

on_key_press(event)[source]

Handle keyboard shortcuts.

start_pan(event)[source]

Start panning.

do_pan(event)[source]

Pan the canvas.

end_pan(event)[source]

End panning.

on_mousewheel(event)[source]

Zoom with mouse wheel.

reset_classifications()[source]

Reset all to exclude except first two.

finish()[source]

Close window - nuclear option for Jupyter compatibility.

run()[source]

Run the interactive selector with proper cleanup.

get_classified_masks()[source]

Return dictionary of classified masks with original mask objects.

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.extract_exif(image_path)[source]

Extract EXIF data from image.

Return type:

Dict

planet_ruler.camera.get_camera_model(exif_data)[source]

Extract camera model from EXIF data.

Return type:

Optional[str]

planet_ruler.camera.get_focal_length_mm(exif_data)[source]

Extract focal length in mm from EXIF.

Return type:

Optional[float]

planet_ruler.camera.get_focal_length_35mm_equiv(exif_data)[source]

Get 35mm equivalent focal length (more reliable for phones).

Return type:

Optional[float]

planet_ruler.camera.get_image_dimensions(image_path)[source]

Get image width and height in pixels.

Return type:

Tuple[int, int]

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

Return type:

Tuple[float, float]

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.

Return type:

Dict

planet_ruler.camera.infer_camera_type(exif_data)[source]

Try to infer camera type from EXIF data even if exact model unknown.

Return type:

Optional[str]

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:

dict

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:

float

planet_ruler.camera.get_initial_radius(planet='earth', perturbation_factor=0.5, seed=None)[source]

Get initial radius guess with perturbation.

Parameters:
  • planet (str) – Planet name

  • perturbation_factor (float) – Relative perturbation (default: 0.5 = ±50%)

  • seed (Optional[int]) – Random seed for reproducibility (default: None = unseeded)

Return type:

float

planet_ruler.camera.create_config_from_image(image_path, altitude_m=None, planet='earth', param_tolerance=0.1, perturbation_factor=0.5, seed=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 image

  • altitude_m (Optional[float]) – Altitude in meters (REQUIRED if not in GPS data)

  • planet (str) – Planet name for initial radius guess (default: ‘earth’)

  • param_tolerance (float) – Fractional tolerance for parameter limits (default: 0.1 = ±10%)

  • perturbation_factor (float) – Initial radius perturbation (default: 0.5 = ±50%)

  • seed (Optional[int]) – Random seed for reproducibility (default: None = unseeded)

Returns:

Configuration ready for planet_ruler

Return type:

dict

Raises:

ValueError – If altitude cannot be determined from GPS and not provided manually

Notes

  • Initial radius is randomly perturbed to avoid local minima and prove data-driven results

  • For uncertainty quantification, consider running multiple times (multi-start optimization)

  • Theta parameters (orientation) have wide default limits to handle r-h coupling

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.

Parameters:
  • params (list) – Values of dictionary elements in a list.

  • template (list) – Ordered list of target keys.

Returns:

Parameter dictionary.

Return type:

param_dict (dict)

planet_ruler.fit.pack_parameters(params, template)[source]

Turn a dict of parameters (or defaults) into a list.

Parameters:
  • params (dict) – Parameter dictionary (subset or full keys of template).

  • template (dict) – Template (full) parameter dictionary.

Returns:

List of parameter values.

Return type:

param_list (list)

class planet_ruler.fit.CostFunction(target, function, free_parameters, init_parameter_values, loss_function='l2', kernel_smoothing=5.0, directional_smoothing=30, directional_decay_rate=0.15, prefer_direction=None)[source]

Bases: object

Wrapper to simplify interface with the minimization at hand.

Parameters:
  • target (np.ndarray) – True value(s), e.g., the actual limb position. For gradient_field loss, this should be the image.

  • function (Callable) – Function mapping parameters to target of interest.

  • free_parameters (list) – List of free parameter names.

  • init_parameter_values (dict) – Initial values for named parameters.

  • loss_function (str) – Type of loss function, must be one of [‘l2’, ‘l1’, ‘log-l1’, ‘gradient_field’].

  • kernel_smoothing – For gradient_field - initial blur for gradient direction estimation. Makes the gradient field smoother for directional sampling.

  • directional_smoothing – For gradient_field - sampling distance along gradients

  • directional_decay_rate – For gradient_field - exponential decay for samples

  • prefer_direction (Optional[str]) – For gradient_field - prefer ‘up’ or ‘down’ gradients where ‘up’ means dark-sky/bright-planet and v.v. (None = no preference, choose best gradient regardless of direction)

__init__(target, function, free_parameters, init_parameter_values, loss_function='l2', kernel_smoothing=5.0, directional_smoothing=30, directional_decay_rate=0.15, prefer_direction=None)[source]
cost(params)[source]

Compute prediction and use desired metric to reduce difference from truth to a cost. AKA loss function.

Parameters:

params (np.ndarray | dict) – Parameter values, either packed into array or as dict.

Returns:

Cost given parameters.

Return type:

cost (float)

evaluate(params)[source]

Compute prediction given parameters.

Parameters:

params (np.ndarray | dict) – Parameter values, either packed into array or as dict.

Returns:

Prediction value(s).

Return type:

prediction (np.ndarray)

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:

dict

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.

Parameters:
  • uncertainty_result (dict) – Result from calculate_parameter_uncertainty

  • units (str) – Units to display (e.g., “km”, “m”, “degrees”)

Returns:

Formatted string representation of result

Return type:

str

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.

Parameters:
  • im_arr (np.ndarray) – Array of image values.

  • gradient (bool) – Display as gradient (y-axis).

  • show (bool) – Display the image.

Return type:

None

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).

Parameters:
  • y (np.ndarray) – Array of image values.

  • show (bool) – Display the image.

  • c (str) – Color of the limb.

  • s (int) – Size of marker.

  • alpha (float) – Opacity of markers.

Return type:

None

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:

None

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:

None

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.

Parameters:
  • observation (object) – Instance of LimbObservation (must have used differential evolution minimizer).

  • show_points (bool) – Show the individual population members in addition to the contour.

  • log (bool) – Set the y-scale to log.

Returns:

None

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.

Parameters:
  • observation (object) – Instance of LimbObservation.

  • x_min (int) – Left edge in pixels.

  • x_max (int) – Right edge in pixels.

  • y_min (int) – Bottom edge in pixels.

  • y_max (int) – Top edge in pixels.

Return type:

None

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:

None

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).

  • scale (Optional[float]) – Arrow scale factor (None = auto).

  • 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:

None

planet_ruler.plot.plot_segmentation_masks(observation)[source]

Display all the classes/masks generated by the segmentation.

Parameters:

observation (object) – Instance of LimbObservation.

Return type:

None

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:

tuple

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:
  • config (dict) – Configuration dictionary with keys: - ‘init_parameter_values’: dict of parameter initial values - ‘parameter_limits’: dict of [lower, upper] limits

  • strict (bool) – If True, raise exceptions. If False, only log warnings.

Raises:

AssertionError – If strict=True and validation fails

Return type:

None

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.

planet_ruler.cli.load_config(config_path)[source]

Load configuration from YAML or JSON file.

Return type:

Dict[str, Any]

planet_ruler.cli.main()[source]

Main CLI entry point.

planet_ruler.cli.measure_command(args)[source]

Handle the measure command.

planet_ruler.cli.demo_command(args)[source]

Handle the demo command.

planet_ruler.cli.list_command(args)[source]

Handle the list command.

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: object

Capture 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:
  • max_lines (int, default 5) – Maximum number of recent lines to keep

  • line_width (int, default 55) – Maximum width before wrapping (dashboard constraint)

  • capture_stderr (bool, default True) – Also capture stderr (warnings, errors)

Examples

>>> capture = OutputCapture(max_lines=5)
>>> with capture:
...     print("Iteration 100: loss = 123.45")
...     print("Warning: parameter drift detected")
>>> lines = capture.get_lines()
__init__(max_lines=5, line_width=55, capture_stderr=True)[source]
write(text)[source]

Write text to buffer (called by sys.stdout redirect).

Return type:

None

flush()[source]

Flush (no-op, required for file-like interface).

Return type:

None

get_lines()[source]

Get recent output lines (thread-safe).

Return type:

List[str]

clear()[source]

Clear captured output.

Return type:

None

__enter__()[source]

Start capturing (context manager).

__exit__(exc_type, exc_val, exc_tb)[source]

Stop capturing and restore streams.

planet_ruler.dashboard.is_jupyter()[source]

Check if running in Jupyter notebook.

Return type:

bool

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: object

Live 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)

iteration

Current iteration number

Type:

int

loss_history

Loss values over time

Type:

list of float

param_history

Parameter values over time

Type:

list of dict

__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.

Parameters:
  • current_params (dict) – Current parameter values (r, h, f, w, etc.)

  • loss (float) – Current loss function value

Return type:

None

finalize(success=True)[source]

Show final summary after optimization completes.

Parameters:

success (bool, default True) – Whether optimization converged successfully

Return type:

None

start_stage(stage_num, resolution_label, max_iter)[source]

Start a new optimization stage (for multi-resolution).

Parameters:
  • stage_num (int) – Stage number (1-indexed)

  • resolution_label (str) – Resolution label (e.g., “1/4x”, “1/2x”, “full”)

  • max_iter (int) – Maximum iterations for this stage

Return type:

None

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

planet_ruler.demo.make_dropdown()[source]
planet_ruler.demo.load_demo_parameters(demo)[source]
planet_ruler.demo.display_text(filepath)[source]

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:

planet_ruler.geometry.horizon_distance(r, h)

Estimate the distance to the horizon (limb) given a height and radius.

planet_ruler.geometry.limb_camera_angle(r, h)

The angle the camera must tilt in theta_x or theta_y to center the limb.

planet_ruler.geometry.focal_length(w, fov)

The size of the CCD (inferred) based on focal length and field of view.

planet_ruler.geometry.detector_size(f, fov)

The size of the CCD (inferred) based on focal length and field of view.

planet_ruler.geometry.field_of_view(f, w)

The size of the CCD (inferred) based on focal length and field of view.

planet_ruler.geometry.get_rotation_matrix(...)

Compute combined rotation matrix from Euler angles.

Image Processing Functions

Computer vision and image analysis:

planet_ruler.image.load_image(filepath)

Load a 3 or 4-channel image from filepath into an array.

planet_ruler.image.gradient_break(im_arr[, ...])

Scan each vertical line of an image for the maximum change in brightness gradient -- usually corresponds to a horizon.

planet_ruler.image.smooth_limb(y[, method, ...])

Smooth the limb position values.

planet_ruler.image.fill_nans(limb)

Fill NaNs for the limb position values.

planet_ruler.image.bilinear_interpolate(...)

Bilinear interpolation for 2D array at non-integer coordinates.

planet_ruler.image.gradient_field(image[, ...])

Pre-compute gradient field AND its derivatives for fast sub-pixel interpolation.

Manual Annotation Functions

Interactive limb detection and annotation:

planet_ruler.annotate.TkLimbAnnotator(image_path)

Tkinter-based interactive tool for manually annotating planet limbs.

planet_ruler.annotate.TkLimbAnnotator.run()

Start the application.

planet_ruler.annotate.TkLimbAnnotator.get_target()

Get the current sparse target array.

planet_ruler.annotate.TkLimbAnnotator.generate_target()

Generate sparse target array.

planet_ruler.annotate.TkLimbAnnotator.save_points()

Save points to JSON.

planet_ruler.annotate.TkLimbAnnotator.load_points()

Load points from JSON.

Camera Parameter Functions

Automatic camera parameter extraction:

planet_ruler.camera.extract_exif(image_path)

Extract EXIF data from image.

planet_ruler.camera.get_camera_model(exif_data)

Extract camera model from EXIF data.

planet_ruler.camera.get_focal_length_mm(...)

Extract focal length in mm from EXIF.

planet_ruler.camera.get_focal_length_35mm_equiv(...)

Get 35mm equivalent focal length (more reliable for phones).

planet_ruler.camera.extract_camera_parameters(...)

Automatically extract all camera parameters from any camera image.

planet_ruler.camera.get_gps_altitude(image_path)

Extract GPS altitude from EXIF data if available.

planet_ruler.camera.create_config_from_image(...)

Create a complete planet_ruler configuration from an image.

planet_ruler.camera.get_initial_radius([...])

Get initial radius guess with perturbation.

Coordinate Transform Functions

Camera geometry and projections:

planet_ruler.geometry.intrinsic_transform(...)

Transform from camera coordinates into image coordinates.

planet_ruler.geometry.extrinsic_transform(...)

Transform from world coordinates into camera coordinates.

planet_ruler.geometry.limb_arc(r, n_pix_x, ...)

Calculate limb position analytically at each pixel x-coordinate.

planet_ruler.geometry.limb_arc_sample(r, ...)

Calculate the limb orientation in an image given the physical parameters of the system.

Optimization Functions

Parameter fitting and uncertainty:

planet_ruler.fit.pack_parameters(params, ...)

Turn a dict of parameters (or defaults) into a list.

planet_ruler.fit.unpack_parameters(params, ...)

Turn a list of parameters back into a dict.

planet_ruler.fit.calculate_parameter_uncertainty(...)

Calculate parameter uncertainty from fitting results.

planet_ruler.fit.format_parameter_result(...)

Format parameter uncertainty results for display.

planet_ruler.uncertainty.calculate_parameter_uncertainty(...)

Calculate uncertainty for a fitted parameter using appropriate method.

planet_ruler.uncertainty._uncertainty_from_population(...)

Uncertainty from differential evolution population.

planet_ruler.uncertainty._uncertainty_from_hessian(...)

Uncertainty from inverse Hessian (covariance) at optimum.

planet_ruler.uncertainty._uncertainty_from_profile(...)

Uncertainty from profile likelihood.

planet_ruler.uncertainty._uncertainty_from_bootstrap(...)

Uncertainty from bootstrap: run optimizer multiple times with different seeds.

Validation Functions

Configuration and parameter validation:

planet_ruler.validation.validate_limb_config(config)

Validate that a planet_ruler configuration is internally consistent.

Command-Line Functions

CLI interface and commands:

planet_ruler.cli.main()

Main CLI entry point.

planet_ruler.cli.measure_command(args)

Handle the measure command.

planet_ruler.cli.demo_command(args)

Handle the demo command.

planet_ruler.cli.list_command(args)

Handle the list command.

planet_ruler.cli.load_config(config_path)

Load configuration from YAML or JSON file.

Visualization Functions

Plotting and display:

planet_ruler.plot.plot_image(im_arr[, ...])

Display an image using matplotlib.pyplot.imshow.

planet_ruler.plot.plot_limb(y[, show, c, s, ...])

Display the limb (usually on top of an image).

planet_ruler.plot.plot_3d_solution(r[, h, ...])

Plot a limb solution in 3D.

planet_ruler.plot.plot_topography(image)

Display the full limb, including the section not seen in the image.

planet_ruler.plot.plot_diff_evol_posteriors(...)

Extract and display the final state population of a differential evolution minimization.

planet_ruler.plot.plot_full_limb(observation)

Display the full limb, including the section not seen in the image.

planet_ruler.plot.plot_segmentation_masks(...)

Display all the classes/masks generated by the segmentation.

Classes

CostFunction

class planet_ruler.fit.CostFunction(target, function, free_parameters, init_parameter_values, loss_function='l2', kernel_smoothing=5.0, directional_smoothing=30, directional_decay_rate=0.15, prefer_direction=None)[source]

Bases: object

Wrapper to simplify interface with the minimization at hand.

Parameters:
  • target (np.ndarray) – True value(s), e.g., the actual limb position. For gradient_field loss, this should be the image.

  • function (Callable) – Function mapping parameters to target of interest.

  • free_parameters (list) – List of free parameter names.

  • init_parameter_values (dict) – Initial values for named parameters.

  • loss_function (str) – Type of loss function, must be one of [‘l2’, ‘l1’, ‘log-l1’, ‘gradient_field’].

  • kernel_smoothing – For gradient_field - initial blur for gradient direction estimation. Makes the gradient field smoother for directional sampling.

  • directional_smoothing – For gradient_field - sampling distance along gradients

  • directional_decay_rate – For gradient_field - exponential decay for samples

  • prefer_direction (Optional[str]) – For gradient_field - prefer ‘up’ or ‘down’ gradients where ‘up’ means dark-sky/bright-planet and v.v. (None = no preference, choose best gradient regardless of direction)

__init__(target, function, free_parameters, init_parameter_values, loss_function='l2', kernel_smoothing=5.0, directional_smoothing=30, directional_decay_rate=0.15, prefer_direction=None)[source]
cost(params)[source]

Compute prediction and use desired metric to reduce difference from truth to a cost. AKA loss function.

Parameters:

params (np.ndarray | dict) – Parameter values, either packed into array or as dict.

Returns:

Cost given parameters.

Return type:

cost (float)

evaluate(params)[source]

Compute prediction given parameters.

Parameters:

params (np.ndarray | dict) – Parameter values, either packed into array or as dict.

Returns:

Prediction value(s).

Return type:

prediction (np.ndarray)

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: object

Base class for planet observations.

Parameters:

image_filepath (str) – Path to image file.

__init__(image_filepath)[source]
plot(gradient=False, show=True)[source]

Display the observation and all current features.

Parameters:
  • gradient (bool) – Show the image gradient instead of the raw image.

  • show (bool) – Show – useful as False if intending to add more to the plot before showing.

Return type:

None

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: PlanetObservation

Observation of a planet’s limb (horizon).

Parameters:
  • image_filepath (str) – Path to image file.

  • fit_config (str) – Path to fit config file.

  • limb_detection (str) – Method to locate the limb in the image.

  • minimizer (str) – Choice of minimizer. Supports ‘differential-evolution’, ‘dual-annealing’, and ‘basinhopping’.

__init__(image_filepath, fit_config, limb_detection='manual', minimizer='differential-evolution')[source]
analyze(detect_limb_kwargs=None, fit_limb_kwargs=None)[source]

Perform complete limb analysis: detection + fitting in one call.

Parameters:
  • detect_limb_kwargs (dict) – Optional arguments for detect_limb()

  • fit_limb_kwargs (dict) – Optional arguments for fit_limb()

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:

float

property altitude_km: float

Get the observer altitude in kilometers.

Returns:

Observer altitude in km, or 0 if not fitted

Return type:

float

property focal_length_mm: float

Get the camera focal length in millimeters.

Returns:

Focal length in mm, or 0 if not fitted

Return type:

float

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:

float

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:

Dict

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’)

plot_3d(**kwargs)[source]

Create 3D visualization of the planetary geometry.

Parameters:

**kwargs – Arguments passed to plot_3d_solution

Return type:

None

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.

Parameters:

fit_config (str or dict) – Path to configuration file.

Return type:

None

register_limb(limb)[source]

Register a detected limb.

Parameters:

limb (np.ndarray) – Limb vector (y pixel coordinates).

Return type:

LimbObservation

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:

LimbObservation

smooth_limb(fill_nan=True, **kwargs)[source]

Apply the smooth_limb function to current observation.

Parameters:

fill_nan (bool) – Fill any NaNs in the limb.

Return type:

None

fit_limb(loss_function='l2', max_iter=15000, resolution_stages=None, max_iter_per_stage=None, n_jobs=1, seed=0, image_smoothing=None, kernel_smoothing=5.0, directional_smoothing=50, directional_decay_rate=0.15, prefer_direction=None, minimizer=None, minimizer_preset='balanced', minimizer_kwargs=None, warm_start=False, dashboard=False, dashboard_kwargs=None, target_planet='earth', verbose=False)[source]

Fit the limb to determine planetary parameters.

Supports single-resolution or multi-resolution (coarse-to-fine) optimization. Multi-resolution is recommended for gradient_field loss to avoid local minima.

Parameters:
  • loss_function (Literal['l2', 'l1', 'log-l1', 'gradient_field']) – Loss function type - ‘l2’, ‘l1’, ‘log-l1’: Traditional (requires detected limb) - ‘gradient_field’: Direct gradient alignment (no detection needed)

  • max_iter (int) – Maximum iterations (for single-resolution or total if multires)

  • resolution_stages (Union[List[int], Literal['auto'], None]) –

    Resolution strategy - None: Single resolution (original behavior) - ‘auto’: Auto-determine stages based on image size - List[int]: Custom stages, e.g., [4, 2, 1] = 1/4 → 1/2 → full NOTE: Multi-resolution only works with gradient_field loss functions.

    Traditional loss functions (l2, l1, log-l1) require single resolution.

  • max_iter_per_stage (Optional[List[int]]) – Iterations per stage (auto if None)

  • n_jobs (int) – Number of parallel workers

  • seed (int) – Random seed for reproducibility

  • image_smoothing (Optional[float]) – 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) – For gradient_field - initial blur for gradient direction estimation. Makes the gradient field smoother for directional sampling.

  • directional_smoothing (int) – For gradient_field - sampling distance along gradients

  • directional_decay_rate (float) – For gradient_field - exponential decay for samples

  • prefer_direction (Optional[Literal['up', 'down']]) – For gradient_field - prefer ‘up’ or ‘down’ gradients where ‘up’ means dark-sky/bright-planet and v.v. (None = no preference, choose best gradient regardless of direction)

  • minimizer (str) – Choice of minimizer. Supports ‘differential-evolution’, ‘dual-annealing’, and ‘basinhopping’.

  • minimizer_preset (Literal['fast', 'balanced', 'robust', 'scipy-default']) – Optimization strategy - ‘fast’: Quick convergence, may miss global minimum - ‘balanced’: Good trade-off (default) - ‘robust’: Thorough exploration, slower

  • minimizer_kwargs (Optional[Dict]) – Override specific minimizer parameters (advanced)

  • warm_start (bool) – If True, use previous fit’s results as starting point (useful for iterative refinement). If False (default), use original init_parameter_values. Note: Multi-resolution stages always warm-start from previous stages automatically. This parameter is for warm-starting across separate fit_limb() calls.

  • dashboard (bool) – Show live progress dashboard during optimization

  • dashboard_kwargs (Optional[Dict]) – Additional kwargs for FitDashboard - output_capture: OutputCapture instance for print/log display - show_output: Show output section (default True if capture provided) - max_output_lines: Number of output lines to show (default 3) - min_refresh_delay: Fixed refresh delay (0.0 for adaptive, default) - refresh_frequency: Refresh every N iterations (default 1)

  • target_planet (str) – Reference planet for dashboard comparisons (‘earth’, ‘mars’, ‘jupiter’, ‘saturn’, ‘moon’, ‘pluto’)

  • verbose (bool) – Print detailed progress

Returns:

For method chaining

Return type:

self

Examples

# Simple single-resolution fit obs.fit_limb()

# Auto multi-resolution for gradient field obs.fit_limb(loss_function=’gradient_field’, resolution_stages=’auto’)

# Remove image artifacts before optimization obs.fit_limb(

loss_function=’gradient_field’, resolution_stages=’auto’, image_smoothing=2.0, # Remove crater rims, striations kernel_smoothing=5.0 # Smooth gradient field

)

# Custom stages with robust optimization obs.fit_limb(

loss_function=’gradient_field’, resolution_stages=[8, 4, 2, 1], minimizer_preset=’robust’

)

# Override specific minimizer parameters obs.fit_limb(

loss_function=’gradient_field’, minimizer_kwargs={‘popsize’: 25, ‘atol’: 0.5}

)

# Dashboard with output capture from planet_ruler.dashboard import OutputCapture capture = OutputCapture() with capture:

obs.fit_limb(

loss_function=’gradient_field’, dashboard=True, dashboard_kwargs={‘output_capture’: capture}

)

# Iterative refinement with warm start obs.fit_limb(loss_function=’gradient_field’, resolution_stages=’auto’) obs.fit_limb(loss_function=’gradient_field’, warm_start=True,

minimizer_preset=’robust’) # Refine with more thorough search

save_limb(filepath)[source]

Save the detected limb position as a numpy array.

Parameters:

filepath (str) – Path to save file.

Return type:

None

load_limb(filepath)[source]

Load the detected limb position from a numpy array.

Parameters:

filepath (str) – Path to save file.

Return type:

None

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: object

Method-agnostic mask-based segmentation.

Supports pluggable backends (SAM, custom algorithms, etc.) with optional downsampling and interactive classification.

__init__(image, method='sam', downsample_factor=1, interactive=True, **backend_kwargs)[source]

Initialize mask segmenter.

Parameters:
  • image (ndarray) – Input image (H x W x 3)

  • method (str) – Backend method (‘sam’ or ‘custom’)

  • downsample_factor (int) – Downsample factor for speed (1 = no downsampling)

  • interactive (bool) – Use interactive GUI for classification

  • **backend_kwargs – Backend-specific arguments

segment()[source]

Run segmentation pipeline.

Return type:

ndarray

Returns:

Limb coordinates (1D array of y-values)

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, initial_stretch=1.0, initial_zoom=None)[source]

Bases: object

Tkinter-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, initial_stretch=1.0, initial_zoom=None)[source]

Initialize the annotation tool.

Parameters:
  • image_path (str) – Path to the image to annotate

  • initial_stretch (float) – Initial vertical stretch factor

  • initial_zoom (float) – Initial zoom level (None = auto-fit)

create_widgets()[source]

Create all UI widgets.

auto_fit_zoom()[source]

Automatically set zoom to fit image in window.

set_zoom(zoom)[source]

Set absolute zoom level.

adjust_zoom(factor)[source]

Adjust zoom by a multiplicative factor.

on_scroll_zoom(event)[source]

Handle scroll wheel for zooming.

set_stretch(stretch)[source]

Set absolute stretch level.

adjust_stretch(delta)[source]

Adjust stretch by an additive amount.

update_stretched_image()[source]

Update the displayed image with current zoom and stretch.

redraw_points()[source]

Redraw all annotation points at current zoom and stretch.

on_left_click(event)[source]

Add a point at click location.

on_right_click(event)[source]

Undo last point.

clear_all()[source]

Clear all points.

update_status()[source]

Update status text.

get_status_text()[source]

Generate status text.

generate_target()[source]

Generate sparse target array.

save_points()[source]

Save points to JSON.

load_points()[source]

Load points from JSON.

get_target()[source]

Get the current sparse target array.

run()[source]

Start the application.

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.