diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 863b2b118050e53eb797e5ea2d9d6efa16d4562b..80d995e24115061e88a04fac08285b7e995c706f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -54,14 +54,6 @@ variables: - bash ./.ci/unit_tests.sh -test:3.8: - image: python:3.8 - <<: *test_template - -test:3.9: - image: python:3.9 - <<: *test_template - test:3.10: image: python:3.10 <<: *test_template @@ -69,3 +61,11 @@ test:3.10: test:3.11: image: python:3.11 <<: *test_template + +test:3.12: + image: python:3.12 + <<: *test_template + +test:3.13: + image: python:3.13 + <<: *test_template diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2f22513656f4848cde1746f1c5554dd847affe9b..06757401208da409e58ac6ac9fd66b85db9ab692 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,17 @@ ===================== +0.13.0 (Tuesday 17th December 2024) +----------------------------------- + + +* Pyfeeds now ignores any tests found within the input data, benchmark data, + or output directories, in case any of those are contained within the + specified test directories. +* New `evalImageMaxDiff` evaluation routine, which evaluates an image based + on the maximum absolute difference to the benchmark image. + + 0.12.4 (Thursday 18th January 2024) ----------------------------------- diff --git a/pyfeeds/__init__.py b/pyfeeds/__init__.py index 89b3a488b78523f89a422e428fb9e72c0949131d..ef100b9277cd791d474a8e70ddb6324728a9eaf7 100644 --- a/pyfeeds/__init__.py +++ b/pyfeeds/__init__.py @@ -5,7 +5,7 @@ # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -__version__ = '0.12.4' +__version__ = '0.13.0' """The pyfeeds version number. """ diff --git a/pyfeeds/evaluate.py b/pyfeeds/evaluate.py index b40f108cbe3108c720f19fb254815721ed6d0866..56dec2f1a86bcee73c71adef78e7a4dd2813d6dc 100644 --- a/pyfeeds/evaluate.py +++ b/pyfeeds/evaluate.py @@ -436,12 +436,25 @@ def evalHeaderRestrictDims(pyf, testfile, benchmark): def evalImage(pyf, testfile, benchmark): """Evaluation routine which compares the data from two NIFTI images. + Returns the mean difference between the two images, normalised by + the combined data range. The :func:`cmpArrays` function does the calculation. """ data1 = pyf.imageCache[testfile][1] data2 = pyf.imageCache[benchmark][1] - return cmpArrays(data1, data2) + return cmpArrays(data1, data2, metric='mean') + + +def evalImageMaxDiff(pyf, testfile, benchmark): + """Evaluation routine which compares the data from two NIFTI images. + Returns the maximum absolute difference between the two images. + + The :func:`cmpArrays` function does the calculation. + """ + data1 = pyf.imageCache[testfile][1] + data2 = pyf.imageCache[benchmark][1] + return cmpArrays(data1, data2, metric='max') def evalNumericalText(pyf, testfile, benchmark): @@ -564,10 +577,10 @@ def evalGiftiVertices(pyf, testfile, benchmark): return cmpArrays(verts1, verts2) -def cmpArrays(arr1, arr2): +def cmpArrays(arr1, arr2, metric='mean'): """Compares the values in the given ``numpy`` arrays. - Returns the mean difference between the two arrays, normalised + Returns the mean or max difference between the two arrays, normalised by the combined data range of the two arrays. """ @@ -607,10 +620,13 @@ def cmpArrays(arr1, arr2): if denom == 0: return 0 - normdiff = np.abs((arr2 - arr1) / denom) + if metric == 'mean': + normdiff = np.abs((arr2 - arr1) / denom) + # The final error is the mean error across all voxels + return normdiff.mean() - # The final error is the mean error across all voxels - return normdiff.mean() + elif metric == 'max': + return np.max(np.abs(arr2 - arr1)) def cmpVectorArrays(arr1, arr2): diff --git a/pyfeeds/main.py b/pyfeeds/main.py index 659985356a3033aa20950abb31b87be9785bc3ec..043b4136c7e1d936330247cd0cf01bd97b16167c 100755 --- a/pyfeeds/main.py +++ b/pyfeeds/main.py @@ -62,6 +62,7 @@ upon which sub-command the user has specified: import os.path as op import os +import pathlib as pl import sys import logging import fnmatch @@ -226,10 +227,23 @@ class Pyfeeds: testDirs = functools.reduce(lambda a, b: a + b, testDirs) testDirs = sorted(set(testDirs)) + testDirs = [op.abspath(td) for td in testDirs] if len(testDirs) == 0: return [] + # Do not consider anything within the + # input, benchmark, or output directories + def filterOut(dirs, filterDir): + if filterDir is None: + return dirs + fdir = pl.Path(op.abspath(filterDir)) + return [td for td in dirs if fdir not in pl.Path(td).parents] + + testDirs = filterOut(testDirs, self.outputDir) + testDirs = filterOut(testDirs, self.benchmarkDir) + testDirs = filterOut(testDirs, self.inputDir) + # Find the deepest directory in # the file system which contains # all of the detected tests.