Visão Computacional aplicado ao Tabuleiro de Xadrez

Visão Computacional aplicado ao Tabuleiro de Xadrez

O que é visão computacional?

Visão computacional é uma área da ciência computacional e da inteligência artificial que busca replicar a visão humana para que máquinas sejam capazes de analisar, interpretar e extrair informações de imagens e vídeos, permitindo que decisões sejam tomadas de forma autônoma. Dessa forma, torna-se uma ferramenta útil em diversas aplicações, incluindo o mapeamento do tabuleiro de xadrez.

Por que realizar uma ODP (Oficina de Prototipagem) abordando esse tema?

A visão computacional é uma área repleta de possibilidades, o que nos motiva a explorar e criar projetos inovadores utilizando essa tecnologia. No PET Elétrica da UFJF, já desenvolvemos um braço robótico capaz de jogar xadrez, mas identificamos uma oportunidade de elevar esse projeto a um novo nível. Em vez de precisarmos intervir para indicar as jogadas, queremos criar um sistema que permita ao robô jogar partidas de xadrez de maneira totalmente autônoma. Para isso, propomos uma ODP (Oficina de Prototipagem), que tem como objetivo desenvolver um software capaz de reconhecer o tabuleiro em tempo real e identificar os movimentos das peças. O objetivo é integrar essa solução ao nosso braço robótico, tornando o sistema mais inteligente e autônomo.

Configurações Iniciais

Passo 1: Configuração do Ambiente

Primeiro, é importante garantir que você tenha uma IDE compatível com Python. O Visual Studio Code e o PyCharm são ótimas opções para desenvolvimento com Python. Neste exemplo, utilizarei o Visual Studio Code.

Passo 2: Criação de um Ambiente Virtual

Para organizar seu projeto e evitar instalar as bibliotecas diretamente no sistema, é recomendável criar um ambiente virtual. Isso ajuda a manter as dependências do projeto isoladas, evitando conflitos entre bibliotecas de outros projetos. 

Para isso, abra um novo terminal clicando em Terminal > New Terminal.

Execute o comando abaixo no terminal para criar o ambiente virtual:

python -m venv nome_do_ambiente

Neste exemplo, o nome escolhido foi .venv. Depois de criado, ative o ambiente virtual com o seguinte comando:

No windows:

nome_do_ambiente\Scripts\activate

No macOS e Linux:

source nome_do_ambiente/bin/activate

Se o ambiente virtual for ativado corretamente, o nome do seu ambiente virtual deverá aparecer no terminal, indicando que tudo o que for baixado a partir desse momento será alocado nesse diretório.

Passo 3: Instalação das Bibliotecas

Com o ambiente virtual ativo, instale as bibliotecas OpenCV e NumPy. Essas bibliotecas são essenciais para o processamento de imagens e a realização de cálculos.

Programação

Importação das Bibliotecas

Antes de começar a explicar o código, é necessário importar as bibliotecas. Isso pode ser feito escrevendo:

Import cv2

Import numpy as np

Função get_points

A função get_points permite que o usuário selecione quatro pontos manualmente com o clique do botão esquerdo do mouse. Esses pontos serão armazenados em uma lista chamada points, onde cada entrada contém as coordenadas x e y do ponto selecionado. A cada clique, um pequeno círculo verde será desenhado na posição do ponto na imagem, ajudando o usuário a visualizar onde os pontos estão sendo marcados.

Esses pontos são fundamentais para indicar as extremidades do tabuleiro de xadrez. Com essas coordenadas, o programa pode identificar as 64 casas do tabuleiro.

Após a seleção dos quatro pontos, caso a marcação tenha sido feita corretamente, o usuário pode pressionar qualquer tecla para fechar a janela de seleção, e o programa retornará a lista de coordenadas desses pontos, dando continuidade ao processamento do tabuleiro.

Função apply_perspective_transform

Essa função será responsável por receber uma imagem e aplicar uma transformação de perspectiva. Se a imagem foi capturada inclinada ou de um ângulo desfavorável, a função retorna uma versão reta e alinhada do tabuleiro, o que facilita o processamento e a análise.

Função draw_chessboard_grid

