opencv / opencv

Open Source Computer Vision Library
https://opencv.org
Apache License 2.0
78.75k stars 55.79k forks source link

Strange results of matchTemplate using TM_CCORR_NORMED with mask #15768

Open AutumnSun1996 opened 5 years ago

AutumnSun1996 commented 5 years ago
System information (version)
Detailed description

Results of cv.matchTemplate(image, needle, cv.TM_CCORR_NORMED, mask=mask) sometimes contains many values larger than 1 at stange position.

It happens only for TM_CCORR_NORMED with mask. Also, it seems if the image has odd width the problem will not be triggered.

Steps to reproduce

This only happens with some special shapes of image, it seems odd width never trigger the bug.

opencvtest.zip

out.txt shows the result produced by python cvtest.py on my machine, including output of cv.getBuildInformation().

I'm not sure what causes this problem. Opencv 3.4.4 on windows and 3.1.0 on mac has the same problem, but the err_count varies. Another build of 4.1.1 on linux doesn't has this problem.

alalek commented 5 years ago

larger than 1 at strange position

Floating-point calculations are not very accurate in general. What is the max observed value?

If you have strong requirement of [0; 1] range, then it is better to clamp values after matchTemplate() call

AutumnSun1996 commented 5 years ago

There are values like 17.003805 or 3.4028235e+38. The problem is when this problem occurs, the values after clamp maybe still not what we want from the function. My usage is to check of two images are similar, using a fixed thresh. When the problem happens, my program always thinks the two images are the same, while they are quit different.

catree commented 5 years ago

Could this be related? #15214

AutumnSun1996 commented 5 years ago

I think #15214 is a possible solution. Is there any way I can test it on my machine or with my python code?

AutumnSun1996 commented 4 years ago

I found some clues about the problem. It seems like some sort of overflow or underflow problem. If the image and needle has only zeros, the result will be nan. And when the image and needle are close to zero, the strange results appears, and likely with some nan. The 4.4.0 version fixed the problem in some conditions, but not all. There are still 3.4028235e+38 (0xffff7f7f) and -3.4028235e+38(0xffff7fff) in results.

I implemented a TM_CCORR_NORMED algorithm in pytorch. It still gives nan, but not strange huge floats.

pbrimmers-isra commented 3 years ago

I still have same problem with version 4.5.2 that nan and inf are occuring with TM_CCORR_NORMED and mask provided. I currently asssume that these values are not usefull and have an extra step to set these values to 0 but this means an extra step with iteration over full array.

joecabezas commented 3 years ago

Have same issue when using template matching on version 4.5.3, with a mask, come values in the result comes as nan, which is expected if the method is using division by zero in the underlying implementation or something shen using masks

chriscauley commented 2 years ago

If anyone's looking for a one line fix, you can just set them to zero. This is definitely a hack though.

    res = cv2.matchTemplate(img1, img2, cv2.TM_CCORR_NORMED, mask=img2)
    res[res == float('inf')] = 0 # bug https://github.com/opencv/opencv/issues/15768                     
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
hmzawz2 commented 1 year ago

images.zip Here is an example. Use template image to match source image (with a all 1 mask, same size as template image), the result response map has inf and -inf. My code is like this:

    // source is source.png, tmpl is template.png
    cv::Mat mask(tmpl.rows, tmpl.cols, CV_8UC1, cv::Scalar(1));
    cv::Mat opencv_res_map;
    cv::matchTemplate(source, tmpl, opencv_res_map, cv::TM_CCOEFF_NORMED, mask);
    opencv_res_map.convertTo(opencv_res_map, CV_64FC1);

    // check
    for (int idy = 0; idy < opencv_res_map.rows; idy++)
    {
        for (int idx = 0; idx < opencv_res_map.cols; idx++)
        {
            const double& d1 = opencv_res_map.at<double>(idy, idx);
            if (d1 < -1.-1e-9 || d1 > 1.+1e-9)
            {
                // inf or -inf
            }
        }
    }

And I use opencv4.5.5 on windows11, msvc 19.

If this problem is caused by overflow, should we check whether other results value is correct?