-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add transforms for Digital Pathology #100
Conversation
@shekhardw Please specify the absolute path to load the test data. (pytest is run in root of the repository so that relative paths are not available). How to reproduce
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check/auto-format using black/isort.
pip install black
python -m black python/cucim/src/cucim/core/ --check # check
python -m black python/cucim/src/cucim/core/ # autofix
pip install isort
python -m isort python/cucim/src/cucim/core/ --check # check
python -m isort python/cucim/src/cucim/core/ # autofix
python/cucim/src/cucim/core/operations/color/tests/test_color_jitter.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @shekhardw,
In general the kernels look nice and performance is great, but input validations, docstrings and test cases are very minimal.
For example, it is not always clear from the docstrings:
1.) which dtypes are supported
2.) whether RGB, RGBA and/or arbitrary number of "channels" are supported
3.) whether an additional "batch" dimension is supported.
I have made a number of suggestions, but it could probably use another pass after these are addressed.
I did not review the C++ code for all individual CUDA kernels, but the ones I looked at seemed good to me.
from typing import Any, List, Optional, Tuple | ||
|
||
import numpy as np | ||
import scipy.ndimage as ndimage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick, but to be consistent with the cucim.skimage
and upstream scikit-image, I would use
import scipy.ndimage as ndi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes made
diff_im = np_output - arr | ||
diff_total_value = np.abs(np.sum(diff_im)) | ||
|
||
assert diff_total_value > 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This very minimal test coverage that only verifies that the kernels run without crashing and that some change to the image has occurred.
Why not place this in a named test function, though, so pytest would report it as a test case?
You could then add additional tests in separate functions. Additional simple tests would be:
- a test case with all factors set to 0, verifying that the image is unmodified in that case.
- a test with a batch image, e.g. using
cupy_arr_batch = cupy.stack((cupy_arr, ) * 8, axis=0)
. In this case since all elements in the batch are identical we could also verify that for the output. - a test verifying that providing
numpy.ndarray
input results in anumpy.ndarray
output - same output with C or Fortran-ordered inputs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test cases included for all factors 0, batch image, cupy input, numpy input. Color jitter picks a random sequence to apply 4 operations. Test functions included in all test files.
python/cucim/src/cucim/core/operations/spatial/rotate_and_flip.py
Outdated
Show resolved
Hide resolved
python/cucim/src/cucim/core/operations/spatial/rotate_and_flip.py
Outdated
Show resolved
Hide resolved
python/cucim/src/cucim/core/operations/spatial/rotate_and_flip.py
Outdated
Show resolved
Hide resolved
R = np.random.RandomState() | ||
|
||
if R.rand() < prob: | ||
return image_rotate_90(img, spatial_axis) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return image_rotate_90(img, spatial_axis) | |
return image_flip(img, spatial_axis) |
wrong function!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
|
||
import cucim.core.operations.spatial as spt | ||
|
||
img = Image.open(os.path.join(os.path.abspath(os.path.dirname(__file__)), "1.png")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the source of this dice image? (i.e. is it public domain?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is any rights issue it should be easy enough to just use one of the images bundled with scikit-image.
e.g. for an RGB image:
skimage.data.astronaut()
(will need to transpose dimensions to get the color axis from last to first)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test image updated to use astronaut image from scikit-image. Additional test cases included.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @grlee77 for the review! @shekhardw would address your comments.
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from cucim.core.operations.color import color_jitter as color_jitter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @grlee77 . Let me chime in this question.
We didn't decide what/how to expose APIs from cucim.core.operations
and it looks like MONAI's transforms require APIs in a specific form and prone to change.
For this reason, Shekhar and I decided to use cucim.core.operations.expose.transform for exposing APIs needed by MONAI transforms, without messing up cucim.core.operations.xxx
namespaces.
We need to discuss how to use cucim.core.operations
namespace in detail, as a group, in the next cuCIM bi-weekly meeting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me.
please format the updated code so that it can pass CI/CD checks.
@grlee77 Please help review and approval the changes.
return arr_o | ||
|
||
def test_rand_flip_numpy_input(): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: blank line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
corrected.
assert np.allclose(output,flip_arr) | ||
|
||
def test_rand_flip_zero_prob(): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: blank line
please check other parts of test methods that start with a blank line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
corrected and all test functions checked.
rerun tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing the comments @shekhardw. The tests are looking a lot better now and can always be expanded further as needed if issues crop up.
Thanks for also adding the dimension info to the docstrings.
Out of curiosity, what is the source of the 1.png
image used in the color tests? (it appears as somewhat random colors). Is there a particular reason for testing with this image?
# limitations under the License. | ||
|
||
from cucim.core.operations.color import color_jitter as color_jitter | ||
from cucim.core.operations.intensity import rand_zoom as rand_zoom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from cucim.core.operations.intensity import rand_zoom as rand_zoom | |
from cucim.core.operations.intensity import rand_zoom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@grlee77 1.png was created out of random array. I will change it with astronaut image (to be consistent).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also change import skimage
to import skimage.data
in the tests as suggested in the recently added comments. I was surprised the former works and I think it may not work across all older scikit-image versions!
It happens to work because there is a from .data import data_dir
in skimage.__init__
which apparently loads data
into the skimage
namespace (see here). It is safer not to rely on this incidental import of data
and just import it explicitly.
python/cucim/src/cucim/core/operations/color/tests/test_color_jitter.py
Outdated
Show resolved
Hide resolved
python/cucim/src/cucim/core/operations/intensity/tests/test_rand_zoom.py
Outdated
Show resolved
Hide resolved
python/cucim/src/cucim/core/operations/intensity/tests/test_scaling.py
Outdated
Show resolved
Hide resolved
python/cucim/src/cucim/core/operations/intensity/tests/test_zoom.py
Outdated
Show resolved
Hide resolved
python/cucim/src/cucim/core/operations/spatial/tests/test_flip.py
Outdated
Show resolved
Hide resolved
python/cucim/src/cucim/core/operations/spatial/tests/test_random_flip.py
Outdated
Show resolved
Hide resolved
Thanks @grlee77 test cases updated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Shekhar! 😄
Had a couple questions/suggestion below. For simplicity and clearer discussion focused only on one case of the pattern for discussion, but the same could be applied in other places where that pattern is used
def get_params(brightness: Optional[List[float]], | ||
contrast: Optional[List[float]], | ||
saturation: Optional[List[float]], | ||
hue: Optional[List[float]] | ||
) -> Tuple[np.ndarray, Optional[float], | ||
Optional[float], Optional[float], | ||
Optional[float]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary for these to be closures? Or could we define module level functions for these tasks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shekhardw, any thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose to fix this in a separate MR.
_logger.error("[cucim] " + str(e), exc_info=True) | ||
_logger.info("Error executing color jitter on GPU") | ||
raise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to handle logging within cuCIM? Typically this is the sort of thing that would be configured in a user application (unless for some reason the exception would never be seen)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shekhardw, any thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose to fix this in a separate MR.
extern "C" { | ||
__global__ void brightnessjitter_kernel(unsigned char *input_rgb, \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to put these in C file? Might make this is easier to read/lint/test/etc. and also use directly if desired. From the Python side we could always load the file into a str
for use as needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shekhardw, any thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I propose to fix this in a separate MR.
python/cucim/src/cucim/core/operations/color/tests/test_color_jitter.py
Outdated
Show resolved
Hide resolved
python/cucim/src/cucim/core/operations/expose/tests/test_expose.py
Outdated
Show resolved
Hide resolved
def test_exposed_transforms(): | ||
assert color_jitter is not None | ||
assert image_flip is not None | ||
assert image_rotate_90 is not None | ||
assert scale_intensity is not None | ||
assert zoom is not None | ||
assert rand_zoom is not None | ||
assert rand_image_flip is not None | ||
assert rand_image_rotate_90 is not None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the goal just to test that these just exist or use them in some way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just the existence for exposing. Each of the transforms has its own set of test cases.
@jakirkham Thanks for your valuable feedback. I have updated expose related changes. |
Thanks Shekhar! 😄 There's a couple comments above that may have been missed. Bumped them above. Please let me know your thoughts on those |
Hi @jakirkham, I propose to address the 3 remaining commments on closure, logging and cuda kernel in a separate MR. Are you fine with that? |
In that case could you please raise an issue tracking these? |
Co-authored-by: Gregory R. Lee <[email protected]>
…aling.py Co-authored-by: Gregory R. Lee <[email protected]>
Co-authored-by: Gregory R. Lee <[email protected]>
…om_flip.py Co-authored-by: Gregory R. Lee <[email protected]>
…om.py Co-authored-by: Gregory R. Lee <[email protected]>
…nd_zoom.py Co-authored-by: Gregory R. Lee <[email protected]>
…jitter.py Co-authored-by: Gregory R. Lee <[email protected]>
…e.py Co-authored-by: jakirkham <[email protected]>
…jitter.py Co-authored-by: jakirkham <[email protected]>
bdfd569
to
c77b8f8
Compare
./src/cucim/core/operations/expose/transform.py:18:1: F40 'cucim.core.operations.spatial.rand_image_rotate_90' imported but unused
@gpucibot merge |
Thanks Shekhar for the PR and Gigon and Greg for the reviews! 😄 |
closes #114 Removes the try/except wrappers with logging of errors from the digital pathology transforms added in #100. Also renames a variable `to_cupy` to `to_numpy` instead to reflect its actual function. **For reviewers**: There appear to be many lines changed here, but it is mostly a change in indentation due to removal of `try/except` blocks with `_logger` calls. Also some nested function definitions within `color_jitter` were moved outside of that function. Authors: - Gregory Lee (https://github.com/grlee77) Approvers: - Gigon Bae (https://github.com/gigony) - https://github.com/jakirkham
Implement transforms for color jitter, image scale intensity, image flip and rotate included for batched RGB images.
This will be used exclusively for digital pathology training pipelines.
closes #105