Skip to content

Constellation Detection: Computer Vision for Astronomy

Template matching and image processing for identifying star constellations

Jithendra Puppala
Jithendra Puppala
3 min read 23 views
Constellation Detection: Computer Vision for Astronomy
Tech Stack: Python OpenCV NumPy SIFT Image Processing

Constellation Detection: Computer Vision Meets Astronomy

For my NYU Computer Vision course, I tackled an interesting challenge: can we automatically identify star constellations in night sky images? Turns out, classical computer vision techniques work remarkably well.

Problem Statement

Given an image of the night sky, identify which constellation(s) are visible. Challenges: - Stars appear as small points of light - Images can be rotated, scaled, or partially visible - Background noise and atmospheric effects - Multiple constellations in one image

Classical CV Approach

Step 1: Star Detection

Use blob detection to find stars:

import cv2
import numpy as np

def detect_stars(image):
    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Gaussian blur to reduce noise
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)

    # Threshold to get bright points
    _, thresh = cv2.threshold(blurred, 200, 255, cv2.THRESH_BINARY)

    # Find contours (stars)
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, 
                                   cv2.CHAIN_APPROX_SIMPLE)

    # Extract star positions
    stars = []
    for cnt in contours:
        M = cv2.moments(cnt)
        if M['m00'] > 0:
            cx = int(M['m10'] / M['m00'])
            cy = int(M['m01'] / M['m00'])
            stars.append((cx, cy))

    return stars

Step 2: Feature Extraction

Create geometric features from star patterns:

def extract_constellation_features(stars):
    """Compute pairwise distances and angles"""
    features = []
    n = len(stars)

    for i in range(n):
        for j in range(i+1, n):
            # Distance between stars
            dist = np.linalg.norm(
                np.array(stars[i]) - np.array(stars[j])
            )

            # Angle
            angle = np.arctan2(
                stars[j][1] - stars[i][1],
                stars[j][0] - stars[i][0]
            )

            features.append({
                'distance': dist,
                'angle': angle,
                'star_pair': (i, j)
            })

    return features

Step 3: Template Matching

Match detected pattern against known constellations:

def match_constellation(detected_features, templates):
    """Find best matching constellation template"""
    best_match = None
    best_score = 0

    for name, template in templates.items():
        score = compute_match_score(detected_features, template)

        if score > best_score:
            best_score = score
            best_match = name

    return best_match, best_score

def compute_match_score(features, template):
    """Compare feature sets using geometric hashing"""
    matches = 0
    tolerance = 0.1  # 10% tolerance

    for f1 in features:
        for f2 in template:
            dist_diff = abs(f1['distance'] - f2['distance'])
            angle_diff = abs(f1['angle'] - f2['angle'])

            if (dist_diff < tolerance * f2['distance'] and
                angle_diff < tolerance):
                matches += 1

    return matches / len(template)

Advanced Techniques

Handling Rotation and Scale

Use SIFT for scale/rotation invariance:

def detect_with_sift(image, template):
    # Initialize SIFT
    sift = cv2.SIFT_create()

    # Detect keypoints and descriptors
    kp1, des1 = sift.detectAndCompute(image, None)
    kp2, des2 = sift.detectAndCompute(template, None)

    # FLANN matcher
    index_params = dict(algorithm=1, trees=5)
    search_params = dict(checks=50)
    flann = cv2.FlannBasedMatcher(index_params, search_params)

    matches = flann.knnMatch(des1, des2, k=2)

    # Ratio test
    good_matches = []
    for m, n in matches:
        if m.distance < 0.7 * n.distance:
            good_matches.append(m)

    return len(good_matches)

Partial Visibility Handling

Sometimes only part of a constellation is visible:

def partial_match(detected_stars, constellation_template):
    """Match even if only subset of stars visible"""
    min_stars = 3  # Minimum stars needed

    # Try all subsets of detected stars
    from itertools import combinations

    best_match = 0
    for subset in combinations(detected_stars, min_stars):
        score = match_subset(subset, constellation_template)
        best_match = max(best_match, score)

    return best_match

Results

Tested on 50 images: - Accuracy: 87% for full constellations - Partial detection: 72% accuracy - Processing time: ~200ms per image - Robustness: Works with 15° rotation, 20% scale variation

Most recognized: - Orion: 95% (distinctive pattern) - Big Dipper: 92% (bright stars) - Cassiopeia: 89% (W shape)

Challenging: - Faint constellations: 65% - Crowded star fields: 70%

Real-World Applications

  1. Amateur astronomy: Help identify what you're seeing
  2. Educational tools: Interactive sky maps
  3. Telescope guidance: Auto-point telescopes
  4. Astrophotography: Auto-label star trail photos

Lessons Learned

  1. Classical CV still powerful: Sometimes simpler is better than deep learning
  2. Domain knowledge matters: Understanding astronomy helps
  3. Robustness through redundancy: Multiple matching techniques improve accuracy
  4. Template quality matters: Better templates = better matches

This project showed me that computer vision isn't always about neural networks - sometimes geometric reasoning is exactly what you need.

Share:

Get In Touch

I'll respond within 24-48 hours