Skip to main content

Superpixel PSF, ensquared-energy, FWHM, and MTF analysis with a NiceGUI interface

Project description

Superpixel

GitHub repository

superpixel models the effective point-spread function (PSF) of a detector superpixel. It combines an optical PSF with a gain-weighted array of shifted square-subpixel responses, or models the complete detector response as a single point at the superpixel origin. It handles different subpixel gains or dead subpixels.

The package calculates:

  • ensquared energy in a rectangular window or encircled energy in a circular aperture;
  • the radiometric barycenter;
  • orthogonal PSF cuts through the barycenter and their FWHM values;
  • the normalized 2D MTF, its x/y cuts, and values at the tiled-superpixel Nyquist frequencies.

Superpixel requires Python 3.10 or newer and uses uv for dependency management.

Python API

From a source checkout, create the environment and install the locked dependencies:

uv sync

Basic analysis

A typical analysis creates an optical PSF, constructs the sampled superpixel response, and evaluates all metrics:

import numpy as np

from superpixel import (
    IntegrationWindow,
    analyze_superpixel,
    build_superpixel_psf,
    gaussian_psf,
)

psf = build_superpixel_psf(
    gaussian_psf(fwhm_x=14.4, fwhm_y=12.0),
    shape=(2, 3),
    pitch=(10.0, 10.0),
    subpixel_side=9.0,
    gains=np.array(
        [
            [1.00, 0.95, 1.05],
            [0.90, 1.10, 1.00],
        ]
    ),
    model_half_width=(80.0, 80.0),
    sample_spacing=(0.2, 0.2),
)

analysis = analyze_superpixel(
    psf,
    IntegrationWindow(
        center=(0.0, 0.0),
        size=(30.0, 20.0),
    ),
)

print("Integrated energy:", analysis.ensquared_energy)
print("Barycenter:", analysis.barycenter)
print("FWHM:", analysis.cuts.fwhm_x.width, analysis.cuts.fwhm_y.width)
print(
    "MTF at Nyquist:",
    analysis.mtf.at_nyquist_x,
    analysis.mtf.at_nyquist_y,
)

build_superpixel_psf returns a SuperpixelPSF whose sampled density is normalized so that its integral over the numerical grid is one. output_grid_energy_fraction is the fraction of the internally normalized, gain-weighted effective PSF that lies inside the requested output grid before this final normalization. It depends on relative gains when shifted subpixel responses are cropped by different amounts. Multiplying all gains by the same factor has no effect. Increase model_half_width until the results converge, particularly for an Airy PSF.

analyze_superpixel returns the PSF, integrated energy, barycenter, barycenter-centered PSF cuts and FWHM values, and the complete 2D MTF with its cuts.

Coordinate and unit conventions

  • Coordinate pairs and sizes are (x, y) = (column, row).
  • Array shapes and gain matrices are (rows, columns).
  • pitch=(pitch_x, pitch_y), model_half_width=(x, y), and sample_spacing=(x, y).
  • All lengths in one analysis must use the same unit.
  • The tiled-superpixel Nyquist frequencies are 1 / (2 * columns * pitch_x) and 1 / (2 * rows * pitch_y).
  • A full-superpixel point response has no detector pitch and therefore no detector-defined Nyquist frequency.

Optical PSF models

A Gaussian can be specified using sigma or FWHM values. Its optional rotation angle is measured counter-clockwise in radians:

from superpixel import gaussian_psf

model = gaussian_psf(fwhm_x=14.4, fwhm_y=10.0, angle=0.2)

An Airy PSF accepts either its first-zero radius or wavelength and f-number:

from superpixel import airy_psf

model = airy_psf(wavelength=0.55, f_number=8.0)
# Alternatively: airy_psf(first_zero_radius=5.37)

Wavelength and first-zero radius must use the same length unit as the detector geometry.

Any vectorized callable with the signature psf(x, y) can be supplied:

import numpy as np


def custom_psf(x, y):
    sigma_x = 5.0
    sigma_y = 7.0
    return np.exp(-0.5 * ((x / sigma_x) ** 2 + (y / sigma_y) ** 2))

The callable receives NumPy arrays and must return finite, non-negative values. It does not need to be pre-normalized.

For trusted local code stored as text, custom_psf_from_code compiles a function named psf. This executes Python and is not a security sandbox.

Optical and detector response modes

Pass None as the optical PSF to model ideal optics with only the square detector response:

pixel_only = build_superpixel_psf(
    None,
    subpixel_side=10.0,
)

Square-subpixel mode is the default and requires a positive subpixel_side. There is no per-subpixel point-response mode.

Use detector_response="point" to replace the complete subpixel array with a single point response at the origin. Shape, pitch, side length, and gains are ignored in this mode:

optics_only = build_superpixel_psf(
    gaussian_psf(fwhm_x=14.4),
    detector_response="point",
)

