diff --git a/cfelgeom.py b/cfelgeom.py index 63582d7c9102a5b3c1b53637744f867e2a8f92b7..ba7849d25a92acf091fe641596c213b7834456b4 100644 --- a/cfelgeom.py +++ b/cfelgeom.py @@ -23,8 +23,140 @@ files. import numpy +def apply_geometry_from_file(data_as_slab, geometry_filename): + """Parse a geometry file and applies the geometry to detector data in 'slab' format. + Turns a 2d array of pixel values into an array containing a representation of the + physical layout of the detector, keeping the origin of the reference system at the + beam interaction point. + + Args: + + data_as_slab (numpy.ndarray): the pixel values to which geometry is to be applied. + + geometry_filename (str): geometry filename. + + Returns: + + im_out (numpy.ndarray data_as_slab.dtype): Array containing a representation of the + physical layout of the detector, with the origin of the reference system at the + beam interaction point. + """ + + yx, slab_shape, img_shape = pixel_maps_for_image_view(geometry_filename) + im_out = numpy.zeros(img_shape, dtype=data_as_slab.dtype) + + im_out[yx[0], yx[1]] = data_as_slab.ravel() + return im_out + + +def apply_geometry_from_pixel_maps(data_as_slab, yx, im_out=None): + """Applies geometry, in the form of pixel maps, to detector data in 'slab' format. + Turns a 2d array of pixel values into an array containing a representation of the + physical layout of the detector, keeping the origin of the reference system at the + beam interaction point. + + Args: + + data_as_slab (numpy.ndarray): the pixel values to which geometry is to be applied. + + yx (tuple): the yx pixel maps describing the geometry of the detector. + Each map is a numpy.ndarray + + im_out (numpy.ndarray) optional: array to hold the output. If not provided, + one will be generated automatically. + + + Returns: + + im_out (numpy.ndarray data_as_slab.dtype): Array containing a representation of the + physical layout of the detector, with the origin of the reference system at the + beam interaction point. + """ + if im_out is None: + im_out = numpy.zeros(data_as_slab.shape, dtype=data_as_slab.dtype) + + im_out[yx[0], yx[1]] = data_as_slab.ravel() + return im_out + + +def pixel_maps_for_image_view(geometry_filename): + """Parse the geometry file and pixel maps for an array in 'slab' format + containing pixel values. The pixel maps can be used to create a representation + of the physical layout of the detector in a pyqtgraph ImageView widget (i.e. + they apply the detector geometry setting the origin of the reference + system is in the top left corner of the output array). + + Args: + + geometry_filename (str): geometry filename. + + Returns: + + (y, x) (numpy.ndarray int, numpy.ndarray int): pixel maps + + slab_shape tuple (int, int): shape of the original geometry uncorrected array + (the pixel values in "slab" format). + + img_shape tuple (int, int): shape of the array needed to contain the + representation of the physical layout of the detector. + """ + pixm = pixel_maps_from_geometry_file(geometry_filename) + x, y = pixm[0], pixm[1] + slab_shape = x.shape + + # find the smallest size of cspad_geom that contains all + # xy values but is symmetric about the origin + n = 2 * int(max(abs(y.max()), abs(y.min()))) + 2 + m = 2 * int(max(abs(x.max()), abs(x.min()))) + 2 + + # convert y x values to i j values + i = numpy.array(y, dtype=numpy.int) + n/2 - 1 + j = numpy.array(x, dtype=numpy.int) + m/2 - 1 + + yx = (i.flatten(), j.flatten()) + img_shape = (n, m) + return yx, slab_shape, img_shape + + +def parse_xy(string): + """Parse the x, y values from strings that have the format: + '1x + 2.0y'. + + Args: + + string (str): the string to be parsed. + + Returns: + + [x, y] [float, float]: + len two list of floats, e.g.: + 1x + 2.y --> [1., 2.] + 1x --> [1., 0.] + x --> [1., 0.] + """ + x = y = 0 + + if string.find('x') is not -1: + xs = string.split('x')[0].split(' ')[-1] + if len(xs) > 0: + x = float(xs) + else: + x = 1. + + if string.find('y') is not -1: + ys = string.split('y')[0].split(' ')[-1] + if len(ys) > 0: + y = float(ys) + else: + y = 1. + return [x, y] + + def pixel_maps_from_geometry_file(fnam): - """Extracts pixel maps from a CrystFEL-style geometry file. + """Extracts pixel maps from a CrystFEL-style geometry file. The pixel maps + can be used to create a representation of the physical layout of the + detector, keeping the origin of the reference system at the beam interaction + point. Args: @@ -35,7 +167,6 @@ def pixel_maps_from_geometry_file(fnam): x,y,r (numpy.ndarray float, numpy.ndarray float, numpy.ndarray float): slab-like pixel maps with respectively x, y coordinates of the pixel and distance of the pixel from the center of the reference system - (usually the beam position). """ f = open(fnam, 'r') @@ -46,7 +177,9 @@ def pixel_maps_from_geometry_file(fnam): detector_dict = {} - panel_lines = [x for x in f_lines if '/' in x and len(x.split('/')) == 2 and x.split('/')[1].split('=')[0].strip() in keyword_list] + panel_lines = [x for x in f_lines if '/' in x and + len(x.split('/')) == 2 and x.split('/')[1].split('=')[0].strip() in keyword_list and + 'bad_' not in x.split('/')[0].strip()] for pline in panel_lines: items = pline.split('=')[0].split('/') @@ -67,12 +200,8 @@ def pixel_maps_from_geometry_file(fnam): parsed_detector_dict[p]['max_fs'] = int(detector_dict[p]['max_fs']) parsed_detector_dict[p]['min_ss'] = int(detector_dict[p]['min_ss']) parsed_detector_dict[p]['max_ss'] = int(detector_dict[p]['max_ss']) - parsed_detector_dict[p]['fs'] = [] - parsed_detector_dict[p]['fs'].append(float(detector_dict[p]['fs'].split('x')[0])) - parsed_detector_dict[p]['fs'].append(float(detector_dict[p]['fs'].split('x')[1].split('y')[0])) - parsed_detector_dict[p]['ss'] = [] - parsed_detector_dict[p]['ss'].append(float(detector_dict[p]['ss'].split('x')[0])) - parsed_detector_dict[p]['ss'].append(float(detector_dict[p]['ss'].split('x')[1].split('y')[0] ) ) + parsed_detector_dict[p]['fs'] = parse_xy(detector_dict[p]['fs']) + parsed_detector_dict[p]['ss'] = parse_xy(detector_dict[p]['ss']) parsed_detector_dict[p]['corner_x'] = float(detector_dict[p]['corner_x']) parsed_detector_dict[p]['corner_y'] = float(detector_dict[p]['corner_y']) @@ -85,7 +214,8 @@ def pixel_maps_from_geometry_file(fnam): for p in parsed_detector_dict.keys(): # get the pixel coords for this asic i, j = numpy.meshgrid(numpy.arange(parsed_detector_dict[p]['max_ss'] - parsed_detector_dict[p]['min_ss'] + 1), - numpy.arange(parsed_detector_dict[p]['max_fs'] - parsed_detector_dict[p]['min_fs'] + 1), indexing='ij') + numpy.arange(parsed_detector_dict[p]['max_fs'] - parsed_detector_dict[p]['min_fs'] + 1), + indexing='ij') # # make the y-x ( ss, fs ) vectors, using complex notation @@ -95,8 +225,11 @@ def pixel_maps_from_geometry_file(fnam): # r = i * dy + j * dx + r_0 # - y[parsed_detector_dict[p]['min_ss']: parsed_detector_dict[p]['max_ss'] + 1, parsed_detector_dict[p]['min_fs']: parsed_detector_dict[p]['max_fs'] + 1] = r.real - x[parsed_detector_dict[p]['min_ss']: parsed_detector_dict[p]['max_ss'] + 1, parsed_detector_dict[p]['min_fs']: parsed_detector_dict[p]['max_fs'] + 1] = r.imag + y[parsed_detector_dict[p]['min_ss']: parsed_detector_dict[p]['max_ss'] + 1, + parsed_detector_dict[p]['min_fs']: parsed_detector_dict[p]['max_fs'] + 1] = r.real + + x[parsed_detector_dict[p]['min_ss']: parsed_detector_dict[p]['max_ss'] + 1, + parsed_detector_dict[p]['min_fs']: parsed_detector_dict[p]['max_fs'] + 1] = r.imag r = numpy.sqrt(numpy.square(x) + numpy.square(y)) @@ -123,7 +256,7 @@ def coffset_from_geometry_file(fnam): for line in f_lines: if line.startswith('coffset'): - coffset = float(line.split('=')[1].split('#')[0]) + coffset = float(line.split('=')[1].split(';')[0]) return coffset @@ -148,6 +281,6 @@ def res_from_geometry_file(fnam): for line in f_lines: if line.startswith('res'): - res = float(line.split('=')[1].split('#')[0]) + res = float(line.split('=')[1].split(';')[0]) return res