def r_vs_depletion_dose():
    """A plot of resolution improvement vs. depletion dose, for fixed
    step size and excitation brightness, and a single line-STED
    orientation: """
    depletion_brightnesses = np.linspace(0, 18, 200)
    resolution_improvement = {'point': [], 'line': []}
    depletion_dose = {'point': [], 'line': []}
    for depletion_brightness in depletion_brightnesses:
        for psf_type in ('point', 'line'):
            report = psf_report(psf_type=psf_type,
                                excitation_brightness=0.25,
                                depletion_brightness=depletion_brightness,
                                steps_per_excitation_psf_width=4,
                                pulses_per_position=1,
                                verbose=False)
            resolution_improvement[psf_type].append(
                report['resolution_improvement_descanned'])
            depletion_dose[psf_type].append(report['depletion_dose'])
    fig = plt.figure(figsize=(12, 4), dpi=100)
    plt.plot(depletion_dose['point'],
             resolution_improvement['point'],
             '.',
             label='Descanned point STED')
    plt.plot(depletion_dose['line'],
             resolution_improvement['line'],
             '-',
             label='Descanned line STED')
    plt.xlim(0, 750)
    plt.ylim(1, 4.5)
    plt.legend(loc='lower right')
    plt.grid()
    plt.xlabel('Depletion dose (saturation units)')
    plt.ylabel('Resolution improvement (vs. diffraction limit)')
    plt.title('Resolution improvement vs. depletion dose\n' +
              'for fixed step size and fixed excitation brightness')
    plt.savefig(os.path.join(output_directory, '2_r_vs_depletion_dose.svg'),
                bbox_inches='tight')
    plt.close(fig)
