Repository navigation
Add a fast histogram-based median filter - #317
Conversation
This is based on code in opencv-contrib, but has been expanded to support a more flexible choice of histogram bins and integer data types. Add more thorough parameterized test cases to verify expected behavior. Fix: disallow size 1 filters from histogram-based code path expand tests for the out parameter Add tests for int8, int16 and uint16 as well add more user-friendly shared memory limit checks
insufficient resources are available for the histogram-based approach
This can be used to allow overriding the 'partitions' setting for the histogram-based algorithm also fix bug where 'sorting' mode was preferred for small kernels even if the user explicitly asked for algorithm='histogram'
This seems to have fixed stochastic test failures locally
With this fix, cases like cp.int16 data with values in range [128, 384) will give the correct result and use only 256 histogram bins.
Do not allow hist_size to be less than 256 within _median_hist remove debugging code
unsure why some failures were observed with hist_size 4096 on CI need to revisit at some point
jakirkham
left a comment
There was a problem hiding this comment.
Thanks Greg! 🙏
Had a couple questions above
| # TODO: Can't currently pass an output array into _median_hist as a | ||
| # new array currently needs to be created during padding. | ||
| temp = _median_hist(image, footprint, mode=mode, cval=cval, | ||
| **algorithm_kwargs) |
There was a problem hiding this comment.
Would it make sense to turn this TODO into an issue?
There was a problem hiding this comment.
Sure, I opened #352 to mention this as well as potentially extending to 3D
|
I see there is an isolated test failure with 16-bit that I can't reproduce locally. I am tempted for now to only enable this histogram-based algorithm for 8-bit integer types. |
passes locally, but fails in CI test job
|
That seems reasonable. We can always relax it later after we fix issues with other types |
| __device__ void histogramAddAndSubCoarse(HIST_INT_T* H, const HIST_INT_T* hist_colAdd, const HIST_INT_T* hist_colSub){ | ||
| int tx = threadIdx.x; | ||
| if (tx<HIST_SIZE_COARSE){ | ||
| H[tx]+=hist_colAdd[tx]-hist_colSub[tx]; | ||
| } | ||
| } |
There was a problem hiding this comment.
Maybe it is worth pulling this code out into C files that we load and then compile?
There was a problem hiding this comment.
That was the route I ended up going with the distance transform PR. We could do something similar here.
There was a problem hiding this comment.
Thanks Greg! 🙏
Think we may want to start doing this in more places going forward. Filed issue ( #375 ) about it
| # TODO: look into using CUB's Block-wise collectives (e.g. BlockScan) | ||
| def _gen_preamble_median(hist_size_coarse, hist_size_fine): |
There was a problem hiding this comment.
Should we file an issue about this?
| rng = cp.random.default_rng(123) | ||
| shape = (350, 407) | ||
| if int_dtype == cp.uint16: | ||
| # test with 12-bit range stored in 16-bit integers (e.g. DICOM) |
There was a problem hiding this comment.
Possibly also worth an issue to track
jakirkham
left a comment
There was a problem hiding this comment.
Thanks Greg! 🙏
Generally this looks good. Noted a few things above that we might want to follow up on
|
@gigony could you please look at this as well? |
gigony
left a comment
There was a problem hiding this comment.
Thanks Greg! Looks good to me!
|
@gpucibot merge |
|
Thanks Greg for the PR and Gigon for helping review! 🙏 |

Background
closes #320
This PR adds a fast histogram-based median filter. It is based on a paper by Oded Green [1] as available in
opencv_contrib. It is a GPU-based adaptation of an earlier CPU-based publication [2].This new functionality has been added to the existing
medianfunction and will be automatically used when possible (although the user can override the automated choice via a new keyword-onlyalgorithmparameter.The CUDA file here has a BSD 3-clause license up top, although the repository states that it is Apache 2.0 licensed, so that is likely out of date. We do already list OpenCV's extra modules and the Apache 2.0 license for it in our 3rdparty license folder.
References
[1] O. Green, "Efficient Scalable Median Filtering Using Histogram-Based Operations," in IEEE Transactions on Image Processing, vol. 27, no. 5, pp. 2217-2228, May 2018, https://doi.org/10.1109/TIP.2017.2781375.
[2] S. Perreault and P. Hebert, "Median Filtering in Constant Time," in IEEE Transactions on Image Processing, vol. 16, no. 9, pp. 2389-2394, Sept. 2007, https://doi.org/10.1109/TIP.2007.902329.
Improvements
There are a number of generalizations in the implementation here as compared to the one in OpenCV:
Notes/Limitations
The GPU I tested this on has a static shared memory limit of 48k per block. This means that the
HFineshared memory array can exceed the available GPU memory once there are more than ~12000 gray levels in the histogram. So, for int16 or uint16, we I can only run the algorithm on images where the min/max range does not exceed that value. One fairly common case in medical imaging is DICOM images with 12-bit range (0-4095) that are stored at 16-bit int. This shared memory limit is compatible with this case.Performance
see summary in the next comment