def get_pixel_corners(self, use_cython=True): """ Calculate the position of the corner of the pixels This should be overwritten by class representing non-contiguous detector (Xpad, ...) :return: 4D array containing: pixel index (slow dimension) pixel index (fast dimension) corner index (A, B, C or D), triangles or hexagons can be handled the same way vertex position (z,y,x) """ if self._pixel_corners is None: with self._sem: if self._pixel_corners is None: p1 = (numpy.arange(self.shape[0] + 1.0) * self._pixel1).astype(numpy.float32) t2 = numpy.arange(self.shape[1] + 1.0) * (self._pixel2 / self.radius) p2 = (self.radius * numpy.sin(t2)).astype(numpy.float32) p3 = (self.radius * (numpy.cos(t2) - 1.0)).astype( numpy.float32) if bilinear and use_cython: d1 = mathutil.expand2d(p1, self.shape[1] + 1, False) d2 = mathutil.expand2d(p2, self.shape[0] + 1, True) d3 = mathutil.expand2d(p3, self.shape[0] + 1, True) corners = bilinear.convert_corner_2D_to_4D( 3, d1, d2, d3) else: p1.shape = -1, 1 p1.strides = p1.strides[0], 0 p2.shape = 1, -1 p2.strides = 0, p2.strides[1] p3.shape = 1, -1 p3.strides = 0, p3.strides[1] corners = numpy.zeros( (self.shape[0], self.shape[1], 4, 3), dtype=numpy.float32) corners[:, :, 0, 0] = p3[:, :-1] corners[:, :, 0, 1] = p1[:-1, :] corners[:, :, 0, 2] = p2[:, :-1] corners[:, :, 1, 0] = p3[:, :-1] corners[:, :, 1, 1] = p1[1:, :] corners[:, :, 1, 2] = p2[:, :-1] corners[:, :, 2, 1] = p1[1:, :] corners[:, :, 2, 2] = p2[:, 1:] corners[:, :, 2, 0] = p3[:, 1:] corners[:, :, 3, 0] = p3[:, 1:] corners[:, :, 3, 1] = p1[:-1, :] corners[:, :, 3, 2] = p2[:, 1:] self._pixel_corners = corners return self._pixel_corners
def get_pixel_corners(self, correct_binning=False, use_cython=True): """ Calculate the position of the corner of the pixels This should be overwritten by class representing non-contiguous detector (Xpad, ...) :param correct_binning: If True, check that the produced array have the right shape regarding binning :param use_cython: set to False for testing :return: 4D array containing: pixel index (slow dimension) pixel index (fast dimension) corner index (A, B, C or D), triangles or hexagons can be handled the same way vertex position (z,y,x) """ if self._pixel_corners is None: with self._sem: if self._pixel_corners is None: p1, p2, p3 = self._get_compact_pixel_corners() if bilinear and use_cython: d1 = mathutil.expand2d(p1, self.shape[1] + 1, False) d2 = mathutil.expand2d(p2, self.shape[0] + 1, True) d3 = mathutil.expand2d(p3, self.shape[0] + 1, True) corners = bilinear.convert_corner_2D_to_4D(3, d1, d2, d3) else: p1.shape = -1, 1 p1.strides = p1.strides[0], 0 p2.shape = 1, -1 p2.strides = 0, p2.strides[1] p3.shape = 1, -1 p3.strides = 0, p3.strides[1] corners = numpy.zeros((self.shape[0], self.shape[1], 4, 3), dtype=numpy.float32) corners[:, :, 0, 0] = p3[:, :-1] corners[:, :, 0, 1] = p1[:-1, :] corners[:, :, 0, 2] = p2[:, :-1] corners[:, :, 1, 0] = p3[:, :-1] corners[:, :, 1, 1] = p1[1:, :] corners[:, :, 1, 2] = p2[:, :-1] corners[:, :, 2, 1] = p1[1:, :] corners[:, :, 2, 2] = p2[:, 1:] corners[:, :, 2, 0] = p3[:, 1:] corners[:, :, 3, 0] = p3[:, 1:] corners[:, :, 3, 1] = p1[:-1, :] corners[:, :, 3, 2] = p2[:, 1:] self._pixel_corners = corners if correct_binning and self._pixel_corners.shape[:2] != self.shape: return self._rebin_pixel_corners() else: return self._pixel_corners
def get_pixel_corners(self, use_cython=True): """ Calculate the position of the corner of the pixels This should be overwritten by class representing non-contiguous detector (Xpad, ...) :return: 4D array containing: pixel index (slow dimension) pixel index (fast dimension) corner index (A, B, C or D), triangles or hexagons can be handled the same way vertex position (z,y,x) """ if self._pixel_corners is None: with self._sem: if self._pixel_corners is None: p1 = (numpy.arange(self.shape[0] + 1.0) * self._pixel1).astype(numpy.float32) t2 = numpy.arange(self.shape[1] + 1.0) * (self._pixel2 / self.radius) p2 = (self.radius * numpy.sin(t2)).astype(numpy.float32) p3 = (self.radius * (numpy.cos(t2) - 1.0)).astype(numpy.float32) if bilinear and use_cython: d1 = mathutil.expand2d(p1, self.shape[1] + 1, False) d2 = mathutil.expand2d(p2, self.shape[0] + 1, True) d3 = mathutil.expand2d(p3, self.shape[0] + 1, True) corners = bilinear.convert_corner_2D_to_4D(3, d1, d2, d3) else: p1.shape = -1, 1 p1.strides = p1.strides[0], 0 p2.shape = 1, -1 p2.strides = 0, p2.strides[1] p3.shape = 1, -1 p3.strides = 0, p3.strides[1] corners = numpy.zeros((self.shape[0], self.shape[1], 4, 3), dtype=numpy.float32) corners[:, :, 0, 0] = p3[:, :-1] corners[:, :, 0, 1] = p1[:-1, :] corners[:, :, 0, 2] = p2[:, :-1] corners[:, :, 1, 0] = p3[:, :-1] corners[:, :, 1, 1] = p1[1:, :] corners[:, :, 1, 2] = p2[:, :-1] corners[:, :, 2, 1] = p1[1:, :] corners[:, :, 2, 2] = p2[:, 1:] corners[:, :, 2, 0] = p3[:, 1:] corners[:, :, 3, 0] = p3[:, 1:] corners[:, :, 3, 1] = p1[:-1, :] corners[:, :, 3, 2] = p2[:, 1:] self._pixel_corners = corners return self._pixel_corners
def get_pixel_corners(self, d1=None, d2=None): """Calculate the position of the corner of the pixels This should be overwritten by class representing non-contiguous detector (Xpad, ...) Precision float32 is ok: precision of 1µm for a detector size of 1m :return: 4D array containing: pixel index (slow dimension) pixel index (fast dimension) corner index (A, B, C or D), triangles or hexagons can be handled the same way vertex position (z,y,x) """ if self._pixel_corners is None: with self._sem: if self._pixel_corners is None: edges1, edges2 = self.calc_pixels_edges() p1 = mathutil.expand2d(edges1, self.shape[1] + 1, False) p2 = mathutil.expand2d(edges2, self.shape[0] + 1, True) # p3 = None self._pixel_corners = numpy.zeros( (self.shape[0], self.shape[1], 4, 3), dtype=numpy.float32) self._pixel_corners[:, :, 0, 1] = p1[:-1, :-1] self._pixel_corners[:, :, 0, 2] = p2[:-1, :-1] self._pixel_corners[:, :, 1, 1] = p1[1:, :-1] self._pixel_corners[:, :, 1, 2] = p2[1:, :-1] self._pixel_corners[:, :, 2, 1] = p1[1:, 1:] self._pixel_corners[:, :, 2, 2] = p2[1:, 1:] self._pixel_corners[:, :, 3, 1] = p1[:-1, 1:] self._pixel_corners[:, :, 3, 2] = p2[:-1, 1:] # if p3 is not None: # # non flat detector # self._pixel_corners[:, :, 0, 0] = p3[:-1, :-1] # self._pixel_corners[:, :, 1, 0] = p3[1:, :-1] # self._pixel_corners[:, :, 2, 0] = p3[1:, 1:] # self._pixel_corners[:, :, 3, 0] = p3[:-1, 1:] return self._pixel_corners
def calc_cartesian_positions(self, d1=None, d2=None, center=True, use_cython=True): if (d1 is None) or d2 is None: d1 = mathutil.expand2d( numpy.arange(self.MAX_SHAPE[0]).astype(numpy.float32), self.MAX_SHAPE[1], False) d2 = mathutil.expand2d( numpy.arange(self.MAX_SHAPE[1]).astype(numpy.float32), self.MAX_SHAPE[0], True) corners = self.get_pixel_corners() if center: # avoid += It modifies in place and segfaults d1 = d1 + 0.5 d2 = d2 + 0.5 if False and use_cython: p1, p2, p3 = bilinear.calc_cartesian_positions(d1.ravel(), d2.ravel(), corners, is_flat=False) p1.shape = d1.shape p2.shape = d2.shape p3.shape = d2.shape else: # TODO verifiedA verifier i1 = d1.astype(int).clip(0, corners.shape[0] - 1) i2 = d2.astype(int).clip(0, corners.shape[1] - 1) delta1 = d1 - i1 delta2 = d2 - i2 pixels = corners[i1, i2] if pixels.ndim == 3: A0 = pixels[:, 0, 0] A1 = pixels[:, 0, 1] A2 = pixels[:, 0, 2] B0 = pixels[:, 1, 0] B1 = pixels[:, 1, 1] B2 = pixels[:, 1, 2] C0 = pixels[:, 2, 0] C1 = pixels[:, 2, 1] C2 = pixels[:, 2, 2] D0 = pixels[:, 3, 0] D1 = pixels[:, 3, 1] D2 = pixels[:, 3, 2] else: A0 = pixels[:, :, 0, 0] A1 = pixels[:, :, 0, 1] A2 = pixels[:, :, 0, 2] B0 = pixels[:, :, 1, 0] B1 = pixels[:, :, 1, 1] B2 = pixels[:, :, 1, 2] C0 = pixels[:, :, 2, 0] C1 = pixels[:, :, 2, 1] C2 = pixels[:, :, 2, 2] D0 = pixels[:, :, 3, 0] D1 = pixels[:, :, 3, 1] D2 = pixels[:, :, 3, 2] # points A and D are on the same dim1 (Y), they differ in dim2 (X) # points B and C are on the same dim1 (Y), they differ in dim2 (X) # points A and B are on the same dim2 (X), they differ in dim1 (Y) # points C and D are on the same dim2 (X), they differ in dim1 ( p1 = A1 * (1.0 - delta1) * (1.0 - delta2) \ + B1 * delta1 * (1.0 - delta2) \ + C1 * delta1 * delta2 \ + D1 * (1.0 - delta1) * delta2 p2 = A2 * (1.0 - delta1) * (1.0 - delta2) \ + B2 * delta1 * (1.0 - delta2) \ + C2 * delta1 * delta2 \ + D2 * (1.0 - delta1) * delta2 p3 = A0 * (1.0 - delta1) * (1.0 - delta2) \ + B0 * delta1 * (1.0 - delta2) \ + C0 * delta1 * delta2 \ + D0 * (1.0 - delta1) * delta2 # To ensure numerical consitency with cython procedure. p1 = p1.astype(numpy.float32) p2 = p2.astype(numpy.float32) p3 = p3.astype(numpy.float32) return p1, p2, p3
def calc_cartesian_positions(self, d1=None, d2=None, center=True, use_cython=True): """ Calculate the position of each pixel center in cartesian coordinate and in meter of a couple of coordinates. The half pixel offset is taken into account here !!! Adapted to Nexus detector definition :param d1: the Y pixel positions (slow dimension) :type d1: ndarray (1D or 2D) :param d2: the X pixel positions (fast dimension) :type d2: ndarray (1D or 2D) :param center: retrieve the coordinate of the center of the pixel :param use_cython: set to False to test Numpy implementation :return: position in meter of the center of each pixels. :rtype: ndarray d1 and d2 must have the same shape, returned array will have the same shape. """ if self.shape: if (d1 is None) or (d2 is None): d1 = mathutil.expand2d( numpy.arange(self.shape[0]).astype(numpy.float32), self.shape[1], False) d2 = mathutil.expand2d( numpy.arange(self.shape[1]).astype(numpy.float32), self.shape[0], True) corners = self.get_pixel_corners() if center: # note += would make an increment in place which is bad (segfault !) d1 = d1 + 0.5 d2 = d2 + 0.5 if bilinear and use_cython: p1, p2, _p3 = bilinear.calc_cartesian_positions( d1.ravel(), d2.ravel(), corners) p1.shape = d1.shape p2.shape = d2.shape else: i1 = d1.astype(int).clip(0, corners.shape[0] - 1) i2 = d2.astype(int).clip(0, corners.shape[1] - 1) delta1 = d1 - i1 delta2 = d2 - i2 pixels = corners[i1, i2] A1 = pixels[:, :, 0, 1] A2 = pixels[:, :, 0, 2] B1 = pixels[:, :, 1, 1] B2 = pixels[:, :, 1, 2] C1 = pixels[:, :, 2, 1] C2 = pixels[:, :, 2, 2] D1 = pixels[:, :, 3, 1] D2 = pixels[:, :, 3, 2] # points A and D are on the same dim1 (Y), they differ in dim2 (X) # points B and C are on the same dim1 (Y), they differ in dim2 (X) # points A and B are on the same dim2 (X), they differ in dim1 # p2 = mean(A2,B2) + delta2 * (mean(C2,D2)-mean(A2,C2)) p1 = A1 * (1.0 - delta1) * (1.0 - delta2) \ + B1 * delta1 * (1.0 - delta2) \ + C1 * delta1 * delta2 \ + D1 * (1.0 - delta1) * delta2 p2 = A2 * (1.0 - delta1) * (1.0 - delta2) \ + B2 * delta1 * (1.0 - delta2) \ + C2 * delta1 * delta2 \ + D2 * (1.0 - delta1) * delta2 # To ensure numerical consitency with cython procedure. p1 = p1.astype(numpy.float32) p2 = p2.astype(numpy.float32) return p1, p2, None
def calc_cartesian_positions(self, d1=None, d2=None, center=True, use_cython=True): """ Calculate the position of each pixel center in cartesian coordinate and in meter of a couple of coordinates. The half pixel offset is taken into account here !!! Adapted to Nexus detector definition :param d1: the Y pixel positions (slow dimension) :type d1: ndarray (1D or 2D) :param d2: the X pixel positions (fast dimension) :type d2: ndarray (1D or 2D) :param center: retrieve the coordinate of the center of the pixel :param use_cython: set to False to test Python implementeation :return: position in meter of the center of each pixels. :rtype: ndarray d1 and d2 must have the same shape, returned array will have the same shape. """ if (d1 is None) or d2 is None: d1 = mathutil.expand2d(numpy.arange(self.shape[0]).astype(numpy.float32), self.shape[1], False) d2 = mathutil.expand2d(numpy.arange(self.shape[1]).astype(numpy.float32), self.shape[0], True) corners = self.get_pixel_corners() if center: # avoid += It modifies in place and segfaults d1 = d1 + 0.5 d2 = d2 + 0.5 if bilinear and use_cython: p1, p2, p3 = bilinear.calc_cartesian_positions(d1.ravel(), d2.ravel(), corners, is_flat=False) p1.shape = d1.shape p2.shape = d2.shape p3.shape = d2.shape else: i1 = d1.astype(int).clip(0, corners.shape[0] - 1) i2 = d2.astype(int).clip(0, corners.shape[1] - 1) delta1 = d1 - i1 delta2 = d2 - i2 pixels = corners[i1, i2] if pixels.ndim == 3: A0 = pixels[:, 0, 0] A1 = pixels[:, 0, 1] A2 = pixels[:, 0, 2] B0 = pixels[:, 1, 0] B1 = pixels[:, 1, 1] B2 = pixels[:, 1, 2] C0 = pixels[:, 2, 0] C1 = pixels[:, 2, 1] C2 = pixels[:, 2, 2] D0 = pixels[:, 3, 0] D1 = pixels[:, 3, 1] D2 = pixels[:, 3, 2] else: A0 = pixels[:, :, 0, 0] A1 = pixels[:, :, 0, 1] A2 = pixels[:, :, 0, 2] B0 = pixels[:, :, 1, 0] B1 = pixels[:, :, 1, 1] B2 = pixels[:, :, 1, 2] C0 = pixels[:, :, 2, 0] C1 = pixels[:, :, 2, 1] C2 = pixels[:, :, 2, 2] D0 = pixels[:, :, 3, 0] D1 = pixels[:, :, 3, 1] D2 = pixels[:, :, 3, 2] # points A and D are on the same dim1 (Y), they differ in dim2 (X) # points B and C are on the same dim1 (Y), they differ in dim2 (X) # points A and B are on the same dim2 (X), they differ in dim1 (Y) # points C and D are on the same dim2 (X), they differ in dim1 ( p1 = A1 * (1.0 - delta1) * (1.0 - delta2) \ + B1 * delta1 * (1.0 - delta2) \ + C1 * delta1 * delta2 \ + D1 * (1.0 - delta1) * delta2 p2 = A2 * (1.0 - delta1) * (1.0 - delta2) \ + B2 * delta1 * (1.0 - delta2) \ + C2 * delta1 * delta2 \ + D2 * (1.0 - delta1) * delta2 p3 = A0 * (1.0 - delta1) * (1.0 - delta2) \ + B0 * delta1 * (1.0 - delta2) \ + C0 * delta1 * delta2 \ + D0 * (1.0 - delta1) * delta2 # To ensure numerical consitency with cython procedure. p1 = p1.astype(numpy.float32) p2 = p2.astype(numpy.float32) p3 = p3.astype(numpy.float32) return p1, p2, p3
def calc_cartesian_positions(self, d1=None, d2=None, center=True, use_cython=True): """ Calculate the position of each pixel center in cartesian coordinate and in meter of a couple of coordinates. The half pixel offset is taken into account here !!! :param d1: the Y pixel positions (slow dimension) :type d1: ndarray (1D or 2D) :param d2: the X pixel positions (fast dimension) :type d2: ndarray (1D or 2D) :return: p1, p2 position in meter of the center of each pixels. :rtype: 2-tuple of numpy.ndarray d1 and d2 must have the same shape, returned array will have the same shape. """ if self.shape: if (d1 is None) or (d2 is None): d1 = mathutil.expand2d(numpy.arange(self.shape[0]).astype(numpy.float32), self.shape[1], False) d2 = mathutil.expand2d(numpy.arange(self.shape[1]).astype(numpy.float32), self.shape[0], True) if self.offset1 is None or self.offset2 is None: delta1 = delta2 = 0. else: if d2.ndim == 1: d1n = d1.astype(numpy.int32) d2n = d2.astype(numpy.int32) delta1 = self.offset1[d1n, d2n] / 100.0 # Offsets are in percent of pixel delta2 = self.offset2[d1n, d2n] / 100.0 else: if d1.shape == self.offset1.shape: delta1 = self.offset1 / 100.0 # Offsets are in percent of pixel delta2 = self.offset2 / 100.0 elif d1.shape[0] > self.offset1.shape[0]: # probably working with corners s0, s1 = self.offset1.shape delta1 = numpy.zeros(d1.shape, dtype=numpy.int32) # this is the natural type for pilatus CBF delta2 = numpy.zeros(d2.shape, dtype=numpy.int32) delta1[:s0, :s1] = self.offset1 delta2[:s0, :s1] = self.offset2 mask = numpy.where(delta1[-s0:, :s1] == 0) delta1[-s0:, :s1][mask] = self.offset1[mask] delta2[-s0:, :s1][mask] = self.offset2[mask] mask = numpy.where(delta1[-s0:, -s1:] == 0) delta1[-s0:, -s1:][mask] = self.offset1[mask] delta2[-s0:, -s1:][mask] = self.offset2[mask] mask = numpy.where(delta1[:s0, -s1:] == 0) delta1[:s0, -s1:][mask] = self.offset1[mask] delta2[:s0, -s1:][mask] = self.offset2[mask] delta1 = delta1 / 100.0 # Offsets are in percent of pixel delta2 = delta2 / 100.0 # former arrays were integers else: logger.warning("Surprising situation !!! please investigate: offset has shape %s and input array have %s", self.offset1.shape, d1.shape) delta1 = delta2 = 0. if center: # Eiger detectors images are re-built to be contiguous delta1 += 0.5 delta2 += 0.5 # For Eiger, p1 = (self._pixel1 * (delta1 + d1)) p2 = (self._pixel2 * (delta2 + d2)) return p1, p2, None