Capítulo 1: Introdução à Visão Computacional

Contents

Capítulo 1: Introdução à Visão Computacional#

cover

Google Colab, Linux e Bibliotecas#

O Google Colab (Colaboratory) é um ambiente de nuvem baseado em Jupyter Notebooks que permite escrever e executar código Python diretamente no navegador. Para tarefas de visão computacional, ele se destaca por ser uma plataforma de “configuração zero”, eliminando a necessidade de instalar complexos drivers de GPU ou ambientes locais pesados.

Neste capítulo, exploraremos como utilizar essa plataforma para acelerar o desenvolvimento de projetos, desde a configuração básica até o uso de aceleradores de hardware.

colab_interface

Por que utilizar o Google Colab?#

Diferente de um ambiente local, o Colab oferece infraestrutura poderosa de forma gratuita, sendo um divisor de águas para estudantes e pesquisadores.

Principais Vantagens:

  • Zero Configuração – Sem necessidade de instalar Python ou bibliotecas localmente para começar.

  • Acesso Gratuito a GPUs (Unidades de Processamento Gráfico) – Essencial para processar grandes volumes de imagens e treinar redes neurais profundas.

  • Integração com Ecossistema Google – Salve seus notebooks e datasets diretamente no Google Drive.

  • Portabilidade – Acesse seus projetos de qualquer computador com acesso à internet.

Configurando o Hardware (GPU)#

Para tarefas de Visão Computacional, é altamente recomendável ativar a aceleração por hardware:

  1. Vá em Ambiente de execução > Alterar tipo de ambiente de execução.

  2. Em Acelerador de hardware, selecione T4 GPU (ou superior, se disponível).

Gerenciamento de Pacotes: Do pip ao uv#

Gerenciar bibliotecas é uma parte vital do fluxo de trabalho. Embora o pip seja o padrão histórico, ferramentas mais modernas como o uv estão redefinindo a velocidade e a confiabilidade nesse processo.

O Gerenciador de Pacotes pip#

O pip (Python Package Installer) é o gerenciador padrão do ecossistema Python. No ambiente do Google Colab, ele é a ferramenta primária para garantir a persistência das bibliotecas instaladas durante a sessão de execução. É recomendável utilizar o prefixo ! antes dos comandos para assegurar a execução via shell.

Principais Comandos do pip

Ação

Comando

Descrição

Listar

!pip list

Exibe todos os pacotes instalados e suas respectivas versões.

Versões Disponíveis

!pip index versions <pacote>

Lista todas as versões disponíveis de um pacote no PyPI.

Instalar

!pip install <pacote>

Baixa e instala a versão mais recente de uma biblioteca.

Instalar via Arquivo

!pip install -r requirements.txt

Instala todos os pacotes listados em um arquivo de dependências.

Versão Específica

!pip install <pacote>==<versão>

Instala uma versão exata para garantir compatibilidade.

Atualizar

!pip install --upgrade <pacote>

Atualiza um pacote já instalado para a última versão estável.

Informações

!pip show <pacote>

Mostra detalhes como autor, local de instalação e dependências.

Remover

!pip uninstall -y <pacote>

Remove uma biblioteca do ambiente atual.

Exportar

!pip freeze > requirements.txt

Gera um arquivo com todas as dependências do projeto.

O Gerenciador de Pacotes uv#

O uv é um gerenciador de pacotes e ambientes virtuais escrito em Rust, desenvolvido pela Astral (mesma equipe do Ruff). Ele foi projetado para ser um substituto moderno e extremamente rápido do pip, pip-tools e virtualenv.

Por que usar o uv?

Característica

Benefício

Velocidade Extrema

Até 100x mais rápido que o pip em operações de cache.

Gerenciamento de Python

Instala e alterna entre versões do Python automaticamente.

Ambientes Virtuais

Cria ambientes virtuais (.venv) em milissegundos.

Resolução de Dependências

Algoritmo moderno que evita conflitos complexos.

Unificação

Substitui múltiplas ferramentas (pip, pip-tools, pyenv, poetry) em um único executável.

Comandos equivalentes no uv

No pip

No uv

!pip install <pacote>

!uv pip install <pacote>

!pip list

!uv pip list

!pip freeze > reqs.txt

!uv pip freeze > reqs.txt

!pip uninstall <pacote>

!uv pip uninstall <pacote>

# Listar todos os pacotes instalados no ambiente
!uv pip list

# Mostrar informações detalhadas sobre um pacote específico
!pip show numpy

# Remover um pacote e suas dependências
!uv pip uninstall opencv-python -y

# Gerar um arquivo requirements.txt para garantir a reprodutibilidade do projeto
!uv pip freeze > requirements.txt

# Reinstalar dependências a partir de um arquivo requirements.txt
!uv pip install -r requirements.txt

O que cada biblioteca faz?#

  • NumPy – Manipulação eficiente de arrays e matrizes, essencial para processamento numérico e imagens.

  • OpenCV – Biblioteca poderosa para processamento de imagens e vídeos, incluindo filtros, transformações e detecção de objetos.

  • Matplotlib – Ferramenta para visualização de gráficos e exibição de imagens processadas.

  • Scikit-Image – Conjunto avançado de algoritmos para análise e manipulação de imagens.

  • Pillow – Manipulação e conversão de imagens em diversos formatos.

Dica: No Google Colab, muitas dessas bibliotecas já vêm pré-instaladas. Porém, rodar o comando acima garante que você tenha a versão mais atualizada para o seu projeto.

Estrutura de Diretórios do Linux#

Antes de explorarmos os comandos do terminal, é fundamental entender como o sistema de arquivos do Linux está organizado. Diferente do Windows, que usa letras de unidade (C:, D:), o Linux possui uma estrutura hierárquica única, onde tudo começa a partir de um diretório raiz /.

Veja abaixo uma representação simplificada dos principais diretórios:

/             ← Raiz do sistema (tudo começa aqui)
├── home/         ← Diretórios pessoais dos usuários
│  ├── joao/       ← Pasta do usuário "joao"
│  │  ├── Documentos/
│  │  ├── Downloads/
│  │  └── projetos/
│  └── maria/       ← Pasta do usuário "maria"
│
├── bin/          ← Programas essenciais (ls, cp, mv, cat...)
├── usr/          ← Programas e bibliotecas do usuário
│  ├── bin/        ← Programas adicionais
│  └── lib/        ← Bibliotecas compartilhadas
│
├── etc/          ← Arquivos de configuração do sistema
├── var/          ← Dados variáveis (logs, cache, banco de dados)
├── tmp/          ← Arquivos temporários
├── root/         ← Diretório pessoal do administrador (root)
└── dev/          ← Dispositivos de hardware (HD, USB, GPU...)

