Newer
Older
Valerio Mariani
committed
# This file is part of cfelpyutils.
#
# cfelpyutils is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cfelpyutils is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cfelpyutils. If not, see <http://www.gnu.org/licenses/>.
"""
Valerio Mariani
committed
Geometry utilities.
Valerio Mariani
committed
Valerio Mariani
committed
This module contains the implementation of several functions used to
Valerio Mariani
committed
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import collections
import numpy
Valerio Mariani
committed
PixelMaps = collections.namedtuple( # pylint: disable=C0103
typename='PixelMaps',
field_names=['x', 'y', 'z', 'r', 'phi']
Valerio Mariani
committed
)
"""
Valerio Mariani
committed
The first three fields, named "x" "y" and "z" respectively, store the
pixel maps for the x,the y and the z coordinates. The fourth field,
named "r", is a pixel map storing the distance of each pixel in the
data array from the center of the reference system. The fifth field,
'phi', is instead a pixel map that stores the angles that vectors
connecting each pixel with the center of the reference system make with
respect to the x axis of the reference system.
Valerio Mariani
committed
"""
Valerio Mariani
committed
def compute_pix_maps(geometry):
"""
Compute pixel maps from a CrystFEL geometry object.
Valerio Mariani
committed
Take as input a CrystFEL-style geometry object (A dictionary
returned by the function load_crystfel_geometry function in the
crystfel_utils module) and return a :obj:`PixelMaps` tuple . The
origin the reference system used by the pixel maps is set at the
beam interaction point.
Valerio Mariani
committed
Args:
geometry (Dict): A CrystFEL geometry object (A dictionary
returned by the
:obj:`cfelpyutils.crystfel_utils.load_crystfel_geometry`
function).
Valerio Mariani
committed
Returns:
PixelMaps: A :obj:`PixelMaps` tuple storing the pixel maps
(numpy.ndarrays of float).
Valerio Mariani
committed
"""
Valerio Mariani
committed
max_fs_in_slab = numpy.array([
geometry['panels'][k]['max_fs']
for k in geometry['panels']
]).max()
Valerio Mariani
committed
max_ss_in_slab = numpy.array([
geometry['panels'][k]['max_ss']
for k in geometry['panels']
]).max()
Valerio Mariani
committed
x_map = numpy.zeros(
Valerio Mariani
committed
shape=(max_ss_in_slab + 1, max_fs_in_slab + 1),
Valerio Mariani
committed
dtype=numpy.float32 # pylint: disable=E1101
Valerio Mariani
committed
)
Valerio Mariani
committed
y_map = numpy.zeros(
Valerio Mariani
committed
shape=(max_ss_in_slab + 1, max_fs_in_slab + 1),
Valerio Mariani
committed
dtype=numpy.float32 # pylint: disable=E1101
Valerio Mariani
committed
)
z_map = numpy.zeros(
shape=(max_ss_in_slab + 1, max_fs_in_slab + 1),
dtype=numpy.float32 # pylint: disable=E1101
)
# Iterate over the panels. For each panel, determine the pixel
Valerio Mariani
committed
# indices, then compute the x,y vectors. Finally, copy the
# panel pixel maps into the detector-wide pixel maps.
Valerio Mariani
committed
for pan in geometry['panels']:
if 'clen' in geometry['panels'][pan]:
pan_clen = geometry['panels'][pan]['clen']
else:
pan_clen = 0.0
Valerio Mariani
committed
ss_grid, fs_grid = numpy.meshgrid(
Valerio Mariani
committed
numpy.arange(
geometry['panels'][pan]['max_ss'] -
geometry['panels'][pan]['min_ss'] +
1
),
numpy.arange(
geometry['panels'][pan]['max_fs'] -
geometry['panels'][pan]['min_fs'] +
1
),
indexing='ij'
)
Valerio Mariani
committed
y_panel = (
ss_grid * geometry['panels'][pan]['ssy'] +
fs_grid * geometry['panels'][pan]['fsy'] +
geometry['panels'][pan]['cny']
)
Valerio Mariani
committed
x_panel = (
ss_grid * geometry['panels'][pan]['ssx'] +
fs_grid * geometry['panels'][pan]['fsx'] +
geometry['panels'][pan]['cnx']
)
Valerio Mariani
committed
Valerio Mariani
committed
geometry['panels'][pan]['min_ss']:
geometry['panels'][pan]['max_ss'] + 1,
geometry['panels'][pan]['min_fs']:
geometry['panels'][pan]['max_fs'] + 1
Valerio Mariani
committed
] = x_panel
Valerio Mariani
committed
Valerio Mariani
committed
geometry['panels'][pan]['min_ss']:
geometry['panels'][pan]['max_ss'] + 1,
geometry['panels'][pan]['min_fs']:
geometry['panels'][pan]['max_fs'] + 1
Valerio Mariani
committed
] = y_panel
Valerio Mariani
committed
z_map[
geometry['panels'][pan]['min_ss']:
geometry['panels'][pan]['max_ss'] + 1,
geometry['panels'][pan]['min_fs']:
geometry['panels'][pan]['max_fs'] + 1
] = pan_clen
Valerio Mariani
committed
r_map = numpy.sqrt(numpy.square(x_map) + numpy.square(y_map))
phi_map = numpy.arctan2(y_map, x_map)
Valerio Mariani
committed
return PixelMaps(x_map, y_map, z_map, r_map, phi_map)
Valerio Mariani
committed
Valerio Mariani
committed
def compute_min_array_size(pixel_maps):
Valerio Mariani
committed
"""
Compute the minimum size of an array stroing the applied geometry.
Return the minimum size of an array that can store data on which
the geometry information described by the pixel maps has been
applied.
Valerio Mariani
committed
The returned array shape is big enough to display all the input
pixel values in the reference system of the physical detector. The
Valerio Mariani
committed
array is also supposed to be centered at the center of the
reference system of the detector (i.e: the beam interaction point).
Valerio Mariani
committed
Args:
pixel_maps (PixelMaps): a :obj:`PixelMaps` tuple.
Valerio Mariani
committed
Returns:
Tuple[int, int]: a numpy-style shape tuple storing the minimum
array size.
Valerio Mariani
committed
"""
# Find the largest absolute values of x and y in the maps. Since
# the returned array is centered on the origin, the minimum array
# size along a certain axis must be at least twice the maximum
# value for that axis. 2 pixels are added for good measure.
Valerio Mariani
committed
x_map, y_map = pixel_maps.x, pixel_maps.y
y_minimum = 2 * int(max(abs(y_map.max()), abs(y_map.min()))) + 2
x_minimum = 2 * int(max(abs(x_map.max()), abs(x_map.min()))) + 2
Valerio Mariani
committed
return (y_minimum, x_minimum)
Valerio Mariani
committed
Valerio Mariani
committed
def compute_visualization_pix_maps(geometry):
Valerio Mariani
committed
"""
Valerio Mariani
committed
Compute pixel maps for visualization of the data.
Valerio Mariani
committed
Valerio Mariani
committed
The pixel maps can be used for to display the data in a Pyqtgraph
ImageView widget.
Valerio Mariani
committed
Args:
geometry (Dict): A CrystFEL geometry object (A dictionary
Valerio Mariani
committed
returned by the
:obj:`cfelpyutils.crystfel_utils.load_crystfel_geometry`
function).
Valerio Mariani
committed
Returns:
Valerio Mariani
committed
PixelMaps: A PixelMaps tuple containing the adjusted pixel
maps. The first two fields, named "x" and "y" respectively,
store the pixel maps for the x coordinate and the y
coordinates (as ndarrays of type int). The third, fourth and
fifth fields ("z", "r" and "phi") are just set to None.
Valerio Mariani
committed
"""
Valerio Mariani
committed
# Shift the origin of the reference system from the beam position
# to the top-left of the image that will be displayed. Compute the
# size of the array needed to display the data, then use this
# information to estimate the magnitude of the shift.
Valerio Mariani
committed
pixel_maps = compute_pix_maps(geometry)
min_shape = compute_min_array_size(pixel_maps)
new_x_map = numpy.array(
object=pixel_maps.x,
dtype=numpy.int
) + min_shape[1] // 2 - 1
Valerio Mariani
committed
new_y_map = numpy.array(
object=pixel_maps.y,
dtype=numpy.int
) + min_shape[0] // 2 - 1
Valerio Mariani
committed
return PixelMaps(new_x_map, new_y_map, None, None, None)
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
def apply_geometry_to_data(data, geometry):
"""
Apply geometry to data.
Returns a new numpy array containing the input data with the geometry applied.
The array is ready to be displayed using a library like matplotlib or pyqtgraph.
Args:
data (ndarray): a numpy array storing the data to which geometry should be
applied.
geometry (Dict): A CrystFEL geometry object (A dictionary
returned by the
:obj:`cfelpyutils.crystfel_utils.load_crystfel_geometry`
function).
Returns:
ndarray: a new array containing the input data to which the geometry has been
applied. The returned array shape is big enough to display all the input data
in the reference system of the physical detector. The array is also supposed to
be centered at the center of the reference system of the detector (i.e: the
beam interaction point).
"""
pixel_maps = compute_pix_maps(geometry)
min_array_shape = compute_min_array_size(pixel_maps)
visualization_array = numpy.zeros(min_array_shape, dtype=float)
visual_pixel_maps = compute_visualization_pix_maps(geometry)
visualization_array[visual_pixel_maps.y.flatten(), visual_pixel_maps.x.flatten()] = data.ravel().astype(visualization_array.dtype)
return visualization_array