Converting a class function to iterate through multiple images with OpenCV and Python

I’m using OpenCV to detect images in real-time. This is working with single images. I’m trying to update the script to work with multiple images at once with the end goal to be to detect a few different elements on the screen at once and draw rectangles around them while returning the centre points of the images for use elsewhere.

I’ve gotten as far as passing the images in from my main.py file and initiating the search.

# load source images
source_images = glob.glob(r'C:\\\*.jpg')

# perform the search
search = Search(source_images)

I can’t seem to pass loaded_images into my find function. I’ve tried defining loaded_images in the Class properties and within the constructor but neither work and give me the error NameError: name 'loaded_images' is not defined

class Search:

    # properties
    needle_img = None
    needle_w = 0
    needle_h = 0
    method = None

    # empty list to store the source images
    #loaded_images = [] <-------------------------------------------------

    # constructor                                        
    def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
                     
        # Set the method we're using when we load the image 
        self.method = method

        # empty list to store the source images
        # loaded_images = [] <-------------------------------------------------
        
        # load the needle image into loaded_iamges array
        for img in source_images:
            self.needle_img = cv.imread(img, cv.IMREAD_UNCHANGED)
            loaded_images.append(self.needle_img)

            return self.loaded_images <--------------------------------

    def find(self, haystack_img, threshold=0.5, debug_mode=None):
        
        for img in loaded_images:
            # Save the dimensions of the needle images
            self.needle_w = self.needle_img.shape[1]
            self.needle_h = self.needle_img.shape[0]
            
            # run the OpenCV algorithm
            result = cv.matchTemplate(haystack_img, self.needle_img, self.method)

I also wrote this next code segment that matches multiple images on a screenshot rather than a real-time image to try and figure out what I was doing wrong. It’s helped me get this far but I am now stuck.

# load source images
source_images = glob.glob(r'C:\\\*.jpg')
# empty list to store the source images
loaded_images = []

for img in source_images:
    needle_img = cv.imread(img, 0)
    loaded_images.append(needle_img)

haystack_img = cv.imread(r'C:\\\both.jpg')
haystack_img = cv.cvtColor(haystack_img, cv.COLOR_BGR2GRAY)

#loop for matching
for needles in loaded_images:
    
    #save the dimensions of the needle images
    (tH, tW) = needles.shape[:2]
    result = cv.matchTemplate(haystack_img, needles, cv.TM_CCOEFF)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
    top_left = max_loc
    
    bottom_right = (top_left[0] + tW, top_left[1] + tH)
    cv.rectangle(haystack_img, top_left, bottom_right, 255, 2)

cv.imshow('Result', haystack_img)
cv.waitKey(0)

Answer

Either pass loaded images directly to find:

class Search:              
    def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
                     
        # Set the method we're using when we load the image 
        self.method = method

    def find(self, haystack_img, loaded_images, threshold=0.5, debug_mode=None):
        for img in loaded_images:
            # Save the dimensions of the needle images
            self.needle_w = self.needle_img.shape[1]
            self.needle_h = self.needle_img.shape[0]
            # you are overwriting the same needle_w and needle_h variable over and over again in a loop...
            
            # run the OpenCV algorithm
            result = cv.matchTemplate(haystack_img, self.needle_img, self.method)

Or store loaded images in field:

class Search:                                    
    def __init__(self, source_images, method=cv.TM_CCOEFF_NORMED):
        # Set the method we're using when we load the image 
        self.method = method

        self.loaded_images = [] 
        # load the needle image into loaded_iamges array
        for img in source_images:
            needle_img = cv.imread(img, cv.IMREAD_UNCHANGED)
            self.loaded_images.append(needle_img)

    def find(self, haystack_img, threshold=0.5, debug_mode=None):
        for img in self.loaded_images:
            # Save the dimensions of the needle images
            self.needle_w = self.needle_img.shape[1]
            self.needle_h = self.needle_img.shape[0]
            # you are overwriting the same needle_w and needle_h variable over and over again in a loop...
            
            # run the OpenCV algorithm
            result = cv.matchTemplate(haystack_img, self.needle_img, self.method)

In general you need to learn about scopes and difference between class and instance variables. Also I don’t see here why you would need class when you would probably only have one instance of it, so maybe read about OOP in general.