예제 #1
0
 def _calibrate_laser(self):
     if not os.path.exists('shaker_calibration.tif'):
         # Define simple mirror and laser voltages. The mirror sweeps
         # left-to-right, hopefully at constant speed. The modulator
         # turns on during (roughly) the middle of this sweep, at a
         # constant voltage. This voltage increases linearly over a
         # series of measurements.
         # The mirror voltage is easy:
         measurement_pixels = 80000
         mirror_voltage = np.linspace(0, 2, 3*measurement_pixels)
         # The modulator voltage is slightly trickier:
         desired_num_illuminations = 45
         num_snaps = max(1, int(np.round(desired_num_illuminations /
                                         self.idp.buffer_shape[0])))
         num_illuminations = num_snaps * self.idp.buffer_shape[0]
         modulator_max_voltage = 0.6
         modulator_voltage = np.linspace(
             0, modulator_max_voltage, num_illuminations)
         illuminations = (
             modulator_voltage.reshape(num_illuminations, 1) *
             np.ones(measurement_pixels).reshape(1, measurement_pixels)
             ).reshape(num_snaps,
                       self.idp.buffer_shape[0],
                       measurement_pixels)
         for s in range(num_snaps):
             self.snap_mirror_motion(
                 mirror_voltage,
                 measurement_start_pixel=(mirror_voltage.shape[0] -
                                          measurement_pixels) // 2,
                 measurement_pixels=measurement_pixels,
                 filename='calibration%i.tif'%s,
                 illumination=illuminations[s, :, :])
         data = []
         for s in range(num_snaps):
             data.append(np_tif.tif_to_array('calibration%i.tif'%s
                                             ).astype(np.float32))
             os.remove('calibration%i.tif'%s)
         data = np.concatenate(data, axis=0) # Lazy but effective
         variation = filters.median_filter(data.std(axis=0)**2 /
                                           data.mean(axis=0),
                                           size=3)
         mask = variation > 0.4 * variation.max()
         calibration = (data * mask.reshape((1,) + mask.shape)
                 ).sum(axis=-1).sum(axis=-1)
         calibration -= calibration[0]
         np_tif.array_to_tif(np.array([modulator_voltage,
                                       calibration]),
                             'shaker_calibration.tif')
     calibration = np_tif.tif_to_array('shaker_calibration.tif'
                                       ).astype(np.float64)
     self.laser_calibration = {
         'modulator_voltage': calibration[0, 0, :],
         'illumination_brightness': filters.gaussian_filter(
             calibration[0, 1, :], sigma=0.5)}
     return None
def main():
    output_prefix = os.path.abspath(os.path.join(
        os.getcwd(), os.pardir, os.pardir, 'big_images', 'Figure_2_temp/'))
    psfs, comparisons = calculate_psfs(output_prefix) # Lots going on; see below.
    for im_name in ('cat', 'astronaut', 'lines', 'rings'):
        print("\nTest image:", im_name)
        if deconvolution_is_complete(psfs, output_prefix, im_name):
            print("Using saved deconvolution results.")
        else:
            # Create a deconvolution object for each of the psfs created above
            deconvolvers = {
                name: st.Deconvolver(
                    psf, (os.path.join(output_prefix, im_name+'_'+name+'_')))
                for name, psf in psfs.items()}
            # Use our test object to create simulated data for each imaging
            # method and dump the data to disk:
            test_object = np_tif.tif_to_array(
                'test_object_'+ im_name +'.tif').astype(np.float64)
            for decon_object in deconvolvers.values():
                decon_object.create_data_from_object(
                    test_object, total_brightness=5e10)
                decon_object.record_data()
            # Deconvolve each of our objects, saving occasionally:
            print('Deconvolving...')
            for i, save in st.logarithmic_progress(range(2**10 + 1)):
                for decon_object in deconvolvers.values():
                    decon_object.iterate()
                    if save: decon_object.record_iteration()
            print('\nDone deconvolving.')
        create_figure(comparisons, output_prefix, im_name)
    # Copy the final figures into their own directory:
    src_dir = os.path.join(output_prefix, '1_Figures')
    dst_dir = os.path.abspath(os.path.join(output_prefix,os.pardir,'figure_2'))
    if os.path.isdir(dst_dir): shutil.rmtree(dst_dir)
    shutil.copytree(src_dir, dst_dir)
def main():
    output_prefix = os.path.abspath(
        os.path.join(os.getcwd(), os.pardir, os.pardir, 'big_images',
                     'Figure_3_temp'))
    for output_dir in (os.path.join(output_prefix, '1_gif_figs'),
                       os.path.join(output_prefix, '2_mp4_figs')):
        if not os.path.isdir(output_dir): os.makedirs(output_dir)
    fig = plt.figure(figsize=(10, 4), dpi=100)
    psf_width = 25  # Same as Figure 2, in object pixels
    for im_name in (
            'lines',
            'rings_2',
    ):
        obj = np_tif.tif_to_array('test_object_' + im_name +
                                  '.tif') / 255 + 1e-6
        for fov_size in (
                1,
                2,
        ):
            obj = np.tile(obj, (1, fov_size, fov_size))
            print("\nTest object:", im_name, obj.shape, obj.dtype)
            for R in (1, 2, 3):
                comparison_name = ('%s_%ix%iFOV_%0.2fxR' %
                                   (im_name, fov_size, fov_size, R)).replace(
                                       '.', 'p')
                for imaging_type in ('descan_point', 'nondescan_multipoint'):
                    simulate_imaging(obj,
                                     imaging_type,
                                     psf_width,
                                     R,
                                     num_orientations=1,
                                     pulses_per_position=1,
                                     pad=psf_width,
                                     comparison_name=comparison_name)
                for imaging_type in ('descan_line', 'rescan_line'):
                    for num_orientations in (2, 4, 6):
                        simulate_imaging(obj,
                                         imaging_type,
                                         psf_width,
                                         R,
                                         num_orientations,
                                         pulses_per_position=1,
                                         pad=int(0.45 * max(obj.shape)),
                                         comparison_name=comparison_name)
    # Copy the final figure files into their own directory:
    src_dir = os.path.join(output_prefix, '2_mp4_figs')
    dst_dir = os.path.abspath(
        os.path.join(output_prefix, os.pardir, 'figure_3'))
    if os.path.isdir(dst_dir): shutil.rmtree(dst_dir)
    shutil.copytree(src_dir, dst_dir)
예제 #4
0
def main():
    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_5'):
        os.mkdir('./../images/figure_5')

    num_delays = 3
    image_h = 128
    image_w = 380

    # power calibration:
    # for a given voltage input to the AOM, how many milliwatts do we
    # expect the AOM to output
    green_max_mW = 130  # measured with power meter before sample
    # When the experiment is run, the acquisition code sends voltages to
    # the AOM via the analog out card. The maximum voltage is the same
    # as was used to deliver the max power to the power meter (see
    # previous line). We replaced the green filter with neutral density
    # filters and measured green power on the camera while running the
    # same acquisition code used to take the fluorescence data.
    green_powers = np.array(
        (123, 296, 574, 926, 1591, 2666, 3815))  # units: camera counts
    camera_background_counts = 100
    green_powers = green_powers - camera_background_counts
    green_powers = green_powers * green_max_mW / max(green_powers)  # units: mW

    red_max_mW = 230  # measured with power meter before sample
    red_powers = np.array((0, red_max_mW))

    data = np_tif.tif_to_array(
        './../../stimulated_emission_imaging-data' +
        '/2018_03_05_STE_depletion_orange_bead_15_low_power' +
        '/dataset_green_all_powers_up.tif').astype(np.float64)

    # get rid of overexposed rows at top and bottom of images
    less_rows = 3
    data = data[:, 0 + less_rows:data.shape[1] - less_rows, :]

    # reshape to hyperstack
    data = data.reshape((
        len(green_powers),
        num_delays,
        data.shape[1],
        data.shape[2],
    ))

    # fluorescence image (no STE depletion) stack: the average between
    # max negative and positive red/green pulse delay images
    fluorescence_stack = 0.5 * (data[:, 0, :, :] + data[:, 2, :, :])
    fluorescence_stack_green_early = data[:, 2, :, :]
    # fluorescence image (with STE depletion) stack
    depleted_stack = data[:, 1, :, :]  # zero red/green delay

    # get background signal level
    top_bg = 10
    bot_bg = 20
    left_bg = 10
    right_bg = 20
    fluorescence_signal_bg = (fluorescence_stack[:, top_bg:bot_bg,
                                                 left_bg:right_bg].mean(
                                                     axis=2).mean(axis=1))
    fluorescence_signal_bg_green_early = (
        fluorescence_stack_green_early[:, top_bg:bot_bg,
                                       left_bg:right_bg].mean(axis=2).mean(
                                           axis=1))
    depleted_signal_bg = (depleted_stack[:, top_bg:bot_bg,
                                         left_bg:right_bg].mean(axis=2).mean(
                                             axis=1))

    # crop, bg subtract, and downsample brightest fluorescence image to
    # include on the saturation plot as an inset
    top = 0
    bot = top + 112
    left = 152
    right = left + 130
    fluorescence_cropped = (
        fluorescence_stack_green_early[-1, top:bot, left:right] -
        fluorescence_signal_bg_green_early[-1])
    fluorescence_cropped = bucket(fluorescence_cropped, bucket_size=(8, 8))
    fluorescence_cropped[-2:-1,
                         1:6] = np.max(fluorescence_cropped)  # scale bar

    # average points around center lobe of the fluorescence image to get
    # "average signal level" for darkfield and STE images
    top = 29
    bot = top + 48
    left = 190
    right = left + 48
    fluorescence_signal = (fluorescence_stack[:, top:bot, left:right].mean(
        axis=2).mean(axis=1))
    fluorescence_signal = fluorescence_signal - fluorescence_signal_bg
    depleted_signal = (depleted_stack[:, top:bot,
                                      left:right].mean(axis=2).mean(axis=1))
    depleted_signal = depleted_signal - depleted_signal_bg

    # IMPORTANT PARAMETERS FOR FIT
    brightness = 142  #97
    mW_per_kex = 37  #80
    mW_per_kdep = 5000  #10000

    # equally spaced fluorophore alignment angles (wrt laser polarization)
    K = 400  # number of angles to try
    theta = np.linspace(0, np.pi, K)
    N = K - 1  #number of spaces between angles

    #population weight for each angle
    #norm = normalization factor for summation over theta
    #(phi not necessary due to symmetry)
    norm = np.pi / 2 / N
    n2_weight = np.sin(theta) * norm

    sigma_weight = (np.cos(theta))**2  # cross section weight for each angle

    # finely sample green powers
    green_powers_fine = np.linspace(green_powers[0], green_powers[-1], 100)

    # initiate array that contains model fluorescence v. green at every angle
    model_fl_all = np.zeros((theta.shape[0], green_powers_fine.shape[0]))
    model_fl_max_all = np.zeros((theta.shape[0], green_powers_fine.shape[0]))
    model_fl_min_all = np.zeros((theta.shape[0], green_powers_fine.shape[0]))
    model_fl_dep_all = np.zeros((theta.shape[0], green_powers_fine.shape[0]))
    model_fl_dep_max_all = np.zeros(
        (theta.shape[0], green_powers_fine.shape[0]))
    model_fl_dep_min_all = np.zeros(
        (theta.shape[0], green_powers_fine.shape[0]))

    # compute model fluorescence for each angle
    for theta_index in range(N):
        n2 = n2_weight[theta_index]
        sigma = sigma_weight[theta_index]

        # compute rate constants
        kex = green_powers_fine / mW_per_kex * sigma
        kex_min = 1 / (1 / kex * 0.93)
        kex_max = 1 / (1 / kex * 1.07)
        kdep = red_powers[-1] / mW_per_kdep * sigma
        kdep_min = 1 / (1 / kdep * 0.6)
        kdep_max = 1 / (1 / kdep * 1.4)

        # predict fluorescence
        model_fl = kex / (1 + kex) * n2
        model_fl_all[theta_index, :] = model_fl
        model_fl_max = kex_max / (1 + kex_max) * n2
        model_fl_max_all[theta_index, :] = model_fl_max
        model_fl_min = kex_min / (1 + kex_min) * n2
        model_fl_min_all[theta_index, :] = model_fl_min
        # predict depleted fluorescence
        model_fl_dep = kex / (1 + kex + kdep) * n2
        model_fl_dep_all[theta_index, :] = model_fl_dep
        model_fl_dep_max = kex / (1 + kex + kdep_max) * n2
        model_fl_dep_max_all[theta_index, :] = model_fl_dep_max
        model_fl_dep_min = kex / (1 + kex + kdep_min) * n2
        model_fl_dep_min_all[theta_index, :] = model_fl_dep_min

    # sum weighted fluorescence over all angles
    model_fl = model_fl_all.sum(axis=0)
    model_fl_max = model_fl_max_all.sum(axis=0)
    model_fl_min = model_fl_min_all.sum(axis=0)
    model_fl_dep = model_fl_dep_all.sum(axis=0)
    model_fl_dep_max = model_fl_dep_max_all.sum(axis=0)
    model_fl_dep_min = model_fl_dep_min_all.sum(axis=0)

    fig = plt.figure()
    ax1 = fig.add_subplot(1, 1, 1)
    ax1.plot(green_powers_fine,
             model_fl * brightness,
             '-',
             label=r'Model, $h_{stim}\sigma_{23}=0$',
             color='green')
    ax1.fill_between(green_powers_fine,
                     model_fl_max * brightness,
                     model_fl_min * brightness,
                     color="#C0FFC0")
    dep_mult = ("{:.3f}".format(kdep))
    ax1.plot(green_powers_fine,
             model_fl_dep * brightness,
             '-',
             label=(r'Model, $h_{stim}\sigma_{23}=' + dep_mult +
                    r'(1/\tau_{exc})$'),
             color='red')
    ax1.fill_between(green_powers_fine,
                     model_fl_dep_max * brightness,
                     model_fl_dep_min * brightness,
                     color='#FFD0D0')
    plt.ylabel('Average pixel brightness (sCMOS counts)', fontsize=15)
    ax1.plot(green_powers,
             fluorescence_signal,
             'o',
             label='Measured, no depletion',
             color='green')
    ax1.plot(green_powers,
             depleted_signal,
             'o',
             label='Measured, with depletion (230 mW)',
             color='red')
    ax1.set_xlabel('Excitation power (mW)', fontsize=16)
    plt.axis([0, 140, 0, 73])
    leg = plt.legend(loc='lower right', title='Fluorescence', fontsize=14)
    plt.setp(leg.get_title(), fontsize=15)
    plt.grid()
    ax2 = ax1.twiny()
    formatter = FuncFormatter(
        lambda green_powers, pos: '{:0.2f}'.format(green_powers / mW_per_kex))
    ax2.xaxis.set_major_formatter(formatter)
    ax2.set_xlim(ax1.get_xlim())
    ax2.set_xlabel(r'$h_{exc}\sigma_{01}/(1/\tau_{exc})$', fontsize=17)
    ax2 = ax1.twinx()
    formatter = FuncFormatter(
        lambda model_fl, pos: '{:0.2f}'.format(model_fl / brightness))
    ax2.yaxis.set_major_formatter(formatter)
    ax2.set_ylim(ax1.get_ylim())
    ax2.set_ylabel(r'Orientation-averaged excitation fraction $\bar n_2$',
                   fontsize=17)

    a = plt.axes([0.17, 0.6, .25, .25])
    plt.imshow(fluorescence_cropped, cmap=plt.cm.gray, interpolation='nearest')
    plt.xticks([])
    plt.yticks([])
    a.text(0.9,
           1.5,
           'Orange bead',
           fontsize=14,
           color='white',
           fontweight='bold')
    rect = patches.Rectangle((4.6, 2.9),
                             6,
                             6,
                             linewidth=1,
                             linestyle='dashed',
                             edgecolor='y',
                             facecolor='none')
    a.add_patch(rect)
    plt.savefig(
        './../images/figure_5/fluorescence_depletion_orange_dye_brightness_optimal.svg'
    )
    plt.show()

    # change brightness * 1.5 and scale rate constant to fit
    brightness_hi = brightness * 1.5
    rate_const_mod = 1.91
    extra_kdep_mod = 0.7

    # modify rate constants to fit new brightness
    mW_per_kex_hi = mW_per_kex * rate_const_mod
    mW_per_kdep_hi = mW_per_kdep * rate_const_mod * extra_kdep_mod

    # compute model fluorescence for each angle
    for theta_index in range(N):
        n2 = n2_weight[theta_index]
        sigma = sigma_weight[theta_index]

        # compute rate constants
        kex = green_powers_fine / mW_per_kex_hi * sigma
        kex_min = 1 / (1 / kex * 0.93)
        kex_max = 1 / (1 / kex * 1.07)
        kdep = red_powers[-1] / mW_per_kdep_hi * sigma
        kdep_min = 1 / (1 / kdep * 0.6)
        kdep_max = 1 / (1 / kdep * 1.4)

        # predict fluorescence
        model_fl = kex / (1 + kex) * n2
        model_fl_all[theta_index, :] = model_fl
        model_fl_max = kex_max / (1 + kex_max) * n2
        model_fl_max_all[theta_index, :] = model_fl_max
        model_fl_min = kex_min / (1 + kex_min) * n2
        model_fl_min_all[theta_index, :] = model_fl_min
        # predict depleted fluorescence
        model_fl_dep = kex / (1 + kex + kdep) * n2
        model_fl_dep_all[theta_index, :] = model_fl_dep
        model_fl_dep_max = kex / (1 + kex + kdep_max) * n2
        model_fl_dep_max_all[theta_index, :] = model_fl_dep_max
        model_fl_dep_min = kex / (1 + kex + kdep_min) * n2
        model_fl_dep_min_all[theta_index, :] = model_fl_dep_min

    # sum weighted fluorescence over all angles
    model_fl = model_fl_all.sum(axis=0)
    model_fl_max = model_fl_max_all.sum(axis=0)
    model_fl_min = model_fl_min_all.sum(axis=0)
    model_fl_dep = model_fl_dep_all.sum(axis=0)
    model_fl_dep_max = model_fl_dep_max_all.sum(axis=0)
    model_fl_dep_min = model_fl_dep_min_all.sum(axis=0)

    fig = plt.figure()
    ax1 = fig.add_subplot(1, 1, 1)
    ax1.plot(green_powers_fine,
             model_fl * brightness_hi,
             '-',
             label=r'Model, $h_{stim}\sigma_{23}=0$',
             color='green')
    ax1.fill_between(green_powers_fine,
                     model_fl_max * brightness_hi,
                     model_fl_min * brightness_hi,
                     color="#C0FFC0")
    dep_mult = ("{:.3f}".format(kdep))
    ax1.plot(green_powers_fine,
             model_fl_dep * brightness_hi,
             '-',
             label=(r'Model, $h_{stim}\sigma_{23}=' + dep_mult +
                    r'(1/\tau_{exc})$'),
             color='red')
    ax1.fill_between(green_powers_fine,
                     model_fl_dep_max * brightness_hi,
                     model_fl_dep_min * brightness_hi,
                     color='#FFD0D0')
    plt.ylabel('Average pixel brightness (sCMOS counts)', fontsize=15)
    ax1.plot(green_powers,
             fluorescence_signal,
             'o',
             label='Measured, no depletion',
             color='green')
    ax1.plot(green_powers,
             depleted_signal,
             'o',
             label='Measured, with depletion (230 mW)',
             color='red')
    ax1.set_xlabel('Excitation power (mW)', fontsize=16)
    plt.axis([0, 140, 0, 73])
    leg = plt.legend(loc='lower right', title='Fluorescence', fontsize=14)
    plt.setp(leg.get_title(), fontsize=15)
    plt.grid()
    ax2 = ax1.twiny()
    formatter = FuncFormatter(
        lambda green_powers, pos: '{:0.2f}'.format(green_powers / mW_per_kex))
    ax2.xaxis.set_major_formatter(formatter)
    ax2.set_xlim(ax1.get_xlim())
    ax2.set_xlabel(r'$h_{exc}\sigma_{01}/(1/\tau_{exc})$', fontsize=17)
    ax2 = ax1.twinx()
    formatter = FuncFormatter(
        lambda model_fl, pos: '{:0.2f}'.format(model_fl / brightness_hi))
    ax2.yaxis.set_major_formatter(formatter)
    ax2.set_ylim(ax1.get_ylim())
    ax2.set_ylabel(r'Orientation-averaged excitation fraction $\bar n_2$',
                   fontsize=17)

    a = plt.axes([0.17, 0.6, .25, .25])
    plt.imshow(fluorescence_cropped, cmap=plt.cm.gray, interpolation='nearest')
    plt.xticks([])
    plt.yticks([])
    a.text(0.9,
           1.5,
           'Orange bead',
           fontsize=14,
           color='white',
           fontweight='bold')
    rect = patches.Rectangle((4.6, 2.9),
                             6,
                             6,
                             linewidth=1,
                             linestyle='dashed',
                             edgecolor='y',
                             facecolor='none')
    a.add_patch(rect)
    plt.savefig(
        './../images/figure_5/fluorescence_depletion_orange_dye_brightness_hi.svg'
    )
    plt.show()

    # change brightness / 1.5 and scale rate constant to fit
    brightness_lo = brightness / 1.5
    rate_const_mod = 2.3
    extra_kdep_mod = 0.6

    # modify rate constants to fit new brightness
    mW_per_kex_lo = mW_per_kex / rate_const_mod
    mW_per_kdep_lo = mW_per_kdep / rate_const_mod / extra_kdep_mod

    # compute model fluorescence for each angle
    for theta_index in range(N):
        n2 = n2_weight[theta_index]
        sigma = sigma_weight[theta_index]

        # compute rate constants
        kex = green_powers_fine / mW_per_kex_lo * sigma
        kex_min = 1 / (1 / kex * 0.93)
        kex_max = 1 / (1 / kex * 1.07)
        kdep = red_powers[-1] / mW_per_kdep_lo * sigma
        kdep_min = 1 / (1 / kdep * 0.6)
        kdep_max = 1 / (1 / kdep * 1.4)

        # predict fluorescence
        model_fl = kex / (1 + kex) * n2
        model_fl_all[theta_index, :] = model_fl
        model_fl_max = kex_max / (1 + kex_max) * n2
        model_fl_max_all[theta_index, :] = model_fl_max
        model_fl_min = kex_min / (1 + kex_min) * n2
        model_fl_min_all[theta_index, :] = model_fl_min
        # predict depleted fluorescence
        model_fl_dep = kex / (1 + kex + kdep) * n2
        model_fl_dep_all[theta_index, :] = model_fl_dep
        model_fl_dep_max = kex / (1 + kex + kdep_max) * n2
        model_fl_dep_max_all[theta_index, :] = model_fl_dep_max
        model_fl_dep_min = kex / (1 + kex + kdep_min) * n2
        model_fl_dep_min_all[theta_index, :] = model_fl_dep_min

    # sum weighted fluorescence over all angles
    model_fl = model_fl_all.sum(axis=0)
    model_fl_max = model_fl_max_all.sum(axis=0)
    model_fl_min = model_fl_min_all.sum(axis=0)
    model_fl_dep = model_fl_dep_all.sum(axis=0)
    model_fl_dep_max = model_fl_dep_max_all.sum(axis=0)
    model_fl_dep_min = model_fl_dep_min_all.sum(axis=0)

    fig = plt.figure()
    ax1 = fig.add_subplot(1, 1, 1)
    ax1.plot(green_powers_fine,
             model_fl * brightness_lo,
             '-',
             label=r'Model, $h_{stim}\sigma_{23}=0$',
             color='green')
    ax1.fill_between(green_powers_fine,
                     model_fl_max * brightness_lo,
                     model_fl_min * brightness_lo,
                     color="#C0FFC0")
    dep_mult = ("{:.3f}".format(kdep))
    ax1.plot(green_powers_fine,
             model_fl_dep * brightness_lo,
             '-',
             label=(r'Model, $h_{stim}\sigma_{23}=' + dep_mult +
                    r'(1/\tau_{exc})$'),
             color='red')
    ax1.fill_between(green_powers_fine,
                     model_fl_dep_max * brightness_lo,
                     model_fl_dep_min * brightness_lo,
                     color='#FFD0D0')
    plt.ylabel('Average pixel brightness (sCMOS counts)', fontsize=15)
    ax1.plot(green_powers,
             fluorescence_signal,
             'o',
             label='Measured, no depletion',
             color='green')
    ax1.plot(green_powers,
             depleted_signal,
             'o',
             label='Measured, with depletion (230 mW)',
             color='red')
    ax1.set_xlabel('Excitation power (mW)', fontsize=16)
    plt.axis([0, 140, 0, 73])
    leg = plt.legend(loc='lower right', title='Fluorescence', fontsize=14)
    plt.setp(leg.get_title(), fontsize=15)
    plt.grid()
    ax2 = ax1.twiny()
    formatter = FuncFormatter(
        lambda green_powers, pos: '{:0.2f}'.format(green_powers / mW_per_kex))
    ax2.xaxis.set_major_formatter(formatter)
    ax2.set_xlim(ax1.get_xlim())
    ax2.set_xlabel(r'$h_{exc}\sigma_{01}/(1/\tau_{exc})$', fontsize=17)
    ax2 = ax1.twinx()
    formatter = FuncFormatter(
        lambda model_fl, pos: '{:0.2f}'.format(model_fl / brightness_lo))
    ax2.yaxis.set_major_formatter(formatter)
    ax2.set_ylim(ax1.get_ylim())
    ax2.set_ylabel(r'Orientation-averaged excitation fraction $\bar n_2$',
                   fontsize=17)

    a = plt.axes([0.17, 0.6, .25, .25])
    plt.imshow(fluorescence_cropped, cmap=plt.cm.gray, interpolation='nearest')
    plt.xticks([])
    plt.yticks([])
    a.text(0.9,
           1.5,
           'Orange bead',
           fontsize=14,
           color='white',
           fontweight='bold')
    rect = patches.Rectangle((4.6, 2.9),
                             6,
                             6,
                             linewidth=1,
                             linestyle='dashed',
                             edgecolor='y',
                             facecolor='none')
    a.add_patch(rect)
    plt.savefig(
        './../images/figure_5/fluorescence_depletion_orange_dye_brightness_lo.svg'
    )
    plt.show()

    return None
