def get_radial_profile(self, canvas_inner_radius_nm: float, px_size_nm: float) -> np.ndarray: outer_radius_nm = get_canvas_radius_nm(canvas_inner_radius_nm, extend_sides_to_diagonal=True) outer_radius_px, _ = get_canvas_dimensions_px(outer_radius_nm, px_size_nm) pixels_2d = self.get_numpy_array(canvas_inner_radius_nm, px_size_nm, extend_sides_to_diagonal=True) return pixels_2d[outer_radius_px - 1, outer_radius_px - 1:]
def get_radial_profile(self, canvas_inner_radius_nm: float, px_size_nm: float) -> np.ndarray: """ Returns a numpy array representation of the pattern data as a radial profile. """ outer_radius_nm = get_canvas_radius_nm(canvas_inner_radius_nm, extend_sides_to_diagonal=True) outer_radius_px, _ = get_canvas_dimensions_px(outer_radius_nm, px_size_nm) return_value = np.zeros(outer_radius_px) radial_profile_result = radial_profile( self.get_numpy_array(canvas_inner_radius_nm, px_size_nm)) return_value[:len(radial_profile_result)] = radial_profile_result return return_value
def get_frc_curve_from_kernels2d( kernels2d: np.ndarray, run_instance: "mdl.RunInstance") -> Tuple[np.ndarray, np.ndarray]: """ Returns a tuple that contains arrays of X and Y values respectively of the resulting FRC curve from the given simulated kernels. run_instance must be a RunInstance without any multivalues. """ # Pixel size in nanometres px_size_nm = run_instance.imaging_system_settings.scanning_step_size # Radius in pixels of all 2D patterns (PSF, illumination patterns, pinholes etc.) canvas_inner_rad_nm = run_instance.simulation_settings.canvas_inner_radius canvas_outer_rad_nm = get_canvas_radius_nm(canvas_inner_rad_nm, extend_sides_to_diagonal=True) canvas_outer_rad_px, _ = get_canvas_dimensions_px(canvas_outer_rad_nm, px_size_nm) # Calculate canvas parameters half_canvas_side = np.int(np.floor((canvas_outer_rad_px - 1) / 2)) freq_arr = np.fft.fftfreq(2 * half_canvas_side - 1, px_size_nm)[:half_canvas_side] freq_arr_step = freq_arr[1] - freq_arr[0] # Retrieve/calculate sample/detector related parameters combined_sample_properties = run_instance.sample_properties.get_combined_properties( px_size_nm) input_power = combined_sample_properties.input_power D_0_0 = combined_sample_properties.D_origin pinhole = run_instance.imaging_system_settings.pinhole_function noise_var = run_instance.detector_properties.get_total_readout_noise_var( canvas_inner_rad_nm, pinhole) # Fourier transform kernels ft_kernels2d = np.abs(np.fft.fft2(kernels2d)) # Calculate the power of signal and power of noise Ps = input_power * (np.abs(ft_kernels2d[0])**2 ) # Denominator of eq (35) in publication Pn = D_0_0 * ft_kernels2d[ 1, 0, 0] + noise_var # Numerator of eq (35) in publication frc_spectra2d = Ps / (Ps + Pn) # Eq (35) frc_spectra = radial_profile(frc_spectra2d, fftshift=True) # Return X and Y values return np.arange(0, len(frc_spectra)) * freq_arr_step, frc_spectra
def get_numpy_array(self, canvas_inner_radius_nm: Optional[float] = None, px_size_nm: Optional[float] = None, _: bool = False) -> np.ndarray: """ Returns a numpy array representation of the 2D array. If canvas_inner_radius_nm and px_size_nm are set, the 2D array will be resized under the assumption that it has an inner radius of the same size as the constant canvas_inner_radius_nm. """ if canvas_inner_radius_nm is not None and px_size_nm is not None: _, canvas_side_length_px = get_canvas_dimensions_px( get_canvas_radius_nm(canvas_inner_radius_nm), px_size_nm) canvas_size = (canvas_side_length_px, canvas_side_length_px) if self.value.shape != canvas_size: return resize(self.value, canvas_size, order=3) return self.value
def expand_kernels_to_2d(*kernels, canvas_inner_radius_nm: float, px_size_nm: float) -> np.ndarray: """ Expands radial kernels to 2D arrays. The output will be an array containing these 2D arrays. """ canvas_outer_rad_px, _ = get_canvas_dimensions_px( get_canvas_radius_nm(canvas_inner_radius_nm, extend_sides_to_diagonal=True), px_size_nm ) half_side = np.int(np.floor((canvas_outer_rad_px - 1) / 2)) kernels2d = np.array([ expand_dimensions( np.linspace(0, canvas_outer_rad_px - 1, canvas_outer_rad_px), kernels[i], half_side ) for i in range(len(kernels)) ]) return kernels2d
def _simulate_single( run_instance: "mdl.RunInstance") -> Tuple[np.ndarray, np.ndarray]: """ Main function to run the simulations. run_instance must be a RunInstance without any multivalues. """ px_size_nm = run_instance.imaging_system_settings.scanning_step_size # Pixel size nanometers # Radius in pixels of all 2D patterns (PSF, illumination patterns, pinholes etc.) canvas_inner_rad_nm = run_instance.simulation_settings.canvas_inner_radius canvas_outer_rad_nm = get_canvas_radius_nm(canvas_inner_rad_nm, extend_sides_to_diagonal=True) canvas_outer_rad_px, _ = get_canvas_dimensions_px(canvas_outer_rad_nm, px_size_nm) # Calculate G(x,y), the convolution of the detection PSF and the pinhole function psf = run_instance.imaging_system_settings.optical_psf pinhole = run_instance.imaging_system_settings.pinhole_function radial_psf_and_pinhole = ( isinstance(psf.pattern_data, mdl.RadialPatternData) and isinstance(pinhole.pattern_data, mdl.RadialPatternData)) psf_arr = psf.get_numpy_array( canvas_inner_rad_nm, px_size_nm, extend_sides_to_diagonal=radial_psf_and_pinhole) #Create 2D array with 2D-sum = 1 psf_arr = psf_arr / psf_arr.sum() pinhole_arr = pinhole.get_numpy_array( canvas_inner_rad_nm, px_size_nm, extend_sides_to_diagonal=radial_psf_and_pinhole) G_2D = fftconvolve(pinhole_arr, psf_arr, mode="same") G2_2D = fftconvolve(pinhole_arr**2, psf_arr, mode="same") if radial_psf_and_pinhole: G_rad = G_2D[canvas_outer_rad_px - 1, canvas_outer_rad_px - 1:] G2_rad = G2_2D[canvas_outer_rad_px - 1][canvas_outer_rad_px - 1:] else: G_rad = np.zeros(canvas_outer_rad_px) G2_rad = np.zeros(canvas_outer_rad_px) radial_profile_result = radial_profile(G_2D) radial_profile_result_2 = radial_profile(G2_2D) G_rad[:len(radial_profile_result)] = radial_profile_result G2_rad[:len(radial_profile_result)] = radial_profile_result_2 # Calculate collection efficiency if isinstance(psf.pattern_data, mdl.AiryNAPatternData): collection_efficiency = na_to_collection_efficiency( run_instance.imaging_system_settings.optical_psf.pattern_data.na, run_instance.imaging_system_settings.refractive_index) else: raise ValueError( "Unsupported optical PSF type (only Airy from NA is currently supported)" ) # Calculate ON-state probabilities after illumination P_on = np.zeros(canvas_outer_rad_px) for pulse_index, pulse in enumerate(run_instance.pulse_scheme.pulses): illumination_pattern_rad = pulse.illumination_pattern.get_radial_profile( canvas_inner_rad_nm, px_size_nm) response = run_instance.fluorophore_settings.get_response( pulse.wavelength) if pulse_index < len(run_instance.pulse_scheme.pulses) - 1: P_on = expected_P_on( P_pre=P_on, R_on=pulse.max_intensity * response.cross_section_off_to_on * illumination_pattern_rad, R_off=pulse.max_intensity * response.cross_section_on_to_off * illumination_pattern_rad, T_exp=pulse.duration) else: # Last pulse (readout pulse) exp_kernel, var_kernel = make_kernels( num_fluorophore_simulations=run_instance.simulation_settings. num_kernel_detection_iterations, quantum_efficiency=run_instance.detector_properties. quantum_efficiency, collection_efficiency=collection_efficiency, max_intensity=pulse.max_intensity, relative_readout_intensity=illumination_pattern_rad, P_pre=P_on, CS_on_switch=response.cross_section_off_to_on, CS_off_switch=response.cross_section_on_to_off, CS_fluorescent=response.cross_section_emission, T_obs=pulse.duration, G=G_rad, G2=G2_rad) return exp_kernel, var_kernel raise ValueError("Input didn't contain any pulses!")