Superpixel PSF, ensquared-energy, FWHM, and MTF analysis with a NiceGUI interface
Project description
Superpixel
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), andsample_spacing=(x, y).- All lengths in one analysis must use the same unit.
- The tiled-superpixel Nyquist frequencies are
1 / (2 * columns * pitch_x)and1 / (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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file superpixel-1.1.0.tar.gz.
File metadata
- Download URL: superpixel-1.1.0.tar.gz
- Upload date:
- Size: 28.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
605f8c5bd6362ecbd863d4ff634504c7180d6da5b5ec02dc2a87f4d29d7f2057
|
|
| MD5 |
313681d2369c809e1c679b43dfc752d3
|
|
| BLAKE2b-256 |
61128641a5e7b86796124fe09413d8ea9599abcbed3ce5e800972a40de5ef9ad
|
File details
Details for the file superpixel-1.1.0-py3-none-any.whl.
File metadata
- Download URL: superpixel-1.1.0-py3-none-any.whl
- Upload date:
- Size: 32.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fdccc1e13b0b79dd7c2dda422cbf66470f7b5a64545db837614f9565e414c3f1
|
|
| MD5 |
8b3bc5bcd1ee3acdb71486145edb5573
|
|
| BLAKE2b-256 |
f6e9a2a1573de9936d9f8dcfe44a8281925d7f1c905683cfc0f3a5fdf76999a5
|