AgentSkillsCN

lightsheet-psf-deconvolution

安全地将 Jupyter Notebook 代码重构为 Python 模块。

SKILL.md
--- frontmatter
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: 2025-12-27

Lightsheet PSF for Deconvolution - Fixing Horizontal Banding Artifacts

Experiment Overview

ItemDetails
Date2025-12-27
GoalFix horizontal banding artifacts in Python deconvolution by implementing true lightsheet PSF
EnvironmentKINTSUGI KDecon module, HiPerGator, CuPy GPU
StatusRESOLVED

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)

python
# 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)

python
# 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

python
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

python
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

python
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

python
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)

AttemptWhy it FailedLesson Learned
Ignoring fcyl/slitwidth as "metadata only"These aren't metadata - they define the PSF shapeCheck MATLAB reference code before dismissing parameters
Using same NA for both excitation and emissionLightsheet has separate illumination NALightsheet optics are fundamentally different from widefield
Using same coordinate system for both PSFsLightsheet illuminates perpendicular to detectionThe coordinate swap (z,0,x) is essential

Key Parameters

ParameterTypical ValueDescription
fcyl1 mmCylinder lens focal length
slitwidth6.5 mmSlit aperture width
NA_ls~0.956Calculated: sin(atan(slitwidth / (2 * fcyl)))
hist_clip0.01%Histogram clipping for output normalization

Diagnostic Signs

Symptom: Horizontal banding artifacts after deconvolution

  1. Check if fcyl and slitwidth are being passed to PSF generation
  2. Check if PSF uses coordinate swap for excitation component
  3. 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 calculation
  • notebooks/Kdecon/main.py: Python deconvolution interface
  • Commit 37d1693: fix(decon): implement true lightsheet PSF matching MATLAB LsDeconv