pythonopencvopencv3.0matchtemplate

python opencv cv2 matchTemplate with transparency


OpenCV 3.0.0 added the ability to specify a mask while performing templateMatch. When I specify a mask I get this error: error: (-215) (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 in function matchTemplateMask

Template image (PNG with transparency):

enter image description here

Source image:

enter image description here

Code

# read the template emoji with the alpha channel
template = cv2.imread(imagePath, cv2.IMREAD_UNCHANGED)
channels = cv2.split(template)
zero_channel = np.zeros_like(channels[0])
mask = np.array(channels[3])

# all elements in alpha_channel that have value 0 are set to 1 in the mask matrix
mask[channels[3] == 0] = 1

# all elements in alpha_channel that have value 100 are set to 0 in the mask matrix
mask[channels[3] == 100] = 0

transparent_mask = cv2.merge([zero_channel, zero_channel, zero_channel, mask])

print image.shape, image.dtype  # (72, 232, 3) uint8
print template.shape, template.dtype  # (40, 40, 4) uint8
print transparent_mask.shape, transparent_mask.dtype    # (40, 40, 4) uint8

# find the matches
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED, mask=transparent_mask)

Is something wrong with the image type? I am unable to find any examples (in Python) using the new mask parameter of the matchTemplate method. Does anyone know how to create the mask?


Solution

  • I was able to get this to work using Python 2.7.13 and opencv-python==3.1.0.4

    Here is the code for it.

    import cv2
    import numpy as np
    import sys
    
    if len(sys.argv) < 3:
        print 'Usage: python match.py <template.png> <image.png>'
        sys.exit()
    
    template_path = sys.argv[1]
    template = cv2.imread(template_path, cv2.IMREAD_UNCHANGED)
    channels = cv2.split(template)
    zero_channel = np.zeros_like(channels[0])
    mask = np.array(channels[3])
    
    image_path = sys.argv[2]
    image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
    
    mask[channels[3] == 0] = 1
    mask[channels[3] == 100] = 0
    
    # transparent_mask = None
    # According to http://www.devsplanet.com/question/35658323, we can only use
    # cv2.TM_SQDIFF or cv2.TM_CCORR_NORMED
    # All methods can be seen here:
    # http://docs.opencv.org/2.4/doc/tutorials/imgproc/histograms/template_matching/template_matching.html#which-are-the-matching-methods-available-in-opencv
    method = cv2.TM_SQDIFF  # R(x,y) = \sum _{x',y'} (T(x',y')-I(x+x',y+y'))^2 (essentially, sum of squared differences)
    
    transparent_mask = cv2.merge([zero_channel, zero_channel, zero_channel, mask])
    result = cv2.matchTemplate(image, template, method, mask=transparent_mask)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    print 'Lowest squared difference WITH mask', min_val
    
    # Now we'll try it without the mask (should give a much larger error)
    transparent_mask = None
    result = cv2.matchTemplate(image, template, method, mask=transparent_mask)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    print 'Lowest squared difference WITHOUT mask', min_val
    

    Here it is as a gist.

    Essentially, you need to make sure you're using the right matching method.