What is the function of "mask = np.zeros(thresh.shape, dtype="uint8")" here?

I am not a programmer, but, using Python, I have pieced together an OMR marking programme from bits of code I got from the web and some ideas of my own.

To my amazement, it works very well!

Most of it I can understand, at least in principle.

Is there some kind person who could explain a little of what is happening in the function countPixels() below ?
What for example does np.zeros() do??

If a question has 3 choices, A, B or C, then:

contours are the contours found by:

thresh2 = thresh.copy()
contours2, hierarchy = cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

They represent the bubbles A, B and C and are sorted left to right.

minimum_pixels is just the minimum number of pixels to count as a marked bubble (for my present set-up, 800 works well.)

and thresh is:

thresh = cv2.threshold(grey, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]

Here is my function:

def countPixels(contours, minimum_pixels, thresh):
    # at the moment this can only accept 1 correct answer. Work on that.
    for (j, c) in enumerate(contours):            
        mask = np.zeros(thresh.shape, dtype="uint8")
        cv2.drawContours(mask, [c], -1, 255, -1)
        mask = cv2.bitwise_and(thresh, thresh, mask=mask)
        total = cv2.countNonZero(mask)
        # if the number of pixels is > minimum_pixels, we found the marked choice
        if total >= minimum_pixels:                
            print('number of pixels is: ', total)                
            bubbled = (total, j)
            return bubbled
            # there is no answer A, so this counts as 0
            bubbled = (total, 'A')
    return bubbled


the function assesses which of the passed contours (for the circles) is most filled in (“bubbled”), and returns the number of pixels as well as the index of that bubble

it’s all working with “masks”, i.e. data that is black or white. drawContours basically builds a mask that says where one bubble is (you should imshow it). bitwise and… produces an intersection, so only the pixels of that bubble are respected in this step. countNonZero just counts the pixels that aren’t black.

use imshow and waitKey to look at data.

I think you will find this useful:

Thank you!

Sorry I took so long to say thank you, I was preoccupied with other stuff.

Very useful link, thanks again!