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_limb()  # → 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(method='manual')          # Interactive GUI (default)
# obs.detect_limb(method='gradient-break')  # Simple gradient-based detection
# obs.detect_limb(method='gradient-field')  # Gradient flow analysis
# obs.detect_limb(method='segmentation')    # AI-powered (requires PyTorch)

# OR skip detection and use gradient-field optimization directly:
# obs.fit_limb(loss_function='gradient_field')  # Direct gradient optimization

obs.fit_limb()     # Multi-resolution parameter optimization
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

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 Methods

  • Manual: Interactive GUI with precision controls (default)
  • Gradient-field: Automated detection via directional blur and flux analysis
  • AI Segmentation: Deep learning-powered (optional)

Advanced Camera Models

  • Camera parameter optimization
  • Multiple camera configurations supported
  • Flexible parameter fitting framework

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.8+
  • 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_limb()

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

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(method='manual')  # Interactive GUI detection
# obs.detect_limb(method='gradient-break')  # Simple gradient detection
# obs.detect_limb(method='gradient-field')  # Gradient flow analysis

# OR use gradient-field optimization (skips traditional detection):
# obs.fit_limb(loss_function='gradient_field')  # Direct gradient optimization

obs.detect_limb()
obs.fit_limb()

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_limb()
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.detect_limb(method='gradient-break')  # Automated gradient-based detection
obs.fit_limb()                             # Multi-resolution parameter optimization

# OR use gradient-field optimization (more advanced):
# obs.fit_limb(loss_function='gradient_field', 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.detect_limb(method='gradient-break')  # Automated detection
obs.fit_limb()                            # Multi-resolution fitting

# OR single-step gradient-field optimization:
# obs.fit_limb(loss_function='gradient_field', 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.fit.optimize_parameters(obs, method='differential_evolution') # Optimization
pr.uncertainty.calculate_parameter_uncertainty(obs, 'r')         # Uncertainty estimation
pr.plot.show_analysis(obs, style='comprehensive')                # Visualization

# Key methods
obs.detect_limb(method='manual')          # Interactive detection (default)
obs.fit_limb(resolution_stages='auto')    # Multi-resolution optimization
obs.plot()                                # Show results with uncertainty

# Advanced gradient-field optimization:
# obs.fit_limb(loss_function='gradient_field', resolution_stages='auto')

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
pytest tests/ -v  # Verify everything works

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-1.7.0.tar.gz (7.8 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-1.7.0-py3-none-any.whl (98.0 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for planet_ruler-1.7.0.tar.gz
Algorithm Hash digest
SHA256 40f2061d9de063e2df1e197ca4263b38144c9ba63abd47c3517c926825d2570e
MD5 436a5255f7219fe5c178f09ab208d665
BLAKE2b-256 4ddb7d00d2a76447d6daa874f6a8995cbfe85db70dc8028f491b3a7717ef1f3a

See more details on using hashes here.

File details

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

File metadata

  • Download URL: planet_ruler-1.7.0-py3-none-any.whl
  • Upload date:
  • Size: 98.0 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-1.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a23d703688649093c44c50becc30d0e69dae461199baf47ce9259c42b0315b89
MD5 ae571097dfe106e01316543525568a1f
BLAKE2b-256 7a60b706042c349a948c7bd0f32f61531cce237e93d76b0a1aa1729ec187fb63

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