Entendendo os principais diretórios:

Diretório

Descrição

/

Diretório raiz, ponto de partida de toda a estrutura

/home

Contém as pastas pessoais de cada usuário do sistema

/bin

Binários essenciais do sistema (comandos básicos como ls, cp)

/usr

Programas e bibliotecas instalados pelo usuário

/etc

Arquivos de configuração do sistema e aplicações

/var

Dados que mudam frequentemente (logs, cache)

/tmp

Arquivos temporários (apagados ao reiniciar)

/root

Pasta pessoal do superusuário (administrador)

/dev

Representação de dispositivos de hardware

Comandos e Terminal Linux para IA#

O domínio do terminal Linux é uma habilidade essencial para quem trabalha com Inteligência Artificial e Visão Computacional. Seja no Google Colab (que roda sobre máquinas virtuais Linux), em servidores de treinamento ou em sua própria máquina, os comandos apresentados aqui serão utilizados diariamente.

No Google Colab, prefixe os comandos com ! para executá-los no shell do sistema.



Criação e Manipulação de Arquivos#

Comando

Descrição

Exemplo

echo

Exibe texto na tela ou escreve em arquivos

!echo "Olá, mundo!"

mkdir

Cria um diretório

!mkdir datasets

mkdir -p

Cria diretórios aninhados

!mkdir -p projeto/dados/treino

touch

Cria arquivo vazio ou atualiza data de modificação

!touch config.yaml

cp

Copia arquivos

!cp modelo.h5 backup_modelo.h5

cp -r

Copia diretórios recursivamente

!cp -r datasets/ datasets_backup/

mv

Move ou renomeia arquivos/diretórios

!mv modelo_v1.h5 modelos/

rm

Remove arquivos

!rm arquivo_temporario.txt

rm -r

Remove diretórios e conteúdo

!rm -r pasta_antiga/

rm -rf

Remove forçadamente (use com cuidado!)

!rm -rf __pycache__/

tree

Exibe estrutura de diretórios em árvore (instalar: apt install tree)

!tree -L 2

Usando echo para criar arquivos:

# Cria (ou sobrescreve) o arquivo config.yaml com o conteúdo fornecido
!echo "learning_rate: 0.001" > config.yaml

# Adiciona uma nova linha ao final do arquivo existente
!echo "batch_size: 32" >> config.yaml

Monitoramento e Informações do Sistema#

Comando

Descrição

Exemplo

top / htop

Monitora processos, CPU e RAM em tempo real

!top

nvidia-smi

Exibe informações da GPU (essencial para IA)

!nvidia-smi

df -h

Mostra uso de espaço em disco

!df -h

free -h

Mostra uso de memória RAM

!free -h

cat /proc/cpuinfo

Detalhes sobre o processador

!cat /proc/cpuinfo

lsb_release -a

Informações sobre a versão do Linux

!lsb_release -a

Exercício: Fundamentos de Terminal Linux#

Este exercício prático consolida os comandos básicos de terminal Linux. Você irá criar uma estrutura de projeto, trabalhar com arquivos, configurar permissões, criar e executar um script, e monitorar processos.

Nota: No Google Colab, adicione o prefixo ! antes de cada comando para executá-lo no shell (exemplo: !pwd ao invés de pwd).

Passo 1: Criar estrutura do projeto#

# Verificar onde estamos
pwd

# Criar pasta do projeto com subpastas
mkdir -p meu_projeto/scripts meu_projeto/logs

# Verificar a estrutura criada
ls -la meu_projeto/

Passo 2: Criar arquivos de configuração#

# Criar um arquivo de configuração simples
echo "# Configurações do Projeto" > meu_projeto/config.txt
echo "EPOCHS=100" >> meu_projeto/config.txt
echo "LEARNING_RATE=0.001" >> meu_projeto/config.txt

# Verificar o conteúdo
cat meu_projeto/config.txt

Passo 3: Criar um script Python#

# Criar um script Python simples
echo '#!/usr/bin/env python3
import time
import os

print("=== Meu Primeiro Script ===")
print(f"Diretorio atual: {os.getcwd()}")
print("Processando...")

for i in range(5):
  print(f"Passo {i+1}/5")
  time.sleep(1)

print("Concluido!")
' > meu_projeto/scripts/treinar.py

# Verificar se o arquivo foi criado
cat meu_projeto/scripts/treinar.py

Passo 4: Verificar e ajustar permissões#

# Ver as permissões atuais
ls -l meu_projeto/scripts/treinar.py

# Tornar o script executável
chmod +x meu_projeto/scripts/treinar.py

# Verificar as novas permissões (observe o 'x')
ls -l meu_projeto/scripts/treinar.py

Passo 5: Executar o script#

# Executar o script Python
python meu_projeto/scripts/treinar.py

# Ou executar diretamente (já que demos permissão)
./meu_projeto/scripts/treinar.py

Passo 6: Redirecionar saída para arquivo de log#

# Executar e salvar a saída em um arquivo de log
python meu_projeto/scripts/treinar.py > meu_projeto/logs/saida.log 2>&1

# Verificar o conteúdo do log
cat meu_projeto/logs/saida.log

Passo 7: Monitorar processos#

# Ver processos Python em execução
ps aux | grep python | head -5

# Ver uso de memória
free -h

# Ver espaço em disco
df -h | head -3

Conceitos de Visão Computacional#

A Visão Computacional (VC) é o campo da Inteligência Artificial que capacita os computadores a enxergarem e interpretarem o mundo visual de forma semelhante ao ser humano [Szeliski, 2022]. Mais do que apenas capturar luz, o objetivo fundamental da VC é transformar dados brutos — um amontoado de números — em conhecimento semântico, permitindo que máquinas tomem decisões baseadas no que veem.

Enquanto nossos olhos e cérebro percebem formas e cores instantaneamente, para o computador, uma imagem é uma matriz numérica. O desafio da área é criar algoritmos que consigam identificar padrões (bordas, texturas, objetos) dentro dessa estrutura matemática.

Onde a Visão Computacional impacta o mundo?#

As aplicações são vastas e estão em constante expansão:

  • Biometria e Segurança: O reconhecimento facial identifica indivíduos com base em geometria facial única, sendo a base de sistemas de autenticação em smartphones.

  • Mobilidade Autônoma: Veículos autônomos utilizam a detecção e rastreamento de objetos para identificar pedestres, placas e semáforos.

  • Saúde Digital: Na medicina, a VC auxilia na análise de imagens diagnósticas (raio-X, tomografias), detectando patologias com alta precisão.

  • Indústria 4.0: Sistemas de inspeção visual automática monitoram linhas de produção, identificando defeitos em peças com velocidade sobre-humana.

  • Realidade Aumentada (AR): Permite o alinhamento espacial de objetos virtuais sobre o mundo real, essencial para jogos e treinamentos técnicos.


