From dd37b51c088894fa9436762ba428204b96cd3137 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 31 Jan 2025 12:37:05 +0000 Subject: [PATCH 1/8] BF:: copy+paste typo --- pyfeeds/evaluate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfeeds/evaluate.py b/pyfeeds/evaluate.py index 3f9bc3c..287a060 100644 --- a/pyfeeds/evaluate.py +++ b/pyfeeds/evaluate.py @@ -413,8 +413,8 @@ def loadImage(pyf, filename): return pyf.imageCache[filename] else: img = nib.load(filename) - if isinstance(image, nib.Nifti1Image): - data = np.asanyarray(image.dataobj) + if isinstance(img, nib.Nifti1Image): + data = np.asanyarray(img.dataobj) else: data = None return img, data -- GitLab From 4708e507ab1ebdb1476680ce75ea77f2cc9f822c Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 31 Jan 2025 12:37:17 +0000 Subject: [PATCH 2/8] MNT: Accept pathlib.Path, normalise to string for image cache keys. All other eval routines should already be compatible --- pyfeeds/imagecache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfeeds/imagecache.py b/pyfeeds/imagecache.py index aefca31..c101fc0 100644 --- a/pyfeeds/imagecache.py +++ b/pyfeeds/imagecache.py @@ -77,7 +77,7 @@ class ImageCache: returned value will be ``None``. """ - imagefile = op.realpath(op.abspath(imagefile)) + imagefile = str(op.realpath(op.abspath(imagefile))) image = self.__images.get(imagefile, None) if image is not None: @@ -108,7 +108,7 @@ class ImageCache: # it in this way because all keys # passed to __getitem__ get # transformed in the same way. - imagefile = op.realpath(op.abspath(imagefile)) + imagefile = str(op.realpath(op.abspath(imagefile))) imagesize = size(image) self.__images[ imagefile] = image, data self.__imagesizes[imagefile] = imagesize -- GitLab From 80c380c802b1b031badb8e25037d58ab11ab0a75 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 31 Jan 2025 12:38:02 +0000 Subject: [PATCH 3/8] RF: Adjust evalHeader so it compares all valid dimensions. Replace alldims arg with ndims for comparing 3D --- pyfeeds/evaluate.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/pyfeeds/evaluate.py b/pyfeeds/evaluate.py index 287a060..e4eb575 100644 --- a/pyfeeds/evaluate.py +++ b/pyfeeds/evaluate.py @@ -420,9 +420,11 @@ def loadImage(pyf, filename): return img, data -def evalHeader(testfile, benchmark, alldims=True, pyf=None): +def evalHeader(testfile, benchmark, ndims=None, pyf=None): """Evaluation routine which compares the header fields of two NIFTI - images. + images. By default the dim/pixdim values for every dimension are compared, + but the ``ndims`` argument can be used if you only want to compare the + first three dimensions for instance. Returns 0 if they all match, 1 otherwise. """ @@ -437,14 +439,17 @@ def evalHeader(testfile, benchmark, alldims=True, pyf=None): 'qoffset_x', 'qoffset_y', 'qoffset_z', 'srow_x', 'srow_y', 'srow_z'] + if ndims is None: + ndims = max(img1.header['dim'][0], + img2.header['dim'][0]) + for f in fields: f1 = hdr1[f] f2 = hdr2[f] - if (not alldims) and (f in ('dim', 'pixdim')): - ndim = img1.header['dim'][0] - f1 = f1[:ndim + 1] - f2 = f2[:ndim + 1] + if f in ('dim', 'pixdim'): + f1 = f1[:ndims + 1] + f2 = f2[:ndims + 1] if not np.all(np.isclose(f1, f2)): return 1 @@ -453,14 +458,8 @@ def evalHeader(testfile, benchmark, alldims=True, pyf=None): def evalHeaderRestrictDims(testfile, benchmark, pyf=None): - """Evaluation routine which compares the header fields of two NIFTI - images. For the `dim` and `pixdim` fields, only the entries which - are expected to be valid (e.g. `dim1`, `dim2`, and `dim3` for a 3D image) - are compared. - - Returns 0 if they all match, 1 otherwise. - """ - return evalHeader(testfile, benchmark, alldims=False, pyf=pyf) + """Legacy alias for ``evalHeader``. """ + return evalHeader(testfile, benchmark, pyf=pyf) def evalImage(testfile, benchmark, pyf=None): -- GitLab From c4c97158cfce152b644a0f78d906949d8f1140b1 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 31 Jan 2025 12:42:08 +0000 Subject: [PATCH 4/8] BF: Not passing pyfeeds object to loadImage --- pyfeeds/evaluate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfeeds/evaluate.py b/pyfeeds/evaluate.py index e4eb575..d7d8e6d 100644 --- a/pyfeeds/evaluate.py +++ b/pyfeeds/evaluate.py @@ -429,8 +429,8 @@ def evalHeader(testfile, benchmark, ndims=None, pyf=None): Returns 0 if they all match, 1 otherwise. """ - img1 = loadImage(testfile)[0] - img2 = loadImage(benchmark)[0] + img1 = loadImage(pyf, testfile)[0] + img2 = loadImage(pyf, benchmark)[0] hdr1 = img1.header hdr2 = img2.header fields = ['dim', 'pixdim', 'intent_code', -- GitLab From 596657c7a74cce30b2855a78de7e5c212158a682 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 31 Jan 2025 12:46:10 +0000 Subject: [PATCH 5/8] BF: Typos/mistakes in tempdir routine --- pyfeeds/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfeeds/common.py b/pyfeeds/common.py index 33a49f7..3f02e75 100644 --- a/pyfeeds/common.py +++ b/pyfeeds/common.py @@ -45,10 +45,10 @@ def tempdir(): prevdir = os.getcwd() - with tempfile.TemporaryDirectory(delete=True) as td: + with tempfile.TemporaryDirectory() as td: try: - os.chdir(testdir) - yield td.name + os.chdir(td) + yield td finally: os.chdir(prevdir) -- GitLab From bb5d2df9b3975dbfe9d5a46c8264af4adc353e16 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 31 Jan 2025 12:53:19 +0000 Subject: [PATCH 6/8] TEST: Tests are useful it turns out. Use pyfeeds.common.tempdir instead of pyfeeds.tests.tempdir --- pyfeeds/tests/__init__.py | 13 ---- pyfeeds/tests/test_config.py | 2 +- pyfeeds/tests/test_evaluate.py | 105 ++++++++++++++++++++++++++++++++- pyfeeds/tests/test_hashing.py | 3 +- pyfeeds/tests/test_testing.py | 3 +- 5 files changed, 109 insertions(+), 17 deletions(-) diff --git a/pyfeeds/tests/__init__.py b/pyfeeds/tests/__init__.py index 7feda33..a3ec65b 100644 --- a/pyfeeds/tests/__init__.py +++ b/pyfeeds/tests/__init__.py @@ -9,8 +9,6 @@ import os import sys import os.path as op -import contextlib -import tempfile from io import StringIO @@ -61,17 +59,6 @@ class CaptureStdout(object): return self.__mock_stderr.read() -@contextlib.contextmanager -def tempdir(): - prevdir = os.getcwd() - with tempfile.TemporaryDirectory() as td: - try: - os.chdir(td) - yield td - finally: - os.chdir(prevdir) - - def makepaths(paths): for path in paths: dirname = op.dirname(path) diff --git a/pyfeeds/tests/test_config.py b/pyfeeds/tests/test_config.py index 647201f..02811dc 100644 --- a/pyfeeds/tests/test_config.py +++ b/pyfeeds/tests/test_config.py @@ -13,7 +13,7 @@ from unittest import mock import pyfeeds.main as main -from pyfeeds.tests import tempdir +from pyfeeds.common import tempdir def test_loadPyfeedsConfig(): diff --git a/pyfeeds/tests/test_evaluate.py b/pyfeeds/tests/test_evaluate.py index 4c9b4a0..98ebaf9 100644 --- a/pyfeeds/tests/test_evaluate.py +++ b/pyfeeds/tests/test_evaluate.py @@ -11,9 +11,10 @@ import os.path as op import numpy as np import nibabel as nib -from . import tempdir, makepaths, maketest, makepyfeeds, CaptureStdout +from . import makepaths, maketest, makepyfeeds, CaptureStdout from pyfeeds import testing, evaluate +from pyfeeds.common import tempdir def test_evaluateTestAgainstBenchmark(): @@ -79,6 +80,9 @@ def test_evalVectorImage(): assert evaluate.evalVectorImage(fname1, fname1, pyf=pyf) == 0 assert evaluate.evalVectorImage(fname2, fname2, pyf=pyf) == 0 assert evaluate.evalVectorImage(fname1, fname2, pyf=pyf) != 0 + assert evaluate.evalVectorImage(fname1, fname1) == 0 + assert evaluate.evalVectorImage(fname2, fname2) == 0 + assert evaluate.evalVectorImage(fname1, fname2) != 0 def test_evalImage(): @@ -98,3 +102,102 @@ def test_evalImage(): assert evaluate.evalImage(fname1, fname1, pyf=pyf) == 0 assert evaluate.evalImage(fname2, fname2, pyf=pyf) == 0 assert evaluate.evalImage(fname1, fname2, pyf=pyf) != 0 + assert evaluate.evalImage(fname1, fname1) == 0 + assert evaluate.evalImage(fname2, fname2) == 0 + assert evaluate.evalImage(fname1, fname2) != 0 + + +def test_evalHeader(): + + arr1 = -1 + 2 * np.random.random((10, 10, 10, 10)) + arr2 = -1 + 2 * np.random.random((10, 10, 10, 10)) + arr3 = -1 + 2 * np.random.random((10, 10, 10, 20)) + arr4 = -1 + 2 * np.random.random(( 5, 5, 5, 20)) + + with tempdir(): + + pyf = makepyfeeds() + fname1 = 'image1.nii.gz' + fname2 = 'image2.nii.gz' + fname3 = 'image3.nii.gz' + fname4 = 'image4.nii.gz' + + nib.Nifti1Image(arr1, np.eye(4)).to_filename(fname1) + nib.Nifti1Image(arr2, np.eye(4)).to_filename(fname2) + nib.Nifti1Image(arr3, np.eye(4)).to_filename(fname3) + nib.Nifti1Image(arr4, np.eye(4)).to_filename(fname4) + + assert evaluate.evalHeader(fname1, fname1, pyf=pyf) == 0 + assert evaluate.evalHeader(fname2, fname2, pyf=pyf) == 0 + assert evaluate.evalHeader(fname3, fname3, pyf=pyf) == 0 + assert evaluate.evalHeader(fname4, fname4, pyf=pyf) == 0 + assert evaluate.evalHeader(fname1, fname1) == 0 + assert evaluate.evalHeader(fname2, fname2) == 0 + assert evaluate.evalHeader(fname3, fname3) == 0 + assert evaluate.evalHeader(fname4, fname4) == 0 + + assert evaluate.evalHeader(fname1, fname2) == 0 + assert evaluate.evalHeader(fname1, fname3) != 0 + assert evaluate.evalHeader(fname1, fname4) != 0 + assert evaluate.evalHeader(fname1, fname3, ndims=3) == 0 + assert evaluate.evalHeader(fname3, fname4) != 0 + assert evaluate.evalHeader(fname3, fname4, ndims=3) != 0 + + assert evaluate.evalHeaderRestrictDims(fname1, fname2, pyf=pyf) == 0 + assert evaluate.evalHeaderRestrictDims(fname1, fname3, pyf=pyf) != 0 + assert evaluate.evalHeaderRestrictDims(fname1, fname2) == 0 + assert evaluate.evalHeaderRestrictDims(fname1, fname3) != 0 + + +def test_evalImageMaxDiff(): + + arr1 = np.zeros((10, 10, 10)) + arr2 = np.zeros((10, 10, 10)) + + arr1[0, 0, 0] = 100 + + with tempdir(): + + pyf = makepyfeeds() + fname1 = 'image1.nii.gz' + fname2 = 'image2.nii.gz' + nib.Nifti1Image(arr1, np.eye(4)).to_filename(fname1) + nib.Nifti1Image(arr2, np.eye(4)).to_filename(fname2) + + assert evaluate.evalImageMaxDiff(fname1, fname1, pyf=pyf) == 0 + assert evaluate.evalImageMaxDiff(fname1, fname1) == 0 + assert evaluate.evalImageMaxDiff(fname1, fname2) == 100 + +def test_evalNumericalText(): + + arr1 = np.random.random(100) + arr2 = np.random.random(100) * 4 + + with tempdir(): + + pyf = makepyfeeds() + fname1 = 'data1.txt' + fname2 = 'data2.txt' + np.savetxt(fname1, arr1) + np.savetxt(fname2, arr2) + + assert evaluate.evalNumericalText(fname1, fname1, pyf=pyf) == 0 + assert evaluate.evalNumericalText(fname1, fname1) == 0 + assert evaluate.evalNumericalText(fname1, fname2) != 0 + +def test_evalMD5(): + + arr1 = np.random.random(100) + arr2 = np.random.random(100) * 4 + + with tempdir(): + + pyf = makepyfeeds() + fname1 = 'data1.txt' + fname2 = 'data2.txt' + np.savetxt(fname1, arr1) + np.savetxt(fname2, arr2) + + assert evaluate.evalMD5(fname1, fname1, pyf=pyf) == 0 + assert evaluate.evalMD5(fname1, fname1) == 0 + assert evaluate.evalMD5(fname1, fname2) != 0 diff --git a/pyfeeds/tests/test_hashing.py b/pyfeeds/tests/test_hashing.py index bb303a7..808cab8 100644 --- a/pyfeeds/tests/test_hashing.py +++ b/pyfeeds/tests/test_hashing.py @@ -13,8 +13,9 @@ import os.path as op import time from pyfeeds import hashing, testing +from pyfeeds.common import tempdir -from . import CaptureStdout, makepyfeeds, maketest, makepaths, tempdir +from . import CaptureStdout, makepyfeeds, maketest, makepaths def test_genHashes(): diff --git a/pyfeeds/tests/test_testing.py b/pyfeeds/tests/test_testing.py index 3dcadbb..2761aa0 100644 --- a/pyfeeds/tests/test_testing.py +++ b/pyfeeds/tests/test_testing.py @@ -10,9 +10,10 @@ import os import datetime import os.path as op -from . import tempdir, makepaths, maketest, makepyfeeds, CaptureStdout +from . import makepaths, maketest, makepyfeeds, CaptureStdout from pyfeeds import testing +from pyfeeds.common import tempdir def test_findTestDirs(): -- GitLab From 477f1738ba8c13f9e0d269fc180a64bcef7783b9 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 31 Jan 2025 12:55:12 +0000 Subject: [PATCH 7/8] DOC: Changelog --- CHANGELOG.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 66c835e..911b6fe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,16 @@ ===================== +0.14.1 (Friday 31st January 2025) +--------------------------------- + +* Fixed a few bugs introduced in the previous release. +* Adjusted the ``evalHeader`` routine - now it compares ``dim`` and ``pixdim`` + for all valid dimensions by default. The ``alldims`` parameter has been + replaced with a ``ndims`` parameter, allowing the number of dimensions to + be set (e.g. when you only want to compare the first three dimensions). + + 0.14.0 (Friday 31st January 2025) --------------------------------- -- GitLab From 1f1b86ea085b8f611bb253888289159d3ece51d2 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Fri, 31 Jan 2025 12:55:22 +0000 Subject: [PATCH 8/8] MNT: Version --- pyfeeds/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfeeds/__init__.py b/pyfeeds/__init__.py index 5f1fb6a..3171118 100644 --- a/pyfeeds/__init__.py +++ b/pyfeeds/__init__.py @@ -5,7 +5,7 @@ # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -__version__ = '0.14.0' +__version__ = '0.14.1' """The pyfeeds version number. """ -- GitLab