Skip to main content

Measure planetary radii from horizon photographs using computer vision and geometry

Project description

PLANET RULER

Measure planetary radii with nothing but a camera and science!

PyPI version Documentation CI/CD Pipeline codecov License Python Versions

Got a horizon photo? Measure your planet in 3 lines of code!

import planet_ruler as pr
obs = pr.LimbObservation("horizon_photo.jpg", "config/camera.yaml")
obs.detect_limb().fit_arc()  # Planet radius: 6,234 km

Commercial Aircraft View
Ground Level
~100 ft
Horizon appears flat
High Altitude Balloon
Commerical Aircraft
~35,000 ft
Perceptible curvature
Space Station View
International Space Station
~250 miles
Dramatic spherical curvature

Quick Start

Installation

From PyPI (recommended):

pip install planet-ruler

The package name is planet-ruler (with hyphen), but you import it with an underscore:

import planet_ruler as pr  # Import uses underscore

Optional dependencies:

For ML segmentation:

pip install planet-ruler[ml]

For Jupyter notebooks:

pip install planet-ruler[jupyter]

For everything:

pip install planet-ruler[all]

From source (development):

git clone https://github.com/bogsdarking/planet_ruler.git
cd planet_ruler
pip install -e .

After installation, the command-line tool is available:

planet-ruler --help

Python API

import planet_ruler as pr

# Basic analysis
obs = pr.LimbObservation("photo.jpg", "camera_config.yaml")

# Choose detection method:
obs.detect_limb(detection_method='manual')          # Interactive GUI (default)
# obs.detect_limb(detection_method='gradient-break')  # Simple gradient-based detection
# obs.detect_limb(detection_method='gradient-field')  # Gradient flow analysis
# obs.detect_limb(detection_method='segmentation')    # AI-powered (requires PyTorch)

# OR skip detection and fit directly to image gradients:
# obs.fit_gradient(resolution_stages='auto')  # Gradient-field optimization

obs.fit_arc()      # Fit to detected limb points
obs.plot()         # Visualize results

# Access results with uncertainty
print(f"Radius: {obs.best_parameters['r']/1000:.0f} ± {obs.radius_uncertainty:.0f} km")

Command Line

# Measure planetary radius using existing config file
planet-ruler measure photo.jpg --camera-config camera_config.yaml

# Auto-generate config from image EXIF data (requires altitude)
planet-ruler measure photo.jpg --auto-config --altitude 10668

# Auto-config with specific planet (affects initial radius guess)
planet-ruler measure photo.jpg --auto-config --altitude 10668 --planet mars

# Choose detection method (manual, gradient-break, gradient-field, or segmentation)
planet-ruler measure photo.jpg --auto-config --altitude 10668 --detection-method gradient-field

# Try built-in examples
planet-ruler demo --planet earth

The Science Behind It

The Problem: How big is your planet?

The Solution: Depending on your altitude, planetary curvature becomes visible in the horizon. By measuring this curvature and accounting for your camera, we can reverse-engineer the planet's size.

How It Works (Click to expand)
  1. Capture: Photograph showing horizon/limb from altitude
  2. Detect: Choose your detection method:
    • Manual: Interactive GUI for precise point selection (default, no dependencies)
    • Gradient-field: Automated detection using gradient flow analysis
    • Segmentation: AI-powered detection (requires PyTorch + Segment Anything)
  3. Measure: Extract curvature from the detected horizon
  4. Model: Account for camera optics, altitude, and viewing geometry
  5. Optimize: Fit theoretical curves to observations using multi-resolution optimization
  6. Uncertainty: Quantify measurement precision using population spread, Hessian approximation, or profile likelihood

Mathematical Foundation:

  • Spherical geometry and horizon distance calculations
  • Camera intrinsic/extrinsic parameter modeling
  • Gradient-field analysis with directional blur and flux integration
  • Multi-resolution optimization with coarse-to-fine refinement
  • Non-linear optimization with robust error handling

Why This Works

A fundamental question: At what altitude does Earth's curvature become detectable in a photograph?

The surprising answer: With modern phone cameras, curvature is detectable from essentially ground level (1-3 meters altitude).

The geometric signal—the vertical "sag" of the horizon arc across your image—exceeds one pixel even at remarkably low altitudes:

  • 10 m altitude: ~4 pixels of curvature (detectable)
  • 100 m altitude: ~12 pixels of curvature (easily measurable)
  • 10 km altitude (airplane): ~115 pixels of curvature (excellent signal)
  • 100 km altitude (space): ~360 pixels of curvature (hemisphere-scale)