def main():
    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_5'):
        os.mkdir('./../images/figure_5')

    num_reps = 10  # number of times a power/delay stack was taken
    num_delays = 5

    # power calibration:
    # for a given voltage input to the AOM, how many milliwatts do we
    # expect the AOM to output
    green_max_mW = 1450  # measured with power meter before sample
    # When the experiment is run, the acquisition code sends voltages to
    # the AOM via the analog out card. The maximum voltage is the same
    # as was used to deliver the max power to the power meter (see
    # previous line). We replaced the green filter with neutral density
    # filters and measured green power on the camera while running the
    # same acquisition code used to take the fluorescence data.
    # calibration units: camera counts
    green_powers = np.array((113.9, 119.6, 124.5, 135, 145.5, 159.5, 175.3,
                             193.1, 234.5, 272.2, 334.1, 385.7, 446.1))
    # 100-102 is roughly the baseline level, but ~0.4 s exposure caused
    # ~13 pixel counts of extra light, so we subtract 113.9
    green_powers = green_powers - min(green_powers)
    green_powers = green_powers * green_max_mW / max(green_powers)  # units: mW

    # red powers calibrated using power meter and AOM transmitting 1.25us pulses
    red_max_mW = 300  # measured with power meter before sample
    red_bg = 26.6
    red_powers = np.array((26.6, 113, 198, 276, 353, 438, 537))
    red_powers -= red_bg
    red_powers = red_powers * red_max_mW / max(red_powers)

    # load fluorescence signal data (no spatial resolution)
    data = np_tif.tif_to_array(
        './../../stimulated_emission_imaging-data' +
        '/2016_11_14_modulated_imaging_depletion_nanodiamond_7' +
        '/data_point_signal.tif').astype(np.float64)
    bg = np_tif.tif_to_array(
        './../../stimulated_emission_imaging-data' +
        '/2016_11_14_modulated_imaging_depletion_nanodiamond_7' +
        '/data_point_bg.tif').astype(np.float64)

    # subtract signal from corner of image where there is no fluorophore
    data = data - bg

    # reshape to hyperstack
    data = data.reshape((
        num_reps,
        len(red_powers),
        len(green_powers),
        num_delays,
    ))

    # zero red/green delay is 3rd out of 5 delays (hence index 2)
    depleted_stack = data[:, :, :, 2]
    # max red/green delay is avg of 1st and 5th out of 5 delays
    fluorescence_stack = 0.5 * (data[:, :, :, 0] + data[:, :, :, 4])
    # regular fluorescence is with zero power in the depletion beam,
    # (hence index 0)
    fluorescence_signal_mean = depleted_stack[:, 0, :].mean(axis=0)
    fluorescence_signal_max = depleted_stack[:, 0, :].max(axis=0)
    fluorescence_signal_min = depleted_stack[:, 0, :].min(axis=0)
    fluorescence_signal_std = depleted_stack[:, 0, :].std(axis=0)
    ##    # regular fluorescence is max delay (no depletion) max red power
    ##    fluorescence_signal_mean = fluorescence_stack[:,-1,:].mean(axis=0)
    ##    fluorescence_signal_max = fluorescence_stack[:,-1,:].max(axis=0)
    ##    fluorescence_signal_min = fluorescence_stack[:,-1,:].min(axis=0)
    ##    fluorescence_signal_std = fluorescence_stack[:,-1,:].std(axis=0)
    # maximally depleted fluorescence is with max power in the depletion
    # beam (hence index -1)
    depleted_signal_mean = depleted_stack[:, -1, :].mean(axis=0)
    depleted_signal_max = depleted_stack[:, -1, :].max(axis=0)
    depleted_signal_min = depleted_stack[:, -1, :].min(axis=0)
    depleted_signal_std = depleted_stack[:, -1, :].std(axis=0)

    # make inset image of representative data
    # image is already cropped, averaged (10 reps) and bg subtracted
    inset_image = np_tif.tif_to_array(
        './../../stimulated_emission_imaging-data' +
        '/2016_11_14_modulated_imaging_depletion_nanodiamond_7' +
        '/rep_image_single_shot.tif').astype(np.float64)
    inset_image = inset_image[0, :, :]  # there's only one image
    inset_image = bucket(inset_image, bucket_size=(8, 8))
    inset_image[-2:-1, 1:6] = np.max(inset_image)  # scale bar

    # IMPORTANT PARAMETERS FOR FIT
    brightness = 230  #160
    mW_per_kex = 505  #1130
    mW_per_kdep = 480  #1100

    # equally spaced fluorophore alignment angles (wrt laser polarization)
    K = 400  # number of angles to try
    theta = np.linspace(0, np.pi, K)
    N = K - 1  #number of spaces between angles

    #population weight for each angle
    #norm = normalization factor for summation over theta
    #(phi not necessary due to symmetry)
    norm = np.pi / 2 / N
    n2_weight = np.sin(theta) * norm

    sigma_weight = (np.cos(theta))**2  # cross section weight for each angle

    # finely sample green powers
    green_powers_fine = np.linspace(green_powers[0], green_powers[-1], 100)

    # initiate array that contains model fluorescence v. green at every angle
    model_fl_all = np.zeros((theta.shape[0], green_powers_fine.shape[0]))
    model_fl_max_all = np.zeros((theta.shape[0], green_powers_fine.shape[0]))
    model_fl_min_all = np.zeros((theta.shape[0], green_powers_fine.shape[0]))
    model_fl_dep_all = np.zeros((theta.shape[0], green_powers_fine.shape[0]))
    model_fl_dep_max_all = np.zeros(
        (theta.shape[0], green_powers_fine.shape[0]))
    model_fl_dep_min_all = np.zeros(
        (theta.shape[0], green_powers_fine.shape[0]))

    # compute model fluorescence for each angle
    for theta_index in range(N):
        n2 = n2_weight[theta_index]
        sigma = sigma_weight[theta_index]

        # compute rate constants
        kex = green_powers_fine / mW_per_kex * sigma
        kex_min = 1 / (1 / kex * 0.93)
        kex_max = 1 / (1 / kex * 1.07)
        kdep = red_powers[-1] / mW_per_kdep * sigma
        kdep_min = 1 / (1 / kdep * 0.6)
        kdep_max = 1 / (1 / kdep * 1.4)

        # predict fluorescence
        model_fl = kex / (1 + kex) * n2
        model_fl_all[theta_index, :] = model_fl
        model_fl_max = kex_max / (1 + kex_max) * n2
        model_fl_max_all[theta_index, :] = model_fl_max
        model_fl_min = kex_min / (1 + kex_min) * n2
        model_fl_min_all[theta_index, :] = model_fl_min
        # predict depleted fluorescence
        model_fl_dep = kex / (1 + kex + kdep) * n2
        model_fl_dep_all[theta_index, :] = model_fl_dep
        model_fl_dep_max = kex / (1 + kex + kdep_max) * n2
        model_fl_dep_max_all[theta_index, :] = model_fl_dep_max
        model_fl_dep_min = kex / (1 + kex + kdep_min) * n2
        model_fl_dep_min_all[theta_index, :] = model_fl_dep_min

    # sum weighted fluorescence over all angles
    model_fl = model_fl_all.sum(axis=0)
    model_fl_max = model_fl_max_all.sum(axis=0)
    model_fl_min = model_fl_min_all.sum(axis=0)
    model_fl_dep = model_fl_dep_all.sum(axis=0)
    model_fl_dep_max = model_fl_dep_max_all.sum(axis=0)
    model_fl_dep_min = model_fl_dep_min_all.sum(axis=0)

    fig = plt.figure()
    ax1 = fig.add_subplot(1, 1, 1)
    # plot measured data mean and std
    ax1.errorbar(green_powers,
                 fluorescence_signal_mean,
                 yerr=fluorescence_signal_std,
                 fmt='o',
                 linewidth=3,
                 capthick=2,
                 label='Measured, no depletion',
                 color='green')
    ax1.errorbar(green_powers,
                 depleted_signal_mean,
                 yerr=depleted_signal_std,
                 fmt='o',
                 linewidth=3,
                 capthick=2,
                 label='Measured, with depletion (300 mW)',
                 color='red')
    ax1.set_xlabel('Excitation power (mW)', fontsize=16)
    # plot model fit
    ax1.plot(green_powers_fine,
             model_fl * brightness,
             '-',
             label=r'Model, $h_{stim}\sigma_{23}=0$',
             color='green')
    ax1.fill_between(green_powers_fine,
                     model_fl_max * brightness,
                     model_fl_min * brightness,
                     color="#C0FFC0")
    dep_mult = ("{:.2f}".format(kdep))
    ax1.plot(green_powers_fine,
             model_fl_dep * brightness,
             '-',
             label=(r'Model, $h_{stim}\sigma_{23}=' + dep_mult +
                    r'(1/\tau_{exc})$'),
             color='red')
    ax1.fill_between(green_powers_fine,
                     model_fl_dep_max * brightness,
                     model_fl_dep_min * brightness,
                     color='#FFD0D0')
    plt.ylabel('Average pixel brightness (sCMOS counts)', fontsize=15)
    plt.axis([0, 1600, 0, 102])
    leg = plt.legend(loc='lower right', title='Fluorescence', fontsize=14)
    plt.setp(leg.get_title(), fontsize=15)
    plt.grid()
    # plot other axes
    ax2 = ax1.twiny()
    formatter = FuncFormatter(
        lambda green_powers, pos: '{:0.2f}'.format(green_powers / mW_per_kex))
    ax2.xaxis.set_major_formatter(formatter)
    ax2.set_xlim(ax1.get_xlim())
    ax2.set_xlabel(r'$h_{exc}\sigma_{01}/(1/\tau_{exc})$', fontsize=17)
    ax2 = ax1.twinx()
    formatter = FuncFormatter(
        lambda model_fl, pos: '{:0.2f}'.format(model_fl / brightness))
    ax2.yaxis.set_major_formatter(formatter)
    ax2.set_ylim(ax1.get_ylim())
    ax2.set_ylabel(r'Orientation-averaged excitation fraction $\bar n_2$',
                   fontsize=17)
    # inset image
    a = plt.axes([0.17, 0.6, .25, .25])
    plt.imshow(inset_image, cmap=plt.cm.gray, interpolation='nearest')
    plt.xticks([])
    plt.yticks([])
    a.text(0.4,
           1.5,
           'Nanodiamond',
           fontsize=14,
           color='white',
           fontweight='bold')
    rect = patches.Rectangle((4.6, 3.9),
                             6,
                             6,
                             linewidth=1,
                             linestyle='dashed',
                             edgecolor='y',
                             facecolor='none')
    a.add_patch(rect)
    plt.savefig(
        './../images/figure_5/fluorescence_depletion_nd_brightness_optimal.svg'
    )
    plt.show()

    # change brightness * 1.5 and scale rate constant to fit
    brightness_hi = brightness * 1.5
    rate_const_mod = 1.83
    extra_kdep_mod = 0.6

    # modify rate constants to fit new brightness
    mW_per_kex_hi = mW_per_kex * rate_const_mod
    mW_per_kdep_hi = mW_per_kdep * rate_const_mod * extra_kdep_mod

    # compute model fluorescence for each angle
    for theta_index in range(N):
        n2 = n2_weight[theta_index]
        sigma = sigma_weight[theta_index]

        # compute rate constants
        kex = green_powers_fine / mW_per_kex_hi * sigma
        kex_min = 1 / (1 / kex * 0.93)
        kex_max = 1 / (1 / kex * 1.07)
        kdep = red_powers[-1] / mW_per_kdep_hi * sigma
        kdep_min = 1 / (1 / kdep * 0.6)
        kdep_max = 1 / (1 / kdep * 1.4)

        # predict fluorescence
        model_fl = kex / (1 + kex) * n2
        model_fl_all[theta_index, :] = model_fl
        model_fl_max = kex_max / (1 + kex_max) * n2
        model_fl_max_all[theta_index, :] = model_fl_max
        model_fl_min = kex_min / (1 + kex_min) * n2
        model_fl_min_all[theta_index, :] = model_fl_min
        # predict depleted fluorescence
        model_fl_dep = kex / (1 + kex + kdep) * n2
        model_fl_dep_all[theta_index, :] = model_fl_dep
        model_fl_dep_max = kex / (1 + kex + kdep_max) * n2
        model_fl_dep_max_all[theta_index, :] = model_fl_dep_max
        model_fl_dep_min = kex / (1 + kex + kdep_min) * n2
        model_fl_dep_min_all[theta_index, :] = model_fl_dep_min

    # sum weighted fluorescence over all angles
    model_fl = model_fl_all.sum(axis=0)
    model_fl_max = model_fl_max_all.sum(axis=0)
    model_fl_min = model_fl_min_all.sum(axis=0)
    model_fl_dep = model_fl_dep_all.sum(axis=0)
    model_fl_dep_max = model_fl_dep_max_all.sum(axis=0)
    model_fl_dep_min = model_fl_dep_min_all.sum(axis=0)

    fig = plt.figure()
    ax1 = fig.add_subplot(1, 1, 1)
    # plot measured data mean and std
    ax1.errorbar(green_powers,
                 fluorescence_signal_mean,
                 yerr=fluorescence_signal_std,
                 fmt='o',
                 linewidth=3,
                 capthick=2,
                 label='Measured, no depletion',
                 color='green')
    ax1.errorbar(green_powers,
                 depleted_signal_mean,
                 yerr=depleted_signal_std,
                 fmt='o',
                 linewidth=3,
                 capthick=2,
                 label='Measured, with depletion (300 mW)',
                 color='red')
    ax1.set_xlabel('Excitation power (mW)', fontsize=16)
    # plot model fit
    ax1.plot(green_powers_fine,
             model_fl * brightness_hi,
             '-',
             label=r'Model, $h_{stim}\sigma_{23}=0$',
             color='green')
    ax1.fill_between(green_powers_fine,
                     model_fl_max * brightness_hi,
                     model_fl_min * brightness_hi,
                     color="#C0FFC0")
    dep_mult = ("{:.2f}".format(kdep))
    ax1.plot(green_powers_fine,
             model_fl_dep * brightness_hi,
             '-',
             label=(r'Model, $h_{stim}\sigma_{23}=' + dep_mult +
                    r'(1/\tau_{exc})$'),
             color='red')
    ax1.fill_between(green_powers_fine,
                     model_fl_dep_max * brightness_hi,
                     model_fl_dep_min * brightness_hi,
                     color='#FFD0D0')
    plt.ylabel('Average pixel brightness (sCMOS counts)', fontsize=15)
    plt.axis([0, 1600, 0, 102])
    leg = plt.legend(loc='lower right', title='Fluorescence', fontsize=14)
    plt.setp(leg.get_title(), fontsize=15)
    plt.grid()
    # plot other axes
    ax2 = ax1.twiny()
    formatter = FuncFormatter(
        lambda green_powers, pos: '{:0.2f}'.format(green_powers / mW_per_kex))
    ax2.xaxis.set_major_formatter(formatter)
    ax2.set_xlim(ax1.get_xlim())
    ax2.set_xlabel(r'$h_{exc}\sigma_{01}/(1/\tau_{exc})$', fontsize=17)
    ax2 = ax1.twinx()
    formatter = FuncFormatter(
        lambda model_fl, pos: '{:0.2f}'.format(model_fl / brightness_hi))
    ax2.yaxis.set_major_formatter(formatter)
    ax2.set_ylim(ax1.get_ylim())
    ax2.set_ylabel(r'Orientation-averaged excitation fraction $\bar n_2$',
                   fontsize=17)
    # inset image
    a = plt.axes([0.17, 0.6, .25, .25])
    plt.imshow(inset_image, cmap=plt.cm.gray, interpolation='nearest')
    plt.xticks([])
    plt.yticks([])
    a.text(0.4,
           1.5,
           'Nanodiamond',
           fontsize=14,
           color='white',
           fontweight='bold')
    rect = patches.Rectangle((4.6, 3.9),
                             6,
                             6,
                             linewidth=1,
                             linestyle='dashed',
                             edgecolor='y',
                             facecolor='none')
    a.add_patch(rect)
    plt.savefig(
        './../images/figure_5/fluorescence_depletion_nd_brightness_hi.svg')
    plt.show()

    # change brightness / 1.5 and scale rate constant to fit
    brightness_lo = brightness / 1.5
    rate_const_mod = 2
    extra_kdep_mod = 0.75

    # modify rate constants to fit new brightness
    mW_per_kex_lo = mW_per_kex / rate_const_mod
    mW_per_kdep_lo = mW_per_kdep / rate_const_mod / extra_kdep_mod

    # compute model fluorescence for each angle
    for theta_index in range(N):
        n2 = n2_weight[theta_index]
        sigma = sigma_weight[theta_index]

        # compute rate constants
        kex = green_powers_fine / mW_per_kex_lo * sigma
        kex_min = 1 / (1 / kex * 0.93)
        kex_max = 1 / (1 / kex * 1.07)
        kdep = red_powers[-1] / mW_per_kdep_lo * sigma
        kdep_min = 1 / (1 / kdep * 0.6)
        kdep_max = 1 / (1 / kdep * 1.4)

        # predict fluorescence
        model_fl = kex / (1 + kex) * n2
        model_fl_all[theta_index, :] = model_fl
        model_fl_max = kex_max / (1 + kex_max) * n2
        model_fl_max_all[theta_index, :] = model_fl_max
        model_fl_min = kex_min / (1 + kex_min) * n2
        model_fl_min_all[theta_index, :] = model_fl_min
        # predict depleted fluorescence
        model_fl_dep = kex / (1 + kex + kdep) * n2
        model_fl_dep_all[theta_index, :] = model_fl_dep
        model_fl_dep_max = kex / (1 + kex + kdep_max) * n2
        model_fl_dep_max_all[theta_index, :] = model_fl_dep_max
        model_fl_dep_min = kex / (1 + kex + kdep_min) * n2
        model_fl_dep_min_all[theta_index, :] = model_fl_dep_min

    # sum weighted fluorescence over all angles
    model_fl = model_fl_all.sum(axis=0)
    model_fl_max = model_fl_max_all.sum(axis=0)
    model_fl_min = model_fl_min_all.sum(axis=0)
    model_fl_dep = model_fl_dep_all.sum(axis=0)
    model_fl_dep_max = model_fl_dep_max_all.sum(axis=0)
    model_fl_dep_min = model_fl_dep_min_all.sum(axis=0)

    fig = plt.figure()
    ax1 = fig.add_subplot(1, 1, 1)
    # plot measured data mean and std
    ax1.errorbar(green_powers,
                 fluorescence_signal_mean,
                 yerr=fluorescence_signal_std,
                 fmt='o',
                 linewidth=3,
                 capthick=2,
                 label='Measured, no depletion',
                 color='green')
    ax1.errorbar(green_powers,
                 depleted_signal_mean,
                 yerr=depleted_signal_std,
                 fmt='o',
                 linewidth=3,
                 capthick=2,
                 label='Measured, with depletion (300 mW)',
                 color='red')
    ax1.set_xlabel('Excitation power (mW)', fontsize=16)
    # plot model fit
    ax1.plot(green_powers_fine,
             model_fl * brightness_lo,
             '-',
             label=r'Model, $h_{stim}\sigma_{23}=0$',
             color='green')
    ax1.fill_between(green_powers_fine,
                     model_fl_max * brightness_lo,
                     model_fl_min * brightness_lo,
                     color="#C0FFC0")
    dep_mult = ("{:.2f}".format(kdep))
    ax1.plot(green_powers_fine,
             model_fl_dep * brightness_lo,
             '-',
             label=(r'Model, $h_{stim}\sigma_{23}=' + dep_mult +
                    r'(1/\tau_{exc})$'),
             color='red')
    ax1.fill_between(green_powers_fine,
                     model_fl_dep_max * brightness_lo,
                     model_fl_dep_min * brightness_lo,
                     color='#FFD0D0')
    plt.ylabel('Average pixel brightness (sCMOS counts)', fontsize=15)
    plt.axis([0, 1600, 0, 102])
    leg = plt.legend(loc='lower right', title='Fluorescence', fontsize=14)
    plt.setp(leg.get_title(), fontsize=15)
    plt.grid()
    # plot other axes
    ax2 = ax1.twiny()
    formatter = FuncFormatter(
        lambda green_powers, pos: '{:0.2f}'.format(green_powers / mW_per_kex))
    ax2.xaxis.set_major_formatter(formatter)
    ax2.set_xlim(ax1.get_xlim())
    ax2.set_xlabel(r'$h_{exc}\sigma_{01}/(1/\tau_{exc})$', fontsize=17)
    ax2 = ax1.twinx()
    formatter = FuncFormatter(
        lambda model_fl, pos: '{:0.2f}'.format(model_fl / brightness_lo))
    ax2.yaxis.set_major_formatter(formatter)
    ax2.set_ylim(ax1.get_ylim())
    ax2.set_ylabel(r'Orientation-averaged excitation fraction $\bar n_2$',
                   fontsize=17)
    # inset image
    a = plt.axes([0.17, 0.6, .25, .25])
    plt.imshow(inset_image, cmap=plt.cm.gray, interpolation='nearest')
    plt.xticks([])
    plt.yticks([])
    a.text(0.4,
           1.5,
           'Nanodiamond',
           fontsize=14,
           color='white',
           fontweight='bold')
    rect = patches.Rectangle((4.6, 3.9),
                             6,
                             6,
                             linewidth=1,
                             linestyle='dashed',
                             edgecolor='y',
                             facecolor='none')
    a.add_patch(rect)
    plt.savefig(
        './../images/figure_5/fluorescence_depletion_nd_brightness_lo.svg')
    plt.show()

    return None
예제 #6
0
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_4'):
        os.mkdir('./../images/figure_4')

    less_rows = 3  # top and bottom image rows tend to saturate
    num_reps = 3000  #original number of reps
    reps_avgd = 1
    reps_per_set = int(num_reps / reps_avgd)
    num_delays = 3
    dark_counts = 100
    sets = ['a', 'b', 'c']
    image_center = np.array([[52, 192], [52, 192], [52, 192]])
    # change image center coordinate height due to cropping
    image_center = image_center - np.array([less_rows, 0])
    assert len(sets) == len(image_center)
    height = 128
    width = 380
    lbhw = 28  # half width of box around main image lobe
    # get bg level for brightness calibration across all data sets
    ref_data_set = 'a'
    ref_filename = ('./../../stimulated_emission_imaging-data' +
                    '/2018_03_05_STE_phase_bleaching_orange_bead_14_' +
                    ref_data_set +
                    '/STE_phase_angle_1_green_130mW_red_230mW.tif')
    set_data = np_tif.tif_to_array(ref_filename).astype(
        np.float64) - dark_counts
    set_data = set_data.reshape(reps_per_set, num_delays, height, width)
    # get rid of overexposed rows at top and bottom of images
    set_data = set_data[:, :, 0 + less_rows:height - less_rows, :]

    avg_laser_brightness = get_bg_level(set_data.mean(axis=(0, 1)))

    all_STE_images = np.zeros(
        (reps_per_set * len(sets), height - less_rows * 2, width))
    STE_signal = np.zeros((reps_per_set * len(sets)))
    bg_signal = np.zeros((reps_per_set * len(sets)))

    for my_index, my_set in enumerate(sets):
        filename = ('./../../stimulated_emission_imaging-data' +
                    '/2018_03_05_STE_phase_bleaching_orange_bead_14_' +
                    my_set + '/STE_phase_angle_1_green_130mW_red_230mW.tif')
        set_data = np_tif.tif_to_array(filename).astype(
            np.float64) - dark_counts
        assert set_data.shape == (reps_per_set * num_delays, height, width)
        set_data = set_data.reshape(reps_per_set, num_delays, height, width)
        set_data = set_data[:, :, 0 + less_rows:height - less_rows, :]

        # scale all images to have the same background brightness. This
        # amounts to a correction of roughly 1% or less
        local_laser_brightness = get_bg_level(set_data)
        set_data = set_data * (avg_laser_brightness /
                               local_laser_brightness).reshape(
                                   set_data.shape[0], set_data.shape[1], 1, 1)

        # get zero delay and max delay images
        zero_delay_images = set_data[:, 1, :, :]
        max_delay_images = set_data[:, 0:3:2, :, :].mean(axis=1)

        # local range in global set
        begin = my_index * reps_per_set
        end = begin + reps_per_set

        # stim emission image is the image with green/red simultaneous minus
        # image with/red green not simultaneous
        STE_image_set = zero_delay_images - max_delay_images
        all_STE_images[begin:end] = STE_image_set

        # average points around main STE image lobe and add to STE_signal list
        ste_y, ste_x = image_center[my_index]
        STE_signal[begin:end] = STE_image_set[:, ste_y - lbhw:ste_y + lbhw,
                                              ste_x - lbhw:ste_x +
                                              lbhw].mean(axis=2).mean(axis=1)

    # average consecutive STE images for better SNR (mandatory)
    bucket_width = 50
    all_STE_images = bucket(all_STE_images,
                            (bucket_width, 1, 1)) / bucket_width

    # average consecutive STE signal levels for better SNR (optional)
    signal_bucket_width = 50
    orig_STE_signal = STE_signal
    STE_signal = np.array([STE_signal])
    STE_signal = bucket(STE_signal,
                        (1, signal_bucket_width)) / signal_bucket_width
    STE_signal = STE_signal[0, :]

    # choose images to display and laterally smooth them
    sigma = 9  # tune this parameter to reject high spatial frequencies
    STE_display_imgs = np.array([
        all_STE_images[0, :, :],
        all_STE_images[int(all_STE_images.shape[0] / 2), :, :],
        all_STE_images[-1, :, :]
    ])
    STE_display_imgs = gaussian_filter(STE_display_imgs,
                                       sigma=(0, sigma, sigma))

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    bucket_width = 8  # bucket width in pixels
    STE_display_imgs = bucket(
        STE_display_imgs, (1, bucket_width, bucket_width)) / bucket_width**2

    # crop images left and right sides
    less_cols = 5
    STE_display_imgs = STE_display_imgs[:, :, less_cols:-less_cols]

    # get max and minimum values to display images with unified color scale
    max_pixel_value = np.max(STE_display_imgs)
    min_pixel_value = np.min(STE_display_imgs)
    print(max_pixel_value, min_pixel_value)
    max_pixel_value = 102.6  # from figure_8_crimson.py
    min_pixel_value = -49  # from figure_8_crimson.py

    STE_display_imgs[:, -2, 1:6] = max_pixel_value  # scale bar

    # number of accumulated excitation pulses for x axis of bleaching plot
    pulses_per_exposure = 8
    exposures_per_delay_scan = 3
    num_delay_scans = len(sets) * num_reps
    orig_pulses_axis = (np.arange(num_delay_scans) * pulses_per_exposure *
                        exposures_per_delay_scan)
    pulses_axis = ((np.arange(num_delay_scans / signal_bucket_width) + 0.5) *
                   pulses_per_exposure * exposures_per_delay_scan *
                   signal_bucket_width)

    plt.figure(figsize=(13, 5))
    plt.plot(orig_pulses_axis,
             orig_STE_signal,
             'o',
             markersize=2.5,
             markerfacecolor='none',
             markeredgecolor='blue')
    plt.plot(pulses_axis, STE_signal, color='red')

    # lines from images to data points
    for x in np.arange(50189, 162114, 1000):
        plt.plot(
            [pulses_axis[0], x],
            [STE_signal[0], 76],
            'k',
            lw=0.1,
        )
    for x in np.arange(213673, 325598, 1000):
        plt.plot(
            [pulses_axis[int(pulses_axis.shape[0] / 2)], x],
            [STE_signal[int(STE_signal.shape[0] / 2)], 76],
            'k',
            lw=0.1,
        )
    for x in np.arange(377157, 489081, 1000):
        plt.plot(
            [pulses_axis[-1], x],
            [STE_signal[-1], 76],
            'k',
            lw=0.1,
        )

    plt.axis([-2000, 502800 + 2000, -25, 110])
    plt.grid()
    plt.ylabel('Average pixel brightness (sCMOS counts)', fontsize=14)
    plt.xlabel('Number of excitation pulses delivered to sample', fontsize=18)
    a = plt.axes([.2, .7, .18, .18])
    plt.imshow(STE_display_imgs[0, :, :],
               cmap=plt.cm.gray,
               interpolation='nearest',
               vmax=max_pixel_value,
               vmin=min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    a = plt.axes([.45, .7, .18, .18])
    plt.imshow(STE_display_imgs[1, :, :],
               cmap=plt.cm.gray,
               interpolation='nearest',
               vmax=max_pixel_value,
               vmin=min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    a = plt.axes([.7, .7, .18, .18])
    plt.imshow(STE_display_imgs[2, :, :],
               cmap=plt.cm.gray,
               interpolation='nearest',
               vmax=max_pixel_value,
               vmin=min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    plt.savefig('./../images/figure_4/STE_v_fluence_orange.svg')
    plt.savefig('./../images/figure_4/STE_v_fluence_orange.png', dpi=300)
    plt.show()

    return None
예제 #7
0
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_4'):
        os.mkdir('./../images/figure_4')

    crop_rows = 3 # these image rows tend to saturate
    num_reps = 3000 #original number of reps
    reps_avgd = 1
    reps_per_set = int(num_reps/reps_avgd)
    num_delays = 3
    sets = ['a', 'b', 'c', 'd', 'e']
    image_center = [
        [75, 179],
        [77, 180],
        [79, 180],
        [82, 180],
        [84, 177]]
    assert len(sets) == len(image_center)
    height = 128
    width = 380
    lbhw = 28 # half width of box around main image lobe
    crop_half_height = 43
    crop_half_width = 80
    bg_up = 9
    bg_down = 115
    bg_left = 335
    bg_right = 373
    bg_level = 101.7

    all_fl_images = np.zeros((
        reps_per_set*len(sets),
        crop_half_height * 2,
        crop_half_width * 2))
    fl_signal = np.zeros((reps_per_set*len(sets)))
    bg_signal = np.zeros((reps_per_set*len(sets)))
    
    for my_index, my_set in enumerate(sets):
        filename = (
            './../../stimulated_emission_imaging-data' +
            '/2018_05_08_crimson_fluorescence_depletion_bleaching_bead_8' +
            '/STE_depletion_green_1010mW_red_230mW_' + my_set + '.tif')
        set_data = np_tif.tif_to_array(filename).astype(np.float64)
        assert set_data.shape == ((reps_per_set+1)*num_delays,height,width)
        set_data = set_data.reshape(reps_per_set+1,num_delays,height,width)
        set_data = set_data[1::, ...]
        begin = my_index * reps_per_set
        end = begin + reps_per_set

        fl_y, fl_x = image_center[my_index]

        # for fluorescence images just average all three images in delay scan
        fl_image_set = set_data.mean(axis=1)
        all_fl_images[begin:end, :, :] = fl_image_set[
            :,
            fl_y - crop_half_height:fl_y + crop_half_height,
            fl_x - crop_half_width:fl_x + crop_half_width]

        # average points around main STE image lobe and add to STE_signal list
        fl_signal[begin:end] = fl_image_set[
            :,
            fl_y - lbhw:fl_y + lbhw,
            fl_x - lbhw:fl_x + lbhw
            ].mean(axis=2).mean(axis=1)

        # average lots of points away from STE image
        bg_signal[begin:end] = fl_image_set[
            :, bg_up:bg_down, bg_left:bg_right].mean(axis=2).mean(axis=1)

    # subtract background signal
    fl_signal -= bg_level
    all_fl_images -= bg_level

    # average consecutive fl images for better SNR of weak fluorescence (optional)
    bucket_width = 50
    all_fl_images = bucket(
        all_fl_images, (bucket_width, 1, 1)) / bucket_width

##    fl_signal = fl_signal / max(fl_signal) # normalize

    # average consecutive STE signal levels for better SNR (optional)
    signal_bucket_width = 50
    orig_fl_signal = fl_signal
    fl_signal = np.array([fl_signal])
    fl_signal = bucket(
        fl_signal, (1, signal_bucket_width)) / signal_bucket_width
    fl_signal = fl_signal[0, :]

    # choose images to display
    fl_display_imgs = np.array([
        all_fl_images[0, :, :],
        all_fl_images[int(all_fl_images.shape[0] / 2), :, :],
        all_fl_images[-1, :, :]])

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    bucket_width = 8 # bucket width in pixels
    fl_display_imgs = bucket(
        fl_display_imgs, (1, bucket_width, bucket_width)) / bucket_width**2

    # get max and minimum values to display images with unified color scale
    max_pixel_value = np.max(fl_display_imgs)
    min_pixel_value = np.min(fl_display_imgs)
    print(max_pixel_value, min_pixel_value)

    fl_display_imgs[:, -2, 1:6] = max_pixel_value # scale bar


    half_level = np.argmin(np.absolute(orig_fl_signal - 0.5))
    quarter_level = np.argmin(np.absolute(orig_fl_signal - 0.25))
    eighth_level = np.argmin(np.absolute(orig_fl_signal - 0.125))
##    half_level = np.argmin(np.absolute(orig_fl_signal_norm - 0.5))
##    quarter_level = np.argmin(np.absolute(orig_fl_signal_norm - 0.25))
##    eighth_level = np.argmin(np.absolute(orig_fl_signal_norm - 0.125))
    pulses_100h = 3 * 8 * half_level
    pulses_hq = 3 * 8 * (quarter_level - half_level)
    pulses_qe = 3 * 8 * (eighth_level - quarter_level)

    print('100% to half level in', pulses_100h, 'pulses')
    print('Half to quarter level in', pulses_hq, 'pulses')
    print('Quarter to eighth level in', pulses_qe, 'pulses')
    
    # number of accumulated excitation pulses for x axis of bleaching plot
    pulses_per_exposure = 8
    exposures_per_delay_scan = 3
    num_delay_scans = len(sets) * num_reps
    orig_pulses_axis = (
        np.arange(num_delay_scans) *
        pulses_per_exposure *
        exposures_per_delay_scan)
    pulses_axis = (
        (np.arange(num_delay_scans / signal_bucket_width) + 0.5) *
        pulses_per_exposure *
        exposures_per_delay_scan *
        signal_bucket_width)

    # finally plot
    plt.figure(figsize=(13,5))
    plt.plot(
        orig_pulses_axis, orig_fl_signal,
        'o', markersize = 2.5,
        markerfacecolor='none', markeredgecolor='blue')
    plt.plot(pulses_axis, fl_signal, color='red')

    # lines from images to data points
    for x in np.arange(60878, 151424, 2000):
        plt.plot(
            [pulses_axis[0], x],
            [fl_signal[0], 27.7],
            'k', lw=0.1,
            )
    for x in np.arange(224362, 314908, 1000):
        plt.plot(
            [pulses_axis[int(pulses_axis.shape[0] / 2)], x],
            [fl_signal[int(fl_signal.shape[0] / 2)], 27.7],
            'k', lw=0.1,
            )
    for x in np.arange(387846, 478392, 1000):
        plt.plot(
            [pulses_axis[-1], x],
            [fl_signal[-1], 27.7],
            'k', lw=0.1,
            )

    plt.axis([0-2000, 502800 + 2000, -9, 40])

    plt.grid()
    plt.ylabel('Fluorescence brightness (sCMOS counts)', fontsize=14)
    plt.xlabel('Number of excitation pulses delivered to sample', fontsize=18)
    a = plt.axes([.2, .7, .18, .18])
    plt.imshow(
        fl_display_imgs[0, :, :],
        cmap=plt.cm.gray,
        interpolation='nearest',
        vmax = max_pixel_value,
        vmin = min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    a = plt.axes([.45, .7, .18, .18])
    plt.imshow(
        fl_display_imgs[1, :, :],
        cmap=plt.cm.gray,
        interpolation='nearest',
        vmax = max_pixel_value,
        vmin = min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    a = plt.axes([.7, .7, .18, .18])
    plt.imshow(
        fl_display_imgs[2, :, :],
        cmap=plt.cm.gray,
        interpolation='nearest',
        vmax = max_pixel_value,
        vmin = min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    plt.savefig('./../images/figure_4/fl_v_fluence_crimson.svg')
    plt.savefig('./../images/figure_4/fl_v_fluence_crimson.png', dpi=300)
    plt.show()

    return None
def main():
    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_3'):
        os.mkdir('./../images/figure_3')

    #####################################################################
    # meltmount mix data
    data = np_tif.tif_to_array(
        './../../stimulated_emission_imaging-data' +
        '/2018_02_23_STE_phase_cr_bead_4' +
        '/dataset_green_1010mW_single_shot.tif').astype(np.float64)

    # get rid of overexposed rows at top and bottom of images
    less_rows = 3
    data = data[:, 0+less_rows:data.shape[1]-less_rows, :]
    data = data[:, ::-1, :] # flip up down

    # reshape to hyperstack
    num_delays = 3
    data = data.reshape((
        data.shape[0]/num_delays,# phase plate angle number
        num_delays,
        data.shape[1],
        data.shape[2],
        ))

    # Get the average pixel brightness in the background region of the
    # meltmount mix data. We'll use it to account for laser intensity
    # fluctuations
    avg_laser_brightness = get_bg_level(data.mean(axis=(0, 1)))
    
    # scale all images to have the same background brightness. This
    # amounts to a correction of roughly 1% or less
    local_laser_brightness = get_bg_level(data)
    data = data * (avg_laser_brightness / local_laser_brightness).reshape(
        data.shape[0], data.shape[1], 1, 1)

    # get zero delay images, max delay images and phase contrast images
    zero_delay_images = data[:, 1, :, :] # zero red/green delay
    max_delay_images = data[
        :, 0:3:2, :, :].mean(axis=1) # average max and min delay
    phase_contrast_images = data[:, 0, :, :] # red before green (min delay)
    
    # from the image where red/green are simultaneous, subtract the
    # average of the max and min delay images
    STE_stack = zero_delay_images - max_delay_images

    # phase contrast image (no STE) stack: there is a large background
    # variation that has nothing to do with the sample; it's due to
    # multiple reflections in the microscope. Some of it moves when you
    # move the phase plate, and some of it doesn't. This step subtracts
    # off the stationary component. For each image we use in the figure,
    # we subtract the minimum contrast image with the closest phase plate angle.
    # minimum contrast phase plate angle closest to first 7 phase plate angles:
    min_contrast_index_1 = 5
    # minimum contrast phase plate angle closest to last 7 phase plate angles:
    min_contrast_index_2 = 11
    phase_stack = phase_contrast_images
    phase_stack[0:8, ...] = phase_stack[0:8, ...] - phase_contrast_images[
        min_contrast_index_1:min_contrast_index_1 + 1, :, :]
    phase_stack[8:15, ...] = phase_stack[8:15, ...] - phase_contrast_images[
        min_contrast_index_2:min_contrast_index_2 + 1, :, :]

    # Luckily the non-stationary component is comprised of stripes that
    # are completely outside of the microscope's spatial pass-band. The
    # smoothing step below strongly attenuates this striping artifact
    # with almost no effect on spatial frequencies due to the sample.
    sigma = 9 # tune this parameter to reject high spatial frequencies
    STE_stack = gaussian_filter(STE_stack, sigma=(0, sigma, sigma))
    phase_stack = gaussian_filter(phase_stack, sigma=(0, sigma, sigma))

    # crop images to center bead and fit into figure
    top = 0
    bot = 122
    left = 109
    right = 361
    phase_cropped = phase_stack[:, top:bot, left:right]
    STE_cropped = STE_stack[:, top:bot, left:right]

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    bucket_width = 8 # bucket width in pixels
    phase_cropped = bucket(
        phase_cropped, (1, bucket_width, bucket_width)) / bucket_width**2
    STE_cropped = bucket(
        STE_cropped, (1, bucket_width, bucket_width)) / bucket_width**2

    # display images from the two phase plate angles that maximize bead
    # contrast (+/- contrast)
    zero_phase_angle = 8
    pi_phase_angle = 0
    n_mix_zero_phase_bead_image = phase_cropped[zero_phase_angle, :, :]
    n_mix_pi_phase_bead_image = phase_cropped[pi_phase_angle, :, :]
    n_mix_zero_phase_STE_image = STE_cropped[zero_phase_angle, :, :]
    n_mix_pi_phase_STE_image = STE_cropped[pi_phase_angle, :, :]
    
    #####################################################################
    #####################################################################
    # meltmount n = 1.54 data
    data = np_tif.tif_to_array(
        './../../stimulated_emission_imaging-data' +
        '/2018_02_27_STE_phase_n_1_54_cr_bead_0' +
        '/dataset_green_970mW_single_shot.tif').astype(np.float64)

    # get rid of overexposed rows at top and bottom of images
    data = data[:, 0+less_rows:data.shape[1]-less_rows, :]

    # reshape to hyperstack
    data = data.reshape((
        data.shape[0]/num_delays,# phase plate angle number
        num_delays,
        data.shape[1],
        data.shape[2],
        ))
    
    # scale all images to have the same background brightness. This
    # amounts to a correction of roughly 1% or less
    local_laser_brightness = get_bg_level(data)
    data = data * (avg_laser_brightness / local_laser_brightness).reshape(
        data.shape[0], data.shape[1], 1, 1)

    # get zero delay images, max delay images and phase contrast images
    zero_delay_images = data[:, 1, :, :] # zero red/green delay
    max_delay_images = data[
        :, 0:3:2, :, :].mean(axis=1) # average max and min delay
    phase_contrast_images = data[:, 0, :, :] # red before green (min delay)
    
    # from the image where red/green are simultaneous, subtract the
    # average of the max and min delay images
    STE_stack = zero_delay_images - max_delay_images

    # phase contrast image (no STE) stack: there is a large background
    # variation that has nothing to do with the sample; it's due to
    # multiple reflections in the microscope. Some of it moves when you
    # move the phase plate, and some of it doesn't. This step subtracts
    # off the stationary component. For each image we use in the figure,
    # we subtract the minimum contrast image with the closest phase plate angle.
    # minimum contrast phase plate angle closest to first 7 phase plate angles:
    min_contrast_index_1 = 5
    # minimum contrast phase plate angle closest to last 7 phase plate angles:
    min_contrast_index_2 = 11
    phase_stack = phase_contrast_images
    phase_stack[0:8, ...] = phase_stack[0:8, ...] - phase_contrast_images[
        min_contrast_index_1:min_contrast_index_1 + 1, :, :]
    phase_stack[8:15, ...] = phase_stack[8:15, ...] - phase_contrast_images[
        min_contrast_index_2:min_contrast_index_2 + 1, :, :]

    # Luckily the non-stationary component is comprised of stripes that
    # are completely outside of the microscope's spatial pass-band. The
    # smoothing step below strongly attenuates this striping artifact
    # with almost no effect on spatial frequencies due to the sample.
    STE_stack = gaussian_filter(STE_stack, sigma=(0, sigma, sigma))
    phase_stack = gaussian_filter(phase_stack, sigma=(0, sigma, sigma))

    # crop images to center bead and fit into figure
    top = 0
    bot = 122
    left = 44
    right = 296
    phase_cropped = phase_stack[:,top:bot,left:right]
    STE_cropped = STE_stack[:,top:bot,left:right]

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    phase_cropped = bucket(
        phase_cropped, (1, bucket_width, bucket_width)) / bucket_width**2
    STE_cropped = bucket(
        STE_cropped, (1, bucket_width, bucket_width)) / bucket_width**2

    # display images from the two phase plate angles that maximize bead
    # contrast (+/- contrast)
    zero_phase_angle = 8
    pi_phase_angle = 13
    n_1_53_zero_phase_bead_image = phase_cropped[zero_phase_angle, :, :]
    n_1_53_pi_phase_bead_image = phase_cropped[pi_phase_angle, :, :]
    n_1_53_zero_phase_STE_image = STE_cropped[zero_phase_angle, :, :]
    n_1_53_pi_phase_STE_image = STE_cropped[pi_phase_angle, :, :]
    
    #####################################################################
    #####################################################################
    # meltmount n = 1.61 data
    data = np_tif.tif_to_array(
        './../../stimulated_emission_imaging-data' +
        '/2018_02_26_STE_phase_n_1_61_cr_bead_0' +
        '/dataset_green_1060mW_single_shot.tif').astype(np.float64)

    # get rid of overexposed rows at top and bottom of images
    data = data[:, 0+less_rows:data.shape[1]-less_rows, :]
    data = data[:, ::-1, :] # flip up down

    # reshape to hyperstack
    data = data.reshape((
        data.shape[0]/num_delays,# phase plate angle number
        num_delays,
        data.shape[1],
        data.shape[2],
        ))

    # scale all images to have the same background brightness. This
    # amounts to a correction of roughly 1% or less
    local_laser_brightness = get_bg_level(data)
    data = data * (avg_laser_brightness / local_laser_brightness).reshape(
        data.shape[0], data.shape[1], 1, 1)

    # get zero delay images, max delay images and phase contrast images
    zero_delay_images = data[:, 1, :, :] # zero red/green delay
    max_delay_images = data[
        :, 0:3:2, :, :].mean(axis=1) # average max and min delay
    phase_contrast_images = data[:, 0, :, :] # red before green (min delay)
    
    # from the image where red/green are simultaneous, subtract the
    # average of the max and min delay images
    STE_stack = zero_delay_images - max_delay_images
    
    # phase contrast image (no STE) stack: there is a large background
    # variation that has nothing to do with the sample; it's due to
    # multiple reflections in the microscope. Some of it moves when you
    # move the phase plate, and some of it doesn't. This step subtracts
    # off the stationary component. For each image we use in the figure,
    # we subtract the minimum contrast image with the closest phase plate angle.
    # minimum contrast phase plate angle closest to first 7 phase plate angles:
    min_contrast_index_1 = 5
    # minimum contrast phase plate angle closest to last 7 phase plate angles:
    min_contrast_index_2 = 11
    phase_stack = phase_contrast_images
    phase_stack[0:8, ...] = phase_stack[0:8, ...] - phase_contrast_images[
        min_contrast_index_1:min_contrast_index_1 + 1, :, :]
    phase_stack[8:15, ...] = phase_stack[8:15, ...] - phase_contrast_images[
        min_contrast_index_2:min_contrast_index_2 + 1, :, :]

    # Luckily the non-stationary component is comprised of stripes that
    # are completely outside of the microscope's spatial pass-band. The
    # smoothing step below strongly attenuates this striping artifact
    # with almost no effect on spatial frequencies due to the sample.
    STE_stack = gaussian_filter(STE_stack, sigma=(0, sigma, sigma))
    phase_stack = gaussian_filter(phase_stack, sigma=(0, sigma, sigma))

    # crop images to center bead and fit into figure
    top = 0
    bot = 122
    left = 59
    right = 311
    phase_cropped = phase_stack[:,top:bot,left:right]
    STE_cropped = STE_stack[:,top:bot,left:right]

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    phase_cropped = bucket(
        phase_cropped, (1, bucket_width, bucket_width)) / bucket_width**2
    STE_cropped = bucket(
        STE_cropped, (1, bucket_width, bucket_width)) / bucket_width**2

    # display images from the two phase plate angles that maximize bead
    # contrast (+/- contrast)
    zero_phase_angle = 8
    pi_phase_angle = 0
    n_1_61_zero_phase_bead_image = phase_cropped[zero_phase_angle, :, :]
    n_1_61_pi_phase_bead_image = phase_cropped[pi_phase_angle, :, :]
    n_1_61_zero_phase_STE_image = STE_cropped[zero_phase_angle, :, :]
    n_1_61_pi_phase_STE_image = STE_cropped[pi_phase_angle, :, :]
    #####################################################################
    #####################################################################
    
    # start plotting all the images

    # get max and min values to unify the colorbar
    all_phase = np.concatenate((
        n_mix_zero_phase_bead_image,
        n_1_53_zero_phase_bead_image,
        n_1_61_zero_phase_bead_image,
        n_mix_pi_phase_bead_image,
        n_1_53_pi_phase_bead_image,
        n_1_61_pi_phase_bead_image), axis=0)
    all_STE = np.concatenate((
        n_mix_zero_phase_STE_image,
        n_1_53_zero_phase_STE_image,
        n_1_61_zero_phase_STE_image,
        n_mix_pi_phase_STE_image,
        n_1_53_pi_phase_STE_image,
        n_1_61_pi_phase_STE_image), axis=0)
    max_phase = int(np.amax(all_phase)) + 1
    min_phase = int(np.amin(all_phase)) - 1
    max_ste = int(np.amax(all_STE)) + 1
    min_ste = int(np.amin(all_STE)) - 1

    # make scale bar black to give lower limit on colorbar
    bar_left = 1
    bar_right = 6
    bar_vert = -2
    n_mix_zero_phase_bead_image[bar_vert, bar_left:bar_right] = min_phase
    n_1_53_zero_phase_bead_image[bar_vert, bar_left:bar_right] = min_phase
    n_1_61_zero_phase_bead_image[bar_vert, bar_left:bar_right] = min_phase
    n_mix_pi_phase_bead_image[bar_vert, bar_left:bar_right] = min_phase
    n_1_53_pi_phase_bead_image[bar_vert, bar_left:bar_right] = min_phase
    n_1_61_pi_phase_bead_image[bar_vert, bar_left:bar_right] = min_phase
    n_mix_zero_phase_STE_image[bar_vert, bar_left:bar_right] = min_ste
    n_1_53_zero_phase_STE_image[bar_vert, bar_left:bar_right] = min_ste
    n_1_61_zero_phase_STE_image[bar_vert, bar_left:bar_right] = min_ste
    n_mix_pi_phase_STE_image[bar_vert, bar_left:bar_right] = min_ste
    n_1_53_pi_phase_STE_image[bar_vert, bar_left:bar_right] = min_ste
    n_1_61_pi_phase_STE_image[bar_vert, bar_left:bar_right] = min_ste

    # create wider image comprised of three side-by-side images
    # get width of wider image
    num_angles, height, width = STE_cropped.shape
    between_pics = int(16 / bucket_width)
    big_width = width*3 + between_pics*2
    # initialize wide phase contrast image and make "between color" white
    between_color = max_phase # makes it white and gives upper limit on colorbar
    zero_phase_bead_image = np.zeros((height,big_width)) + between_color
    pi_phase_bead_image = np.zeros((height,big_width)) + between_color
    # initialize wide STE image and make "between color" white
    between_color = max_ste # makes it white and gives upper limit on colorbar
    zero_phase_STE_image = np.zeros((height,big_width)) + between_color
    pi_phase_STE_image = np.zeros((height,big_width)) + between_color
    # n = 1.53 images on left side of wide image
    left = 0
    right = width
    zero_phase_bead_image[:,left:right] = n_1_53_zero_phase_bead_image
    pi_phase_bead_image[:,left:right] = n_1_53_pi_phase_bead_image
    zero_phase_STE_image[:,left:right] = n_1_53_zero_phase_STE_image
    pi_phase_STE_image[:,left:right] = n_1_53_pi_phase_STE_image
    # n = 1.58/1.61 mix images in center of wide image
    left = width + between_pics
    right = width*2 + between_pics
    zero_phase_bead_image[:,left:right] = n_mix_zero_phase_bead_image
    pi_phase_bead_image[:,left:right] = n_mix_pi_phase_bead_image
    zero_phase_STE_image[:,left:right] = n_mix_zero_phase_STE_image
    pi_phase_STE_image[:,left:right] = n_mix_pi_phase_STE_image
    # n = 1.61 on right side of wide image
    left = width*2 + between_pics*2
    right = big_width
    zero_phase_bead_image[:,left:right] = n_1_61_zero_phase_bead_image
    pi_phase_bead_image[:,left:right] = n_1_61_pi_phase_bead_image
    zero_phase_STE_image[:,left:right] = n_1_61_zero_phase_STE_image
    pi_phase_STE_image[:,left:right] = n_1_61_pi_phase_STE_image


    # generate and save plot

    fig, (ax0, ax1) = plt.subplots(nrows=2,ncols=1,figsize=(20,7))

    cax0 = ax0.imshow(pi_phase_bead_image, cmap=plt.cm.gray,
                      interpolation='nearest', vmax=2500, vmin=-4200)
    ax0.axis('off')
    divider = make_axes_locatable(ax0)
    cax = divider.append_axes("right",size="1%",pad=0.25)
    plt.colorbar(cax0, cax = cax)
    ax0.set_title('Phase contrast image of scattered light from bead',fontsize=30)
    ax0.text(
        12, 14, r'$\Delta n\approx +0.05$',
        fontsize=38, color='black', fontweight='bold')
    ax0.text(
        53, 14, r'$\Delta n\approx 0$',
        fontsize=38, color='black', fontweight='bold')
    ax0.text(
        79, 14, r'$\Delta n\approx -0.01$',
        fontsize=38, color='black', fontweight='bold')


    cax1 = ax1.imshow(pi_phase_STE_image, cmap=plt.cm.gray,
                      interpolation='nearest')
    
    divider = make_axes_locatable(ax1)
    cax = divider.append_axes("right",size="1%",pad=0.25)
    plt.colorbar(cax1, cax = cax)
    ax1.text(
        12, 14 ,r'$\Delta n\approx +0.05$',
        fontsize=38, color='black', fontweight='bold')
    ax1.text(
        53, 14, r'$\Delta n\approx 0$',
        fontsize=38, color='black', fontweight='bold')
    ax1.text(
        79, 14, r'$\Delta n\approx -0.01$',
        fontsize=38, color='black', fontweight='bold')
    ax1.set_title('Change due to excitation',fontsize=30,)
    ax1.axis('off')
    plt.savefig('./../images/figure_3/STE_crimson_bead_pi_phase.svg',
                bbox_inches='tight', pad_inches=0.1)
    plt.show()

    fig, (ax0, ax1) = plt.subplots(nrows=2,ncols=1,figsize=(20,7))

    cax0 = ax0.imshow(zero_phase_bead_image, cmap=plt.cm.gray,
                      interpolation='nearest', vmin=-2300)
    ax0.axis('off')
    divider = make_axes_locatable(ax0)
    cax = divider.append_axes("right",size="1%",pad=0.25)
    plt.colorbar(cax0, cax = cax)
    ax0.set_title('Phase contrast image of scattered light from bead',fontsize=30)
    ax0.text(
        12, 14, r'$\Delta n\approx +0.05$',
        fontsize=38, color='white', fontweight='bold')
    ax0.text(
        53, 14, r'$\Delta n\approx 0$',
        fontsize=38, color='white', fontweight='bold')
    ax0.text(
        79, 14, r'$\Delta n\approx -0.01$',
        fontsize=38, color='white', fontweight='bold')


    cax1 = ax1.imshow(zero_phase_STE_image, cmap=plt.cm.gray,
                      interpolation='nearest')
    
    divider = make_axes_locatable(ax1)
    cax = divider.append_axes("right",size="1%",pad=0.25)
    plt.colorbar(cax1, cax = cax)
    ax1.text(
        12, 14, r'$\Delta n\approx +0.05$',
        fontsize=38, color='white', fontweight='bold')
    ax1.text(
        53, 14, r'$\Delta n\approx 0$',
        fontsize=38, color='white', fontweight='bold')
    ax1.text(
        79, 14, r'$\Delta n\approx -0.01$',
        fontsize=38, color='white', fontweight='bold')
    ax1.set_title('Change due to excitation',fontsize=30)
    ax1.axis('off')
    plt.savefig('./../images/figure_3/STE_crimson_bead_zero_phase.svg',
                bbox_inches='tight', pad_inches=0.1)
    plt.show()

    return None
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_A5'):
        os.mkdir('./../images/figure_A5')

    z_voltage = '_46000mV'

    # 1. brightness-correct each delay scan by using the entire image
    # brightness at max/min delay as an estimate of the red laser
    # brightness over that particular delay scan
    # 2. repetition average the images for each delay
    # 3. register the repetition averaged "green off" image to the
    # corresponding "green on" image

    num_delays = 5
    num_reps = 200
    width = 380
    height = 128
    less_rows = 3

    filename = ('./../../stimulated_emission_imaging-data' +
                '/2016_11_02_STE_z_stack_darkfield' +
                '/STE_darkfield_113_green_1500mW_red_300mW' + z_voltage +
                '_many_delays.tif')
    filename_ctrl = ('./../../stimulated_emission_imaging-data' +
                     '/2016_11_02_STE_z_stack_darkfield' +
                     '/STE_darkfield_113_green_0mW_red_300mW' + z_voltage +
                     '_many_delays.tif')
    data = np_tif.tif_to_array(filename).astype(np.float64)
    data_ctrl = np_tif.tif_to_array(filename_ctrl).astype(np.float64)
    # reshape data arrays: 0th axis is rep number, 1st is delay number
    data = data.reshape(num_reps, num_delays, height, width)
    data_ctrl = data_ctrl.reshape(num_reps, num_delays, height, width)
    # crop to remove overexposed rows
    data = data[:, :, less_rows:height - less_rows, :]
    data_ctrl = data_ctrl[:, :, less_rows:height - less_rows, :]
    # get slice for reference brightness
    ref_slice_1 = data[:, 0, :, :].mean(axis=0)
    ref_slice_2 = data[:, 4, :, :].mean(axis=0)
    ref_slice = (ref_slice_1 + ref_slice_2) / 2
    # red beam brightness correction
    red_avg_brightness = ref_slice.mean(axis=1).mean(axis=0)
    # for green on data
    local_laser_brightness = ((data[:, 0, :, :] + data[:, 4, :, :]) /
                              2).mean(axis=2).mean(axis=1)
    local_calibration_factor = red_avg_brightness / local_laser_brightness
    local_calibration_factor = local_calibration_factor.reshape(
        num_reps, 1, 1, 1)
    data = data * local_calibration_factor
    # for green off data
    local_laser_brightness = ((data_ctrl[:, 0, :, :] + data_ctrl[:, 4, :, :]) /
                              2).mean(axis=2).mean(axis=1)
    local_calibration_factor = red_avg_brightness / local_laser_brightness
    local_calibration_factor = local_calibration_factor.reshape(
        num_reps, 1, 1, 1)
    data_ctrl = data_ctrl * local_calibration_factor

    # repetition average both image sets
    data_rep_avg = data.mean(axis=0)
    data_ctrl_rep_avg = data_ctrl.mean(axis=0)

    # registration shift control data to match "green on" data
    align_to_this_slice = data_rep_avg[0, :, :]
    print("Computing registration shifts (no green)...")
    shifts = stack_registration(data_ctrl_rep_avg,
                                align_to_this_slice=align_to_this_slice,
                                refinement='integer',
                                register_in_place=True,
                                background_subtraction='edge_mean')
    print(shifts)
    print("... done computing shifts.")

    # replace arrays with repetition averaged images
    data = data_rep_avg
    data_ctrl = data_ctrl_rep_avg

    # from the image where red/green are simultaneous, subtract the
    # average of images taken when the delay magnitude is greatest
    diff_imgs = data - data[0, :, :].reshape(1, data.shape[1], data.shape[2])
    diff_imgs_ctrl = data_ctrl - data_ctrl[0, :, :].reshape(
        1, data_ctrl.shape[1], data_ctrl.shape[2])
    diff_imgs = diff_imgs - diff_imgs_ctrl  #subtract crosstalk
    darkfield_img = data[0, :, :]

    # plot darkfield and stim emission signal
    top = 1
    bot = 116
    left = 104 - 20
    right = 219 + 20
    diff_imgs_cropped = diff_imgs[:, top:bot, left:right]
    darkfield_img_cropped = darkfield_img[top:bot, left:right]

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    bucket_width = 8  # bucket width in pixels
    diff_imgs_cropped = bucket(
        diff_imgs_cropped, (1, bucket_width, bucket_width)) / bucket_width**2
    darkfield_img_cropped = bucket(
        darkfield_img_cropped, (bucket_width, bucket_width)) / bucket_width**2

    # get max and min pixel values for plotting
    max_diff = np.amax(diff_imgs_cropped)
    min_diff = np.amin(diff_imgs_cropped)
    # make scale bars
    diff_imgs_cropped[:, -2:-1, 1:5] = min_diff
    darkfield_img_cropped[-2:-1, 1:5] = np.amax(darkfield_img_cropped)

    # make delay scan single image
    spacing = 4  #pixels between images
    full_scan_img = np.zeros((diff_imgs_cropped.shape[1],
                              diff_imgs_cropped.shape[2] * 5 + spacing * 4))
    full_scan_img = full_scan_img + max_diff  # make sure spacing is white

    for i in range(diff_imgs_cropped.shape[0]):
        print(i)
        x_position = (diff_imgs_cropped.shape[2] + spacing) * i
        full_scan_img[:,
                      x_position:x_position + diff_imgs_cropped.shape[2]] = (
                          diff_imgs_cropped[i, :, :])
    fig = plt.figure()
    plt.imshow(full_scan_img,
               cmap=plt.cm.gray,
               interpolation='nearest',
               vmax=max_diff,
               vmin=min_diff)
    plt.axis('off')
    plt.savefig('./../images/figure_A5/darkfield_STE_delay_scan.svg',
                bbox_inches='tight')
    plt.show()
    fig = plt.figure()
    plt.imshow(darkfield_img_cropped,
               cmap=plt.cm.gray,
               interpolation='nearest')
    plt.axis('off')
    plt.savefig('./../images/figure_A5/darkfield_image.svg',
                bbox_inches='tight')
    plt.show()

    return None
def main():
    # each raw data stack has a full red and green power scan with red
    # varying slowly and green varying more quickly and green/red pulse
    # delay varying the quickest (5 delays, middle delay is 0 delay)

    num_reps = 200  # number power scans taken
    num_red_powers = 7
    num_green_powers = 13
    num_delays = 5
    image_h = 128
    image_w = 380
    less_rows = 3  # top/bottom 3 rows may contain leakage from outside pixels
    top = less_rows
    bot = image_h - less_rows

    # assume no sample motion during a single power scan
    # allocate hyperstack to carry power/delay-averaged images for registration
    data_rep = np.zeros((
        num_reps,
        image_h - less_rows * 2,
        image_w,
    ),
                        dtype=np.float64)
    data_rep_bg = np.zeros((
        num_reps,
        image_h - less_rows * 2,
        image_w,
    ),
                           dtype=np.float64)

    # allocate array to carry a number corresponding to the average red
    # beam brightness for each red power
    red_avg_brightness = np.zeros((num_red_powers))

    # populate hyperstack from data
    for rep_num in range(num_reps):
        filename = 'STE_darkfield_power_delay_scan_' + str(rep_num) + '.tif'
        print("Loading", filename)
        imported_power_scan = np_tif.tif_to_array(filename).astype(
            np.float64)[:, top:bot, :]
        red_avg_brightness += get_bg_level(
            imported_power_scan.reshape(
                num_red_powers, num_green_powers, num_delays,
                image_h - less_rows * 2,
                image_w).mean(axis=1).mean(axis=1)) / (2 * num_reps)
        data_rep[rep_num, :, :] = imported_power_scan.mean(axis=0)
        filename_bg = ('STE_darkfield_power_delay_scan_' + str(rep_num) +
                       '_green_blocked.tif')
        print("Loading", filename_bg)
        imported_power_scan_bg = np_tif.tif_to_array(filename_bg).astype(
            np.float64)[:, top:bot, :]
        red_avg_brightness += get_bg_level(
            imported_power_scan_bg.reshape(
                num_red_powers, num_green_powers, num_delays,
                image_h - less_rows * 2,
                image_w).mean(axis=1).mean(axis=1)) / (2 * num_reps)
        data_rep_bg[rep_num, :, :] = imported_power_scan_bg.mean(axis=0)

    # reshape red_avg_brightness to add a dimension for multiplication
    # with a brightness array with dimensions num_red_powers X num_green
    # powers X num_delays
    red_avg_brightness = red_avg_brightness.reshape(num_red_powers, 1, 1)

    # pick image/slice for all stacks to align to
    representative_rep_num = 0
    align_slice = data_rep[representative_rep_num, :, :]

    # save pre-registered average data (all powers for each rep)
    np_tif.array_to_tif(data_rep, 'dataset_not_registered_power_avg.tif')
    np_tif.array_to_tif(data_rep_bg,
                        'dataset_green_blocked_not_registered_power_avg.tif')

    # compute registration shifts
    print("Computing registration shifts...")
    shifts = stack_registration(data_rep,
                                align_to_this_slice=align_slice,
                                refinement='integer',
                                register_in_place=True,
                                background_subtraction='edge_mean')
    print("Computing registration shifts (no green) ...")
    shifts_bg = stack_registration(data_rep_bg,
                                   align_to_this_slice=align_slice,
                                   refinement='integer',
                                   register_in_place=True,
                                   background_subtraction='edge_mean')

    # save registered average data (all powers for each rep) and shifts
    np_tif.array_to_tif(data_rep, 'dataset_registered_power_avg.tif')
    np_tif.array_to_tif(data_rep_bg,
                        'dataset_green_blocked_registered_power_avg.tif')
    np_tif.array_to_tif(shifts, 'shifts.tif')
    np_tif.array_to_tif(shifts_bg, 'shifts_bg.tif')

    # now apply shifts to raw data and compute space-averaged signal
    # and representative images

    # define box around main lobe for computing space-averaged signal
    rect_top = 44
    rect_bot = 102
    rect_left = 172
    rect_right = 228

    # initialize hyperstacks for signal (with/without green light)
    print('Applying shifts to raw data...')
    signal = np.zeros((
        num_reps,
        num_red_powers,
        num_green_powers,
        num_delays,
    ),
                      dtype=np.float64)
    signal_bg = np.zeros((
        num_reps,
        num_red_powers,
        num_green_powers,
        num_delays,
    ),
                         dtype=np.float64)
    data_hyper_shape = (num_red_powers, num_green_powers, num_delays, image_h,
                        image_w)

    # get representative image cropping coordinates
    rep_top = 22
    rep_bot = 122
    rep_left = 136
    rep_right = 262

    # initialize representative images (with/without green light)
    darkfield_image = np.zeros(
        (  #num_reps,
            rep_bot - rep_top,
            rep_right - rep_left,
        ),
        dtype=np.float64)
    STE_image = np.zeros(
        (  #num_reps,
            rep_bot - rep_top,
            rep_right - rep_left,
        ),
        dtype=np.float64)
    darkfield_image_bg = np.zeros(
        (  #num_reps,
            rep_bot - rep_top,
            rep_right - rep_left,
        ),
        dtype=np.float64)
    STE_image_bg = np.zeros(
        (  #num_reps,
            rep_bot - rep_top,
            rep_right - rep_left,
        ),
        dtype=np.float64)

    # finally apply shifts and compute output data
    for rep_num in range(num_reps):
        filename = 'STE_darkfield_power_delay_scan_' + str(rep_num) + '.tif'
        data = np_tif.tif_to_array(filename).astype(np.float64)[:, top:bot, :]
        filename_bg = ('STE_darkfield_power_delay_scan_' + str(rep_num) +
                       '_green_blocked.tif')
        data_bg = np_tif.tif_to_array(filename_bg).astype(
            np.float64)[:, top:bot, :]
        print(filename)
        print(filename_bg)
        # apply registration shifts
        apply_registration_shifts(data,
                                  registration_shifts=[shifts[rep_num]] *
                                  data.shape[0],
                                  registration_type='nearest_integer',
                                  edges='sloppy')
        apply_registration_shifts(data_bg,
                                  registration_shifts=[shifts_bg[rep_num]] *
                                  data_bg.shape[0],
                                  registration_type='nearest_integer',
                                  edges='sloppy')
        # re-scale images to compensate for red beam brightness fluctuations
        # for regular data
        local_laser_brightness = get_bg_level(
            data.reshape(num_red_powers, num_green_powers, num_delays,
                         data.shape[-2], data.shape[-1]))
        local_calibration_factor = red_avg_brightness / local_laser_brightness
        local_calibration_factor = local_calibration_factor.reshape(
            num_red_powers * num_green_powers * num_delays, 1, 1)
        data = data * local_calibration_factor
        # for green blocked data
        local_laser_brightness_bg = get_bg_level(
            data_bg.reshape(num_red_powers, num_green_powers, num_delays,
                            data.shape[-2], data.shape[-1]))
        local_calibration_factor_bg = (red_avg_brightness /
                                       local_laser_brightness_bg)
        local_calibration_factor_bg = local_calibration_factor_bg.reshape(
            num_red_powers * num_green_powers * num_delays, 1, 1)
        data_bg = data_bg * local_calibration_factor_bg
        # draw rectangle around bright lobe and spatially average signal
        data_space_avg = data[:, rect_top:rect_bot,
                              rect_left:rect_right].mean(axis=2).mean(axis=1)
        data_bg_space_avg = data_bg[:, rect_top:rect_bot,
                                    rect_left:rect_right].mean(axis=2).mean(
                                        axis=1)
        # reshape 1D signal and place in output file
        signal[rep_num, :, :, :] = data_space_avg.reshape(
            num_red_powers, num_green_powers, num_delays)
        signal_bg[rep_num, :, :, :] = data_bg_space_avg.reshape(
            num_red_powers, num_green_powers, num_delays)
        # capture average images for max red/green power
        image_green_power = num_green_powers - 1
        image_red_power = num_red_powers - 1
        STE_image += data[-3,  # Zero delay, max red power, max green power
                          rep_top:rep_bot, rep_left:rep_right] / num_reps
        darkfield_image += data[
            -1,  # max red-green delay (2.5 us), max red power, max green power
            rep_top:rep_bot, rep_left:
            rep_right] / num_reps / 2  # one of two maximum absolute red/green delay values
        darkfield_image += data[
            -5,  # min red-green delay (-2.5 us), max red power, max green power
            rep_top:rep_bot, rep_left:
            rep_right] / num_reps / 2  # one of two maximum absolute red/green delay values
        STE_image_bg += data_bg[
            -3,  # Zero delay, max red power, max green power
            rep_top:rep_bot, rep_left:rep_right] / num_reps
        darkfield_image_bg += data_bg[
            -1,  # max red-green delay (2.5 us), max red power, max green power
            rep_top:rep_bot, rep_left:
            rep_right] / num_reps / 2  # one of two maximum absolute red/green delay values
        darkfield_image_bg += data_bg[
            -5,  # min red-green delay (-2.5 us), max red power, max green power
            rep_top:rep_bot, rep_left:
            rep_right] / num_reps / 2  # one of two maximum absolute red/green delay values

    print('Done applying shifts')

    signal_tif_shape = (signal.shape[0] * signal.shape[1], signal.shape[2],
                        signal.shape[3])

    print("Saving...")
    np_tif.array_to_tif(signal.reshape(signal_tif_shape),
                        'signal_all_scaled.tif')
    np_tif.array_to_tif(signal_bg.reshape(signal_tif_shape),
                        'signal_green_blocked_all_scaled.tif')
    np_tif.array_to_tif(darkfield_image, 'darkfield_image_avg.tif')
    np_tif.array_to_tif(darkfield_image_bg, 'darkfield_image_bg_avg.tif')
    np_tif.array_to_tif(STE_image, 'STE_image_avg.tif')
    np_tif.array_to_tif(STE_image_bg, 'STE_image_bg_avg.tif')
    print("... done.")

    return None
예제 #11
0
## Set input file name and acquisition parameters for processing
input_filename = ('transmitted_z_step_10_uF2.tif')
cropped_filename = os.path.splitext(input_filename)[0] + '_cropped.tif'
input_filename = os.path.join(input_directory, input_filename)
cropped_filename = os.path.join(temp_directory, cropped_filename)
num_tps = 1000  # Number of time points in series
left_crop = 400
right_crop = 750
top_crop = 0
bottom_crop = 0

## If cropped file exists then load
if os.path.exists(cropped_filename):
    print('Found cropped tif, loading...', end='', sep='')
    data = np_tif.tif_to_array(cropped_filename)
    print('done')
    print('tif shape (t, y, x) =', data.shape)

## If no file found then load original and crop
else:
    print('Loading original file...', end='', sep='')
    data = np_tif.tif_to_array(input_filename)
    print('done')
    data = data.reshape((num_tps, ) + data.shape[-2:])
    print('tif shape (t, y, x) =', data.shape)
    print('Cropping...', end='', sep='')
    if left_crop or right_crop > 0:
        data = data[:, :, left_crop:-right_crop]
    if top_crop or bottom_crop > 0:
        data = data[:, top_crop:-bottom_crop, :]
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_A8'):
        os.mkdir('./../images/figure_A8')
    root_string = ('./../../stimulated_emission_imaging-data' +
                   '/2016_10_24_pulse_length')
    pulse_text = [
        '_4x_long_pulse/',
        '_3x_long_pulse/',
        '_2x_long_pulse/',
        '/',
    ]

    pulsewidths = np.array([4, 3, 2, 1])

    # location of center lobe
    bright_spot_x = np.array([160, 160, 162, 182])
    bright_spot_y = np.array([86, 86, 83, 78]) - 3
    # half of roughtly half width of center lobe
    qtr_width = 10

    # cropping area defined
    top_left_x = np.array([110, 110, 111, 130])
    top_left_y = np.array([39, 39, 37, 33]) - 3
    crop_width = 98
    crop_height = 85

    # where on the plot should the cropped images be
    plot_pos_y = [0.655, 0.515, 0.39, 0.11]
    plot_pos_x = [0.16, 0.22, 0.34, 0.67]

    STE_signal = np.zeros(4)
    STE_signal_relative = np.zeros(4)
    STE_image_cropped = np.zeros((4, 10, 12))
    num_reps = 200
    num_delays = 5

    for i in range(4):
        pulsewidth = pulsewidths[i]
        folder_string = root_string + '/nanodiamond_7' + pulse_text[i]
        filename = (
            folder_string +
            'STE_darkfield_117_5_green_1500mW_red_300mW_many_delays.tif')
        assert os.path.exists(filename)
        data = np_tif.tif_to_array(filename).astype(np.float64)
        filename = (folder_string + 'STE_darkfield_117_5_green_0mW_red_300mW' +
                    '_many_delays.tif')
        assert os.path.exists(filename)
        data_bg = np_tif.tif_to_array(filename).astype(np.float64)
        # reshape to hyperstack
        data = data.reshape(num_reps, num_delays, data.shape[1], data.shape[2])
        data_bg = data_bg.reshape(num_reps, num_delays, data_bg.shape[1],
                                  data_bg.shape[2])
        # get rid of overexposed rows at top and bottom of images
        less_rows = 3
        data = data[:, :, less_rows:data.shape[2] - less_rows, :]
        data_bg = data_bg[:, :, less_rows:data_bg.shape[2] - less_rows, :]
        # repetition average
        data = data.mean(axis=0)
        data_bg = data_bg.mean(axis=0)
        # subtract crosstalk
        data = data - data_bg
        # subtract avg of green off images from green on images
        data_simult = data[2, :, :]
        data_non_simult = 0.5 * (data[0, :, :] + data[4, :, :])
        STE_image = data_simult - data_non_simult
        # capture stim emission signal
        my_col = bright_spot_x[i]
        my_row = bright_spot_y[i]
        main_lobe = STE_image[my_row - qtr_width:my_row + qtr_width,
                              my_col - qtr_width:my_col + qtr_width]
        STE_signal[i] = np.mean(main_lobe)
        STE_signal_relative[i] = STE_signal[i]
        # crop stim emission image
        STE_image_cropped_single = STE_image[top_left_y[i]:top_left_y[i] +
                                             crop_height,
                                             top_left_x[i]:top_left_x[i] +
                                             crop_width, ]
        # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
        # This is not great for viewing, because fluctuations can swamp the
        # signal. This step bins the pixels into a more typical size.
        bucket_width = 8  # bucket width in pixels
        STE_image_cropped_single = bucket(
            STE_image_cropped_single,
            (bucket_width, bucket_width)) / bucket_width**2
        # load into final image array
        STE_image_cropped[i, :, :] = STE_image_cropped_single

    # get max and min vals
    STE_max = np.amax(STE_image_cropped)
    STE_min = np.amin(STE_image_cropped)
    STE_image_cropped[:, -2:-1, 1:5] = STE_min  # scale bar

    my_intensity = 1 / pulsewidths

    fig, ax1 = plt.subplots()
    ax1.plot(my_intensity,
             STE_signal_relative,
             'o',
             color='black',
             markersize=10)
    plt.ylim(ymin=-140, ymax=0)
    ax1.set_ylabel('Average signal brightness (pixel counts)')
    ax1.tick_params('y', colors='k')
    plt.xlabel('Normalized laser intensity (constant energy)')
    plt.grid()
    for i in range(4):
        a = plt.axes([plot_pos_x[i], plot_pos_y[i], .12, .12])
        plt.imshow(STE_image_cropped[i, :, :],
                   cmap=plt.cm.gray,
                   interpolation='nearest',
                   vmax=STE_max,
                   vmin=STE_min)
        plt.xticks([])
        plt.yticks([])

    # plot energy per exposure
    green_uJ = np.array([54, 54, 54, 54])
    red_uJ = np.array([10, 10, 10, 10])
    ax2 = ax1.twinx()
    ax2.plot(my_intensity, green_uJ, '--b', linewidth=2)
    ax2.plot(my_intensity, red_uJ, '--b', linewidth=2)
    ax2.set_ylabel('Optical energy per exposure (µJ)', color='blue')
    ax2.tick_params('y', colors='b')
    ax2.set_ylim(ymin=-1, ymax=61.5)
    ax1.set_xlim(xmin=0, xmax=1.125)

    # annotate with red/green pulses
    im = plt.imread('green_shortpulse.png')
    a = plt.axes([0.773, 0.812, .08, .08], frameon=False)
    plt.imshow(im)
    plt.xticks([])
    plt.yticks([])
    im = plt.imread('green_longpulse.png')
    a = plt.axes([0.24, 0.772, .1, .1], frameon=False)
    plt.imshow(im)
    plt.xticks([])
    plt.yticks([])
    im = plt.imread('red_shortpulse.png')
    a = plt.axes([0.773, 0.25, .08, .08], frameon=False)
    plt.imshow(im)
    plt.xticks([])
    plt.yticks([])
    im = plt.imread('red_longpulse.png')
    a = plt.axes([0.24, 0.21, .1, .1], frameon=False)
    plt.imshow(im)
    plt.xticks([])
    plt.yticks([])

    plt.savefig('./../images/figure_A8/darkfield_nd_pulse_length_scan.svg')
    plt.show()

    return None
예제 #13
0
def main():
    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_A2'):
        os.mkdir('./../images/figure_A2')

    # meltmount mix data
    data = np_tif.tif_to_array('./../../stimulated_emission_imaging-data' +
                               '/2018_02_23_STE_phase_cr_bead_4' +
                               '/dataset_green_1010mW_single_shot.tif').astype(
                                   np.float64)

    # get rid of overexposed rows at top and bottom of images
    less_rows = 3
    data = data[:, 0 + less_rows:data.shape[1] - less_rows, :]
    data = data[:, ::-1, :]  # flip up down

    # reshape to hyperstack
    num_delays = 3
    data = data.reshape((
        data.shape[0] / num_delays,  # phase plate angle number
        num_delays,
        data.shape[1],
        data.shape[2],
    ))

    # Get the average pixel brightness in the background region of the
    # meltmount mix data. We'll use it to account for laser intensity
    # fluctuations
    avg_laser_brightness = get_bg_level(data.mean(axis=(0, 1)))

    # scale all images to have the same background brightness. This
    # amounts to a correction of roughly 1% or less
    local_laser_brightness = get_bg_level(data)
    data = data * (avg_laser_brightness / local_laser_brightness).reshape(
        data.shape[0], data.shape[1], 1, 1)

    # get zero delay images, max delay images and phase contrast images
    zero_delay_images = data[:, 1, :, :]  # zero red/green delay
    control_images = data[:, 0, :, :]  # red before green
    max_delay_images = data[:, 2, :, :]  # red after green
    phase_contrast_images = data[:, 0, :, :]  # red before green

    # from the image where red/green are simultaneous, subtract the
    # average of the max and min delay images
    STE_stack = zero_delay_images - control_images
    control_stack = control_images - control_images
    max_delay_stack = max_delay_images - control_images

    # phase contrast image (no STE) stack: there is a large background
    # variation that has nothing to do with the sample; it's due to
    # multiple reflections in the microscope. Some of it moves when you
    # move the phase plate, and some of it doesn't. This step subtracts
    # off the stationary component. For each image we use in the figure,
    # we subtract the minimum contrast image with the closest phase plate angle.
    # minimum contrast phase plate angle closest to first 7 phase plate angles:
    min_contrast_index_1 = 5
    # minimum contrast phase plate angle closest to last 7 phase plate angles:
    min_contrast_index_2 = 11
    phase_stack = phase_contrast_images
    phase_stack[0:8, ...] = phase_stack[0:8, ...] - phase_contrast_images[
        min_contrast_index_1:min_contrast_index_1 + 1, :, :]
    phase_stack[8:15, ...] = phase_stack[8:15, ...] - phase_contrast_images[
        min_contrast_index_2:min_contrast_index_2 + 1, :, :]

    # Luckily the non-stationary component is comprised of stripes that
    # are completely outside of the microscope's spatial pass-band. The
    # smoothing step below strongly attenuates this striping artifact
    # with almost no effect on spatial frequencies due to the sample.
    sigma = 9  # tune this parameter to reject high spatial frequencies
    STE_stack = gaussian_filter(STE_stack, sigma=(0, sigma, sigma))
    max_delay_stack = gaussian_filter(max_delay_stack, sigma=(0, sigma, sigma))
    control_stack = gaussian_filter(control_stack, sigma=(0, sigma, sigma))
    phase_stack = gaussian_filter(phase_stack, sigma=(0, sigma, sigma))

    # crop images to center bead and fit into figure
    top = 0
    bot = 122
    left = 109
    right = 361
    phase_cropped = phase_stack[:, top:bot, left:right]
    STE_cropped = STE_stack[:, top:bot, left:right]
    max_delay_cropped = max_delay_stack[:, top:bot, left:right]
    control_cropped = control_stack[:, top:bot, left:right]

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    bucket_width = 8  # bucket width in pixels
    phase_cropped = bucket(phase_cropped,
                           (1, bucket_width, bucket_width)) / bucket_width**2
    STE_cropped = bucket(STE_cropped,
                         (1, bucket_width, bucket_width)) / bucket_width**2
    max_delay_cropped = bucket(
        max_delay_cropped, (1, bucket_width, bucket_width)) / bucket_width**2
    control_cropped = bucket(control_cropped,
                             (1, bucket_width, bucket_width)) / bucket_width**2

    # display images from the two phase plate angles that maximize bead
    # contrast (+/- contrast)
    zero_phase_angle = 8
    pi_phase_angle = 0
    zero_phase_bead_image = phase_cropped[zero_phase_angle, :, :]
    pi_phase_bead_image = phase_cropped[pi_phase_angle, :, :]
    zero_phase_STE_image = STE_cropped[zero_phase_angle, :, :]
    pi_phase_STE_image = STE_cropped[pi_phase_angle, :, :]
    zero_phase_max_delay_image = max_delay_cropped[zero_phase_angle, :, :]
    pi_phase_max_delay_image = max_delay_cropped[pi_phase_angle, :, :]
    zero_phase_control_image = control_cropped[zero_phase_angle, :, :]
    pi_phase_control_image = control_cropped[pi_phase_angle, :, :]

    # start plotting all the images

    # combine images into single array
    diff_imgs = np.concatenate(
        (pi_phase_control_image.reshape(1, pi_phase_control_image.shape[0],
                                        pi_phase_control_image.shape[1]),
         pi_phase_STE_image.reshape(1, pi_phase_STE_image.shape[0],
                                    pi_phase_STE_image.shape[1]),
         pi_phase_max_delay_image.reshape(1, pi_phase_max_delay_image.shape[0],
                                          pi_phase_max_delay_image.shape[1])),
        axis=0)

    # get max and min values to unify the color scale
    diff_max = np.amax(diff_imgs)
    diff_min = -diff_max  #np.amin(diff_imgs)
    phase_max = np.amax(pi_phase_bead_image) * 3
    phase_min = np.amin(pi_phase_bead_image) * 3
    print(phase_max, phase_min)
    # scale bars
    diff_imgs[:, -2:-1, 1:6] = diff_min
    pi_phase_bead_image[-2:-1, 1:6] = phase_min

    # make delay scan single image
    spacing = 4  # pixels between images
    full_scan_img = np.zeros(
        (diff_imgs.shape[1], diff_imgs.shape[2] * 3 + spacing * 2))
    full_scan_img = full_scan_img + diff_max  # make sure spacing is white
    for i in range(diff_imgs.shape[0]):
        print(i)
        x_position = (diff_imgs.shape[2] + spacing) * i
        full_scan_img[:, x_position:x_position +
                      diff_imgs.shape[2]] = (diff_imgs[i, :, :])
    fig = plt.figure()
    plt.imshow(full_scan_img,
               cmap=plt.cm.gray,
               interpolation='nearest',
               vmax=diff_max,
               vmin=diff_min)
    plt.axis('off')
    plt.savefig('./../images/figure_A2/phase_STE_delay_scan.svg',
                bbox_inches='tight')
    plt.show()
    fig = plt.figure()
    plt.imshow(pi_phase_bead_image, cmap=plt.cm.gray, interpolation='nearest')
    plt.axis('off')
    plt.savefig('./../images/figure_A2/phase_image.svg', bbox_inches='tight')
    plt.show()

    return None
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_4'):
        os.mkdir('./../images/figure_4')

    less_rows = 3 # top and bottom image rows tend to saturate
    num_reps = 3000 #original number of reps not counting first blank delay scan
    reps_avgd = 1
    reps_per_set = int(num_reps/reps_avgd)
    num_delays = 3
    dark_counts = 100
    height = 128
    width = 380
    lbhw = 28 # half width of box around main image lobe
    sets = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
    image_center = np.array([
        [75, 193],
        [79, 193],
        [81, 193],
        [82, 193],
        [84, 193],
        [84, 193],
        [89, 193]])
    # change image center coordinate height due to cropping
    image_center = image_center - np.array([less_rows, 0])
    assert len(sets) == len(image_center)

    # get bg level for brightness calibration across all data sets
    ref_data_set = 'a'
    ref_filename = (
        './../../stimulated_emission_imaging-data' +
        '/2018_05_08_crimson_STE_phase_bleaching_bead_0' +
        '/STE_phase_angle_1_green_1010mW_red_230mW_' + ref_data_set + '.tif')
    set_data = np_tif.tif_to_array(
        ref_filename).astype(np.float64) - dark_counts
    set_data = set_data.reshape(reps_per_set + 1, num_delays, height, width)
    # get rid of overexposed rows at top and bottom of images
    # as well as first delay scan which has all lasers off
    set_data = set_data[1::, :, 0 + less_rows:height - less_rows, :]
    # use outer regions of image to estimate average laser brightness.
    # Use this brightness to re-scale all other images prior to other
    # operations, such as subtracting zero delay and max delay images
    avg_laser_brightness = get_bg_level(set_data.mean(axis=(0, 1)))

    # initialize array to hold all images from multiple data sets (a, b, c...)
    all_STE_images = np.zeros((
        reps_per_set * len(sets), height-less_rows*2, width))
    STE_signal = np.zeros((reps_per_set * len(sets))) # STE signal from all sets
    
    for my_index, my_set in enumerate(sets):
        filename = (
            './../../stimulated_emission_imaging-data' +
            '/2018_05_08_crimson_STE_phase_bleaching_bead_0' +
            '/STE_phase_angle_1_green_1010mW_red_230mW_' + my_set + '.tif')
        set_data = np_tif.tif_to_array(
            filename).astype(np.float64) - dark_counts
        assert set_data.shape == ((reps_per_set + 1) * num_delays, height, width)
        set_data = set_data.reshape(reps_per_set + 1, num_delays, height, width)
        # crop overexposed rows from top and bottom of image
        # as well as first delay scan which has all lasers off
        set_data = set_data[1::, :, 0 + less_rows:height - less_rows, :]

        # scale all images to have the same background brightness. This
        # amounts to a correction of roughly 1% or less
        local_laser_brightness = get_bg_level(set_data)
        set_data = set_data * (
            avg_laser_brightness / local_laser_brightness).reshape(
                set_data.shape[0], set_data.shape[1], 1, 1)

        # get zero delay and max delay images
        # zero red/green delay (1st index)
        zero_delay_images = set_data[:, 1, :, :]
        # max +/- red/green delay (avg of 0th and 2nd indices)
        max_delay_images = set_data[:, 0:3:2, :, :].mean(axis=1)

        # stim emission image is the image with green/red simultaneous minus
        # image with/red green not simultaneous
        STE_image_set = zero_delay_images - max_delay_images
        # local range in global set
        begin = my_index * reps_per_set
        end = begin + reps_per_set
        # populate global data set with images from all sets (a, b, c...)
        all_STE_images[begin:end, :, :] = STE_image_set

        # average points around main STE image lobe and add to STE_signal list
        ste_y, ste_x = image_center[my_index]
        STE_signal[begin:end] = STE_image_set[
            :,
            ste_y - lbhw:ste_y + lbhw,
            ste_x - lbhw:ste_x + lbhw
            ].mean(axis=2).mean(axis=1)

    # average consecutive STE images for better SNR (mandatory)
    bucket_width = 50
    all_STE_images = bucket(
        all_STE_images, (bucket_width, 1, 1)) / bucket_width

    # average consecutive STE signal levels for better SNR (optional)
    signal_bucket_width = 50
    orig_STE_signal = STE_signal
    STE_signal = np.array([STE_signal])
    STE_signal = bucket(
        STE_signal, (1, signal_bucket_width)) / signal_bucket_width
    STE_signal = STE_signal[0, :]

    # choose images to display and laterally smooth them
    sigma = 9 # tune this parameter to reject high spatial frequencies
    STE_display_imgs = np.array([
        all_STE_images[0, :, :],
        all_STE_images[int(all_STE_images.shape[0] / 2), :, :],
        all_STE_images[-1, :, :]])    
    STE_display_imgs = gaussian_filter(
        STE_display_imgs, sigma=(0, sigma, sigma))

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    bucket_width = 8 # bucket width in pixels
    STE_display_imgs = bucket(
        STE_display_imgs, (1, bucket_width, bucket_width)) / bucket_width**2

    # crop images left and right sides
    less_cols = 5
    STE_display_imgs = STE_display_imgs[:, :, less_cols:-less_cols]

    # get max and minimum values to display images with unified color scale
    max_pixel_value = np.max(STE_display_imgs)
    min_pixel_value = np.min(STE_display_imgs)
    print(max_pixel_value, min_pixel_value)

    STE_display_imgs[:, -2, 1:6] = max_pixel_value # scale bar

    # number of accumulated excitation pulses for x axis of bleaching plot
    pulses_per_exposure = 8
    exposures_per_delay_scan = 3
    num_delay_scans = len(sets) * num_reps
    orig_pulses_axis = (np.arange(num_delay_scans) *
                        pulses_per_exposure *
                        exposures_per_delay_scan)
    pulses_axis = (
        (np.arange(num_delay_scans / signal_bucket_width) + 0.5) *
        pulses_per_exposure *
        exposures_per_delay_scan *
        signal_bucket_width)

    # get rid of signal from dust particle (shots 17219 - 17226)
    first_bad_shot = 17219
    num_bad_shots = 8
    orig_mask = np.arange(num_bad_shots) + first_bad_shot
    orig_STE_signal = np.delete(orig_STE_signal, orig_mask)
    orig_pulses_axis = np.delete(orig_pulses_axis, orig_mask)
    bucket_mask = (first_bad_shot + num_bad_shots // 2) // signal_bucket_width
    STE_signal = np.delete(STE_signal, bucket_mask)
    pulses_axis = np.delete(pulses_axis, bucket_mask)

    # finally plot
    plt.figure(figsize=(13,5))
    plt.plot(
        orig_pulses_axis, orig_STE_signal,
        'o', markersize = 2.5,
        markerfacecolor='none', markeredgecolor='blue')
    plt.plot(pulses_axis, STE_signal, color='red')

    # lines from images to data points
    for x in np.arange(50189, 162114, 3000):
        plt.plot(
            [pulses_axis[0], x],
            [STE_signal[0], 76],
            'k', lw=0.1,
            )
    for x in np.arange(213673, 325598, 1000):
        plt.plot(
            [pulses_axis[int(pulses_axis.shape[0] / 2)], x],
            [STE_signal[int(STE_signal.shape[0] / 2)], 76],
            'k', lw=0.1,
            )
    for x in np.arange(377157, 489081, 1000):
        plt.plot(
            [pulses_axis[-1], x],
            [STE_signal[-1], 76],
            'k', lw=0.1,
            )

    plt.axis([0-2000, np.max(pulses_axis)+2000, -25, 110])
    print(np.max(pulses_axis))
    plt.grid()
    plt.ylabel('Average pixel brightness (sCMOS counts)', fontsize=14)
    plt.xlabel('Number of excitation pulses delivered to sample', fontsize=18)
    a = plt.axes([.2, .7, .18, .18])
    plt.imshow(
        STE_display_imgs[0, :, :],
        cmap=plt.cm.gray,
        interpolation='nearest',
        vmax = max_pixel_value,
        vmin = min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    a = plt.axes([.45, .7, .18, .18])
    plt.imshow(
        STE_display_imgs[1, :, :],
        cmap=plt.cm.gray,
        interpolation='nearest',
        vmax = max_pixel_value,
        vmin = min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    a = plt.axes([.7, .7, .18, .18])
    plt.imshow(
        STE_display_imgs[2, :, :],
        cmap=plt.cm.gray,
        interpolation='nearest',
        vmax = max_pixel_value,
        vmin = min_pixel_value)
    plt.xticks([])
    plt.yticks([])
    plt.savefig('./../images/figure_4/STE_v_fluence_crimson.svg')
    plt.savefig('./../images/figure_4/STE_v_fluence_crimson.png', dpi=300)
    plt.show()

    STE_signal = STE_signal/max(STE_signal)

    half_level = pulses_axis[np.argmin(np.absolute(STE_signal - 0.5))]
    quarter_level = pulses_axis[np.argmin(np.absolute(STE_signal - 0.25))]
    eighth_level = pulses_axis[np.argmin(np.absolute(STE_signal - 0.125))]
    pulses_100h = half_level
    pulses_hq = quarter_level - half_level
    pulses_qe = eighth_level - quarter_level

    print('100% to half level in', pulses_100h, 'pulses')
    print('Half to quarter level in', pulses_hq, 'pulses')
    print('Quarter to eighth level in', pulses_qe, 'pulses')

    plt.figure()
    plt.plot(orig_STE_signal)
    plt.show()

    plt.figure()
    plt.plot(STE_signal)
    plt.show()


    return None
예제 #15
0
if not os.path.isdir(output_directory): os.mkdir(output_directory)

## Set input file name and acquisition parameters for processing
mz_step = 15 # microscope z step
num_f = 5 # number of files
num_slices = 9 # number of z slices in a stack
input_filename = ('SpXcyan-dmi8GFPcube_dmi8z_%1s.0.tif')
input_filename_list = num_f*[None]
hyperstack_filename = os.path.splitext(input_filename)[0] + '_hyperstack.tif'
input_filename = os.path.join(input_directory, input_filename)
hyperstack_filename = os.path.join(temp_directory, hyperstack_filename)

## If hyperstack file exists then skip processing
if os.path.exists(hyperstack_filename):
    print('Found hyperstack tif, loading...', end='')
    data = np_tif.tif_to_array(hyperstack_filename)
    data = data.reshape((num_f, num_slices) + data.shape[-2:])
    print('done')

## If no hyperstack file then process original data
else:
    data_list = num_f*[None]
    for fn in range(num_f):
        input_filename_list[fn] = (input_filename
                                   %((fn-int(0.5*(num_f-1)))*mz_step))
        print('Found input files, loading...', end='')
        data_list[fn] = np_tif.tif_to_array(input_filename_list[fn])
        print('done')
    print('Creating np data array...', end='')
    data = np.asarray(data_list)
    data = data.reshape((num_f, num_slices) + data.shape[-2:])        
def create_figure(comparisons, output_prefix, im_name):
    print("Constructing figure images...")
    figure_dir = os.path.join(output_prefix, "1_Figures")
    if not os.path.isdir(figure_dir): os.makedirs(figure_dir)
    for c in sorted(comparisons.keys()):
        # Calculating the filenames and loading the images isn't too
        # hard but the code looks like a bucket full of crabs:
        num_angles = len(comparisons[c]['line_sted_psfs'])
        point_estimate_filename = os.path.join(
            output_prefix, im_name+'_'+c+'_point_sted_estimate_history.tif')
        point_estimate = np_tif.tif_to_array(
            point_estimate_filename)[-1, :, :].astype(np.float64)
        line_estimate_filename = os.path.join(
            output_prefix,
            im_name+'_'+c+'_line_%i_angles_sted_estimate_history.tif'%num_angles)
        line_estimate = np_tif.tif_to_array(
            line_estimate_filename)[-1, :, :].astype(np.float64)
        true_object_filename = os.path.join(
            output_prefix, im_name + '_' + c + '_point_sted_object.tif')
        true_object = np_tif.tif_to_array(
            true_object_filename)[0, :, :].astype(np.float64)
        # Not that my "publication-quality" matplotlib figure-generating
        # code is anything like readable...
        for i in range(2):
            # Comparison of point-STED and line-STED images that use the
            # same excitation and depletion dose. Side by side, and
            # overlaid with switching.
            fig = plt.figure(figsize=(10, 4), dpi=100)
            plt.suptitle("Image comparison")
            ax = plt.subplot(1, 3, 1)
            plt.imshow(point_estimate, cmap=plt.cm.gray)
            ax.axes.get_xaxis().set_ticks([])
            ax.axes.get_yaxis().set_ticks([])
            if c.startswith('1p0x'):
                ax.set_xlabel("(a) Point confocal")
            else:
                ax.set_xlabel("(a) Point STED, R=%0.1f"%(
                    comparisons[c]['point']['resolution_improvement_descanned']))
            ax = plt.subplot(1, 3, 2)
            plt.imshow(line_estimate, cmap=plt.cm.gray)
            ax.axes.get_xaxis().set_ticks([])
            ax.axes.get_yaxis().set_ticks([])
            if c.startswith('1p0x'):
                ax.set_xlabel("(b) %i-line confocal with equal dose"%num_angles)
            else:
                ax.set_xlabel("(b) %i-line STED with equal dose"%num_angles)
            ax = plt.subplot(1, 3, 3)
            plt.imshow((point_estimate, line_estimate)[i], cmap=plt.cm.gray)
            ax.axes.get_xaxis().set_ticks([])
            ax.axes.get_yaxis().set_ticks([])
            if c.startswith('1p0x'):
                ax.set_xlabel(("(c) Comparison (point confocal)",
                               "(c) Comparison (%i-line confocal)"%num_angles)[i])
            else:
                ax.set_xlabel(("(c) Comparison (point STED)",
                               "(c) Comparison (%i-line STED)"%num_angles)[i])
            plt.subplots_adjust(left=0, bottom=0, right=1, top=1,
                                wspace=0, hspace=0)
            plt.savefig(os.path.join(
                output_prefix, 'figure_2_'+ im_name +'_'+ c + '_%i.svg'%i),
                        bbox_inches='tight')
            plt.close(fig)
        imagemagick_failure = False
        try: # Try to convert the SVG output to animated GIF
            call(["convert",
                  "-loop", "0",
                  "-delay", "100", os.path.join(
                      output_prefix, 'figure_2_'+ im_name +'_'+ c + '_0.svg'),
                  "-delay", "100", os.path.join(
                      output_prefix, 'figure_2_'+ im_name +'_'+ c + '_1.svg'),
                  os.path.join(
                      figure_dir, 'figure_2_' + im_name + '_' + c + '.gif')])
        except: # Don't expect this conversion to work on anyone else's system.
            if not imagemagick_failure:
                print("Gif conversion failed. Is ImageMagick installed?")
            imagemagick_failure = True
        # Error vs. spatial frequency for the point vs. line images
        # plotted above
        fig = plt.figure(figsize=(10, 4), dpi=100)
        def fourier_error(x):
            return (np.abs(np.fft.fftshift(np.fft.fftn(x - true_object))) /
                    np.prod(true_object.shape))
        ax = plt.subplot2grid((12, 12), (0, 0), rowspan=8, colspan=8)
        plt.imshow(np.log(1 + np.hstack((fourier_error(point_estimate),
                                         fourier_error(line_estimate)))),
                   cmap=plt.cm.gray)
        ax.axes.get_xaxis().set_ticks([])
        ax.axes.get_yaxis().set_ticks([])
        n_x, n_y = true_object.shape
        ang = (0) * 2*np.pi/360
        rad = 0.3
        x0, x1 = (0.5 + rad * np.array((-np.cos(ang), np.cos(ang)))) * n_x
        y0, y1 = (0.5 + rad * np.array((-np.sin(ang), np.sin(ang)))) * n_y
        ax.plot([x0, x1], [y0, y1], 'go-')
        ax.plot([x0 + n_x, x1 + n_x], [y0, y1], 'b+-')
        ang = (90/num_angles) * 2*np.pi/360
        x0_2, x1_2 = (0.5 + rad * np.array((-np.cos(ang), np.cos(ang)))) * n_x
        y0_2, y1_2 = (0.5 + rad * np.array((-np.sin(ang), np.sin(ang)))) * n_y
        ax.plot([x0_2 + n_x, x1_2 + n_x], [y0_2, y1_2], 'b+:')
        ax.set_xlabel("(d) Error vs. spatial frequency")
        # Error vs. spatial frequency for lines extracted from the
        # 2D fourier error plots
        ax = plt.subplot2grid((12, 12), (2, 8), rowspan=6, colspan=4)
        samps = 1000
        xy = np.vstack((np.linspace(x0, x1, samps), np.linspace(y0, y1, samps)))
        xy_2 = np.vstack((np.linspace(x0_2, x1_2, samps),
                          np.linspace(y0_2, y1_2, samps)))
        z_point = map_coordinates(np.transpose(fourier_error(point_estimate)), xy)
        z_line = map_coordinates(np.transpose(fourier_error(line_estimate)), xy)
        z_line_2 = map_coordinates(np.transpose(fourier_error(line_estimate)),
                                   xy_2)
        ax.semilogy(gaussian_filter(z_point, sigma=samps/80),
                    'g-', label='Point')
        ax.semilogy(gaussian_filter(z_line, sigma=samps/80),
                    'b-', label='Line, best')
        ax.semilogy(gaussian_filter(z_line_2, sigma=samps/80),
                    'b:', label='Line, worst')
        ax.axes.get_xaxis().set_ticks([])
        ax.set_xlabel("(e) Error vs. spatial frequency")
        plt.ylim(5e0, 9e5)
        ax.yaxis.tick_right()
        ax.grid('on')
        ax.legend(loc=(-0.1, .75), fontsize=8)
        # PSFs for point and line STED
        ax = plt.subplot2grid((12, 12), (10, 0), rowspan=2, colspan=2)
        plt.imshow(comparisons[c]['point_sted_psf'][0][0, :, :],
                   cmap=plt.cm.gray)
        ax.axes.get_xaxis().set_ticks([])
        ax.axes.get_yaxis().set_ticks([])
        for n, im in enumerate(comparisons[c]['line_sted_psfs']):
            ax = plt.subplot2grid((12, 12), (10, 2+n), rowspan=2, colspan=1)
            plt.imshow(im[0, :, :], cmap=plt.cm.gray)
            ax.axes.get_xaxis().set_ticks([])
            ax.axes.get_yaxis().set_ticks([])
        fig.text(x=0.16, y=0.25, s="(f) Point PSF", fontsize=10)
        fig.text(x=0.26, y=0.25, s="(g) Line PSF(s)", fontsize=10)
        # Save the results
        fig.text(x=0.65, y=0.83,
                 s=("Excitation dose: %6s\n"%(
                     "%0.1f"%(comparisons[c]['point']['excitation_dose'])) +
                    "Depletion dose: %7s"%(
                        "%0.1f"%(comparisons[c]['point']['depletion_dose']))),
                 fontsize=12, fontweight='bold')
        plt.subplots_adjust(left=None, bottom=None, right=None, top=None,
                            wspace=0, hspace=0)
        plt.savefig(os.path.join(
            figure_dir, 'figure_2_'+ im_name +'_'+ c + '.svg'),
                    bbox_inches='tight')
        plt.close(fig)
    print("Done constructing figure images.")
예제 #17
0
 def load_data_from_tif(self, filename):
     self.noisy_measurement = np_tif.tif_to_array(filename) + 1e-9
     assert self.noisy_measurement.shape == 3
     assert self.noisy_measurement.min() >= 0
     return None
예제 #18
0
data_rep = np.zeros((
    num_reps,
    num_red_powers,
    num_green_powers,
    num_delays,
    image_h,
    image_w,
),
                    dtype=np.float64)

# populate hyperstack
for rep_num in range(num_reps):
    filename = 'STE_darkfield_power_delay_scan_' + str(rep_num) + '.tif'
    print("Loading", filename)
    data_rep[rep_num,
             ...] = np_tif.tif_to_array(filename).reshape(data_rep.shape[1:])

stack_shape = (
    data_rep.shape[0] * data_rep.shape[1] * data_rep.shape[2] *
    data_rep.shape[3],
    data_rep.shape[4],
    data_rep.shape[5],
)

print("Smoothing in time...")
register_me = gaussian_filter(data_rep.reshape(stack_shape),
                              sigma=(num_green_powers * num_delays / 2, 0, 0))

print("Computing registration shifts...")
shifts = stack_registration(register_me,
                            align_to_this_slice=1508,
예제 #19
0
def main():

    num_angles = 32
    angles = range(num_angles)
    num_reps_original = 1000
    num_delays = 5
    image_h = 128
    image_w = 380
    less_rows = 3

    # define repetitions with dust particles crossing field of view or
    # extreme red power fluctuations
    gr_on_remove = {
        1: [[432, 435], [694, 719], [860, 865]],
        5: [[54, 57], [505, 508], [901, 906]],
        10: [[214, 218], [421, 428]],
        14: [[356, 360], [391, 394], [711, 713], [774, 802]],
        18: [[208, 210], [661, 667], [989, 992]],
        21: [[181, 187]],
        22: [[63, 70], [328, 333], [440, 451], [544, 557], [897, 902],
             [935, 964]],
        24: [[287, 306], [922, 924]],
        25: [[69, 73], [639, 675], [880, 898]],
        26: [[667, 677]],
        27: [[9, 16], [557, 560], [664, 669]],
        29: [[219, 221], [366, 369], [452, 458], [871, 875]],
    }
    gr_off_remove = {
        1: [[706, 708]],
        5: [[219, 222], [505, 510], [553, 557]],
        7: [[158, 165], [213, 220], [310, 316], [493, 497], [950, 961]],
        12: [[173, 176], [432, 434], [914, 922]],
        13: [[494, 527]],
        14: [[983, 987]],
        15: [[451, 458], [698, 715], [873, 883]],
        16: [[171, 178]],
        17: [[100, 104], [323, 327]],
        21: [[51, 56], [293, 295], [385, 390], [858, 864]],
        22: [[106, 109], [279, 285], [565, 580], [829, 834], [904, 924]],
    }

    green_powers = [
        '_0mW',
        '_1500mW',
    ]

    remove_dict_list = [gr_off_remove,
                        gr_on_remove]  # same order as green_powers

    red_powers = [
        '_300mW',
    ]
    rd_pow = red_powers[0]

    data_mean = np.zeros((
        len(green_powers),
        num_angles,
        num_delays,
        image_h - 2 * less_rows,
        image_w,
    ))

    for gr_pow_num, gr_pow in enumerate(green_powers):
        remove_range_dict = remove_dict_list[gr_pow_num]
        for ang in angles:
            filename = ('STE_' + 'darkfield_' + str(ang) + '_green' + gr_pow +
                        '_red' + rd_pow + '_many_delays.tif')
            assert os.path.exists(filename)
            print("Loading", filename)
            data = np_tif.tif_to_array(filename).astype(np.float64)
            assert data.shape == (num_delays * num_reps_original, image_h,
                                  image_w)
            # Stack to hyperstack
            data = data.reshape(num_reps_original, num_delays, image_h,
                                image_w)
            # crop data to remove over-exposed stuff
            data = data[:, :, less_rows:image_h - less_rows, :]

            # delete repetitions with dust particles crossing field of view
            if ang in remove_range_dict:
                remove_range_list = remove_range_dict[ang]
                for my_range in reversed(remove_range_list):
                    first = my_range[0]
                    last = my_range[1]
                    delete_length = last - first + 1
                    data = np.delete(data, first + np.arange(delete_length), 0)
            print(ang, data.shape)

            # Get the average pixel brightness in the background region of the
            # phase contrast data. We'll use it to account for laser intensity
            # fluctuations
            avg_laser_brightness = get_bg_level(data.mean(axis=(0, 1)))

            # scale all images to have the same background brightness. This
            # amounts to a correction of roughly 1% or less
            local_laser_brightness = get_bg_level(data)
            data = data * (avg_laser_brightness /
                           local_laser_brightness).reshape(
                               data.shape[0], data.shape[1], 1, 1)

            # Average data over repetitions
            data = data.mean(axis=0)
            # Put data in file to be saved
            data_mean[gr_pow_num, ang, ...] = data

        # save data for a particular green power
        print("Saving...")
        tif_shape = (num_angles * num_delays, image_h - 2 * less_rows, image_w)
        np_tif.array_to_tif(
            data_mean[gr_pow_num, :, :, :, :].reshape(tif_shape),
            ('dataset_green' + gr_pow + '.tif'))
        print("Done saving.")

    return None
예제 #20
0
    only variation is due to noise.
    """
    photoelectrons = np.round(photoelectrons_per_count * a).astype(np.int64)
    # Flip a coin for each photoelectron to decide which substack it
    # gets assigned to:
    out_1 = np.random.binomial(photoelectrons, 0.5)
    return out_1, photoelectrons - out_1

if __name__ == '__main__':
    try:
        from scipy.ndimage import gaussian_filter
    except ImportError:
        def gaussian_filter(x, sigma): return x # No blurring; whatever.
    ## Simple debugging tests. Put a 2D TIF where python can find it.
    print("Loading test object...")
    obj = np_tif.tif_to_array('blobs.tif').astype(np.float64)
    print(" Done.")
    shifts = [
        [0, 0],
        [-1, 1],
        [-2, 1],
        [-3, 1],
        [-4, 1],
        [-5, 1],
        [-1, 15],
        [-2, 0],
        [-3, 21],
        [-4, 0],
        [-5, 0],
        ]
    bucket_size = (1, 4, 4)
예제 #21
0
data_list = []
for gr_pow in green_powers:
    for ang in angles:
        for rd_pow in red_powers:
            for z_v in z_voltages:
                filename = (
                    'fluorescence' +
##                    'phase_angle_' + ang +
                    '_green' + gr_pow +
                    '_red' + rd_pow +
                    z_v +
                    '_up.tif')
                assert os.path.exists(filename)
                print("Loading", filename)
                data = np_tif.tif_to_array(filename).astype(np.float64)
                assert data.shape == (3*1, 128, 380)
                data = data.reshape(1, 3, 128, 380) # Stack to hyperstack
                data = data.mean(axis=0, keepdims=True) # Sum over reps
                data_list.append(data)
print("Done loading.")
data = np.concatenate(data_list)
##    print('data shape is',data.shape)
##    mean_subtracted = data - data.mean(axis=-3, keepdims=True)
tif_shape = (data.shape[0] * data.shape[1], data.shape[2], data.shape[3])
    
    


print("Saving...")
np_tif.array_to_tif(data.reshape(tif_shape),('dataset_green_all_powers_up.tif'))
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_A4'):
        os.mkdir('./../images/figure_A4')

    z_voltages = [
        '_34000mV',
        '_36000mV',
        '_38000mV',
        '_40000mV',
        '_42000mV',
        '_44000mV',
        '_46000mV',
        '_48000mV',
        '_50000mV',
        '_52000mV',
        '_54000mV',
        '_56000mV',
        '_58000mV',
        '_60000mV',
        '_62000mV',
        '_64000mV',
        '_66000mV',
        '_68000mV',
        '_70000mV',
    ]

    bg_level = 111.5

    # for each piezo voltage (axial location) we:
    # 1. repetition average the images for each delay
    # 2. register the repetition averaged "green off" image to the
    # corresponding "green on" image

    num_delays = 5
    num_reps = 20
    num_z = len(z_voltages)
    width = 380
    height = 128
    less_rows = 3
    data = np.zeros((num_z, num_delays, height - less_rows * 2, width))
    data_ctrl = np.zeros((num_z, num_delays, height - less_rows * 2, width))

    for z_index, z_v in enumerate(z_voltages):
        print('Piezo voltage', z_v)
        filename = ('./../../stimulated_emission_imaging-data' +
                    '/2016_11_02_STE_z_stack_depletion' +
                    '/STE_darkfield_113_green_1500mW_red_300mW' + z_v +
                    '_many_delays.tif')
        filename_ctrl = ('./../../stimulated_emission_imaging-data' +
                         '/2016_11_02_STE_z_stack_depletion' +
                         '/STE_darkfield_113_green_1500mW_red_0mW' + z_v +
                         '_many_delays.tif')
        data_z = np_tif.tif_to_array(filename).astype(np.float64) - bg_level
        data_z_ctrl = np_tif.tif_to_array(filename_ctrl).astype(
            np.float64) - bg_level
        # reshape data arrays: 0th axis is rep number, 1st is delay number
        data_z = data_z.reshape(num_reps, num_delays, height, width)
        data_z_ctrl = data_z_ctrl.reshape(num_reps, num_delays, height, width)
        # crop to remove overexposed rows (not necessary for low signal
        # measurements like fluorescence but whatever)
        data_z = data_z[:, :, less_rows:height - less_rows, :]
        data_z_ctrl = data_z_ctrl[:, :, less_rows:height - less_rows, :]

        # repetition average both image sets
        data_z_rep_avg = data_z.mean(axis=0)
        data_z_ctrl_rep_avg = data_z_ctrl.mean(axis=0)

        # registration shift control data to match "green on" data
        align_to_this_slice = data_z_rep_avg[0, :, :]
        print("Computing registration shift (no red)...")
        shifts = stack_registration(data_z_ctrl_rep_avg,
                                    align_to_this_slice=align_to_this_slice,
                                    refinement='integer',
                                    register_in_place=True,
                                    background_subtraction='edge_mean')
        print("... done computing shifts.")

        # build arrays with repetition averaged images
        data[z_index, ...] = data_z_rep_avg
        data_ctrl[z_index, ...] = data_z_ctrl_rep_avg

    # from the image where red/green are simultaneous, subtract the
    # average of images taken when the delay magnitude is greatest
    depletion_stack = (
        data[:, 2, :, :] -  # zero red/green delay
        0.5 * (data[:, 0, :, :] + data[:, 4, :, :])  # max red/green delay
    )
    crosstalk_stack = (
        data_ctrl[:, 2, :, :] -  # zero red/green delay
        0.5 *
        (data_ctrl[:, 0, :, :] + data_ctrl[:, 4, :, :])  # max red/green delay
    )
    # darkfield image (no STE) stack
    fluorescence_stack = 0.5 * (data[:, 0, :, :] + data[:, 4, :, :])

    # plot darkfield and stim emission signal
    top = 0
    bot = 106
    left = 92
    right = 240
    fluorescence_cropped = fluorescence_stack[:, top:bot, left:right]
    depletion_cropped = (depletion_stack[:, top:bot, left:right] -
                         crosstalk_stack[:, top:bot, left:right])

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    bucket_width = 8  # bucket width in pixels
    fluorescence_cropped = bucket(
        fluorescence_cropped,
        (1, bucket_width, bucket_width)) / bucket_width**2
    depletion_cropped = bucket(
        depletion_cropped, (1, bucket_width, bucket_width)) / bucket_width**2

    depletion_y_z = np.zeros(
        (depletion_cropped.shape[0] * 3, depletion_cropped.shape[1]))
    fluorescence_y_z = np.zeros(
        (fluorescence_cropped.shape[0] * 3, fluorescence_cropped.shape[1]))
    depletion_x_z = np.zeros(
        (depletion_cropped.shape[0] * 3, depletion_cropped.shape[2]))
    fluorescence_x_z = np.zeros(
        (fluorescence_cropped.shape[0] * 3, fluorescence_cropped.shape[2]))

    for z_num in range(fluorescence_cropped.shape[0]):
        # get darkfield and STE images and create yz and xz views
        depletion_image = depletion_cropped[z_num, :, :]
        depletion_y_z[z_num * 3:(z_num + 1) *
                      3, :] = depletion_image.mean(axis=1)
        depletion_x_z[z_num * 3:(z_num + 1) *
                      3, :] = depletion_image.mean(axis=0)

        fluorescence_image = fluorescence_cropped[z_num, :, :]
        fluorescence_y_z[z_num * 3:(z_num + 1) *
                         3, :] = fluorescence_image.mean(axis=1)
        fluorescence_x_z[z_num * 3:(z_num + 1) *
                         3, :] = fluorescence_image.mean(axis=0)

        # generate and save plot

        # scale bar
        fluorescence_image[-2:-1, 1:6] = 353.11
        depletion_image[-2:-1, 1:6] = -34.01

        fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(14, 5))

        cax0 = ax0.imshow(fluorescence_image,
                          cmap=plt.cm.gray,
                          interpolation='nearest',
                          vmax=353.11,
                          vmin=0)
        ax0.axis('off')
        cbar0 = fig.colorbar(cax0, ax=ax0)
        ax0.set_title('A', fontsize=30)

        cax1 = ax1.imshow(depletion_image,
                          cmap=plt.cm.gray,
                          interpolation='nearest',
                          vmax=0.61,
                          vmin=-34.01)
        cbar1 = fig.colorbar(cax1, ax=ax1)
        ax1.set_title('B', fontsize=30)
        ax1.axis('off')
        plt.close()

    # x and y projections of stack of fluorescence images
    fluorescence_y_z[-2:-1, 1:6] = np.max(fluorescence_y_z,
                                          (0, 1))  # scale bar
    fluorescence_x_z[-2:-1, 1:6] = np.max(fluorescence_x_z,
                                          (0, 1))  # scale bar
    fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(14, 14))
    cax0 = ax0.imshow(fluorescence_y_z,
                      cmap=plt.cm.gray,
                      interpolation='nearest')
    ax0.axis('off')
    cbar0 = fig.colorbar(cax0, ax=ax0)
    ax0.set_title('A', fontsize=30)

    cax1 = ax1.imshow(fluorescence_x_z,
                      cmap=plt.cm.gray,
                      interpolation='nearest')
    cbar1 = fig.colorbar(cax1, ax=ax1)
    ax1.set_title('B', fontsize=30)
    ax1.axis('off')
    plt.show()

    # x projection of stack of fluorescence and depletion images
    fluorescence_y_z[-2:-1, 1:6] = np.max(fluorescence_y_z,
                                          (0, 1))  # scale bar
    depletion_y_z[-2:-1, 1:6] = np.min(depletion_y_z, (0, 1))  # scale bar
    fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(14, 14))
    cax0 = ax0.imshow(fluorescence_y_z,
                      cmap=plt.cm.gray,
                      interpolation='nearest')
    ax0.axis('off')
    cbar0 = fig.colorbar(cax0, ax=ax0)
    ax0.set_title('A', fontsize=30)

    cax1 = ax1.imshow(depletion_y_z, cmap=plt.cm.gray, interpolation='nearest')
    cbar1 = fig.colorbar(cax1, ax=ax1)
    ax1.set_title('B', fontsize=30)
    ax1.axis('off')
    plt.show()

    # y projection of stack of fluorescence and depletion images
    fluorescence_x_z[-2:-1, 1:6] = np.max(fluorescence_x_z,
                                          (0, 1))  # scale bar
    depletion_x_z[-2:-1, 1:6] = np.min(depletion_x_z, (0, 1))  # scale bar

    fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(14, 14))
    cax0 = ax0.imshow(fluorescence_x_z,
                      cmap=plt.cm.gray,
                      interpolation='nearest')
    ax0.axis('off')
    cbar0 = fig.colorbar(cax0, ax=ax0)
    ax0.set_title('A', fontsize=30)

    cax1 = ax1.imshow(depletion_x_z, cmap=plt.cm.gray, interpolation='nearest')
    cbar1 = fig.colorbar(cax1, ax=ax1)
    ax1.set_title('B', fontsize=30)
    ax1.axis('off')
    plt.savefig('./../images/figure_A4/fluorescence_nd_image_xz.svg')
    plt.show()

    # average points around center lobe of the nanodiamond image to get
    # "average signal level" for darkfield and STE images
    top = 18
    bot = 78
    left = 133
    right = 188
    depletion_signal = (depletion_stack[:, top:bot,
                                        left:right].mean(axis=2).mean(axis=1))
    crosstalk_signal = (crosstalk_stack[:, top:bot,
                                        left:right].mean(axis=2).mean(axis=1))
    fluorescence_signal = (fluorescence_stack[:, top:bot, left:right].mean(
        axis=2).mean(axis=1))

    # plot signal v z
    z_list = a = np.arange(-1400, 2301, 200)
    plt.figure()
    plt.plot(z_list, fluorescence_signal, '.-', color='black')
    plt.title('Fluorescence image main lobe brightness')
    plt.xlabel('Z (nm)')
    plt.ylabel('Average intensity (CMOS pixel counts)')
    plt.grid()
    plt.show()
    plt.figure()
    plt.plot(z_list,
             depletion_signal,
             '.-',
             label='depletion signal',
             color='blue')
    plt.plot(z_list,
             crosstalk_signal,
             '.-',
             label='AOM crosstalk',
             color='green')
    plt.title('Depletion signal main lobe intensity')
    plt.xlabel('Z (nm)')
    plt.ylabel('Change in fluorescence light signal (CMOS pixel counts)')
    plt.legend(loc='lower right')
    plt.grid()
    plt.show()

    return None
예제 #23
0
## Set input file name and acquisition parameters for processing
input_filename = (
    'Spirostomum-spx_cyan_100_vol_60um_stk_10um_step_10ms_exp3_worm-010_cropped.tif'
)
registered_filename = os.path.splitext(input_filename)[0] + '_registered.tif'
input_filename = os.path.join(input_directory, input_filename)
registered_filename = os.path.join(temp_directory, registered_filename)
num_slices = 7  # Number of z stack slices
num_tps = 50  # Number of volumes or time points in series
border = 40  # Number of pixels to crop around image after registration

## If registerd file exists then skip registration and cropping
if os.path.exists(registered_filename):
    print('Found registered tif, loading...', end='')
    data = np_tif.tif_to_array(registered_filename)
    data = data.reshape((num_tps, num_slices) + data.shape[-2:])
    print('done')
    print('tif shape (t, z, y, x) =', data.shape)
## If no registered file is found then process original data file
else:
    print('Loading tif...', end='')
    data = np_tif.tif_to_array(input_filename)
    data = data.reshape((num_tps, num_slices) + data.shape[-2:])
    print('done')
    print('tif shape (t, z, y, x) =', data.shape)
    # Register data
    # Since our volumes were taken bidirectionally we also need to fix
    # the order.
    print('Registering...', end='', sep='')
    for which_t in range(num_tps):
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_A9'):
        os.mkdir('./../images/figure_A9')
    folder_string = ('./../../stimulated_emission_imaging-data' +
                     '/2017_05_09_pulse_length_scan_')

    pulsewidths = np.array([8, 4, 2, 1])

    # location of center lobe
    bright_spot_x = np.array([219, 235, 204, 207]) - 1
    bright_spot_y = np.array([47, 71, 37, 66]) - 1

    # cropping area defined
    top_left_x = [133, 146, 114, 118]
    top_left_y = [0, 4, 0, 4]  #[0, 7, 0, 5]
    crop_width = 175
    crop_height = 118

    # where on the plot should the cropped images be
    plot_pos_y = [0.105, 0.245, 0.37, 0.62]
    plot_pos_x = [0.25, 0.34, 0.51, 0.77]

    STE_signal = np.zeros(4)
    STE_signal_relative = np.zeros(4)
    STE_image_cropped = np.zeros((4, 14, 21))
    qtr_width = 12
    num_reps = 50
    num_delays = 3
    for i in range(4):
        pulsewidth = pulsewidths[i]
        extra_text = ''
        if pulsewidth == 4: extra_text = '_again_good'
        rest_of_folder_string = (str(pulsewidth) + 'us' + extra_text)
        filename = (folder_string + rest_of_folder_string +
                    '/STE_phase_angle_2_green_1395mW_red_300mW.tif')
        assert os.path.exists(filename)
        data = np_tif.tif_to_array(filename).astype(np.float64)
        filename = (folder_string + rest_of_folder_string +
                    '/STE_phase_angle_2_green_0mW_red_300mW.tif')
        assert os.path.exists(filename)
        data_ctrl = np_tif.tif_to_array(filename).astype(np.float64)
        # get rid of overexposed rows at top and bottom of images
        less_rows = 3
        data = data[:, less_rows:data.shape[1] - less_rows, :]
        data_ctrl = data_ctrl[:, less_rows:data_ctrl.shape[1] - less_rows, :]
        # Get the average pixel brightness in the background region of the
        # phase contrast image. We'll use it to account for laser intensity
        # fluctuations
        avg_laser_brightness = get_bg_level(data.mean(axis=0))
        # scale all images to have the same background brightness. This
        # amounts to a correction of roughly 1% or less
        local_laser_brightness = get_bg_level(data)
        data = data * (avg_laser_brightness / local_laser_brightness).reshape(
            data.shape[0], 1, 1)
        # do the same for control (green off) data
        local_laser_brightness_ctrl = get_bg_level(data_ctrl)
        data_ctrl = data_ctrl * (avg_laser_brightness /
                                 local_laser_brightness_ctrl).reshape(
                                     data_ctrl.shape[0], 1, 1)
        # reshape to hyperstack
        data = data.reshape(num_reps, num_delays, data.shape[1], data.shape[2])
        data_ctrl = data_ctrl.reshape(num_reps, num_delays, data_ctrl.shape[1],
                                      data_ctrl.shape[2])
        # now that brightness is corrected, repetition average the data
        data = data.mean(axis=0)
        data_ctrl = data_ctrl.mean(axis=0)
        # subtract avg of green off images from green on images
        data_simult = data[1, :, :]
        data_non_simult = 0.5 * (data[0, :, :] + data[2, :, :])
        STE_image = data_simult - data_non_simult
        data_simult_ctrl = data_ctrl[1, :, :]
        data_non_simult_ctrl = 0.5 * (data_ctrl[0, :, :] + data_ctrl[2, :, :])
        STE_image_ctrl = data_simult_ctrl - data_non_simult_ctrl
        # subtract AOM effects (even though it doesn't seem like there are any)
        STE_image = STE_image  # - STE_image_ctrl
        # capture stim emission signal
        my_col = bright_spot_x[i]
        my_row = bright_spot_y[i]
        main_lobe = STE_image[my_row - qtr_width:my_row + qtr_width,
                              my_col - qtr_width:my_col + qtr_width]
        left_edge = STE_image[:, qtr_width * 2]
        STE_signal[i] = np.mean(main_lobe)
        STE_signal_relative[i] = STE_signal[i] - np.mean(left_edge)
        # crop stim emission image
        STE_image_cropped_single = STE_image[top_left_y[i]:top_left_y[i] +
                                             crop_height,
                                             top_left_x[i]:top_left_x[i] +
                                             crop_width, ]
        # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
        # This is not great for viewing, because fluctuations can swamp the
        # signal. This step bins the pixels into a more typical size.
        bucket_width = 8  # bucket width in pixels
        STE_image_cropped_single = bucket(
            STE_image_cropped_single,
            (bucket_width, bucket_width)) / bucket_width**2
        STE_image_cropped[i, :, :] = STE_image_cropped_single

    # get max/min values for plot color scaling
    STE_max = np.amax(STE_image_cropped)
    STE_min = np.amin(STE_image_cropped)
    STE_image_cropped[:, -2:-1, 1:6] = STE_max  # scale bar

    my_intensity = 1 / pulsewidths

    fig, ax1 = plt.subplots()
    ax1.plot(my_intensity,
             STE_signal_relative,
             'o',
             color='black',
             markersize=10)
    plt.ylim(ymin=0, ymax=196)
    ax1.set_ylabel('Average signal brightness (pixel counts)', color='black')
    ax1.tick_params('y', colors='k')
    plt.xlabel('Normalized laser intensity (constant energy)')
    plt.grid()
    for i in range(4):
        a = plt.axes([plot_pos_x[i], plot_pos_y[i], .12, .12])
        plt.imshow(STE_image_cropped[i, :, :],
                   cmap=plt.cm.gray,
                   interpolation='nearest',
                   vmax=STE_max,
                   vmin=STE_min)
        plt.xticks([])
        plt.yticks([])

    # plot energy per exposure
    green_uJ = np.array([10, 10, 10, 10])
    red_uJ = np.array([2, 2, 2, 2])
    ax2 = ax1.twinx()
    ax2.plot(my_intensity, green_uJ, '--b', linewidth=2)
    ax2.plot(my_intensity, red_uJ, '--b', linewidth=2)
    ax2.set_ylabel('Optical energy per exposure (µJ)', color='blue')
    ax2.tick_params('y', colors='b')
    ax2.set_ylim(ymin=0, ymax=11.4)
    ax1.set_xlim(xmin=0, xmax=1.125)

    # annotate with red/green pulses
    im = plt.imread('green_shortpulse.png')
    a = plt.axes([0.773, 0.81, .08, .08], frameon=False)
    plt.imshow(im)
    plt.xticks([])
    plt.yticks([])
    im = plt.imread('green_longpulse.png')
    a = plt.axes([0.16, 0.77, .1, .1], frameon=False)
    plt.imshow(im)
    plt.xticks([])
    plt.yticks([])
    im = plt.imread('red_shortpulse.png')
    a = plt.axes([0.773, 0.25, .08, .08], frameon=False)
    plt.imshow(im)
    plt.xticks([])
    plt.yticks([])
    im = plt.imread('red_longpulse.png')
    a = plt.axes([0.16, 0.21, .1, .1], frameon=False)
    plt.imshow(im)
    plt.xticks([])
    plt.yticks([])

    plt.savefig(
        './../images/figure_A9/phase_contrast_dye_pulse_length_scan.svg')
    plt.show()

    return None
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_6'):
        os.mkdir('./../images/figure_6')
    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_A5'):
        os.mkdir('./../images/figure_A5')

    z_voltages = [
        '_34000mV',
        '_36000mV',
        '_38000mV',
        '_40000mV',
        '_42000mV',
        '_44000mV',
        '_46000mV',
        '_48000mV',
        '_50000mV',
        '_52000mV',
        '_54000mV',
        '_56000mV',
        '_58000mV',
        '_60000mV',
        '_62000mV',
    ]

    # for each piezo voltage (axial location) we:
    # 1. brightness-correct each delay scan by using the entire image
    # brightness at max/min delay as an estimate of the red laser
    # brightness over that particular delay scan
    # 2. repetition average the images for each delay
    # 3. register the repetition averaged "green off" image to the
    # corresponding "green on" image

    num_delays = 5
    num_reps = 200
    num_z = len(z_voltages)
    width = 380
    height = 128
    less_rows = 3
    data = np.zeros((num_z, num_delays, height - less_rows * 2, width))
    data_ctrl = np.zeros((num_z, num_delays, height - less_rows * 2, width))

    for z_index, z_v in enumerate(z_voltages):
        print('Piezo voltage', z_v)
        filename = ('./../../stimulated_emission_imaging-data' +
                    '/2016_11_02_STE_z_stack_darkfield' +
                    '/STE_darkfield_113_green_1500mW_red_300mW' + z_v +
                    '_many_delays.tif')
        filename_ctrl = ('./../../stimulated_emission_imaging-data' +
                         '/2016_11_02_STE_z_stack_darkfield' +
                         '/STE_darkfield_113_green_0mW_red_300mW' + z_v +
                         '_many_delays.tif')
        data_z = np_tif.tif_to_array(filename).astype(np.float64)
        data_z_ctrl = np_tif.tif_to_array(filename_ctrl).astype(np.float64)
        # reshape data arrays: 0th axis is rep number, 1st is delay number
        data_z = data_z.reshape(num_reps, num_delays, height, width)
        data_z_ctrl = data_z_ctrl.reshape(num_reps, num_delays, height, width)
        # crop to remove overexposed rows
        data_z = data_z[:, :, less_rows:height - less_rows, :]
        data_z_ctrl = data_z_ctrl[:, :, less_rows:height - less_rows, :]
        # get slice for reference brightness
        ref_slice_1 = data_z[:, 0, :, :].mean(axis=0)
        ref_slice_2 = data_z[:, 4, :, :].mean(axis=0)
        ref_slice = (ref_slice_1 + ref_slice_2) / 2
        # red beam brightness correction
        red_avg_brightness = ref_slice.mean(axis=1).mean(axis=0)
        # for green on data
        local_laser_brightness = ((data_z[:, 0, :, :] + data_z[:, 4, :, :]) /
                                  2).mean(axis=2).mean(axis=1)
        local_calibration_factor = red_avg_brightness / local_laser_brightness
        local_calibration_factor = local_calibration_factor.reshape(
            num_reps, 1, 1, 1)
        data_z = data_z * local_calibration_factor
        # for green off data
        local_laser_brightness = (
            (data_z_ctrl[:, 0, :, :] + data_z_ctrl[:, 4, :, :]) /
            2).mean(axis=2).mean(axis=1)
        local_calibration_factor = red_avg_brightness / local_laser_brightness
        local_calibration_factor = local_calibration_factor.reshape(
            num_reps, 1, 1, 1)
        data_z_ctrl = data_z_ctrl * local_calibration_factor

        # repetition average both image sets
        data_z_rep_avg = data_z.mean(axis=0)
        data_z_ctrl_rep_avg = data_z_ctrl.mean(axis=0)

        # registration shift control data to match "green on" data
        align_to_this_slice = data_z_rep_avg[0, :, :]
        print("Computing registration shifts (no green)...")
        shifts = stack_registration(data_z_ctrl_rep_avg,
                                    align_to_this_slice=align_to_this_slice,
                                    refinement='integer',
                                    register_in_place=True,
                                    background_subtraction='edge_mean')
        print("... done computing shifts.")

        # build arrays with repetition averaged images
        data[z_index, ...] = data_z_rep_avg
        data_ctrl[z_index, ...] = data_z_ctrl_rep_avg

    # from the image where red/green are simultaneous, subtract the
    # average of images taken when the delay magnitude is greatest
    STE_stack = (
        data[:, 2, :, :] -  # zero red/green delay
        0.5 * (data[:, 0, :, :] + data[:, 4, :, :])  # max red/green delay
    )
    crosstalk_stack = (
        data_ctrl[:, 2, :, :] -  # zero red/green delay
        0.5 *
        (data_ctrl[:, 0, :, :] + data_ctrl[:, 4, :, :])  # max red/green delay
    )
    # darkfield image (no STE) stack
    darkfield_stack = 0.5 * (data[:, 0, :, :] + data[:, 4, :, :])

    # plot darkfield and stim emission signal
    top = 1
    bot = 116
    left = 104
    right = 219
    darkfield_cropped = darkfield_stack[:, top:bot, left:right]
    STE_cropped = (STE_stack[:, top:bot, left:right] -
                   crosstalk_stack[:, top:bot, left:right])

    # Our pixels are tiny (8.7 nm/pixel) to give large dynamic range.
    # This is not great for viewing, because fluctuations can swamp the
    # signal. This step bins the pixels into a more typical size.
    bucket_width = 8  # bucket width in pixels
    darkfield_cropped = bucket(
        darkfield_cropped, (1, bucket_width, bucket_width)) / bucket_width**2
    STE_cropped = bucket(STE_cropped,
                         (1, bucket_width, bucket_width)) / bucket_width**2

    STE_y_z = np.zeros((STE_cropped.shape[0] * 3, STE_cropped.shape[1]))
    darkfield_y_z = np.zeros((STE_cropped.shape[0] * 3, STE_cropped.shape[1]))
    STE_x_z = np.zeros((STE_cropped.shape[0] * 3, STE_cropped.shape[2]))
    darkfield_x_z = np.zeros((STE_cropped.shape[0] * 3, STE_cropped.shape[2]))

    for z_num in range(STE_cropped.shape[0]):
        # get darkfield and STE images and create yz and xz views
        STE_image = STE_cropped[z_num, :, :]
        STE_y_z[z_num * 3:(z_num + 1) * 3, :] = STE_image.mean(axis=1)
        STE_x_z[z_num * 3:(z_num + 1) * 3, :] = STE_image.mean(axis=0)

        darkfield_image = darkfield_cropped[z_num, :, :]
        darkfield_y_z[z_num * 3:(z_num + 1) *
                      3, :] = darkfield_image.mean(axis=1)
        darkfield_x_z[z_num * 3:(z_num + 1) *
                      3, :] = darkfield_image.mean(axis=0)

        # generate and save plot

        # scale bar
        darkfield_image[-2:-1, 1:6] = 60105
        STE_image[-2:-1, 1:6] = -138

        fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(14, 5))

        cax0 = ax0.imshow(darkfield_image,
                          cmap=plt.cm.gray,
                          interpolation='nearest',
                          vmax=60106,
                          vmin=282)
        ax0.axis('off')
        cbar0 = fig.colorbar(cax0, ax=ax0)
        ax0.set_title('A', fontsize=30)

        cax1 = ax1.imshow(STE_image,
                          cmap=plt.cm.gray,
                          interpolation='nearest',
                          vmax=5.5,
                          vmin=-138)
        cbar1 = fig.colorbar(cax1, ax=ax1)
        ax1.set_title('B', fontsize=30)
        ax1.axis('off')
        plt.savefig('./../images/figure_6/darkfield_STE_image_' + str(z_num) +
                    '.svg')
        plt.close()

    # save x projection of stack
    darkfield_y_z[-2:-1, 1:6] = np.max(darkfield_y_z, (0, 1))  # scale bar
    STE_y_z[-2:-1, 1:6] = np.min(STE_y_z, (0, 1))  # scale bar
    fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(14, 14))
    cax0 = ax0.imshow(darkfield_y_z, cmap=plt.cm.gray, interpolation='nearest')
    ax0.axis('off')
    cbar0 = fig.colorbar(cax0, ax=ax0)
    ax0.set_title('A', fontsize=30)

    cax1 = ax1.imshow(STE_y_z, cmap=plt.cm.gray, interpolation='nearest')
    cbar1 = fig.colorbar(cax1, ax=ax1)
    ax1.set_title('B', fontsize=30)
    ax1.axis('off')
    plt.savefig('./../images/figure_A5/darkfield_STE_image_yz.svg')
    plt.show()

    # save y projection of stack
    darkfield_x_z[-2:-1, 1:6] = np.max(darkfield_x_z, (0, 1))  # scale bar
    STE_x_z[-2:-1, 1:6] = np.min(STE_x_z, (0, 1))  # scale bar

    fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(14, 14))
    cax0 = ax0.imshow(darkfield_x_z, cmap=plt.cm.gray, interpolation='nearest')
    ax0.axis('off')
    cbar0 = fig.colorbar(cax0, ax=ax0)
    ax0.set_title('A', fontsize=30)

    cax1 = ax1.imshow(STE_x_z, cmap=plt.cm.gray, interpolation='nearest')
    cbar1 = fig.colorbar(cax1, ax=ax1)
    ax1.set_title('B', fontsize=30)
    ax1.axis('off')
    plt.savefig('./../images/figure_A5/darkfield_STE_image_xz.svg')
    plt.show()

    # average points around center lobe of the nanodiamond image to get
    # "average signal level" for darkfield and STE images
    top = 38
    bot = 74
    left = 144
    right = 177
    STE_signal = (STE_stack[:, top:bot, left:right].mean(axis=2).mean(axis=1))
    crosstalk_signal = (crosstalk_stack[:, top:bot,
                                        left:right].mean(axis=2).mean(axis=1))
    darkfield_signal = (darkfield_stack[:, top:bot,
                                        left:right].mean(axis=2).mean(axis=1))

    # plot signal v z
    z_list = a = np.arange(-1400, 1401, 200)
    plt.figure()
    plt.plot(z_list, darkfield_signal, '.-', color='black')
    plt.title('Darkfield image main lobe brightness')
    plt.xlabel('Z (nm)')
    plt.ylabel('Average intensity (CMOS pixel counts)')
    plt.grid()
    plt.show()
    plt.figure()
    plt.plot(z_list, STE_signal, '.-', label='STE signal', color='blue')
    plt.plot(z_list,
             crosstalk_signal,
             '.-',
             label='AOM crosstalk',
             color='green')
    plt.title('Stimulated emission signal main lobe intensity')
    plt.xlabel('Z (nm)')
    plt.ylabel('Change in scattered light signal (CMOS pixel counts)')
    plt.legend(loc='lower right')
    plt.grid()
    plt.show()

    return None
input_filename = ('Yeast_cyto_FL-spx_cyan_600_vol_20um_stk_5um_step_2ms_exp_'
                  'FL_only_hd_100_volumes.tif')
re_ordered_filename = os.path.splitext(input_filename)[0] + '_re_ordered.tif'
input_filename = os.path.join(input_directory, input_filename)
re_ordered_filename = os.path.join(temp_directory, re_ordered_filename)
num_slices = 5  # Number of z stack slices
num_tps = 600  # Number of volumes or time points in series
left_crop = 50
right_crop = 100
top_crop = 1
bottom_crop = 1

## If re-ordered file exists then skip re-ordering and cropping
if os.path.exists(re_ordered_filename):
    print('Found re-ordered tif, loading...', end='', sep='')
    data = np_tif.tif_to_array(re_ordered_filename)
    data = data.reshape((num_tps, num_slices) + data.shape[-2:])
    print('done')
    print('tif shape (t, z, y, z) =', data.shape)
## If no registered file is found then process original data file
else:
    print('Loading tif...', end='', sep='')
    data = np_tif.tif_to_array(input_filename)
    data = data.reshape((num_tps, num_slices) + data.shape[-2:])
    print('done')
    print('tif shape (t, z, y, z) =', data.shape)
    # Since our volumes were taken bidirectionally we also need to fix
    # the order.
    print('Re-ordering...', end='', sep='')
    for which_t in range(num_tps):
        # Flip every other volume in the Z-direction
예제 #27
0
def main():

    assert os.path.isdir('./../images')
    if not os.path.isdir('./../images/figure_A7'):
        os.mkdir('./../images/figure_A7')

    data_path = ('./../../stimulated_emission_imaging-data' +
                 '/2016_11_18_modulated_imaging_darkfield_nanodiamond_7' +
                 '_extra_green_filter/')

    num_reps = 200  # number of times a power/delay stack was taken
    num_delays = 5

    # power calibration
    # red max power is 300 mW
    # green max power is 1450 mW
    # green powers calibrated using camera
    green_max_mW = 1450
    green_powers = np.array((113.9, 119.6, 124.5, 135, 145.5, 159.5, 175.3,
                             193.1, 234.5, 272.2, 334.1, 385.7, 446.1))
    # 0th power is AOM with 0 volts
    green_powers = green_powers - min(green_powers)
    green_powers = green_powers * green_max_mW / max(green_powers)
    green_powers = np.around(green_powers).astype(int)
    sparse_green_nums = [0, 7, 10, 12]
    green_powers_sparse = green_powers[sparse_green_nums]

    # red powers calibrated using camera
    red_max_mW = 300
    red_powers = np.array((26.6, 113, 198, 276, 353, 438, 537))
    # 0th power is AOM with 0 volts
    red_powers = red_powers - min(red_powers)
    red_powers = red_powers * red_max_mW / max(red_powers)
    red_powers = np.around(red_powers).astype(int)
    sparse_red_nums = [0, 3, 6]
    red_powers_sparse = red_powers[sparse_red_nums]

    # load representative images
    STE_image_filename = (data_path + 'STE_image_avg.tif')
    STE_image = np_tif.tif_to_array(STE_image_filename).astype(np.float64)
    STE_image_bg_filename = (data_path + 'STE_image_bg_avg.tif')
    STE_image_bg = np_tif.tif_to_array(STE_image_bg_filename).astype(
        np.float64)
    darkfield_image_filename = (data_path + 'darkfield_image_avg.tif')
    darkfield_image = np_tif.tif_to_array(darkfield_image_filename).astype(
        np.float64)
    darkfield_image_bg_filename = (data_path + 'darkfield_image_bg_avg.tif')
    darkfield_image_bg = np_tif.tif_to_array(
        darkfield_image_bg_filename).astype(np.float64)
    STE_delta_image = STE_image - darkfield_image
    STE_delta_image_bg = STE_image_bg - darkfield_image_bg
    # crosstalk correction
    STE_delta_image_corrected = STE_delta_image - STE_delta_image_bg

    # load space-averaged signal data
    filename = (data_path + 'signal_all_scaled.tif')
    data = np_tif.tif_to_array(filename).astype(np.float64)
    bg_filename = (data_path + 'signal_green_blocked_all_scaled.tif')
    bg = np_tif.tif_to_array(bg_filename).astype(np.float64)

    # reshape to hyperstack
    data = data.reshape((
        num_reps,
        len(red_powers),
        len(green_powers),
        num_delays,
    ))
    bg = bg.reshape((
        num_reps,
        len(red_powers),
        len(green_powers),
        num_delays,
    ))

    # compute STE signal
    # (delay #2 -> 0 us delay, delay #0 and #4 -> +/- 2.5 us delay)
    STE_signal = data[:, :, :, 2] - 0.5 * (data[:, :, :, 0] + data[:, :, :, 4])
    STE_signal_bg = bg[:, :, :, 2] - 0.5 * (bg[:, :, :, 0] + bg[:, :, :, 4])

    # crosstalk correction
    STE_signal_corrected = STE_signal - STE_signal_bg

    # remove bad repetition(s)
    STE_signal_corrected = np.delete(STE_signal_corrected, 188, 0)
    STE_signal_corrected = np.delete(STE_signal_corrected, 185, 0)
    STE_signal_corrected = np.delete(STE_signal_corrected, 148, 0)
    STE_signal_corrected = np.delete(STE_signal_corrected, 63, 0)
    STE_signal_corrected = np.delete(STE_signal_corrected, 5, 0)
    # also for regular signal in order to compute correct standard
    # deviation without outliers
    STE_signal = np.delete(STE_signal, 188, 0)
    STE_signal = np.delete(STE_signal, 185, 0)
    STE_signal = np.delete(STE_signal, 148, 0)
    STE_signal = np.delete(STE_signal, 63, 0)
    STE_signal = np.delete(STE_signal, 5, 0)

    # standard deviation
    STE_signal_std = np.std(STE_signal, axis=0)

    # repetition average
    STE_signal_corrected = STE_signal_corrected.mean(axis=0)

    plt.figure()
    for (pow_num, rd_pow) in enumerate(red_powers):
        plt.errorbar(green_powers,
                     STE_signal_corrected[pow_num, :],
                     yerr=STE_signal_std[pow_num, :] / (200**0.5),
                     label=('Stimulation power = ' + str(rd_pow) + ' mW'))
    plt.xlabel('Excitation power (mW)')
    plt.ylabel('Change in scattered light signal (CMOS pixel counts)')
    plt.legend(loc='lower left')
    plt.ylim(-52, 6)
    plt.xlim(-15, 1465)
    plt.grid()
    plt.savefig('./../images/figure_A7/STE_v_green_power.svg')
    plt.show()

    # plot sparse power scan data
    # all green powers, sparse red powers
    plt.figure()
    for (pow_num, rd_pow) in enumerate(red_powers):
        if pow_num in sparse_red_nums:
            plt.errorbar(green_powers,
                         STE_signal_corrected[pow_num, :],
                         yerr=STE_signal_std[pow_num, :] / (200**0.5),
                         label=('Stimulation power = ' + str(rd_pow) + ' mW'))
    plt.xlabel('Excitation power (mW)')
    plt.ylabel('Change in scattered light signal (CMOS pixel counts)')
    plt.legend(loc='lower left')
    plt.ylim(-40, 6)
    plt.xlim(-15, 1465)
    plt.grid()
    plt.savefig('./../images/figure_A7/STE_v_green_power_sparse.svg')
    plt.show()

    #all red powers, sparse green powers
    plt.figure()
    for (pow_num, gr_pow) in enumerate(green_powers):
        if pow_num in sparse_green_nums:
            plt.errorbar(red_powers,
                         STE_signal_corrected[:, pow_num],
                         yerr=STE_signal_std[:, pow_num] / (200**0.5),
                         label=('Excitation power = ' + str(gr_pow) + ' mW'))
    plt.xlabel('Stimulation power (mW)')
    plt.ylabel('Change in scattered light signal (CMOS pixel counts)')
    plt.legend(loc='lower left')
    plt.xlim(-3, 303)
    plt.grid()
    plt.savefig('./../images/figure_A7/STE_v_red_power_sparse.svg')
    plt.show()

    # plot darkfield and stim emission images

    darkfield_image = darkfield_image[0, 0:-1, :]
    STE_delta_image = STE_delta_image[0, 0:-1, :]
    STE_delta_image_corrected = STE_delta_image_corrected[0, 0:-1, :]

    # downsample darkfield and STE delta images
    bucket_width = 8
    bucket_shape = (bucket_width, bucket_width)
    darkfield_image = bucket(darkfield_image, bucket_shape) / bucket_width**2
    STE_delta_image = bucket(STE_delta_image, bucket_shape) / bucket_width**2
    STE_delta_image_corrected = bucket(STE_delta_image_corrected,
                                       bucket_shape) / bucket_width**2

    # scale bar
    darkfield_image[-2:-1, 1:6] = np.max(darkfield_image)
    STE_delta_image[-2:-1, 1:6] = np.min(STE_delta_image)
    STE_delta_image_corrected[-2:-1, 1:6] = np.min(STE_delta_image_corrected)

    fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(19, 5))

    cax0 = ax0.imshow(darkfield_image,
                      cmap=plt.cm.gray,
                      interpolation='nearest')
    ax0.axis('off')
    cbar0 = fig.colorbar(cax0, ax=ax0)

    cax1 = ax1.imshow(STE_delta_image_corrected,
                      cmap=plt.cm.gray,
                      interpolation='nearest')
    cbar1 = fig.colorbar(cax1, ax=ax1)
    ax1.axis('off')

    ax0.text(0.4, 2.2, 'A', fontsize=72, color='white', fontweight='bold')
    ax1.text(0.4, 2.2, 'B', fontsize=72, color='black', fontweight='bold')
    plt.show()

    return None
    # gets assigned to:
    out_1 = np.random.binomial(photoelectrons, 0.5)
    return out_1, photoelectrons - out_1


if __name__ == '__main__':
    try:
        from scipy.ndimage import gaussian_filter
    except ImportError:

        def gaussian_filter(x, sigma):
            return x  # No blurring; whatever.

    ## Simple debugging tests. Put a 2D TIF where python can find it.
    print("Loading test object...")
    obj = np_tif.tif_to_array('blobs.tif').astype(np.float64)
    print(" Done.")
    shifts = [
        [0, 0],
        [-1, 1],
        [-2, 1],
        [-3, 1],
        [-4, 1],
        [-5, 1],
        [-1, 15],
        [-2, 0],
        [-3, 21],
        [-4, 0],
        [-5, 0],
    ]
    bucket_size = (1, 4, 4)