Anatomia da Imagem Digital: Pixels e Matrizes#

Toda imagem digital é um mosaico de pixels (abreviação de Picture Element), que representam a menor unidade de informação visual. Quando dizemos que um computador “vê” uma imagem, ele está, na verdade, processando uma matriz de valores numéricos.

Resolução e Amostragem#

A quantidade total de pixels define a resolução. Por exemplo, uma imagem Full HD possui 1920x1080 pixels (cerca de 2 milhões de pontos), enquanto uma imagem 4K (Ultra HD) possui 3840x2160 pixels (aproximadamente 8,2 milhões).

No mundo real, vemos isso na contagem de Megapixels das câmeras de smartphones: uma câmera de 12MP captura imagens com 12 milhões de pixels.

  • Vantagem: Quanto maior a resolução, maior o nível de detalhe. Isso é crucial para identificar objetos pequenos à distância em sistemas de vigilância ou detectar microfraturas em exames médicos.

  • O Desafio Computacional: Imagens de alta resolução exigem muito mais memória e processamento. Em Visão Computacional, é comum redimensionar imagens para resoluções menores (como 224x224 ou 640x640) antes de enviá-las para modelos de Deep Learning, visando equilibrar a precisão com a velocidade de resposta (latência).

Canais de Cor e Quantização#

No modelo digital mais comum, as cores são decompostas em três canais principais: Red (Vermelho), Green (Verde) e Blue (Azul). A combinação desses canais em diferentes proporções permite a representação de uma vasta gama de cores.

  • O que é Quantização? É o processo de converter a intensidade contínua da luz em valores numéricos discretos. Em imagens padrão de 8 bits, cada canal possui 2⁸ = 256 níveis de intensidade.

  • A Escala 0-255:

  • 0 representa a ausência total de luz (preto no canal).

  • 255 representa a intensidade máxima (saturação daquela cor).

  • True Color (Cores Reais): Como temos 3 canais de 8 bits, cada pixel é representado por 24 bits no total. Isso permite 16.777.216 combinações de cores (256³), o que é suficiente para criar transições suaves (gradientes) que o olho humano percebe como naturais.

Dica técnica: Enquanto fotos comuns usam 8 bits, áreas como imagens médicas ou fotografia profissional (RAW) utilizam 10, 12 ou até 16 bits por canal para capturar detalhes ínfimos em sombras e luzes intensas.

Exemplo de Cálculo: Quanto de memória uma imagem consome?

Para calcular o tamanho de uma imagem não compactada em memória (RAM), multiplicamos: Largura × Altura × Canais × Bytes por Canal

  • Imagem Full HD (RGB): 1920 × 1080 pixels.

  • Canais: 3 (Vermelho, Verde e Azul).

  • Profundidade: 8 bits (ou 1 byte) por canal.

Cálculo: \(1920 \times 1080 \times 3 \times 1 \text{ byte} = 6.220.800 \text{ bytes} \approx \mathbf{5,93 \text{ MB}}\)

Perceba que, embora 6 MB pareça pouco, um vídeo Full HD a 60 quadros por segundo consumiria cerca de 355 MB por segundo se não houvesse compressão!

Tipos de Imagens#

As imagens digitais podem assumir diferentes representações matemáticas, dependendo do objetivo do processamento. As três formas mais fundamentais são:

  • Imagens Coloridas (RGB):

  • Cada pixel é representado por um trio de valores: (R, G, B).

  • É o formato padrão para capturar a riqueza visual do mundo real.

  • Uso: Fotografias, vídeos e qualquer aplicação onde a cor seja uma característica essencial para distinguir objetos (ex: identificar a cor de um semáforo).

  • Escala de Cinza (Grayscale):

  • Cada pixel possui apenas um valor de intensidade (0 a 255).

  • Vantagem: Reduz a quantidade de dados em 1/3 comparado ao RGB, acelerando o processamento sem perder informações de forma e textura.

  • Uso: Reconhecimento facial, leitura de placas de veículos e processamento de exames de Raio-X.

  • Imagens Binárias (Preto e Branco):

  • Cada pixel possui apenas dois estados possíveis: 0 (preto) ou 255 (branco).

  • Geralmente são geradas a partir de um processo chamado limiarização (thresholding).

  • Uso: Segmentação de objetos (separar o objeto do fundo), detecção de contornos e sistemas de OCR (Reconhecimento Óptico de Caracteres) para leitura de documentos digitalizados.

Veja a Ilustração a seguir sobre uma imagem digital.

setup

A seguir, nosso primeiro exemplo prático sobre representação de imagens em diferentes formatos. Geraremos uma imagem RGB com gradientes de cor, convertê-la-emos para tons de cinza usando o canal verde e, por fim, aplicaremos um limiar para criar uma imagem binária.

O código abaixo demonstra esse processo visualmente.

import numpy as np
import matplotlib.pyplot as plt

# 1. Gerar a Imagem RGB (Anatomia: combinando 3 matrizes de canais)
# Criamos gradientes de 0 a 255 usando meshgrid para representar intensidades
x = np.linspace(0, 255, 256, dtype=np.uint8)
R, G = np.meshgrid(x, x) 
B = 255 - G # Canal azul oposto ao verde para criar variação visual

# np.dstack "empilha" as matrizes R, G, B na terceira dimensão (profundidade)
img_rgb = np.dstack([R, G, B])

# 2. Tons de Cinza: Redução de informação (média aritmética dos canais)
img_gray = img_rgb.mean(axis=2).astype(np.uint8)

# 3. Binária: Decisão baseada em limiar (Thresholding)
img_bin = (img_gray > 127).astype(np.uint8) * 255

# Visualização Lado a Lado
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
titles = ["Matriz RGB (3 Canais)", "Matriz Cinza (1 Canal)", "Matriz Binária (0 ou 255)"]
images = [img_rgb, img_gray, img_bin]

for ax, title, img in zip(axes, titles, images):
  ax.imshow(img, cmap='gray' if img.ndim == 2 else None)
  ax.set_title(title)
  ax.axis("off")

plt.tight_layout()
plt.show()
../../_images/7b986d0b89f62a005a654b508876074ac9d73aaf646fc46507ceda2f5973a46c.png

Processamento Digital de Imagens#

O que é Processamento Digital de Imagens (PDI)?#

