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

[Bug]: IndexSeries: Linked Images has no parent. #1884

Closed
3 tasks done
zhiwen10 opened this issue Apr 3, 2024 · 3 comments
Closed
3 tasks done

[Bug]: IndexSeries: Linked Images has no parent. #1884

zhiwen10 opened this issue Apr 3, 2024 · 3 comments

Comments

@zhiwen10
Copy link

zhiwen10 commented Apr 3, 2024

What happened?

I'm adding image indexSeries to existing NWB files, like example here.

However, I'm getting an error message below:
OrphanContainerBuildError: stimuli (stimuli): Linked Images 'images_name' has no parent. Remove the link or ensure the linked container is added properly.

Mostly likely a bug in my own code, since I'm new to this. Any ideas?

Steps to Reproduce

# open the NWB file in r+ mode and add manip trigger to acquisition and stim1, stim2 to stimulus
from PIL import Image
import matplotlib.pyplot as plt

img0 = Image.open(r"E:\DANDI\image_series\natural_scene_0.tiff") 
img1 = Image.open(r"E:\DANDI\image_series\natural_scene_1.tiff") 
image0 = GrayscaleImage(name='natural_scene_0', data= np.array(img0));
image1 = GrayscaleImage(name='natural_scene_1', data= np.array(img1));
img_sequence = [image0,image1]

# plt.imshow(img1,cmap = 'gray')
images = Images(
    name="images_name", 
    images = img_sequence,
    description="images description",
    order_of_images = ImageReferences("order_of_images", img_sequence),
)

idx_series = IndexSeries(name="stimuli",data=[0, 1, 0, 1],indexed_images=images,unit="N/A",timestamps=[0.1, 0.2, 0.3, 0.4])

with NWBHDF5IO(filename, "r+") as io:
    read_nwbfile = io.read()
    read_nwbfile.add_stimulus(idx_series)

    # write the modified NWB file
    io.write(read_nwbfile)


### Traceback

