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_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
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_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
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(): 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_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_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_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
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
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