예제 #1
0
    def test_trim_and_mask(self):
        """ Test that regions that only have masks contributions are not present
        in the angular average """
        image = np.ones(shape=(256, 256), dtype=np.float)
        center = (image.shape[0] / 2, image.shape[1] / 2)
        xc, yc = center

        # Create an image with a wide ring
        extent = np.arange(0, image.shape[0])
        xx, yy = np.meshgrid(extent, extent)
        rr = np.hypot(xx - xc, yy - yc)

        mask = np.ones_like(image, dtype=np.bool)
        mask[rr < 20] = False
        # image[rr < 20] = 0

        radius, intensity = azimuthal_average(image,
                                              center,
                                              mask=mask,
                                              trim=False)
        self.assertEqual(radius.min(), 0)

        radius_trimmed, intensity_trimmed = azimuthal_average(image,
                                                              center,
                                                              mask=mask,
                                                              trim=True)
        self.assertEqual(radius_trimmed.min(), 20)
예제 #2
0
    def test_mask_and_nan(self):
        """ Test that azimuthal_average with masks does not yield NaNs. This can happen for large masks. """
        image = np.ones(shape=(256, 256), dtype=np.int16)
        mask = np.zeros_like(image, dtype=np.bool)
        mask[100:156, 100:156] = True
        _, av = azimuthal_average(image, center=(128, 128), mask=mask, trim=False)

        self.assertFalse(np.any(np.isnan(av)))
예제 #3
0
    def test_trivial_array(self):
        """ Test azimuthal_average on an array of zeroes """
        image = np.zeros(shape=(256, 256), dtype=np.float)
        center = (image.shape[0] / 2, image.shape[1] / 2)

        radius, intensity = azimuthal_average(image, center)

        self.assertTrue(intensity.sum() == 0)
        self.assertSequenceEqual(intensity.shape, radius.shape)
예제 #4
0
def test_azimuthal_average_trivial_array():
    """ Test azimuthal_average on an array of zeroes """
    image = np.zeros(shape=(256, 256), dtype=float)
    center = (image.shape[0] / 2, image.shape[1] / 2)

    radius, intensity = azimuthal_average(image, center)

    assert intensity.sum() == 0
    assert intensity.shape == radius.shape
예제 #5
0
    def test_ring(self):
        """ Test azimuthal_average on an image with a wide ring """
        image = np.zeros(shape=(256, 256), dtype=np.float)
        center = (image.shape[0] / 2, image.shape[1] / 2)
        xc, yc = center

        # Create an image with a wide ring
        extent = np.arange(0, image.shape[0])
        xx, yy = np.meshgrid(extent, extent)
        rr = np.sqrt((xx - xc)**2 + (yy - yc)**2)
        image[np.logical_and(24 < rr, rr < 26)] = 1

        radius, intensity = azimuthal_average(image, center)
        self.assertEqual(intensity.max(), image.max())
        self.assertSequenceEqual(radius.shape, intensity.shape)
예제 #6
0
    def test_angular_bounds(self):
        """ Test azimuthal_average with a restrictive angular_bounds argument """
        image = np.zeros(shape=(256, 256), dtype=np.float)
        center = (image.shape[0] / 2, image.shape[1] / 2)
        xc, yc = center

        # Create an image with a wide ring
        extent = np.arange(0, image.shape[0])
        xx, yy = np.meshgrid(extent, extent)
        rr = np.sqrt((xx - xc)**2 + (yy - yc)**2)
        angles = np.rad2deg(np.arctan2(yy - yc, xx - xc)) + 180
        image[np.logical_and(0 <= angles, angles <= 60)] = 1

        with self.subTest("0 - 360"):
            radius, intensity = azimuthal_average(image,
                                                  center,
                                                  angular_bounds=None)
            r360, int360 = azimuthal_average(image,
                                             center,
                                             angular_bounds=(0, 360))
            self.assertTrue(np.allclose(intensity, int360))

        with self.subTest("Inside angle bounds"):
            radius, intensity = azimuthal_average(image,
                                                  center,
                                                  angular_bounds=(0, 60))
            self.assertTrue(np.allclose(intensity, np.ones_like(intensity)))

        with self.subTest("Overlapping bounds"):
            radius, intensity = azimuthal_average(image,
                                                  center,
                                                  angular_bounds=(15, 75))
            self.assertFalse(np.all(intensity < np.ones_like(intensity)))

        with self.subTest("Outside angle bounds"):
            radius, intensity = azimuthal_average(image,
                                                  center,
                                                  angular_bounds=(60, 360))
            self.assertTrue(np.allclose(intensity, np.zeros_like(intensity)))

        with self.subTest("Inside angle bounds with 360deg rollover"):
            radius, intensity = azimuthal_average(image,
                                                  center,
                                                  angular_bounds=(60 + 360,
                                                                  360 + 360))
            self.assertTrue(np.allclose(intensity, np.zeros_like(intensity)))
예제 #7
0
    def test_ring_with_mask(self):
        """ Test azimuthal_average on an image with a wide ring """
        image = np.zeros(shape=(256, 256), dtype=np.float)
        center = (image.shape[0] / 2, image.shape[1] / 2)
        xc, yc = center

        mask = np.ones_like(image, dtype=np.bool)
        mask[120:140, 0:140] = False

        # Create an image with a wide ring
        extent = np.arange(0, image.shape[0])
        xx, yy = np.meshgrid(extent, extent)
        rr = np.sqrt((xx - xc) ** 2 + (yy - yc) ** 2)
        image[np.logical_and(24 < rr, rr < 26)] = 1
        # Add some ridiculously high value under the mask
        # to see if it will be taken intou account
        image[128, 128] = 10000

        radius, intensity = azimuthal_average(image, center, mask=mask, trim=False)

        # The maximum value outside of the mask area was set to 1
        self.assertEqual(intensity.max(), 1)
        self.assertSequenceEqual(radius.shape, intensity.shape)
예제 #8
0
def test_azimuthal_average_angular_bounds():
    """ Test azimuthal_average with a restrictive angular_bounds argument """
    image = np.zeros(shape=(256, 256), dtype=float)
    center = (image.shape[0] / 2, image.shape[1] / 2)
    xc, yc = center

    # Create an image with a wide ring
    extent = np.arange(0, image.shape[0])
    xx, yy = np.meshgrid(extent, extent)
    rr = np.sqrt((xx - xc)**2 + (yy - yc)**2)
    angles = np.rad2deg(np.arctan2(yy - yc, xx - xc)) + 180
    image[np.logical_and(0 <= angles, angles <= 60)] = 1

    radius, intensity = azimuthal_average(image, center, angular_bounds=None)
    r360, int360 = azimuthal_average(image, center, angular_bounds=(0, 360))
    assert np.allclose(intensity, int360)

    radius, intensity = azimuthal_average(image,
                                          center,
                                          angular_bounds=(0, 60))
    assert np.allclose(intensity, np.ones_like(intensity))

    radius, intensity = azimuthal_average(image,
                                          center,
                                          angular_bounds=(15, 75))
    assert not np.all(intensity < np.ones_like(intensity))

    radius, intensity = azimuthal_average(image,
                                          center,
                                          angular_bounds=(60, 360))
    assert np.allclose(intensity, np.zeros_like(intensity))

    radius, intensity = azimuthal_average(image,
                                          center,
                                          angular_bounds=(60 + 360, 360 + 360))
    assert np.allclose(intensity, np.zeros_like(intensity))
예제 #9
0
    def compute_angular_averages(
        self,
        center=None,
        normalized=False,
        angular_bounds=None,
        trim=True,
        callback=None,
    ):
        """ 
        Compute the angular averages.
        
        Parameters
        ----------
        center : 2-tuple or None, optional
            Center of the diffraction patterns. If None (default), the dataset
            attribute will be used instead.
        normalized : bool, optional
            If True, each pattern is normalized to its integral.
        angular_bounds : 2-tuple of float or None, optional
            Angle bounds are specified in degrees. 0 degrees is defined as the positive x-axis. 
            Angle bounds outside [0, 360) are mapped back to [0, 360).
        trim : bool, optional
            If True, leading/trailing zeros - possibly due to masks - are trimmed.
        callback : callable or None, optional
            Callable of a single argument, to which the calculation progress will be passed as
            an integer between 0 and 100.
        """
        # TODO: allow to cut away regions
        if not any([self.center, center]):
            raise RuntimeError(
                "Center attribute must be either saved in the dataset \
                                as an attribute or be provided.")

        if callback is None:
            callback = lambda i: None

        if center is not None:
            self.center = center

        # Because it is difficult to know the angular averaged data's shape in advance,
        # we calculate it first and store it next
        callback(0)
        results = list()
        for index, timedelay in enumerate(self.time_points):
            px_radius, avg = azimuthal_average(
                self.diff_data(timedelay),
                center=self.center,
                mask=self.valid_mask,
                angular_bounds=angular_bounds,
                trim=False,
            )

            # px_radius is not stored but used once
            results.append(avg)
            callback(int(100 * index / len(self.time_points)))

        # Concatenate arrays for intensity and error
        # If trimming is enabled, there might be a problem where
        # different averages are trimmed to different length
        # therefore, we trim to the most restrictive bounds
        if trim:
            bounds = [_trim_bounds(I) for I in results]
            min_bound = max(min(bound) for bound in bounds)
            max_bound = min(max(bound) for bound in bounds)
            results = [I[min_bound:max_bound] for I in results]
            px_radius = px_radius[min_bound:max_bound]

        rintensity = np.stack(results, axis=0)

        if normalized:
            rintensity /= np.sum(rintensity, axis=1, keepdims=True)

        # We allow resizing. In theory, an angular averave could never be
        # longer than the diagonal of resolution
        self.powder_group["intensity"].resize(rintensity.shape)
        self.powder_group["intensity"].write_direct(rintensity)

        self.powder_group["px_radius"].resize(px_radius.shape)
        self.powder_group["px_radius"].write_direct(px_radius)

        # Use px_radius as placeholder for scattering_vector until calibration
        self.powder_group["scattering_vector"].resize(px_radius.shape)
        self.powder_group["scattering_vector"].write_direct(px_radius)

        self.powder_group["baseline"].resize(rintensity.shape)
        self.powder_group["baseline"].write_direct(np.zeros_like(rintensity))

        self.powder_eq.cache_clear()
        callback(100)