Modern high-resolution sensors (4000+ pixels wide) can resolve these tiny angular deviations. The limiting factors aren't geometric visibility but rather:

  • Atmospheric refraction and haze
  • Optical quality (lens aberrations)
  • Scene complexity (buildings, trees obscuring horizon)
  • Measurement precision in the fitting algorithm

This means horizon-based radius measurement is fundamentally sound across the entire altitude range from ground level to orbit. The method gets easier at higher altitudes (cleaner images, larger signal), but it works even from remarkably low vantage points.

See the math: Minimum Detectable Altitude | Try it yourself: Interactive Notebook

Real Results

Validated on actual space mission data:

Planet Source Estimated True Value Error
Earth ISS Photo 5,516 km 6,371 km 13.4%
Saturn Cassini 65,402 km 58,232 km 12.3%
Pluto New Horizons 1,432 km 1,188 km 20.6%

Key Features

Automatic Camera Detection

  • Auto-extract camera parameters from EXIF data
  • Supports phones, DSLRs, mirrorless, point-and-shoot
  • No manual camera configuration needed

Flexible Detection & Fitting Methods

  • Manual: Interactive GUI with precision controls (default)
  • Gradient-field: Automated detection via directional blur and flux analysis
  • Sagitta: Fast radius estimate from horizon arc-height (new in 2.0)
  • Staged fitting: Chain methods (e.g. sagitta → arc) for speed + accuracy
  • AI Segmentation: Deep learning-powered (optional)

Advanced Camera Models

  • Camera parameter optimization
  • Multi-camera phone support (wide/main/tele inferred from EXIF)
  • Interactive cropping tool (TkImageCropper) with auto-scaled parameters
  • Flexible parameter limit presets (tight / balanced / loose)

Multi-Planetary Support

  • Earth, Saturn, Pluto examples included
  • Extensible to any spherical body

Scientific Rigor

  • Multi-resolution optimization with coarse-to-fine refinement
  • Advanced uncertainty estimation (population spread, Hessian, profile likelihood)
  • Mathematical validation with property tests

Live Progress Dashboard

  • Real-time optimization monitoring
  • Adaptive refresh rate (fast during descent, slow at convergence)
  • Configurable warnings, hints, and output display

Rich Visualizations

  • Interactive plots
  • 3D planetary geometry views

Multiple Interfaces

  • Python API for scripting
  • Command-line tool for automation
  • Jupyter notebooks for exploration

Works with Any Camera

  • iPhones, Android phones, DSLRs, mirrorless
  • Automatic sensor size detection
  • Intelligent parameter estimation

Installation & Setup

Requirements

  • Python 3.10+
  • RAM: 4GB+ recommended (for AI models)
  • Storage: ~2GB for full installation with models

Install Options

Quick Start (Recommended)
# Clone and install in one go
git clone https://github.com/bogsdarking/planet_ruler.git
cd planet_ruler
python -m pip install -e .

# Verify installation
planet-ruler --help
python -c "import planet_ruler; print('Ready to measure planets!')"
Minimal Install (Core features only)
git clone https://github.com/bogsdarking/planet_ruler.git
cd planet_ruler

# Install without heavy AI dependencies
python -m pip install -e . --no-deps
python -m pip install numpy scipy matplotlib pillow pyyaml pandas tqdm seaborn

# Note: Manual horizon detection required without segment-anything
Development Install
git clone https://github.com/bogsdarking/planet_ruler.git
cd planet_ruler

# Full development environment
python -m pip install -e .
python -m pip install -r requirements.txt
python -m pip install -r requirements-test.txt

# Run tests to verify
pytest tests/ -v

Troubleshooting

  • Segment Anything issues? See installation guide
  • M1 Mac problems? Use conda for better compatibility
  • Memory errors? Try the minimal install option

Try It Now

Zero-Configuration Workflow

# Just need your photo and altitude - planet_ruler handles the rest!
from planet_ruler.camera import create_config_from_image
import planet_ruler as pr

# Step 1: Auto-detect camera parameters
config = create_config_from_image("my_photo.jpg", altitude_m=10668)

# Step 2: Measure the planet
obs = pr.LimbObservation("my_photo.jpg", config)
obs.detect_limb().fit_arc()

print(f"Your planet's radius: {obs.best_parameters['r']/1000:.0f} km")

Interactive Demo

# Launch interactive widget with examples (in Jupyter notebook)
from planet_ruler.demo import make_dropdown, load_demo_parameters
demo = make_dropdown()  # Choose Earth, Saturn, or Pluto
params = load_demo_parameters(demo)

Use Your Own Photo

Option 1: Auto-Config

import planet_ruler as pr
from planet_ruler.camera import create_config_from_image

