Skip to content

added optional mask to cv::threshold#26842

Merged
asmorkalov merged 28 commits intoopencv:4.xfrom
chacha21:threshold_with_mask
Mar 12, 2025
Merged

added optional mask to cv::threshold#26842
asmorkalov merged 28 commits intoopencv:4.xfrom
chacha21:threshold_with_mask

Conversation

@chacha21
Copy link
Copy Markdown
Contributor

Proposal for #26777

To avoid code duplication, and keep performance when no mask is used, inner implementation always propagate the const cv::Mat& mask, but they use a template parameter that let the compiler optimize out unecessary tests when the mask is not to be used.

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
  • The PR is proposed to the proper branch
  • There is a reference to the original bug report and related work
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • The feature is well documented and sample code can be built with the project CMake

To avoid code duplication, and keep performance when no mask is used, inner implementation always propagate the const cv::Mat& mask, but they use a template<bool useMask> parameter that let the compiler optimize out unecessary tests when the mask not to be used.
if constexpr(...) requires C++17
the "readParity" is designed to handle the fact that vector operations always read 16 mask values, while data type can represent less pixels
First fix, but the tests sometimes fail, still investigating...
…e, disabling it for now

Still don't understand why unit test randomly fails
…e, disabling it for now

Still don't understand why unit test randomly fails

prepare mutli-channel handling
When therere were non-vectorized pixels  remaining, the mask pointer was not properly reset
that was a memory fault (not always triggered), and the test case only hit the case when that last pixel was greater than the threshold
new strategy to load masks and duplicate bytes to match src.elemSize()
instead of complex byte duplication in loaded mask vector elements, use vx_lut() instead with sliding indices
now that cn>1 works, the simplest case src.type() == CV_8UC1 can be a fast path
ocl should have failed before for the case cn>1
somehow, the ocl currently does not fail as expected even when trying to corrupt the output by setting stupid values to dst in the kernel
The test is not reliable, and I don't know why
there was an error in ocl kernel args, it wasn't even run and relied on CPU implementation
@opencv-alalek
Copy link
Copy Markdown
Contributor

We need "clear" definition what values should be used as result outside of the mask.

Al least, this is why cv::compare() doesn't have mask support.

P.S. If your mask is a rectangle - then you should use ROI approach instead.

@chacha21
Copy link
Copy Markdown
Contributor Author

We need "clear" definition what values should be used as result outside of the mask.

Al least, this is why cv::compare() doesn't have mask support.

Yes, that's what I thought when I started working on the feature.
My most useful usecase is to leave outliers untouched. This assumes that the dst image was pre-existing, otherwise they will be undefined pixel values after being allocated by cv::threshold()
I use such a feature to apply different binarizations to different (non rectangular) parts of an image according to some criteria.

@asmorkalov
Copy link
Copy Markdown
Contributor

@chacha21 Could you list use-cases for the PR?
The proposal was discussed on weekly core team meeting and the team has doubt if we need the change. Alexander's concern is on of reasons.

@chacha21
Copy link
Copy Markdown
Contributor Author

chacha21 commented Jan 31, 2025

@chacha21 Could you list use-cases for the PR? The proposal was discussed on weekly core team meeting and the team has doubt if we need the change. Alexander's concern is on of reasons.

Ok. First of all, no problem to decline the PR, I won't try to force push it.

Here are my two use cases :

case 1

  • A gray-level image with different areas (not rectangular) having different lighting conditions
  • not a single threshold will binarize the whole image, there must be one threshold (from OTSU) per area
  • currently, I have to use multiple masks and combinations to build to final image, and computing the OTSU threshold for a non rectangular area is a problem because of outliers pixels

case 2

  • a gray-level image is displayed to the user
  • the user can draw non-rectangular ROIs (e.g. RotatedRect areas) and want to binarize inside those areas in order to preview how binarization will highlight information of interest

@asmorkalov asmorkalov self-assigned this Mar 3, 2025
@asmorkalov asmorkalov added this to the 4.12.0 milestone Mar 3, 2025
@asmorkalov
Copy link
Copy Markdown
Contributor

Hello @chacha21 Sorry for delay. We discussed to the PR on the Core team meeting and we are ready to merge the PR with several fixes:

  • The new function dst should be defined as InputoutputArray and the function should check that the output buffer is pre-allocated and has enough size. Document the behaviour.
  • The dst value with mask==0 should not be modified as you in your proposal. The behaviour is documented.
  • HAL is not needed here. The case is too exotic.
  • We recommend to use another name like thresholdWithMask to reduce chance of API collision in Python and Java.
  • Add python test for the new function to ensure it's wrapped correctly.

The fixes are proposed to get rid of undefined behaviour for the case where mask==0 and wrap things to Python, Java, etc correctly.

@asmorkalov
Copy link
Copy Markdown
Contributor

Please also rebase and fix merge conflicts.

chacha21 added 2 commits March 7, 2025 14:44
- do not overload threshold, use thresholdWithMask instead
- document required dst if mask is used
- do not use HAL implementation for the masked case
@chacha21
Copy link
Copy Markdown
Contributor Author

chacha21 commented Mar 7, 2025

  • Add python test for the new function to ensure it's wrapped correctly.

I am sorry I have 0 skills in Python

added accuracy test using a rectangular ROI
chacha21 and others added 2 commits March 11, 2025 15:51
- split accuracy tests for different cases (otsu/triangle/fixed)
- clean up non existent HAL versions
- update reference to author
@asmorkalov asmorkalov merged commit 0db6a49 into opencv:4.x Mar 12, 2025
49 of 55 checks passed
@asmorkalov asmorkalov mentioned this pull request Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants