Repository: comic/grand-challenge.org

Issue:
libvips chokes on dzi conversion with sparse inputs
Upload a tif file which is sparse, in `tiff.py` line 212:

```
image = pyvips.Image.new_from_file(
            str(tiff_file.path.absolute()), access="sequential"
        )

        pyvips.Image.dzsave(
            image, dzi_output, tile_size=settings.DZI_TILE_SIZE
        )
```

The `dzsave` will fail with empty tiles, eg:

```
["Image can't be converted to dzi: unable to call dzsave\n  TIFFFillTile: 0: Invalid tile byte count, tile 1\nTIFFFillTile: 0: Invalid tile byte count, tile 3\nTIFFFillTile: 0: Invalid tile byte count, tile 4\nTIFFFillTile: 0: Invalid tile byte count, tile 5\nTIFFFillTile: 0: Invalid tile byte count, tile 6\nTIFFFillTile: 0: Invalid tile byte count, tile 7\nTIFFFillTile: 0: Invalid tile byte count, tile 8\nTIFFFillTile: 0: Invalid tile byte count, tile 9\nTIFFFillTile: 0: Invalid tile byte count, tile 10\nTIFFFillTile: 0: Invalid tile byte count, tile 11\nTIFFFillTile: 0: Invalid tile byte count, tile 12\nTIFFFillTile: 0: Invalid tile byte count, tile 13\nTIFFFillTile: 0: Invalid tile byte count, tile 14\nTIFFFillTile: 0: Invalid tile byte count, tile 15\nTIFFFillTile: 0: Invalid tile byte count, tile 16\nTIFFFillTile: 0: Invalid tile byte count, tile 17\nTIFFFillTile: 0: Invalid tile byte count, tile 18\nTIFFFillTile: 0: Invalid tile byte count, tile 19\nTIFFFillTile: 0: Invalid tile byte count, tile 20\nTIFFFillTile: 0: Invalid tile byte count, tile 23\n"]
```

Test image shared with Miriam on slack.


Discussion:

Resolving patch:
diff --git a/app/grandchallenge/cases/image_builders/tiff.py b/app/grandchallenge/cases/image_builders/tiff.py
--- a/app/grandchallenge/cases/image_builders/tiff.py
+++ b/app/grandchallenge/cases/image_builders/tiff.py
@@ -133,14 +133,18 @@ def image_builder_tiff(path: Path) -> ImageBuilderResult:
 
     for file_path in path.iterdir():
         pk = uuid4()
-
+        dzi_output = None
         try:
             tiff_file = load_tiff_file(path=file_path)
-            dzi_output = create_dzi_images(tiff_file=tiff_file, pk=pk)
         except ValidationError as e:
             invalid_file_errors[file_path.name] = str(e)
             continue
 
+        try:
+            dzi_output = create_dzi_images(tiff_file=tiff_file, pk=pk)
+        except ValidationError as e:
+            invalid_file_errors[file_path.name] = str(e)
+
         image = create_tiff_image_entry(tiff_file=tiff_file, pk=pk)
 
         temp_file = TemporaryFile()
@@ -158,27 +162,29 @@ def image_builder_tiff(path: Path) -> ImageBuilderResult:
             )
         )
 
-        temp_dzi_file = TemporaryFile()
-        with open(dzi_output + ".dzi", "rb") as open_file:
-            buffer = True
-            while buffer:
-                buffer = open_file.read(1024)
-                temp_dzi_file.write(buffer)
+        if dzi_output:
+            temp_dzi_file = TemporaryFile()
+            with open(dzi_output + ".dzi", "rb") as open_file:
+                buffer = True
+                while buffer:
+                    buffer = open_file.read(1024)
+                    temp_dzi_file.write(buffer)
+
+            new_image_files.append(
+                ImageFile(
+                    image=image,
+                    image_type=ImageFile.IMAGE_TYPE_DZI,
+                    file=File(temp_dzi_file, name=f"{image.pk}.dzi"),
+                )
+            )
 
-        new_image_files.append(
-            ImageFile(
-                image=image,
-                image_type=ImageFile.IMAGE_TYPE_DZI,
-                file=File(temp_dzi_file, name=f"{image.pk}.dzi"),
+            dzi_folder_upload = FolderUpload(
+                folder=dzi_output + "_files", image=image
             )
-        )
+            new_folder_upload.append(dzi_folder_upload)
 
-        dzi_folder_upload = FolderUpload(
-            folder=dzi_output + "_files", image=image
-        )
         new_images.append(image)
         consumed_files.add(tiff_file.path.name)
-        new_folder_upload.append(dzi_folder_upload)
 
     return ImageBuilderResult(
         consumed_files=consumed_files,


Test patch: