Repository: mlcommons/GaNDLF

Issue:
For NIfTI images, it would be a good idea to have a "soft" sanity check
**Is your feature request related to a problem? Please describe.**
Sometimes, when a NIfTI image is written, the header of file doesn't _**exactly**_ match what was there in the datastructure, which causes issues for the sanity checker.

**Describe the solution you'd like**
In the sanity checking algorithm, put a "softer" check instead of an absolute zero:
```python
>>> arr_2=np.array(mask_2_from_file.GetDirection())
>>> arr_1=np.array(mask_2.GetDirection())
>>> diff=arr_1-arr_2 
>>> np.sum(diff)    
1.2273507557503116e-09
```
We can have a maximum threshold of 1% difference.

**Describe alternatives you've considered**
This is required to process images.

**Additional context**
N.A.



Discussion:

Resolving patch:
diff --git a/GANDLF/data/ImagesFromDataFrame.py b/GANDLF/data/ImagesFromDataFrame.py
--- a/GANDLF/data/ImagesFromDataFrame.py
+++ b/GANDLF/data/ImagesFromDataFrame.py
@@ -75,6 +75,7 @@ def ImagesFromDataFrame(
     dataframe.index = range(0, num_row)
     # This list will later contain the list of subjects
     subjects_list = []
+    subjects_with_error = []
 
     channelHeaders = headers["channelHeaders"]
     labelHeader = headers["labelHeader"]
@@ -181,7 +182,11 @@ def ImagesFromDataFrame(
                     + subject["subject_id"]
                     + "'"
                 )
-            perform_sanity_check_on_subject(subject, parameters)
+            try:
+                perform_sanity_check_on_subject(subject, parameters)
+            except Exception as e:
+                print(e)
+                subjects_with_error.append(subject["subject_id"])
 
             # # padding image, but only for label sampler, because we don't want to pad for uniform
             if "label" in sampler or "weight" in sampler:
@@ -200,6 +205,12 @@ def ImagesFromDataFrame(
             # Appending this subject to the list of subjects
             subjects_list.append(subject)
 
+    if subjects_with_error:
+        raise ValueError(
+            "The following subjects could not be loaded, please recheck or remove and retry:",
+            subjects_with_error,
+        )
+
     transformations_list = []
 
     # augmentations are applied to the training set only
diff --git a/GANDLF/utils/imaging.py b/GANDLF/utils/imaging.py
--- a/GANDLF/utils/imaging.py
+++ b/GANDLF/utils/imaging.py
@@ -88,6 +88,27 @@ def resize_image(input_image, output_size, interpolator=sitk.sitkLinear):
     )
 
 
+def softer_sanity_check(base_property, new_property, threshold=0.00001):
+    """
+    This function checks if the new property is within the threshold of the base property.
+    Args:
+        base_property (float): The base property to check.
+        new_property (float): The new property to check
+        threshold (float, optional): The threshold to check if the new property is within the base property. Defaults to 0.00001.
+    Returns:
+        bool: Whether the new property is within the threshold of the base property.
+    """
+    arr_1 = np.array(base_property)
+    arr_2 = np.array(new_property)
+    diff = np.sum(arr_1 - arr_2)
+
+    result = False
+    if diff <= threshold:
+        result = True
+
+    return result
+
+
 def perform_sanity_check_on_subject(subject, parameters):
     """
     This function performs sanity check on the subject to ensure presence of consistent header information WITHOUT loading images into memory.
@@ -133,6 +154,7 @@ def perform_sanity_check_on_subject(subject, parameters):
                     # this case is required if any tensor/imaging operation has been applied in dataloader
                     file_reader_current = subject[str(key)].as_sitk()
 
+                # this check needs to be absolute
                 if (
                     file_reader_base.GetDimension()
                     != file_reader_current.GetDimension()
@@ -143,16 +165,18 @@ def perform_sanity_check_on_subject(subject, parameters):
                         + "' are not consistent."
                     )
 
-                if file_reader_base.GetOrigin() != file_reader_current.GetOrigin():
+                # other checks can be softer
+                if not softer_sanity_check(
+                    file_reader_base.GetOrigin(), file_reader_current.GetOrigin()
+                ):
                     raise ValueError(
                         "Origin for Subject '"
                         + subject["subject_id"]
                         + "' are not consistent."
                     )
 
-                if (
-                    file_reader_base.GetDirection()
-                    != file_reader_current.GetDirection()
+                if not softer_sanity_check(
+                    file_reader_base.GetDirection(), file_reader_current.GetDirection()
                 ):
                     raise ValueError(
                         "Orientation for Subject '"
@@ -160,7 +184,9 @@ def perform_sanity_check_on_subject(subject, parameters):
                         + "' are not consistent."
                     )
 
-                if file_reader_base.GetSpacing() != file_reader_current.GetSpacing():
+                if not softer_sanity_check(
+                    file_reader_base.GetSpacing(), file_reader_current.GetSpacing()
+                ):
                     raise ValueError(
                         "Spacing for Subject '"
                         + subject["subject_id"]


Test patch: