Skip to content

azint

AzimuthalIntegrator

This class is an azimuthal integrator

Source code in azint/azint.py
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
class AzimuthalIntegrator():
    """
    This class is an azimuthal integrator 
    """
    def __init__(self,
                 poni: Union[str, Poni],
                 n_splitting: int, 
                 radial_bins: Union[int, Sequence],
                 azimuth_bins: Optional[Union[int, Sequence]] = None,
                 unit: str = 'q',
                 mask: np.ndarray = None, 
                 solid_angle: bool = True,
                 polarization_factor: Optional[float] = None,
                 error_model: Optional[str] = None):
        """
        Args:
            poni: Name of Poni file or instance of Poni
            n_splitting: Each pixel in the image gets split into (n, n) subpixels that get binned individually
            radial_bins: radial bins can either be number of bins or a sequence defining the bin edges in Angstrom^-1.
            azimuth_bins: azimthual bins can either be number of bins or a sequence defining the bin edges between [0, 360] degrees.
            unit: Ouput units for the radial coordindate
            mask: Pixel mask to exclude bad pixels. Pixels marked with 1 will be excluded
            solid_angle: Perform solid angle correction
            polarization_factor: Polarization factor for the polarization correction
                1 (linear horizontal polarization)
                -1 (linear vertical polarization)
            error_model: Error model used to calculate errors in the transformation. Only options is 'poisson' error model

        Attributes:
            radial_axis (ndarray): radial axis depeding on units in q or 2theta
            azimuth_axis (ndarray, optional): azimuth axis in degrees is case of 2D integration
        """

        if error_model and error_model != 'poisson':
            raise RuntimeError('Only poisson error model is supported')

        if unit not in ('q', '2th'):
            raise RuntimeError('Wrong radial unit. Allowed units: q, 2th')

        if isinstance(poni, str):
            poni = Poni.from_file(poni)

        self.unit = unit
        self.error_model = error_model

        pixel_centers = np.mean(poni.det.pixel_corners, axis=2)
        p1 = pixel_centers[..., 1] - poni.poni1
        p2 = pixel_centers[..., 2] - poni.poni2
        p3 = pixel_centers[..., 0] + poni.dist
        tth, phi = transform(poni, p1, p2, p3)

        radial_bins = setup_radial_bins(poni, radial_bins, unit, tth)
        self.radial_axis = 0.5*(radial_bins[1:] + radial_bins[:-1])
        azimuth_bins = setup_azimuth_bins(azimuth_bins)
        self.azimuth_axis = 0.5*(azimuth_bins[1:] + azimuth_bins[:-1]) if azimuth_bins is not None else None

        shape = pixel_centers.shape[:2]
        self.input_size = np.prod(shape)
        if mask is None:
            mask = np.zeros(shape, dtype=np.int8)

        if azimuth_bins is None:
            self.output_shape = [len(radial_bins) - 1]
        else:
            self.output_shape = [len(azimuth_bins) - 1, len(radial_bins) - 1]

        self.sparse_matrix = Sparse(poni, poni.det.pixel_corners, n_splitting, 
                                    mask, unit, radial_bins, azimuth_bins)
        self.norm = self.sparse_matrix.spmv(np.ones(shape[0]*shape[1], dtype=np.float32))
        corrections = setup_corrections(poni, solid_angle, polarization_factor, p1, p2, tth, phi)
        self.sparse_matrix.set_correction(corrections)


    def integrate(self, 
                  img: np.ndarray, 
                  mask: Optional[np.ndarray] = None,
                  normalized: Optional[bool] = True) -> tuple[np.ndarray, np.ndarray, Optional[np.ndarray]]:
        """
        Calculates the azimuthal integration of the input image

        Args:
            img: Input image to be integrated
            mask: Optional pixel mask to exclude bad pixels. Note if mask is constant using the mask argument in
                the constructor is more efficient
            normalized: Whether to return the normalized result or the integrated signal and norm separately

        Returns:
            azimuthal integrated image or just integrated signal if normalized is True
            standard error of the mean (SEM) when error_model is specified else None
            the norm if normalized is False
        """
        img = np.ascontiguousarray(img)

        if img.size != self.input_size:
            raise RuntimeError('Size of image is wrong!\nExpected %d\nActual size %d' %(self.input_size, img.size))
        if mask is None:
            norm = self.norm
        else:
            inverted_mask = 1 - mask
            img = img*inverted_mask
            norm = self.sparse_matrix.spmv(inverted_mask.reshape(-1))

        signal = self.sparse_matrix.spmv_corrected(img).reshape(self.output_shape)
        norm = norm.reshape(self.output_shape)

        errors = None
        if self.error_model:
            # poisson error model
            errors = np.sqrt(self.sparse_matrix.spmv_corrected2(img)).reshape(self.output_shape)
            if normalized:
                errors = np.divide(errors, norm, out=np.zeros_like(errors), where=norm!=0.0)

        if normalized:
            result = np.divide(signal, norm, out=np.zeros_like(signal), where=norm!=0.0)
            return result, errors
        else:
            return signal, errors, norm