sampled_dirac = build_superpixel_psf(
    None,
    detector_response="point",
)

Numerical convolution is performed only when both an optical PSF and the square detector response are enabled.

Integration apertures

The default IntegrationWindow is rectangular. Use a circular aperture by selecting its shape and radius:

window = IntegrationWindow(
    center=(0.0, 0.0),
    shape="circle",
    radius=15.0,
)
analysis = analyze_superpixel(psf, window)

JSON configuration

The UI configuration schema is also available through the Python API:

from superpixel import AppConfig, load_config, save_config

config = AppConfig()
save_config(config, "superpixel-config.json")
restored = load_config("superpixel-config.json")

Configuration objects are validated when constructed or loaded. Invalid values and unsupported configuration versions raise ValueError.

Desktop UI

Start the native NiceGUI application from the repository:

uv run superpixel

The repository entry point can also be run directly:

uv run python run_ui.py

The input area is organized into Optical PSF, Superpixel Configuration, Numerical Grid, and Integrated Energy Analysis sections. Click Simulate to run the same numerical API used directly from Python and update the PSF, MTF, cut plots, and numerical results.

The configuration menu provides:

  • Save configuration to download the current settings as JSON;
  • Load configuration to validate and apply a JSON configuration to the inputs;
  • Reset to defaults to restore the original application settings.

Loading a configuration updates the inputs but does not execute custom PSF code or start a simulation. Review the settings and click Simulate.

Every simulation also saves the current configuration to ~/.superpixel/config.json. The UI restores this file on its next launch. Set SUPERPIXEL_CONFIG_PATH to use a different automatic configuration path.

Custom PSF code entered in the UI is executed as Python. Only run code you trust, and do not expose the UI as a public service while custom code is enabled.

Maintenance and contribution

Development workflow

Synchronize the locked environment after cloning the repository or changing dependencies:

uv sync

Run the complete test suite before submitting a change:

uv run pytest

When dependencies change, update pyproject.toml and refresh uv.lock with the appropriate uv add, uv remove, or uv lock command. Commit both files so development and packaged builds use the same resolved dependencies.

The main source areas are:

  • src/superpixel/simulation.py: sampled PSF construction and detector response modes;
  • src/superpixel/models.py: built-in and custom optical PSF models;
  • src/superpixel/metrics.py: energy, barycenter, cuts, FWHM, and MTF;
  • src/superpixel/analysis.py: high-level analysis orchestration;
  • src/superpixel/plotting.py: Plotly figures;
  • src/superpixel/config.py: validated JSON configuration;
  • src/superpixel/ui.py: the NiceGUI wrapper;
  • tests/: numerical, configuration, plotting, and UI tests.

Keep numerical behavior in the Python API rather than in the UI. The UI should remain a thin wrapper that builds validated inputs, calls the analysis functions, and presents their results. Export intended public interfaces from src/superpixel/__init__.py and add tests for changed behavior.

Build a one-file executable

The application uses NiceGUI native mode and can be packaged with PyInstaller. Build on the same operating system and CPU architecture on which the executable will run; PyInstaller does not cross-compile.

After uv sync, run from the repository root:

uv run build

The build command reads the installed package version and passes Superpixel-<version> to nicegui-pack. Updating the project version therefore also updates the executable name without changing the build command. Run it from the repository root so it can package run_ui.py.

The executable is written to:

  • Windows: dist/Superpixel-<version>.exe
  • macOS: dist/Superpixel-<version>
  • Linux: dist/Superpixel-<version>

License

Superpixel is released under the MIT License.

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

superpixel-1.0.0.tar.gz (28.2 kB view details)

Uploaded Source

Built Distribution

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

superpixel-1.0.0-py3-none-any.whl (32.9 kB view details)

Uploaded Python 3

File details

Details for the file superpixel-1.0.0.tar.gz.

File metadata

  • Download URL: superpixel-1.0.0.tar.gz
  • Upload date:
  • Size: 28.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for superpixel-1.0.0.tar.gz
Algorithm Hash digest
SHA256 a8623fe011020633d526b450a08663887cdcc70a0f4da1d154fefb63f24dd0f0
MD5 51f214c15bfc461518e4a3ca1a2adc1a
BLAKE2b-256 571dd68f0119cf0a6e46c0db33bd414dfe31932aa59dbf1d46337eade50e8bd2

See more details on using hashes here.

File details

Details for the file superpixel-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: superpixel-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 32.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for superpixel-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 29c8967fc3e9158cf589e23f5819b59f9039f406ee10dec039b407e4e4c357a1
MD5 c06d60b876f1538f25dca8fb90f3dd88
BLAKE2b-256 7543eb99823e6b8cca6be165c8d3f712aac4392f719f3c431b4612d37821ed08

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