Harvey-Shack Scatter

A Harvey-Shack profile in pixels, to set as a PSF of its own. The camera’s scattered light, added to diffraction or a set PSF, is set in angles with CameraModel::set_scatter() instead.

template<IsSpectral TSpectral>
class HarveyShackScatter : public huira::PSF<TSpectral>

Harvey-Shack style scattered-light point spread component.

Models the broad, smooth “wings” of a real optical system’s PSF caused by surface micro-roughness, coating defects, and particulate contamination, using an image-plane power-law profile:

I(r) = (1 + (r / r0)^2)^(-b / 2)

which is flat near the origin (finite, integrable core) and falls off as r^-b for r >> r0. Exponents b of roughly 2 to 3 match measured stellar PSF wings (King 1971, Racine 1996).

This component is achromatic: all spectral channels receive the same profile. Set as a PSF (see CameraModel::set_psf()), it is taken as complete within its radius. The camera’s own scattered light, added to diffraction or to a PSF that was set, is set in angles with CameraModel::set_scatter() instead, and reaches across the frame.

Template Parameters:

TSpectral – The spectral type (e.g., RGB, Visible8)

Public Functions

HarveyShackScatter(float falloff_exponent, float r0, float cutoff_radius = 0.f)

Constructs a HarveyShackScatter profile with the given parameters.

Parameters:
  • falloff_exponent – Power-law exponent b (typically 2 to 3).

  • r0 – Shoulder radius in pixels.

  • cutoff_radius – Hard cutoff radius in pixels (0 means no cutoff).

Throws:

std::runtime_error – if the exponent or r0 is not positive and finite, r0 is too small to square, or the cutoff is negative or NaN.

~HarveyShackScatter() override = default
HarveyShackScatter(const HarveyShackScatter&) = delete
HarveyShackScatter &operator=(const HarveyShackScatter&) = delete
virtual TSpectral evaluate(float x, float y) override

Evaluates the scatter profile at the given pixel offset from center.

Parameters:
  • x – Horizontal offset from the center in pixel coordinates.

  • y – Vertical offset from the center in pixel coordinates.

Returns:

The (achromatic) profile value for each spectral channel.

inline virtual bool is_pixel_integrated() const

Whether evaluate() gives the light a pixel centered at (x, y) receives, rather than the PSF’s intensity at that point.

A PSF’s intensity is integrated over each pixel. One that is already integrated over a pixel (see PSFSampling::PixelIntegrated) is instead taken at each pixel’s center, so that it is not integrated twice.

inline virtual bool includes_defocus() const

Whether the PSF already holds the defocus the camera’s focus gives, rather than being a point’s in focus, which the focus then blurs as it blurs diffraction (see PSFFocus).

inline int radius() const

How far from the source, in pixels, the PSF holds light; 0 if not set, in which case CameraModel::set_psf() gives it CameraModel::DEFAULT_PSF_RADIUS.

inline void set_radius(int radius)

Set how far from the source, in pixels, the PSF holds light.

Throws:

std::runtime_error – if the radius is less than 1 or more than MAX_RADIUS.

Public Static Attributes

static constexpr int MAX_RADIUS = 4096

The largest radius a PSF can have, in pixels. On its grid (see GridPsf), a PSF is sampled more coarsely the farther it reaches, to keep within GridPsf::MEMORY_BUDGET: at this radius, only every several pixels.