__init__(poni, n_splitting, radial_bins, azimuth_bins=None, unit='q', mask=None, solid_angle=True, polarization_factor=None, error_model=None)

Parameters:

Name Type Description Default
poni Union[str, Poni]

Name of Poni file or instance of Poni

required
n_splitting int

Each pixel in the image gets split into (n, n) subpixels that get binned individually

required
radial_bins Union[int, Sequence]

radial bins can either be number of bins or a sequence defining the bin edges in Angstrom^-1.

required
azimuth_bins Optional[Union[int, Sequence]]

azimthual bins can either be number of bins or a sequence defining the bin edges between [0, 360] degrees.

None
unit str

Ouput units for the radial coordindate

'q'
mask ndarray

Pixel mask to exclude bad pixels. Pixels marked with 1 will be excluded

None
solid_angle bool

Perform solid angle correction

True
polarization_factor Optional[float]

Polarization factor for the polarization correction 1 (linear horizontal polarization) -1 (linear vertical polarization)

None
error_model Optional[str]

Error model used to calculate errors in the transformation. Only options is 'poisson' error model

None

Attributes:

Name Type Description
radial_axis ndarray

radial axis depeding on units in q or 2theta

azimuth_axis ndarray

azimuth axis in degrees is case of 2D integration

Source code in azint/azint.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def __init__(self,
             poni: Union[str, Poni],
             n_splitting: int, 
             radial_bins: Union[int, Sequence],
             azimuth_bins: Optional[Union[int, Sequence]] = None,
             unit: str = 'q',
             mask: np.ndarray = None, 
             solid_angle: bool = True,
             polarization_factor: Optional[float] = None,
             error_model: Optional[str] = None):
    """
    Args:
        poni: Name of Poni file or instance of Poni
        n_splitting: Each pixel in the image gets split into (n, n) subpixels that get binned individually
        radial_bins: radial bins can either be number of bins or a sequence defining the bin edges in Angstrom^-1.
        azimuth_bins: azimthual bins can either be number of bins or a sequence defining the bin edges between [0, 360] degrees.
        unit: Ouput units for the radial coordindate
        mask: Pixel mask to exclude bad pixels. Pixels marked with 1 will be excluded
        solid_angle: Perform solid angle correction
        polarization_factor: Polarization factor for the polarization correction
            1 (linear horizontal polarization)
            -1 (linear vertical polarization)
        error_model: Error model used to calculate errors in the transformation. Only options is 'poisson' error model

    Attributes:
        radial_axis (ndarray): radial axis depeding on units in q or 2theta
        azimuth_axis (ndarray, optional): azimuth axis in degrees is case of 2D integration
    """

    if error_model and error_model != 'poisson':
        raise RuntimeError('Only poisson error model is supported')

    if unit not in ('q', '2th'):
        raise RuntimeError('Wrong radial unit. Allowed units: q, 2th')

    if isinstance(poni, str):
        poni = Poni.from_file(poni)

    self.unit = unit
    self.error_model = error_model

    pixel_centers = np.mean(poni.det.pixel_corners, axis=2)
    p1 = pixel_centers[..., 1] - poni.poni1
    p2 = pixel_centers[..., 2] - poni.poni2
    p3 = pixel_centers[..., 0] + poni.dist
    tth, phi = transform(poni, p1, p2, p3)

    radial_bins = setup_radial_bins(poni, radial_bins, unit, tth)
    self.radial_axis = 0.5*(radial_bins[1:] + radial_bins[:-1])
    azimuth_bins = setup_azimuth_bins(azimuth_bins)
    self.azimuth_axis = 0.5*(azimuth_bins[1:] + azimuth_bins[:-1]) if azimuth_bins is not None else None

    shape = pixel_centers.shape[:2]
    self.input_size = np.prod(shape)
    if mask is None:
        mask = np.zeros(shape, dtype=np.int8)

    if azimuth_bins is None:
        self.output_shape = [len(radial_bins) - 1]
    else:
        self.output_shape = [len(azimuth_bins) - 1, len(radial_bins) - 1]

    self.sparse_matrix = Sparse(poni, poni.det.pixel_corners, n_splitting, 
                                mask, unit, radial_bins, azimuth_bins)
    self.norm = self.sparse_matrix.spmv(np.ones(shape[0]*shape[1], dtype=np.float32))
    corrections = setup_corrections(poni, solid_angle, polarization_factor, p1, p2, tth, phi)
    self.sparse_matrix.set_correction(corrections)

