Repository: mlcommons/GaNDLF

Issue:
Stride size and patch size only work when `selected_level=0`
**Describe the bug**
In the case of histology inference, `stride_size` and `patch_size` do not scale with `selected_level`.

**To Reproduce**
Steps to reproduce the behavior:
When using different values of `selected_level`, outputs are different.

**Expected behavior**
The output should be same, especially when `patch_size` is defined in terms of microns.

**Screenshots**
N.A.

**GaNDLF Version**
<!-- Put the output of the following command:
python -c 'import GANDLF as g;print(g.__version__)'
-->
0.0.15-dev

**Desktop (please complete the following information):**
 - OS: N.A.
 - Version: N.A.

**Additional context**
N.A.



Discussion:

Resolving patch:
diff --git a/GANDLF/compute/inference_loop.py b/GANDLF/compute/inference_loop.py
--- a/GANDLF/compute/inference_loop.py
+++ b/GANDLF/compute/inference_loop.py
@@ -121,13 +121,8 @@ def inference_loop(
         print(average_epoch_valid_loss, average_epoch_valid_metric)
     elif parameters["modality"] in ["path", "histo"]:
         # set some defaults
-        if not "slide_level" in parameters:
-            parameters["slide_level"] = 0
         parameters["stride_size"] = parameters.get("stride_size", None)
 
-        if not "mask_level" in parameters:
-            parameters["mask_level"] = parameters["slide_level"]
-
         output_to_write = "SubjectID,x_coords,y_coords"
         if parameters["problem_type"] == "regression":
             output_to_write += ",output"
@@ -143,6 +138,15 @@ def inference_loop(
             os_image = openslide.open_slide(
                 row[parameters["headers"]["channelHeaders"]].values[0]
             )
+            max_defined_slide_level = os_image.level_count - 1
+            parameters["slide_level"] = parameters.get("slide_level", 0)
+            parameters["slide_level"] = min(
+                parameters["slide_level"], max_defined_slide_level
+            )
+            parameters["slide_level"] = max(parameters["slide_level"], 0)
+            parameters["mask_level"] = parameters.get(
+                "mask_level", parameters["slide_level"]
+            )
             level_width, level_height = os_image.level_dimensions[
                 int(parameters["slide_level"])
             ]
@@ -152,7 +156,7 @@ def inference_loop(
             Path(subject_dest_dir).mkdir(parents=True, exist_ok=True)
 
             count_map = np.zeros((level_height, level_width), dtype=np.uint8)
-            ## this can probably be made into a single multi-class probability map that functions for all workloads
+            # this can probably be made into a single multi-class probability map that functions for all workloads
             probs_map = np.zeros(
                 (parameters["model"]["num_classes"], level_height, level_width),
                 dtype=np.float16,
@@ -237,14 +241,14 @@ def inference_loop(
                     output_to_write += "\n"
 
             # ensure probability map is scaled
-            # Updating variables to save memory
+            # reusing variables to save memory
             probs_map = np.divide(probs_map, count_map)
 
             # Check if out_probs_map is greater than 1, print a warning
             if np.max(probs_map) > 1:
                 # Print a warning
                 print(
-                    "Warning: Probability map is greater than 1, report the images to GANDLF developers"
+                    "Warning: Probability map is greater than 1, report the images to GaNDLF developers"
                 )
 
             count_map = np.array(count_map * 255, dtype=np.uint16)
@@ -256,9 +260,7 @@ def inference_loop(
                 count_map,
             )
 
-            import cv2
-
-            if parameters["problem_type"] == "segmentation":
+            if parameters["problem_type"] != "segmentation":
                 output_file = os.path.join(
                     subject_dest_dir,
                     "predictions.csv",
@@ -266,54 +268,28 @@ def inference_loop(
                 with open(output_file, "w") as f:
                     f.write(output_to_write)
 
-                for n in range(parameters["model"]["num_classes"]):
-                    file_to_write = os.path.join(
-                        subject_dest_dir, "probability_map_" + str(n) + ".png"
-                    )
-                    heatmap = cv2.applyColorMap(
-                        np.array(
-                            probs_map[n, ...] * 255,
-                            dtype=np.uint8,
-                        ),
-                        cv2.COLORMAP_JET,
-                    )
-                    cv2.imwrite(file_to_write, heatmap)
-
-                    file_to_write = os.path.join(
-                        subject_dest_dir, "seg_map_" + str(n) + ".png"
-                    )
-
-                    segmap = ((probs_map[n, ...] > 0.5).astype(np.uint8)) * 255
+            import cv2
 
-                    cv2.imwrite(file_to_write, segmap)
-            else:
-                output_file = os.path.join(
-                    subject_dest_dir,
-                    "predictions.csv",
+            for n in range(parameters["model"]["num_classes"]):
+                file_to_write = os.path.join(
+                    subject_dest_dir, "probability_map_" + str(n) + ".png"
                 )
-                with open(output_file, "w") as f:
-                    f.write(output_to_write)
-
-                for n in range(parameters["model"]["num_classes"]):
-                    file_to_write = os.path.join(
-                        subject_dest_dir, "probability_map_" + str(n) + ".png"
-                    )
-                    heatmap = cv2.applyColorMap(
-                        np.array(
-                            probs_map[n, ...] * 255,
-                            dtype=np.uint8,
-                        ),
-                        cv2.COLORMAP_JET,
-                    )
-                    cv2.imwrite(file_to_write, heatmap)
+                heatmap = cv2.applyColorMap(
+                    np.array(
+                        probs_map[n, ...] * 255,
+                        dtype=np.uint8,
+                    ),
+                    cv2.COLORMAP_JET,
+                )
+                cv2.imwrite(file_to_write, heatmap)
 
-                    file_to_write = os.path.join(
-                        subject_dest_dir, "seg_map_" + str(n) + ".png"
-                    )
+                file_to_write = os.path.join(
+                    subject_dest_dir, "seg_map_" + str(n) + ".png"
+                )
 
-                    segmap = (probs_map[n, ...] > 0.5).astype(np.uint8)
+                segmap = ((probs_map[n, ...] > 0.5).astype(np.uint8)) * 255
 
-                    cv2.imwrite(file_to_write, segmap)
+                cv2.imwrite(file_to_write, segmap)
 
 
 if __name__ == "__main__":
diff --git a/GANDLF/data/inference_dataloader_histopath.py b/GANDLF/data/inference_dataloader_histopath.py
--- a/GANDLF/data/inference_dataloader_histopath.py
+++ b/GANDLF/data/inference_dataloader_histopath.py
@@ -52,7 +52,7 @@ def __init__(
         self._stride_size = stride_size
         if self._stride_size is None:
             self._stride_size = (
-                (np.array(self._patch_size) / 2).astype(np.int8).tolist()
+                (np.array(self._patch_size) / 2).astype(np.uint16).tolist()
             )
         self._stride_size = get_patch_size_in_microns(wsi_path, self._stride_size)
         self._selected_level = selected_level


Test patch: