Skip to main content

Solar sunspot detection and analysis using adaptive thresholding and connected component analysis

Project description

SuryaPy ☀️

Python Version License GitHub

SuryaPy — from Surya, the Sun in Sanskrit — is a Python package for detecting, analyzing, and measuring sunspots from solar observations. Built on my (Soumit Rao) 2024 summer astronomy internship work at Ashoka University. Check out faq.rst if you have some doubts regarding installation or functions, as well as the cited references.

About The aim of the internship was to help develop a new solar experiment for the Astrolab course AST-1080/PHY-1080 at Ashoka University. We used white light images from our Nikon DSLR and 8 inch CELESTRON Nexstar telescope and measured the rotation periods of the spots as well as the area of the spots. These results and methods were compared using data from the Solar Influences Data Center at the Royal Observatory of Belgium.

This repository contains some of the methods we used. You can find a more detailed explanantiono of sunspots tracking in out SUMMER 2024 Astronomy internship report attached in this repository. For any questions please reach out to me at rsoumit51@gmail.com / soumit.rao_ug2023@ashoka.edu.in

Any reccomendations, collaborations and contributions are welcome. The aim is to keep this repository open and accessible to as many people as possible. Since this repository is new, it may face some minor issues—— any and all feedback is welcome!

Features

SuraPy uses the Bradley-Roth adaptive thresholding algorithm with connected component analysis to identify sunspots, applies physical corrections (foreshortening, limb darkening), and converts measurements to standard solar physics units.

Bradley-Roth Adaptive Thresholding — Integral-image based local contrast detection, robust to varying lighting

Connected Component Analysis — Morphological cleanup + scipy.ndimage.label for automatic sunspot identification

Physical Corrections — Foreshortening correction, limb darkening removal, pixel-to-km² conversion

Spot Tracking — Integration with astrolab's centroid-finding for multi-image registration

Standard Units — Convert to millionths of solar hemisphere (MH), angular distances, and heliocentric coordinates

Built on Astrolab — Leverages Astrolab package by Philip Cherian - https://astrolab.readthedocs.io/en/latest/

Quick Start

from astrolab import imaging as im
from scipy import ndimage as scp
import suryapy

# Load and filter a solar image
raw_image = im.load_image("DSC_1036.JPG")
filtered = scp.gaussian_filter(raw_image, sigma=0.5, radius=2)

# Mask the solar disk and crop
cropped = suryapy.mask_sun(filtered, sun_threshold=50)

# Apply Bradley-Roth thresholding
br_mask = suryapy.b_roth(cropped, threshold=9, Nx=100)

# Find connected components (sunspots)
labeled, n_comp, large_mask, sizes, bbox = suryapy.find_components(
    br_mask, cropped, min_size=1000
)

# Correct for foreshortening at position (y, z)
corrected_area_px = suryapy.foreshortening_correction(
    area_pixels=1276, y=-700, z=-900, R_sun=1511
)

# Convert to physical units
area_km2 = suryapy.pixels_to_km2(corrected_area_px, km_per_pixel=458.58)
area_mh = suryapy.km2_to_millionths(area_km2)

print(f"Sunspot area: {area_mh:.2f} MH")

Installation

From Source (Recommended)

git clone https://github.com/Soumit65/SuryaPy.git
cd SuryaPy
pip install -e .

With Development Tools

pip install -e ".[dev,docs,jupyter]"

From PyPI (Coming Soon)

pip install suryapy

Documentation

Full documentation with tutorials and examples: https://soumit65.github.io/SuryaPy/

  • Getting Started — Workflow and core concepts
  • API Reference — All functions documented
  • Tutorials — Bradley-Roth algorithm, limb darkening, connected components
  • Examples — 6+ complete working examples
  • FAQ — Common questions and troubleshooting

Package Structure

sunspots/
├── __init__.py           # Package exports
├── thresholding.py       # b_roth, mask_sun
├── tracking.py           # find_spot, process, display_spot
├── correction.py         # limb_darkening_correction
└── area.py              # Connected components, foreshortening, unit conversion

Core Functions

Thresholding

# Bradley-Roth adaptive thresholding (your internship algorithm)
br_mask = suryapy.b_roth(image, threshold=9, Nx=100, print_log=True)

# Mask and crop the solar disk
cropped = suryapy.mask_sun(image, sun_threshold=50, crop=True)

Tracking & Alignment

# Find a sunspot centroid (uses astrolab's find_star internally)
spot_pos = suryapy.find_spot(cropped, spot_pos=[-900, -700], search=100)

# Full processing pipeline: crop → find spot
cropped, spot_centroid = suryapy.process(
    image, spot_pos=[-900, -700], print_log=True
)

# Process multiple images at once
crops, spots = suryapy.process_image_list(
    [image1, image2, image3],
    [[-900, -700], [-250, 500], [220, 450]]
)

Corrections & Analysis

# Remove limb darkening via radial median profile
corrected = suryapy.limb_darkening_correction(cropped, show_plot=True)

# Connected component analysis with morphological cleanup
labeled, n, large_mask, sizes, bbox = suryapy.find_components(
    br_mask, cropped, min_size=1000, print_log=True
)

# Inspect a single component
info = suryapy.inspect_component(
    target_label=77, labeled_image=labeled, num_components=n,
    bound_box=bbox, br_mask=br_mask, image=cropped
)