integrate(img, mask=None, normalized=True)

Calculates the azimuthal integration of the input image

Parameters:

Name Type Description Default
img ndarray

Input image to be integrated

required
mask Optional[ndarray]

Optional pixel mask to exclude bad pixels. Note if mask is constant using the mask argument in the constructor is more efficient

None
normalized Optional[bool]

Whether to return the normalized result or the integrated signal and norm separately

True

Returns:

Type Description
ndarray

azimuthal integrated image or just integrated signal if normalized is True

ndarray

standard error of the mean (SEM) when error_model is specified else None

Optional[ndarray]

the norm if normalized is False

Source code in azint/azint.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def integrate(self, 
              img: np.ndarray, 
              mask: Optional[np.ndarray] = None,
              normalized: Optional[bool] = True) -> tuple[np.ndarray, np.ndarray, Optional[np.ndarray]]:
    """
    Calculates the azimuthal integration of the input image

    Args:
        img: Input image to be integrated
        mask: Optional pixel mask to exclude bad pixels. Note if mask is constant using the mask argument in
            the constructor is more efficient
        normalized: Whether to return the normalized result or the integrated signal and norm separately

    Returns:
        azimuthal integrated image or just integrated signal if normalized is True
        standard error of the mean (SEM) when error_model is specified else None
        the norm if normalized is False
    """
    img = np.ascontiguousarray(img)

    if img.size != self.input_size:
        raise RuntimeError('Size of image is wrong!\nExpected %d\nActual size %d' %(self.input_size, img.size))
    if mask is None:
        norm = self.norm
    else:
        inverted_mask = 1 - mask
        img = img*inverted_mask
        norm = self.sparse_matrix.spmv(inverted_mask.reshape(-1))

    signal = self.sparse_matrix.spmv_corrected(img).reshape(self.output_shape)
    norm = norm.reshape(self.output_shape)

    errors = None
    if self.error_model:
        # poisson error model
        errors = np.sqrt(self.sparse_matrix.spmv_corrected2(img)).reshape(self.output_shape)
        if normalized:
            errors = np.divide(errors, norm, out=np.zeros_like(errors), where=norm!=0.0)

    if normalized:
        result = np.divide(signal, norm, out=np.zeros_like(signal), where=norm!=0.0)
        return result, errors
    else:
        return signal, errors, norm