Naming mix-ups.
nuclei_detection.inference.nuclei_detection: line 231 -- wsi_scaling_factor = wsi.metadata["downsampling"]
The variable 'downsampling' comes from 'self.config.downsample' in preprocessing at
preprocessing.patch_extraction.src.patch_extraction: line628-638 -- target_*_to_downsample
In the same lines, there is another variable names 'self.rescaling_factor'.
It seems like that rescaling_factor is a relative zoom between level-scale and target-scale, while downsample is the defination of level-scale.
Actually, downsample is defined as a int, and rescaling_factor is defined as a float.
So the code rescaling_factor = downsample in inference-process causes confusion .
Asynchronous scaling.
nuclei_detection.inference.nuclei_detection: line 233-240 -- xy_global = int(patch_size * n * wsi_scaling_factor - overlap * n)
Both patch_size and overlap are used to calculate global position, but only one of them multiplied by wsi_scaling_factor(actually downsample).
This will cause the prediction results to lose their meaning - they neither represent the original prediction results nor the results corresponding to the original image.
Mistake of np.isclosepreprocessing.patch_extraction.src.utils.patch_util: 167 -- is_close = np.isclose(exponent_fraction, nearest_integer, rtol=tolerance)
The method np.isclose takes two params: rtol && atol.
rtol means relative difference which multiplied by the target number like this:
np.isclose(501, 500, rtot=0.1) # True --- 501 is 10% close to 500atol means absolute difference like this:
np.isclose(0.01, 0, atot=0.1) # True --- 0.01 is 0.1 close to 0
In this code block, the relative difference of target_mpp and base_mpp has been calculated by math.log operation, which may transform multiplication relationships into addition relationships.
Observe this:
Missing preprocess info in metadata
preprocessing.patch_extraction.src.utils.patch_util: 86 -- patch_to_tile_size
When rescaling_factor is not 1.0 (why use '==' && '!=' on float, even the float is in fact a integer? And if rescaling_factor e.q. 1.0, nothing will be different when doing the following calculations), the patch_size will be scaled by rescaling_factor and rounded up with math.ceil.
But the calculation here are not reliable.
I tried to fix the positions with wrong outputed and found that the correct fix process should be in different logics here:
Maybe np.ceil -> np.round; Maybe rescaling -> round(rescaling, 3); Maybe base_mpp -> round(base_mpp, 3)
I have no idea what happened indeed. What I know is that some unknown small errors exhibit significant problems on a full image size of 100_000 pixels.
THE TRUELY PROBLEM is that the values used in the preprocessing process, such as tile_size and rescaling_factor, are not recorded in the metadata.
That means I can not restore the output-coordinates to the original image.
There discovers some related issues.
Naming mix-ups.
nuclei_detection.inference.nuclei_detection: line 231 -- wsi_scaling_factor = wsi.metadata["downsampling"]
The variable 'downsampling' comes from 'self.config.downsample' in preprocessing atpreprocessing.patch_extraction.src.patch_extraction: line628-638 -- target_*_to_downsample
In the same lines, there is another variable names 'self.rescaling_factor'. It seems like thatrescaling_factor
is a relative zoom between level-scale and target-scale, whiledownsample
is the defination of level-scale. Actually,downsample
is defined as a int, andrescaling_factor
is defined as a float. So the coderescaling_factor = downsample
in inference-process causes confusion .Asynchronous scaling.
nuclei_detection.inference.nuclei_detection: line 233-240 -- xy_global = int(patch_size * n * wsi_scaling_factor - overlap * n)
Both patch_size and overlap are used to calculate global position, but only one of them multiplied by wsi_scaling_factor(actually downsample). This will cause the prediction results to lose their meaning - they neither represent the original prediction results nor the results corresponding to the original image.Mistake of
np.isclose
preprocessing.patch_extraction.src.utils.patch_util: 167 -- is_close = np.isclose(exponent_fraction, nearest_integer, rtol=tolerance)
The methodnp.isclose
takes two params: rtol && atol. rtol means relative difference which multiplied by the target number like this:np.isclose(501, 500, rtot=0.1) # True --- 501 is 10% close to 500
atol means absolute difference like this:np.isclose(0.01, 0, atot=0.1) # True --- 0.01 is 0.1 close to 0
In this code block, the relative difference of target_mpp and base_mpp has been calculated by math.log operation, which may transform multiplication relationships into addition relationships. Observe this:It actually should be atol instead.
Missing preprocess info in metadata
preprocessing.patch_extraction.src.utils.patch_util: 86 -- patch_to_tile_size
When rescaling_factor is not 1.0 (why use '==' && '!=' on float, even the float is in fact a integer? And if rescaling_factor e.q. 1.0, nothing will be different when doing the following calculations), the patch_size will be scaled by rescaling_factor and rounded up with math.ceil. But the calculation here are not reliable. I tried to fix the positions with wrong outputed and found that the correct fix process should be in different logics here: Maybenp.ceil -> np.round
; Mayberescaling -> round(rescaling, 3)
; Maybebase_mpp -> round(base_mpp, 3)
I have no idea what happened indeed. What I know is that some unknown small errors exhibit significant problems on a full image size of 100_000 pixels. THE TRUELY PROBLEM is that the values used in the preprocessing process, such as tile_size and rescaling_factor, are not recorded in the metadata. That means I can not restore the output-coordinates to the original image.