O Processamento Digital de Imagens envolve a aplicação de algoritmos matemáticos para transformar ou extrair informações de uma imagem digital. É fundamental distinguir o PDI da Visão Computacional:

  • PDI (Imagem → Imagem): O foco está na transformação. O objetivo é melhorar a qualidade visual ou realçar características para um observador humano ou para facilitar o processamento posterior (ex: remover ruído, ajustar brilho).

  • Visão Computacional (Imagem → Informação): O foco está na interpretação. O objetivo é extrair conhecimento semântico da cena, permitindo que o computador tome decisões baseadas no conteúdo visual (ex: identificar que há um pedestre na via).

Principais Etapas do Fluxo de Trabalho#

Um projeto de Visão Computacional geralmente segue um fluxo estruturado de etapas, onde o resultado de uma fase serve como base para a próxima.

Aquisição da Imagem#

É o ponto de partida, onde a luz do mundo real é convertida em sinais digitais por sensores (câmeras, scanners, satélites). A qualidade nesta etapa — iluminação adequada, foco e resolução — é determinante para o sucesso de todo o projeto.

Aquisição

Exemplo: Captura de imagens médicas para análise diagnóstica.

Pré-processamento#

Nesta fase, preparamos a imagem “limpando” imperfeições e realçando o que é importante. O objetivo é remover distrações (ruído) e normalizar os dados para os algoritmos de análise.

  • Filtros de Suavização: Eliminam granulações indesejadas (ruído).

  • Realce de Contraste: Torna objetos de interesse mais distinguíveis do fundo.

  • Redimensionamento: Ajusta a resolução para otimizar o custo computacional.

Pré-processamento

Exemplo: Redução de ruído tipo "sal e pimenta" para limpar a imagem.

Segmentação#

A segmentação divide a imagem em regiões, isolando os objetos de interesse do restante da cena (fundo). O resultado típico é uma máscara binária.

  • Limiarização (Thresholding): Separa pixels com base na sua intensidade.

  • Detecção de Bordas: Identifica contornos através de mudanças abruptas de cor.

Segmentação

Exemplo: Isolamento de um objeto (maçã) para análise individualizada.

Reconhecimento e Interpretação#

É a etapa final onde o computador atribui significado aos dados. Aqui, os pixels processados são transformados em rótulos (labels), categorias ou decisões automáticas.

  • Extração de Atributos: Análise de geometria, textura e cor dos objetos segmentados.

  • Classificação: Uso de modelos de Machine Learning ou Deep Learning para identificar o que é o objeto.

Reconhecimento

Exemplo: Rastreamento automático e análise de comportamento em neurociência.

Referência: Saiba mais sobre o projeto de rastreamento de camundongos

Abrindo uma Imagem com OpenCV#

O OpenCV (Open Source Computer Vision Library) é a biblioteca padrão da indústria para Visão Computacional. Para carregar uma imagem, utilizamos a função cv2.imread(), que converte o arquivo em um array NumPy.

O Mistério do BGR vs RGB#

Um detalhe fundamental ao trabalhar com OpenCV é que ele carrega as imagens no formato BGR (Azul, Verde, Vermelho), enquanto a maioria das bibliotecas de visualização (como o Matplotlib) utiliza o padrão RGB.

Essa convenção BGR é histórica: na época em que o OpenCV foi criado, os fabricantes de câmeras e displays utilizavam essa ordem por padrão. Por isso, quase sempre precisaremos realizar uma conversão antes de exibir ou processar a imagem em outras ferramentas.

BGR vs RGB

Exemplo Prático: Carregamento e Inspeção#

import cv2
import matplotlib.pyplot as plt

# 1. Carregar a imagem (OpenCV lê em BGR)
img_bgr = cv2.imread('python.png')

# 2. Inspecionar a estrutura da matriz
print(f"Dimensões da imagem (H, W, C): {img_bgr.shape}")
print(f"Tipo de dados: {img_bgr.dtype}")

# 3. Converter para RGB (para visualização correta no Matplotlib)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

# 4. Exibir os resultados
plt.imshow(img_rgb)
plt.title('Imagem Carregada (Formato RGB)')
plt.axis('off')
plt.show()

Destaques do código:

  • img_bgr.shape – Retorna uma tupla com a altura, largura e número de canais (ex: 3 para colorido).

  • cv2.cvtColor(..., cv2.COLOR_BGR2RGB) – Realiza a transposição correta dos canais de cor.

  • plt.axis('off') – Remove as coordenadas do gráfico, mantendo apenas a imagem limpa.

Exercício: Fluxo de Processamento OpenCV#

Objetivo: Implementar um pipeline básico para coleta, conversão e exportação de imagens.

Fase 1: Coleta de Dados. Utilize a biblioteca requests para baixar uma imagem. Use io.BytesIO para decodificar os dados binários diretamente em uma matriz OpenCV via cv2.imdecode.

Fase 2: Pré-processamento. Converta a imagem original para Escala de Cinza (cv2.COLOR_BGR2GRAY).

Fase 3: Identidade Visual. Adicione uma marca d’água legível:

  1. Desenhe um retângulo preenchido (cv2.rectangle) em uma região da imagem.

  2. Sobreponha um texto (cv2.putText) informando a biblioteca utilizada.

Fase 4: Exportação. Salve o resultado final em disco utilizando cv2.imwrite.


Resultado Esperado:

Resultado do Exercício

Operações Aritméticas com Imagens#

As operações aritméticas com imagens permitem manipular seus valores de pixel de maneira precisa, abrindo um leque de possibilidades para processamento e edição. As operações mais comuns incluem:

Adição

A adição de imagens combina duas imagens somando os valores dos pixels correspondentes. A operação pode ser descrita matematicamente como:

\[I_{\text{resultante}}(x, y) = I_1(x, y) + I_2(x, y)\]

onde \( I_{\text{resultante}}(x, y) \) é o valor do pixel na posição \((x, y)\) da imagem resultante, \( I_1(x, y) \) é o valor do pixel na posição \((x, y)\) da primeira imagem, e \( I_2(x, y) \) é o valor do pixel na posição \((x, y)\) da segunda imagem.

Essa operação é útil para:

  • Combinar informações: Ao adicionar imagens de diferentes fontes, podemos integrar informações e criar uma visão mais completa.

  • Ajustar o brilho: Adicionar um valor constante a todos os pixels aumenta o brilho da imagem.

Exemplo: Adicionar duas imagens e realizar uma combinação de informações.

import numpy as np
import matplotlib.pyplot as plt

# Carregar as imagens
img1 = plt.imread('tropical1.png')
img2 = plt.imread('tropical2.png')

# Verificar se as imagens têm o mesmo tamanho
if img1.shape != img2.shape:
  raise ValueError("As imagens devem ter o mesmo tamanho para adição.")

# Adicionar as imagens (PROBLEMA: pode saturar!)
added_img = img1 + img2

# Exibir as imagens lado a lado
fig, axs = plt.subplots(1, 3, figsize=(15, 5))
axs[0].imshow(img1)
axs[0].set_title('Imagem 1')
axs[0].axis('off')

axs[1].imshow(img2)
axs[1].set_title('Imagem 2')
axs[1].axis('off')

axs[2].imshow(added_img)
axs[2].set_title('Imagem Resultante\n(com saturação - áreas brancas)')
axs[2].axis('off')

plt.tight_layout()
plt.show()

Baixar tropical1 tropical2

Resultado da adição direta com saturação

Problema da Saturação:

Quando somamos pixels diretamente, o resultado pode ultrapassar 255 (limite máximo para 8 bits). Por exemplo: 200 + 150 = 350. O NumPy trunca esse valor para 255, causando áreas brancas “estouradas” na imagem, perdendo detalhes importantes.

Solução - Blend/Overlay Correto (usando apenas NumPy):

Para criar um overlay bonito que combina as duas imagens precisamos fazer uma média ponderada:

# Fazer blend (mistura com pesos iguais: 50% de cada imagem)
alpha = 0.5 # Peso da primeira imagem
beta = 0.5  # Peso da segunda imagem (alpha + beta = 1.0)

img_blend = img1 * alpha + img2 * beta

# Visualizar o resultado
fig, axs = plt.subplots(1, 3, figsize=(15, 5))
axs[0].imshow(img1)
axs[0].set_title('Imagem 1')
axs[0].axis('off')

axs[1].imshow(img2)
axs[1].set_title('Imagem 2')
axs[1].axis('off')

axs[2].imshow(img_blend)
axs[2].set_title('Imagem Resultante\n(Blend sem saturação)')
axs[2].axis('off')

plt.tight_layout()
plt.show()
Blend com média ponderada

Note que nesta etapa a imagem melhora, mas ainda possui muito branco. A média ponderada reduz a saturação, porém não elimina completamente as áreas claras excessivas.

Dica - Ajustando os Pesos:

Você pode experimentar diferentes valores de alpha e beta para controlar a contribuição de cada imagem:

# Criar vários blends com diferentes proporções
fig, axs = plt.subplots(1, 4, figsize=(20, 5))

pesos = [(0.7, 0.3), (0.5, 0.5), (0.3, 0.7), (0.2, 0.8)]
for i, (alpha, beta) in enumerate(pesos):
  blend = img1 * alpha + img2 * beta
  axs[i].imshow(blend)
  axs[i].set_title(f'Blend {int(alpha*100)}%-{int(beta*100)}%')
  axs[i].axis('off')

plt.tight_layout()
plt.show()
Diferentes proporções de blend

Desafio:

Observe a imagem abaixo. Ela mostra uma junção equilibrada e visualmente harmoniosa das duas imagens tropicais. Seu desafio é:

    i. Descobrir qual operação matemática foi utilizada para criar este resultado

    ii. Implementar o código que reproduz este efeito

    iii. Explicar por que esta abordagem produz um resultado melhor que a adição simples

../../_images/tropical_sum.png

Dica: Pense em como evitar saturação enquanto mantém o brilho e os detalhes de ambas as imagens.

Subtração

A subtração de imagens calcula a diferença entre os valores dos pixels correspondentes de duas imagens, revelando mudanças ou variações entre elas. A operação pode ser descrita matematicamente como:

\[I_{\text{resultante}}(x, y) = I_1(x, y) - I_2(x, y)\]

onde \( I_{\text{resultante}}(x, y) \) é o valor do pixel na posição \((x, y)\) da imagem resultante, \( I_1(x, y) \) é o valor do pixel na posição \((x, y)\) da primeira imagem, e \( I_2(x, y)\) é o valor do pixel na posição \((x, y)\) da segunda imagem.

Essa operação é útil para:

  • Detectar diferenças: A subtração pode destacar as diferenças entre duas imagens, como mudanças ao longo do tempo ou objetos movidos.

  • Isolar elementos: Ao subtrair uma imagem de fundo de uma imagem com um objeto em primeiro plano, podemos isolar o objeto.

Exemplo: Subtrair uma imagem de referência de outra para destacar as alterações ou isolar um objeto. Veja o exemplo a seguir, onde em um cenário sem muitos objetos, existe um sútil movimento de uma mulher caminhando.

import numpy as np
import matplotlib.pyplot as plt

# Carregar as imagens
img1 = plt.imread('w1.png')
img2 = plt.imread('w2.png')

# Subtrair as imagens
subtracted_img = img1 - img2

# Exibir a imagem resultante da subtração
fig, axs = plt.subplots(1, 3, figsize=(10, 5))

# Exibir a primeira imagem
axs[0].imshow(img1)
axs[0].set_title('Imagem 1')
axs[0].axis('off')

# Exibir a segunda imagem
axs[1].imshow(img2)
axs[1].set_title('Imagem 2')
axs[1].axis('off')

# Exibir a imagem resultante da subtração
axs[2].imshow(subtracted_img)
axs[2].set_title('Imagem Resultante')
axs[2].axis('off')
plt.show()

Baixar walking1 walking2

Atenção - Valores Negativos: A subtração pode gerar valores negativos. O NumPy automaticamente converte para 0 (clipping inferior), o que pode ocultar diferenças bidirecionais. Para visualizar diferenças em ambas as direções, use o valor absoluto:

# Visualizar diferenças absolutas (bidirecional)
subtracted_img_abs = np.abs(img1.astype(np.int16) - img2.astype(np.int16)).astype(np.uint8)

# Comparar métodos
fig, axs = plt.subplots(1, 2, figsize=(12, 5))
axs[0].imshow(subtracted_img), axs[0].set_title('Subtração Simples'), axs[0].axis('off')
axs[1].imshow(subtracted_img_abs), axs[1].set_title('Subtração Absoluta'), axs[1].axis('off')
plt.tight_layout()
plt.show()

Multiplicação

A multiplicação de imagens pode ser realizada de duas maneiras:

  • Multiplicação por uma constante: Multiplicar uma imagem por uma constante altera o contraste da imagem. Um valor maior aumenta o contraste, enquanto um valor menor o reduz. A operação pode ser descrita matematicamente como:

\[I_{\text{resultante}}(x, y) = c \cdot I(x, y)\]

onde \( I_{\text{resultante}}(x, y) \) é o valor do pixel na posição \((x, y)\) da imagem resultante, \( c \) é a constante multiplicativa, e \( I(x, y) \) é o valor do pixel na posição \((x, y)\) da imagem original.

  • Multiplicação por outra imagem: Multiplicar duas imagens elemento a elemento pode ser usado para aplicar máscaras, onde uma imagem define áreas específicas para serem modificadas na outra imagem. A operação pode ser descrita matematicamente como:

\[I_{\text{resultante}}(x, y) = I_1(x, y) \cdot I_2(x, y)\]

onde \( I_{\text{resultante}}(x, y) \) é o valor do pixel na posição \((x, y)\) da imagem resultante, \( I_1(x, y) \) é o valor do pixel na posição \((x, y)\) da primeira imagem, e \( I_2(x, y) \) é o valor do pixel na posição \((x, y)\) da segunda imagem.

Exemplo 1: Multiplicação por Constante (Ajuste de Contraste)

# Carregar imagem
img = plt.imread('w1.png')

# Multiplicação por uma constante
constant = 1.5
multiplied_img = np.clip(img * constant, 0, 1).astype(img.dtype) # Clip para evitar saturação

# Exibir comparação
fig, axs = plt.subplots(1, 2, figsize=(12, 5))

# Exibir a imagem original
axs[0].imshow(img)
axs[0].set_title('Imagem Original')
axs[0].axis('off')

# Exibir a imagem resultante da multiplicação
axs[1].imshow(multiplied_img)
axs[1].set_title(f'Contraste Aumentado (×{constant})')
axs[1].axis('off')
plt.tight_layout()
plt.show()

Baixar walking1

Exemplo 2: Multiplicação por Máscara (Segmentação Circular)

A multiplicação por máscaras permite isolar regiões específicas da imagem, sendo fundamental em operações de segmentação e foco seletivo.

# Carregar imagem
img1 = plt.imread('w1.png')

# Criar uma máscara circular
h, w = img1.shape[:2]
Y, X = np.ogrid[:h, :w]
center_y, center_x = h // 2, w // 2
mask = ((X - center_x)**2 + (Y - center_y)**2 <= (min(h, w) // 3)**2).astype(float)

# Expandir máscara para 3 canais RGB
mask_3d = np.stack([mask] * 3, axis=-1)

# Aplicar máscara (manter apenas região central)
masked_img = (img1 * mask_3d).astype(img1.dtype)

# Visualizar
fig, axs = plt.subplots(1, 3, figsize=(15, 5))
axs[0].imshow(img1), axs[0].set_title('Original'), axs[0].axis('off')
axs[1].imshow(mask, cmap='gray'), axs[1].set_title('Máscara Circular'), axs[1].axis('off')
axs[2].imshow(masked_img), axs[2].set_title('Imagem Mascarada'), axs[2].axis('off')
plt.tight_layout()
plt.show()

Manipulação Direta de Pixels

NumPy permite acesso e modificação de regiões específicas usando indexação (slicing). Importante: Imagens são indexadas como [linha, coluna] ou [y, x], o que é o inverso da convenção cartesiana usual (x, y).

# Carregar imagem (fazer cópia para não modificar original)
img_original = plt.imread('w1.png')
img = img_original.copy()

# Extrair região de interesse (ROI - Region of Interest)
corte = img[200:, 450:900] # Linhas 200 até o fim, colunas 450-900

# Modificar pixels em uma sub-região (retângulo verde)
corte[10:20, 100:300] = [0, 1, 0] # Verde em formato float [0-1]

# Visualizar modificação
fig, axs = plt.subplots(1, 2, figsize=(12, 5))
axs[0].imshow(img_original), axs[0].set_title('Original'), axs[0].axis('off')
axs[1].imshow(img), axs[1].set_title('Com Retângulo Verde'), axs[1].axis('off')
plt.tight_layout()
plt.show()

Resumo das Operações Aritméticas

Operação

Fórmula

Aplicação Principal

Cuidado

Adição

\(I_1 + I_2\)

Combinar informações, aumentar brilho

Saturação em 255 (usar blend)

Subtração

\(I_1 - I_2\)

Detecção de movimento, diferenças

Valores negativos → 0 (usar abs para bidirecional)

Multiplicação (constante)

\(c \cdot I\)

Ajuste de contraste

Saturação (usar np.clip)

Multiplicação (máscara)

\(I_1 \cdot M\)

Segmentação, foco seletivo

Normalizar máscara [0-1]

Divisão

\(I_1 / I_2\)

Normalização, correção de iluminação

Divisão por zero

Dica: Para combinar imagens sem saturação, use blend com pesos que somem 1.0: (img1 * alpha + img2 * beta) onde alpha + beta = 1.0

Conversão entre Espaços de Cor#

A conversão entre espaços de cor é uma operação fundamental em visão computacional, permitindo representar imagens de diferentes formas conforme a necessidade da aplicação. Cada espaço de cor possui características específicas que facilitam determinadas tarefas de processamento e análise.

O que são Espaços de Cor?#

Um espaço de cor é um modelo matemático que define como as cores são representadas numericamente. Enquanto o modelo RGB é intuitivo para exibição em monitores, outros espaços como HSV e LAB são mais adequados para tarefas específicas de análise e segmentação.

Por que utilizar diferentes espaços de cor?

  • RGB (Red, Green, Blue): Padrão para exibição em dispositivos. Os três canais (vermelho, verde, azul) são altamente correlacionados, dificultando a separação de informações de cor e luminosidade.

  • Grayscale (Escala de Cinza): Representa apenas a intensidade luminosa, descartando informação de cor. Reduz o tamanho dos dados em 1/3 e simplifica muitos algoritmos de processamento.

  • HSV (Hue, Saturation, Value): Separa a matiz (cor) da saturação (pureza) e do valor (brilho). Ideal para segmentação por cor, pois é mais robusto a variações de iluminação.

  • LAB (Lightness, A, B): Espaço perceptualmente uniforme. O canal L representa luminosidade, enquanto A (verde-vermelho) e B (azul-amarelo) representam componentes cromáticas. Muito utilizado em correção de cor e análise de similaridade visual.

Conversões Básicas com OpenCV#

O OpenCV utiliza a função cv2.cvtColor() para realizar conversões entre diferentes espaços de cor. A sintaxe geral é:

imagem_convertida = cv2.cvtColor(imagem_original, codigo_conversao)

Exemplo Completo: Explorando Múltiplos Espaços de Cor

import cv2
import matplotlib.pyplot as plt

# Carregar imagem (OpenCV lê em BGR)
img = cv2.imread('w3.png')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # Converter para RGB (Matplotlib)

# Realizar conversões
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

# Visualização comparativa
plt.figure(figsize=(16, 4))
plt.subplot(1, 4, 1), plt.imshow(img_rgb), plt.title('RGB'), plt.axis('off')
plt.subplot(1, 4, 2), plt.imshow(img_gray, cmap='gray'), plt.title('Grayscale'), plt.axis('off')
plt.subplot(1, 4, 3), plt.imshow(img_hsv), plt.title('HSV'), plt.axis('off')
plt.subplot(1, 4, 4), plt.imshow(img_lab), plt.title('LAB'), plt.axis('off')
plt.tight_layout()
plt.show()

Conversões Específicas#

RGB → Grayscale (Escala de Cinza)

A conversão para escala de cinza utiliza uma média ponderada dos canais RGB, considerando a sensibilidade do olho humano a diferentes cores:

\[\text{Gray} = 0.299 \times R + 0.587 \times G + 0.114 \times B\]

RGB → HSV (Matiz, Saturação, Valor)

O espaço HSV é especialmente útil para detecção de objetos por cor, pois separa a informação cromática da intensidade luminosa.

RGB → LAB (Luminância, Canais A/B)

O espaço LAB é perceptualmente uniforme, ou seja, distâncias numéricas correspondem melhor a diferenças visuais percebidas pelo olho humano.

Visualização de Canais Individuais#

Muitas vezes é útil analisar cada canal separadamente para entender como a informação está distribuída.

Canais RGB:

r, g, b = cv2.split(img_rgb)

Canais HSV:

h, s, v = cv2.split(img_hsv)

Principais Conversões do OpenCV#

Conversão

Código OpenCV

Aplicação Típica

BGR → RGB

cv2.COLOR_BGR2RGB

Visualização no Matplotlib

BGR → Grayscale

cv2.COLOR_BGR2GRAY

Simplificar processamento, detecção de bordas

BGR → HSV

cv2.COLOR_BGR2HSV

Segmentação por cor, rastreamento de objetos

BGR → LAB

cv2.COLOR_BGR2LAB

Análise de similaridade de cor, equalização

HSV → BGR

cv2.COLOR_HSV2BGR

Reconstruir imagem após processamento em HSV

Grayscale → BGR

cv2.COLOR_GRAY2BGR

Compatibilizar dimensões com imagens coloridas

Dicas Importantes#

OpenCV vs Matplotlib:

  • OpenCV carrega imagens em BGR por padrão

  • Matplotlib espera RGB para exibição correta

  • Sempre converta com cv2.cvtColor(img, cv2.COLOR_BGR2RGB) antes de usar plt.imshow()

Escolhendo o Espaço de Cor Adequado:

  • Use Grayscale quando a cor não for relevante (OCR, detecção de bordas)

  • Use HSV para segmentação robusta por cor (independente de iluminação)

  • Use LAB para comparações perceptuais de cor

  • Mantenha RGB apenas para visualização final

Desafio - Transições Suaves:

Aproveite que estudou sobre como transitar entre diferentes espaços de cor e tente criar um vídeo com transições suaves como este do link.

Dica: Use cv2.VideoWriter e interpole os valores de conversão quadro a quadro para criar animações fluidas.

Transformações Geométricas#

As transformações geométricas são operações fundamentais em visão computacional que modificam a disposição espacial dos pixels em uma imagem, permitindo alterar sua posição, orientação, tamanho ou forma sem, no entanto, modificar o conteúdo visual intrínseco. Diferentemente das operações aritméticas, que atuam sobre os valores de intensidade dos pixels, as transformações geométricas reorganizam a localização desses pixels no espaço 2D, sendo essenciais para tarefas como correção de perspectiva em documentos, normalização de orientação de objetos detectados, alinhamento de múltiplas capturas de uma mesma cena e, amplamente, no aumento de dados (data augmentation) para o treinamento de redes neurais.

Fundamentação Matemática#

Matematicamente, uma transformação geométrica mapeia cada coordenada original \((x, y)\) de uma imagem para uma nova posição \((x', y')\) através de uma operação matricial. A forma mais clássica e abrangente de representar essas mudanças é por meio de matrizes de transformação afim, que preservam o paralelismo entre linhas. Em um espaço 2D, utilizamos coordenadas homogêneas para permitir que operações como a translação sejam representadas por multiplicações de matrizes 3×3:

\[\begin{split} \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} = \begin{bmatrix} a & b & t_x \\ c & d & t_y \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \end{split}\]

Nessa matriz geral, os parâmetros \(a, b, c, d\) controlam transformações lineares como rotação, escala e cisalhamento, enquanto os termos \(t_x\) e \(t_y\) definem a translação (deslocamento) nos eixos horizontal e vertical, respectivamente.


Translação (Deslocamento)#

A translação consiste no deslocamento de todos os pixels da imagem por uma distância fixa. Matematicamente:

\[\begin{split} \begin{cases} x' = x + t_x \\ y' = y + t_y \end{cases} \end{split}\]

Exemplo Prático:

import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('w3.png')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
tx, ty = 50, 100
M = np.float32([[1, 0, tx], [0, 1, ty]])
img_trans = cv2.warpAffine(img_rgb, M, (img_rgb.shape[1], img_rgb.shape[0]))
plt.imshow(img_trans), plt.show()


Rotação#

A rotação gira a imagem em torno de um ponto central \((C_x, C_y)\) por um ângulo \(\theta\). O OpenCV utiliza cv2.getRotationMatrix2D() para gerar a matriz 2×3 correspondente.

Exemplo Prático:

h, w = img_rgb.shape[:2]
M = cv2.getRotationMatrix2D((w//2, h//2), 45, 1.0)
img_rot = cv2.warpAffine(img_rgb, M, (w, h))
plt.imshow(img_rot), plt.show()


Escala (Redimensionamento)#

A escala altera as dimensões da imagem multiplicando as coordenadas por fatores \(s_x\) e \(s_y\). O OpenCV utiliza algoritmos de interpolação como cv2.INTER_LINEAR (para ampliar) e cv2.INTER_AREA (para reduzir).

Exemplo Prático:

img_res = cv2.resize(img_rgb, (300, 200), interpolation=cv2.INTER_LINEAR)
plt.imshow(img_res), plt.show()


Reflexão (Espelhamento)#

A reflexão inverte a imagem em relação aos eixos. No OpenCV, utilizamos cv2.flip(img, flipCode).

Exemplo Prático:

img_flip = cv2.flip(img_rgb, 1) # 1 para horizontal
plt.imshow(img_flip), plt.show()


Transformação de Perspectiva#

A transformação de perspectiva permite simular mudanças no ponto de vista 3D, retificando planos inclinados. Requer uma matriz 3×3 calculada a partir de 4 pares de pontos correspondentes.

Exemplo Prático: Retificação de Documento

Baixar perspectiva_demo.png

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('perspectiva_demo.png')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 2. Definir os 4 cantos da moldura (Superior Esq, Dir, Inferior Esq, Dir)
pts_origem = np.float32([[360, 275], [1835, 180], [100, 1695], [2245, 1525]])

# 3. Definir saída plana (ex: 1000x600 para manter proporção)
largura, altura = 1000, 600
pts_destino = np.float32([[0, 0], [largura, 0], [0, altura], [largura, altura]])

M = cv2.getPerspectiveTransform(pts_origem, pts_destino)
img_retificada = cv2.warpPerspective(img_rgb, M, (largura, altura))

plt.figure(figsize=(15, 8))
plt.subplot(1, 2, 1), plt.imshow(img_rgb), plt.title('Original'), plt.axis('off')
plt.subplot(1, 2, 2), plt.imshow(img_retificada), plt.title('Retificada (Crop Ajustado)'), plt.axis('off')
plt.tight_layout(), plt.show()

Cisalhamento (Shearing)#

O cisalhamento cria uma distorção de inclinação proporcional à distância de um eixo.

Exemplo Prático:

M_shear = np.float32([[1, 0.3, 0], [0, 1, 0]])
img_shear = cv2.warpAffine(img_rgb, M_shear, (w + int(h*0.3), h))
plt.imshow(img_shear), plt.show()


Boas Práticas em Transformações Geométricas#

Para evitar a degradação da imagem por múltiplas interpolações, a prática recomendada é a composição de matrizes. Multiplicando as matrizes de cada etapa, obtemos uma única matriz final, garantindo que a imagem sofra apenas um processo de reamostragem, preservando a nitidez e otimizando o desempenho.

Ajuste de Gamma: Correção Não-Linear de Brilho#

O ajuste de gamma (ou correção de gamma) é uma técnica essencial em processamento de imagens utilizada para modificar o brilho e o contraste de forma não-linear. Diferente de uma simples adição aritmética, que desloca todos os tons de forma uniforme, a correção de gamma permite realçar detalhes em regiões escuras (sombras) sem causar o estiramento excessivo das áreas já claras, preservando melhor a fidelidade visual da cena.

Fundamentação Matemática#

A transformação de gamma é baseada em uma relação de potência entre os valores de entrada e saída. Para uma imagem com intensidades variando de 0 a 255, a equação que rege esse processo é:

\[ I_{ ext{out}} = 255 \times \left(\frac{I_{\text{in}}}{255}\right)^\gamma \]

Nesta fórmula, o valor do pixel de entrada \(I_{\text{in}}\) é primeiro normalizado para o intervalo \([0, 1]\), elevado à potência do parâmetro \(\gamma\) e, por fim, reescalonado para o intervalo original \([0, 255]\). A natureza dessa função potência é o que define o comportamento do ajuste:

  • Quando \(\gamma < 1\): A curva de mapeamento é côncava, o que resulta em um clareamento da imagem. As intensidades baixas (tons escuros) são expandidas, tornando as sombras mais visíveis.

  • Quando \(\gamma = 1\): A relação é linear e a imagem permanece inalterada.

  • Quando \(\gamma > 1\): A curva é convexa, resultando em um escurecimento da imagem e no aumento do contraste em áreas claras, sendo útil para reduzir o impacto de regiões superexpostas.

Essa técnica é fundamental não apenas para correção estética, mas também para compensar as características não-lineares de dispositivos de captura (sensores) e de exibição (monitores), garantindo que a percepção humana da imagem seja o mais próxima possível da realidade.

Exemplo Prático:

import cv2
import numpy as np
import matplotlib.pyplot as plt

# Carregar imagem (OpenCV lê em BGR)
img = cv2.imread('w3.png')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # Conversão para RGB

# Parâmetros de gamma
gamma_values = [0.4, 1.0, 2.2] # Exemplos para clarear, original e escurecer

# Configurar plot
plt.figure(figsize=(18, 6))

for i, gamma in enumerate(gamma_values, 1):
  # Aplicar correção de gamma
  corrected = np.power(img_rgb/255., gamma)
  corrected = (corrected * 255).astype(np.uint8)

  # Plotar resultados
  plt.subplot(1, 3, i)
  plt.imshow(corrected)
  plt.title(f'γ = {gamma}')
  plt.axis('off')

plt.tight_layout()
plt.show()

Esta abordagem proporciona um controle preciso sobre o realce de imagens, sendo essencial em aplicações como visão computacional, fotografia médica e processamento de vídeo.

Exercício Prático - Data Augmentation (Terminal)#

Objetivo: Criar um script de terminal que gera um dataset aumentado com variações de uma imagem usando transformações geométricas e ajustes de iluminação aleatórios.

Tarefas:

  • Carregue uma imagem de sua escolha

  • Gere 30 variações aplicando transformações aleatórias:

  • Rotação aleatória (entre -30° e +30°)

  • Escala aleatória (entre 0.8x e 1.2x)

  • Translação aleatória (até ±50 pixels)

  • Reflexão horizontal (50% de chance)

  • Ajuste de gamma aleatório (entre 0.7 e 1.3)

  • Salve todas as imagens em uma pasta dataset_aumentado/

  • Mostre no terminal: progresso, estatísticas e exemplos gerados

  • Ao final, crie uma grade visual mostrando 12 exemplos aleatórios

Dicas:

  • Use np.random para valores aleatórios

  • Combine múltiplas transformações em cada imagem

  • Use tqdm para barra de progresso

  • Salve com nomes descritivos: aug_001.png, aug_002.png, etc.


Exercício - Data Augmentation Interativo com Gradio#

Objetivo: Criar uma interface gráfica para visualizar e ajustar transformações de data augmentation em tempo real, gerando datasets personalizados.

Funcionalidades da interface:

Aba 1 - Visualização Individual:

  • Upload de imagem

  • Controles deslizantes para:

  • Rotação (-180° a 180°)

  • Escala (0.5x a 2.0x)

  • Translação X e Y (-100 a 100 pixels)

  • Gamma (0.3 a 3.0)

  • Checkbox para flip horizontal

  • Botão “Aplicar Transformação Aleatória”

  • Visualização lado a lado: Original | Transformada

Aba 2 - Geração de Dataset:

  • Upload de imagem

  • Slider: “Número de variações” (1-100)

  • Configurações de ranges aleatórios:

  • Rotação min/max

  • Escala min/max

  • Gamma min/max

  • Botão “Gerar Dataset”

  • Galeria mostrando todas as variações geradas

  • Botão “Download ZIP” com todas as imagens

Bibliotecas recomendadas:

Gradio (mais simples): https://gradio.app/docs/

Streamlit (mais flexível): https://docs.streamlit.io/

Referências e Conteúdo Extra#

Bibliografia#

[Sze22]

Richard Szeliski. Computer Vision: Algorithms and Applications. Springer, 2nd edition, 2022. The University of Washington. URL: https://szeliski.org/Book/.