Physical Conversions

# Correct for foreshortening (projection angle)
area_corrected = suryapy.foreshortening_correction(
    area_pixels=1276, y=-700, z=-900, R_sun=1511
)

# Pixel → km²
area_km2 = suryapy.pixels_to_km2(area_corrected, km_per_pixel=458.58)

# km² → millionths of solar hemisphere (standard solar physics unit)
area_mh = suryapy.km2_to_millionths(area_km2)

# Angular distance from disk center
ang_dist = suryapy.angular_distance(x_spot=0, y_spot=1000, pixel_radius=1516)

Parameters Reference

Bradley-Roth Thresholding

Parameter Default Effect
threshold 9 Sensitivity (5-20 typical). Higher = more aggressive detection
Nx 100 Window divisor. Larger window if Nx is smaller

Connected Components

Parameter Default Effect
min_size 1000 Minimum component area (pixels) to keep

Calibration (DSLR + SIDC Example)

Instrument km/pixel pixels² → MH
DSLR (internship) 458.58 multiply by (458.58/696000)² × 1e6
SIDC 738.44 multiply by (738.44/696000)² × 1e6

Example Notebook Workflow

from astrolab import imaging as im
from scipy import ndimage as scp
import matplotlib.pyplot as plt
import suryapy as sio  # your alias

# 1. Load and preprocess
raw = im.load_image("DSC_1036.JPG")
filtered = scp.gaussian_filter(raw, sigma=0.5, radius=2)

# 2. Mask and crop
cropped = sio.mask_sun(filtered, sun_threshold=50)

# 3. Apply corrections
corrected = sio.limb_darkening_correction(cropped)

# 4. Threshold
br = sio.b_roth(cropped, threshold=9, Nx=100)

# 5. Morphological cleanup
from scipy.ndimage import binary_opening, binary_closing
struct = np.ones((3, 3))
opened = binary_opening(cropped, structure=struct)
closed = binary_closing(br, structure=struct)
isolated = closed * cropped * opened

# 6. Find components
labeled, n, large, sizes, bbox = sio.find_components(
    br, cropped, min_size=1000
)

# 7. Measure and correct
component_info = sio.inspect_component(1, labeled, n, bbox, br, cropped)
area_px = component_info['area_pixels']
area_corr = sio.foreshortening_correction(area_px, y=-700, z=-900)
area_mh = sio.km2_to_millionths(sio.pixels_to_km2(area_corr, 458.58))

print(f"Sunspot area: {area_mh:.2f} MH")

Dependencies

  • astrolab ≥ 0.1.0 — solar imaging library (your college's work!)
  • numpy ≥ 1.19.0 — array operations
  • scipy ≥ 1.5.0 — ndimage, optimize
  • matplotlib ≥ 3.3.0 — plotting

Requirements

  • Python 3.8+
  • astrolab properly installed and configured

Contributing

Contributions welcome! Fork, branch, commit, and PR.

git checkout -b feature/cool-feature
# ... make changes ...
git commit -m "Add cool feature"
git push origin feature/cool-feature

Citation

If you use SuryaPy in your research:

@software{suryapy2024,
  author = {Rao, Soumit},
  title = {SuryaPy: Solar Sunspot Detection and Analysis},
  year = {2024},
  url = {https://github.com/Soumit65/SuryaPy}
}

License

MIT License. See LICENSE for details.

Acknowledgments

  • Developed as part of the 2024 Summer Astronomy Internship
  • Uses astrolab — the solar imaging library from your college
  • Bradley-Roth algorithm: Bradley & Roth (2007), Journal of Graphics Tools
  • Connected component analysis via scipy.ndimage

I'd like to thank Dr Dipankar Bhattacharaya and Philip Cherian for their endless contributions in my original project. My parents for their immense support in all my endeavours.

References

  1. Bradley, D., & Roth, G. (2007). Adaptive Thresholding using the Integral Image. Journal of Graphics Tools, 12(2), 13-21.
  2. Foreshortening correction formulas from solar physics literature
  3. SIDC/NOAA standards for sunspot area measurement in millionths of hemisphere

Contact


Let's observe the sun. 🌞

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

suryapy-0.1.0.tar.gz (30.6 kB view details)

Uploaded Source

Built Distribution

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

suryapy-0.1.0-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file suryapy-0.1.0.tar.gz.

File metadata

  • Download URL: suryapy-0.1.0.tar.gz
  • Upload date:
  • Size: 30.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for suryapy-0.1.0.tar.gz
Algorithm Hash digest
SHA256 608c5e66504b36a245e6a26c211613a4b8c79f160ff17ec652e39fa543716507
MD5 366b16977886f6fefb4d5d3927dc354e
BLAKE2b-256 f994818ba9163c9206181344e57c8b9491c69e7a0f47704ffed48c5230b2791f

See more details on using hashes here.

File details

Details for the file suryapy-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: suryapy-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for suryapy-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bad61026bb086c1803a57a5dbd40705b3901a21550c20cca6a829267e4c8c0f3
MD5 0039f6f581c88a037afb7bdb6615a4de
BLAKE2b-256 ef6ae195ac160168debf3c2b900658dfa462a70bc5df00bdb3e178e147529134

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