0

I am using OpenCV2 to work on an auto-block function for a game, so simply: If the red indicator that shows up in a specific region has a max_val higher than threshold, press the key specified to block that attack.

There is a template of the indicator with a transparent background, it works on few screenshots but not most of the others, however.

Here is the data I'm using:

Template:

Left Block

Screenshot where it successfully detects: enter image description here

Screenshot where it fails to detect: enter image description here

Code to detect:

import time
import cv2
import pyautogui
import numpy as np


def block_left():
    # while True:
        # screenshot = pyautogui.screenshot(region=(960, 455, 300, 260))
        # region = cv2.imread(np.array(screenshot), cv2.IMREAD_UNCHANGED)
    region = cv2.imread('Screenshots/Left S 1.png', cv2.IMREAD_UNCHANGED)
    block = cv2.imread(r'Block Images/Left Block.png', cv2.IMREAD_UNCHANGED)
    matched = cv2.matchTemplate(region, block, cv2.TM_CCOEFF_NORMED)

    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(matched)
    print(max_val)

    w = block.shape[1]
    h = block.shape[0]

    cv2.rectangle(region, max_loc, (max_loc[0] + w, max_loc[1] + h), (255, 0, 0), 2)

    cv2.imshow('Region', region)
    cv2.waitKey()


block_left()

So, in conclusion, I have tried multiple other methods but all have shown less successful outcomes. Are there any filters, any processing that I can add in order to fix this? Thank you.

Images uploaded are in 8-bit, however images used are 32-bit, but couldn't upload due to size, 32-bit images used are uploaded here: https://ibb.co/r7B7G6B https://ibb.co/r0r9w5T https://ibb.co/KXP3wWc

ChaoCius
  • 88
  • 1
  • 7
  • 1
    You need to make a mask of the red area in your template and then use the mask in matchTemplate(). See the documentation for using a mask image. Read the template image so that it preserves your alpha channel. Then extract the alpha channel as a mask and extract the BGR image under the alpha channel. Then use the BGR image as template and mask, both, in matchTemplate(). – fmw42 Aug 07 '21 at 15:11
  • I just tried and made a mask of the template, added it to the template and used it but still the same results or I might've made a mistake since I am relatively pretty new to OpenCV – ChaoCius Aug 07 '21 at 16:21
  • Show your new code and how you read the template and extracted the mask and your matchTemplate code. – fmw42 Aug 07 '21 at 17:14
  • Try TM_SQDIFF and min_loc – Micka Aug 07 '21 at 17:21
  • See https://stackoverflow.com/questions/61779288/how-to-template-match-a-simple-2d-shape-in-opencv/61780200#61780200 for an example of masked template matching – fmw42 Aug 07 '21 at 17:22
  • 1
    Be aware that template matching is neither scale- nor rotation-invariant. For me it looks like the angleof the red shapes are slightly different in both images. – Micka Aug 07 '21 at 17:24
  • Thanks @Micka I tried TM_SQDIFF with min_loc but still did not work before. The rotation is the same for all left region indicators. – ChaoCius Aug 07 '21 at 18:20

1 Answers1

2

Here is an example of masked template matching in Python/OpenCV using your two images and template with alpha channel.

Image 1:

enter image description here

Image 2:

enter image description here

Template:

enter image description here

import cv2
import numpy as np

# read  image
img = cv2.imread('game2.jpg')

# read template with alpha channel
template_with_alpha = cv2.imread('game_template.png', cv2.IMREAD_UNCHANGED)
hh, ww = template_with_alpha.shape[:2]

# extract base template image and alpha channel and make alpha 3 channels
template = template_with_alpha[:,:,0:3]
alpha = template_with_alpha[:,:,3]
alpha = cv2.merge([alpha,alpha,alpha])

# do masked template matching and save correlation image
correlation = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED, mask=alpha)

# get best match
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(correlation)
max_val_corr = '{:.6f}'.format(max_val)
print("correlation score: " + max_val_corr)
print("match location:", max_loc)

# draw match 
result = img.copy()
cv2.rectangle(result, (max_loc), ( max_loc[0]+ww,  max_loc[1]+hh), (0,0,255), 1)

# save results
cv2.imwrite('game2_matches.jpg', result)

cv2.imshow('template',template)
cv2.imshow('alpha',alpha)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Match for Image 1:

correlation score: 0.983882
match location: (783, 512)

enter image description here

Match for Image 2:

correlation score: 0.938928
match location: (867, 504)

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • 1
    Did you understand what I did? – fmw42 Aug 07 '21 at 18:21
  • 1
    Note that your template is not the same size as the red in the large images, but close enough in these cases. The template is slightly larger. If you have larger scale changes, you might have to do multi-scale template matching. – fmw42 Aug 07 '21 at 18:23
  • To be honest, I am still trying to grasp the logic behind how it worked. I don't know much about alpha channels and how the extraction into 3 channels worked. – ChaoCius Aug 07 '21 at 18:24
  • Right. I know how to do multi-scale template matching, no idea how the masking and alpha channel part works though. – ChaoCius Aug 07 '21 at 18:26
  • 1
    Do you understand how I read in the template so that it had the alpha channel and then separate the two? Then there is an option for a mask in matchTemplate(). That is the difference in doing masked template matching. – fmw42 Aug 07 '21 at 18:37
  • Oh yes! I believe I just passed in the mask itself as the template as I did not know there was a mask parameter. That explains, thank you a lot! – ChaoCius Aug 07 '21 at 18:42
  • 1
    As a bit of explanation, your template does not match well if masking is not used, since the image under the transparenct part is black. So the template is trying to match a lot of black and a little red to your image. That generally does not exist, since the image has texture around the red area. So adding the mask to matchTemplate allows it to ignore those areas in the image corresponding to the black in the mask that you extract from the alpha channel. – fmw42 Aug 07 '21 at 20:59
  • Oh, so transparency was considered as a black background, that explains a lot. Many thanks for the clear explanation. The part I don't get is how the separation of channels was done in code, however I will check the documentation as well, thank you! – ChaoCius Aug 07 '21 at 21:42
  • 1
    It is done using Numpy slicing. I read in the image with transparency. Then I copy only the BGR channels 0,1,2. Then I copy the alpha channel to an image from channel 3. – fmw42 Aug 07 '21 at 21:57