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)
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
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
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
## 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
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
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.")
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
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,
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
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)
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
## 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
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)