def propagate(samples, shape, energies, distance, pixel_size, region=None, apply_phase_factor=False, mollified=True, detector=None, offset=None, queue=None, out=None, t=None, check=True, block=False): """Propagate *samples* with *shape* as (y, x) which are :class:`syris.opticalelements.OpticalElement` instances at *energies* to *distance*. Use *pixel_size*, limit coherence to *region*, *apply_phase_factor* is as by the Fresnel approximation phase factor, *offset* is the sample offset. *queue* an OpenCL command queue, *out* a PyOpenCL Array. If *block* is True, wait for the kernels to finish. If *check* is True, check the transmission function sampling. """ if queue is None: queue = cfg.OPENCL.queue u = cl_array.Array(queue, shape, dtype=cfg.PRECISION.np_cplx) intensity = cl_array.zeros(queue, shape, cfg.PRECISION.np_float) for energy in energies: u.fill(0) u = transfer_many(samples, shape, pixel_size, energy, offset=offset, queue=queue, out=u, t=t, check=check, block=block) if distance != 0 * q.m: lam = energy_to_wavelength(energy) propagator = compute_propagator(u.shape[0], distance, lam, pixel_size, region=region, apply_phase_factor=apply_phase_factor, mollified=mollified, queue=queue, block=block) fft_2(u, queue=queue, block=block) u *= propagator ifft_2(u, queue=queue, block=block) if detector: intensity += detector.convert(abs(u) ** 2, energy) else: intensity += abs(u) ** 2 return intensity
def test_fft(self): data = gpu_util.get_array(np.random.normal(100, 100, size=(4, 4)).astype(cfg.PRECISION.np_float)) orig = gpu_util.get_host(data) data = ip.fft_2(data) ip.ifft_2(data) np.testing.assert_almost_equal(orig, data.get().real, decimal=4) # With a plan from pyfft.cl import Plan plan = Plan((4, 4), queue=cfg.OPENCL.queue) data = ip.fft_2(np.copy(orig), plan=plan) ip.ifft_2(data, plan=plan) np.testing.assert_almost_equal(orig, data.get().real, decimal=4) # Test double precision syris.init(double_precision=True, device_index=0) data = gpu_util.get_array(np.random.normal(100, 100, size=(4, 4)).astype(cfg.PRECISION.np_float)) gt = np.fft.fft2(data.get()) data = ip.fft_2(data) np.testing.assert_almost_equal(gt, data.get(), decimal=4) gt = np.fft.ifft2(data.get()) data = ip.ifft_2(data) np.testing.assert_almost_equal(gt, data.get(), decimal=4)
def test_fft(self): data = gpu_util.get_array( np.random.normal(100, 100, size=(4, 4)).astype(cfg.PRECISION.np_float) ) orig = gpu_util.get_host(data) data = ip.fft_2(data) ip.ifft_2(data) np.testing.assert_almost_equal(orig, data.get().real, decimal=4) # Test double precision default_syris_init(double_precision=True) data = gpu_util.get_array( np.random.normal(100, 100, size=(4, 4)).astype(cfg.PRECISION.np_float) ) gt = np.fft.fft2(data.get()) data = ip.fft_2(data) np.testing.assert_almost_equal(gt, data.get(), decimal=4) gt = np.fft.ifft2(data.get()) data = ip.ifft_2(data) np.testing.assert_almost_equal(gt, data.get(), decimal=4)
def main(): args = parse_args() syris.init() # Propagate to 20 cm d = 5 * q.cm # Compute grid n_camera = 256 n = n_camera * args.supersampling shape = (n, n) material = get_material("pmma_5_30_kev.mat") energy = 15 * q.keV ps = 1 * q.um ps_hd = ps / args.supersampling radius = n / 4.0 * ps_hd fmt = " Wavelength: {}" print(fmt.format(energy_to_wavelength(energy))) fmt = "Pixel size used for propagation: {}" print(fmt.format(ps_hd.rescale(q.um))) print(" Field of view: {}".format(n * ps_hd.rescale(q.um))) fmt = " Sphere diameter: {}" print(fmt.format(2 * radius)) sample = make_sphere(n, radius, pixel_size=ps_hd, material=material) projection = sample.project((n, n), ps_hd).get() * 1e6 projection = decimate(projection, (n_camera, n_camera), average=True).get() # Propagation with a monochromatic plane incident wave hd = propagate([sample], shape, [energy], d, ps_hd).get() ld = decimate(hd, (n_camera, n_camera), average=True).get() kernel = compute_tie_kernel(n_camera, ps, d, material, energy) mju = material.get_attenuation_coefficient(energy).rescale(1 / q.m).magnitude f_ld = fft_2(ld) f_ld *= get_array(kernel.astype(cfg.PRECISION.np_float)) retrieved = ifft_2(f_ld).get().real retrieved = -1 / mju * np.log(retrieved) * 1e6 if args.output_thickness: imageio.imwrite(args.output_thickness, projection) if args.output_projection: imageio.imwrite(args.output_projection, ld) if args.output_retrieved: imageio.imwrite(args.output_retrieved, retrieved) show(hd, title="High resolution") show(ld, title="Low resolution (detector)") show(retrieved, title="Retrieved [um]") show(projection, title="Projection [um]") show(projection - retrieved, title="Projection - retrieved") plt.show()
def make_sequence( self, t_start, t_end, shape=None, shot_noise=True, amplifier_noise=True, source_blur=True, queue=None, ): """Make images between times *t_start* and *t_end*.""" if queue is None: queue = cfg.OPENCL.queue shape_0 = self.detector.camera.shape if shape is None: shape = shape_0 ps_0 = self.detector.pixel_size ps = shape_0[0] / float(shape[0]) * ps_0 fps = self.detector.camera.fps frame_time = 1 / fps times = (np.arange( t_start.simplified.magnitude, t_end.simplified.magnitude, frame_time.simplified.magnitude, ) * q.s) image = cl_array.Array(queue, shape, dtype=cfg.PRECISION.np_float) source_blur_kernel = None if source_blur: source_blur_kernel = self.make_source_blur(shape, ps, queue=queue, block=False) fmt = "Making sequence with shape {} and pixel size {} from {} to {}" LOG.debug(fmt.format(shape, ps, t_start, t_end)) for t_0 in times: image.fill(0) t = t_0 t_next = self.get_next_time(t, ps) while t_next < t_0 + frame_time: LOG.debug("Motion blur: {} -> {}".format(t, t_next)) image += self.compute_intensity(t, t_next, shape, ps) t = t_next t_next = self.get_next_time(t, ps) image += self.compute_intensity(t, t_0 + frame_time, shape, ps) if source_blur: image = ip.ifft_2(ip.fft_2(image) * source_blur_kernel).real camera_image = self.detector.camera.get_image( image, shot_noise=shot_noise, amplifier_noise=amplifier_noise) LOG.debug("Image: {} -> {}".format(t_0, t_0 + frame_time)) yield camera_image
def propagate(samples, shape, energies, distance, pixel_size, region=None, apply_phase_factor=False, mollified=True, detector=None, offset=None, queue=None, out=None, t=None, check=True, block=False): """Propagate *samples* with *shape* as (y, x) which are :class:`syris.opticalelements.OpticalElement` instances at *energies* to *distance*. Use *pixel_size*, limit coherence to *region*, *apply_phase_factor* is as by the Fresnel approximation phase factor, *offset* is the sample offset. *queue* an OpenCL command queue, *out* a PyOpenCL Array. If *block* is True, wait for the kernels to finish. If *check* is True, check the transmission function sampling. """ if queue is None: queue = cfg.OPENCL.queue u = cl_array.Array(queue, shape, dtype=cfg.PRECISION.np_cplx) intensity = cl_array.zeros(queue, shape, cfg.PRECISION.np_float) for energy in energies: u.fill(0) u = transfer_many(samples, shape, pixel_size, energy, offset=offset, queue=queue, out=u, t=t, check=check, block=block) if distance != 0 * q.m: lam = energy_to_wavelength(energy) propagator = compute_propagator(u.shape[0], distance, lam, pixel_size, region=region, apply_phase_factor=apply_phase_factor, mollified=mollified, queue=queue, block=block) fft_2(u, queue=queue, block=block) for sample in samples: try: u *= sample.transfer_fourier(shape, pixel_size, energy, t=t, queue=queue, out=None, block=block) except NotImplementedError: LOG.debug('%s does not support fourier space transfer', sample) u *= propagator ifft_2(u, queue=queue, block=block) if detector: intensity += detector.convert(abs(u) ** 2, energy) else: intensity += abs(u) ** 2 return intensity
def propagate_numerically(n, w, ps, d, lam): """Propagate square aperture numerically.""" LOG.info('Numerical propagation with n: {}, ps: {}'.format(n, ps)) u_0 = np.zeros((n, n), dtype=cfg.PRECISION.np_cplx) wp = int(np.round(w / ps).simplified.magnitude) region = slice(n / 2 - wp, n / 2 + wp, 1) u_0[region, region] = 1 + 0j fp = compute_propagator(n, d, lam, ps, mollified=False) u_0 = fft_2(u_0) u_0 *= fp ifft_2(u_0) res = abs(u_0) ** 2 return crop_to_aperture(res, w, ps)
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 make_sequence(self, t_start, t_end, shape=None, shot_noise=True, amplifier_noise=True, source_blur=True, queue=None): """Make images between times *t_start* and *t_end*.""" if queue is None: queue = cfg.OPENCL.queue shape_0 = self.detector.camera.shape if shape is None: shape = shape_0 ps_0 = self.detector.pixel_size ps = shape_0[0] / float(shape[0]) * ps_0 fps = self.detector.camera.fps frame_time = 1 / fps times = np.arange(t_start.simplified.magnitude, t_end.simplified.magnitude, frame_time.simplified.magnitude) * q.s image = cl_array.Array(queue, shape, dtype=cfg.PRECISION.np_float) source_blur_kernel = None if source_blur: source_blur_kernel = self.make_source_blur(shape, ps, queue=queue, block=False) fmt = 'Making sequence with shape {} and pixel size {} from {} to {}' LOG.debug(fmt.format(shape, ps, t_start, t_end)) for t_0 in times: image.fill(0) t = t_0 t_next = self.get_next_time(t, ps) while t_next < t_0 + frame_time: LOG.debug('Motion blur: {} -> {}'.format(t, t_next)) image += self.compute_intensity(t, t_next, shape, ps) t = t_next t_next = self.get_next_time(t, ps) image += self.compute_intensity(t, t_0 + frame_time, shape, ps) if source_blur: image = ip.ifft_2(ip.fft_2(image) * source_blur_kernel).real camera_image = self.detector.camera.get_image(image, shot_noise=shot_noise, amplifier_noise=amplifier_noise) LOG.debug('Image: {} -> {}'.format(t_0, t_0 + frame_time)) yield camera_image
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