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

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

ndarray

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.

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

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

Tkinter-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

__init__(image_path, initial_parameters=None, initial_zoom=None)[source]

Initialize the crop tool.

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

  • initial_parameters (Optional[Dict]) – Dictionary of current parameters (must include ‘w’ if provided)

  • initial_zoom (Optional[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.

update_display()[source]

Update the displayed image with current zoom.

redraw_crop_rect()[source]

Redraw crop rectangle at current zoom.

on_drag_start(event)[source]

Start dragging to define crop rectangle.

on_drag_motion(event)[source]

Update crop rectangle during drag.

on_drag_end(event)[source]

Finish dragging.

clear_selection()[source]

Clear the crop selection.

update_status()[source]

Update status text.

get_status_text()[source]

Generate status text.

Return type:

str

calculate_scaled_parameters()[source]

Calculate scaled parameters after cropping.

Return type:

Dict

Returns:

Dictionary of scaled parameters

crop_and_save()[source]

Perform crop and optionally save results.

crop_only()[source]

Perform crop without saving to disk (in-memory only).

run()[source]

Run the crop tool (blocking).

get_crop_bounds()[source]

Get crop bounds in original coordinates.

Return type:

Optional[Tuple[int, int, int, int]]

get_scaled_parameters()[source]

Get scaled parameters after cropping.

Return type:

Optional[Dict]

planet_ruler.crop.crop_observation_image(image_path, initial_parameters)[source]

Interactively crop an observation image and get scaled parameters.

Parameters:
  • image_path (str) – Path to image file

  • initial_parameters (Dict) – Current observation parameters (must include ‘w’)

Return type:

Tuple[Image, Dict, Tuple[int, int, int, int]]

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

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

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

property methods: list

Fit methods applied, in order.

property minimizers: list

Minimizers used per stage (None for sagitta).

reset_params()[source]

Restore init_parameter_values to the original loaded values (cold start).

Return type:

LimbObservation

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_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"},
])
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, image=None, initial_stretch=1.0, initial_zoom=None, output_dir=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, 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.

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, prompting user to confirm or edit the save path.

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.

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:
  • json_path (str) – Path to JSON file saved by TkLimbAnnotator

  • return_metadata (bool) – If True, return (target, metadata) tuple

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.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_aperture(exif_data)[source]

Extract aperture (f-number) from EXIF data.

Parameters:

exif_data (Dict) – EXIF dictionary

Return type:

Optional[float]

Returns:

Aperture as float (e.g., 2.4 for f/2.4), or None if not found

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.

Parameters:
  • camera_data (Dict) – Camera entry from CAMERA_DB

  • focal_length_mm (float) – Actual focal length from EXIF

  • aperture (Optional[float]) – F-number from EXIF (optional)

Return type:

Dict

Returns:

Camera module dict with sensor_width, sensor_height, etc.

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.get_image_orientation_from_exif(exif_data=None)[source]

Determine orientation from EXIF Orientation tag only.

Parameters:

exif_data (Optional[Dict]) – Pre-extracted EXIF data

Return type:

Optional[str]

Returns:

‘portrait’, ‘landscape’, or None if can’t determine

planet_ruler.camera.get_image_orientation_from_dimensions(width, height)[source]

Determine orientation from image dimensions (heuristic).

Parameters:
  • width (int) – Image width in pixels

  • height (int) – Image height in pixels

Return type:

str

Returns:

‘portrait’ or ‘landscape’

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

Parameters:
  • params (Dict) – Camera parameters dict (must have image_width_px, image_height_px)

  • exif_data (Optional[Dict]) – Optional pre-extracted EXIF data

Returns:

Modified parameters (same object, modified in-place)

Return type:

params

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:

Optional[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.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 ref by (1 - perturbation_factor), preserving asymmetry in the original bounds. With perturbation_factor=1.0 the full [lo, hi] range is used; with 0.0 only ref is 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)

  • seed (Optional[int]) – Optional seed for reproducibility

Return type:

float

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 to sample_param_from_bounds(). 1.0 (default) samples the full [lo, hi] range; 0.0 returns ref only.

  • seed (Optional[int]) – Optional seed for reproducibility.

  • 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 in param_limits are sampled in the fixed order below (unknown names are appended at the end).

Return type:

Dict[str, float]

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:

float

planet_ruler.camera.check_planet_ruler_crop_metadata(image_path)[source]

Check for crop metadata in sidecar JSON file.

Parameters:

image_path (str) – Path to image file

Return type:

Optional[Dict]

Returns:

Dict with crop metadata if found, None otherwise

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 image

  • altitude_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. Pass limits_preset instead. A deprecation warning is raised; the value is ignored and "balanced" is used.

Returns:

Configuration ready for planet_ruler

Return type:

dict

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

  • ValueError – If limits_preset is 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.

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.BaseFitter[source]

Bases: ABC

Common 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]

abstract fit()[source]
Return type:

dict

class planet_ruler.fit.BaseCostFunction(target, function, free_parameters, init_parameter_values)[source]

Bases: object

Shared evaluation logic for all cost function variants.

__init__(target, function, free_parameters, init_parameter_values)[source]
evaluate(params)[source]

Compute prediction given parameters.

For sparse manual annotation (is_sparse=True), only computes at annotated x-coordinates stored in self.x.

Return type:

ndarray

class planet_ruler.fit.L2CostFunction(target, function, free_parameters, init_parameter_values, loss_function='l2')[source]

Bases: BaseCostFunction

Cost function for l2, l1, and log-l1 loss against a detected limb.

__init__(target, function, free_parameters, init_parameter_values, loss_function='l2')[source]
cost(params)[source]
Return type:

float

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

Cost function using gradient field flux alignment.

__init__(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]
cost(params)[source]
Return type:

float

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

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

__init__(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]
fit()[source]
Return type:

dict

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

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

__init__(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]
fit()[source]
Return type:

dict

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, prompting user to confirm or edit the save path.

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([planet])

Return the known radius for a planet (metres), or 10,000 km for unknown planets.

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

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

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

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

property methods: list

Fit methods applied, in order.

property minimizers: list

Minimizers used per stage (None for sagitta).

reset_params()[source]

Restore init_parameter_values to the original loaded values (cold start).

Return type:

LimbObservation

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_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"},
])
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, image=None, initial_stretch=1.0, initial_zoom=None, output_dir=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, 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.

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, prompting user to confirm or edit the save path.

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.