Detecção da bola

O atual algoritmo para a detecção da bola é simples e faz o uso do conceito de máscara de cor no espaço HSV e cálculo de contornos. Pode ser dividida em:

É interessante notar que um importante critério para identificar a bola foi o da "circularidade". Dentre todos os contornos válidos (área dentro da faixa esperada), o escolhido como bola era o mais circular, seguindo o cálculo definido pelo MATLAB e demonstrado aqui.

A função que implementa essa detecção pode ser vista no código que segue.

Código que implementa a detecção da bola

def detect_ball(frame, output_frame):

    # First we blur the image with a GaussianBlur

    blurred = cv2.GaussianBlur(frame, (11, 11), 0)

   

    # Construct a HSV mask for the green color

    hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)

    mask = cv2.inRange(hsv, greenLower, greenUpper)


    # Erode and dilate the result to remove small noises

    mask = cv2.erode(mask, None, iterations=4)

    mask = cv2.dilate(mask, None, iterations=4)

   

    # Then we calculate the countours of the resulting image

    frame_cnts, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

   

    if len(frame_cnts) == 0:

        return False, output_frame, [], -1, -1, -1


    # Calculate the circularity of the identified countours

    areas = np.array([cv2.contourArea(c) for c in frame_cnts])

    is_reading_valid = (areas > minimum_ball_area) & (areas < maximum_ball_area)


    if np.sum(is_reading_valid) == 0:

        return False, output_frame, [], -1, -1, -1


    perimeters = np.array([cv2.arcLength(c,True) for c in frame_cnts])


    circularities = 4 * np.pi *areas/(perimeters**2)

    circularities = circularities*is_reading_valid

    ball_cnt_idx = np.argmax(circularities)

    # We get the one with the greatest circularity (4*pi*area/(perimeter^2))

    # https://www.mathworks.com/help/images/identifying-round-objects.html;jsessionid=551254009a8e1c007e415ab76902

    c = frame_cnts[ball_cnt_idx]

    # And calculate the minimum enclosing circle

    ((x, y), radius) = cv2.minEnclosingCircle(c)

    M = cv2.moments(c)

   

    # Calculate the shape

    approx = cv2.approxPolyDP(c,0.01*cv2.arcLength(c,True),True)


    # If the shape is really close to a circle and the area is greater than the minimum

    # the contour is considered to be the ball

    if ((len(approx) > 6) & (len(approx) < 23):

        center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))

        cv2.circle(output_frame, (int(x), int(y)), int(radius), (0, 255, 0), 2)


        return True, output_frame, frame_cnts, x, y, radius

    return False, output_frame, [], -1, -1, -1