Visão computacional clássica
1. Introdução a Visão Computacional Clássica
Section titled “1. Introdução a Visão Computacional Clássica”Aqui pessoal, vamos avaliar o seguinte problema: temos uma imagem de um processo e precisamos classificar está imagem. Imagine quantos contextos diferentes está pequena descrição consegue atender: classificar produtos em uma linha de montagem, encontrar erros em um processo produtivo, determinar qual produto deve ser embalado em uma linha de produção, isso só para citar algumas aplicações.
“Legal tudo isso Murilo, mas como podemos realizar tudo isso?”
É aqui que entram nossos algoritmos de visão computacional clássica! Vamos compreender primeiro o fluxo de operações que vamos realizar e depois vamos implementar cada uma delas beleza?
Vamos explorar cada um dos quadros desta figura! Primeiro temos a imagem que vai ser captada para dentro do nosso sistema. Observe um ponto importante aqui: a imagem pode ser captada de várias formas, seja por uma câmera, seja por um scanner, seja por um celular, etc. O importante é que a imagem esteja em um formato que o computador consiga ler e processar. Para isso, existem várias bibliotecas disponíveis, como OpenCV, PIL, entre outras.
Depois de captada a imagem, precisamos realizar o seu pré-processamento. Mas o que é isso? O pré-processamento é uma etapa onde aplicamos algumas operações na imagem, como filtros, redimensionamento, normalização, entre outras. O objetivo do pré-processamento é melhorar a qualidade da imagem e facilitar a extração de características relevantes para a classificação.
Na sequência, temos a segmentação da imagem. Ela é a etapa onde dividimos a imagem em regiões ou objetos de interesse. Isso é importante porque, muitas vezes, a imagem contém várias informações que não são relevantes para a tarefa de classificação. A segmentação nos ajuda a focar apenas nas partes da imagem que realmente importam. Existem várias técnicas de segmentação, como limiarização, segmentação baseada em regiões, entre outras.
Seguido da segmentação, temos a etapa da extração de características. Nesta etapa, extraímos informações relevantes da imagem que serão utilizadas para a classificação. Essas características podem ser, por exemplo, bordas, texturas, cores, formas, entre outras. A escolha das características a serem extraídas depende do problema que estamos tentando resolver e do tipo de imagem que estamos analisando.
Agora com a região de interesse e as característica extraídas das imagens, podemos começar a desenvolver o nosso modelo de classificação. Existem várias técnicas de classificação, como máquinas de vetor de suporte (SVM), árvores de decisão, redes neurais, entre outras. A escolha do modelo depende do problema que estamos tentando resolver e das características extraídas da imagem. O processo de classificação envolve o treinamento do modelo com um conjunto de dados rotulados. Esses dados rotulados são as imagens que serão utilizadas para ensinar o modelo a reconhecer os padrões presentes nas imagens. O modelo aprende a associar as características extraídas das imagens com as classes correspondentes. Após o treinamento, o modelo pode ser utilizado para classificar novas imagens, ou seja, imagens que não foram utilizadas durante o treinamento. O modelo irá analisar as características extraídas da nova imagem e associá-las à classe correspondente.
É importante ressaltar que o desempenho do modelo de classificação precisa ser verificado. Métricas como acurácia, precisão, recall e F1-score são utilizadas para avaliar o desempenho do modelo. Essas métricas ajudam a entender se o modelo está classificando corretamente as imagens e se ele é capaz de generalizar para novas imagens.
“Legal Murilo, mas e como que eu faço para implementar tudo isso?”
Ótima pergunta! Vamos realizar essa implementação em conjunto. Para isso, vamos precisar de algumas coisas para trabalhar:
- Um ambiente virtual Python para instalar as bibliotecas necessárias.
# Pessoal vamos criar um ambiente virtual para instalar as bibliotecas necessárias# Vamos criar um diretório para nossa soluçãomkdir visao-computacionalcd visao-computacional# Agora vamos criar o ambiente virtual - Mac e Linuxpython3 -m venv venv# Vamos ativar o ambiente virtual - Mac e Linuxsource venv/bin/activate- Vamos instalar as bibliotecas necessárias para o projeto.
# Agora vamos instalar as bibliotecas necessárias para o projetopython3 -m pip install opencv-pythonpython3 -m pip install matplotlibpython3 -m pip install scikit-learn- Vamos criar um diretório para armazenar as imagens que vamos utilizar no projeto.
# Agora vamos criar um diretório para armazenar as imagens que vamos utilizar no projetomkdir imagesDentro do diretório images, vamos colocar as imagens que vamos utilizar para o projeto. Para isso, vou deixar as imagens aqui para vocês:
2. Etapa de Pré-processamento
Section titled “2. Etapa de Pré-processamento”Vamos lá, agora já temos o nosso ambiente virtual criado, as bibliotecas instaladas e as imagens que vamos processar. Agora vamos iniciar a nossa etapa de carregamento das imagens. Para isso, vamos criar um primeiro arquivo chamado carregamento.py e vamos colocar o seguinte código nele:
import cv2import matplotlib.pyplot as pltimport osimport numpy as np
# Definindo o caminho para as imagenscaminho_imagens = 'imagens/'# Definindo o tamanho da imagemtamanho_imagem = (512, 512)# Função para carregar as imagensdef carregar_imagens(caminho_imagens): imagens = [] for arquivo in os.listdir(caminho_imagens): if arquivo.endswith('.jpeg'): imagem = cv2.imread(os.path.join(caminho_imagens, arquivo)) imagem = cv2.resize(imagem, tamanho_imagem) imagens.append(imagem) return imagens# Função para exibir as imagensdef exibir_imagens(imagens): # Determina o número de linhas e colunas para exibir as imagens total_colunas = len(imagens) // 2 total_linhas = len(imagens) // total_colunas print(total_linhas, total_colunas) # Se o número de imagens não for divisível pelo número de colunas, adiciona mais uma linha if len(imagens) % total_colunas != 0: total_colunas += 1
for i, imagem in enumerate(imagens): plt.subplot(total_linhas, total_colunas, i + 1) plt.imshow(cv2.cvtColor(imagem, cv2.COLOR_BGR2RGB)) plt.axis('off') plt.show()
# Carregando as imagensimagens = carregar_imagens(caminho_imagens)# Exibindo as imagensexibir_imagens(imagens)Pessoal vamos aqui avaliar o que está acontecendo no nosso código:
- Estamos importando as bibliotecas necessárias para o nosso projeto:
cv2para manipulação de imagens,matplotlib.pyplotpara exibição das imagens,ospara manipulação de arquivos e diretórios enumpypara manipulação de arrays. - Definimos o caminho para as imagens que vamos utilizar no projeto e o tamanho da imagem que vamos utilizar. No nosso caso, estamos utilizando o diretório
imagense o tamanho de 512x512 pixels para as imagens. Aqui tem um ponto interessante para avaliar: o tamanho da imagem pode influenciar no desempenho do nosso modelo de classificação. Imagens muito grandes podem levar mais tempo para serem processadas e podem aumentar o tempo de treinamento do modelo. Por outro lado, imagens muito pequenas podem perder informações importantes. Portanto, é importante encontrar um equilíbrio entre o tamanho da imagem e a qualidade da imagem. Outro ponto importante de se observar é por onde o programa será executado. No caso do exemplo, o programa está dentro de um diretório que está no mesmo nível que o diretórioimagens. Desta forma, o terminal para executar o programa deve estar em um ponto que eleconsiga acessar o diretório imagens. No caso do exemplo, ele está dentro do diretóriovisao-computacional, portanto, para executar o programa, utilizar o comandopython3 visao-computacional/carregamento.py. - Criamos a função
carregar_imagensque vai carregar as imagens do diretórioimagens, redimensioná-las para o tamanho definido e armazená-las em uma lista. - Criamos a função
exibir_imagensque vai exibir as imagens carregadas. Aqui estamos utilizando omatplotlibpara exibir as imagens. Omatplotlibé uma biblioteca muito poderosa para visualização de dados e pode ser utilizada para exibir gráficos, imagens, entre outros. No nosso caso, estamos utilizando oimshowpara exibir as imagens e oaxis('off')para remover os eixos das imagens. Estamos utilizando também osubplotpara exibir as imagens em um grid. O número de linhas e colunas do grid é determinado pelo número de imagens carregadas. Se o número de imagens não for divisível pelo número de colunas, adicionamos mais uma linha. - Por fim, estamos chamando a função
carregar_imagenspara carregar as imagens do diretórioimagense a funçãoexibir_imagenspara exibir as imagens carregadas.
Agora, vamos verificar um pouco sobre as ferramentas de pré-processamento que podemos utilizar para melhorar a qualidade das iamgens. Para isso, vamos compreender quais são os principais filtros que podemos aplicar nas imagens.
2.1 Filtros de Pré-processamento
Section titled “2.1 Filtros de Pré-processamento”Os filtros de pré-processamento de imagens são técnicas aplicadas às imagens antes da análise ou extração de informações. A importância deles reside em:
- Redução de ruído: O ruído é uma variação aleatória de brilho ou cor nas imagens. Filtros como o Gaussiano ou a mediana podem diminuir essa variação, resultando em imagens mais nítidas e facilitando etapas subsequentes.
- Realce de características: Alguns filtros podem destacar bordas, contornos ou outras características relevantes, o que simplifica a segmentação ou a extração de informações.
- Conversão de espaço de cores: Alterar o espaço de cores, por exemplo, para escala de cinza ou HSV, pode simplificar o processamento, dependendo da tarefa.
- Normalização: Ajustar a intensidade dos pixels dentro de uma faixa específica auxilia na consistência do processamento.
- Operações morfológicas: Filtros como erosão e dilatação são úteis para remover pequenas irregularidades ou conectar regiões segmentadas.
Esses filtros adaptam a imagem para torná-la mais adequada para análise posterior, melhorando a qualidade dos resultados em tarefas como segmentação, extração de características ou classificação.
Vamos agora verificar alguns filtros que podemos aplicar nas imagens e quando eles podem ser utilizados.
2.2 Compreendendo o conceito de Filtros
Section titled “2.2 Compreendendo o conceito de Filtros”Para iniciarmos nosso jornada com os filtros, primeiro vamos compreender o que eles são. Podemos entender os filtros como uma pequena matriz (o kernel) que desliza sobre a imagem, realizando uma operação matemática em cada vizinhança de pixels para calcular o novo valor do pixel central.
Como podemos observar na imagem acima, o filtro (kernel) desliza sobre a imagem e realiza uma operação matemática em cada vizinhança de pixels para calcular o novo valor do pixel central. Essa operação pode ser uma soma, uma média, uma multiplicação, entre outras. O resultado dessa operação é o novo valor do pixel central.
O tipo dessa operação depende do filtro que estamos utilizando. Existem vários tipos de filtros, cada um com uma finalidade específica. Podemos classificá-los em duas categorias principais: filtros lineares e filtros não lineares.
Os filtros lineares são aqueles que realizam uma operação linear sobre os pixels da imagem. Isso significa que o novo valor do pixel central é uma combinação linear dos valores dos pixels vizinhos. Os filtros lineares mais comuns são os filtros de média e os filtros de Gauss. Em geral, são utilizados para suavizar a imagem, reduzindo o ruído e melhorando a qualidade da imagem. O filtro de média, por exemplo, calcula a média dos valores dos pixels vizinhos e atribui esse valor ao pixel central. O filtro de Gauss, por sua vez, aplica uma função gaussiana aos pixels vizinhos, dando mais peso aos pixels mais próximos do pixel central. Isso resulta em um efeito de desfoque suave na imagem.
Os filtros não lineares, por outro lado, realizam uma operação não linear sobre os pixels da imagem. Isso significa que o novo valor do pixel central não é uma combinação linear dos valores dos pixels vizinhos. Os filtros não lineares mais comuns são os filtros de mediana e os filtros de máximo. O filtro de mediana, por exemplo, calcula a mediana dos valores dos pixels vizinhos e atribui esse valor ao pixel central. Isso resulta em um efeito de suavização que preserva as bordas da imagem. O filtro de máximo, por sua vez, atribui o valor máximo dos pixels vizinhos ao pixel central. Isso resulta em um efeito de realce das bordas da imagem.
2.3 Aplicando os Filtros
Section titled “2.3 Aplicando os Filtros”Agora vamos aplicar os filtros que conversamos no nosso conjunto de imagens acima. Antes de colocarmos todos os filtros no nosso pipeline de pré-processamento, vamos criar um arquivo chamado filtros.py e investigar cada um dos filtros separadamente. Vamos lá?
2.4 Filtro de Média
Section titled “2.4 Filtro de Média”O filtro de média é um filtro linear que calcula a média dos valores dos pixels vizinhos e atribui esse valor ao pixel central. Isso resulta em um efeito de suavização na imagem, reduzindo o ruído e melhorando a qualidade da imagem. Vamos verificar como ele pode ser implementado utilizando o OpenCV.
import cv2import matplotlib.pyplot as pltimport osimport numpy as np
# Definindo o caminho para as imagenscaminho_imagens = 'imagens/tampinhas_01.jpeg'
# Abre a imagemimagem = cv2.imread(caminho_imagens)
# Redimensiona a imagemimagem = cv2.resize(imagem, (512, 512))
# Converte a imagem para escala de cinzaimagem_cinza = cv2.cvtColor(imagem, cv2.COLOR_BGR2GRAY)
# Aplica o filtro de médiaimagem_media = cv2.blur(imagem_cinza, (5, 5))
# Exibe a imagem original e a imagem filtradaplt.subplot(1, 2, 1)plt.imshow(imagem_cinza, cmap='gray')plt.title('Imagem Original')plt.axis('off')plt.subplot(1, 2, 2)plt.imshow(imagem_media, cmap='gray')plt.title('Imagem Filtrada')plt.axis('off')plt.show()Pontos importantes a serem observados:
- Mesmo não sendo um filtro propriamente, a conversão da imagem para escala de cinza é uma etapa importante no pré-processamento de imagens. Isso porque, muitas vezes, as informações de cor não são relevantes para a tarefa de classificação e a conversão para escala de cinza reduz a complexidade da imagem.
- O filtro de média é aplicado utilizando a função
cv2.blur, que recebe como parâmetros a imagem e o tamanho do kernel (neste caso, 5x5). O tamanho do kernel determina a quantidade de pixels vizinhos que serão considerados para calcular a média. Um kernel maior resulta em uma suavização mais intensa, enquanto um kernel menor resulta em uma suavização mais leve. Testar outros tamanhos de kernel pode ser interessante para verificar o impacto na imagem. - A imagem original e a imagem filtrada são exibidas lado a lado utilizando o
matplotlib. Isso facilita a comparação entre as duas imagens e permite visualizar o efeito do filtro de média. Para utilizar omatplotlib, é necessário importar a biblioteca e utilizar a funçãoplt.imshowpara exibir a imagem. O parâmetrocmap='gray'é utilizado para exibir a imagem em escala de cinza.
2.5 Filtro Gaussiano
Section titled “2.5 Filtro Gaussiano”O filtro gaussiano é um filtro linear que aplica uma função gaussiana aos pixels vizinhos, dando mais peso aos pixels mais próximos do pixel central. Isso resulta em um efeito de desfoque suave na imagem. Vamos verificar como ele pode ser implementado utilizando o OpenCV.
import cv2import matplotlib.pyplot as pltimport osimport numpy as np
# Definindo o caminho para as imagenscaminho_imagens = 'imagens/tampinhas_01.jpeg'
# Abre a imagemimagem = cv2.imread(caminho_imagens)
# Redimensiona a imagemimagem = cv2.resize(imagem, (512, 512))
# Converte a imagem para escala de cinzaimagem_cinza = cv2.cvtColor(imagem, cv2.COLOR_BGR2GRAY)
# Aplica o filtro gaussianoimagem_gaussiana = cv2.GaussianBlur(imagem_cinza, (5, 5), 0)
# Exibe a imagem original e a imagem filtradaplt.subplot(1, 2, 1)plt.imshow(imagem_cinza, cmap='gray')plt.title('Imagem Original')plt.axis('off')plt.subplot(1, 2, 2)plt.imshow(imagem_gaussiana, cmap='gray')plt.title('Imagem Filtrada')plt.axis('off')plt.show()Pontos importantes a serem observados:
- O filtro gaussiano é aplicado utilizando a função
cv2.GaussianBlur, que recebe como parâmetros a imagem, o tamanho do kernel (neste caso, 5x5) e o desvio padrão da distribuição gaussiana (neste caso, 0). O tamanho do kernel e o desvio padrão determinam a quantidade de pixels vizinhos que serão considerados para calcular a média ponderada. Um kernel maior resulta em uma suavização mais intensa, enquanto um kernel menor resulta em uma suavização mais leve. Já o desvio padrão determina a largura do sino da função gaussiana. Um desvio padrão maior resulta em uma suavização mais intensa, enquanto um desvio padrão menor resulta em uma suavização mais leve.
2.6 Filtro de Mediana
Section titled “2.6 Filtro de Mediana”O filtro de mediana é um filtro não linear que calcula a mediana dos valores dos pixels vizinhos e atribui esse valor ao pixel central. Isso resulta em um efeito de suavização que preserva as bordas da imagem. Vamos verificar como ele pode ser implementado utilizando o OpenCV.
import cv2import matplotlib.pyplot as pltimport osimport numpy as np
# Definindo o caminho para as imagenscaminho_imagens = 'imagens/tampinhas_01.jpeg'# Abre a imagemimagem = cv2.imread(caminho_imagens)# Redimensiona a imagemimagem = cv2.resize(imagem, (512, 512))# Converte a imagem para escala de cinzaimagem_cinza = cv2.cvtColor(imagem, cv2.COLOR_BGR2GRAY)