MONAI
MONAI copied to clipboard
Video dataset
Addresses: https://github.com/Project-MONAI/MONAI/issues/4746.
Description
Adds video datasets. Videos can be from file or capture device (e.g., webcam).
Requires a corresponding tutorial.
Status
Hold
Types of changes
- [x] Non-breaking change (fix or new feature that would not break existing functionality).
- [x] New tests added to cover the changes.
Hi @binliunls ,
I think this great PR almost achieved all the necessary features of the video loading part. Please take a look and maybe test it locally in your use case.
Thanks in advance.
Hi @rijobro, I had a few tests on this videodataset this week. However my program stuck when I try to use dataloader whose num_workers equals to 1 or above to load data with the videodataset. I am currently finding the reason why this happened. And I think maybe this would be helpful for you too.
Hi @binliunls ,
Do you mean DataLoader
or ThreadDataLoader with use_thread_workers=True
?
I think this line in this PR is not thread safe:
self.cap.set(cv2.CAP_PROP_POS_FRAMES, index)
And GPU transforms can't work in multi-processing in your use case.
So if num_workers
> 0, both multi-thread and multi-processing workers should not work in theory here.
@rijobro right?
Thanks.
Hi @binliunls ,
Do you mean
DataLoader
orThreadDataLoader with use_thread_workers=True
? I think this line in this PR is not thread safe:self.cap.set(cv2.CAP_PROP_POS_FRAMES, index)
And GPU transforms can't work in multi-processing in your use case.
So if
num_workers
> 0, both multi-thread and multi-processing workers should not work in theory here. @rijobro right?Thanks.
Hi @Nic-Ma : I tried DataLoader and ThreadDataLoader. They both stuck when set the number workers >= 1. @rijobro Could you please double check this problem, in case I did something wrong with the environment or the code.
Hi @binliunls ,
Are you execute transforms on GPU? It should not work with num_workers > 0 in DataLoader.
Thanks.
Hi @binliunls ,
Are you execute transforms on GPU? It should not work with num_workers > 0 in DataLoader.
Thanks.
I tried to move the transform from GPU to CPU and the program still stuck.
Hi @binliunls ,
I think your multi-processing issue may be similar to:
https://www.pythonfixing.com/2022/05/fixed-python-opencv-multiprocessing.html
Let's keep using num_workers=0
with ThreadDataLoader
and GPU transforms
so far.
@rijobro Could you please help confirm the issue? If True, maybe we need to highlight it in the doc-string of VideoDataset
?
Thanks in advance.
Hi @binliunls ,
I think your multi-processing issue may be similar to: https://www.pythonfixing.com/2022/05/fixed-python-opencv-multiprocessing.html Let's keep using
num_workers=0
withThreadDataLoader
andGPU transforms
so far. @rijobro Could you please help confirm the issue? If True, maybe we need to highlight it in the doc-string ofVideoDataset
?Thanks in advance.
Hi @Nic-Ma: The link below solved my problem. It may cause problems when the main process and children process share the same obeject, which is the video "cap" object in our case. I moved the "open_video" function call from the init function to functions like "get_frame", where the "cap" is needed . The new program can run. https://stackoverflow.com/questions/62126858/python-multiprocessing-and-opencv-videocapture-read-error
Hi @binliunls ,
If you move the "open_video" call to every get frame, will it be slower as it frequently opens and closes?
Thanks.
Hi @binliunls ,
If you move the "open_video" call to every get frame, will it be slower as it frequently opens and closes?
Thanks. Hi @Nic-Ma Yes it will be 1.5x slower than @rijobro one if num_worker=0&batch_size=4 for both videodatasets. However @rijobro old videodataset will be 1.5x slower if I set num_worker=4 and keep the batch_size=4 for the new one. Thanks.
Ok, so I will put a multiprocessing
argument into the constructor, which is False
by default. This will do open_video
at construction and only perform once. This will not be threadsafe and the user should use num_workers==0
. If True
, we do open_video
each time a new frame is requested. This will be threadsafe, useful if num_workers>0
but slower if num_workers==0
. Does that sound reasonable?
Hi @rijobro ,
I think it's OK to add this multiprocessing
, the solution looks good to me.
But I think it's "process safe" not "thread safe", the VideoDataset
still can't work with ThreadDataLoader
if set use_thread_workers=True
. It's not a problem so far.
Thanks.
@binliunls I've made the various changes (including the RGB option you requested here). Could you give it a go and let me know if it all works as expected?
@Nic-Ma @wyli I'm seeing circular import errors. Is this specific to this PR or are we seeing this elsewhere? I can't see changes in this code affecting the Convolution
class it's mentioning.
I think it's in this PR only, and I couldn't find out the reason...most likely from the unit tests?
@binliunls I've made the various changes (including the RGB option you requested here). Could you give it a go and let me know if it all works as expected?
Hi @rijobro , I will run some tests on this new dataset. Meanwhile I think maybe the channel to front operation in the link below is not necessary, since users can do this by adding "AsChannelFirst" to their transforms. And this will be a block to users who just want to see what the frames look like, because they are going to get a channel first image and have to move the axis again. https://github.com/rijobro/MONAI/blob/2d52bfe27e7dd2f238103cc32f8f1837c1fa8214/monai/data/video_dataset.py#L108
Hm, I think for pytorch based applications, we always want the channel in front of spatial dimensions. Granted, matplotlib and opencv expect them at the end. I'll put this as an option, too. It's true that we could leave it for the transforms, but I think that it's going to be needed frequently enough to have it done by default.
Hi @rijobro , just for your information. There is a similar ensure_channel_first
arg in the LoadImaged transform:
https://github.com/Project-MONAI/MONAI/blob/dev/monai/transforms/io/dictionary.py#L76
Thanks.
@wyli Any idea why I'm seeing this? The import is nested in an optional_import
.
/black
@wyli Any idea why I'm seeing this? The import is nested in an
optional_import
.
I see, that's an issue from type annotations and you've addressed that.
/build
Hi @Nic-Ma, I can make the minor doc updates in https://github.com/Project-MONAI/MONAI/pull/4838
/build
/build
error from the tests, I suspect the docker image lacks FFmpeg which is under GPL licence @rijobro @binliunls any idea?
[2022-08-08T13:17:56.960Z] ======================================================================
[2022-08-08T13:17:56.960Z] ERROR: test_dataset (tests.test_video_datasets.TestVideoFileDataset)
[2022-08-08T13:17:56.960Z] ----------------------------------------------------------------------
[2022-08-08T13:17:56.960Z] Traceback (most recent call last):
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/tests/test_video_datasets.py", line 117, in test_dataset
[2022-08-08T13:17:56.960Z] super().test_dataset(known_num_frames, known_fps)
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/tests/test_video_datasets.py", line 70, in test_dataset
[2022-08-08T13:17:56.960Z] ds = self.get_ds(max_num_frames=max_num_frames)
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/tests/test_video_datasets.py", line 40, in get_ds
[2022-08-08T13:17:56.960Z] return self.ds(video_source=self.video_source, transform=TRANSFORMS, *args, **kwargs) # type: ignore
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 127, in __init__
[2022-08-08T13:17:56.960Z] VideoDataset.__init__(self, *args, **kwargs)
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 74, in __init__
[2022-08-08T13:17:56.960Z] self.cap = self.open_video(video_source)
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 94, in open_video
[2022-08-08T13:17:56.960Z] raise RuntimeError(f"Failed to open video: {video_source}")
[2022-08-08T13:17:56.960Z] RuntimeError: Failed to open video: /home/jenkins/agent/workspace/MONAI-premerge/monai/tests/testing_data/endo.mp4
[2022-08-08T13:17:56.960Z]
[2022-08-08T13:17:56.960Z] ======================================================================
[2022-08-08T13:17:56.960Z] ERROR: test_multiple_sources (tests.test_video_datasets.TestVideoFileDataset)
[2022-08-08T13:17:56.960Z] ----------------------------------------------------------------------
[2022-08-08T13:17:56.960Z] Traceback (most recent call last):
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/tests/test_video_datasets.py", line 61, in test_multiple_sources
[2022-08-08T13:17:56.960Z] ds1 = self.get_ds()
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/tests/test_video_datasets.py", line 40, in get_ds
[2022-08-08T13:17:56.960Z] return self.ds(video_source=self.video_source, transform=TRANSFORMS, *args, **kwargs) # type: ignore
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 127, in __init__
[2022-08-08T13:17:56.960Z] VideoDataset.__init__(self, *args, **kwargs)
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 74, in __init__
[2022-08-08T13:17:56.960Z] self.cap = self.open_video(video_source)
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 94, in open_video
[2022-08-08T13:17:56.960Z] raise RuntimeError(f"Failed to open video: {video_source}")
[2022-08-08T13:17:56.960Z] RuntimeError: Failed to open video: /home/jenkins/agent/workspace/MONAI-premerge/monai/tests/testing_data/endo.mp4
[2022-08-08T13:17:56.960Z]
[2022-08-08T13:17:56.960Z] ======================================================================
[2022-08-08T13:17:56.960Z] ERROR: test_multiprocessing (tests.test_video_datasets.TestVideoFileDataset)
[2022-08-08T13:17:56.960Z] ----------------------------------------------------------------------
[2022-08-08T13:17:56.960Z] Traceback (most recent call last):
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/tests/test_video_datasets.py", line 54, in test_multiprocessing
[2022-08-08T13:17:56.960Z] ds = self.get_ds(max_num_frames=100, multiprocessing=multiprocessing)
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/tests/test_video_datasets.py", line 40, in get_ds
[2022-08-08T13:17:56.960Z] return self.ds(video_source=self.video_source, transform=TRANSFORMS, *args, **kwargs) # type: ignore
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 128, in __init__
[2022-08-08T13:17:56.960Z] num_frames = self.get_num_frames()
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 139, in get_num_frames
[2022-08-08T13:17:56.960Z] num_frames = int(self._get_cap().get(cv2.CAP_PROP_FRAME_COUNT))
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 99, in _get_cap
[2022-08-08T13:17:56.960Z] return self.open_video(self.video_source) if self.multiprocessing else self.cap
[2022-08-08T13:17:56.960Z] File "/home/jenkins/agent/workspace/MONAI-premerge/monai/monai/data/video_dataset.py", line 94, in open_video
[2022-08-08T13:17:56.960Z] raise RuntimeError(f"Failed to open video: {video_source}")
[2022-08-08T13:17:56.960Z] RuntimeError: Failed to open video: /home/jenkins/agent/workspace/MONAI-premerge/monai/tests/testing_data/endo.mp4
[2022-08-08T13:17:56.960Z]
[2022-08-08T13:17:56.960Z] ----------------------------------------------------------------------
error from the tests, I suspect the docker image lacks FFmpeg which is under GPL licence @rijobro @binliunls any idea?
Yes, I have encountered this problem in docker environment several times. Not only lack the FFMPEG, but also the opencv-python in the docker has some problems to load the ffmpeg if installed. I usually delete the opencv-python in the docker manually and reinstall the ffmpeg and opencv-python to make it work for loading videos. However I haven't tested this using the latest docker, so I cannot say whether will this problem appear in it.
ok, the VideoDataset module is generic and doesn't strictly require ffmpeg, the test cases need it. do you have simpler test cases?
ok, the VideoDataset module is generic and doesn't strictly require ffmpeg, the test cases need it. do you have simpler test cases?
Hi @wyli ,
I think the VideoDataset
depends on OpenCV, the get_frame
and open_video
, etc.
How about making the opencv tests as skipped in CI? Like cupy
tests?
https://github.com/Project-MONAI/MONAI/blob/dev/tests/test_cucim_transform.py#L65
Thanks.
there is a version of opencv in our docker image release, it's probably from the base image and compiled without ffmpeg. In order to use the video dataset, what would be the installation steps from a docker image user's perspective?
Hi @rijobro ,
Would you like to fix the CI issue and let's merge this PR then?
Thanks in advance.
Will wait to see if this passes CI, added .avi example video in case .mp4 codec missing. If both missing, skip. Also silence stderr error from OpenCV C++.