Skip to content

Commit 7d7b28a

Browse files
authored
Merge branch 'main' into 2644_automatic_dynamic_mask
2 parents ccd9226 + c18d2d6 commit 7d7b28a

File tree

6 files changed

+87
-39
lines changed

6 files changed

+87
-39
lines changed

src/pyFAI/benchmark/__init__.py

+19-8
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"Benchmark for Azimuthal integration of PyFAI"
2525

2626
__author__ = "Jérôme Kieffer"
27-
__date__ = "21/05/2024"
27+
__date__ = "11/03/2025"
2828
__license__ = "MIT"
2929
__copyright__ = "2012-2024 European Synchrotron Radiation Facility, Grenoble, France"
3030

@@ -256,6 +256,7 @@ def __init__(self, nbr=10, repeat=1, memprofile=False, unit="2th_deg", max_size=
256256
self.unit = unit
257257
self.out_2d = (500, 360)
258258
self.max_size = max_size or sys.maxunicode
259+
self.plot_y_range = [1, 1000]
259260

260261
def get_cpu(self):
261262
if self._cpu is None:
@@ -363,7 +364,7 @@ def bench_1d(self, method="splitBBox", check=False, opencl=None, function="integ
363364
device = ' '.join(str(ocl.platforms[platformid].devices[deviceid]).split())
364365
print("Working on device: %s platform: %s device: %s" % (devicetype, platform, device))
365366
# label = ("%s %s %s %s %s" % (function, devicetype, self.LABELS[method.method[1:4]], platform, device)).replace(" ", "_")
366-
label = f'{devicetype}:{platform}:{device} / {function}: {method.split_lower}_{method.algo_lower}_{method.impl_lower}'
367+
label = f'{devicetype}:{platform}:{device} / {function}: ({method.split_lower},{method.algo_lower},{method.impl_lower})'
367368
method = IntegrationMethod.select_method(dim=1, split=method.split_lower,
368369
algo=method.algo_lower, impl=method.impl_lower,
369370
target=(opencl["platformid"], opencl["deviceid"]))[0]
@@ -639,24 +640,25 @@ def init_curve(self):
639640
self.ax.set_yscale("log", base=2)
640641
except Exception:
641642
self.ax.set_yscale("log", basey=2)
642-
t = [0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000]
643+
t = [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
643644
self.ax.set_yticks([float(i) for i in t])
644645
self.ax.set_yticklabels([str(i)for i in t])
645646
self.ax.set_xlim(0.0, 20)
646-
self.ax.set_ylim(0.5, 1500)
647+
self.ax.set_ylim(0.75 * self.plot_y_range[0],
648+
1.5 * self.plot_y_range[1])
647649
self.ax.set_title(f'CPU: {self.get_cpu()}\nGPU: {self.get_gpu()}')
648650

649651
# Display detector markers (vertical lines)
650652
self.ax.vlines(
651653
x=data_sizes,
652-
ymin=self.ax.get_ylim()[0],
653-
ymax=self.ax.get_ylim()[1],
654+
ymin=0,
655+
ymax=100*self.plot_y_range[1],
654656
linestyles='dashed',
655657
alpha=0.4,
656658
colors='black',
657659
)
658660
for size, detector_label in zip(data_sizes, detector_names):
659-
self.ax.text(x=size, y=0.6, s=detector_label, rotation=270, fontsize=7)
661+
self.ax.text(x=size, y=0.8, s=detector_label, rotation=270, fontsize=7)
660662

661663
update_fig(self.fig)
662664

@@ -674,7 +676,11 @@ def new_curve(self, results, label, style="-", marker="x"):
674676
self.plot_x = list(results.keys())
675677
self.plot_x.sort()
676678
self.plot_y = [1000.0 / results[i] for i in self.plot_x]
679+
self.plot_y_range = [min(min(self.plot_y_range), min(self.plot_y)),
680+
max(max(self.plot_y_range), max(self.plot_y))]
677681
self.plot = self.ax.plot(self.plot_x, self.plot_y, marker + style, label=label)[0]
682+
self.ax.set_ylim(0.75 * self.plot_y_range[0],
683+
1.5 * self.plot_y_range[1])
678684

679685
handles, labels = self.ax.get_legend_handles_labels()
680686
self.ax.legend(
@@ -697,9 +703,14 @@ def new_point(self, size, exec_time):
697703
if not self.plot:
698704
return
699705

706+
y_value = 1000.0 / exec_time
700707
self.plot_x.append(size)
701-
self.plot_y.append(1000.0 / exec_time)
708+
self.plot_y.append(y_value)
702709
self.plot.set_data(self.plot_x, self.plot_y)
710+
self.plot_y_range = [min(self.plot_y_range[0], y_value),
711+
max(self.plot_y_range[1], y_value)]
712+
self.ax.set_ylim(0.75 * self.plot_y_range[0],
713+
1.5 * self.plot_y_range[1])
703714
update_fig(self.fig)
704715

705716
def display_all(self):

src/pyFAI/detectors/_xspectrum.py

+21-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Project: Fast Azimuthal integration
55
# https://github.com/silx-kit/pyFAI
66
#
7-
# Copyright (C) 2022-2024 European Synchrotron Radiation Facility, Grenoble, France
7+
# Copyright (C) 2022-2025 European Synchrotron Radiation Facility, Grenoble, France
88
#
99
# Principal author: Jérôme Kieffer ([email protected])
1010
#
@@ -34,7 +34,7 @@
3434
__contact__ = "[email protected]"
3535
__license__ = "MIT"
3636
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
37-
__date__ = "25/06/2024"
37+
__date__ = "20/02/2025"
3838
__status__ = "production"
3939

4040
import numpy
@@ -51,6 +51,7 @@ class _Lambda(Detector):
5151
# This detector does not exist but those are place-holder
5252
MODULE_SIZE = (256, 256)
5353
MODULE_GAP = (4, 4)
54+
DUMMY = 0
5455
force_pixel = True
5556

5657
def __init__(self, pixel1=55e-6, pixel2=55e-6, max_shape=None, module_size=None, orientation=0):
@@ -128,3 +129,21 @@ class Lambda10M(_Lambda):
128129
"""
129130
MAX_SHAPE = (2596, 4676)
130131
aliases = ["Lambda 10M"]
132+
133+
class Lambda9M(_Lambda):
134+
"""
135+
LAMBDA 9M detector
136+
"""
137+
MAX_SHAPE = (3868, 3227)
138+
aliases = ["Lambda 9M"]
139+
140+
def calc_mask(self):
141+
"""
142+
Returns a generic mask for module based detectors...
143+
"""
144+
if self.max_shape is None:
145+
raise NotImplementedError("Generic Lambda detector does not know"
146+
"its max size ...")
147+
mask = numpy.zeros(self.max_shape, dtype=numpy.int8)
148+
logger.warning("Lambda9M mask is detector specific, no pixel are actually masked")
149+
return mask

src/pyFAI/gui/pilx/MainWindow.py

+20-23
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@
4646
from silx.gui import qt
4747
from silx.gui.colors import Colormap
4848
from silx.image.marchingsquares import find_contours
49-
from silx.io.url import DataUrl
50-
from silx.io import get_data
5149
from silx.gui.plot.items.image import ImageBase
5250

5351
from .models import ImageIndices
@@ -56,17 +54,19 @@
5654
compute_radial_values,
5755
get_dataset,
5856
get_indices_from_values,
57+
get_mask_image,
5958
get_radial_dataset,
6059
)
6160
from .widgets.DiffractionImagePlotWidget import DiffractionImagePlotWidget
6261
from .widgets.IntegratedPatternPlotWidget import IntegratedPatternPlotWidget
6362
from .widgets.MapPlotWidget import MapPlotWidget
6463
from .widgets.TitleWidget import TitleWidget
6564
from ...io.integration_config import WorkerConfig
66-
from ...utils.mathutil import binning as rebin_fct
67-
from ...detectors import Detector
65+
from ...utils.mathutil import binning
66+
6867
logger = logging.getLogger(__name__)
6968

69+
7070
class MainWindow(qt.QMainWindow):
7171
sigFileChanged = qt.Signal(str)
7272

@@ -253,28 +253,25 @@ def getMask(self, image, maskfile=None):
253253
:return: 2D array
254254
"""
255255
if maskfile:
256-
mask_image = get_data(url=DataUrl(maskfile))
257-
if mask_image.shape != image.shape:
258-
binning = [m//i for i, m in zip(image.shape, mask_image.shape)]
259-
if min(binning)<1:
260-
mask_image = None
261-
else:
262-
mask_image = rebin_fct(mask_image, binning)
256+
mask_image = get_mask_image(maskfile, image.shape)
263257
else:
264258
mask_image = None
259+
265260
detector = self.worker_config.poni.detector
266-
if detector:
267-
detector_mask = detector.mask
268-
if detector.shape != image.shape:
269-
detector.guess_binning(image)
270-
detector_mask = rebin_fct(detector_mask, detector.binning)
271-
if mask_image is None:
272-
mask_image = detector_mask
273-
else:
274-
numpy.logical_or(mask_image, detector_mask, out=mask_image)
275-
detector.mask = mask_image
276-
mask_image = detector.dynamic_mask(image)
277-
return mask_image
261+
if not detector:
262+
return mask_image
263+
264+
detector_mask = detector.mask
265+
if detector.shape != image.shape:
266+
detector.guess_binning(image)
267+
detector_mask = binning(detector_mask, detector.binning)
268+
269+
if mask_image is None:
270+
detector.mask = detector_mask
271+
else:
272+
detector.mask = numpy.logical_or(mask_image, detector_mask)
273+
274+
return detector.dynamic_mask(image)
278275

279276
def displayImageAtIndices(self, indices: ImageIndices):
280277
if self._file_name is None:

src/pyFAI/gui/pilx/utils.py

+24-4
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,28 @@
2828

2929
"""Tool to visualize diffraction maps."""
3030
from __future__ import annotations
31+
3132
__author__ = "Loïc Huder"
3233
__contact__ = "[email protected]"
3334
__license__ = "MIT"
3435
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
3536
__date__ = "19/02/2025"
3637
__status__ = "development"
3738

38-
from typing import Iterable, Optional
3939
import logging
40+
from typing import Iterable, Optional, Tuple
41+
4042
logger = logging.getLogger(__name__)
41-
import json
43+
import os.path
44+
4245
import h5py
4346
import numpy
44-
import os.path
47+
from silx.io import get_data
48+
from silx.io.url import DataUrl
49+
4550
from ...integrator.azimuthal import AzimuthalIntegrator
46-
from ...detectors import Detector
4751
from ...io.integration_config import WorkerConfig
52+
from ...utils.mathutil import binning
4853

4954

5055
def compute_radial_values(worker_config: WorkerConfig) -> numpy.ndarray:
@@ -115,3 +120,18 @@ def guess_axis_path(existing_axis_path: str, parent: h5py.Group) -> str | None:
115120
return guessed_axis_path
116121

117122
return None
123+
124+
125+
def get_mask_image(maskfile: str, image_shape: Tuple[int, int]) -> numpy.ndarray | None:
126+
"""Retrieves mask image from the URL. Rebin to match"""
127+
mask_image = get_data(url=DataUrl(maskfile))
128+
assert isinstance(mask_image, numpy.ndarray)
129+
if mask_image.shape == image_shape:
130+
return mask_image
131+
132+
# If mismatched shapes, try to rebin
133+
bin_size = [m // i for i, m in zip(image_shape, mask_image.shape)]
134+
if bin_size[0] == 0 or bin_size[1] == 0:
135+
return None
136+
137+
return binning(mask_image, bin_size)

src/pyFAI/integrator/azimuthal.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1590,7 +1590,7 @@ def medfilt1d_ng(self, data,
15901590

15911591
intpl = integr.medfilt(data, **kwargs)
15921592
else:
1593-
raise RuntimeError("Not yet implemented. Sorry")
1593+
raise RuntimeError(f"Method {method} is not yet implemented. Please report an issue on https://github.com/silx-kit/pyFAI/issues/new")
15941594
result = Integrate1dResult(intpl.position * unit.scale, intpl.intensity, intpl.sem)
15951595
result._set_method_called("sigma_clip_ng")
15961596
result._set_method(method)

src/pyFAI/test/test_azimuthal_integrator.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
__contact__ = "[email protected]"
3434
__license__ = "MIT"
3535
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
36-
__date__ = "29/01/2025"
36+
__date__ = "11/03/2025"
3737

3838
import unittest
3939
import os
@@ -304,6 +304,7 @@ def test_medfilt1d(self):
304304
self.assertLess(rwp, 0.1, "Rwp trimmed-mean Cython/OpenCL: %.3f" % rwp_ocl)
305305
ref = ocl = pyt = rwp = rwp_ocl = rwp_pyt = None
306306

307+
@unittest.skipIf(UtilsTest.low_mem, "test using >100Mb")
307308
def test_radial(self):
308309
"Non regression for #1602"
309310
res = self.ai.integrate_radial(self.data, npt=360, npt_rad=10,

0 commit comments

Comments
 (0)