```python
---------------------------------------------------------------------------
OrphanContainerBuildError                 Traceback (most recent call last)
Cell In[53], line 9
      7 read_nwbfile.add_stimulus(idx_series)
      8 # write the modified NWB file
----> 9 io.write(read_nwbfile)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\utils.py:668, in docval.<locals>.dec.<locals>.func_call(*args, **kwargs)
    666 def func_call(*args, **kwargs):
    667     pargs = _check_args(args, kwargs)
--> 668     return func(args[0], **pargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\backends\hdf5\h5tools.py:377, in HDF5IO.write(self, **kwargs)
    372     raise UnsupportedOperation(("Cannot write to file %s in mode '%s'. "
    373                                 "Please use mode 'r+', 'w', 'w-', 'x', or 'a'")
    374                                % (self.source, self.__mode))
    376 cache_spec = popargs('cache_spec', kwargs)
--> 377 super().write(**kwargs)
    378 if cache_spec:
    379     self.__cache_spec()

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\utils.py:668, in docval.<locals>.dec.<locals>.func_call(*args, **kwargs)
    666 def func_call(*args, **kwargs):
    667     pargs = _check_args(args, kwargs)
--> 668     return func(args[0], **pargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\backends\io.py:98, in HDMFIO.write(self, **kwargs)
     95     herd.to_zip(path=self.herd_path)
     97 """Write a container to the IO source."""
---> 98 f_builder = self.__manager.build(container, source=self.__source, root=True)
     99 self.write_builder(f_builder, **kwargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\utils.py:668, in docval.<locals>.dec.<locals>.func_call(*args, **kwargs)
    666 def func_call(*args, **kwargs):
    667     pargs = _check_args(args, kwargs)
--> 668     return func(args[0], **pargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\manager.py:180, in BuildManager.build(self, **kwargs)
    175 elif not self.__is_active_builder(result) and container.modified:
    176     # if builder was built on file read and is then modified (append mode), it needs to be rebuilt
    177     self.logger.debug("Rebuilding modified %s '%s' (source: %s, extended spec: %s)"
    178                       % (container.__class__.__name__, container.name,
    179                          repr(source), spec_ext is not None))
--> 180     result = self.__type_map.build(container, self, builder=result, source=source, spec_ext=spec_ext,
    181                                    export=export)
    182     self.logger.debug("Done rebuilding %s '%s'" % (container.__class__.__name__, container.name))
    183 else:

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\utils.py:668, in docval.<locals>.dec.<locals>.func_call(*args, **kwargs)
    666 def func_call(*args, **kwargs):
    667     pargs = _check_args(args, kwargs)
--> 668     return func(args[0], **pargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\manager.py:770, in TypeMap.build(self, **kwargs)
    768 if manager is None:
    769     manager = BuildManager(self)
--> 770 builder = obj_mapper.build(container, manager, builder=builder, source=source, spec_ext=spec_ext, export=export)
    772 # add additional attributes (namespace, data_type, object_id) to builder
    773 namespace, data_type = self.get_container_ns_dt(container)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\utils.py:668, in docval.<locals>.dec.<locals>.func_call(*args, **kwargs)
    666 def func_call(*args, **kwargs):
    667     pargs = _check_args(args, kwargs)
--> 668     return func(args[0], **pargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:717, in ObjectMapper.build(self, **kwargs)
    715         builder = GroupBuilder(name, parent=parent, source=source)
    716     self.__add_datasets(builder, self.__spec.datasets, container, manager, source, export)
--> 717     self.__add_groups(builder, self.__spec.groups, container, manager, source, export)
    718     self.__add_links(builder, self.__spec.links, container, manager, source, export)
    719 else:

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:1037, in ObjectMapper.__add_groups(self, builder, groups, container, build_manager, source, export)
   1035 self.__add_datasets(sub_builder, spec.datasets, container, build_manager, source, export)
   1036 self.__add_links(sub_builder, spec.links, container, build_manager, source, export)
-> 1037 self.__add_groups(sub_builder, spec.groups, container, build_manager, source, export)
   1038 empty = sub_builder.is_empty()
   1039 if not empty or (empty and spec.required):

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:1037, in ObjectMapper.__add_groups(self, builder, groups, container, build_manager, source, export)
   1035 self.__add_datasets(sub_builder, spec.datasets, container, build_manager, source, export)
   1036 self.__add_links(sub_builder, spec.links, container, build_manager, source, export)
-> 1037 self.__add_groups(sub_builder, spec.groups, container, build_manager, source, export)
   1038 empty = sub_builder.is_empty()
   1039 if not empty or (empty and spec.required):

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:1050, in ObjectMapper.__add_groups(self, builder, groups, container, build_manager, source, export)
   1048 self.__check_quantity(attr_value, spec, container)
   1049 if attr_value is not None:
-> 1050     self.__add_containers(builder, spec, attr_value, build_manager, source, container, export)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:1108, in ObjectMapper.__add_containers(self, builder, spec, value, build_manager, source, parent_container, export)
   1106 elif isinstance(value, list):
   1107     for container in value:
-> 1108         self.__add_containers(builder, spec, container, build_manager, source, parent_container, export)
   1109 else:  # pragma: no cover
   1110     msg = ("Received %s, expected AbstractContainer or a list of AbstractContainers."
   1111            % value.__class__.__name__)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:1070, in ObjectMapper.__add_containers(self, builder, spec, value, build_manager, source, parent_container, export)
   1068 self.logger.debug("    Building newly instantiated %s '%s'" % (value.__class__.__name__, value.name))
   1069 if isinstance(spec, BaseStorageSpec):
-> 1070     new_builder = build_manager.build(value, source=source, spec_ext=spec, export=export)
   1071 else:
   1072     new_builder = build_manager.build(value, source=source, export=export)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\utils.py:668, in docval.<locals>.dec.<locals>.func_call(*args, **kwargs)
    666 def func_call(*args, **kwargs):
    667     pargs = _check_args(args, kwargs)
--> 668     return func(args[0], **pargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\manager.py:171, in BuildManager.build(self, **kwargs)
    167             raise ValueError("Cannot change container_source once set: '%s' %s.%s"
    168                              % (container.name, container.__class__.__module__,
    169                                 container.__class__.__name__))
    170 # NOTE: if exporting, then existing cached builder will be ignored and overridden with new build result
--> 171 result = self.__type_map.build(container, self, source=source, spec_ext=spec_ext, export=export)
    172 self.prebuilt(container, result)
    173 self.__active_prebuilt(result)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\utils.py:668, in docval.<locals>.dec.<locals>.func_call(*args, **kwargs)
    666 def func_call(*args, **kwargs):
    667     pargs = _check_args(args, kwargs)
--> 668     return func(args[0], **pargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\manager.py:770, in TypeMap.build(self, **kwargs)
    768 if manager is None:
    769     manager = BuildManager(self)
--> 770 builder = obj_mapper.build(container, manager, builder=builder, source=source, spec_ext=spec_ext, export=export)
    772 # add additional attributes (namespace, data_type, object_id) to builder
    773 namespace, data_type = self.get_container_ns_dt(container)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\utils.py:668, in docval.<locals>.dec.<locals>.func_call(*args, **kwargs)
    666 def func_call(*args, **kwargs):
    667     pargs = _check_args(args, kwargs)
--> 668     return func(args[0], **pargs)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:718, in ObjectMapper.build(self, **kwargs)
    716     self.__add_datasets(builder, self.__spec.datasets, container, manager, source, export)
    717     self.__add_groups(builder, self.__spec.groups, container, manager, source, export)
--> 718     self.__add_links(builder, self.__spec.links, container, manager, source, export)
    719 else:
    720     if builder is None:

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:973, in ObjectMapper.__add_links(self, builder, links, container, build_manager, source, export)
    971     self.logger.debug("        Skipping link - no attribute value")
    972     continue
--> 973 self.__add_containers(builder, spec, attr_value, build_manager, source, container, export)

File E:\anaconda3\envs\pynwb\lib\site-packages\hdmf\build\objectmapper.py:1063, in ObjectMapper.__add_containers(self, builder, spec, value, build_manager, source, parent_container, export)
   1058 if value.parent is None:
   1059     if (value.container_source == parent_container.container_source or
   1060             build_manager.get_builder(value) is None):
   1061         # value was removed (or parent not set) and there is a link to it in same file
   1062         # or value was read from an external link
-> 1063         raise OrphanContainerBuildError(builder, value)
   1065 if value.modified or export:
   1066     # writing a newly instantiated container (modified is False only after read) or as if it is newly
   1067     # instantianted (export=True)
   1068     self.logger.debug("    Building newly instantiated %s '%s'" % (value.__class__.__name__, value.name))

OrphanContainerBuildError: stimuli (stimuli): Linked Images 'images_name' has no parent. Remove the link or ensure the linked container is added properly.

Operating System

Windows

Python Executable

Conda

Python Version

3.8

Package Versions

No response

Code of Conduct

@oruebel
Copy link
Contributor

oruebel commented Apr 4, 2024

At first glance, I think the images needs to be added to the NWB file as well. Can you try adding read_nwbfile.add_acquisition(images) to see if that fixes the issue? I.e., I think the code should look as follows:

with NWBHDF5IO(filename, "r+") as io:
    read_nwbfile = io.read()
    read_nwbfile.add_acquisition(images)
    read_nwbfile.add_stimulus(idx_series)

    # write the modified NWB file
    io.write(read_nwbfile)

@zhiwen10
Copy link
Author

zhiwen10 commented Apr 4, 2024

That worked!
For structural clarity, i did read_nwbfile.add_stimulus_template(images) instead, which also worked well.
Many thanks!

@oruebel
Copy link
Contributor

oruebel commented Apr 4, 2024

Glad that worked! I'm closing this issue for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants