We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
MockHistoDataGenerator generates corrupted tiff files
Update tiffwriter arguments after upgrade in #691 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py`
Content:
```
1 #  ------------------------------------------------------------------------------------------
2 #  Copyright (c) Microsoft Corporation. All rights reserved.
3 #  Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4 #  ------------------------------------------------------------------------------------------
5 from enum import Enum
6 from pathlib import Path
7 from typing import Any, Optional, Tuple, List, Union
8 
9 import numpy as np
10 import pandas as pd
11 import torch
12 from tifffile import TiffWriter
13 from torch import Tensor
14 from health_cpath.datasets.panda_dataset import PandaDataset
15 from testhisto.mocks.base_data_generator import MockHistoDataGenerator, MockHistoDataType, PANDA_N_CLASSES
16 
17 
18 class TilesPositioningType(Enum):
19     DIAGONAL = 0
20     RANDOM = 1
21 
22 
23 class MockPandaSlidesGenerator(MockHistoDataGenerator):
24     """Generator class to create mock WSI on the fly.
25         If tiles positioning is diagonal, a mock WSI resembles to:
26                                 [**      ]
27                                 [  **    ]
28                                 [    **  ]
29                                 [      **]
30         where * represents 2 tiles stitched along the Y axis.
31         If tiles positioning is random, tiles are positioned randomly on the WSI grid.
32     """
33 
34     ISUP_GRADE = "isup_grade"
35 
36     def __init__(
37         self,
38         n_levels: int = 3,
39         n_repeat_diag: int = 4,
40         n_repeat_tile: int = 2,
41         background_val: Union[int, float] = 255,
42         tiles_pos_type: TilesPositioningType = TilesPositioningType.DIAGONAL,
43         n_tiles_list: Optional[List[int]] = None,
44         **kwargs: Any,
45     ) -> None:
46         """
47         :param n_levels: Number of levels for multi resolution WSI.
48         :param n_repeat_diag: Number of repeat time along the diagonal axis, defaults to 4.
49         :param n_repeat_tile: Number of repeat times of a tile along both Y and X axes, defaults to 2.
50         :param background_val: A value to assign to the background, defaults to 255.
51         :param tiles_pos_type: The tiles positioning type to define how tiles should be positioned within the WSI grid,
52         defaults to TilesPositioningType.DIAGONAL.
53         :param n_tiles_list: A list to use different n_tiles per slide for randomly positioned tiles.
54         :param kwargs: Same params passed to MockHistoDataGenerator.
55         """
56         super().__init__(**kwargs)
57 
58         self.n_levels = n_levels
59         self.n_repeat_diag = n_repeat_diag
60         self.n_repeat_tile = n_repeat_tile
61         self.background_val = background_val
62         self.tiles_pos_type = tiles_pos_type
63 
64         self.step_size = self.tile_size * self.n_repeat_tile
65         self._dtype = np.uint8 if type(background_val) == int else np.float32
66         self.img_size: int = self.n_repeat_diag * self.n_repeat_tile * self.tile_size
67         self.n_tiles_list = n_tiles_list
68 
69         if self.n_tiles_list:
70             assert len(self.n_tiles_list) == self.n_slides, "n_tiles_list length should be equal to n_slides"
71             assert self.tiles_pos_type == TilesPositioningType.RANDOM, "different n_tiles enabled only for randomly "
72             "positionned tiles."
73 
74     def validate(self) -> None:
75         assert (
76             self.n_slides >= PANDA_N_CLASSES
77         ), f"The number of slides should be >= PANDA_N_CLASSES (i.e., {PANDA_N_CLASSES})"
78 
79     def create_mock_metadata_dataframe(self) -> pd.DataFrame:
80         """Create a mock dataframe with random metadata."""
81         isup_grades = np.tile(list(self.ISUP_GRADE_MAPPING.keys()), self.n_slides // PANDA_N_CLASSES + 1,)
82         mock_metadata: dict = {
83             col: [] for col in [PandaDataset.SLIDE_ID_COLUMN, PandaDataset.MASK_COLUMN, *PandaDataset.METADATA_COLUMNS]
84         }
85         for slide_id in range(self.n_slides):
86             mock_metadata[PandaDataset.SLIDE_ID_COLUMN].append(f"_{slide_id}")
87             mock_metadata[PandaDataset.MASK_COLUMN].append(f"_{slide_id}_mask")
88             mock_metadata[self.DATA_PROVIDER].append(np.random.choice(self.DATA_PROVIDERS_VALUES))
89             mock_metadata[self.ISUP_GRADE].append(isup_grades[slide_id])
90             mock_metadata[self.GLEASON_SCORE].append(np.random.choice(self.ISUP_GRADE_MAPPING[isup_grades[slide_id]]))
91         df = pd.DataFrame(data=mock_metadata)
92         csv_filename = self.dest_data_path / PandaDataset.DEFAULT_CSV_FILENAME
93         df.to_csv(csv_filename, index=False)
94 
95     def create_mock_wsi(self, tiles: Tensor) -> Tuple[np.ndarray, Optional[np.ndarray]]:
96         if self.tiles_pos_type == TilesPositioningType.DIAGONAL:
97             return self._create_wsi_from_stitched_tiles_along_diagonal_axis(tiles)
98         elif self.tiles_pos_type == TilesPositioningType.RANDOM:
99             return self._create_wsi_from_randomly_positioned_tiles(tiles), None
100         else:
101             raise NotImplementedError
102 
103     def _create_wsi_from_stitched_tiles_along_diagonal_axis(self, tiles: Tensor) -> Tuple[np.ndarray, np.ndarray]:
104         """Create a whole slide image by stitching tiles along the diagonal axis.
105 
106         :param tiles: A tensor of tiles of shape (n_tiles, n_channels, tile_size, tile_size).
107         :return: returns a wsi of shape (img_size, img_size, n_channels) and the tiles used to create it.
108         The image is  in channels_last format so that it can save by TiffWriter.
109         """
110         mock_image = np.full(
111             shape=(self.n_channels, self.img_size, self.img_size), fill_value=self.background_val, dtype=self._dtype
112         )
113         dump_tiles = []
114         for i in range(self.n_repeat_diag):
115             if self.mock_type == MockHistoDataType.PATHMNIST:
116                 if i == 0 or self.n_tiles > 1:
117                     tile = (
118                         (tiles[i % self.n_tiles].numpy()).astype(self._dtype)
119                         if self._dtype == np.uint8
120                         else tiles[i % self.n_tiles].numpy()
121                     )
122                     # fill the square diagonal with tile repeated n_repeat_tile times along X and Y axis.
123                     fill_square = np.tile(tile, (self.n_repeat_tile, self.n_repeat_tile))
124                     dump_tiles.append(tile)
125 
126             elif self.mock_type == MockHistoDataType.FAKE:
127                 if i == 0 or self.n_tiles > 1:
128                     # pick a random fake value to fill in the square diagonal.
129                     fill_square = np.random.uniform(0, self.background_val / (self.n_repeat_diag + 1) * (i + 1))
130                     dump_tiles.append(
131                         np.full(
132                             shape=(self.n_channels, self.tile_size, self.tile_size),
133                             fill_value=fill_square,
134                             dtype=self._dtype,
135                         )
136                     )
137             else:
138                 raise NotImplementedError
139             mock_image[
140                 :, self.step_size * i: self.step_size * (i + 1), self.step_size * i: self.step_size * (i + 1)
141             ] = fill_square
142         return np.transpose(mock_image, (1, 2, 0)), np.array(dump_tiles)  # switch to channels_last.
143 
144     def _create_wsi_from_randomly_positioned_tiles(self, tiles: Tensor) -> np.ndarray:
145         """Create a whole slide image by positioning tiles randomly in the whole slide image grid.
146 
147         :param tiles: A tensor of tiles of shape (n_tiles, n_channels, tile_size, tile_size).
148         :return: returns a wsi of shape (img_size, img_size, n_channels) in channels_last format so that it can save by
149         TiffWriter.
150         """
151         mock_image = np.full(
152             shape=(self.n_channels, self.img_size, self.img_size), fill_value=self.background_val, dtype=self._dtype
153         )
154 
155         n_tiles_side = self.img_size // self.tile_size
156         total_n_tiles = n_tiles_side ** 2
157         coords = [
158             (k // n_tiles_side, k % n_tiles_side)
159             for k in np.random.choice(total_n_tiles, size=self.n_tiles, replace=False)
160         ]
161         for i in range(self.n_tiles):
162             x, y = self.tile_size * np.array(coords[i])
163             if self.mock_type == MockHistoDataType.PATHMNIST:
164                 new_tile = tiles[i].numpy()
165             elif self.mock_type == MockHistoDataType.FAKE:
166                 new_tile = np.random.uniform(0, self.background_val / (self.n_repeat_diag + 1) * (i + 1))
167             else:
168                 raise NotImplementedError
169             mock_image[:, x: x + self.tile_size, y: y + self.tile_size] = new_tile
170         return np.transpose(mock_image, (1, 2, 0))
171 
172     @staticmethod
173     def _save_mock_wsi_as_tiff_file(file_path: Path, wsi_levels: List[np.ndarray]) -> None:
174         """Save a mock whole slide image as a tiff file of pyramidal levels.
175         Warning: this function expects images to be in channels_last format (H, W, C).
176 
177         :param file_name: The tiff file name path.
178         :param wsi_levels: List of whole slide images of different resolution levels in channels_last format.
179         """
180         with TiffWriter(file_path, bigtiff=True) as tif:
181             options = dict(photometric="rgb", compression="zlib")
182             for i, wsi_level in enumerate(wsi_levels):
183                 # the subfiletype parameter is a bitfield that determines if the wsi_level is a reduced version of
184                 # another image.
185                 tif.write(wsi_level, **options, subfiletype=int(i > 0))
186 
187     def _create_multi_resolution_wsi(self, mock_image: np.ndarray) -> List[np.ndarray]:
188         """Create multi resolution versions of a mock image via 2 factor downsampling.
189 
190         :param mock_image: A mock image in channels_last format (H, W, 3).
191         :return: Returns a list of n_levels downsampled versions of the original mock image.
192         """
193         levels = [mock_image[:: 2 ** i, :: 2 ** i] for i in range(self.n_levels)]
194         return levels
195 
196     def generate_mock_histo_data(self) -> None:
197         """Create mock wsi and save them as tiff files"""
198         iterator = iter(self.dataloader) if self.dataloader else None
199 
200         slide_dir = self.dest_data_path / "train_images"
201         slide_dir.mkdir(parents=True, exist_ok=True)
202         tile_dir = self.dest_data_path / "dump_tiles"
203         tile_dir.mkdir(parents=True, exist_ok=True)
204 
205         for slide_counter in range(self.n_slides):
206 
207             if self.n_tiles_list:
208                 self.total_tiles = self.n_tiles_list[slide_counter]
209                 self.n_tiles: int = self.n_tiles_list[slide_counter]
210                 self.dataloader: torch.utils.data.DataLoader = self.get_dataloader()
211                 iterator = iter(self.dataloader)
212 
213             tiles, _ = next(iterator) if iterator else (None, None)
214             mock_image, dump_tiles = self.create_mock_wsi(tiles)
215             wsi_levels = self._create_multi_resolution_wsi(mock_image)
216 
217             slide_tiff_filename = self.dest_data_path / "train_images" / f"_{slide_counter}.tiff"
218             self._save_mock_wsi_as_tiff_file(slide_tiff_filename, wsi_levels)
219 
220             if dump_tiles is not None:
221                 dump_tiles_filename = self.dest_data_path / "dump_tiles" / f"_{slide_counter}.npy"
222                 np.save(dump_tiles_filename, dump_tiles)
223
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py b/hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py
--- a/hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py
+++ b/hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py
@@ -9,7 +9,7 @@
 import numpy as np
 import pandas as pd
 import torch
-from tifffile import TiffWriter
+from tifffile.tifffile import TiffWriter, PHOTOMETRIC, COMPRESSION
 from torch import Tensor
 from health_cpath.datasets.panda_dataset import PandaDataset
 from testhisto.mocks.base_data_generator import MockHistoDataGenerator, MockHistoDataType, PANDA_N_CLASSES
@@ -178,7 +178,13 @@
         :param wsi_levels: List of whole slide images of different resolution levels in channels_last format.
         """
         with TiffWriter(file_path, bigtiff=True) as tif:
-            options = dict(photometric="rgb", compression="zlib")
+            options = dict(
+                software='tifffile',
+                metadata={'axes': 'YXC'},
+                photometric=PHOTOMETRIC.RGB,
+                compression=COMPRESSION.ADOBE_DEFLATE,  # ADOBE_DEFLATE aka ZLIB lossless compression
+                tile=(16, 16),
+            )
             for i, wsi_level in enumerate(wsi_levels):
                 # the subfiletype parameter is a bitfield that determines if the wsi_level is a reduced version of
                 # another image.

{"golden_diff": "diff --git a/hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py b/hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py\n--- a/hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py\n+++ b/hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py\n@@ -9,7 +9,7 @@\n import numpy as np\n import pandas as pd\n import torch\n-from tifffile import TiffWriter\n+from tifffile.tifffile import TiffWriter, PHOTOMETRIC, COMPRESSION\n from torch import Tensor\n from health_cpath.datasets.panda_dataset import PandaDataset\n from testhisto.mocks.base_data_generator import MockHistoDataGenerator, MockHistoDataType, PANDA_N_CLASSES\n@@ -178,7 +178,13 @@\n         :param wsi_levels: List of whole slide images of different resolution levels in channels_last format.\n         \"\"\"\n         with TiffWriter(file_path, bigtiff=True) as tif:\n-            options = dict(photometric=\"rgb\", compression=\"zlib\")\n+            options = dict(\n+                software='tifffile',\n+                metadata={'axes': 'YXC'},\n+                photometric=PHOTOMETRIC.RGB,\n+                compression=COMPRESSION.ADOBE_DEFLATE,  # ADOBE_DEFLATE aka ZLIB lossless compression\n+                tile=(16, 16),\n+            )\n             for i, wsi_level in enumerate(wsi_levels):\n                 # the subfiletype parameter is a bitfield that determines if the wsi_level is a reduced version of\n                 # another image.\n", "issue": "MockHistoDataGenerator generates corrupted tiff files\nUpdate tiffwriter arguments after upgrade in #691 \n", "before_files": [{"content": "#  ------------------------------------------------------------------------------------------\n#  Copyright (c) Microsoft Corporation. All rights reserved.\n#  Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.\n#  ------------------------------------------------------------------------------------------\nfrom enum import Enum\nfrom pathlib import Path\nfrom typing import Any, Optional, Tuple, List, Union\n\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom tifffile import TiffWriter\nfrom torch import Tensor\nfrom health_cpath.datasets.panda_dataset import PandaDataset\nfrom testhisto.mocks.base_data_generator import MockHistoDataGenerator, MockHistoDataType, PANDA_N_CLASSES\n\n\nclass TilesPositioningType(Enum):\n    DIAGONAL = 0\n    RANDOM = 1\n\n\nclass MockPandaSlidesGenerator(MockHistoDataGenerator):\n    \"\"\"Generator class to create mock WSI on the fly.\n        If tiles positioning is diagonal, a mock WSI resembles to:\n                                [**      ]\n                                [  **    ]\n                                [    **  ]\n                                [      **]\n        where * represents 2 tiles stitched along the Y axis.\n        If tiles positioning is random, tiles are positioned randomly on the WSI grid.\n    \"\"\"\n\n    ISUP_GRADE = \"isup_grade\"\n\n    def __init__(\n        self,\n        n_levels: int = 3,\n        n_repeat_diag: int = 4,\n        n_repeat_tile: int = 2,\n        background_val: Union[int, float] = 255,\n        tiles_pos_type: TilesPositioningType = TilesPositioningType.DIAGONAL,\n        n_tiles_list: Optional[List[int]] = None,\n        **kwargs: Any,\n    ) -> None:\n        \"\"\"\n        :param n_levels: Number of levels for multi resolution WSI.\n        :param n_repeat_diag: Number of repeat time along the diagonal axis, defaults to 4.\n        :param n_repeat_tile: Number of repeat times of a tile along both Y and X axes, defaults to 2.\n        :param background_val: A value to assign to the background, defaults to 255.\n        :param tiles_pos_type: The tiles positioning type to define how tiles should be positioned within the WSI grid,\n        defaults to TilesPositioningType.DIAGONAL.\n        :param n_tiles_list: A list to use different n_tiles per slide for randomly positioned tiles.\n        :param kwargs: Same params passed to MockHistoDataGenerator.\n        \"\"\"\n        super().__init__(**kwargs)\n\n        self.n_levels = n_levels\n        self.n_repeat_diag = n_repeat_diag\n        self.n_repeat_tile = n_repeat_tile\n        self.background_val = background_val\n        self.tiles_pos_type = tiles_pos_type\n\n        self.step_size = self.tile_size * self.n_repeat_tile\n        self._dtype = np.uint8 if type(background_val) == int else np.float32\n        self.img_size: int = self.n_repeat_diag * self.n_repeat_tile * self.tile_size\n        self.n_tiles_list = n_tiles_list\n\n        if self.n_tiles_list:\n            assert len(self.n_tiles_list) == self.n_slides, \"n_tiles_list length should be equal to n_slides\"\n            assert self.tiles_pos_type == TilesPositioningType.RANDOM, \"different n_tiles enabled only for randomly \"\n            \"positionned tiles.\"\n\n    def validate(self) -> None:\n        assert (\n            self.n_slides >= PANDA_N_CLASSES\n        ), f\"The number of slides should be >= PANDA_N_CLASSES (i.e., {PANDA_N_CLASSES})\"\n\n    def create_mock_metadata_dataframe(self) -> pd.DataFrame:\n        \"\"\"Create a mock dataframe with random metadata.\"\"\"\n        isup_grades = np.tile(list(self.ISUP_GRADE_MAPPING.keys()), self.n_slides // PANDA_N_CLASSES + 1,)\n        mock_metadata: dict = {\n            col: [] for col in [PandaDataset.SLIDE_ID_COLUMN, PandaDataset.MASK_COLUMN, *PandaDataset.METADATA_COLUMNS]\n        }\n        for slide_id in range(self.n_slides):\n            mock_metadata[PandaDataset.SLIDE_ID_COLUMN].append(f\"_{slide_id}\")\n            mock_metadata[PandaDataset.MASK_COLUMN].append(f\"_{slide_id}_mask\")\n            mock_metadata[self.DATA_PROVIDER].append(np.random.choice(self.DATA_PROVIDERS_VALUES))\n            mock_metadata[self.ISUP_GRADE].append(isup_grades[slide_id])\n            mock_metadata[self.GLEASON_SCORE].append(np.random.choice(self.ISUP_GRADE_MAPPING[isup_grades[slide_id]]))\n        df = pd.DataFrame(data=mock_metadata)\n        csv_filename = self.dest_data_path / PandaDataset.DEFAULT_CSV_FILENAME\n        df.to_csv(csv_filename, index=False)\n\n    def create_mock_wsi(self, tiles: Tensor) -> Tuple[np.ndarray, Optional[np.ndarray]]:\n        if self.tiles_pos_type == TilesPositioningType.DIAGONAL:\n            return self._create_wsi_from_stitched_tiles_along_diagonal_axis(tiles)\n        elif self.tiles_pos_type == TilesPositioningType.RANDOM:\n            return self._create_wsi_from_randomly_positioned_tiles(tiles), None\n        else:\n            raise NotImplementedError\n\n    def _create_wsi_from_stitched_tiles_along_diagonal_axis(self, tiles: Tensor) -> Tuple[np.ndarray, np.ndarray]:\n        \"\"\"Create a whole slide image by stitching tiles along the diagonal axis.\n\n        :param tiles: A tensor of tiles of shape (n_tiles, n_channels, tile_size, tile_size).\n        :return: returns a wsi of shape (img_size, img_size, n_channels) and the tiles used to create it.\n        The image is  in channels_last format so that it can save by TiffWriter.\n        \"\"\"\n        mock_image = np.full(\n            shape=(self.n_channels, self.img_size, self.img_size), fill_value=self.background_val, dtype=self._dtype\n        )\n        dump_tiles = []\n        for i in range(self.n_repeat_diag):\n            if self.mock_type == MockHistoDataType.PATHMNIST:\n                if i == 0 or self.n_tiles > 1:\n                    tile = (\n                        (tiles[i % self.n_tiles].numpy()).astype(self._dtype)\n                        if self._dtype == np.uint8\n                        else tiles[i % self.n_tiles].numpy()\n                    )\n                    # fill the square diagonal with tile repeated n_repeat_tile times along X and Y axis.\n                    fill_square = np.tile(tile, (self.n_repeat_tile, self.n_repeat_tile))\n                    dump_tiles.append(tile)\n\n            elif self.mock_type == MockHistoDataType.FAKE:\n                if i == 0 or self.n_tiles > 1:\n                    # pick a random fake value to fill in the square diagonal.\n                    fill_square = np.random.uniform(0, self.background_val / (self.n_repeat_diag + 1) * (i + 1))\n                    dump_tiles.append(\n                        np.full(\n                            shape=(self.n_channels, self.tile_size, self.tile_size),\n                            fill_value=fill_square,\n                            dtype=self._dtype,\n                        )\n                    )\n            else:\n                raise NotImplementedError\n            mock_image[\n                :, self.step_size * i: self.step_size * (i + 1), self.step_size * i: self.step_size * (i + 1)\n            ] = fill_square\n        return np.transpose(mock_image, (1, 2, 0)), np.array(dump_tiles)  # switch to channels_last.\n\n    def _create_wsi_from_randomly_positioned_tiles(self, tiles: Tensor) -> np.ndarray:\n        \"\"\"Create a whole slide image by positioning tiles randomly in the whole slide image grid.\n\n        :param tiles: A tensor of tiles of shape (n_tiles, n_channels, tile_size, tile_size).\n        :return: returns a wsi of shape (img_size, img_size, n_channels) in channels_last format so that it can save by\n        TiffWriter.\n        \"\"\"\n        mock_image = np.full(\n            shape=(self.n_channels, self.img_size, self.img_size), fill_value=self.background_val, dtype=self._dtype\n        )\n\n        n_tiles_side = self.img_size // self.tile_size\n        total_n_tiles = n_tiles_side ** 2\n        coords = [\n            (k // n_tiles_side, k % n_tiles_side)\n            for k in np.random.choice(total_n_tiles, size=self.n_tiles, replace=False)\n        ]\n        for i in range(self.n_tiles):\n            x, y = self.tile_size * np.array(coords[i])\n            if self.mock_type == MockHistoDataType.PATHMNIST:\n                new_tile = tiles[i].numpy()\n            elif self.mock_type == MockHistoDataType.FAKE:\n                new_tile = np.random.uniform(0, self.background_val / (self.n_repeat_diag + 1) * (i + 1))\n            else:\n                raise NotImplementedError\n            mock_image[:, x: x + self.tile_size, y: y + self.tile_size] = new_tile\n        return np.transpose(mock_image, (1, 2, 0))\n\n    @staticmethod\n    def _save_mock_wsi_as_tiff_file(file_path: Path, wsi_levels: List[np.ndarray]) -> None:\n        \"\"\"Save a mock whole slide image as a tiff file of pyramidal levels.\n        Warning: this function expects images to be in channels_last format (H, W, C).\n\n        :param file_name: The tiff file name path.\n        :param wsi_levels: List of whole slide images of different resolution levels in channels_last format.\n        \"\"\"\n        with TiffWriter(file_path, bigtiff=True) as tif:\n            options = dict(photometric=\"rgb\", compression=\"zlib\")\n            for i, wsi_level in enumerate(wsi_levels):\n                # the subfiletype parameter is a bitfield that determines if the wsi_level is a reduced version of\n                # another image.\n                tif.write(wsi_level, **options, subfiletype=int(i > 0))\n\n    def _create_multi_resolution_wsi(self, mock_image: np.ndarray) -> List[np.ndarray]:\n        \"\"\"Create multi resolution versions of a mock image via 2 factor downsampling.\n\n        :param mock_image: A mock image in channels_last format (H, W, 3).\n        :return: Returns a list of n_levels downsampled versions of the original mock image.\n        \"\"\"\n        levels = [mock_image[:: 2 ** i, :: 2 ** i] for i in range(self.n_levels)]\n        return levels\n\n    def generate_mock_histo_data(self) -> None:\n        \"\"\"Create mock wsi and save them as tiff files\"\"\"\n        iterator = iter(self.dataloader) if self.dataloader else None\n\n        slide_dir = self.dest_data_path / \"train_images\"\n        slide_dir.mkdir(parents=True, exist_ok=True)\n        tile_dir = self.dest_data_path / \"dump_tiles\"\n        tile_dir.mkdir(parents=True, exist_ok=True)\n\n        for slide_counter in range(self.n_slides):\n\n            if self.n_tiles_list:\n                self.total_tiles = self.n_tiles_list[slide_counter]\n                self.n_tiles: int = self.n_tiles_list[slide_counter]\n                self.dataloader: torch.utils.data.DataLoader = self.get_dataloader()\n                iterator = iter(self.dataloader)\n\n            tiles, _ = next(iterator) if iterator else (None, None)\n            mock_image, dump_tiles = self.create_mock_wsi(tiles)\n            wsi_levels = self._create_multi_resolution_wsi(mock_image)\n\n            slide_tiff_filename = self.dest_data_path / \"train_images\" / f\"_{slide_counter}.tiff\"\n            self._save_mock_wsi_as_tiff_file(slide_tiff_filename, wsi_levels)\n\n            if dump_tiles is not None:\n                dump_tiles_filename = self.dest_data_path / \"dump_tiles\" / f\"_{slide_counter}.npy\"\n                np.save(dump_tiles_filename, dump_tiles)\n", "path": "hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py"}], "after_files": [{"content": "#  ------------------------------------------------------------------------------------------\n#  Copyright (c) Microsoft Corporation. All rights reserved.\n#  Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.\n#  ------------------------------------------------------------------------------------------\nfrom enum import Enum\nfrom pathlib import Path\nfrom typing import Any, Optional, Tuple, List, Union\n\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom tifffile.tifffile import TiffWriter, PHOTOMETRIC, COMPRESSION\nfrom torch import Tensor\nfrom health_cpath.datasets.panda_dataset import PandaDataset\nfrom testhisto.mocks.base_data_generator import MockHistoDataGenerator, MockHistoDataType, PANDA_N_CLASSES\n\n\nclass TilesPositioningType(Enum):\n    DIAGONAL = 0\n    RANDOM = 1\n\n\nclass MockPandaSlidesGenerator(MockHistoDataGenerator):\n    \"\"\"Generator class to create mock WSI on the fly.\n        If tiles positioning is diagonal, a mock WSI resembles to:\n                                [**      ]\n                                [  **    ]\n                                [    **  ]\n                                [      **]\n        where * represents 2 tiles stitched along the Y axis.\n        If tiles positioning is random, tiles are positioned randomly on the WSI grid.\n    \"\"\"\n\n    ISUP_GRADE = \"isup_grade\"\n\n    def __init__(\n        self,\n        n_levels: int = 3,\n        n_repeat_diag: int = 4,\n        n_repeat_tile: int = 2,\n        background_val: Union[int, float] = 255,\n        tiles_pos_type: TilesPositioningType = TilesPositioningType.DIAGONAL,\n        n_tiles_list: Optional[List[int]] = None,\n        **kwargs: Any,\n    ) -> None:\n        \"\"\"\n        :param n_levels: Number of levels for multi resolution WSI.\n        :param n_repeat_diag: Number of repeat time along the diagonal axis, defaults to 4.\n        :param n_repeat_tile: Number of repeat times of a tile along both Y and X axes, defaults to 2.\n        :param background_val: A value to assign to the background, defaults to 255.\n        :param tiles_pos_type: The tiles positioning type to define how tiles should be positioned within the WSI grid,\n        defaults to TilesPositioningType.DIAGONAL.\n        :param n_tiles_list: A list to use different n_tiles per slide for randomly positioned tiles.\n        :param kwargs: Same params passed to MockHistoDataGenerator.\n        \"\"\"\n        super().__init__(**kwargs)\n\n        self.n_levels = n_levels\n        self.n_repeat_diag = n_repeat_diag\n        self.n_repeat_tile = n_repeat_tile\n        self.background_val = background_val\n        self.tiles_pos_type = tiles_pos_type\n\n        self.step_size = self.tile_size * self.n_repeat_tile\n        self._dtype = np.uint8 if type(background_val) == int else np.float32\n        self.img_size: int = self.n_repeat_diag * self.n_repeat_tile * self.tile_size\n        self.n_tiles_list = n_tiles_list\n\n        if self.n_tiles_list:\n            assert len(self.n_tiles_list) == self.n_slides, \"n_tiles_list length should be equal to n_slides\"\n            assert self.tiles_pos_type == TilesPositioningType.RANDOM, \"different n_tiles enabled only for randomly \"\n            \"positionned tiles.\"\n\n    def validate(self) -> None:\n        assert (\n            self.n_slides >= PANDA_N_CLASSES\n        ), f\"The number of slides should be >= PANDA_N_CLASSES (i.e., {PANDA_N_CLASSES})\"\n\n    def create_mock_metadata_dataframe(self) -> pd.DataFrame:\n        \"\"\"Create a mock dataframe with random metadata.\"\"\"\n        isup_grades = np.tile(list(self.ISUP_GRADE_MAPPING.keys()), self.n_slides // PANDA_N_CLASSES + 1,)\n        mock_metadata: dict = {\n            col: [] for col in [PandaDataset.SLIDE_ID_COLUMN, PandaDataset.MASK_COLUMN, *PandaDataset.METADATA_COLUMNS]\n        }\n        for slide_id in range(self.n_slides):\n            mock_metadata[PandaDataset.SLIDE_ID_COLUMN].append(f\"_{slide_id}\")\n            mock_metadata[PandaDataset.MASK_COLUMN].append(f\"_{slide_id}_mask\")\n            mock_metadata[self.DATA_PROVIDER].append(np.random.choice(self.DATA_PROVIDERS_VALUES))\n            mock_metadata[self.ISUP_GRADE].append(isup_grades[slide_id])\n            mock_metadata[self.GLEASON_SCORE].append(np.random.choice(self.ISUP_GRADE_MAPPING[isup_grades[slide_id]]))\n        df = pd.DataFrame(data=mock_metadata)\n        csv_filename = self.dest_data_path / PandaDataset.DEFAULT_CSV_FILENAME\n        df.to_csv(csv_filename, index=False)\n\n    def create_mock_wsi(self, tiles: Tensor) -> Tuple[np.ndarray, Optional[np.ndarray]]:\n        if self.tiles_pos_type == TilesPositioningType.DIAGONAL:\n            return self._create_wsi_from_stitched_tiles_along_diagonal_axis(tiles)\n        elif self.tiles_pos_type == TilesPositioningType.RANDOM:\n            return self._create_wsi_from_randomly_positioned_tiles(tiles), None\n        else:\n            raise NotImplementedError\n\n    def _create_wsi_from_stitched_tiles_along_diagonal_axis(self, tiles: Tensor) -> Tuple[np.ndarray, np.ndarray]:\n        \"\"\"Create a whole slide image by stitching tiles along the diagonal axis.\n\n        :param tiles: A tensor of tiles of shape (n_tiles, n_channels, tile_size, tile_size).\n        :return: returns a wsi of shape (img_size, img_size, n_channels) and the tiles used to create it.\n        The image is  in channels_last format so that it can save by TiffWriter.\n        \"\"\"\n        mock_image = np.full(\n            shape=(self.n_channels, self.img_size, self.img_size), fill_value=self.background_val, dtype=self._dtype\n        )\n        dump_tiles = []\n        for i in range(self.n_repeat_diag):\n            if self.mock_type == MockHistoDataType.PATHMNIST:\n                if i == 0 or self.n_tiles > 1:\n                    tile = (\n                        (tiles[i % self.n_tiles].numpy()).astype(self._dtype)\n                        if self._dtype == np.uint8\n                        else tiles[i % self.n_tiles].numpy()\n                    )\n                    # fill the square diagonal with tile repeated n_repeat_tile times along X and Y axis.\n                    fill_square = np.tile(tile, (self.n_repeat_tile, self.n_repeat_tile))\n                    dump_tiles.append(tile)\n\n            elif self.mock_type == MockHistoDataType.FAKE:\n                if i == 0 or self.n_tiles > 1:\n                    # pick a random fake value to fill in the square diagonal.\n                    fill_square = np.random.uniform(0, self.background_val / (self.n_repeat_diag + 1) * (i + 1))\n                    dump_tiles.append(\n                        np.full(\n                            shape=(self.n_channels, self.tile_size, self.tile_size),\n                            fill_value=fill_square,\n                            dtype=self._dtype,\n                        )\n                    )\n            else:\n                raise NotImplementedError\n            mock_image[\n                :, self.step_size * i: self.step_size * (i + 1), self.step_size * i: self.step_size * (i + 1)\n            ] = fill_square\n        return np.transpose(mock_image, (1, 2, 0)), np.array(dump_tiles)  # switch to channels_last.\n\n    def _create_wsi_from_randomly_positioned_tiles(self, tiles: Tensor) -> np.ndarray:\n        \"\"\"Create a whole slide image by positioning tiles randomly in the whole slide image grid.\n\n        :param tiles: A tensor of tiles of shape (n_tiles, n_channels, tile_size, tile_size).\n        :return: returns a wsi of shape (img_size, img_size, n_channels) in channels_last format so that it can save by\n        TiffWriter.\n        \"\"\"\n        mock_image = np.full(\n            shape=(self.n_channels, self.img_size, self.img_size), fill_value=self.background_val, dtype=self._dtype\n        )\n\n        n_tiles_side = self.img_size // self.tile_size\n        total_n_tiles = n_tiles_side ** 2\n        coords = [\n            (k // n_tiles_side, k % n_tiles_side)\n            for k in np.random.choice(total_n_tiles, size=self.n_tiles, replace=False)\n        ]\n        for i in range(self.n_tiles):\n            x, y = self.tile_size * np.array(coords[i])\n            if self.mock_type == MockHistoDataType.PATHMNIST:\n                new_tile = tiles[i].numpy()\n            elif self.mock_type == MockHistoDataType.FAKE:\n                new_tile = np.random.uniform(0, self.background_val / (self.n_repeat_diag + 1) * (i + 1))\n            else:\n                raise NotImplementedError\n            mock_image[:, x: x + self.tile_size, y: y + self.tile_size] = new_tile\n        return np.transpose(mock_image, (1, 2, 0))\n\n    @staticmethod\n    def _save_mock_wsi_as_tiff_file(file_path: Path, wsi_levels: List[np.ndarray]) -> None:\n        \"\"\"Save a mock whole slide image as a tiff file of pyramidal levels.\n        Warning: this function expects images to be in channels_last format (H, W, C).\n\n        :param file_name: The tiff file name path.\n        :param wsi_levels: List of whole slide images of different resolution levels in channels_last format.\n        \"\"\"\n        with TiffWriter(file_path, bigtiff=True) as tif:\n            options = dict(\n                software='tifffile',\n                metadata={'axes': 'YXC'},\n                photometric=PHOTOMETRIC.RGB,\n                compression=COMPRESSION.ADOBE_DEFLATE,  # ADOBE_DEFLATE aka ZLIB lossless compression\n                tile=(16, 16),\n            )\n            for i, wsi_level in enumerate(wsi_levels):\n                # the subfiletype parameter is a bitfield that determines if the wsi_level is a reduced version of\n                # another image.\n                tif.write(wsi_level, **options, subfiletype=int(i > 0))\n\n    def _create_multi_resolution_wsi(self, mock_image: np.ndarray) -> List[np.ndarray]:\n        \"\"\"Create multi resolution versions of a mock image via 2 factor downsampling.\n\n        :param mock_image: A mock image in channels_last format (H, W, 3).\n        :return: Returns a list of n_levels downsampled versions of the original mock image.\n        \"\"\"\n        levels = [mock_image[:: 2 ** i, :: 2 ** i] for i in range(self.n_levels)]\n        return levels\n\n    def generate_mock_histo_data(self) -> None:\n        \"\"\"Create mock wsi and save them as tiff files\"\"\"\n        iterator = iter(self.dataloader) if self.dataloader else None\n\n        slide_dir = self.dest_data_path / \"train_images\"\n        slide_dir.mkdir(parents=True, exist_ok=True)\n        tile_dir = self.dest_data_path / \"dump_tiles\"\n        tile_dir.mkdir(parents=True, exist_ok=True)\n\n        for slide_counter in range(self.n_slides):\n\n            if self.n_tiles_list:\n                self.total_tiles = self.n_tiles_list[slide_counter]\n                self.n_tiles: int = self.n_tiles_list[slide_counter]\n                self.dataloader: torch.utils.data.DataLoader = self.get_dataloader()\n                iterator = iter(self.dataloader)\n\n            tiles, _ = next(iterator) if iterator else (None, None)\n            mock_image, dump_tiles = self.create_mock_wsi(tiles)\n            wsi_levels = self._create_multi_resolution_wsi(mock_image)\n\n            slide_tiff_filename = self.dest_data_path / \"train_images\" / f\"_{slide_counter}.tiff\"\n            self._save_mock_wsi_as_tiff_file(slide_tiff_filename, wsi_levels)\n\n            if dump_tiles is not None:\n                dump_tiles_filename = self.dest_data_path / \"dump_tiles\" / f\"_{slide_counter}.npy\"\n                np.save(dump_tiles_filename, dump_tiles)\n", "path": "hi-ml-cpath/testhisto/testhisto/mocks/slides_generator.py"}]}