{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Capítulo 1: Introdução à Visão Computacional"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"## 🟡⚫ Google Colab, Linux e Bibliotecas\n",
"\n",
"O **Google Colab** (Colaboratory) é uma ferramenta poderosa e acessível para tarefas de **visão computacional**. Neste material, exploraremos como essa plataforma pode facilitar o desenvolvimento de projetos nessa área, desde a configuração inicial até o uso de recursos avançados. Além disso, veremos por que o Google Colab se tornou essencial para pesquisadores, estudantes e entusiastas da visão computacional. \n",
"\n",
"### Por que usar o Google Colab? \n",
"\n",
"O Google Colab é uma ferramenta gratuita fornecida pelo Google que permite a execução de código **Python** diretamente no navegador. Baseado no **Jupyter Notebook**, ele é especialmente útil para tarefas que demandam alto poder computacional, como **aprendizado de máquina** e **visão computacional**. Seu ambiente integrado elimina a necessidade de configurações locais complexas, tornando-se uma opção prática e acessível para estudantes, pesquisadores e desenvolvedores. \n",
"\n",
"**Principais Vantagens para Visão Computacional** \n",
"\n",
"✅ **Gratuito e acessível** – Qualquer usuário com uma conta Google pode utilizá-lo, sem necessidade de infraestrutura própria. \n",
"✅ **Acesso a hardware avançado** – Uso gratuito de **GPUs** e **TPUs**, essenciais para o treinamento e inferência de modelos complexos. \n",
"✅ **Integração com o Google Drive** – Facilita o armazenamento e o acesso a grandes conjuntos de dados. \n",
"✅ **Colaboração em tempo real** – Permite que vários usuários editem simultaneamente o mesmo notebook, ideal para projetos em equipe. \n",
"✅ **Ambiente interativo** – Suporte a células de código e texto, proporcionando uma experiência eficiente para desenvolvimento, documentação e visualização de resultados. \n",
"\n",
"**Configurando um Notebook no Google Colab** \n",
"\n",
"- **Acessando o Colab** \n",
" - Acesse o [Google Colab](https://colab.research.google.com). \n",
" - Faça login com sua conta Google. \n",
"\n",
"- **Criando um novo notebook** \n",
" - Vá até **\"Arquivo\"** > **\"Novo Notebook\"**. \n",
" - Comece a escrever e executar código Python imediatamente. \n",
"\n",
"- **Configurando o hardware** \n",
" - Para utilizar **GPUs** ou **TPUs**, acesse **\"Ambiente de execução\"** > **\"Alterar tipo de ambiente de execução\"**. \n",
" - Em **\"Acelerador de hardware\"**, selecione **\"GPU\"** ou **\"TPU\"**, conforme necessário. \n",
"\n",
"- **Salvando seu trabalho** \n",
" - O notebook é salvo automaticamente no seu **Google Drive**. \n",
" - Para salvar manualmente ou criar uma cópia, vá em **\"Arquivo\"** e selecione a opção desejada.\n",
" \n",
"\n",
"### Gerenciando Bibliotecas com `pip` \n",
"\n",
"O `pip` (Python Package Installer) é o gerenciador de pacotes padrão do Python. Ele permite instalar, atualizar, remover e gerenciar versões de bibliotecas, facilitando o controle de dependências nos projetos. \n",
"\n",
"No Google Colab, todos os comandos do `pip` podem ser executados diretamente no ambiente usando `!pip` no início. \n",
"\n",
"\n",
"Instalando Bibliotecas \n",
"\n",
"Para projetos de **Visão Computacional**, algumas das bibliotecas mais utilizadas incluem: \n",
"\n",
"```python\n",
"# Instalação de bibliotecas essenciais\n",
"!pip install numpy opencv-python matplotlib scikit-image pillow\n",
"``` \n",
"\n",
"Se precisar instalar múltiplos pacotes ao mesmo tempo, basta separá-los por espaço. \n",
"\n",
"\n",
"**Listando Pacotes Instalados** \n",
"\n",
"Para verificar quais bibliotecas estão instaladas no ambiente e suas respectivas versões: \n",
"\n",
"```python\n",
"# Listar todos os pacotes instalados\n",
"!pip list\n",
"\n",
"# Mostrar informações detalhadas sobre um pacote específico (exemplo: OpenCV)\n",
"!pip show opencv-python\n",
"``` \n",
"\n",
"**Gerando e Utilizando um Arquivo de Requisitos** \n",
"\n",
"O comando `pip freeze` lista todos os pacotes instalados no formato `nome==versão`, útil para replicar ambientes. \n",
"\n",
"```python\n",
"# Gerar um arquivo requirements.txt com todas as dependências do ambiente\n",
"!pip freeze > requirements.txt\n",
"\n",
"# Instalar dependências a partir de um arquivo requirements.txt\n",
"!pip install -r requirements.txt\n",
"``` \n",
"\n",
"Isso permite compartilhar o ambiente com outros desenvolvedores ou restaurá-lo posteriormente. \n",
"\n",
"\n",
"**Verificando e Especificando Versões de Pacotes** \n",
"\n",
"Para garantir compatibilidade, você pode instalar versões específicas: \n",
"\n",
"```python\n",
"# Instalar uma versão exata (exemplo: numpy 1.21.0)\n",
"!pip install numpy==1.21.0\n",
"```\n",
"\n",
"**Caso queira listar as versões disponíveis de um pacote, utilize:** \n",
"\n",
"```python\n",
"# Listar versões disponíveis de um pacote usando PyPI\n",
"!pip index versions numpy\n",
"``` \n",
"\n",
"Caso o comando acima não funcione no seu ambiente, outra alternativa é verificar diretamente no [PyPI](https://pypi.org/project/numpy/#history). \n",
"\n",
"\n",
"**Atualizando Pacotes**\n",
"\n",
"Para manter suas bibliotecas sempre atualizadas: \n",
"\n",
"```python\n",
"# Atualizar um pacote específico\n",
"!pip install --upgrade numpy\n",
"```\n",
"\n",
"**Caso queira verificar quais pacotes estão desatualizados e atualizá-los: ** \n",
"\n",
"```python\n",
"# Listar pacotes desatualizados\n",
"!pip list --outdated\n",
"\n",
"# Atualizar todos os pacotes (use com cautela)\n",
"!pip list --outdated | cut -d ' ' -f 1 | xargs -n1 pip install -U\n",
"``` \n",
"\n",
"\n",
"**Desinstalando Pacotes** \n",
"\n",
"Se precisar remover um pacote do ambiente, use: \n",
"\n",
"```python\n",
"# Remover um pacote (exemplo: numpy)\n",
"!pip uninstall numpy -y\n",
"``` \n",
"O argumento `-y` confirma a remoção automaticamente. \n",
"\n",
"### O que cada biblioteca faz? \n",
"\n",
"- **NumPy** – Manipulação eficiente de arrays e matrizes, essencial para processamento numérico e imagens. \n",
"- **OpenCV** – Biblioteca poderosa para processamento de imagens e vídeos, incluindo filtros, transformações e detecção de objetos. \n",
"- **Matplotlib** – Ferramenta para visualização de gráficos e exibição de imagens processadas. \n",
"- **Scikit-Image** – Conjunto avançado de algoritmos para análise e manipulação de imagens. \n",
"- **Pillow** – Manipulação e conversão de imagens em diversos formatos. \n",
"\n",
"💡 **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. \n",
"\n",
"Aqui está uma versão aprimorada da tabela, incluindo melhorias na formatação, novas explicações e o uso do `nohup` para executar comandos em segundo plano: \n",
"\n",
"\n",
"### Comandos e Terminal Linux \n",
"\n",
"Como estamos trabalhando em uma máquina virtual Linux no Google Colab, podemos utilizar comandos do terminal para gerenciar arquivos, configurar o ambiente e realizar diversas tarefas administrativas. Isto também é útil para computadores rodando SO Linux. \n",
"\n",
"> 📌 **No Google Colab, os comandos do terminal devem ser precedidos por `!`.** \n",
"\n",
"**Exemplos de Comandos Úteis** \n",
"\n",
"| Comando | Descrição | Exemplo de Uso |\n",
"|------------------------------------|--------------------------------------------------|------------------------------|\n",
"| **Listar arquivos e diretórios** | Exibe arquivos e pastas no diretório atual | `ls` |\n",
"| **Listar arquivos com detalhes** | Mostra permissões, tamanho e data de modificação | `ls -l` |\n",
"| **Exibir estrutura de diretórios** | Mostra a árvore de diretórios e arquivos | `tree` |\n",
"| **Mudar de diretório** | Acessa um diretório específico | `cd /content/meu_diretorio` |\n",
"| **Criar um novo diretório** | Cria uma pasta | `mkdir novo_diretorio` |\n",
"| **Mover ou renomear** | Move ou renomeia um arquivo ou diretório | `mv arquivo.txt novo_diretorio/` |\n",
"| **Copiar um arquivo ou diretório** | Copia um arquivo ou pasta | `cp arquivo.txt copia_arquivo.txt` |\n",
"| **Remover um arquivo** | Deleta um arquivo específico | `rm arquivo.txt` |\n",
"| **Remover um diretório** | Exclui uma pasta e seu conteúdo | `rm -r pasta_a_remover` |\n",
"| **Verificar o caminho atual** | Exibe o caminho do diretório onde você está | `pwd` |\n",
"| **Verificar espaço em disco** | Mostra o uso do disco | `df -h` |\n",
"| **Verificar memória disponível** | Exibe o uso da RAM | `free -h` |\n",
"| **Exibir uso da CPU em tempo real**| Mostra os processos ativos e consumo de CPU | `top` |\n",
"| **Exibir processos em execução** | Lista processos rodando no sistema | `ps aux` |\n",
"| **Rodar um processo em segundo plano** | Executa um comando sem interrupção, mesmo após fechar o Colab | `nohup python meu_script.py &` |\n",
"| **Ver conteúdo de um arquivo** | Exibe o conteúdo de um arquivo de texto | `cat arquivo.txt` |\n",
"| **Buscar texto dentro de arquivos**| Procura por palavras ou padrões dentro de arquivos | `grep \"erro\" log.txt` |\n",
"| **Alterar permissões de um arquivo** | Modifica permissões de leitura, escrita e execução | `chmod +x script.sh` (torna executável) |\n",
"\n",
"Caso precise de mais comandos ou explicações, é só avisar! 🚀\n",
"\n",
"**Executando Comandos em Segundo Plano com `nohup`** \n",
"\n",
"Se você deseja rodar um script que pode demorar (como o treinamento de um modelo de Visão Computacional) sem que ele seja interrompido ao fechar a aba do Colab, use `nohup`: \n",
"\n",
"```bash\n",
"!nohup python meu_script.py > saida.log 2>&1 &\n",
"```\n",
"\n",
"🔹 **Explicação:** \n",
"- `nohup` impede que o processo seja encerrado ao fechar o terminal. \n",
"- `> saida.log` salva a saída do programa no arquivo `saida.log`. \n",
"- `2>&1` redireciona mensagens de erro para o mesmo arquivo de saída. \n",
"- `&` executa o processo em segundo plano. \n",
"\n",
"💡 Para acompanhar a execução, visualize a saída do log com: \n",
"\n",
"```bash\n",
"!tail -f saida.log\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Ambientes Virtuais em Python \n",
"\n",
"**Para que servem?** \n",
"✔ Isolam dependências por projeto \n",
"✔ Evitam conflitos de versões \n",
"✔ Mantêm o Python global limpo \n",
"\n",
"**🐍 Principais Gerenciadores** \n",
"\n",
"- **venv** (Padrão Python) [Projeto](https://docs.python.org/3/library/venv.html) \n",
"```bash\n",
"python -m venv env # Cria\n",
"source env/bin/activate # Ativa (Linux/Mac) | env\\Scripts\\activate (Win)\n",
"deactivate # Desativa\n",
"```\n",
"\n",
"- **conda** (Anaconda/Miniconda) [Projeto](https://docs.conda.io/en/latest/) \n",
"```bash\n",
"conda create -n env python=3.9 # Cria\n",
"conda activate env # Ativa\n",
"conda deactivate # Desativa\n",
"```\n",
"\n",
"- **virtualenv** (Alternativa) [Projeto](https://virtualenv.pypa.io/en/latest/) \n",
"```bash\n",
"pip install virtualenv # Instala\n",
"virtualenv env # Cria\n",
"source env/bin/activate # Ativa (Linux/Mac) | env\\Scripts\\activate (Win)\n",
"deactivate # Desativa\n",
"```\n",
"\n",
"- **pipenv** (Pip + Virtualenv) [Projeto](https://pipenv.pypa.io/en/latest/) \n",
"```bash\n",
"pip install pipenv # Instala\n",
"pipenv install # Cria e instala pacotes\n",
"pipenv shell # Ativa\n",
"exit # Desativa\n",
"```\n",
"\n",
"- **Poetry** (Moderno) [Projeto](https://python-poetry.org/docs/) \n",
"```bash\n",
"pip install poetry # Instala\n",
"poetry new projeto # Cria projeto\n",
"poetry shell # Ativa\n",
"exit # Desativa\n",
"```\n",
"\n",
"**Exemplo de Fluxo de Trabalho Típico com o Conda** \n",
"```bash\n",
"# Criar ambiente com Python 3.10 e pacotes básicos\n",
"conda create -n meu_projeto python=3.10 numpy pandas -y\n",
"\n",
"# Ativar o ambiente\n",
"conda activate meu_projeto\n",
"\n",
"# Instalar pacotes adicionais (usando conda ou pip)\n",
"conda install matplotlib scikit-learn # via conda\n",
"pip install opencv-python # via pip se não disponível no conda\n",
"\n",
"# Listar pacotes instalados\n",
"conda list\n",
"\n",
"# Exportar configuração do ambiente\n",
"conda env export > environment.yml\n",
"\n",
"# Desativar o ambiente\n",
"conda deactivate\n",
"\n",
"# --- Para recriar o ambiente em outra máquina ---\n",
"conda env create -f environment.yml\n",
"conda activate meu_projeto\n",
"``` "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 📂 EXERCÍCIO: Manipulação de Arquivos no Terminal Linux (Google Colab)\n",
"\n",
"INSTRUÇÕES:\n",
"- Execute cada comando na ordem apresentada.\n",
"- Complete os trechos indicados com \"???\", inserindo os comandos corretos.\n",
"\n",
"Sobre a Exclamação (!) no Google Colab:\n",
"\n",
"No Google Colab, comandos de terminal Linux podem ser executados diretamente nas células do notebook usando **\"!\" (exclamação) antes do comando**. Isso permite rodar comandos como `ls`, `mkdir`, `mv` e muitos outros, como se estivéssemos em um terminal Linux comum. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```bash\n",
"# Baixando uma imagem da internet com o comando wget\n",
"!wget -O minha_imagem.jpg https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/800px-Cat03.jpg\n",
"\n",
"# Desafio: Liste os arquivos no diretório atual para garantir que a imagem foi baixada\n",
"!???\n",
"```\n",
"\n",
"```bash\n",
"# Exibir o caminho do diretório atual\n",
"!pwd \n",
"\n",
"# Listar os arquivos e diretórios presentes no ambiente de trabalho\n",
"!ls -lh\n",
"```\n",
"\n",
"```bash\n",
"# Criar um diretório chamado \"imagens\"\n",
"!mkdir imagens\n",
"\n",
"# Desafio: Mover a imagem baixada para dentro do diretório recém-criado\n",
"!mv ??? imagens/\n",
"\n",
"# Verificar os arquivos dentro da pasta \"imagens\" para garantir que a imagem foi movida\n",
"!ls -lh imagens/\n",
"```\n",
"\n",
"```bash\n",
"# Criar uma cópia da imagem dentro do mesmo diretório\n",
"!cp imagens/minha_imagem.jpg imagens/minha_imagem_backup.jpg\n",
"\n",
"# Desafio: Renomear a cópia da imagem para \"foto_gato.jpg\"\n",
"!mv ??? imagens/foto_gato.jpg\n",
"\n",
"# Verificar os arquivos na pasta para confirmar as mudanças\n",
"!ls -lh imagens/\n",
"```\n",
"\n",
"```bash\n",
"# Desafio: Remover a imagem \"foto_gato.jpg\" da pasta \"imagens\"\n",
"!rm ???\n",
"\n",
"# Excluir a pasta inteira e seu conteúdo (CUIDADO!)\n",
"!rm -r imagens\n",
"\n",
"# Desafio: Listar novamente os arquivos no diretório atual para garantir que a pasta foi removida\n",
"!???\n",
"```\n",
"\n",
"```python\n",
"# Conectar o Colab ao Google Drive (Execute apenas uma vez)\n",
"from google.colab import drive\n",
"drive.mount('/content/drive')\n",
"```\n",
"\n",
"```bash\n",
"# Criar um diretório no Google Drive\n",
"!mkdir /content/drive/MyDrive/meus_arquivos_colab\n",
"\n",
"# Desafio: Mover um arquivo para o Google Drive\n",
"!mv ??? /content/drive/MyDrive/meus_arquivos_colab/\n",
"\n",
"# Verificar se o arquivo foi salvo corretamente\n",
"!ls -lh /content/drive/MyDrive/meus_arquivos_colab/\n",
"```\n",
"\n",
"```bash\n",
"# Criar um script de teste que roda infinitamente\n",
"!echo \"while true; do echo 'Rodando...'; sleep 5; done\" > processo.sh\n",
"\n",
"# Executar o script em segundo plano com 'nohup'\n",
"!nohup bash processo.sh > saida.log 2>&1 &\n",
"\n",
"# Desafio: Visualizar o log gerado em tempo real\n",
"!tail -f ???\n",
"\n",
"# Desafio final: Encontrar o PID do processo e finalizá-lo\n",
"!ps aux | grep ???\n",
"!kill -9 ???\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"🧐 Reflexão Final \n",
"\n",
"Agora que você completou o exercício, **pare e reflita** sobre cada comando executado. \n",
"\n",
"- Você conseguiu compreender o que cada um deles faz? \n",
"- Foi capaz de resolver os desafios sem olhar diretamente a resposta? \n",
"- Caso tenha encontrado dificuldades, onde exatamente ocorreu a dúvida? \n",
"\n",
"Tente **explicar detalhadamente** cada comando e processo para um colega ou para você mesmo. Se puder ensinar alguém, significa que realmente compreendeu. \n",
"\n",
"Por fim, esteja preparado para **apresentar e justificar** suas respostas ao professor. Se houver algo que não entendeu, volte, revise e tente mais uma vez!\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 👁️ Conceitos de Visão Computacional \n",
"\n",
"A **Visão Computacional** é um ramo da Ciência da Computação que permite que máquinas processem e interpretem imagens e vídeos, replicando a percepção visual humana. Através de algoritmos avançados, os computadores analisam padrões visuais e extraem informações úteis para diversas aplicações. \n",
"\n",
"Uma das áreas mais conhecidas é o **reconhecimento facial**, que identifica indivíduos com base em características únicas do rosto. Essa tecnologia está presente em sistemas de segurança, redes sociais e autenticação biométrica. Outra aplicação essencial é a **detecção de objetos**, utilizada em veículos autônomos e sistemas de vigilância para identificar pedestres, placas e obstáculos em tempo real. \n",
"\n",
"Na área da saúde, a Visão Computacional auxilia na **análise de imagens médicas**, tornando exames como radiografias e tomografias mais precisos na detecção de doenças. Em segurança, sistemas inteligentes são capazes de monitorar ambientes, reconhecendo atividades suspeitas e alertando operadores sobre possíveis ameaças. Já na **realidade aumentada (AR)**, a tecnologia permite sobrepor elementos virtuais ao mundo real, criando experiências imersivas para jogos, treinamentos e comércio. \n",
"\n",
"### Imagem Digital: Pixels, Resolução e Canais de Cor \n",
"\n",
"Toda imagem digital é composta por **pixels**, que são as menores unidades visuais. A quantidade total de pixels determina a **resolução**, expressa como largura × altura (exemplo: 1920×1080), influenciando diretamente a qualidade da imagem. \n",
"\n",
"As cores são representadas por diferentes canais. No modelo **RGB (Red, Green, Blue)**, cada pixel combina três intensidades de cor para formar uma ampla variedade de tons. Essa estrutura permite que computadores processem imagens de forma eficiente e reproduzam cores com fidelidade. \n",
"\n",
"### Tipos de Imagens \n",
"\n",
"As imagens digitais podem ter diferentes representações, dependendo da aplicação. No modelo **RGB**, cada pixel possui três valores (vermelho, verde e azul), sendo o formato mais comum para fotografias e vídeos. Já as imagens em **escala de cinza** utilizam apenas tons de preto ao branco, sendo amplamente empregadas em exames médicos e reconhecimento de padrões. Por fim, as **imagens binárias** contêm apenas dois valores (preto e branco) e são usadas em segmentação de objetos e reconhecimento óptico de caracteres (OCR).\n",
"\n",
"Veja a Ilustração a seguir sobre uma imagem digital. \n",
"\n",
"\n",
"\n",
"A seguir, nosso primeiro exemplo prático sobre representação de imagens em diferentes formatos. \n",
"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**. \n",
"\n",
"O código abaixo demonstra esse processo visualmente."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABJYAAAGXCAYAAADh89pxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAmn0lEQVR4nO3deXSV9Z0/8E8IkAQiiCwKaBEBpQouB0en7KjUBUVUYEBRcMV9RouKo6KiSF1wZKxYHRSmQrSDICpTd+zUdWwVcRspSmgteNhkcaMKeX5/8MstlwQJX1HUvl7n5BzybPf7PDfc5L7v5/N9CrIsywIAAAAAtlKt7T0AAAAAAL6fBEsAAAAAJBEsAQAAAJBEsAQAAABAEsESAAAAAEkESwAAAAAkESwBAAAAkESwBAAAAEASwRIAAABsIxMnToy77757ew8DvjWCJQDgO2/YsGGx++67b/PjnnvuudG7d+9tftxvw+677x7Dhg3b3sPYooULF0ZBQUFMnjx5q/d9/PHHo7S0NJYtW7btBwawkYKCgrjmmmu+9nF+/etfx0UXXRT/8A//sFX7fV9e06E6giW+1yZPnhwFBQW5r9q1a0fLli1j2LBhsWjRomr3efTRR+OYY46JnXfeOerWrRs77bRTdO/ePcaNGxdr1qzJ23b33XfPO35xcXG0a9cuLrnkkvjoo4++jVME/s6Vl5fH+eefH3vuuWfUq1cv6tWrF3vvvXecd9558cYbb2zv4X2vlZeXx8SJE+Nf//Vfq6xbs2ZNXHvttbHffvtFaWlplJSURIcOHeKyyy6LxYsXb4fRbr0ZM2ZEQUFBTJw4cbPbPPXUU1FQUBD//u///i2OrOaOOOKIaNu2bYwdO3Z7DwX4Htr0vUJBQUE0a9YsevXqFY899tg2f7z3338/zj333Jg2bVoccMAB2/z48F1Ve3sPALaF0aNHR+vWrWPt2rXx8ssvx+TJk+P555+Pt956K4qLiyMioqKiIk4//fSYPHlydOzYMc4999zYbbfd4uOPP46XXnoprrzyyvjNb34TzzzzTN6x999///jZz34WERFr166NV199NW677bb4n//5n3jllVe+9XMF/n7MmjUr/umf/ilq164dJ510Uuy3335Rq1atePfdd2PGjBlx5513Rnl5ebRq1Wp7D/V7afz48dG6devo1atX3vIFCxbEYYcdFn/+859jwIABcdZZZ0XdunXjjTfeiHvuuSceeuih+OMf/7idRl1zffr0iYYNG0ZZWVmcccYZ1W5TVlYWhYWFMWjQoG95dDU3fPjwGDFiRFx77bWxww47bO/hAN9Dle8VsiyLJUuWxOTJk+Ooo46KRx99NI4++uiIiPj888+jdu2v9/Z47ty5MWnSpDjiiCO2et958+ZFrVrqPvh+Eizxg3DkkUfGgQceGBERZ5xxRjRp0iRuvPHGeOSRR2LgwIEREXHTTTfF5MmT46KLLopx48ZFQUFBbv9//ud/jg8//DB+9atfVTl2y5YtY8iQIbnvzzjjjCgtLY1bbrkl5s+fH+3atfuGzw74e/T+++/HoEGDolWrVvHMM89E8+bN89bfeOONMWHChC3+Efrpp59G/fr1v8mhfi99+eWXMXXq1Dj77LPzlq9bty6OP/74WLJkSfz2t7+Nrl275q0fM2ZM3Hjjjd/mUJMVFRVF//79Y9KkSbF48eJo0aJF3vq1a9fGQw89FL17945mzZp9rcf67LPPol69el/rGJtzwgknxAUXXBDTpk2L00477Rt5DOCHbeP3ChERp59+euy8885x//3354Klyg+jv47jjz9+q7bPsizWrl0bJSUlUVRU9LUfH7YXkSg/SN26dYuIDW/MIjb8wXvjjTfGPvvsEzfffHNeqFSpefPmcdlll9Xo+LvssktExNf+VANgc2666ab49NNPY9KkSVVCpYgNrz8XXnhh7Lbbbrllw4YNi9LS0nj//ffjqKOOih122CFOOumkiIh47rnnYsCAAfGjH/0oioqKYrfddouLLrooPv/889z+kyZNioKCgpgzZ06Vx7vhhhuisLAw12Y8f/78OOGEE2KXXXaJ4uLi2HXXXWPQoEGxevXqvP2mTJkSBx10UNSrVy8aNWoU3bt3jyeffDK3/uGHH44+ffpEixYtoqioKNq0aRPXXXddrF+/fovXqKKiIm677bbYZ599ori4OHbeeecYPnx4rFy5cov7Pv/887F8+fI47LDD8pZPnz495s6dG1dccUWVUCkiokGDBjFmzJjc9zW5rhF/e24WLVoU/fr1i9LS0mjatGmMGDGiyrnecsst0blz52jcuHGUlJREp06d4sEHH9ziOVVnyJAhUVFREQ888ECVdf/93/8dq1evzv2MRGx4vjp16hQlJSWx0047xaBBg+KDDz7I269nz57RoUOHePXVV6N79+5Rr169XDvhqlWrYtiwYdGwYcPYcccdY+jQobFq1apqx/buu+9G//79Y6eddori4uI48MAD45FHHqmyXbNmzWLfffeNhx9+OOkaAGxqxx13jJKSkry/5TedY+maa66JgoKCeO+992LYsGGx4447RsOGDePUU0+Nzz77LO94kyZNikMOOSSaNWsWRUVFsffee8edd95Z5XF33333OProo+OJJ56IAw88MEpKSuKuu+7Krdt4jqWPPvooRowYER07dozS0tJo0KBBHHnkkTF37txtezFgG/CumB+khQsXRkREo0aNImLDG4hVq1bFiBEjorCwcKuO9eWXX8by5csjYsOnu3PmzIlbb701unfvHq1bt96m4waoNGvWrGjbtm0cfPDBW7XfunXr4vDDD4+uXbvGLbfckqsimTZtWnz22WdxzjnnROPGjeOVV16J22+/Pf7yl7/EtGnTIiKif//+cd5558XUqVOrzA0xderU6NmzZ7Rs2TK++OKLOPzww+Ovf/1rXHDBBbHLLrvEokWLYtasWbFq1apo2LBhRERce+21cc0110Tnzp1j9OjRUbdu3fjf//3fmD17dvz0pz+NiA3zX5SWlsbFF18cpaWlMXv27Bg1alSsWbMmbr755q881+HDh8fkyZPj1FNPjQsvvDDKy8vjF7/4RcyZMydeeOGFqFOnzmb3ffHFF6OgoKDKeVYGGyeffHKNrndNrmul9evXx+GHHx4HH3xw3HLLLfH000/HuHHjok2bNnHOOefkths/fnz07ds3TjrppPjiiy/igQceiAEDBsSsWbOiT58+NRpXpe7du8euu+4aZWVlcfHFF+etKysri3r16kW/fv0iYkM11lVXXRUDBw6MM844I5YtWxa33357dO/ePebMmRM77rhjbt8VK1bEkUceGYMGDYohQ4bEzjvvHFmWxbHHHhvPP/98nH322fHjH/84HnrooRg6dGiVcb399tvRpUuXaNmyZYwcOTLq168f//Vf/xX9+vWL6dOnx3HHHZe3fadOnWLmzJlbde4AlVavXh3Lly+PLMti6dKlcfvtt8cnn3yS15WwOQMHDozWrVvH2LFj47XXXouJEydGs2bN8qpXJ0yYEB06dIi+fftG7dq14+GHH45zzz03Kioq4rzzzss73rx582Lw4MExfPjw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C7/2HdF+9KMfRbdu3eLXv/51TJkyJVq3bh2dO3fOrT/++OOjsLAwrr322io/W1mWVZl/sDpHHXVUrFu3Lu8W2+vXr4/bb789b7tmzZpFz54946677ooPP/ywynGWLVtWZdmrr74aP/nJT7Y4BoCa+PLLL+PJJ5+MunXrxo9//OOvdazKFt0vv/wyt2zlypVx7733fq3jFhYWVnk9njZtWpU56OC7QMUSP1iXXHJJDBgwICZPnhxnn312jBw5Mv7v//4vbr755njyySfjhBNOiF133TVWrlwZr732WkybNi2aNWsWxcXFecdZtGhRTJkyJSI2vOGZO3du3HXXXdGkSRNtcMA3pl27dlFWVhaDBw+OvfbaK0466aTYb7/9IsuyKC8vj7KysqhVq1aN2s7at28fbdq0iREjRsSiRYuiQYMGMX369GpbwSqdcsopMWLEiIiIKm1ws2fPjvPPPz8GDBgQe+65Z6xbty7uu+++KCwszE3w3bZt27jiiiviuuuui27dusXxxx8fRUVF8fvf/z5atGgRY8eOjc6dO0ejRo1i6NChceGFF0ZBQUHcd999NQrNevToEcOHD4+xY8fG66+/Hj/96U+jTp06MX/+/Jg2bVqMHz8++vfvv9n9u3btGo0bN46nn346DjnkkNzyOnXqxIwZM+Kwww6L7t27x8CBA6NLly5Rp06dePvtt6OsrCwaNWoUY8aMSbquW9KnT5+49dZb44gjjogTTzwxli5dGnfccUe0bds23njjjeTjRmx4Hs8666xYvHhxLhyr1KZNm7j++uvj8ssvj4ULF0a/fv1ihx12iPLy8njooYfirLPOyv08bM4xxxwTXbp0iZEjR8bChQtj7733jhkzZlQ7V9Ydd9wRXbt2jY4dO8aZZ54Ze+yxRyxZsiReeuml+Mtf/hJz587Nbbt06dJ44403qtyyG6CmHnvssdwNIZYuXRplZWUxf/78GDlyZG6epFS9e/eOOnXqRN++fWP48OHx8ccfx9133x0tWrSo9oOhmjr66KNj9OjRceqpp0bnzp3jzTffjKlTp+aqPuE75du7AR1se5W3EP39739fZd369euzNm3aZG3atMnWrVuXW/7QQw9lRx11VNa0adOsdu3a2Y477ph17do1u/nmm7NVq1blHaPylqCVX7Vq1cqaNWuWDR48OHvvvfe+8fMDeO+997Jzzjkna9u2bVZcXJyVlJRk7du3z84+++zs9ddfz9t26NChWf369as9zjvvvJMddthhWWlpadakSZPszDPPzObOnVvlNvCVPvzww6ywsDDbc889q6xbsGBBdtppp2Vt2rTJiouLs5122inr1atX9vTTT1fZ9t57780OOOCArKioKGvUqFHWo0eP7Kmnnsqtf+GFF7J//Md/zEpKSrIWLVpkl156afbEE09kEZE9++yzeefWqlWrKse/++67s06dOmUlJSXZDjvskHXs2DG79NJLs8WLF2/miv7NhRdemLVt27badStXrsxGjRqVdezYMatXr15WXFycdejQIbv88suzDz/8MLddTa/r5p6byttZb+yee+7J2rVrlxUVFWXt27fPJk2aVO12m96aeks++uijrKioKIuI7J133ql2m+nTp2ddu3bN6tevn9WvXz9r3759dt5552Xz5s3LbdOjR49sn332qXb/FStWZCeffHLWoEGDrGHDhtnJJ5+czZkzp9qfs/fffz875ZRTsl122SWrU6dO1rJly+zoo4/OHnzwwbzt7rzzzqxevXrZmjVranyuAFn2t/cKG38VFxdn+++/f3bnnXdmFRUVuW0jIrv66qtz31e+7i5btqzaY5aXl+eWzZw5M+vYsWNWXFyc7bHHHtm4ceOye++9t8p2rVq1yvr06VPtWDd9TV+7dm32s5/9LGvevHlWUlKSdenSJXvppZeyHj16ZD169Pg6lwW2uYIs24paegDg78Ly5cujefPmMWrUqM3euev7bsGCBdG+fft47LHHcjdl4LvngAMOiJ49e8a//du/be+hAADVMMcSAFDF5MmTY/369XHyySdv76F8Y/bYY484/fTT4+c///n2Hgqb8fjjj8f8+fO3OJE6ALD9qFgCAHJmz54d77zzTlx11VXRq1evmDFjxvYeEgAA32GCJQAgp2fPnvHiiy9Gly5dYsqUKXm3TgYAgE0JlgAAAABIYo4lAAAAAJIIlgAAAABIIlgCAAAAIEntmm5YUBARkUUU/P8pmQqyv31t/P3m/v1tbvddHJNr8f3Z7rs4Jtfi+7Pdd2JMEVn8/++/ZQUbfllEQUFBjf+dum5bH6NSrVq18tZtunzTZdv7GF+1z5bOsybXaXNj3JpruLkxbenYNT3/1HHU9By/ar+tGdPW/EymjmlbP//b4v/Qthi7a13za721x9getvfjwzfh2muvjVGjRm3vYcB2oWIJAAAAgCQ1r1j6JkcBAAAAwPeOiiUAAAAAkqhYAgAAACCJYAkAAACAJIIlAAAAAJKYYwkAAACAJCqWAAAAAEgiWAIAAAAgiVY4AAAAAJKoWAIAAAAgiWAJAAAAgCRa4QAAAABIomIJAAAAgCSCJQAAAACSCJYAAAAASGKOJQAAAACSqFgCAAAAIIlgCQAAAIAkWuEAAAAASKJiCQAAAIAkgiUAAAAAkmiFAwAAACCJiiUAAAAAkgiWAAAAAEgiWAIAAAAgiTmWAAAAAEiiYgkAAACAJIIlAAAAAJJohQMAAAAgiYolAAAAAJIIlgAAAABIohUOAAAAgCQqlgAAAABIIlgCAAAAIIlgCQAAAIAk5lgCAAAAIImKJQAAAACSCJYAAAAASKIVDgAAAIAkKpYAAAAASCJYAgAAACCJYAkAAACAJOZYAgAAACCJiiUAAAAAkgiWAAAAAEiiFQ4AAACAJCqWAAAAAEgiWAIAAAAgiVY4AAAAAJKoWAIAAAAgiWAJAAAAgCSCJQAAAACSmGMJAAAAgCQqlgAAAABIIlgCAAAAIIlWOAAAAACSqFgCAAAAIIlgCQAAAIAkWuEAAAAASKJiCQAAAIAkgiUAAAAAkgiWAAAAAEhijiUAAAAAkqhYAgAAACCJYAkAAACAJFrhAAAAAEiiYgkAAACAJIIlAAAAAJJohQMAAAAgiYolAAAAAJIIlgAAAABIIlgCAAAAIIk5lgAAAABIomIJAAAAgCSCJQAAAACSaIUDAAAAIImKJQAAAACSCJYAAAAASKIVDgAAAIAkKpYAAAAASCJYAgAAACCJYAkAAACAJOZYAgAAACCJiiUAAAAAkgiWAAAAAEiiFQ4AAACAJCqWAAAAAEgiWAIAAAAgiVY4AAAAAJKoWAIAAAAgiWAJAAAAgCSCJQAAAACSmGMJAAAAgCQqlgAAAABIIlgCAAAAIIlWOAAAAACSqFgCAAAAIIlgCQAAAIAkWuEAAAAASKJiCQAAAIAkgiUAAAAAkgiWAAAAAEhijiUAAAAAkqhYAgAAACCJYAkAAACAJFrhAAAAAEiiYgkAAACAJIIlAAAAAJIIlgAAAABIYo4lAAAAAJKoWAIAAAAgiWAJAAAAgCRa4QAAAABIomIJAAAAgCSCJQAAAACSaIUDAAAAIImKJQAAAACSCJYAAAAASCJYAgAAACCJOZYAAAAASKJiCQAAAIAkgiUAAAAAkmiFAwAAACCJiiUAAAAAkgiWAAAAAEiiFQ4AAACAJCqWAAAAAEgiWAIAAAAgiWAJAAAAgCTmWAIAAAAgiYolAAAAAJIIlgAAAABIohUOAAAAgCQqlgAAAABIIlgCAAAAIIlWOAAAAACSqFgCAAAAIIlgCQAAAIAkgiUAAAAAkphjCQAAAIAkKpYAAAAASCJYAgAAACCJVjgAAAAAkqhYAgAAACCJYAkAAACAJFrhAAAAAEiiYgkAAACAJIIlAAAAAJIIlgAAAABIYo4lAAAAAJKoWAIAAAAgiWAJAAAAgCRa4QAAAABIomIJAAAAgCSCJQAAAACSaIUDAAAAIImKJQAAAACSCJYAAAAASCJYAgAAACCJOZYAAAAASKJiCQAAAIAkgiUAAAAAkmiFAwAAACCJiiUAAAAAkgiWAAAAAEiiFQ4AAACAJCqWAAAAAEgiWAIAAAAgiWAJAAAAgCTmWAIAAAAgiYolAAAAAJIIlgAAAABIohUOAAAAgCQqlgAAAABIIlgCAAAAIIlWOAAAAACSqFgCAAAAIIlgCQAAAIAkgiUAAAAAkphjCQAAAIAkKpYAAAAASCJYAgAAACCJVjgAAAAAkqhYAgAAACCJYAkAAACAJIIlAAAAAJKYYwkAAACAJCqWAAAAAEgiWAIAAAAgiVY4AAAAAJKoWAIAAAAgiWAJAAAAgCRa4QAAAABIomIJAAAAgCSCJQAAAACSCJYAAAAASGKOJQAAAACSqFgCAAAAIIlgCQAAAIAkWuEAAAAASKJiCQAAAIAkgiUAAAAAkmiFAwAAACCJiiUAAAAAkgiWAAAAAEgiWAIAAAAgiTmWAAAAAEiiYgkAAACAJIIlAAAAAJJohQMAAAAgiYolAAAAAJIIlgAAAABIohUOAAAAgCQqlgAAAABIIlgCAAAAIIlgCQAAAIAk5lgCAAAAIImKJQAAAACSCJYAAAAASKIVDgAAAIAkKpYAAAAASCJYAgAAACCJVjgAAAAAkqhYAgAAACCJYAkAAACAJIIlAAAAAJKYYwkAAACAJCqWAAAAAEgiWAIAAAAgiVY4AAAAAJKoWAIAAAAgiWAJAAAAgCRa4QAAAABIomIJAAAAgCSCJQAAAACSCJYAAAAASGKOJQAAAACSqFgCAAAAIIlgCQAAAIAkWuEAAAAASKJiCQAAAIAkgiUAAAAAkmiFAwAAACCJiiUAAAAAkgiWAAAAAEgiWAIAAAAgiTmWAAAAAEiiYgkAAACAJIIlAAAAAJJohQMAAAAgiYolAAAAAJIIlgAAAABIIlgCAAAAIIk5lgAAAABIomIJAAAAgCSCJQAAAACSaIUDAAAAIImKJQAAAACSCJYAAAAASKIVDgAAAIAkKpYAAAAASCJYAgAAACCJYAkAAACAJOZYAgAAACCJiiUAAAAAkgiWAAAAAEiiFQ4AAACAJCqWAAAAAEgiWAIAAAAgiVY4AAAAAJKoWAIAAAAgiWAJAAAAgCSCJQAAAACSmGMJAAAAgCQqlgAAAABIIlgCAAAAIIlWOAAAAACSqFgCAAAAIIlgCQAAAIAkWuEAAAAASKJiCQAAAIAkgiUAAAAAkgiWAAAAAEhijiUAAAAAkqhYAgAAACCJYAkAAACAJFrhAAAAAEiiYgkAAACAJIIlAAAAAJJohQMAAAAgiYolAAAAAJIIlgAAAABIIlgCAAAAIIk5lgAAAABIomIJAAAAgCSCJQAAAACSaIUDAAAAIImKJQAAAACSCJYAAAAASKIVDgAAAIAkKpYAAAAASCJYAgAAACCJYAkAAACAJOZYAgAAACCJiiUAAAAAkgiWAAAAAEiiFQ4AAACAJCqWAAAAAEgiWAIAAAAgiVY4AAAAAJKoWAIAAAAgiWAJAAAAgCSCJQAAAACSmGMJAAAAgCQqlgAAAABIIlgCAAAAIIlWOAAAAACSqFgCAAAAIIlgCQAAAIAkWuEAAAAASKJiCQAAAIAkgiUAAAAAkgiWAAAAAEhijiUAAAAAkqhYAgAAACCJYAkAAACAJFrhAAAAAEiiYgkAAACAJIIlAAAAAJIIlgAAAABIYo4lAAAAAJKoWAIAAAAgiWAJAAAAgCRa4QAAAABIomIJAAAAgCSCJQAAAACSaIUDAAAAIImKJQAAAACSCJYAAAAASCJYAgAAACCJOZYAAAAASKJiCQAAAIAkgiUAAAAAkmiFAwAAACCJiiUAAAAAkgiWAAAAAEiiFQ4AAACAJCqWAAAAAEgiWAIAAAAgiWAJAAAAgCTmWAIAAAAgiYolAAAAAJIIlgAAAABIohUOAAAAgCQqlgAAAABIIlgCAAAAIIlWOAAAAACSqFgCAAAAIIlgCQAAAIAkgiUAAAAAkphjCQAAAIAkKpYAAAAASCJYAgAAACBJQZZl2fYeBAAAAADfP+ZYAgAAACCJYAkAAACAJIIlAAAAAJIIlgAAAABIIlgCAAAAIIlgCQAAAIAkgiUAAAAAkgiWAAAAAEgiWAIAAAAgyf8DlXX3YHF1kb4AAAAASUVORK5CYII=",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import warnings\n",
"\n",
"# Ignorar avisos do Matplotlib para evitar mensagens desnecessárias\n",
"warnings.filterwarnings(\"ignore\", category=UserWarning, module=\"matplotlib\")\n",
"\n",
"# Criar uma imagem RGB com gradientes suaves\n",
"img_rgb = np.zeros((256, 256, 3), dtype=np.uint8) # Inicializa uma matriz de zeros (imagem preta) com 3 canais de cor (RGB)\n",
"\n",
"# Preenchendo os canais de cor com gradientes:\n",
"img_rgb[:, :, 0] = np.linspace(0, 255, 256, dtype=np.uint8).reshape((256, 1)) # Canal Vermelho (R): varia verticalmente\n",
"img_rgb[:, :, 1] = np.linspace(0, 255, 256, dtype=np.uint8).reshape((1, 256)) # Canal Verde (G): varia horizontalmente\n",
"img_rgb[:, :, 2] = np.flip(img_rgb[:, :, 1], axis=1) # Canal Azul (B): inverso do verde, criando um gradiente oposto\n",
"\n",
"# Converter a imagem para tons de cinza usando apenas o canal verde\n",
"img_grayscale = img_rgb[:, :, 1] # Mantemos apenas os valores do canal verde\n",
"\n",
"# Criar uma imagem binária (thresholding) com limiar de 127\n",
"img_binary = (img_grayscale > 127).astype(np.uint8) * 255 # Pixels acima do limiar tornam-se brancos (255), os demais pretos (0)\n",
"\n",
"# Criar a figura com 3 subplots para exibir as diferentes representações da imagem\n",
"fig, axes = plt.subplots(1, 3, figsize=(12, 4)) # Cria um layout de 1 linha e 3 colunas\n",
"\n",
"# Definição dos títulos e das imagens a serem exibidas\n",
"titles = [\"RGB\", \"Grayscale (Canal Verde)\", \"Binária\"]\n",
"images = [img_rgb, img_grayscale, img_binary]\n",
"cmaps = [None, \"gray\", \"gray\"] # Definição do colormap para cada imagem (apenas grayscale e binária precisam de escala cinza)\n",
"\n",
"# Loop para exibir as imagens nos subplots\n",
"for ax, img, title, cmap in zip(axes, images, titles, cmaps):\n",
" ax.imshow(img, cmap=cmap) # Mostra a imagem\n",
" ax.set_title(title) # Define o título da imagem\n",
" ax.axis(\"off\") # Remove os eixos para melhor visualização\n",
"\n",
"# Ajusta o espaçamento entre os gráficos para melhor exibição\n",
"plt.tight_layout()\n",
"plt.show() # Exibe as imagens\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Processamento Digital de Imagens\n",
"\n",
"### O que é Processamento Digital de Imagens?\n",
"\n",
"O **processamento digital de imagens** envolve a aplicação de algoritmos computacionais para modificar, aprimorar ou extrair informações de imagens digitais. Essa área tem um papel fundamental em diversas aplicações, como medicina, segurança, automação e inteligência artificial. As imagens processadas podem ser adquiridas por câmeras digitais, scanners ou sensores especializados, como aqueles usados em exames médicos e sistemas de vigilância.\n",
"\n",
"### Principais Etapas do Processamento de Imagens\n",
"\n",
"O processamento de imagens envolve diversas etapas essenciais, cada uma com objetivos específicos para a manipulação e extração de informação visual.\n",
"\n",
"#### Aquisição da Imagem\n",
"\n",
"A etapa inicial consiste na captura da imagem por meio de dispositivos como câmeras digitais, sensores embarcados ou scanners. O resultado é uma imagem digital que servirá como base para as próximas fases do processamento.\n",
"\n",
"**Exemplo:** Captura de imagens médicas, como tomografias, para análise computacional.\n",
"\n",
" \n",
"\n",
"#### Pré-processamento\n",
"\n",
"Antes da análise propriamente dita, a imagem precisa ser ajustada para melhorar sua qualidade e reduzir interferências. Essa etapa pode incluir:\n",
"\n",
"- **Remoção de ruído**: Uso de filtros espaciais, como o Gaussiano, para eliminar interferências indesejadas.\n",
"- **Ajuste de contraste**: Realce de detalhes por meio da manipulação da intensidade dos pixels.\n",
"- **Equalização de histograma**: Redistribui os níveis de cinza para melhorar a visibilidade de padrões.\n",
"- **Suavização**: Redução de variações abruptas na imagem para minimizar artefatos indesejados.\n",
"\n",
"**Exemplo:** Aplicação de um filtro Gaussiano para reduzir ruído em imagens biométricas.\n",
"\n",
" \n",
"\n",
"#### Segmentação\n",
"\n",
"A segmentação divide a imagem em regiões de interesse, isolando objetos do fundo. As principais técnicas incluem:\n",
"\n",
"- **Limiarização (thresholding)**: Separar pixels com base em um valor de intensidade, gerando uma imagem binária.\n",
"- **Segmentação baseada em regiões**: Agrupar pixels semelhantes para definir objetos distintos.\n",
"- **Detecção de bordas**: Identificar contornos de objetos por meio de mudanças abruptas de intensidade.\n",
"\n",
"**Exemplo:** Uso de limiarização para separar objetos em imagens de exames laboratoriais.\n",
"\n",
" \n",
"\n",
"#### Reconhecimento de Objetos\n",
"\n",
"Nesta etapa, os objetos previamente segmentados são analisados e classificados. As abordagens mais comuns incluem:\n",
"\n",
"- **Técnicas de aprendizado de máquina**: Algoritmos treinados para identificar padrões em imagens.\n",
"- **Redes neurais convolucionais (CNNs)**: Modelos avançados capazes de reconhecer categorias complexas de objetos.\n",
"- **Métodos baseados em características**: Análise de formas, texturas e padrões presentes na imagem.\n",
"\n",
"**Exemplo:** Identificação automática de camundongos em experimentos de neurociência.\n",
"\n",
" \n",
"\n",
"🔗 **Referência:** [Detecção de camundongos em experimentos](https://github.com/heltonmaia/ECT-proj-cnn-mice)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Abrindo uma Imagem com OpenCV \n",
"\n",
"Antes de realizarmos operações aritméticas com imagens, precisamos aprender a carregar uma imagem utilizando a biblioteca **OpenCV**. O OpenCV é uma das bibliotecas mais populares para processamento de imagens e visão computacional. \n",
"\n",
"Para abrir uma imagem, utilizamos a função `cv2.imread()`, que lê o arquivo e o armazena como um array NumPy. \n",
"\n",
"Lembre-se de que o OpenCV carrega imagens no formato **BGR (Blue, Green, Red)**, enquanto outras bibliotecas, como o Matplotlib, utilizam o padrão **RGB (Red, Green, Blue)**. Isso pode causar diferenças na exibição da imagem. \n",
"\n",
"\n",
"\n",
"\n",
"```python\n",
"import cv2\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Carregar a imagem\n",
"imagem = cv2.imread('imagem_exemplo.png')\n",
"\n",
"# Converter de BGR para RGB (padrão do OpenCV é BGR, mas o Matplotlib usa RGB)\n",
"imagem_rgb = cv2.cvtColor(imagem, cv2.COLOR_BGR2RGB)\n",
"\n",
"# Exibir a imagem\n",
"plt.imshow(imagem_rgb)\n",
"plt.title('Imagem Carregada')\n",
"plt.axis('off')\n",
"plt.show()\n",
"```\n",
"\n",
"**Explicação do código:** \n",
"🔹 `cv2.imread('imagem_exemplo.png')` – Carrega a imagem do arquivo. \n",
"🔹 `cv2.cvtColor(imagem, cv2.COLOR_BGR2RGB)` – Converte a imagem de BGR para RGB para exibição correta no Matplotlib. \n",
"🔹 `plt.imshow(imagem_rgb)` – Exibe a imagem com as cores corretas. \n",
"\n",
"Após carregar a imagem, podemos manipulá-la e aplicar diversas transformações!\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 📝 Exercício: Baixar, Converter e Adicionar Texto com OpenCV\n",
"\n",
"**Objetivo:** \n",
"Você deverá baixar uma imagem da internet, convertê-la para escala de cinza, adicionar um texto personalizado e salvar o resultado no Google Drive. \n",
"\n",
"**Instruções** \n",
"\n",
"- **Baixe uma imagem da web** \n",
" - Use `requests` para baixar uma imagem de uma URL (exemplo: `\"https://heltonmaia.com/computervision/_images/cover.jpeg\"`). \n",
"\n",
"- **Converta para escala de cinza** \n",
" - Aplique `cv2.COLOR_BGR2GRAY` para transformar a imagem em preto e branco. \n",
"\n",
"- **Adicione um texto à imagem** \n",
" - Use `cv2.putText()` para inserir um texto (ex: `\"OpenCV Challenge\"`). \n",
" - Defina: \n",
" - Posição (`(x, y)`). \n",
" - Fonte (`cv2.FONT_HERSHEY_SIMPLEX`). \n",
" - Escala (`1.0`). \n",
" - Cor (`255` para branco em imagens em cinza). \n",
" - Espessura (`2`). \n",
"\n",
"- **Exiba a imagem processada** \n",
" - Mostre o resultado com `cv2.imshow()` (ou `cv2_imshow` no Google Colab). \n",
"\n",
"- **Salve no Google Drive** \n",
" - Monte o Drive (`drive.mount('/content/drive')`). \n",
" - Salve a imagem processada em uma pasta específica. \n",
"\n",
"---\n",
"\n",
"**💡 Dicas Úteis** \n",
"\n",
"**Posicionamento do texto:** \n",
" - Use `(50, 50)` para colocar o texto no canto superior esquerdo. \n",
"\n",
"**Se estiver no Google Colab:** \n",
" - Substitua `cv2.imshow()` por: \n",
" ```python\n",
" from google.colab.patches import cv2_imshow\n",
" cv2_imshow(imagem_processada) \n",
" ```\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Operações Aritméticas com Imagens\n",
"\n",
"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:\n",
"\n",
"**Adição:** A adição de imagens combina duas imagens somando os valores dos pixels correspondentes. A operação pode ser descrita matematicamente como:\n",
"\n",
"$I_{\\text{resultante}}(x, y) = I_1(x, y) + I_2(x, y)$\n",
"\n",
"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.\n",
"\n",
"Essa operação é útil para:\n",
"\n",
"- **Combinar informações:** Ao adicionar imagens de diferentes fontes, podemos integrar informações e criar uma visão mais completa.\n",
"- **Ajustar o brilho:** Adicionar um valor constante a todos os pixels aumenta o brilho da imagem.\n",
"\n",
"Exemplo: Adicionar duas imagens e realizar uma combinação de informações.\n",
"\n",
"```python\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Carregar as imagens\n",
"img1 = plt.imread('tropical1.png')\n",
"img2 = plt.imread('tropical2.png')\n",
"\n",
"# Verificar se as imagens têm o mesmo tamanho\n",
"if img1.shape != img2.shape:\n",
" raise ValueError(\"As imagens devem ter o mesmo tamanho para adição.\")\n",
"\n",
"# Adicionar as imagens\n",
"added_img = img1 + img2\n",
"\n",
"# Exibir as imagens lado a lado\n",
"fig, axs = plt.subplots(1, 3, figsize=(10, 5))\n",
"\n",
"# Exibir a primeira imagem\n",
"axs[0].imshow(img1)\n",
"axs[0].set_title('Imagem 1')\n",
"axs[0].axis('off')\n",
"\n",
"# Exibir a segunda imagem\n",
"axs[1].imshow(img2)\n",
"axs[1].set_title('Imagem 2')\n",
"axs[1].axis('off')\n",
"\n",
"# Exibir a imagem resultante da adição\n",
"axs[2].imshow(added_img)\n",
"axs[2].set_title('Imagem Resultante')\n",
"axs[2].axis('off')\n",
"plt.show()\n",
"```\n",
"Baixar [tropical1](tropical1.png) [tropical2](tropical2.png)\n",
"\n",
"Para evitar a saturação, como poderíamos melhorar o código para obter a imagem abaixo?\n",
"\n",
"\n",
"\n",
"**Subtração:** A subtração de imagens calcula a diferença entre os valores dos pixels correspondentes de duas imagens. A operação pode ser descrita matematicamente como:\n",
"\n",
"$ I_{\\text{resultante}}(x, y) = I_1(x, y) - I_2(x, y) $\n",
"\n",
"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.\n",
"\n",
"Essa operação é útil para:\n",
"\n",
"- **Detectar diferenças:** A subtração pode destacar as diferenças entre duas imagens, como mudanças ao longo do tempo ou objetos movidos.\n",
"- **Isolar elementos:** Ao subtrair uma imagem de fundo de uma imagem com um objeto em primeiro plano, podemos isolar o objeto.\n",
"\n",
"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.\n",
"\n",
"```python\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Carregar as imagens\n",
"img1 = plt.imread('w1.png')\n",
"img2 = plt.imread('w2.png')\n",
"\n",
"# Subtrair as imagens\n",
"subtracted_img = img1 - img2\n",
"\n",
"# Exibir a imagem resultante da subtração\n",
"fig, axs = plt.subplots(1, 3, figsize=(10, 5))\n",
"\n",
"# Exibir a primeira imagem\n",
"axs[0].imshow(img1)\n",
"axs[0].set_title('Imagem 1')\n",
"axs[0].axis('off')\n",
"\n",
"# Exibir a segunda imagem\n",
"axs[1].imshow(img2)\n",
"axs[1].set_title('Imagem 2')\n",
"axs[1].axis('off')\n",
"\n",
"# Exibir a imagem resultante da subtração\n",
"axs[2].imshow(subtracted_img)\n",
"axs[2].set_title('Imagem Resultante')\n",
"axs[2].axis('off')\n",
"plt.show()\n",
"```\n",
"Baixar [walking1](w1.png) [walking2](w2.png)\n",
"\n",
"**Multiplicação:** A multiplicação de imagens pode ser realizada de duas maneiras:\n",
"\n",
"- **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:\n",
"\n",
"$ I_{\\text{resultante}}(x, y) = c \\cdot I(x, y) $\n",
"\n",
"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.\n",
"\n",
"- **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:\n",
"\n",
"$ I_{\\text{resultante}}(x, y) = I_1(x, y) \\cdot I_2(x, y) $\n",
"\n",
"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.\n",
"\n",
"Exemplo: Multiplicar uma imagem por uma constante para aumentar o contraste ou aplicar uma máscara para editar partes específicas da imagem. Utilize uma imagem de sua preferência.\n",
"\n",
"```python\n",
"# Multiplicação por uma constante\n",
"constant = 1.5\n",
"multiplied_img = img1 * constant\n",
"\n",
"# Exibir a imagem resultante da multiplicação por uma constante\n",
"fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
"\n",
"# Exibir a imagem original\n",
"axs[0].imshow(img1)\n",
"axs[0].set_title('Imagem Original')\n",
"axs[0].axis('off')\n",
"\n",
"# Exibir a imagem resultante da multiplicação\n",
"axs[1].imshow(multiplied_img)\n",
"axs[1].set_title('Imagem com Contraste Aumentado')\n",
"axs[1].axis('off')\n",
"plt.show()\n",
"```\n",
"Baixar [walking1](w1.png) \n",
"\n",
"**Manipulação de pixels**\n",
"```python\n",
"# Cortar uma região da imagem\n",
"corte = img1[200:, 450:900] # observe a indexação linha x coluna\n",
"corte[10:20,100:300] = [0,1,0] # Acessando uma região de pixels e definindo para verde\n",
"\n",
"plt.imshow(corte)\n",
"```\n",
"\n",
"Essas operações básicas são fundamentais para manipulação e análise de imagens, permitindo ajustes precisos e modificações específicas que são essenciais em várias aplicações de Visão Computacional."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 🎨 Conversão entre Espaços de Cor\n",
"\n",
"A conversão entre espaços de cor é essencial em processamento de imagens. O OpenCV (`cv2.cvtColor`) permite transformar imagens entre diferentes formatos (BGR, Grayscale, HSV, RGB, LAB). Abaixo, exemplos de conversão com visualização lado a lado usando `plt.imshow`:\n",
"\n",
"Baixar [walking3](w3.png) \n",
"\n",
"**- Carregar Imagem Original (BGR)**\n",
"```python\n",
"import cv2\n",
"import matplotlib.pyplot as plt\n",
"\n",
"img = cv2.imread('w3.png') # OpenCV carrega em BGR\n",
"img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # Converter para RGB (Matplotlib)\n",
"plt.imshow(img_rgb)\n",
"plt.title('Original (RGB)')\n",
"```\n",
"\n",
"**- Conversões para Outros Espaços de Cor**\n",
"**BGR → Grayscale (Escala de Cinza)**\n",
"\n",
"```python\n",
"img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n",
"plt.figure(figsize=(12, 6))\n",
"plt.subplot(1, 2, 1), plt.imshow(img_rgb), plt.title('Original (RGB)'), plt.axis('off')\n",
"plt.subplot(1, 2, 2), plt.imshow(img_gray, cmap='gray'), plt.title('Grayscale'), plt.axis('off')\n",
"plt.tight_layout(), plt.show()\n",
"```\n",
"\n",
"**BGR → HSV (Matiz, Saturação, Valor)**\n",
"```python\n",
"img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)\n",
"plt.figure(figsize=(12, 6))\n",
"plt.subplot(1, 2, 1), plt.imshow(img_rgb), plt.title('Original (RGB)'), plt.axis('off')\n",
"plt.subplot(1, 2, 2), plt.imshow(img_hsv), plt.title('HSV'), plt.axis('off')\n",
"plt.tight_layout(), plt.show()\n",
"```\n",
"\n",
"**BGR → LAB (Luminância, Canais A/B)**\n",
"```python\n",
"img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)\n",
"plt.figure(figsize=(12, 6))\n",
"plt.subplot(1, 2, 1), plt.imshow(img_rgb), plt.title('Original (RGB)'), plt.axis('off')\n",
"plt.subplot(1, 2, 2), plt.imshow(img_lab), plt.title('LAB'), plt.axis('off')\n",
"plt.tight_layout(), plt.show()\n",
"```\n",
"\n",
"**Visualização de Todos os Espaços de Cores**\n",
"```python\n",
"plt.figure(figsize=(16, 4))\n",
"plt.subplot(1, 4, 1), plt.imshow(img_rgb), plt.title('RGB'), plt.axis('off')\n",
"plt.subplot(1, 4, 2), plt.imshow(img_gray, cmap='gray'), plt.title('Grayscale'), plt.axis('off')\n",
"plt.subplot(1, 4, 3), plt.imshow(img_hsv), plt.title('HSV'), plt.axis('off')\n",
"plt.subplot(1, 4, 4), plt.imshow(img_lab), plt.title('LAB'), plt.axis('off')\n",
"plt.tight_layout(), plt.show()\n",
"```\n",
"\n",
"**Dicas Importantes:**\n",
"\n",
"**- OpenCV vs Matplotlib**: \n",
" - OpenCV usa **BGR** por padrão; Matplotlib usa **RGB**. Sempre converta com `cv2.COLOR_BGR2RGB` antes de exibir no `plt.imshow`.\n",
"\n",
"**- Canais Individuais (HSV/LAB)**: \n",
" Para visualizar canais separados (ex: H, S, V):\n",
" ```python\n",
" h, s, v = cv2.split(img_hsv)\n",
" plt.imshow(h, cmap='gray'), plt.title('Canal H (Matiz)'), plt.axis('off'), plt.show()\n",
" ```\n",
"\n",
"**- Transições Suaves (Vídeo)**: \n",
" Para criar um vídeo de transição entre espaços de cor (como no exemplo do link), use `cv2.VideoWriter` interpolando os valores de conversão quadro a quadro.\n",
"\n",
"Aproveite que estudou sobre como transitar em diferente canais de cor e tente fazer um vídeo como este do [link](color_transitions_slowmotion.mp4).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Transformações Geométricas\n",
"\n",
"As transformações geométricas permitem modificar a posição, orientação e escala de uma imagem. Abaixo, exemplos de **translação**, **rotação**, **escala**, **perspectiva** e **operações aritméticas**, seguindo o mesmo padrão de formatação e exibição usado no exemplo de rotação. \n",
"\n",
"**- Translação (Deslocamento)** \n",
"Desloca a imagem nos eixos **x** e **y** usando uma matriz de transformação afim: \n",
"\n",
"```python\n",
"matriz_translacao = np.float32([[1, 0, deslocamento_x], # [a, b, tx] → controla eixo X \n",
" [0, 1, deslocamento_y]]) # [c, d, ty] → controla eixo Y \n",
"``` \n",
"**Onde:** \n",
"- `1` e `0` mantêm a escala original (sem distorção) \n",
"- `deslocamento_x` e `deslocamento_y` definem o movimento em pixels \n",
"- Valores positivos: direita/baixo | Valores negativos: esquerda/cima \n",
"\n",
"**Exemplo:** \n",
"```python\n",
"import cv2 \n",
"import numpy as np \n",
"import matplotlib.pyplot as plt \n",
"\n",
"img = cv2.imread('w3.png') \n",
"img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) \n",
"\n",
"# Aplica translação \n",
"deslocamento_x, deslocamento_y = 50, 100 \n",
"img_transladada = cv2.warpAffine(img, matriz_translacao, (img.shape[1], img.shape[0])) \n",
"\n",
"# Exibe resultados \n",
"plt.figure(figsize=(12, 6)) \n",
"plt.subplot(1, 2, 1), plt.imshow(img), plt.title('Original'), plt.axis('off') \n",
"plt.subplot(1, 2, 2), plt.imshow(img_transladada), plt.title(f'Transladada ({deslocamento_x}, {deslocamento_y})'), plt.axis('off') \n",
"plt.show() \n",
"``` \n",
"\n",
"**Saída:** \n",
"- Imagem original (esquerda) e transladada (direita) \n",
"- Áreas deslocadas além dos limites ficam pretas.\n",
"\n",
"**- Rotação** \n",
"Gira a imagem em torno de um ponto central (`cv2.getRotationMatrix2D`). \n",
"\n",
"```python\n",
"(h, w) = img.shape[:2]\n",
"centro = (w // 2, h // 2)\n",
"angulo = 45 # Graus\n",
"escala = 1.0\n",
"matriz_rotacao = cv2.getRotationMatrix2D(centro, angulo, escala)\n",
"img_rotacionada = cv2.warpAffine(img, matriz_rotacao, (w, h))\n",
"\n",
"plt.figure(figsize=(12, 6))\n",
"plt.subplot(1, 2, 1), plt.imshow(img), plt.title('Original'), plt.axis('off')\n",
"plt.subplot(1, 2, 2), plt.imshow(img_rotacionada), plt.title(f'Rotacionada ({angulo}°)'), plt.axis('off')\n",
"plt.tight_layout()\n",
"plt.show()\n",
"``` \n",
"\n",
"**- Escala (Redimensionamento)** \n",
"Altera as dimensões da imagem (`cv2.resize`). \n",
"\n",
"```python\n",
"nova_largura, nova_altura = 300, 200 # Novas dimensões (largura, altura)\n",
"img_redimensionada = cv2.resize(img, (nova_largura, nova_altura))\n",
"\n",
"plt.figure(figsize=(12, 6))\n",
"plt.subplot(1, 2, 1), plt.imshow(img), plt.title(f'Original ({w}x{h})'), plt.axis('off')\n",
"plt.subplot(1, 2, 2), plt.imshow(img_redimensionada), plt.title(f'Redimensionada ({nova_largura}x{nova_altura})'), plt.axis('off')\n",
"plt.tight_layout()\n",
"plt.show()\n",
"``` \n",
"\n",
"**- Transformação de Perspectiva** \n",
"Muda o ponto de vista da imagem (`cv2.getPerspectiveTransform` + `cv2.warpPerspective`). \n",
"\n",
"```python\n",
"pontos_originais = np.float32([[50, 50], [200, 50], [50, 200], [200, 200]])\n",
"pontos_destino = np.float32([[10, 100], [200, 50], [100, 250], [250, 200]])\n",
"matriz_perspectiva = cv2.getPerspectiveTransform(pontos_originais, pontos_destino)\n",
"img_perspectiva = cv2.warpPerspective(img, matriz_perspectiva, (w, h))\n",
"\n",
"plt.figure(figsize=(12, 6))\n",
"plt.subplot(1, 2, 1), plt.imshow(img), plt.title('Original'), plt.axis('off')\n",
"plt.subplot(1, 2, 2), plt.imshow(img_perspectiva), plt.title('Transformação de Perspectiva'), plt.axis('off')\n",
"plt.tight_layout()\n",
"plt.show()\n",
"``` \n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Ajuste de Gamma para Clareamento de Imagens com OpenCV e Matplotlib\n",
"\n",
"O ajuste de gamma é uma técnica fundamental em processamento de imagens para modificar o brilho e contraste de forma não-linear. Esta abordagem é particularmente útil para realçar detalhes em regiões escuras sem afetar excessivamente as áreas claras.\n",
"\n",
"**Fundamentos Matemáticos**\n",
"A transformação de gamma segue a equação:\n",
"\n",
"$$ I_{\\text{out}} = 255 \\times \\left(\\frac{I_{\\text{in}}}{255}\\right)^\\gamma $$\n",
"\n",
"Onde:\n",
"- $I_{\\text{in}}$: Valor do pixel de entrada (0 a 255)\n",
"- $\\gamma$: Parâmetro de correção\n",
"- $I_{\\text{out}}$: Valor do pixel processado\n",
"\n",
"**Efeitos do Parâmetro Gamma**\n",
"| Valor de γ | Efeito na Imagem | Aplicação Típica |\n",
"|------------|---------------------------|----------------------------|\n",
"| γ < 1 | Clareamento | Melhorar sombras |\n",
"| γ = 1 | Nenhuma alteração | - |\n",
"| γ > 1 | Escurecimento | Reduzir áreas superexpostas|\n",
"\n",
"**Implementação Prática**\n",
"```python\n",
"import cv2\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Carregar imagem (OpenCV lê em BGR)\n",
"img = cv2.imread('w3.png')\n",
"img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # Conversão para RGB\n",
"\n",
"# Parâmetros de gamma\n",
"gamma_values = [0.4, 1.0, 2.2] # Exemplos para clarear, original e escurecer\n",
"\n",
"# Configurar plot\n",
"plt.figure(figsize=(18, 6))\n",
"\n",
"for i, gamma in enumerate(gamma_values, 1):\n",
" # Aplicar correção de gamma\n",
" corrected = np.power(img_rgb/255., gamma)\n",
" corrected = (corrected * 255).astype(np.uint8)\n",
" \n",
" # Plotar resultados\n",
" plt.subplot(1, 3, i)\n",
" plt.imshow(corrected)\n",
" plt.title(f'γ = {gamma}')\n",
" plt.axis('off')\n",
"\n",
"plt.tight_layout()\n",
"plt.show()\n",
"```\n",
"\n",
"\n",
"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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 📝 Exercício - **Processamento de Imagens com Gradio**\n",
"\n",
"**Objetivo:** Implementar um aplicativo interativo que permita realizar diversas operações de processamento de imagens, incluindo transformações geométricas, manipulação de cores e ajustes de iluminação. O aplicativo deve ser desenvolvido usando uma das bibliotecas indicadas para criar uma interface amigável.\n",
"\n",
"**Tarefas a serem implementadas:**\n",
"\n",
"1. **Transformações Geométricas**\n",
"\n",
" - **Translação:** Permitir ao usuário especificar os valores de deslocamento horizontal e vertical.\n",
"\n",
" - **Rotação:** Oferecer um controle deslizante para selecionar o ângulo de rotação (0° a 360°).\n",
"\n",
" - **Escala:** Incluir opções para ampliar/reduzir a imagem com fatores configuráveis.\n",
"\n",
"2. **Operações de Cores**\n",
"\n",
" - **Conversão de espaços de cor:** Converter de RGB para Grayscale, HSV e outros.\n",
"\n",
" - **Ajuste de contraste:** Multiplicação por constante com controle deslizante para ajuste.\n",
"\n",
"3. **Correção Gamma e Clareamento**\n",
"\n",
" - **Controle de gamma:** Implementar um controle deslizante para ajustar o valor gamma (0.1 a 3.0).\n",
"\n",
"Lembre-se de permitir que as imagens modificadas possam ser salvas.\n",
"\n",
"**Bibliotecas:**\n",
"\n",
"🔗 Consulte a documentação do Gradio para implementação: [https://gradio.app/docs/](https://gradio.app/docs/)\n",
"\n",
"🔗 Consulte a documentação do Streamlit para implementação: [https://docs.streamlit.io/](https://docs.streamlit.io/)\n",
"\n",
"🔗 Consulte a documentação do NiceGUI para implementação: [https://nicegui.io/documentation](https://nicegui.io/documentation) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 🧠 Exercícios Conceituais\n",
"\n",
"**1. Explorando a Visão Computacional**\n",
"\n",
"Descreva com suas próprias palavras o que é **visão computacional**. \n",
"- Quais são seus principais objetivos? \n",
"- Por que essa área é considerada estratégica dentro da inteligência artificial? \n",
"- Como ela tem sido aplicada em diferentes áreas do conhecimento e da indústria?\n",
"\n",
"**2. Fundamentos da Imagem Digital**\n",
"\n",
"O capítulo apresenta os conceitos de **pixels, resolução e canais de cor** (RGB, escala de cinza, binário). \n",
"\n",
"- Como a **resolução** influencia a quantidade de detalhes contidos em uma imagem? \n",
"- De que forma essa característica impacta o desempenho de algoritmos de análise visual? \n",
"- Em que contextos pode ser mais vantajoso utilizar imagens em **RGB**, **tons de cinza** ou **binárias**? Argumente com base em objetivos diferentes de análise.\n",
"\n",
"**3. Etapas do Processamento de Imagens**\n",
"\n",
"O fluxo do processamento digital de imagens inclui as etapas de **aquisição**, **pré-processamento**, **segmentação** e **reconhecimento**. \n",
"\n",
"- Escolha um cenário que envolva análise de imagens (como monitoramento ambiental, inspeção visual, mobilidade urbana etc.). \n",
"- Descreva como essas etapas podem ser aplicadas nesse contexto, destacando o papel de cada uma para alcançar um resultado útil.\n",
"\n",
"**4. Bibliotecas Python Essenciais**\n",
"\n",
"As bibliotecas **NumPy**, **OpenCV**, **Matplotlib**, **Scikit-Image** e **Pillow** são frequentemente utilizadas em projetos de visão computacional.\n",
"\n",
"- Qual é a principal finalidade de cada uma dessas ferramentas? \n",
"- Aponte situações específicas em que cada biblioteca pode oferecer recursos decisivos para o desenvolvimento de soluções com imagens.\n",
"\n",
"**5. Gerenciamento de Dependências com `pip`**\n",
"\n",
"O uso do `pip` e de arquivos `requirements.txt` é essencial para organizar ambientes de desenvolvimento.\n",
"\n",
"- Por que é importante manter um controle claro sobre as bibliotecas e versões utilizadas em um projeto? \n",
"- Que tipos de dificuldades podem surgir quando diferentes membros da equipe ou sistemas operacionais tentam reproduzir um código sem dependências bem definidas?\n",
"\n",
"**6. Utilizando Comandos Linux**\n",
"\n",
"Imagine que você precisa organizar e preparar um conjunto de imagens no Google Colab usando comandos de terminal. \n",
"\n",
"- Descrevendo uma sequência de comandos Linux que inclua: \n",
" - Criar uma pasta para armazenar imagens \n",
" - Listar arquivos\n",
" - Mover imagens para essa nova pasta\n",
" - Copiar um conjunto de arquivos para backup\n",
"- Ao final, responda: \n",
" - Como o conhecimento da **estrutura de diretórios** e do sistema de arquivos do Linux pode ajudar nesse processo? \n",
" - O que são **permissões de arquivos** no Linux e como elas podem impactar o acesso e manipulação de imagens?\n",
"\n",
"**7. Operações Aritméticas com Imagens**\n",
"\n",
"Somar ou subtrair imagens pode revelar diferentes tipos de informação.\n",
"\n",
"- Quando a **adição** de imagens pode ser vantajosa em análise visual? \n",
"- E a **subtração**, como pode destacar mudanças ou padrões? \n",
"- Proponha situações em que essas operações seriam especialmente reveladoras.\n",
"\n",
"**8. Conversão entre Espaços de Cor**\n",
"\n",
"A conversão entre espaços como RGB, Grayscale, HSV e LAB é recorrente em tarefas visuais.\n",
"\n",
"- Por que diferentes espaços de cor são usados dependendo do problema a ser resolvido? \n",
"- Em que tipo de tarefa a conversão para **HSV** ou **LAB** pode facilitar o processamento ou melhorar os resultados?\n",
"\n",
"**9. Transformações Geométricas em Imagens**\n",
"\n",
"As transformações de imagem — como **translação**, **rotação** e **escala** — são fundamentais para adaptar a imagem ao contexto da análise.\n",
"\n",
"- Escolha uma situação que envolva múltiplos ângulos, posições ou tamanhos de objetos em uma imagem. \n",
"- Explique como essas transformações ajudam a tornar o sistema mais robusto ou sensível a variações.\n",
"\n",
"**10. Ajuste de Gamma em Imagens**\n",
"\n",
"O **ajuste de gamma** modifica a relação entre brilho e contraste na imagem.\n",
"\n",
"- O que acontece com uma imagem quando usamos um **valor de gamma menor que 1**? Em que circunstâncias isso é benéfico? \n",
"- E se o **valor for maior que 1**? Em que situações isso pode melhorar a visualização ou a extração de informações?\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Referências e Conteúdo Extra\n",
"\n",
"- Livro(s)\n",
" - [Curso de Python](https://heltonmaia.com/pythonbook/intro.html)\n",
"- Sites Oficiai(s)\n",
" - [Documentação Oficial do Numpy](https://numpy.org/doc/)\n",
" - [Documentação Oficial do Matplotlib](https://matplotlib.org/)\n",
" - [Documentação Oficial do OpenCV](https://docs.opencv.org/)\n",
" - [Documentação Oficial do Scikit-image](https://scikit-image.org/)\n",
"- Projeto(s)\n",
" - [Mice Tracking](https://github.com/heltonmaia/ECT-proj-cnn-mice) \n"
]
}
],
"metadata": {
"colab": {
"provenance": [],
"toc_visible": true
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.20"
}
},
"nbformat": 4,
"nbformat_minor": 4
}