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.