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 pad(image, region=None, out=None, value=0, queue=None, block=False): """Pad a 2D *image*. *region* is the region to pad as (y_0, x_0, height, width). If not specified, the next power of two dimensions are used and the image is centered in the padded one. The final image dimensions are height x width and the filling starts at (y_0, x_0), *out* is the pyopencl Array instance, if not specified it will be created. *out* is also returned. *value* is the padded value. If *block* is True, wait for the copy to finish. """ if region is None: shape = tuple([next_power_of_two(n) for n in image.shape]) y_0 = (shape[0] - image.shape[0]) / 2 x_0 = (shape[1] - image.shape[1]) / 2 region = (y_0, x_0) + shape if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl_array.zeros(queue, (region[2], region[3]), dtype=image.dtype) + value image = g_util.get_array(image, queue=queue) n_bytes = image.dtype.itemsize y_0, x_0, height, width = region src_origin = (0, 0, 0) dst_origin = (n_bytes * x_0, y_0, 0) region = (n_bytes * image.shape[1], image.shape[0], 1) LOG.debug('pad, shape: %s, src_origin: %s, dst_origin: %s, region: %s', image.shape, src_origin, dst_origin, region) _copy_rect(image, out, src_origin, dst_origin, region, queue, block=block) return out
def crop(image, region, out=None, queue=None, block=False): """Crop a 2D *image*. *region* is the region to crop as (y_0, x_0, height, width), *out* is the pyopencl Array instance, if not specified it will be created. *out* is also returned. If *block* is True, wait for the copy to finish. """ if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl.array.Array(queue, (region[2], region[3]), dtype=image.dtype) image = g_util.get_array(image, queue=queue) n_bytes = image.dtype.itemsize y_0, x_0, height, width = region src_origin = (n_bytes * x_0, y_0, 0) dst_origin = (0, 0, 0) region = (n_bytes * width, height, 1) LOG.debug( "crop, shape: %s, src_origin: %s, dst_origin: %s, region: %s", image.shape, src_origin, dst_origin, region, ) _copy_rect(image, out, src_origin, dst_origin, region, queue, block=block) return out
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 bin_image(image, summed_shape, offset=(0, 0), average=False, out=None, queue=None, block=False): """Bin an *image*. The resulting buffer has shape *summed_shape* (y, x). *Offset* (y, x) is the offset to the original *image*. *summed_shape* has to be a divisor of the original shape minus the *offset*. If *average* is True, the summed pixel is normalized by the region area. *out* is the pyopencl Array instance, if not specified it will be created. *out* is also returned. If *block* is True, wait for the copy to finish. """ if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl.array.Array(queue, summed_shape, dtype=cfg.PRECISION.np_float) image = g_util.get_array(image, queue=queue) orig_shape = (image.shape[0] - offset[0], image.shape[1] - offset[1]) region = (orig_shape[0] / summed_shape[0], orig_shape[1] / summed_shape[1]) if orig_shape[0] % summed_shape[0] or orig_shape[1] % summed_shape[1]: raise RuntimeError('Final shape {} must be a divisor '.format(summed_shape) + 'of the original shape {}'.format(image.shape)) LOG.debug('bin_image, shape: %s, summed_shape: %s, offset: %s, average: %s', image.shape, summed_shape, offset, average) ev = cfg.OPENCL.programs['improc'].sum(queue, (summed_shape[::-1]), None, out.data, image.data, vec.make_int2(*region[::-1]), np.int32(image.shape[1]), vec.make_int2(*offset[::-1]), np.int32(average)) if block: ev.wait() return out
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 _fft_2(data, inverse=False, queue=None, block=True): """Execute FFT on *data*, which is first converted to a pyopencl array and retyped to complex. """ if not queue: queue = cfg.OPENCL.queue thread = ocl_api().Thread(queue) data = g_util.get_array(data, queue=queue) if data.dtype != cfg.PRECISION.np_cplx: data = data.astype(cfg.PRECISION.np_cplx) if queue not in cfg.OPENCL.fft_plans: cfg.OPENCL.fft_plans[queue] = {} if data.shape not in cfg.OPENCL.fft_plans[queue]: LOG.debug("Creating FFT Plan for {} and shape {}".format(queue, data.shape)) _fft = FFT(data, axes=(0, 1)) cfg.OPENCL.fft_plans[queue][data.shape] = _fft.compile(thread, fast_math=False) plan = cfg.OPENCL.fft_plans[queue][data.shape] LOG.debug("fft_2, shape: %s, inverse: %s", data.shape, inverse) # plan.execute(data.data, inverse=inverse, wait_for_finish=block) plan(data, data, inverse=inverse) if block: thread.synchronize() return data
def pad(image, region=None, out=None, value=0, queue=None, block=False): """Pad a 2D *image*. *region* is the region to pad as (y_0, x_0, height, width). If not specified, the next power of two dimensions are used and the image is centered in the padded one. The final image dimensions are height x width and the filling starts at (y_0, x_0), *out* is the pyopencl Array instance, if not specified it will be created. *out* is also returned. *value* is the padded value. If *block* is True, wait for the copy to finish. """ if region is None: shape = tuple([next_power_of_two(n) for n in image.shape]) y_0 = (shape[0] - image.shape[0]) // 2 x_0 = (shape[1] - image.shape[1]) // 2 region = (y_0, x_0) + shape if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl_array.zeros(queue, (region[2], region[3]), dtype=image.dtype) + value image = g_util.get_array(image, queue=queue) n_bytes = image.dtype.itemsize y_0, x_0, height, width = region src_origin = (0, 0, 0) dst_origin = (n_bytes * x_0, y_0, 0) region = (n_bytes * image.shape[1], image.shape[0], 1) LOG.debug( "pad, shape: %s, src_origin: %s, dst_origin: %s, region: %s", image.shape, src_origin, dst_origin, region, ) _copy_rect(image, out, src_origin, dst_origin, region, queue, block=block) return out
def _test(): shape = 8, 4 dtypes = ['i', 'u', 'f'] lengths = [2, 4, 8] types = [ np.dtype('{}{}'.format(dt, length)) for dt, length in itertools.product(dtypes, lengths) ] types.append(np.dtype('i1')) types.append(np.dtype('u1')) types += [np.dtype('c8'), np.dtype('c16')] for dtype in types: np_data = np.arange(shape[0] * shape[1]).reshape(shape).astype(dtype) # host -> Array cl_data = gu.get_array(np_data) np.testing.assert_equal(np_data, cl_data.get()) # Array -> Array res = gu.get_array(cl_data) np.testing.assert_equal(res.get(), cl_data.get()) # Array -> host host_data = gu.get_host(cl_data) np.testing.assert_equal(np_data, host_data) # host -> host host_data = gu.get_host(np_data) np.testing.assert_equal(np_data, host_data) if dtype.kind != 'c': # numpy -> Image and Image -> Array image = gu.get_image(np_data) back = gu.get_array(image).get() np.testing.assert_equal(back, np_data) # Image -> host host_data = gu.get_host(image) np.testing.assert_equal(host_data, np_data) # Array -> Image image = gu.get_image(cl_data) back = gu.get_array(image).get() np.testing.assert_equal(back, np_data) # Image -> Image image_2 = gu.get_image(image) back = gu.get_array(image_2).get() np.testing.assert_equal(back, np_data)
def _test(): shape = 8, 4 dtypes = ["i", "u", "f"] lengths = [2, 4, 8] types = [ np.dtype("{}{}".format(dt, length)) for dt, length in itertools.product(dtypes, lengths) ] types.append(np.dtype("i1")) types.append(np.dtype("u1")) types += [np.dtype("c8"), np.dtype("c16")] for dtype in types: np_data = np.arange(shape[0] * shape[1]).reshape(shape).astype(dtype) # host -> Array cl_data = gu.get_array(np_data) np.testing.assert_equal(np_data, cl_data.get()) # Array -> Array res = gu.get_array(cl_data) np.testing.assert_equal(res.get(), cl_data.get()) # Array -> host host_data = gu.get_host(cl_data) np.testing.assert_equal(np_data, host_data) # host -> host host_data = gu.get_host(np_data) np.testing.assert_equal(np_data, host_data) if gu.are_images_supported() and dtype.kind != "c": # numpy -> Image and Image -> Array image = gu.get_image(np_data) back = gu.get_array(image).get() np.testing.assert_equal(back, np_data) # Image -> host host_data = gu.get_host(image) np.testing.assert_equal(host_data, np_data) # Array -> Image image = gu.get_image(cl_data) back = gu.get_array(image).get() np.testing.assert_equal(back, np_data) # Image -> Image image_2 = gu.get_image(image) back = gu.get_array(image_2).get() np.testing.assert_equal(back, np_data)
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 compute_intensity(wavefield, queue=None, out=None, block=False): if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl.array.Array(queue, wavefield.shape, dtype=cfg.PRECISION.np_float) wavefield = g_util.get_array(wavefield, queue=queue) ev = cfg.OPENCL.programs["improc"].compute_intensity( queue, wavefield.shape[::-1], None, wavefield.data, out.data ) if block: ev.wait() return out
def compute_intensity(wavefield, queue=None, out=None, block=False): if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl.array.Array(queue, wavefield.shape, dtype=cfg.PRECISION.np_float) wavefield = g_util.get_array(wavefield, queue=queue) ev = cfg.OPENCL.programs['improc'].compute_intensity(queue, wavefield.shape[::-1], None, wavefield.data, out.data) if block: ev.wait() return out
def crop(image, region, out=None, queue=None, block=False): """Crop a 2D *image*. *region* is the region to crop as (y_0, x_0, height, width), *out* is the pyopencl Array instance, if not specified it will be created. *out* is also returned. If *block* is True, wait for the copy to finish. """ if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl.array.Array(queue, (region[2], region[3]), dtype=image.dtype) image = g_util.get_array(image, queue=queue) n_bytes = image.dtype.itemsize y_0, x_0, height, width = region src_origin = (n_bytes * x_0, y_0, 0) dst_origin = (0, 0, 0) region = (n_bytes * width, height, 1) LOG.debug('crop, shape: %s, src_origin: %s, dst_origin: %s, region: %s', image.shape, src_origin, dst_origin, region) _copy_rect(image, out, src_origin, dst_origin, region, queue, block=block) return out
def _fft_2(data, inverse=False, plan=None, queue=None, block=True): """Execute FFT on *data*, which is first converted to a pyopencl array and retyped to complex. """ data = g_util.get_array(data, queue=queue) if data.dtype != cfg.PRECISION.np_cplx: data = data.astype(cfg.PRECISION.np_cplx) if not plan: if not queue: queue = cfg.OPENCL.queue if queue not in cfg.OPENCL.fft_plans: cfg.OPENCL.fft_plans[queue] = {} if data.shape not in cfg.OPENCL.fft_plans[queue]: LOG.debug('Creating FFT Plan for {} and shape {}'.format(queue, data.shape)) cfg.OPENCL.fft_plans[queue][data.shape] = Plan(data.shape, dtype=cfg.PRECISION.np_cplx, queue=queue) plan = cfg.OPENCL.fft_plans[queue][data.shape] LOG.debug('fft_2, shape: %s, inverse: %s', data.shape, inverse) plan.execute(data.data, inverse=inverse, wait_for_finish=block) return data
def __init__(self, thickness, pixel_size, material=None, queue=None): super(StaticBody, self).__init__(material) self.thickness = g_util.get_array(thickness.simplified.magnitude, queue=queue) self.pixel_size = make_tuple(pixel_size, num_dims=2)