# Auto-generate config from image EXIF data
config = create_config_from_image("your_photo.jpg", altitude_m=10668, planet="earth")

# Use the auto-generated config
obs = pr.LimbObservation("your_photo.jpg", config)
obs.detect_limb()
obs.fit_arc()

print(f"Detected camera: {config['camera_info']['camera_model']}")
print(f"Fitted radius: {obs.best_parameters['r']/1000:.0f} km")

Option 2: Manual Config File

import planet_ruler as pr

# Requires camera configuration file
obs = pr.LimbObservation(
    "your_photo.jpg",
    "config/your_camera.yaml"
)

# Choose detection method: 'manual', 'gradient-break', 'gradient-field', or 'segmentation'
obs.detect_limb(detection_method='manual')  # Interactive GUI detection
# obs.detect_limb(detection_method='gradient-break')  # Simple gradient detection
# obs.detect_limb(detection_method='gradient-field')  # Gradient flow analysis

# OR skip detection and fit directly to image gradients:
# obs.fit_gradient(resolution_stages='auto')

obs.detect_limb()
obs.fit_arc()

print(f"Fitted radius: {obs.best_parameters['r']/1000:.0f} km")

Camera Configuration Template

# config/your_camera.yaml
description: "Your camera setup"
free_parameters:
  - r  # planetary radius
  - h  # altitude
  - f  # focal length
  - w  # sensor width

init_parameter_values:
  r: 6371000      # Earth radius in meters
  h: 400000       # Altitude in meters
  f: 0.05         # Focal length in meters
  w: 0.035        # Sensor width in meters

parameter_limits:
  r: [1000000, 20000000]
  h: [100000, 1000000]
  f: [0.01, 0.2]
  w: [0.01, 0.1]

Usage Examples

Example 1: Smartphone Photo with Auto-Config

import planet_ruler as pr
from planet_ruler.camera import create_config_from_image

# Your iPhone/Android photo with horizon - just need altitude!
config = create_config_from_image(
    "airplane_window_photo.jpg",
    altitude_m=10668,  # 35,000 feet in meters
    planet="earth"
)

print(f"Detected: {config['camera_info']['camera_model']}")
# -> "iPhone 14 Pro" (or your camera model)

# Measure the planet
obs = pr.LimbObservation("airplane_window_photo.jpg", config)
obs.detect_limb()
obs.fit_arc()
obs.plot()

print(f"Earth radius: {obs.best_parameters['r']/1000:.0f} km")

Example 2: Command Line with Auto-Config

# Simple one-liner with any camera!
planet-ruler measure my_photo.jpg --auto-config --altitude 10668

# With specific planet and detection method
planet-ruler measure mars_photo.jpg --auto-config --altitude 4500 --planet mars --detection-method gradient-field

# Override auto-detected parameters if needed
planet-ruler measure photo.jpg --auto-config --altitude 10668 --focal-length 50

Example 3: Earth from ISS (Traditional Config)

import planet_ruler as pr

# Load ISS Earth photo with manual configuration
obs = pr.LimbObservation(
    "demo/images/iss064e002941.jpg",
    "config/earth_iss_1.yaml"
)

# Full analysis pipeline
obs.fit_gradient(resolution_stages='auto')
obs.plot()                                 # Visualize results

# Results
print(f"Best fit parameters: {obs.best_parameters}")
print(f"Planetary radius (r): {obs.best_parameters['r']/1000:.0f} km")

Example 4: Saturn from Cassini

# Analyze Saturn's limb from Cassini spacecraft
obs = pr.LimbObservation(
    "demo/images/saturn_pia21341-1041.jpg",
    "config/saturn-cassini-1.yaml"
)

# Two-step analysis
obs.fit_gradient(resolution_stages='auto')

# Rich visualization
from planet_ruler.plot import plot_3d_solution
plot_3d_solution(**obs.best_parameters)  # 3D planetary geometry view

Documentation & Resources

Learning Resources

Resource Description Best For
Interactive Tutorial Complete walkthrough with examples First-time users
API Documentation Detailed function reference Developers
Camera Setup Guide Configuration examples Custom setups
Example Gallery Real space mission results Inspiration

Quick References

# Core classes and functions
pr.LimbObservation(image_path, fit_config)                  # Main analysis class
pr.geometry.horizon_distance(altitude, radius)              # Theoretical calculations
pr.uncertainty.calculate_parameter_uncertainty(obs, 'r')   # Uncertainty estimation

# Fitting methods (choose one, or chain via fit_limb stages)
obs.fit_arc()                              # Fit arc to detected limb points (default)
obs.fit_gradient(resolution_stages='auto') # Fit directly to image gradients
obs.fit_sagitta()                          # Fast sagitta-based radius estimate

# Multi-stage chained fit (sagitta → arc is a reliable combo)
obs.fit_limb(stages=[{"method": "sagitta"}, {"method": "arc"}])

# Other key methods
obs.detect_limb(detection_method='manual')  # Interactive horizon detection
obs.plot()                                  # Show results with uncertainty

Use Cases & Applications

  • Astronomy courses: Demonstrate planetary geometry concepts
  • Computer vision: Real-world optimization and AI applications
  • Mathematics: Applied geometry and curve-fitting examples
  • Physics: Observational techniques and measurement uncertainty

Limitations & Best Practices

Accuracy Expectations

  • Typical accuracy: ~20%
  • Best case: ~15% with optimal conditions and camera calibration (so far!)
  • Factors affecting precision: Image quality, horizon clarity, altitude, camera specs

Technical Limitations

  • Optimization challenges: Complex parameter space → potential local minima (mitigated by multi-resolution optimization)
  • Detection method trade-offs: Manual (precise, time-intensive), gradient-field (automated, works for clear horizons), AI segmentation (most versatile, requires PyTorch)
  • Computational cost: Multi-resolution optimization can be slow on older hardware

Best Practices

  1. Image quality: Sharp, high-resolution horizons work best
  2. Altitude: Higher = more curvature = better measurements
  3. Camera knowledge: Focal length and sensor specs improve results
  4. Horizon clarity: Mountains, clouds, or haze reduce accuracy
  5. Run multiple optimizations and compare results for consistency

Contributing

We welcome contributions from astronomers, developers, educators, and enthusiasts!

Planet Ruler is maintained by one developer in their spare time. Issue responses may take 3-7 days. Before opening an issue, please check the documentation and existing issues.

Quick Contribution Setup

# Fork the repo, then:
git clone https://github.com/YOUR_USERNAME/planet_ruler.git
cd planet_ruler
python -m pip install -e . && python -m pip install -r requirements.txt && python -m pip install -r requirements-test.txt
python -m pytest tests/ -v -m "not slow" -m "not real_data" # Verify everything works

Advanced Testing

More comprehensive tests/suites require images stored in Git LFS. To run these:

# Install Git LFS (one-time)
git lfs install

# Pull test images
git lfs pull

# Run tests
python -m pytest tests/

To skip tests requiring images:

python -m pytest -m "not real_data"

Ways to Contribute

Type Examples Good For
Bug Reports Detection failures, optimization issues Everyone
Features New algorithms, UI improvements Developers
Documentation Tutorials, examples, API docs Educators
Examples New planetary bodies, camera setups Researchers
Testing Edge cases, performance tests QA enthusiasts

Contribution Guidelines

First-time contributors welcome! Look for issues labeled good first issue

Acknowledgments & References

Built With

Scientific References

Inspiration

If you've ever wondered about the size of your planet, you are not alone -- humanity has tried to measure this throughout the ages. Though Earth is large enough to defy the usual methods we have for measuring things, a creative mind can do it with surprisingly little. Eratosthenes, in ancient Greece, was able to do it to impressive accuracy using only a rod and the sun. How much better can we do today?

License

Licensed under the Apache License, Version 2.0 - see LICENSE file for details.


μεταξὺ δὲ τοῦ πυρὸς καὶ τῶν δεσμωτῶν
between the fire and the captives -- Plato

⭐ Star this repoReport issuesJoin discussions

Made with ❤️ for curious minds exploring our cosmic neighborhood

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

planet_ruler-2.0.0.tar.gz (5.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

planet_ruler-2.0.0-py3-none-any.whl (118.3 kB view details)

Uploaded Python 3

File details

Details for the file planet_ruler-2.0.0.tar.gz.

File metadata

  • Download URL: planet_ruler-2.0.0.tar.gz
  • Upload date:
  • Size: 5.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for planet_ruler-2.0.0.tar.gz
Algorithm Hash digest
SHA256 396b6a336bbc5f645912ce7cb5c8f559339bb9aeb2549205a20a9e49e45ed08d
MD5 a7d9802a4d01bc6c76a3acd907fa0784
BLAKE2b-256 1b4ab7344a8235c5f57ca2659bbcf1eb9df68f8805090415ce32c59f5fa3803e

See more details on using hashes here.

File details

Details for the file planet_ruler-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: planet_ruler-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 118.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for planet_ruler-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a99fb02eef25124c7abe87cc7da51dccf9b2746df58a0737b7fc6fde9c9322d9
MD5 58c0ca802a97acf2fdc247eae74e0672
BLAKE2b-256 d1f85928a37a74862c26065b7842699a352dfdffa9acb5fb914fcf39a0055fa4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page