| name | lightsheet-psf-deconvolution |
| description | KINTSUGI deconvolution: True lightsheet PSF calculation to fix horizontal banding artifacts. Trigger: deconvolution artifacts, horizontal banding, PSF issues, fcyl, slitwidth, lightsheet, LsDeconv. |
| author | KINTSUGI Team |
| date | Sat Dec 27 2025 00:00:00 GMT+0000 (Coordinated Universal Time) |
Lightsheet PSF for Deconvolution - Fixing Horizontal Banding Artifacts
Experiment Overview
| Item | Details |
|---|---|
| Date | 2025-12-27 |
| Goal | Fix horizontal banding artifacts in Python deconvolution by implementing true lightsheet PSF |
| Environment | KINTSUGI KDecon module, HiPerGator, CuPy GPU |
| Status | RESOLVED |
Context
The Python KDecon module was producing horizontal banding artifacts after deconvolution. The original MATLAB LsDeconv.m worked correctly. Investigation revealed that Python was using a widefield PSF that ignored the lightsheet parameters (fcyl and slitwidth), while MATLAB used a true lightsheet PSF.
Root Cause
The lightsheet PSF is fundamentally different from a widefield PSF:
Widefield PSF (WRONG for lightsheet data)
# Both excitation and emission use same NA and coordinate system
psf_ex = PSF(x, y, z, NA, n, lambda_ex)
psf_em = PSF(x, y, z, NA, n, lambda_em)
psf = psf_ex * psf_em
Lightsheet PSF (CORRECT - from MATLAB LsDeconv.m)
# Excitation uses lightsheet NA and SWAPPED coordinates (z, 0, x)
# Emission uses objective NA and standard coordinates (x, y, z)
NA_ls = np.sin(np.arctan(slitwidth / (2 * fcyl))) # ~0.956 for fcyl=1, slitwidth=6.5
psf_ex = PSF(z, 0, x, NA_ls, n, lambda_ex) # Note: z->x, y->0, x->z
psf_em = PSF(x, y, z, NA_obj, n, lambda_em) # Standard coordinates
psf = psf_ex * psf_em
The coordinate swap (z, 0, x) instead of (x, y, z) models the lightsheet illuminating perpendicular to the detection axis.
Solution
1. Added _psf_light_sheet_full() function to psf.py
def _psf_light_sheet_full(x, y, z, NA_obj, n, lambda_ex, lambda_em, NA_ls):
"""
True lightsheet PSF matching MATLAB LsDeconv implementation.
"""
# Lightsheet excitation PSF - coordinate swap (z, 0, x)
psf_ex = _psf_single_wavelength(z, 0, x, NA_ls, n, lambda_ex)
# Objective emission PSF - standard (x, y, z)
psf_em = _psf_single_wavelength(x, y, z, NA_obj, n, lambda_em)
return psf_ex * psf_em
2. Updated generate_psf() to use lightsheet mode when parameters provided
def generate_psf(dxy, dz, NA, n, lambda_ex, lambda_em,
fcyl=None, slitwidth=None, ...):
use_lightsheet = fcyl is not None and slitwidth is not None
if use_lightsheet:
NA_ls = np.sin(np.arctan(slitwidth / (2 * fcyl)))
# Use _psf_light_sheet_full instead of _psf_light_sheet
3. Updated main.py to pass parameters through
def _compute_psf(self):
self._psf, self._psf_info = generate_psf(
self.dxy, self.dz, self.NA, self.rf,
self.lambda_ex, self.lambda_em,
fcyl=self.fcyl, # ADD
slitwidth=self.slitwidth, # ADD
verbose=self.verbose
)
4. Updated notebook DECON_PARAMS
DECON_PARAMS = {
'xy_vox': 377,
'z_vox': 1500,
'iterations': 25,
'mic_NA': 0.75,
'tissue_RI': 1.44,
'damping': 0,
'stop_criterion': 5.0,
'device': 'auto',
'hist_clip': 0.01, # ADD - histogram clipping percentage
'slit_aper': 6.5, # ADD - slit aperture width (mm)
'f_cyl': 1, # ADD - cylinder lens focal length (mm)
}
Failed Attempts (Critical)
| Attempt | Why it Failed | Lesson Learned |
|---|---|---|
| Ignoring fcyl/slitwidth as "metadata only" | These aren't metadata - they define the PSF shape | Check MATLAB reference code before dismissing parameters |
| Using same NA for both excitation and emission | Lightsheet has separate illumination NA | Lightsheet optics are fundamentally different from widefield |
| Using same coordinate system for both PSFs | Lightsheet illuminates perpendicular to detection | The coordinate swap (z,0,x) is essential |
Key Parameters
| Parameter | Typical Value | Description |
|---|---|---|
fcyl |
1 mm | Cylinder lens focal length |
slitwidth |
6.5 mm | Slit aperture width |
NA_ls |
~0.956 | Calculated: sin(atan(slitwidth / (2 * fcyl))) |
hist_clip |
0.01% | Histogram clipping for output normalization |
Diagnostic Signs
Symptom: Horizontal banding artifacts after deconvolution
- Check if
fcylandslitwidthare being passed to PSF generation - Check if PSF uses coordinate swap for excitation component
- Compare PSF shape - lightsheet PSF should be anisotropic (narrower in lightsheet direction)
Key Insights
- MATLAB is the reference - When Python produces artifacts MATLAB doesn't, compare implementations line-by-line
- "Metadata" parameters may be functional - Just because a parameter isn't in the main algorithm doesn't mean it's unused
- PSF shape determines deconvolution quality - Wrong PSF = wrong deconvolution = artifacts
- Coordinate swaps are not typos - In optics code, coordinate manipulations usually have physical meaning
References
- MATLAB LsDeconv.m (lines 560, 652-654): Original lightsheet deconvolution implementation
notebooks/Kdecon/psf.py: Python PSF calculationnotebooks/Kdecon/main.py: Python deconvolution interface- Commit
37d1693: fix(decon): implement true lightsheet PSF matching MATLAB LsDeconv