Essa função assume que a imagem de entrada tem o tamanho de 640×640 pixels e define que cada casa do tabuleiro de xadrez terá 80 pixels de largura e altura. Em seguida, é feito um laço duplo para percorrer todas as 8 linhas e 8 colunas do tabuleiro, calculando as coordenadas dos cantos superior e inferior de cada casa. Com essas coordenadas, a função cv2.rectangle desenha os retângulos que representam as casas do tabuleiro na imagem. Após isso, a função cv2.putText é usada para adicionar as anotações de cada casa, utilizando a notação padrão de xadrez (como “a8”, “b8”, etc.). Por fim, a imagem modificada é retornada.

Função detect_changes

Inicialmente, a função detect_changes recebe duas imagens, uma referente à jogada anterior e outra da jogada que acabou de ocorrer. Utilizando cv2.absdiff, compara-se a diferença entre essas imagens, gerando uma nova imagem, onde as áreas de mudança são destacadas em branco e as áreas sem alteração permanecem em preto.

Essa imagem resultante é convertida para tons de cinza e passa por um processo de detecção de contornos. Cada contorno identificado representa uma modificação, mas para evitar a detecção de pequenas variações irrelevantes, apenas contornos com área superior a 1600 pixels são considerados um movimento válido.

Por fim, as coordenadas da casa onde ocorreram as mudanças detectadas são armazenadas em uma lista chamada changes, que contém todas as informações sobre os movimentos observados.

Inicialização da câmera

Nesta parte do código, a câmera é inicializada. Para uma webcam, basta passar o parâmetro 0 ou 1 para a função cv2.VideoCapture, que identificará a câmera automaticamente. Caso queira usar um celular como câmera, você pode utilizar um aplicativo como o DroidCam, iniciar a transmissão e obter o endereço IP fornecido pelo app. Esse IP será usado no lugar do número 1 na função cv2.VideoCapture para acessar o vídeo em tempo real do celular.

Em seguida, há uma verificação para garantir que a câmera foi inicializada corretamente. Quando a câmera estiver bem posicionada, pressione a tecla ‘c’ para capturar a imagem do tabuleiro e iniciar a aplicação.

Utilizando a função get_points e apply_perspective_transform

Com a imagem de configuração capturada, vamos chamar as funções mencionadas anteriormente: get_points, para identificar as bordas do tabuleiro e suas casas, e apply_perspective_transform, para corrigir a perspectiva da imagem. Esse processo ajuda a ajustar a imagem, tornando-a mais adequada para a análise.

Utilizando a função draw_chessboard_grid

Uma vez que a imagem tenha sido tratada, podemos chamar a função draw_chessboard_grid para desenhar as casas do tabuleiro e adicionar seus nomes.

Atualizando imagens, realizando comparações e detectando o movimento feito

Inicialmente, as variáveis frame1 e frame2 são definidas como None, representando a ausência de um valor. Essas variáveis irão posteriormente assumir os valores das fotos capturadas.

No loop, seguimos os mesmos passos realizados anteriormente: verificamos se a câmera foi inicializada, aplicamos a correção de perspectiva e desenhamos as casas do tabuleiro de xadrez.

Nesta segunda parte do loop, será o centro de comando, onde controlamos quando uma jogada foi realizada ou quando o jogo foi finalizado.

Quando pressionamos ‘c’ pela primeira vez e a variável frame1 tem o valor None, isso indica que o jogo está prestes a começar, e uma foto do tabuleiro em sua condição inicial é registrada.

Com o valor da variável frame1 diferente de None após o primeiro lance, capturamos uma nova imagem, armazenamos em frame2 e chamamos a função detect_changes (explicada anteriormente) para identificar o movimento realizado. Em seguida, o movimento da peça é impresso no terminal

Finalização do jogo

O loop se repete a cada movimento, até o jogo acabar. Quando isso ocorrer, basta pressionar a tecla ‘q’ para encerrar o programa.

Resultados do projeto

Conseguimos desenvolver um protótipo eficiente que atingiu seus objetivos, como identificar as jogadas por meio de uma câmera. No futuro, o sistema poderá ser integrado ao braço robótico. No entanto, o projeto ainda não é totalmente autônomo. Existem oportunidades para aprimorar a identificação dos vértices do tabuleiro e, ao integrar o projeto ao braço robótico, será possível criar mecanismos que identifiquem um lance finalizado sem a necessidade de pressionar nenhuma tecla.

Além de aprimorar um projeto já concretizado, essa experiência também foi uma excelente oportunidade de aprendizado para o grupo. Envolveu conhecimentos de visão computacional, programação e trabalho em equipe, proporcionando um valioso desenvolvimento técnico e pessoal para todos os participantes.



Gustavo Garcia