Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot copy subclass of Subject with keyword arguments #1181

Open
1 task done
c-winder opened this issue Jun 21, 2024 · 2 comments · May be fixed by #1186
Open
1 task done

Cannot copy subclass of Subject with keyword arguments #1181

c-winder opened this issue Jun 21, 2024 · 2 comments · May be fixed by #1186
Labels
bug Something isn't working

Comments

@c-winder
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Bug summary

A subclass of Subject with keyword arguments cannot be copied with copy.copy() which is used in transform.py. Raises TypeError: A subject without images cannot be created.

Code for reproduction

import copy
import torchio as tio


class Subject(tio.Subject):

    def __init__(self, name: str, **kwargs):
        kwargs['name'] = name
        super().__init__(**kwargs)


colin = tio.datasets.Colin27()
subject = Subject(name='colin', **colin)
copy.copy(subject)

Actual outcome

Raises TypeError: A subject without images cannot be created.

The subclass cannot be copied as _subject_copy_helper in subject.py calls new = new_subj_cls(result_dict).

In the above example, result_dict = {'name:'colin', 'brain': ...} which means that name=result_dict on subclass recreation. This is the only parameter the subclass can see and hence it contains no images.

This can be fixed by changing _subject_copy_helper, to unpack result_dict as below, although I'm unsure if this is the best way of implementing the fix.

import copy
import torchio as tio
from typing import Any
from typing import Callable
from typing import Dict


class Subject(tio.Subject):

    def __init__(self, name: str, **kwargs):
        kwargs['name'] = name
        super().__init__(**kwargs)

    def __copy__(self):
        return _subject_copy_helper(self, type(self))


def _subject_copy_helper(
    old_obj: Subject,
    new_subj_cls: Callable[[Dict[str, Any]], Subject],
):
    result_dict = {}
    for key, value in old_obj.items():
        if isinstance(value, tio.Image):
            value = copy.copy(value)
        else:
            value = copy.deepcopy(value)
        result_dict[key] = value

    new = new_subj_cls(**result_dict)
    new.applied_transforms = old_obj.applied_transforms[:]
    return new


colin = tio.datasets.Colin27()
subject = Subject(name='colin', **colin)
copy.copy(subject)

Error messages

Traceback (most recent call last):
  File "D:\Programs\Anaconda\envs\body-comp\lib\site-packages\IPython\core\interactiveshell.py", line 3526, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-2-b4528fe5d1e0>", line 1, in <module>
    runfile('F:\\body-comp\\Playground\\test3.py', wdir='F:\\body-comp\\Playground')
  File "D:\Programs\PyCharm 2022.2\plugins\python\helpers\pydev\_pydev_bundle\pydev_umd.py", line 197, in runfile
    pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script
  File "D:\Programs\PyCharm 2022.2\plugins\python\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "F:\body-comp\Playground\test3.py", line 17, in <module>
    copy.copy(broken_subject)
  File "D:\Programs\Anaconda\envs\body-comp\lib\copy.py", line 84, in copy
    return copier(x)
  File "D:\Programs\Anaconda\envs\body-comp\lib\site-packages\torchio\data\subject.py", line 75, in __copy__
    return _subject_copy_helper(self, type(self))
  File "D:\Programs\Anaconda\envs\body-comp\lib\site-packages\torchio\data\subject.py", line 445, in _subject_copy_helper
    new = new_subj_cls(result_dict)
  File "F:\body-comp\Playground\test3.py", line 11, in __init__
    super().__init__(**kwargs)
  File "D:\Programs\Anaconda\envs\body-comp\lib\site-packages\torchio\data\subject.py", line 62, in __init__
    self._parse_images(self.get_images(intensity_only=False))
  File "D:\Programs\Anaconda\envs\body-comp\lib\site-packages\torchio\data\subject.py", line 101, in _parse_images
    raise TypeError('A subject without images cannot be created')
TypeError: A subject without images cannot be created

Expected outcome

Subclass should copy correctly.

System info

Platform:   Windows-10-10.0.19045-SP0
TorchIO:    0.19.7
PyTorch:    2.0.1
SimpleITK:  2.3.1 (ITK 5.3)
NumPy:      1.26.3
Python:     3.10.13 | packaged by Anaconda, Inc. | (main, Sep 11 2023, 13:24:38) [MSC v.1916 64 bit (AMD64)]
@c-winder c-winder added the bug Something isn't working label Jun 21, 2024
@fepegar
Copy link
Owner

fepegar commented Jun 28, 2024

Hi, @c-winder. Thanks for reporting. Would you be willing to contribute with a PR? We can start by adding your snippet in a unit test, and your fix would make the new test pass.

@c-winder
Copy link
Author

Sorry for the delay @fepegar, I've raised a PR which fixes the bug and extends test_copy_subclass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
2 participants