• Unidade 2
  • Capítulo 5 - Visão Robótica
  • Atividades
  • 5 - Exemplo de Resolução de Exercício
  • 5.2 - Resolução

Resolução de Exercício - Visão Computacional

Primeiro, leia atentamente o enunciado do exercício. Se desejar, você pode tentar resolvê-lo antes de ler a resolução, se não, baixe o arquivo do gabarito e acompanhe a resolução.

A resolução foi levemente alterada para que o códig rode no notebook, mas o resultado é o mesmo.

Pelo enunciado, o objetivo de exercício é implementar um algoritmo que identifique a posição das bandeiras na imagem, em qualquer posição e tamanho.

Para isso, o primeiro passo é sempre ler a imagem e visualizá-la.

1. Visualização do Problema

import cv2
import numpy as np
import os

for i in range(1,5):
    img = cv2.imread(f'img/q1/teste{i}.png')
    cv2.imshow(f'teste{i}.png', img)

cv2.waitKey(0)
cv2.destroyAllWindows()

img = cv2.imread('img/q1/teste1.png')
  • Mas qual bandeira é de qual país? Dica: Google

Pelas imagens, podemos ver que por mais que as bandeiras sejam parecidas, elas possuem algumas cores diferentes. Podemos usar isso para identificar as bandeiras.

Note o seguinte:

  • Note que apenas uma bandeira (Irlanda) possui a cor laranja.

  • Apenas duas bandeiras (Italia e Irlanda) possuem a cor verde.

  • Das bandeiras que possuem as cores vermelha e branca (Singapura, Peru e Monaco):

    • A bandeira do Peru possui mais da cor vermelha que da cor branca.

    • A bandeira da Singapura possui uma lua e estrelas em branco, ou seja, possui mais branco que vermelho.

    • A bandeira de Monaco possui cerca de metade de cada cor.

Com isso em mente, já encontramos uma forma de identificar as bandeiras. Vamos encontrar as bandeiras na imagem, cortá-las e comparar as cores. Então podemos ir para o passo 2.

2. Detecção das Bandeiras

Em todas as imagem, note que o fundo é sempre preto. Podemos usar isso para encontrar as bandeiras.

Se criarmos uma máscara que ignore o fundo, podemos encontrar as bandeiras facilmente. Isso foi feito na função abaixo:

def get_all_contours(bgr):
    gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY)
    mask = cv2.inRange(gray, 20, 255)
    contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    rects = []
    for contour in contours:
        rect = cv2.boundingRect(contour)
        rects.append(rect)

    return bgr, rects

bgr, rects = get_all_contours(img)

bgr_ = bgr.copy()
for rect in rects:
    x, y, w, h = rect
    cv2.rectangle(bgr_, (x, y), (x+w, y+h), (0, 255, 0), 2)

cv2.imshow('img', bgr_)
cv2.waitKey()
cv2.destroyAllWindows()

Nesta parte foi feito o seguinte:

  • Converter a imagem para Grayscale, que facilita a segmentação de branco ou preto.
  • Encontramos os contornos das mascaras, que são as bandeiras.
  • Encontramos o retângulo que envolve o contorno, que é a posição da bandeira.
  • Desenhamos o retângulo na imagem original.

3. Comparação de Cores

Agora, vamos encontrar os limites de cada cor na imagem, usar a função cv2.inRange, e calcular a area de cada cor. A cor com maior área é a cor predominante na imagem.

Isso foi feito na função abaixo:

colors = {
    'orange': ((0, 100, 100), (20, 255, 255)),
    'red': ((170, 100, 100), (180, 255, 255)),
    'green': ((70, 100, 100), (80, 255, 255)),
    'white': ((0, 0, 200), (180, 20, 255)),
}

kernel = np.ones((5,5),np.uint8)

bandeiras = []

def checar_bandeiras(hsv, rects):
    for rect in rects:
        x, y, w, h = rect
        crop = hsv[y:y+h, x:x+w]
        cv2.imshow('crop', cv2.cvtColor(crop, cv2.COLOR_HSV2BGR))

        # Irlanda
        if np.sum(cv2.inRange(crop, colors['orange'][0], colors['orange'][1])) > 0:
            bandeiras.append(('irlanda', (x, y), (x+w, y+h)))
        elif np.sum(cv2.inRange(crop, colors['green'][0], colors['green'][1])) > 0:
            bandeiras.append(('italia', (x, y), (x+w, y+h)))
        else:
            mask_r = cv2.inRange(crop, colors['red'][0], colors['red'][1])
            red = np.sum(mask_r) / 255
            mask_w = cv2.inRange(crop, colors['white'][0], colors['white'][1])
            white = np.sum(mask_w) / 255
            if white > red:
                bandeiras.append(('singapura', (x, y), (x+w, y+h)))
            elif red > white:
                bandeiras.append(('peru', (x, y), (x+w, y+h)))
            else:
                bandeiras.append(('monaco', (x, y), (x+w, y+h)))

hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
checar_bandeiras(hsv, rects)

Nesta parte foi feito o seguinte:

  • Criamos um dicinário com os limites de cada cor.

  • Para cada bandeira detectada, foi feito:

    • Criamos uma máscara para a cor laranja, e calculamos a área. Se a área for maior que 0, a bandeira é da Irlanda.
    • Criamos uma máscara para a cor verde, e calculamos a área. Se a área for maior que 0, a bandeira é da Itália (pois a Irlanda já foi detectada).
    • Criamos uma máscara para a cor vermelha e para cor branca, e calculamos a área de cada uma. Se a área vermelha for maior que a branca, a bandeira é do Peru. Se a área branca for maior que a vermelha, a bandeira é da Singapura. As demais são a bandeira é de Monaco.

Agora, podemos finalizar o código, indicando a posição e o país de cada bandeira.

Abaixo usamos a função cv2.putText para escrever o nome do país na imagem, que recebe os seguintes parâmetros:

  • A imagem onde será escrito o texto.
  • O texto a ser escrito.
  • A posição do texto.
  • A fonte do texto.
  • O tamanho do texto.
  • A cor do texto.
  • A espessura do texto.
  • O tipo de linha do texto.
def draw_bandeiras(bgr):
    for bandeira in bandeiras:
        cv2.rectangle(bgr, bandeira[1], bandeira[2], (255, 0, 0), 5)
        cv2.putText(bgr, bandeira[0], bandeira[1], cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2, cv2.LINE_AA)

draw_bandeiras(bgr)
cv2.imshow('img', bgr)
cv2.waitKey()
cv2.destroyAllWindows()

Agora, execute o script do gabarito e veja o resultado.