async def start_one(batch_index, region_index): """Start one backprojector with a specific GPU ID in a separate thread.""" # first slice offset offset = 0 for i in range(batch_index): for j, region in self._regions[i]: offset += len(np.arange(*region)) batch = self._regions[batch_index] offset += sum([len(np.arange(*reg)) for j, reg in batch[:region_index]]) gpu_index, region = self._regions[batch_index][region_index] bp = GeneralBackproject(self.args, resources=self._resources[region_index], gpu_index=gpu_index, do_normalization=do_normalization, region=region, copy_inputs=self.copy_inputs) if do_normalization: if reuse_normalization: darks = self.darks if self.average_normalization else self.darks[:1] flats = self.flats if self.average_normalization else self.flats[:1] darks_generator = async_generate(darks) flats_generator = async_generate(flats) else: darks_generator = self._produce_normalization(self.darks, self._darks_condition) flats_generator = self._produce_normalization(self.flats, self._flats_condition) await asyncio.gather(bp.average_darks(darks_generator), bp.average_flats(flats_generator)) await self._consume(offset, bp(self._produce()))
async def test_default_write(self): await self.walker.write(async_generate([self.data, self.data])) self.assertTrue(op.exists(op.join(self.path, 'frame_000000.tif'))) self.assertTrue(op.exists(op.join(self.path, 'frame_000001.tif'))) # Cannot write if directory is not empty with self.assertRaises(StorageError): await self.walker.write(async_generate([self.data])) # Make a new one ... self.walker.descend('foo') await self.walker.write(async_generate([self.data])) self.assertTrue( op.exists(op.join(self.path, 'foo', 'frame_000000.tif')))
async def test_out_of_fov(self): images = np.random.normal(size=(10, self.image_source.size, self.image_source.size)) with self.assertRaises(ValueError) as ctx: tips = await find_needle_tips(async_generate(images)) rotation_axis(tips) self.assertEqual("No sample tip points found.", str(ctx.exception))
def score(vector): for (parameter, value) in zip(parameters, vector): setattr(self.args, parameter.replace('-', '_'), [value]) run_in_loop(self.backproject(async_generate(projections))) result = sgn * self.volume[0] LOG.info('Optimization vector: %s, result: %g', vector, result) return result
async def run_test(data, shape=None, dtype=None): accumulate = Accumulate(shape=shape, dtype=dtype) await accumulate(async_generate(data)) np.testing.assert_equal(accumulate.items, data) target_type = np.ndarray if shape else list self.assertTrue(isinstance(accumulate.items, target_type)) if shape: self.assertEqual(accumulate.items.dtype, data.dtype)
async def test_sphere_all_partially_outside(self): self.camera.size = 128 self.camera.rotation_radius = self.camera.size // 2 self.camera.scale = (.25, .25, .25) frames = (await self.acquire_frames())[0] centers = await find_sphere_centers(async_generate(frames), correlation_threshold=0.9) roll, pitch = rotation_axis(centers)[:2] # No sphere is completely in the FOV, filter the ones with low correlation coefficient and # at least coarsely close match should be found to the ellipse assert np.abs(roll - await self.z_motor.get_position()) < 1 * q.deg assert np.abs(pitch - await self.x_motor.get_position()) < 1 * q.deg
def find_parameters(self, parameters, projections=None, metrics=('sag',), regions=None, iterations=1, fwhm=0, minimize=(True,), z=None, method='powell', method_options=None, guesses=None, bounds=None, store=True): """Find reconstruction parameters. *parameters* (see :attr:`.GeneralBackprojectArgs.z_parameters`) are the names of the parameters which should be found, *projections* are the input data and if not specified, the ones from last reconstruction are used. *z* specifies the height in which the parameter is looked for. If *store* is True, the found parameter values are stored in the reconstruction arguments. Optimization is done either brute-force if *regions* are not specified or one of the scipy minimization methods is used, see below. If *regions* are specified, they are reconstructed for the corresponding parameters and a metric from *metrics* list is applied. Thus, first parameter in *parameters* is reconstructed within the first region in *regions* and the first metric (see :attr:`.GeneralBackprojectArgs.slice_metrics`) in *metrics* is applied and so on. If *metrics* is of length 1 then it is applied to all parameters. *minimize* is a tuple specifying whether each parameter in the list should be minimized (True) or maximized (False). After every parameter is processed, the parameter optimization result is stored and the next parameter is optimized in such a way, that the result of the optimization of the previous parameter already takes place. *iterations* specifies how many times are all the parameters reconstructed. *fwhm* specifies the full width half maximum of the gaussian window used to filter out the low frequencies in the metric, which is useful when the region for a metric is large. If the *fwhm* is specified, the region must be at least 4 * fwhm large. If *fwhm* is 0 no filtering is done. If *regions* is not specified, :func:`scipy.minimize` is used to find the parameter, where the optimization method is given by the *method* parameter, *method_options* are passed as *options* to the minimize function and *guesses* are initial guesses in the order of the *parameters* list. If *bounds* are given, they represent the domains where to look for parameters, they are (min, max) tuples, also in the order of the *parameters* list. See documentation of :func:`scipy.minimize` for the list of minimization methods which support bounds specification. In this approach only the first in *metrics* is taken into account because the optimization happens on all parameters simultaneously, the same holds for *minimize*. """ if projections is None: if self.projections is None: raise GeneralBackprojectManagerError('*projections* must be specified if no ' ' reconstructions have been done yet') projections = self.projections orig_args = self.args self.args = copy.deepcopy(self.args) if regions is None: # No region specified, do a real optimization on the parameters vector from scipy import optimize def score(vector): for (parameter, value) in zip(parameters, vector): setattr(self.args, parameter.replace('-', '_'), [value]) run_in_loop(self.backproject(async_generate(projections))) result = sgn * self.volume[0] LOG.info('Optimization vector: %s, result: %g', vector, result) return result self.args.z_parameter = 'z' z = z or 0 self.args.region = [z, z + 1, 1.] self.args.slice_metric = metrics[0] sgn = 1 if minimize[0] else -1 if guesses is None: guesses = [] for parameter in parameters: if parameter == 'center-position-x': guesses.append(self.args.width / 2) else: guesses.append(0.) LOG.info('Guesses: %s', guesses) result = optimize.minimize(score, guesses, method=method, bounds=bounds, options=method_options) LOG.info('%s', result.message) result = result.x else: # Regions specified, reconstruct given regions for given parameters and simply search # for extrema of the given metrics self.args.z = z or 0 if fwhm: for region in regions: if len(np.arange(*region)) < 4 * fwhm: raise ValueError('All regions must be at least 4 * fwhm large ' 'when fwhm is specified') result = [] if len(metrics) == 1: metrics = metrics * len(parameters) if len(minimize) == 1: minimize = minimize * len(parameters) for i in range(iterations): for (parameter, region, metric, minim) in zip(parameters, regions, metrics, minimize): self.args.slice_metric = metric self.args.z_parameter = parameter self.args.region = region run_in_loop(self.backproject(async_generate(projections))) sgn = 1 if minim else -1 values = self.volume if fwhm: values = filter_low_frequencies(values, fwhm=fwhm)[2 * int(fwhm): -2 * int(fwhm)] param_result = (np.argmin(sgn * values) + 2 * fwhm) * region[2] + region[0] setattr(self.args, parameter.replace('-', '_'), [param_result]) if i == iterations - 1: result.append(param_result) LOG.info('Optimizing %s, region: %s, metric: %s, minimize: %s, result: %g', parameter, region, metric, minim, param_result) LOG.info('Optimization result: %s', result) if store: for (parameter, value) in zip(parameters, result): setattr(orig_args, parameter.replace('-', '_'), [value]) self.args = orig_args return result
async def test_ok(dsetname): await self.walker.write(async_generate([self.data]), dsetname=dsetname)
async def test_raises(dsetname): with self.assertRaises(ValueError): await self.walker.write(async_generate([self.data]), dsetname=dsetname)
async def test_sphere_fully_inside(self): (frames, gt) = await self.acquire_frames() centers = await find_sphere_centers(async_generate(frames)) self.check(gt, centers)
async def test_dset_exists(self): await self.walker.write(async_generate([self.data])) with self.assertRaises(StorageError): await self.walker.write(async_generate([self.data]))
async def test_custom_write(self): await self.walker.write(async_generate([self.data]), dsetname='foo-{}.tif') self.assertTrue(op.exists(op.join(self.path, 'foo-0.tif')))
async def test_coroutine(self): await self.walker.write(async_generate(self.data), dsetname='foo') self.check()
async def test_sphere_some_partially_outside(self): self.camera.rotation_radius = self.camera.size // 2 (frames, gt) = await self.acquire_frames() centers = await find_sphere_centers(async_generate(frames)) self.check(gt, centers)
async def test_same_directory_different_dset(self): await self.walker.write(async_generate([self.data])) await self.walker.write(async_generate([self.data]), dsetname='bar-{}.tif')
async def asyncSetUp(self): await null(self.timer(async_generate([1, 2, 3])))
async def _reconstruct(self, producer): await self.manager.backproject(producer) if self.walker: with self.walker.inside(self.slice_directory): producer = async_generate(self.manager.volume) await self.walker.write(producer, dsetname='slice_{:>04}.tif')