diff --git a/src/pyvale/dic/_dicchecks.py b/src/pyvale/dic/_dicchecks.py index b8a50632..5333274f 100644 --- a/src/pyvale/dic/_dicchecks.py +++ b/src/pyvale/dic/_dicchecks.py @@ -87,9 +87,17 @@ def _multiwindow_init(subset_size: int, f"{multiwindow_search_area[i]} at index {i}") overlap = [x * (1.0-multiwindow_overlap) for x in multiwindow_subset_size] + configured_overlap = list(map(int, overlap)) overlap.append(subset_step) overlap = list(map(int,overlap)) + # Avoid running the same final FFT level twice when size and overlap are identical + if (multiwindow_subset_size and + multiwindow_subset_size[-1] == subset_size and + multiwindow_search_area[-1] == subset_size and + configured_overlap[-1] == subset_step): + return configured_overlap, multiwindow_subset_size, multiwindow_search_area + multiwindow_subset_size.append(subset_size) multiwindow_search_area.append(subset_size) @@ -245,7 +253,9 @@ def _check_thresholds(threshold: float, raise ValueError("Optimizer precision must be a float strictly " "between 0 and 1.") -def _check_subsets(subset_size: int, subset_step: int) -> None: +def _check_subsets(subset_size: int, + subset_step: int, + method: str | None = None) -> None: """ Parameters @@ -254,6 +264,9 @@ def _check_subsets(subset_size: int, subset_step: int) -> None: Threshold for the Levenberg optimization method. subset_step : int Threshold for the brute-force optimization method. + method : str, optional + Scan method. Even subset sizes are supported by the FFT-only + ``MULTIWINDOW`` method, but remain disallowed for the other methods. Raises ------ @@ -262,8 +275,8 @@ def _check_subsets(subset_size: int, subset_step: int) -> None: """ - # Enforce scalar types for non-FFT methods - if subset_size % 2 == 0: + # The FFT-only multiwindow path handles even-sized subsets explicitly. + if subset_size % 2 == 0 and method != "MULTIWINDOW": raise ValueError("subset_size must be an odd number.") # check if subset_step is larger than the subset_size diff --git a/src/pyvale/dic/dic2d.py b/src/pyvale/dic/dic2d.py index 235fcd09..b3f5c0dc 100644 --- a/src/pyvale/dic/dic2d.py +++ b/src/pyvale/dic/dic2d.py @@ -223,7 +223,7 @@ def calculate_2d(reference: np.ndarray | str | Path, dicchecks._check_thresholds(threshold, precision) common_util.check_output_directory(str(output_basepath), output_prefix, print_level) - dicchecks._check_subsets(subset_size, subset_step) + dicchecks._check_subsets(subset_size, subset_step, method_enum.value) updated_seeds = dicchecks._check_and_update_rg_seed(seed, roi_mask, method_enum.value, w, h, subset_size, subset_step) num_params = dicchecks._check_shape_function(shape_function_enum) diff --git a/src/pyvale/dic/dic3d.py b/src/pyvale/dic/dic3d.py index 950bedff..8a87b2ae 100644 --- a/src/pyvale/dic/dic3d.py +++ b/src/pyvale/dic/dic3d.py @@ -78,7 +78,9 @@ def calculate_3d(reference: list[np.ndarray] | list[str] | list[Path], the existing flat format ``[x0, y0, x1, y1, ...]`` or tuple format ``[(x0, y0), (x1, y1), ...]``. If the method is not RG, this will be ignored. subset_size : int, optional - Size of the square subset window in pixels (default: 21). + Size of the square subset window in pixels (default: 21). Must be odd + except for the FFT-only ``"MULTIWINDOW"`` method, which also supports + even sizes. subset_step : int, optional Step size between subset centers in pixels (default: 10). correlation_criteria : str, optional @@ -245,7 +247,7 @@ def calculate_3d(reference: list[np.ndarray] | list[str] | list[Path], # checks on the config dicchecks._check_thresholds(threshold, precision) common_util.check_output_directory(str(output_basepath), output_prefix, print_level) - dicchecks._check_subsets(subset_size, subset_step) + dicchecks._check_subsets(subset_size, subset_step, method_enum.value) updated_seeds = dicchecks._check_and_update_rg_seed(seed, roi_mask, method_enum.value, w0, h0, subset_size, subset_step) num_params = dicchecks._check_shape_function(shape_function_enum) diff --git a/tests/dic/test_dic.py b/tests/dic/test_dic.py index c1baa6e4..a4da1d9d 100644 --- a/tests/dic/test_dic.py +++ b/tests/dic/test_dic.py @@ -466,8 +466,8 @@ def test_2d_multiwindow_fft_large(dic_temp_dir: Path) -> None: deformed=def_large, roi_mask=roi_rigid.mask, seed=seed, - subset_size=31, - subset_step=15, + subset_size=32, + subset_step=16, max_displacement=100, correlation_criteria="ZNSSD", shape_function="RIGID",