{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Capítulo 1: Introdução à Visão Computacional" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![cover](cover.jpeg)\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", "\"setup\"\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", "\"setup\" \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", "\"setup\" \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", "\"setup\" \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", "\"setup\" \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", "![](bgr2rgb.png)\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", "\"Imagem\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", "![](w_gamma.jpg)\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 }