Search code examples
pythongeneratorvalueerrortrackpy

Trackpy tp.batch() gives generator already executing error


I am trying to track some particles in a video using trackpy. I'm following the walkthrough from the website: http://soft-matter.github.io/trackpy/v0.4.2/tutorial/walkthrough.html

After processing a few frames (usually around 14 frames, sometimes 0), it gives me a Value error that sais: "generator already executing"

I cannot figure out how to solve this issue, I hope someone does.

Python: 3.9.4 Trackpy: 0.5.0

The full error:

ValueError                                Traceback (most recent call last)
<ipython-input-8-ff6dcf7a7595> in <module>
----> 1 f = tp.batch(frames[100:300], masksize, minmass=minmass, invert=True);

~\.conda\envs\trackpyenv\lib\site-packages\trackpy\feature.py in batch(frames, diameter, output, meta, processes, after_locate, **kwargs)
    556         all_features = []
    557         for i, features in enumerate(map_func(curried_locate, frames)):
--> 558             image = frames[i]
    559             if hasattr(image, 'frame_no') and image.frame_no is not None:
    560                 frame_no = image.frame_no

~\.conda\envs\trackpyenv\lib\site-packages\slicerator\__init__.py in __getitem__(self, key)
    234         if not (isinstance(key, slice) or
    235                 isinstance(key, collections.Iterable)):
--> 236             return self._get(self._map_index(key))
    237         else:
    238             rel_indices, new_length = key_to_indices(key, len(self))

~\.conda\envs\trackpyenv\lib\site-packages\slicerator\__init__.py in _get(self, key)
    205 
    206     def _get(self, key):
--> 207         return self._ancestor[key]
    208 
    209     def _map_index(self, key):

~\.conda\envs\trackpyenv\lib\site-packages\slicerator\__init__.py in __getitem__(self, i)
    478         indices, new_length = key_to_indices(i, len(self))
    479         if new_length is None:
--> 480             return self._get(indices)
    481         else:
    482             return Slicerator(self, indices, new_length, self._propagate_attrs)

~\.conda\envs\trackpyenv\lib\site-packages\slicerator\__init__.py in _get(self, key)
    459         # We need to copy here: else any _proc_func that acts inplace would
    460         # change the ancestor value.
--> 461         return self._proc_func(*(copy(a[key]) for a in self._ancestors))
    462 
    463     def __repr__(self):

~\.conda\envs\trackpyenv\lib\site-packages\slicerator\__init__.py in <genexpr>(.0)
    459         # We need to copy here: else any _proc_func that acts inplace would
    460         # change the ancestor value.
--> 461         return self._proc_func(*(copy(a[key]) for a in self._ancestors))
    462 
    463     def __repr__(self):

~\.conda\envs\trackpyenv\lib\site-packages\slicerator\__init__.py in __getitem__(self, i)
    186                 indices, new_length = key_to_indices(i, len(self))
    187                 if new_length is None:
--> 188                     return self._get(indices)
    189                 else:
    190                     return cls(self, indices, new_length, propagate_attrs)

~\.conda\envs\trackpyenv\lib\site-packages\pims\base_frames.py in __getitem__(self, key)
     96         """__getitem__ is handled by Slicerator. In all pims readers, the data
     97         returning function is get_frame."""
---> 98         return self.get_frame(key)
     99 
    100     def __iter__(self):

~\.conda\envs\trackpyenv\lib\site-packages\pims\base_frames.py in get_frame(self, i)
    590         coords.update(**{k: v for k, v in zip(self.iter_axes, iter_coords)})
    591 
--> 592         result = self._get_frame_wrapped(**coords)
    593         if hasattr(result, 'metadata'):
    594             metadata = result.metadata

~\.conda\envs\trackpyenv\lib\site-packages\pims\imageio_reader.py in get_frame_2D(self, **coords)
    100     def get_frame_2D(self, **coords):
    101         i = coords['t'] if 't' in coords else 0
--> 102         frame = self.reader.get_data(i)
    103         return Frame(frame, frame_no=i, metadata=frame.meta)
    104 

~\.conda\envs\trackpyenv\lib\site-packages\imageio\core\format.py in get_data(self, index, **kwargs)
    344             self._BaseReaderWriter_last_index = index
    345             try:
--> 346                 im, meta = self._get_data(index, **kwargs)
    347             except StopIteration:
    348                 raise IndexError(index)

~\.conda\envs\trackpyenv\lib\site-packages\imageio\plugins\ffmpeg.py in _get_data(self, index)
    379             else:
    380                 if (index < self._pos) or (index > self._pos + 100):
--> 381                     self._initialize(index)
    382                 else:
    383                     self._skip_frames(index - self._pos - 1)

~\.conda\envs\trackpyenv\lib\site-packages\imageio\plugins\ffmpeg.py in _initialize(self, index)
    393             # Close the current generator, and thereby terminate its subprocess
    394             if self._read_gen is not None:
--> 395                 self._read_gen.close()
    396 
    397             iargs = []

ValueError: generator already executing

Solution

  • I stumbled on the same (or similar) issue. The root cause seems to be trying to use more than one process to execute the batch code, while some internal function call isn't thread-safe. A workaround would be to disable multi-processes by calling batch with processes=1, e.g.:

    f = tp.batch(frames[100:300], masksize, minmass=minmass, invert=True, processes=1);
    

    See trackpy.batch for reference.

    Calling it a workaround as this would cause the code to execute serially, one frame at a time. Then again, better than not executing at all...