Repository: lightly-ai/lightly

Issue:
lightly-upload fails with video dataset
# lightly-upload fails with video dataset

Bug introduced in #417 which breaks image upload for video datasets because we try to determine the file size of each image on disk.

Relevant lines:
https://github.com/lightly-ai/lightly/blob/faff18868914f6884dab73a00ec4940e35106f48/lightly/api/api_workflow_upload_dataset.py#L143

Possible fix: Wrap the file size extraction in a try-catch and use the old method as a fallback.


Discussion:

Resolving patch:
diff --git a/lightly/data/dataset.py b/lightly/data/dataset.py
--- a/lightly/data/dataset.py
+++ b/lightly/data/dataset.py
@@ -283,6 +283,10 @@ def dump(self,
     def get_filepath_from_filename(self, filename: str, image: PIL.Image.Image = None):
         """Returns the filepath given the filename of the image
 
+        There are three cases:
+        - The dataset is a regular dataset with the images in the input dir.
+        - The dataset is a video dataset, thus the images have to be saved in a temporary folder.
+        - The dataset is a torch dataset, thus the images have to be saved in a temporary folder.
         Args:
             filename:
                 The filename of the image
@@ -290,18 +294,25 @@ def get_filepath_from_filename(self, filename: str, image: PIL.Image.Image = Non
                 The image corresponding to the filename
 
         Returns:
+            The filename to the image, either the exiting one (case 1) or a newly created jpg (case 2, 3)
 
         """
-        if hasattr(self, 'input_dir') and isinstance(self.input_dir, str):
-            return os.path.join(self.input_dir, filename)
-        else:
-            if image is None:
-                raise ValueError("This LightlyDataset was created from a torch dataset and thus has no input_dir."
-                                 "Thus you must provide the image to be able to save it and return the path to it.")
-            folder_path = tempfile.mkdtemp()
-            filepath = os.path.join(folder_path,filename) + '.jpg'
-            image.save(filepath)
-            return filepath
+
+        has_input_dir = hasattr(self, 'input_dir') and isinstance(self.input_dir, str)
+        if has_input_dir:
+            path_to_image = os.path.join(self.input_dir, filename)
+            if os.path.isfile(path_to_image):
+                # Case 1
+                return path_to_image
+
+        if image is None:
+            raise ValueError("The parameter image must not be None for VideoDatasets and TorchDatasets")
+
+        # Case 2 and 3
+        folder_path = tempfile.mkdtemp()
+        filepath = os.path.join(folder_path,filename) + '.jpg'
+        image.save(filepath)
+        return filepath
 
 
     @property


Test patch:
diff --git a/tests/api_workflow/test_api_workflow_upload_dataset.py b/tests/api_workflow/test_api_workflow_upload_dataset.py
--- a/tests/api_workflow/test_api_workflow_upload_dataset.py
+++ b/tests/api_workflow/test_api_workflow_upload_dataset.py
@@ -2,11 +2,14 @@
 import tempfile
 import pathlib
 
+import numpy as np
 import torchvision
 from lightly.data.dataset import LightlyDataset
 
 from tests.api_workflow.mocked_api_workflow_client import MockedApiWorkflowSetup
 
+import cv2
+
 
 class TestApiWorkflowUploadDataset(MockedApiWorkflowSetup):
     def setUp(self) -> None:
@@ -61,3 +64,20 @@ def test_corrupt_dataset_from_folder(self):
         self.corrupt_fake_dataset()
         self.api_workflow_client.upload_dataset(input=self.folder_path)
         self.api_workflow_client.upload_dataset(input=self.folder_path)
+
+    def create_fake_video_dataset(self, n_videos=5, n_frames_per_video=10, w=32, h=32, c=3, extension='avi'):
+
+
+        self.video_input_dir = tempfile.mkdtemp()
+        self.frames = (np.random.randn(n_frames_per_video, w, h, c) * 255).astype(np.uint8)
+
+        for i in range(n_videos):
+            path = os.path.join(self.video_input_dir, f'output-{i}.{extension}')
+            out = cv2.VideoWriter(path, 0, 1, (w, h))
+            for frame in self.frames:
+                out.write(frame)
+            out.release()
+
+    def test_upload_video_dataset_from_folder(self):
+        self.create_fake_video_dataset()
+        self.api_workflow_client.upload_dataset(input=self.video_input_dir, max_workers=0)
diff --git a/tests/data/test_VideoDataset.py b/tests/data/test_VideoDataset.py
--- a/tests/data/test_VideoDataset.py
+++ b/tests/data/test_VideoDataset.py
@@ -7,13 +7,8 @@
 import PIL
 import torchvision
 
-try:
-    from lightly.data._video import VideoDataset, _make_dataset
-    import av
-    import cv2
-    VIDEO_DATASET_AVAILABLE = True
-except Exception:
-    VIDEO_DATASET_AVAILABLE = False
+from lightly.data._video import VideoDataset, _make_dataset
+import cv2
 
 class TestVideoDataset(unittest.TestCase):
 
@@ -40,10 +35,6 @@ def create_dataset(self, n_videos=5, n_frames_per_video=10, w=32, h=32, c=3):
             out.release()
 
     def test_video_similar_timestamps_for_different_backends(self):
-        if not VIDEO_DATASET_AVAILABLE:
-            warnings.warn(
-                'Did not test video dataset because of missing requirements')
-            return
 
         self.create_dataset()
 
@@ -73,10 +64,6 @@ def test_video_similar_timestamps_for_different_backends(self):
 
     def test_video_dataset_from_folder(self):
 
-        if not VIDEO_DATASET_AVAILABLE:
-            warnings.warn(
-                'Did not test video dataset because of missing requirements')
-            return
 
         self.create_dataset()