예제 #2
0
def create_figure(
        psf_type='point',  # Point or line
        excitation_brightness=0.2,  # Peak brightness in saturation units
        depletion_brightness=20,  # Peak brightness in saturation units
        steps_per_excitation_psf_width=12,  # Small? Bad res. Big? Bad dose.
        pulses_per_position=1,  # Think of this as "dwell time"
):
    args = locals()
    args['verbose'] = True
    args['output_dir'] = None
    ###################
    # Calculations
    ###################
    # Calculate the relevant PSFs and light dosages:
    print("Generating figure with parameters:")
    report = psf_report(**args)
    # Also calculate the same PSFs on a much finer grid, so we can show
    # band limits and sampling correctly:
    fine_args = dict(args)
    fine_args['steps_per_excitation_psf_width'] = 30
    fine_args['verbose'] = False
    fine_report = psf_report(**fine_args)
    fine_excitation = fine_report['psfs']['excitation'][0, :, :]
    fine_depletion = fine_report['psfs']['depletion'][0, :, :]
    fine_excitation_frac = fine_report['psfs']['excitation_fraction'][0, :, :]
    fine_depletion_frac = fine_report['psfs']['depletion_fraction'][0, :, :]
    fine_sted = fine_report['psfs']['sted'][0, :, :]
    # Calculate the pixel positions of the samples:
    step_size_ratio = (fine_args['steps_per_excitation_psf_width'] /
                       args['steps_per_excitation_psf_width'])
    if psf_type == 'point':
        y_vals = np.arange(0, fine_excitation.shape[1], step_size_ratio)
    elif psf_type == 'line':
        y_vals = [fine_excitation.shape[1] // 2]
    sample_points_x, sample_points_y = [], []
    for x in np.arange(0, fine_excitation.shape[0], step_size_ratio):
        for y in y_vals:
            sample_points_x.append(x)
            sample_points_y.append(y)
    ###################
    # Plotting
    ###################
    fig = plt.figure(figsize=(20, 5), dpi=100)
    fig.text(x=0.21, y=0.02, s='.',
             color='white')  #Hack, for consistent margins
    fig.text(x=0.23, y=0.02, s="(d)", fontsize=15)
    fig.text(x=0.25,
             y=-0.01,
             s='' + "Excitation dose:%8s\n" %
             ('%0.2f' % report['excitation_dose']) + " Depletion dose:%8s" %
             ('%0.2f' % report['depletion_dose']),
             fontweight=800,
             multialignment='left',
             family='monospace')
    fig.text(x=0.41, y=0.02, s="(e)", fontsize=15)
    fig.text(x=0.43,
             y=-0.01,
             s='' + " Emissions:%5s per molecule\n" %
             ('%0.2f' % report['expected_emission']) +
             "Resolution:%5sx diffraction" %
             ('%0.1f' % report['resolution_improvement_descanned']),
             fontweight=800,
             multialignment='left',
             family='monospace')
    #####
    # Plot a 1D lineout of the excitation and depletion illumination,
    # with sample points overlayed:
    #####
    ax = plt.subplot(2, 3, 1)
    plt.title("(a) Illumination brightness")
    # Excitation
    ax.plot(fine_excitation[fine_excitation.shape[0] // 2, :],
            c='blue',
            linestyle=':',
            linewidth=3)
    for t in ax.get_yticklabels():
        t.set_color('blue')
    ax.axes.get_xaxis().set_ticks([])
    ax.set_ylabel('Excitation fluence per pulse', color='blue')
    plt.ylim(0, fine_excitation[fine_excitation.shape[0] // 2, :].max())
    # Sample points overlayed
    ax.set_xlabel('Scan position')
    for x in set(sample_points_x):
        ax.axvline(x, ymin=0, ymax=0.1, c='gray')
    # Depletion
    ax2 = ax.twinx()
    ax2.plot(fine_depletion[fine_depletion.shape[0] // 2, :], color='green')
    ax2.set_ylabel('Depletion fluence per pulse', color='green')
    plt.ylim(0, max(fine_depletion[fine_depletion.shape[0] // 2, :].max(),
                    0.1))
    for t in ax2.get_yticklabels():
        t.set_color('green')
    #####
    # Plot a 2D color version of the excitation and depletion
    # illumination, with the sample points overlayed:
    #####
    ax = plt.subplot(2, 3, 4)

    def norm(x):
        if x.max() == x.min(): return np.zeros_like(x)
        return (x - x.min()) / (x.max() - x.min())

    rgb = np.zeros(fine_excitation.shape + (3, ))
    rgb[:, :, 2] = norm(fine_excitation)
    rgb[:, :, 1] = norm(fine_depletion)
    ax.imshow(rgb, interpolation='nearest')
    ax.scatter(sample_points_x, sample_points_y, c='gray', s=1)
    plt.xlim(0, rgb.shape[0])
    plt.ylim(rgb.shape[1] * 0.17, rgb.shape[1] * 0.83)
    ax.axes.get_xaxis().set_ticks([])
    ax.axes.get_yaxis().set_ticks([])
    #####
    # Plot a 1D lineout of the excitation and depletion probabilities,
    # with sample points overlayed:
    #####
    ax = plt.subplot(2, 3, 2)
    plt.title("(b) Transition probability")
    # Excitation
    ax.plot(fine_excitation_frac[fine_excitation_frac.shape[0] // 2, :],
            color='blue',
            linestyle=':',
            label=' Pre-depletion',
            linewidth=3)
    ax.plot(fine_sted[fine_sted.shape[0] // 2, :],
            color='blue',
            label='Post-depletion',
            linewidth=3)
    for t in ax.get_yticklabels():
        t.set_color('blue')
    ax.axes.get_xaxis().set_ticks([])
    ax.set_ylabel('Excitation probability per pulse', color='blue')
    plt.ylim(0, 1.19)
    # Sample points overlayed
    ax.set_xlabel('Scan position')
    for x in set(sample_points_x):
        ax.axvline(x, ymin=0, ymax=0.1, c='gray')
    # Depletion
    ax2 = ax.twinx()
    ax2.plot(1 - fine_depletion_frac[fine_depletion_frac.shape[0] // 2, :],
             color='green')
    ax2.set_ylabel('Depletion probability per pulse', color='green')
    plt.ylim(0, 1.19)
    for t in ax2.get_yticklabels():
        t.set_color('green')
    # Shenanigans to get the legend overlay right:
    ax.set_zorder(1)
    ax.set_frame_on(False)
    ax2.set_frame_on(True)
    ax.legend(loc=(0.0, 0.855), fontsize=9, framealpha=0.9)
    #####
    # Plot a 2D color version of the excitation and depletion
    # probabilities, with the sample points overlayed:
    #####
    ax = plt.subplot(2, 3, 5)
    rgb = np.zeros(fine_excitation_frac.shape + (3, ))
    rgb[:, :, 2] = norm(fine_sted)
    rgb[:, :, 1] = norm(1 - fine_depletion_frac)
    ax.imshow(rgb, interpolation='nearest')
    ax.scatter(sample_points_x, sample_points_y, c='gray', s=3)
    plt.xlim(0, rgb.shape[0])
    plt.ylim(rgb.shape[1] * 0.17, rgb.shape[1] * 0.83)
    ax.axes.get_xaxis().set_ticks([])
    ax.axes.get_yaxis().set_ticks([])
    #####
    # Plot a 1D lineout of the excitation and STED OTF,
    # with the Nyquist frequency overlayed:
    #####
    ax = plt.subplot(1, 3, 3)
    plt.title("(c) Frequency performance")

    # Excitation and STED OTFs:
    def freqs(x, dc_term):
        otf = np.fft.fftshift(np.abs(np.fft.fft(x)))
        otf /= otf.max()
        otf *= dc_term
        return otf

    if psf_type == 'point':
        exc_dc_term = (report['psfs']['excitation_fraction'].sum() *
                       report['pulses_per_position'])
        sted_dc_term = (report['psfs']['sted'].sum() *
                        report['pulses_per_position'])
    elif psf_type == 'line':
        midline = report['psfs']['excitation'].shape[1] // 2
        exc_dc_term = (
            report['pulses_per_position'] *
            report['psfs']['excitation_fraction'][0, midline, :].sum())
        sted_dc_term = (report['pulses_per_position'] *
                        report['psfs']['sted'][0, midline, :].sum())
    exc_otf = freqs(fine_excitation_frac[fine_excitation_frac.shape[0] //
                                         2, :],
                    dc_term=exc_dc_term)
    ax.semilogy(exc_otf,
                label='Excitation OTF',
                linewidth=3,
                linestyle=':',
                color='blue')
    sted_otf = freqs(fine_sted[fine_sted.shape[0] // 2, :],
                     dc_term=(sted_dc_term))
    ax.semilogy(sted_otf, label='STED OTF', linewidth=3, color='blue')
    # Sampling frequency overlayed
    ax.axvline(len(exc_otf) // 2 + 0.5 * len(exc_otf) / step_size_ratio,
               color='red',
               linewidth=4,
               ymin=0,
               ymax=0.8,
               alpha=0.5)
    ax.axvline(len(exc_otf) // 2 - 0.5 * len(exc_otf) / step_size_ratio,
               color='red',
               linewidth=4,
               ymin=0,
               ymax=0.8,
               alpha=0.5,
               label="Nyquist limit")
    plt.xlim(fine_excitation_frac.shape[1] * 0.25,
             fine_excitation_frac.shape[1] * 0.75)
    plt.ylim(1e-2, 9e3)
    ax.set_xlabel('Spatial frequency')
    ax.set_ylabel('Transmission amplitude')
    ax.axes.get_xaxis().set_ticks([])
    ax.legend(loc=(0.04, 0.88), fontsize=9)
    #####
    # Make everything line up
    #####
    plt.subplots_adjust(left=0.25,
                        bottom=0.0,
                        right=0.75,
                        top=1.0,
                        wspace=0.52,
                        hspace=-0.1)
    ###################
    # Save the figure
    ###################
    filename = (psf_type + '_' + ('%0.2fexc' %
                                  (excitation_brightness)).replace('.', 'p') +
                '_' + ('%0.2fdep' % (depletion_brightness)).replace('.', 'p') +
                '_' + '%03isamps' % steps_per_excitation_psf_width + '_' +
                '%03ipulses' % pulses_per_position + '.svg')
    print("Saving:", filename, '\n')
    output_prefix = os.path.abspath(
        os.path.join(os.getcwd(), os.pardir, os.pardir, 'big_images',
                     'figure_1'))
    if not os.path.isdir(output_prefix): os.makedirs(output_prefix)
    plt.savefig(os.path.join(output_prefix, filename), bbox_inches='tight')
    plt.close(fig)
def r_vs_depletion_dose_variable_steps():
    """A plot of resolution improvement vs. depletion dose, for fixed
    excitation brightness and a single line-STED orientation, but with
    a step size which varies to keep steps per STED PSF fixed:"""
    depletion_brightnesses = np.linspace(0.1, 18, 200)
    resolution_improvement = {'point': [], 'line': []}
    depletion_dose = {'point': [], 'line': []}
    print('Calculating', end='')
    for depletion_brightness in depletion_brightnesses:
        print('.', end='', sep='')
        for psf_type in ('point', 'line'):
            fine_sampled_report = psf_report(
                psf_type=psf_type,
                excitation_brightness=0.25,
                depletion_brightness=depletion_brightness,
                steps_per_excitation_psf_width=16,
                pulses_per_position=1,
                verbose=False)
            fine_resolution_improvement = fine_sampled_report[
                'resolution_improvement_descanned']
            steps_per_excitation_psf_width = 4 * fine_resolution_improvement
            report = psf_report(
                psf_type=psf_type,
                excitation_brightness=0.25,
                depletion_brightness=depletion_brightness,
                steps_per_excitation_psf_width=steps_per_excitation_psf_width,
                pulses_per_position=1,
                verbose=False)
            resolution_improvement[psf_type].append(
                report['resolution_improvement_descanned'])
            depletion_dose[psf_type].append(report['depletion_dose'])
    print('\nDone calculating.')
    fig = plt.figure(figsize=(12, 4), dpi=100)
    plt.plot(depletion_dose['point'],
             resolution_improvement['point'],
             '.',
             label='Descanned point STED')
    plt.plot(depletion_dose['line'],
             resolution_improvement['line'],
             '-',
             label='Descanned line STED')
    plt.xlim(-500, 12500)
    plt.ylim(1, 4.5)
    plt.legend(loc='lower right')
    plt.grid()
    plt.xlabel('Depletion dose (saturation units)')
    plt.ylabel('Resolution improvement (vs. diffraction limit)')
    plt.title('Resolution improvement vs. depletion dose\n' +
              'for variable step size and fixed excitation brightness')
    plt.savefig(os.path.join(output_directory,
                             '3_r_vs_depletion_dose_variable_steps.svg'),
                bbox_inches='tight')
    plt.close(fig)
    fig = plt.figure(figsize=(12, 4), dpi=100)
    plt.semilogx(depletion_dose['point'],
                 resolution_improvement['point'],
                 '.',
                 label='Descanned point STED')
    plt.semilogx(depletion_dose['line'],
                 resolution_improvement['line'],
                 '-',
                 label='Descanned line STED')
    plt.xlim(1e1, 2e4)
    plt.ylim(1, 4.5)
    plt.legend(loc='lower right')
    plt.grid()
    plt.xlabel('Depletion dose (saturation units)')
    plt.ylabel('Resolution improvement (vs. diffraction limit)')
    plt.title('Resolution improvement vs. depletion dose\n' +
              'for variable step size and fixed excitation brightness')
    plt.savefig(os.path.join(output_directory,
                             '4_r_vs_log_depletion_dose_variable_steps.svg'),
                bbox_inches='tight')
    plt.close(fig)
def psf_comparison_pair(
    point_resolution_improvement,
    line_resolution_improvement,
    point_emissions_per_molecule,
    line_emissions_per_molecule,
    line_scan_type, # 'descanned' or 'rescanned'
    line_num_orientations,
    max_excitation_brightness=0.25,
    steps_per_improved_psf_width=4, # Actual sampling, for Nyquist
    steps_per_excitation_psf_width=25, # Display sampling, for convenience
    ):
    """
    Compute a pair of PSFs, line-STED and point-STED, so we can compare
    their resolution and photodose.
    """
    # Compute correctly sampled PSFs, to get emission levels and light
    # dose:
    print("Calculating point-STED psf...")
    point = st.tune_psf(
        psf_type='point',
        scan_type='descanned',
        desired_resolution_improvement=point_resolution_improvement,
        desired_emissions_per_molecule=point_emissions_per_molecule,
        max_excitation_brightness=max_excitation_brightness,
        steps_per_improved_psf_width=steps_per_improved_psf_width,
        verbose_results=True)
    print("Calculating line-STED psf,", line_num_orientations, "orientations...")
    line = st.tune_psf(
        psf_type='line',
        scan_type=line_scan_type,
        desired_resolution_improvement=line_resolution_improvement,
        desired_emissions_per_molecule=line_emissions_per_molecule,
        max_excitation_brightness=max_excitation_brightness,
        steps_per_improved_psf_width=steps_per_improved_psf_width,
        verbose_results=True)
    # Compute finely sampled PSFs, to interpolate the PSF shapes onto a
    # consistent pixel size for display:
    fine_point = st.psf_report(
        psf_type='point',
        excitation_brightness=point['excitation_brightness'],
        depletion_brightness=point['depletion_brightness'],
        steps_per_excitation_psf_width=steps_per_excitation_psf_width,
        pulses_per_position=point['pulses_per_position'],
        verbose=False)
    fine_line = st.psf_report(
        psf_type='line',
        excitation_brightness=line['excitation_brightness'],
        depletion_brightness=line['depletion_brightness'],
        steps_per_excitation_psf_width=steps_per_excitation_psf_width,
        pulses_per_position=line['pulses_per_position'],
        verbose=False)
    # Normalize the finely sampled PSFs to give the proper emission level:
    def norm(x):
        return x / x.sum()
    point_sted_psf = [point['expected_emission'] *
                      norm(fine_point['psfs']['descan_sted'])]
    assert line['pulses_per_position'] >= line_num_orientations
    psf = {'descanned': 'descan_sted', 'rescanned': 'rescan_sted'}[line_scan_type]
    line_sted_psfs = [1 / line_num_orientations *
                      line['expected_emission'] *
                      rotate(norm(fine_line['psfs'][psf]), angle)
                      for angle in np.arange(0, 180, 180/line_num_orientations)]
    return {'point_sted_psf': point_sted_psf,
            'line_sted_psfs': line_sted_psfs,
            'point': point,
            'line': line}