def get_image(self, photons, shot_noise=True, amplifier_noise=True, psf=True, queue=None): """Get digital counts image from incoming *photons*. The resulting image is based on the incoming photons and dark current. We apply noise based on EMVA 1288 standard according to which the variance :math:`\sigma_y^2 = K^2 ( \sigma_e^2 + \sigma_d^2 ) + \sigma_q^2`, where :math:`K` is the system gain, :math:`\sigma_e^2` is the poisson- distributed shot noise variance, :math:`\sigma_d^2` is the normal distributed electronics noise variance and :math:`\sigma_q^2` is the quantization noise variance. If *shot_noise* is False don't apply it. If *amplifier_noise* is False don't apply it as well. If *psf* is False don't apply the point spread function. """ if self._last_input_shape != photons.shape: self._last_input_shape = photons.shape self._bin_factor = (photons.shape[0] / self.shape[0], photons.shape[1] / self.shape[1]) if queue is None: queue = cfg.OPENCL.queue # Shot noise # Adjust dark current for later binning and gain dark = float( self.dark_current) / self._bin_factor[0] / self._bin_factor[1] electrons = dark + gutil.get_host(photons) if self._bin_factor != (1, 1): if psf: sigma = (fwnm_to_sigma(self._bin_factor[0]), fwnm_to_sigma(self._bin_factor[1])) small = decimate(electrons, self.shape, sigma=sigma, queue=queue) else: small = bin_image(electrons, self.shape, queue=queue) electrons = gutil.get_host(small) if shot_noise: electrons = np.random.poisson(electrons) if amplifier_noise and self.amplifier_sigma > 0: # Add electronics noise electrons = np.random.normal(electrons, self.amplifier_sigma) counts = self.gain * electrons # Cut the values beyond the maximum represented grey value given by # bytes per pixel. counts[counts > self.max_grey_value] = self.max_grey_value # Apply quantization noise return counts.astype(self.dtype)
def make_source_blur(self, shape, pixel_size, queue=None, block=False): """Make geometrical source blurring kernel with *shape* (y, x) size and *pixel_size*. Use OpenCL command *queue* and *block* if True. """ l = self.source.sample_distance size = self.source.size width = (self.propagation_distance * size[1] / l).simplified.magnitude height = (self.propagation_distance * size[0] / l).simplified.magnitude sigma = (smath.fwnm_to_sigma(height, n=2), smath.fwnm_to_sigma(width, n=2)) * q.m return ip.get_gauss_2d(shape, sigma, pixel_size=pixel_size, fourier=True, queue=queue, block=block)
def make_devices(n, energies, camera=None, highspeed=True, scintillator=None): """Create devices with image shape (*n*, *n*), X-ray *energies*, *camera* and use the high speed setup if *highspeed* is True. """ shape = (n, n) dE = energies[1] - energies[0] if not camera: vis_wavelengths = np.arange(500, 700) * q.nm camera = Camera(11 * q.um, .1, 500, 23, 32, shape, exp_time=1 * q.ms, fps=1000 / q.s, quantum_efficiencies=0.5 * np.ones(len(vis_wavelengths)), wavelengths=vis_wavelengths, dtype=np.float32) else: vis_wavelengths = camera.wavelengths.rescale(q.nm) x = vis_wavelengths.rescale(q.nm).magnitude dx = x[1] - x[0] if scintillator == 'lso' or not (scintillator or highspeed): sigma = fwnm_to_sigma(50) emission = np.exp(-(x - 545) ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi)) lso = get_material('lso_5_30_kev.mat') scintillator = Scintillator(13 * q.um, lso, 36 * np.ones(len(energies)) / q.keV, energies, emission / q.nm, vis_wavelengths, 1.82) elif scintillator == 'luag' or (not scintillator and highspeed): sigma = fwnm_to_sigma(50) emission = np.exp(-(x - 450) ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi)) luag = get_material('luag.mat') scintillator = Scintillator(50 * q.um, luag, 14 * np.ones(len(energies)) / q.keV, energies, emission / q.nm, vis_wavelengths, 1.84) if highspeed: # High speed setup lens = Lens(3, f_number=1.4, focal_length=50 * q.mm, transmission_eff=0.7, sigma=None) else: # High resolution setup lens = Lens(9, na=0.28, sigma=None) detector = Detector(scintillator, lens, camera) source_trajectory = Trajectory([(n / 2, n / 2, 0)] * detector.pixel_size) bm = make_topotomo(dE=dE, trajectory=source_trajectory, pixel_size=detector.pixel_size) return bm, detector
def decimate(image, shape, sigma=None, average=False, queue=None, block=False): """Decimate *image* so that its dimensions match the final *shape*, which has to be a divisor of the original shape. Remove low frequencies by a Gaussian filter with *sigma* pixels. If *sigma* is None, use the FWHM of one low resolution pixel. Use command *queue*, if *block* is True, wait for the copy to finish. """ if queue is None: queue = cfg.OPENCL.queue image = g_util.get_array(image, queue=queue) shape = make_tuple(shape) pow_shape = tuple([next_power_of_two(n) for n in image.shape]) orig_shape = image.shape if image.shape != pow_shape: image = pad(image, region=(0, 0) + pow_shape, queue=queue) if sigma is None: sigma = tuple([fwnm_to_sigma(float(image.shape[i]) / shape[i], n=2) for i in range(2)]) LOG.debug( "decimate, shape: %s, final_shape: %s, sigma: %s, average: %s", image.shape, shape, sigma, average, ) fltr = get_gauss_2d(image.shape, sigma, fourier=True, queue=queue, block=block) image = image.astype(cfg.PRECISION.np_cplx) fft_2(image, queue=queue, block=block) image *= fltr ifft_2(image, queue=queue, block=block) image = crop(image.real, (0, 0) + orig_shape, queue=queue, block=block) return bin_image(image, shape, average=average, queue=queue, block=block)
def decimate(image, shape, sigma=None, average=False, queue=None, block=False): """Decimate *image* so that its dimensions match the final *shape*, which has to be a divisor of the original shape. Remove low frequencies by a Gaussian filter with *sigma* pixels. If *sigma* is None, use the FWHM of one low resolution pixel. Use command *queue*, if *block* is True, wait for the copy to finish. """ if queue is None: queue = cfg.OPENCL.queue image = g_util.get_array(image, queue=queue) shape = make_tuple(shape) pow_shape = tuple([next_power_of_two(n) for n in image.shape]) orig_shape = image.shape if image.shape != pow_shape: image = pad(image, region=(0, 0) + pow_shape, queue=queue) if sigma is None: sigma = tuple([fwnm_to_sigma(float(image.shape[i]) / shape[i], n=2) for i in range(2)]) LOG.debug('decimate, shape: %s, final_shape: %s, sigma: %s, average: %s', image.shape, shape, sigma, average) fltr = get_gauss_2d(image.shape, sigma, fourier=True, queue=queue, block=block) image = image.astype(cfg.PRECISION.np_cplx) fft_2(image, queue=queue, block=block) image *= fltr ifft_2(image, queue=queue, block=block) image = crop(image.real, (0, 0) + orig_shape, queue=queue, block=block) return bin_image(image, shape, average=average, queue=queue, block=block)
def make_source_blur(self, shape, pixel_size, queue=None, block=False): """Make geometrical source blurring kernel with *shape* (y, x) size and *pixel_size*. Use OpenCL command *queue* and *block* if True. """ l = self.source.sample_distance size = self.source.size width = (self.propagation_distance * size[1] / l).simplified.magnitude height = (self.propagation_distance * size[0] / l).simplified.magnitude sigma = (smath.fwnm_to_sigma( height, n=2), smath.fwnm_to_sigma(width, n=2)) * q.m return ip.get_gauss_2d(shape, sigma, pixel_size=pixel_size, fourier=True, queue=queue, block=block)
def get_image(self, photons, shot_noise=True, amplifier_noise=True, psf=True, queue=None): """Get digital counts image from incoming *photons*. The resulting image is based on the incoming photons and dark current. We apply noise based on EMVA 1288 standard according to which the variance :math:`\sigma_y^2 = K^2 ( \sigma_e^2 + \sigma_d^2 ) + \sigma_q^2`, where :math:`K` is the system gain, :math:`\sigma_e^2` is the poisson- distributed shot noise variance, :math:`\sigma_d^2` is the normal distributed electronics noise variance and :math:`\sigma_q^2` is the quantization noise variance. If *shot_noise* is False don't apply it. If *amplifier_noise* is False don't apply it as well. If *psf* is False don't apply the point spread function. """ if self._last_input_shape != photons.shape: self._last_input_shape = photons.shape self._bin_factor = (photons.shape[0] / self.shape[0], photons.shape[1] / self.shape[1]) if queue is None: queue = cfg.OPENCL.queue # Shot noise # Adjust dark current for later binning and gain dark = float(self.dark_current) / self._bin_factor[0] / self._bin_factor[1] electrons = dark + gutil.get_host(photons) if self._bin_factor != (1, 1): if psf: sigma = (fwnm_to_sigma(self._bin_factor[0]), fwnm_to_sigma(self._bin_factor[1])) small = decimate(electrons, self.shape, sigma=sigma, queue=queue) else: small = bin_image(electrons, self.shape, queue=queue) electrons = gutil.get_host(small) if shot_noise: electrons = np.random.poisson(electrons) if amplifier_noise and self.amplifier_sigma > 0: # Add electronics noise electrons = np.random.normal(electrons, self.amplifier_sigma) counts = self.gain * electrons # Cut the values beyond the maximum represented grey value given by # bytes per pixel. counts[counts > self.max_grey_value] = self.max_grey_value # Apply quantization noise return counts.astype(self.dtype)
def compute_sigma_component(ps): fwtm = compute_aliasing_limit(size, lam, ps, distance, fov=size * ps, fourier=True) if region is not None: fwtm_region = compute_aliasing_limit(size, lam, ps, distance, region, fourier=True) fwtm = min(fwtm_region, fwtm) sigma = fwnm_to_sigma(fwtm, n=10) return sigma
def compute_propagator(size, distance, lam, pixel_size, fresnel=True, region=None, apply_phase_factor=False, mollified=True, queue=None, block=False): """Create a propagator with (*size*, *size*) dimensions for propagation *distance*, wavelength *lam* and *pixel_size*. If *fresnel* is True, use the Fresnel approximation, if it is False, use the full propagator (don't approximate the square root). *region* is the diameter of the the wavefront area which is capable of interference. If *apply_phase_factor* is True, apply the phase factor defined by Fresnel approximation. If *mollified* is True the aliased frequencies are suppressed. If command *queue* is specified, execute the kernel on it. If *block* is True, wait for the kernel to finish. """ if size % 2: raise ValueError('Only even sizes are supported') if queue is None: queue = cfg.OPENCL.queue # Check the sampling r_cutoff = compute_aliasing_limit(size, lam, pixel_size, distance, fov=region, fourier=False) min_n = 4 if r_cutoff < min_n: LOG.error('Propagator too narrow, propagation distance too small or pixel size too large') f_cutoff = compute_aliasing_limit(size, lam, pixel_size, distance, fov=region, fourier=True) if f_cutoff < min_n: LOG.error('Propagator too wide, propagation distance too large or pixel size too small') out = cl_array.Array(queue, (size, size), cfg.PRECISION.np_cplx) if apply_phase_factor: phase_factor = np.exp(2 * np.pi * distance.simplified / lam.simplified * 1j) else: phase_factor = 0 + 0j ev = cfg.OPENCL.programs['physics'].propagator(queue, (size / 2 + 1, size / 2 + 1), None, out.data, cfg.PRECISION.np_float(distance.simplified), cfg.PRECISION.np_float(lam.simplified), cfg.PRECISION.np_float(pixel_size.simplified), g_util.make_vcomplex(phase_factor), np.int32(fresnel)) if block: ev.wait() if mollified: fwtm = compute_aliasing_limit(size, lam, pixel_size, distance, fov=size * pixel_size, fourier=True) if region is not None: fwtm_region = compute_aliasing_limit(size, lam, pixel_size, distance, region, fourier=True) fwtm = min(fwtm_region, fwtm) sigma = fwnm_to_sigma(fwtm, n=10) mollifier = get_gauss_2d(size, sigma, fourier=False, queue=queue, block=block) out = out * mollifier return out
def main(): args = parse_args() syris.init() n = 1024 shape = (n, n) ps = 1 * q.um tr = Trajectory([(n / 2, n / 2, 0)] * ps, pixel_size=ps) energy_center = args.energy_center * q.keV fwhm = args.energy_resolution * energy_center sigma = smath.fwnm_to_sigma(fwhm, n=2) # Make sure we resolve the curve nicely energies = np.arange(max(1 * q.keV, energy_center - 2 * fwhm), energy_center + 2 * fwhm, fwhm / 25) * q.keV dE = energies[1] - energies[0] print 'Energy from, to, step, number:', energies[0], energies[-1], dE, len( energies) bm = make_topotomo(dE=dE, pixel_size=ps, trajectory=tr) spectrum_energies = np.arange(1, 50, 1) * q.keV native_spectrum = get_spectrum(bm, spectrum_energies, ps) fltr = GaussianFilter(energies, energy_center, sigma) gauss = get_gauss(energies.magnitude, energy_center.magnitude, sigma.magnitude) filtered_spectrum = get_spectrum(bm, energies, ps) * gauss intensity = propagate([bm, fltr], shape, energies, 0 * q.m, ps).get() show(intensity, title='Intensity for energy range {} - {}'.format( energies[0], energies[-1])) plt.figure() plt.plot(spectrum_energies.magnitude, native_spectrum) plt.title('Source Spectrum') plt.xlabel('Energy [keV]') plt.ylabel('Intensity') plt.figure() plt.plot(energies.magnitude, gauss) plt.title('Gaussian Filter') plt.xlabel('Energy [keV]') plt.ylabel('Transmitted intensity') plt.figure() plt.plot(energies.magnitude, filtered_spectrum) plt.title('Filtered Spectrum') plt.xlabel('Energy [keV]') plt.ylabel('Intensity') plt.show()
def main(): args = parse_args() syris.init() n = 1024 shape = (n, n) ps = 1 * q.um tr = Trajectory([(n / 2, n / 2, 0)] * ps, pixel_size=ps) energy_center = args.energy_center * q.keV fwhm = args.energy_resolution * energy_center sigma = smath.fwnm_to_sigma(fwhm, n=2) # Make sure we resolve the curve nicely energies = np.arange(max(1 * q.keV, energy_center - 2 * fwhm), energy_center + 2 * fwhm, fwhm / 25) * q.keV dE = energies[1] - energies[0] print 'Energy from, to, step, number:', energies[0], energies[-1], dE, len(energies) bm = make_topotomo(dE=dE, pixel_size=ps, trajectory=tr) spectrum_energies = np.arange(1, 50, 1) * q.keV native_spectrum = get_spectrum(bm, spectrum_energies, ps) fltr = GaussianFilter(energies, energy_center, sigma) gauss = get_gauss(energies.magnitude, energy_center.magnitude, sigma.magnitude) filtered_spectrum = get_spectrum(bm, energies, ps) * gauss intensity = propagate([bm, fltr], shape, energies, 0 * q.m, ps).get() show(intensity, title='Intensity for energy range {} - {}'.format(energies[0], energies[-1])) plt.figure() plt.plot(spectrum_energies.magnitude, native_spectrum) plt.title('Source Spectrum') plt.xlabel('Energy [keV]') plt.ylabel('Intensity') plt.figure() plt.plot(energies.magnitude, gauss) plt.title('Gaussian Filter') plt.xlabel('Energy [keV]') plt.ylabel('Transmitted intensity') plt.figure() plt.plot(energies.magnitude, filtered_spectrum) plt.title('Filtered Spectrum') plt.xlabel('Energy [keV]') plt.ylabel('Intensity') plt.show()
def test_varconvolve_gauss(self): from scipy.ndimage import gaussian_filter n = 128 shape = (n, n) image = np.zeros(shape, dtype=cfg.PRECISION.np_float) image[n / 2, n / 2] = 1 sigmas = np.ones_like(image) * 1e-3 result = ip.varconvolve_gauss(image, (sigmas, sigmas), normalized=False).get() # At least one pixel in the midle must exist (just copies the original image) self.assertEqual(1, np.sum(result)) # Test against the scipy implementation sigma = fwnm_to_sigma(5, n=2) sigmas = np.ones_like(image) * sigma gt = gaussian_filter(image, sigma) result = ip.varconvolve_gauss(image, (sigmas, sigmas), normalized=True).get() np.testing.assert_almost_equal(gt, result)
def test_decimate(self): n = 16 sigma = fwnm_to_sigma(1) shape = (n // 2, n // 2) image = np.arange(n * n).reshape(n, n).astype(cfg.PRECISION.np_float) // n ** 2 fltr = get_gauss_2d((n, n), sigma, fourier=True) filtered = np.fft.ifft2(np.fft.fft2(image) * fltr).real gt = bin_cpu(filtered, shape) res = ip.decimate(image, shape, sigma=sigma, average=False).get() np.testing.assert_almost_equal(gt, res, decimal=6) # With averaging res = ip.decimate(image, shape, sigma=sigma, average=True).get() gt = gt / 4 np.testing.assert_almost_equal(gt, res, decimal=6)
def apply_blur(self, intensity, distance, pixel_size, queue=None, block=False): """Apply source blur based on van Cittert-Zernike theorem at *distance*.""" fwhm = (distance * self.size / self.sample_distance).simplified sigma = smath.fwnm_to_sigma(fwhm, n=2) psf = ip.get_gauss_2d(intensity.shape, sigma, pixel_size=pixel_size, fourier=True, queue=queue, block=block) return ip.ifft_2(ip.fft_2(intensity) * psf).real
def test_decimate(self): n = 16 sigma = fwnm_to_sigma(1) shape = (n / 2, n / 2) image = np.arange(n * n).reshape(n, n).astype(cfg.PRECISION.np_float) / n ** 2 fltr = get_gauss_2d((n, n), sigma, fourier=True) filtered = np.fft.ifft2(np.fft.fft2(image) * fltr).real gt = bin_cpu(filtered, shape) res = ip.decimate(image, shape, sigma=sigma, average=False).get() np.testing.assert_almost_equal(gt, res, decimal=6) # With averaging res = ip.decimate(image, shape, sigma=sigma, average=True).get() gt = gt / 4 np.testing.assert_almost_equal(gt, res, decimal=6)
def test_gaussian(self): energies = np.arange(5, 30, 0.1) * q.keV energy = 15 * q.keV sigma = fwnm_to_sigma(1, n=2) * q.keV energy_10 = energy - sigma_to_fwnm(sigma.magnitude, n=10) / 2 * q.keV fltr = GaussianFilter(energies, energy, sigma, peak_transmission=0.5) u_0 = 10 + 0j u_f = fltr.transfer(None, None, energy) u = u_0 * u_f self.assertAlmostEqual(np.abs(u) ** 2, 50, places=4) u_f = fltr.transfer(None, None, energy_10) u = u_0 * u_f self.assertAlmostEqual(np.abs(u) ** 2, 5, places=4) # Test exponent u_f = fltr.transfer(None, None, energy, exponent=True) u = u_0 * np.exp(u_f) self.assertAlmostEqual(np.abs(u) ** 2, 50, places=4) u_f = fltr.transfer(None, None, energy_10, exponent=True) u = u_0 * np.exp(u_f) self.assertAlmostEqual(np.abs(u) ** 2, 5, places=4)
def test_gaussian(self): energies = np.arange(5, 30, 0.1) * q.keV energy = 15 * q.keV sigma = fwnm_to_sigma(1, n=2) * q.keV energy_10 = energy - sigma_to_fwnm(sigma.magnitude, n=10) / 2 * q.keV fltr = GaussianFilter(energies, energy, sigma, peak_transmission=0.5) u_0 = 10 + 0j u_f = fltr.transfer(None, None, energy) u = u_0 * u_f self.assertAlmostEqual(np.abs(u)**2, 50, places=4) u_f = fltr.transfer(None, None, energy_10) u = u_0 * u_f self.assertAlmostEqual(np.abs(u)**2, 5, places=4) # Test exponent u_f = fltr.transfer(None, None, energy, exponent=True) u = u_0 * np.exp(u_f) self.assertAlmostEqual(np.abs(u)**2, 50, places=4) u_f = fltr.transfer(None, None, energy_10, exponent=True) u = u_0 * np.exp(u_f) self.assertAlmostEqual(np.abs(u)**2, 5, places=4)
source_trajectory = Trajectory(pp, velocity=vel) source_trajectory_stat = Trajectory([(n / 2, n / 2, 0)] * ps) undu = SpectraSource(SPECTRA_FILE, DISTANCE, dE, (5, 140) * q.um, detector.pixel_size, source_trajectory, phase_profile='sphere', fluctuation=0.06) undu.trajectory.bind(detector.pixel_size) #bm = make_topotomo(dE=dE, trajectory=source_trajectory, pixel_size=detector.pixel_size) #bm.trajectory.bind(detector.pixel_size) ## == GAUSIAN FILTER (MONOCHROMATOR APPROX) == fwhm = dE_mono * energy * q.eV sigma = smath.fwnm_to_sigma(fwhm, n=2) fltr = GaussianFilter(energies, energy * q.eV, sigma) # == SAMPLE === meshes = read_collada(OBJ_PATH, [(n / 2, n / 2, 0)] * detector.pixel_size, iterations=1) tr = Trajectory([(0 / 2, 0 / 2, 0)] * detector.pixel_size) cb = CompositeBody(tr, bodies=meshes) cb.bind_trajectory(detector.pixel_size) airm = get_material('air_dry.mat') cu = get_material('cu.mat') air_gap = MaterialFilter(3 * q.m, airm) abs_fltr = MaterialFilter(100 * q.um, cu)
def main(): args = parse_args() syris.init() # Propagate to 20 cm d = 20 * q.cm # Compute grid n_camera = 256 n = n_camera * args.supersampling shape = (n, n) material = get_material('pmma_5_30_kev.mat') energies = material.energies dE = energies[1] - energies[0] # Lens with magnification 5 and numerical aperture 0.25 lens = Lens(5, na=0.25) # Considered visible light wavelengths vis_wavelengths = np.arange(500, 700) * q.nm # Simple camera quantum efficiencies cam_qe = 0.1 * np.ones(len(vis_wavelengths)) camera = Camera(10 * q.um, 0.1, 500, 23, 32, (256, 256), exp_time=args.exposure * q.ms, fps=1 / q.s, quantum_efficiencies=cam_qe, wavelengths=vis_wavelengths, dtype=np.float32) # Scintillator emits visible light into a region given by a Gaussian distribution x = camera.wavelengths.rescale(q.nm).magnitude sigma = fwnm_to_sigma(50) emission = np.exp(-(x - 600) ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi)) # Scintillator 50 um thick, light yield 14 and refractive index 1.84 luag = get_material('luag.mat') scintillator = Scintillator(50 * q.um, luag, 14 * np.ones(len(energies)) / q.keV, energies, emission / q.nm, camera.wavelengths, 1.84) detector = Detector(scintillator, lens, camera) # Pixel size used for propagation ps = detector.pixel_size / args.supersampling fmt = 'Pixel size used for propagation: {}' print fmt.format(ps.rescale(q.um)) fmt = ' Effective detector pixel size: {}' print fmt.format(detector.pixel_size.rescale(q.um)) print ' Field of view: {}'.format(n * ps.rescale(q.um)) # Bending magnet source trajectory = Trajectory([(n / 2, n / 2, 0)] * ps) source = make_topotomo(dE=dE, trajectory=trajectory, pixel_size=ps) sample = make_sphere(n, n / 4. * ps, pixel_size=ps, material=material) # Propagation with a monochromatic plane incident wave coherent = propagate([source, sample], shape, [15 * q.keV], d, ps, t=0 * q.s, detector=detector).get() coherent *= camera.exp_time.simplified.magnitude # Decimate to fit the effective pixel size of the detector system coherent_ld = camera.get_image(coherent, shot_noise=False, amplifier_noise=False) # Propagation which takes into account polychromaticity poly = propagate([source, sample], shape, range(10, 30) * q.keV, d, ps, t=0 * q.s, detector=detector).get() poly *= camera.exp_time.simplified.magnitude poly_ld = camera.get_image(poly, shot_noise=args.noise, amplifier_noise=args.noise) # Compute and show some of the used propagators propagator_10 = get_propagator_psf(n, d, ps, 10 * q.keV) propagator_30 = get_propagator_psf(n, d, ps, 30 * q.keV) show(coherent, title='Coherent Supersampled') show(coherent_ld, title='Coherent Detector') show(propagator_10.real, title='Propagator PSF for 10 keV (real part)') show(propagator_30.real, title='Propagator PSF for 30 keV (real part)') show(poly, title='Polychromatic Supersampled') show(poly_ld, title='Polychromatic Detector') plt.show()
def compute_intensity(self, t_0, t_1, shape, pixel_size, queue=None, block=False, flat=False): """Compute intensity between times *t_0* and *t_1*.""" exp_time = (t_1 - t_0).simplified.magnitude if queue is None: queue = cfg.OPENCL.queue u = cl_array.Array(queue, shape, dtype=cfg.PRECISION.np_cplx) u_sample = cl_array.zeros(queue, shape, cfg.PRECISION.np_cplx) intensity = cl_array.zeros(queue, shape, cfg.PRECISION.np_float) for energy in self.energies: u.fill(1) for oeid, oe in enumerate(self.oe): if flat and oe == self.sample: continue u *= oe.transfer(shape, pixel_size, energy, t=t_0, queue=queue, out=u_sample, check=False, block=block) # Propagate and blur optical element when not source if self.distances[oeid] != 0 * q.m and oe != self.source: lam = energy_to_wavelength(energy) propagator = compute_propagator(u.shape[0], self.distances[oeid], lam, pixel_size, queue=queue, block=block, mollified=True) ip.fft_2(u, queue=queue, block=block) sdistance = np.sum(self.distances[:oeid + 1]) fwhm = (self.distances[oeid] * self.source.size / sdistance).simplified sigma = smath.fwnm_to_sigma(fwhm, n=2) psf = ip.get_gauss_2d(shape, sigma, pixel_size=pixel_size, fourier=True, queue=queue, block=block) u *= psf u *= propagator ip.ifft_2(u, queue=queue, block=block) intensity += self.detector.convert(abs(u)**2, energy) return intensity * exp_time
def make_devices(n, energies, camera=None, highspeed=True, scintillator=None): """Create devices with image shape (*n*, *n*), X-ray *energies*, *camera* and use the high speed setup if *highspeed* is True. """ shape = (n, n) dE = energies[1] - energies[0] if not camera: vis_wavelengths = np.arange(500, 700) * q.nm camera = Camera(11 * q.um, .1, 500, 23, 32, shape, exp_time=1 * q.ms, fps=1000 / q.s, quantum_efficiencies=0.5 * np.ones(len(vis_wavelengths)), wavelengths=vis_wavelengths, dtype=np.float32) else: vis_wavelengths = camera.wavelengths.rescale(q.nm) x = vis_wavelengths.rescale(q.nm).magnitude dx = x[1] - x[0] if scintillator == 'lso' or not (scintillator or highspeed): sigma = fwnm_to_sigma(50) emission = np.exp(-(x - 545)**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi)) lso = get_material('lso_5_30_kev.mat') scintillator = Scintillator(13 * q.um, lso, 36 * np.ones(len(energies)) / q.keV, energies, emission / q.nm, vis_wavelengths, 1.82) elif scintillator == 'luag' or (not scintillator and highspeed): sigma = fwnm_to_sigma(50) emission = np.exp(-(x - 450)**2 / (2 * sigma**2)) / (sigma * np.sqrt(2 * np.pi)) luag = get_material('luag.mat') scintillator = Scintillator(50 * q.um, luag, 14 * np.ones(len(energies)) / q.keV, energies, emission / q.nm, vis_wavelengths, 1.84) if highspeed: # High speed setup lens = Lens(3, f_number=1.4, focal_length=50 * q.mm, transmission_eff=0.7, sigma=None) else: # High resolution setup lens = Lens(9, na=0.28, sigma=None) detector = Detector(scintillator, lens, camera) source_trajectory = Trajectory([(n / 2, n / 2, 0)] * detector.pixel_size) bm = make_topotomo(dE=dE, trajectory=source_trajectory, pixel_size=detector.pixel_size) return bm, detector