Este capítulo inaugura a Parte 1 do livro, dedicada aos fundamentos do Processamento Digital de Imagens (PDI). São apresentados a representação matemática de imagens digitais e os principais métodos para sua manipulação.
Compreender a natureza física e matemática da imagem digital \(f(x,y)\).
Identificar as faixas do espectro eletromagnético relevantes para PDI.
Realizar operações básicas: leitura, exibição e salvamento de imagens.
Acessar e modificar intensidades de pixels individualmente.
Aplicar limiarização manual.
Configurar o ambiente de desenvolvimento em Python.
Manipular estruturas de matrizes (NumPy) sem cair em armadilhas de memória.
1.2 Antes de começar: Notebooks interativos
Este material foi construído sob o conceito de Literate Programming (Programação Literária), idealizado por Donald Knuth na década de 1980 (Knuth, 1984). Knuth — também criador do sistema TeX para tipografia digital — propôs que os programas fossem escritos como uma narrativa lógica para seres humanos, intercalando código e documentação.
Para executar uma célula, pressione Shift + Enter ou clique no botão ▶️.
NotaNota sobre o formato
Nas versões renderizadas (PDF ou HTML), o código é apresentado em blocos estáticos para fins de leitura e referência. A execução interativa requer o acesso via Google Colab (disponível no topo da página) ou em ambiente local via VSCode ou Jupyter Notebook.
1.3 Fundamentos
O estudo de sistemas baseados em imagens compreende um ecossistema de disciplinas integradas que transformam dados visuais brutos em conhecimento estruturado. Enquanto algumas áreas focam na geração de representações, outras dedicam-se ao tratamento e à análise desses dados para dar suporte a aplicações tecnológicas complexas.
O diagrama apresentado no Figura 1.1 estabelece a distinção e complementaridade entre o Processamento Digital de Imagens (PDI) e a Visão Computacional (VC). O PDI, destacado em verde, tem como foco a transformação de imagem para imagem, visando melhoria de qualidade ou pré-processamento, como a remoção de ruídos e realce de contraste.
Em contrapartida, a VC, assinalada em azul, foca na interpretação do conteúdo visual para extrair modelos ou informações, como o reconhecimento de objetos e gestos. A região de intersecção ilustra a sinergia entre as áreas, onde o PDI prepara os dados visuais para a interpretação pela VC. O mapa também demonstra as interconexões de ambas as disciplinas com áreas como Robótica, Computação Gráfica, Inteligência Artificial (IA) e Neurociência.
Figura 1.1: Diagrama relacional detalhando as distinções fundamentais, sinergias e interconexões entre PDI e VC no contexto de sistemas baseados em imagens.
1.3.1 👁️ Visão Computacional
Foco:Imagem → Modelo (caminho inverso da Computação Gráfica).
Objetivo: Extrair informação de alto nível a partir de imagens ou vídeos.
Aplicações típicas:
Robótica - detecção de obstáculos, localização e navegação autônoma.
Vigilância e inspeção - reconhecimento de eventos, leitura de placas, controle de qualidade.
Sensoriamento remoto - análise de imagens de satélite, mapeamento ambiental.
Imagens médicas - detecção de tumores, segmentação de órgãos, auxílio ao diagnóstico.
Interação humano‑computador - reconhecimento de gestos, expressões faciais, rastreamento ocular.
Relação com outras áreas: utiliza técnicas de Aprendizado de Máquina e IA para classificar e interpretar cenas; serve como “olhos” da Robótica.
1.3.2 🖼️ Processamento Digital de Imagens (PDI)
Foco:Imagem → Imagem (geralmente - transformação de uma imagem em outra).
Objetivo: Melhorar a qualidade visual ou extrair características de baixo nível.
Aplicações comuns:
Eliminação de ruídos (filtros de média, mediana, gaussiano).
Melhoria de contraste (equalização de histograma, ajuste gamma).
Detecção de bordas (Sobel, Canny, Laplaciano).
Segmentação (limiarização, crescimento de regiões, watershed).
Transformações geométricas (redimensionamento, rotação, correção de perspectiva).
É a base para a maioria dos sistemas de VC (pré‑processamento).
A Computação Gráfica frequentemente aplica PDI para pós‑processamento (ex.: suavização, realce).
Técnicas de IA podem otimizar parâmetros de processamento (ex.: aprendizado de filtros).
Tabela 1.1: Conexão entre PDI, VC e outras áreas da ciência.
Área
Relação com PDI e VC
Inteligência Artificial
Fornece modelos (redes neurais, SVM) que interpretam saídas da VC.
Robótica
Consome dados de VC para tomar decisões (navegação, manipulação).
Aprendizado de Máquina
Usa descritores extraídos pelo PDI/VC para treinar classificadores.
Computação Gráfica
Caminho inverso: modelo → imagem; muitas vezes aplica PDI para renderização realista.
Neurociência
Inspira modelos de PDI (ex.: filtros semelhantes a células ganglionares da retina).
1.4 Etapas do PDI
As etapas do PDI são apresentadas na Figura 1.2, que podem ser compreendidas como uma cadeia de transformações que reduz a redundância dos dados em busca de significado:
Baixo Nível: Atua diretamente sobre os pixels da imagem ruidosa para realizar melhorias e filtragens, gerando como saída uma imagem limpa ou realçada.
Médio Nível: Recebe a imagem tratada e realiza a segmentação e descrição, transformando a matriz de pixels em atributos estruturados (forma, tamanho e textura).
Alto Nível: Utiliza a tabela de atributos para alimentar processos de lógica e inteligência artificial, resultando na decisão ou reconhecimento final (como o diagnóstico médico).
Figura 1.2: Representação do fluxo sequencial de processamento: a saída de cada nível torna-se a entrada do nível subsequente.
A Figura 1.3 detalha a sequência completa do PDI, desde a captura até a interpretação. O fluxo inicia-se na Aquisição da Imagem (1) e prossegue com o Melhoramento (2) e a Restauração (3). Em seguida, o conteúdo é isolado pela Segmentação (4) e refinado pela Morfologia (5). A transição crucial ocorre na Representação e Descrição (6), onde objetos visuais são convertidos em dados matemáticos (área, perímetro etc.), permitindo o Reconhecimento (7). Processos auxiliares incluem o Processamento de Imagem Colorida e a Compressão, que contribuem para a eficiência do armazenamento e da análise.
Figura 1.3: Fluxo detalhado do PDI: da aquisição sensorial à extração de atributos e reconhecimento automatizado, incluindo em processamento colorido e compressão.
1.5 Formação da Imagem e o Espectro
O processo de formação de uma imagem é fundamentado na interação entre a matéria e a energia radiante. Essencialmente, uma imagem é concebida quando um sensor registra a radiação resultante da interação com um objeto físico. No contexto da visão humana e da fotografia convencional, este fenômeno depende de uma fonte de luz que ilumine a cena; as características dos objetos são então codificadas através das variações de intensidade e cor da luz que atinge o sensor, conforme ilustrado na Figura 1.4.
Figura 1.4: Representação do espectro visível e sua posição em relação às demais radiações eletromagnéticas, destacando a variação dos comprimentos de onda de 380 nm a 750 nm.
A luz visível ocupa apenas uma pequena faixa do espectro eletromagnético — entre 380 nm (violeta) e 750 nm (vermelho) — conforme ilustrado na Figura 1.5. Sensores digitais convencionais operam nessa mesma janela, mas equipamentos especializados podem captar radiações invisíveis ao olho humano, como o infravermelho e os raios X. Em PDI, a imagem formada depende diretamente da sensibilidade espectral do sensor utilizado.
Figura 1.5: (A) Espectro eletromagnético completo em escala logarítmica, com destaque para a faixa visível. (B) Detalhe da luz visível (380-750 nm) e suas cores. (C) Decomposição da luz branca pelo prisma: menor comprimento de onda sofre maior refração, separando UV, visível e infravermelho.
A partir desse processo físico de aquisição, torna-se possível modelar matematicamente a imagem digital como uma função bidimensional discreta, na qual cada ponto da cena é representado por amostras numéricas de intensidade luminosa, formalizando assim os conceitos de pixel e de imagem digital apresentados na próxima seção.
1.6 O que é uma Imagem Digital?
Uma imagem digital é formada por uma grade de pixels (Picture Elements), onde cada pixel é a menor unidade elementar da imagem.
DicaO que é um Pixel?
Um pixel é a menor unidade endereçável que compõe uma imagem digital. Cada pixel ocupa uma posição única na grade e armazena um ou mais valores numéricos que representam sua intensidade ou cor.
Representação Matemática
Diferente de uma função contínua, o domínio de uma imagem digital é um plano retangular finito \(\mathbb{E} \subset \mathbb{Z}^2\), que representa a grade de amostragem. Este domínio é indexado por coordenadas inteiras:
\[
\mathbb{E} = \{ (x, y) \in \mathbb{Z}^2 \mid 0 \le x < L,\; 0 \le y < H \}
\tag{1.1}\]
Onde:
\(L\): representa a largura da imagem (número de colunas).
\(H\): representa a altura da imagem (número de linhas).
A imagem digital é uma função que associa cada par de coordenadas \((x,y)\) a um ou mais valores que descrevem a aparência do pixel.
\[
f: \mathbb{E} \to \mathcal{V}
\tag{1.2}\]
O conjunto \(\mathcal{V}\) define os valores possíveis para o pixel (codomínio), variando conforme o tipo de imagem, como demonstrado na Tabela 1.2.
Tabela 1.2: Principais tipos de imagem digital e seus respectivos conjuntos de valores possíveis para cada pixel.
Tipo de imagem
\(\mathcal{V}\) (valores do pixel)
Representação
Binária
\(\{0, 1\}\) ou \(\{0, 255\}\)
⬛◻️
Tons de cinza
\(\{0, 1, \dots, 255\}\)
░▒▓█
Colorida (RGB)
\(\{0, \dots, 255\}^3\) (triplas ordenadas de valores)
🟥🟩🟦
Exemplo prático: Uma imagem colorida no modelo RGB pode ser representada matematicamente por uma função que associa três valores de intensidade a cada pixel. Computacionalmente, essa representação corresponde a três matrizes sobrepostas — os canais vermelho (Red), verde (Green) e azul (Blue) — nas quais cada elemento armazena a intensidade luminosa do respectivo canal em uma determinada posição da imagem.
Para viabilizar os experimentos práticos de PDI e VC, este livro utiliza o ecossistema científico do Python, integrando bibliotecas voltadas à manipulação matricial, ao processamento de imagens e à visualização de resultados, apresentadas a seguir.
1.7 Configuração do Ambiente
Este material utiliza o ecossistema científico do Python, com destaque para NumPy (computação matricial), OpenCV (visão computacional) e a biblioteca morph.py(Zampirolli et al., 2025), desenvolvida para fins didáticos e utilizada ao longo deste livro, conforme apresentado na Tabela 1.3.
O projeto é disponibilizado atualmente em Python e Português. Sua organização, entretanto, permite sua expansão para outras linguagens de programação e idiomas.
Os Exercícios de Programação (EPs) apresentados ao final dos capítulos podem ser validados pelo módulo testsuite.py, que permite executar casos de teste em diferentes linguagens, incluindo Python, C, C++, Java, JavaScript e R. Dessa forma, os mesmos casos de teste podem ser utilizados para verificar as soluções desenvolvidas nos notebooks e no Moodle/VPL.
Tabela 1.3: Principais bibliotecas e módulos utilizados ao longo deste livro para computação matricial, PDI, VC, visualização e validação dos exercícios.
Biblioteca
Função principal
numpy
Representação matricial e operações numéricas
opencv-python
Leitura, escrita e operações de visão computacional
matplotlib
Visualização de imagens e gráficos
morph.py
Abstração didática de operações de PDI
testsuite.py
Execução e validação automática dos EPs
1.8 Versões do morph.py
A biblioteca morph.py possui duas versões públicas:
Versão 1.0 — versão original, publicada juntamente com o artigo apresentado no EduComp 2024: repositório da versão 1.0
A versão 1.1 foi adaptada para atender também às restrições de memória do Moodle/VPL (Laboratório Virtual de Programação). Na versão 1.0, algumas bibliotecas, como matplotlib, requests e skimage, eram carregadas no momento do import morph. Em ambientes com recursos restritos, esse carregamento podia exceder o limite de memória e produzir erros como Jail: out of memory, 128MiB.
Na versão 1.1, esses imports foram substituídos por carregamento lazy: cada biblioteca é importada somente pelo método que efetivamente a utiliza. Assim, o comando import morph mantém o carregamento inicial reduzido, utilizando principalmente numpy e cv2, enquanto recursos adicionais são carregados sob demanda.
A configuração dos notebooks é centralizada no arquivo config.py, disponibilizado no mesmo repositório. Esse arquivo verifica as dependências, procura garantir o uso do OpenCV 5.0 e disponibiliza morph.py e, quando necessário, testsuite.py. Quando o ambiente não possui a versão adequada do OpenCV, o config.py pode reinstalar a versão requerida e, se necessário, solicitar o reinício do ambiente de execução.
Dessa forma, os notebooks utilizam uma única rotina de configuração, reduzindo a repetição de código e facilitando a reprodução do ambiente computacional utilizado neste livro.
1.9 Fundamentos de Matrizes — atenção à cópia de referências
Como uma imagem digital pode ser representada por uma matriz, é importante compreender como criar e manipular matrizes corretamente. Em Python, existe uma armadilha comum ao utilizar o operador * com listas:
AvisoAtenção à cópia de referências
Ao executar m = [[0]*2]*3, não são criadas três linhas independentes. Em vez disso, são criadas três referências para a mesma lista. Consequentemente, a alteração de um elemento em uma das linhas também modificará o elemento correspondente nas demais linhas.
Para visualizar esse comportamento, pode-se executar o código no Python Tutor e compará-lo com a forma correta de criar uma matriz utilizando listas: m = [[0]*2 for _ in range(3)].
Na prática, para o processamento digital de imagens (PDI), recomenda-se utilizar NumPy, que fornece estruturas de dados multidimensionais eficientes e operações vetorizadas adequadas à manipulação de imagens. O código a seguir apresenta diferentes formas de criação de imagens sintéticas, cujos resultados são exibidos na Figura 1.6.
import numpy as np# Criando uma imagem preta (zeros) de 4, 6 pixelsimg_preta = np.zeros((4, 6), dtype='uint8')img_preta[0,0] =255# pixel branco no canto superior esquerdo# Criando uma imagem branca (255) de 4, 6 pixelsimg_branca = np.ones((4, 6), dtype='uint8') *255img_branca[3,5] =0# pixel preto no canto inferior direito# Criando uma imagem aleatória para testes (ruído)img_random = mm.randomImage(4, 6, maxValue=255)print("Matriz aleatória gerada:")print(mm.drawImg(img_random))mm.show( [img_preta, img_branca, img_random], titles=["Predominantemente preta\n(com 1 pixel branco em (0,0))","Predominantemente branca\n(com 1 pixel preto em (3,5))","Imagem aleatória\n(simulação de ruído)" ], cols=3, figsize=(9, 3), axis=True)
Figura 1.6: Exemplos de imagens sintéticas representadas matricialmente.
1.10 Lendo e Exibindo Imagens
Em bibliotecas como o NumPy, OpenCV e scikit-image, imagens digitais são frequentemente representadas computacionalmente como arrays do NumPy. Dessa forma, operações matriciais e conceitos de álgebra linear podem ser aplicados diretamente ao PDI.
Uma das operações fundamentais em PDI é a leitura de imagens.
Na biblioteca morph.py, a função mm.read() permite carregar imagens tanto de arquivos locais quanto de URLs, como ilustrado na Figura 2.7.
Dimensões (H, W, Canais): (1365, 2048, 3)
Tipo de dado: uint8
Figura 1.7: Mandrill (Mandrillus sphinx) em ambiente natural. Crédito: Julien Renoult (CC BY 4.0).
Alternativa: Download da Imagem para Armazenamento Local
Em ambientes nos quais a leitura direta de URLs esteja indisponível — devido a restrições de rede, políticas de firewall ou ausência de conectividade — uma alternativa consiste em realizar previamente o download da imagem para o sistema de arquivos local. Após o armazenamento, a imagem pode ser carregada normalmente pela função mm.read(). No exemplo a seguir, o arquivo é salvo localmente com o nome mandrill.png.
wget -O mandrill.png <URL> realiza o download da imagem e a armazena localmente com o nome especificado;
mm.read('mandrill.png') efetua a leitura do arquivo diretamente do sistema de arquivos, sem necessidade de requisições HTTP adicionais;
essa estratégia reduz a dependência de conectividade durante a execução dos experimentos e evita downloads repetidos da mesma imagem.
DicaNota
O prefixo ! é utilizado em ambientes baseados em notebooks, como Jupyter Notebook, JupyterLab e Google Colab, para executar comandos do sistema operacional diretamente em células de código. Em terminais convencionais, o comando deve ser utilizado sem o prefixo !.
Caso o utilitário wget não esteja instalado, pode-se utilizar alternativamente:
Conforme vimos, uma imagem colorida no espaço RGB é representada pela função:
\[
f: \mathbb{E} \to \{0,1,\dots,255\}^3
\]
Ou seja, para cada pixel \((x,y)\), temos três valores \((R,G,B)\) que definem sua cor.
Conversão para Tons de Cinza
Para converter uma imagem RGB em tons de cinza (grayscale), é necessário combinar os três canais em um único valor de intensidade \(g\), que representa o brilho percebido. Como o olho humano não é igualmente sensível ao vermelho, verde e azul, utiliza-se uma média ponderada. O padrão ITU-R BT.601(ITU-R, 2011) define os seguintes pesos:
\[
g = 0.299\,R + 0.587\,G + 0.114\,B
\tag{1.3}\]
Após o cálculo, o valor \(g\) é arredondado para o inteiro mais próximo e ajustado ao intervalo \([0, 255]\). O resultado é uma nova imagem, agora em tons de cinza, representada por:
A partir da imagem em tons de cinza \(f_{\text{cinza}}(x,y)\), uma operação fundamental é a limiarização, que produz uma imagem binária (apenas preto e branco). Para isso, escolhe-se um valor de corte \(T\) (geralmente no intervalo \([0,255]\)) e define-se:
Exemplo: Com \(T = 128\), pixels com intensidade acima de 128 tornam-se brancos (255); os demais tornam-se pretos (0).
A limiarização é amplamente usada para segmentar objetos do fundo, extrair bordas ou criar máscaras binárias para processamento posterior.
Nota: O valor 255 representa o branco máximo em imagens de 8 bits, enquanto 0 representa o preto absoluto.
Exemplo prático de conversão e limiarização
A Figura 1.8 ilustra os principais passos para transformar uma imagem colorida em tons de cinza e, em seguida, convertê-la em uma imagem binária por limiarização. O código a seguir implementa essas etapas:
# 1. Converter para Tons de Cinzaimg_gray = mm.gray(img)# 2. Aplicar limiar (Pixels > 128 tornam-se 255, outros 0)limiar =128img_binaria = mm.threshold(img_gray, limiar)# Uso da nova funçãomm.show( [img, img_gray, img_binaria], titles=["Original", "Tons de Cinza", f"Binária (T={limiar})"], cols=3)
Figura 1.8: Processamento básico de imagens: (a) imagem original, (b) imagem em tons de cinza, (c) imagem binarizada por limiar (T=128).
1.12 Limiarização pelo método de Otsu
Conforme apresentado na Equação 1.4, a limiarização converte uma imagem em tons de cinza para binária usando um valor de corte \(T\). Até agora fixamos \(T = 128\) manualmente.
img_bin_fixo = mm.threshold(img_gray, T=128)
Entretanto, a escolha manual de \(T\) nem sempre é trivial. A biblioteca mm oferece uma alternativa automática: quando o parâmetro limiarnão é fornecido, a função mm.threshold(img_gray) calcula o valor de \(T\) pelo método de Otsu(Otsu, 1979). Esse método, que será detalhado em capítulos futuros, maximiza a variância entre classes do histograma (frequência de cada tom de cinza), separando automaticamente os pixels de objeto e fundo.
O código abaixo compara a limiarização manual (\(T=128\)) com a automática (Otsu), mostrando também o valor de \(T\) calculado:
import cv2# Limiarização com T fixo (manual)T_fixo =128img_bin_fixo = mm.threshold(img_gray, T_fixo)# Limiarização pelo método de Otsu (T automático)T_otsu, img_bin_otsu = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)print(f'Limiar calculado por Otsu: T = {T_otsu}')# ou simplesmente:# img_bin_otsu = mm.threshold(img_gray)# Exibição lado a ladomm.show( [img_gray, img_bin_fixo, img_bin_otsu], titles=["Tons de Cinza", f"Binária (T={T_fixo})", f"Binária (Otsu, T={T_otsu})"], cols=3)
Limiar calculado por Otsu: T = 95.0
Figura 1.9: Comparação entre limiarização manual (T=128) e automática (Otsu) sobre a imagem em tons de cinza.
A Figura 1.9 mostra que o limiar obtido por Otsu se adapta automaticamente à imagem, resultando em uma binarização mais eficiente do que um valor fixo, especialmente quando as intensidades do objeto e do fundo são bem separadas no histograma. Essa técnica é amplamente utilizada em sistemas de VC para binarização de documentos, detecção de objetos e pré‑processamento de imagens.
a biblioteca mm abstrai toda essa complexidade: basta chamar mm.threshold(img_gray). O limiar de Otsu é calculado automaticamente e a imagem binária é retornada diretamente. Essa abordagem permite concentrar-se no conceito, não nos detalhes de implementação.
1.13 Acesso a Pixels
Em Python com NumPy, uma imagem é estruturada como um array multidimensional. O acesso a um pixel específico utiliza a convenção matricial linha (eixo Y) e coluna (eixo X): img[linha, coluna].
O código da Figura 1.10 demonstra como extrair esses valores em imagens coloridas (RGB) e em tons de cinza, além de isolar a vizinhança imediata do ponto de interesse.
import matplotlib.pyplot as plt# 1. Definição das coordenadas do pixel alvor, c =600, 800# 2. Acesso direto aos valores dos pixelspixel_cinza = img_gray[r, c]pixel_rgb = img[r, c]print(f"📌 VALORES NO PIXEL ALVO ({r}, {c}):")print(f" • Tons de cinza (Escalar) : {pixel_cinza}")print(f" • Colorido (Vetor RGB) : R={pixel_rgb[0]}, G={pixel_rgb[1]}, B={pixel_rgb[2]}")print("-"*50)# 3. Exibição didática da vizinhança 3x3 ao redor do pixel (r, c)# Recorta da linha r-1 até r+1, e da coluna c-1 até c+1vizinhanca_gray = img_gray[r-1:r+2, c-1:c+2]print(f"🔍 MATRIZ DE VIZINHANÇA 3x3 EM TONS DE CINZA:")print(f" (O pixel alvo central está destacado com entre colchetes)\n")# Impressão formatada para destacar o pixel centralfor i, linha inenumerate(vizinhanca_gray): linhas_str = []for j, valor inenumerate(linha):if i ==1and j ==1: linhas_str.append(f"[{valor:3d}]") # Destaca o centro (100, 100)else: linhas_str.append(f" {valor:3d} ")print(" "+" ".join(linhas_str))# 4. Demonstração visual usando o mm.show (opcional, mas excelente para o livro)# Mostra a imagem inteira com um ponto demarcando a regiãoplt.figure(figsize=(5, 5))plt.imshow(img)plt.plot(c, r, 'ro', markersize=8, label=f'Pixel ({r},{c})') # Plota (x, y) -> (c, r)plt.title(f"Localização do Pixel ({r}, {c}) na Imagem")plt.legend()plt.axis('on') # Mantém os eixos para o estudante ver as coordenadas 100, 100plt.show()
📌 VALORES NO PIXEL ALVO (600, 800):
• Tons de cinza (Escalar) : 81
• Colorido (Vetor RGB) : R=92, G=78, B=67
--------------------------------------------------
🔍 MATRIZ DE VIZINHANÇA 3x3 EM TONS DE CINZA:
(O pixel alvo central está destacado com entre colchetes)
83 96 105
85 [ 81] 96
83 77 84
Figura 1.10: Acesso a um pixel específico (r, c) e a representação de sua vizinhança na imagem.
Explicação linha a linha:
img_gray[r, c] — Retorna um único valor inteiro (escalar) entre 0 e 255, que representa a intensidade de brilho do cinza.
img[r, c] — Retorna um vetor com três valores [R, G, B], correspondentes às intensidades dos canais Vermelho, Verde e Azul.
img_gray[r-1:r+2, c-1:c+2] — Realiza um fatiamento (slicing) para extrair a submatriz \(3\times 3\) ao redor do pixel. Esta operação é a base para a implementação de filtros espaciais e convoluções.
O laço for subsequente apenas formata essa submatriz no console, exibindo o pixel central entre colchetes [ ] para fins didáticos.
A função plt.plot(c, r, 'ro') plota um ponto vermelho sobre a imagem para correlacionar a coordenada numérica com sua posição visual. Note que o Matplotlib utiliza a ordem padrão de tela (X, Y), invertendo para (c, r).
Como a indexação em Python começa em zero (zero-based), o canto superior esquerdo da imagem é a coordenada (0,0). Guarde sempre esta regra: a primeira dimensão da matriz controla a altura (linhas/Y) e a segunda controla a largura (colunas/X).
1.14 Resumo
Neste capítulo, apresentaram-se os fundamentos de representação de imagens digitais: a definição de pixel, a estruturação de imagens em matrizes e o impacto da amostragem e da quantização na qualidade final:
Imagem digital = função \(f(x,y)\) que mapeia coordenadas para intensidades (escalares ou vetoriais).
Domínio: conjunto finito \(\mathbb{E} = \{(x,y) \in \mathbb{Z}^2 \mid 0 \le x < L,\; 0 \le y < H\}\).
Tipos principais: binária (\(\mathcal{V} = \{0, 255\}\)), tons de cinza (\(\mathcal{V} = [0,255]\)) e RGB (\(\mathcal{V} = [0,255]^3\)).
Limiarização converte tons de cinza em binária; o método de Otsu determina o valor de corte automaticamente maximizando a variância entre classes.
A biblioteca morph.py (ou mm) oferece funções didáticas para operações básicas de PDI, como mm.gray(), mm.threshold(), mm.show_multiple().
Armadilha NumPy: nunca use [[0]*n]*m para criar matrizes — use sempre np.zeros() ou np.ones().
Acesso a pixels via img[linha, coluna], com indexação zero-based.
O Capítulo 2 abordará histogramas e equalização de contraste.
1.15 🤖 Uso do Gemini Notebook como Tutor Complementar
Nesta edição, além dos notebooks interativos no Google Colab, o Gemini Notebook está disponível como ferramenta complementar de estudo. A plataforma utiliza exclusivamente os documentos fornecidos pelo autor como base de conhecimento, garantindo respostas coerentes com o conteúdo do livro.
Importante🎓 Estude com o Tutor Inteligente
Acesse o ambiente do capítulo pelo link abaixo e explore especialmente as opções Guia de Estudo e Conversa para aprofundar sua compreensão.
O projeto deste capítulo no Gemini Notebook foi construído apenas com o texto em português e os exemplos de código em Python. Se você está estudando pela edição em inglês ou francês, ou acompanhando a trilha em C++, as respostas do tutor podem não corresponder exatamente à versão que você está lendo.
⚠️ Aviso sobre Conteúdo Gerado por IA
A IA é uma poderosa aliada nos estudos, mas o conteúdo gerado pode conter erros ou imprecisões. Consulte sempre livros, artigos científicos e outras fontes acadêmicas confiáveis para validar as informações. Sempre que possível, execute os exemplos práticos fornecidos neste capítulo para verificar os resultados.
Funcionalidades Disponíveis na Plataforma
O Gemini Notebook oferece uma suíte avançada de ferramentas baseadas em IA para transformar o conteúdo estático do livro em uma experiência de aprendizado dinâmica e multimídia. A plataforma utiliza técnicas de RAG (Retrieval-Augmented Generation), fundamentadas no trabalho de Lewis et al. (2020), para basear as respostas estritamente nos documentos fornecidos e minimizar a ocorrência de alucinações.
As principais funcionalidades incluem:
Resumos Multimodais (Áudio e Vídeo): Geração de conversas naturais entre especialistas no formato de Resumo em Áudio (estilo podcast) e Resumo em Vídeo, discutindo os temas centrais do capítulo, como as diferenças entre PDI e VC, ou a interpretação de transformações como a limiarização e o método de Otsu.
Visualização de Estruturas (Mapa Mental e Infográfico): Criação automática de diagramas que conectam visualmente os conceitos, por exemplo, o fluxo de processamento desde a captura da imagem digital, passando pela conversão para tons de cinza, limiarização e segmentação binária.
Ferramentas de Avaliação (Teste e Cartões Didáticos): Geração de Testes de múltipla escolha e Cartões Didáticos (flashcards) para fixação de conhecimento, baseados no texto autoral (ex.: perguntas sobre a fórmula de conversão RGB→cinza ou sobre o funcionamento do limiar global e de Otsu).
Apoio à Apresentação (Slides e Relatórios): Auxílio na estruturação de Apresentações de Slides e na redação de Relatórios técnicos, facilitando a comunicação de resultados de experimentos com imagens.
Análise de Dados (Tabela de Dados): Organização de dados extraídos do texto em tabelas estruturadas, auxiliando na compreensão de exemplos práticos, como a comparação entre diferentes valores de limiar.
Chat Contextualizado: Permite o questionamento direto sobre o código e a teoria, como: “Como implementar a conversão de RGB para tons de cinza usando os pesos do padrão ITU‑R BT.601?” ou “O que acontece com a imagem binária se eu escolher um limiar T=200 em vez de T=128?”.
1.16 Lista de Exercícios
(15%) Com suas próprias palavras, defina imagem digital e pixel. Dê um exemplo concreto de como uma imagem colorida (RGB) é representada matricialmente no computador.
(15%) Explique as diferenças entre imagem binária, tons de cinza (8 bits) e colorida RGB, indicando a faixa de valores possíveis para cada pixel em cada tipo.
(20%) Considerando a fórmula de conversão RGB → tons de cinza do padrão ITU‑R BT.601: \[g = 0.299\,R + 0.587\,G + 0.114\,B\] Calcule o valor do pixel em tons de cinza para \((R,G,B) = (80, 180, 30)\). Arredonde para o inteiro mais próximo.
(20%) O que é limiarização (thresholding)? Explique a diferença entre escolher um limiar \(T\) fixo (ex.: \(T=128\)) e utilizar o método de Otsu para determinação automática do limiar. Em poucas palavras, como o método de Otsu escolhe o limiar?
(15%) No contexto da biblioteca didática mm discutida no capítulo, responda:
(7,5%) Como se acessa o valor do pixel na posição (linha=50, coluna=60) de uma imagem em tons de cinza img_gray?
(7,5%) Qual é a vantagem de usar mm.threshold(img_gray) sem passar o limiar? Compare com a chamada equivalente no OpenCV.
(15%) O que a propriedade img.shape retorna para uma imagem NumPy no formato RGB? Dê um exemplo concreto com uma imagem de 640×480 pixels.
Referências do Capítulo
A fundamentação teórica deste capítulo compreende as seguintes obras de PDI e VC:
Gonzalez; Woods (2018) para os fundamentos de Processamento Digital de Imagens (PDI).
Singh (2019) para a implementação prática de métodos de processamento e análise de imagens.
Szeliski (2022) para o estudo de VC e algoritmos fundamentais.
Bradski; Kaehler (2008) para a aplicação da biblioteca OpenCV em ambiente Python.
Lewis et al. (2020) para o conceito de geração aumentada por recuperação (RAG), utilizado no suporte ao processamento de informações deste material.
1.17 💻 Parte Prática com Exercícios de Programação
🎯 Objetivo deste Caderno
Os Exercícios de Programação (EPs) apresentados a seguir podem ser submetidos também em atividades do Moodle (atividades VPL) que fornecem feedback automático.
Este caderno foi desenvolvido para superar limitações de uso do Moodle. Com ele, deve-se:
Desenvolver: Escrever e editar sua solução diretamente no ambiente Colab.
Validar: Testar seu código localmente utilizando os mesmos casos de teste do Moodle.
Organizar: Salvar seus códigos das atividades VPL de forma segura.
Avaliar: Quando estiver conectado ao Moodle, basta copiar sua solução e clicar em Avaliar no Moodle (se estiver na rede da UFABC) para registrar sua nota oficial.
⚙️ Instruções Passo a Passo
Em um ambiente de execução (como VSCode, Jupyter ou Colab), siga a ordem abaixo para configurar o ambiente e validar seus exercícios:
Preparação do Ambiente
Execute a célula de código abaixo para baixar morph.py e testsuite.py do repositório da disciplina — apenas se ainda não existirem no diretório local. Com ambos em ./, o notebook e os subprocessos do TestSuite encontram o módulo sem configurações extras de caminho.
Nota: O script testsuite.py buscará automaticamente os casos de teste em all/{cap}/cases no GitHub.
Escrevendo o Código
Salve sua solução em uma célula de código usando o comando mágico %%writefile. O nome do arquivo deve seguir o padrão EPX_Y.*, onde X é o capítulo, Y é o exercício e * é a extensão da linguagem.
Exemplo:%%writefile EP01_01.py
Download
Baixe morph.py e testsuite.py executando a célula abaixo:
Após salvar o arquivo com sua solução, execute o comando abaixo (em uma nova célula) para avaliar os testes automáticos:
TestSuite("EP01_01.extensão").run()
Substitua a extensão conforme a linguagem usada:
Linguagem
Extensão
Python
.py
Java
.java
C
.c
C++
.cpp
JavaScript
.js
R
.r
Como funciona: O TestSuite baixa os casos de teste do GitHub, executa seu programa com cada entrada e compara a saída com a esperada – calculando automaticamente sua nota.
Para testar código Python diretamente, sem salvar arquivo, use run_code(codigo) passando o código como string numa variável codigo:
codigo ="""from morph import mm# ... seu código aqui ..."""TestSuite("EP04_01").run_code(codigo)
⚠️ Importante: Regras e Boas Práticas
🔹 Sobre a Entrada de Dados
O seu programa deve ler a entrada padrão (teclado).
Python: Utilize input().
Outras linguagens: Utilize o comando de leitura padrão equivalente (cin, Scanner, etc.).
🔹 Configuração de IA no Colab
Para um melhor aprendizado, recomenda-se desativar o preenchimento automático de código por IA, pois não estará disponível durante as avaliações. Por exemplo no navegador Chrome:
Vá em: Ferramentas > Configurações > IA generativa
Desmarque: Habilitar geração de código
🔹 Integridade Acadêmica (Plágio)
Este recurso de testes locais aplica-se a EPs sem variações. No entanto:
Individualidade: Cada estudante deve desenvolver sua própria solução.
Detecção de Similaridade: O professor utiliza ferramentas que detectam cópias, mesmo com alteração de nomes de variáveis ou espaços em branco.
1.17.1 EP01_01 📏 Três métricas de distância em PDI
Nesta atividade, você deve escrever um programa que calcule as três distâncias clássicas em PDI: Euclidiana (L2), City‑Block (L1) e Chessboard (L∞).
Leia 4 números reais que representam as coordenadas: \(A_x, A_y, B_x, B_y\).
Calcule as três distâncias utilizando as fórmulas:
Imprima os três resultados, cada um em uma linha, formatados com duas casas decimais, na ordem: Euclidiana, City‑block, Chessboard.
📌 Importante:
Utilize as funções matemáticas padrão da sua linguagem: math.sqrt, abs (ou fabs) e max.
A saída deve conter apenas os números (um por linha), sem textos adicionais.
Ver um simulador interativo para esta questão na Figura 1.11 (gráfico com arrasto dos pontos e visualização das três métricas).
1.17.1.1 🖼️ Por que isso importa? – Custo computacional
Em uma imagem 1000×1000 pixels (1 milhão de pixels), calcular a distância de cada pixel a um ponto de referência exige 1 milhão de operações. A escolha da métrica afeta o desempenho:
Métrica
Operações por pixel
Custo relativo (1M pixels)
Quando usar
Euclidiana (L2)
2 subtrações, 2 multiplicações, 1 soma, 1 sqrt
🔴 Mais custosa – sqrt é cara
Distância “real” no espaço contínuo
City‑block (L1)
2 subtrações, 2 abs, 1 soma
🟡 Moderada – sem raiz quadrada
Grids, robótica, imagens binárias
Chessboard (L∞)
2 subtrações, 2 abs, 1 max
🟢 Mais eficiente
Movimentos de peças, morfologia
A função sqrt é computacionalmente mais cara que operações como adição, subtração, multiplicação e valor absoluto. Em CPUs modernas, a diferença pode ser pequena (cerca de 1,5× a 3×), mas em sistemas embarcados ou em laços de milhões de iterações, qualquer ganho importa. Por isso, quando o objetivo é apenas comparar distâncias (ex.: encontrar o ponto mais próximo), use a distância euclidiana ao quadrado.
1.17.1.2 📋 Tarefa (especificação para VPL)
Entrada:
Uma única linha com quatro números reais: Ax Ay Bx By
Saída:
Três linhas, cada uma com um número real de duas casas decimais (Euclidiana, City‑block, Chessboard).
1.17.1.3 📌 Exemplos
Entrada
Saída
Observação
0 0 3 4
5.00 7.00 4.00
Triângulo 3‑4‑5
0 0 1 1
1.41 2.00 1.00
Diagonal unitária
Exemplo de teste de sqrt em Python, com timeit isolando cada operação:
import mathimport timeitN =50_000_000def apenas_soma(): a, b =3.0, 4.0return a + bdef soma_e_sqrt(): a, b =3.0, 4.0return math.sqrt(a*a + b*b)t_soma = timeit.timeit(apenas_soma, number=N)t_sqrt = timeit.timeit(soma_e_sqrt, number=N)print(f"Soma simples : {t_soma:.3f} s")print(f"Soma + sqrt : {t_sqrt:.3f} s")print(f"Razão (sqrt/soma) : {t_sqrt/t_soma:.2f}x")
Soma simples : 2.955 s
Soma + sqrt : 4.853 s
Razão (sqrt/soma) : 1.64x
🎮 Simulador EP01_01: Métricas de Distância no Espaço DiscretoEuclidiana vs City-block vs Chessboard
Clique e arraste os pontos A ou B no plano cartesiano ou ajuste suas coordenadas abaixo para comparar as três métricas de distância em tempo real.
📐 EUCLIDIANA (L2)
5.00
√(Δx² + Δy²)
🧱 CITY-BLOCK (L1)
7.00
|Δx| + |Δy|
🏁 CHESSBOARD (L∞)
4.00
max(|Δx|, |Δy|)
👆 Arraste os pontos A (Roxo) ou B (Laranja) na grade.
Ponto A
Ponto B
Legenda Geométrica: Linha tracejada (Euclidiana), Caminho ortogonal em L (City-block) e Destaque da dimensão máxima (Chessboard).
Euclidiana City-block Chessboard (Máx)
Figura 1.11: Simulador EP01_01: Distâncias Euclidiana, City-block e Chessboard
1.17.1.4 🐍 Python
Basta criar uma célula de código normal e inserir o código Python. A entrada pode ser simulada usando input(), que funciona normalmente.
# Espera que você digite 4 números inteiros ao executar esta célula.# No Jupyter ou Google Colab, a mágica %run -i permite que o script leia do teclado.# No terminal comum, você usaria: python3 EP01_01.py (sem o '!' e sem '%run').# %run -i EP01_01.py
# Envia 4 inteiros como entrada padrão (stdin) para o script EP01_01.py usando um pipe!echo -e "0\n0\n4\n4"| python3 EP01_01.py
5.66
8.00
4.00
TestSuite("EP01_01.py").run()
✔️ EP01_01.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_01.cases
🔍 Testando Python: EP01_01.py
✔️ Caso 1: OK✔️ Caso 2: OK✔️ Caso 3: OK✔️ Caso 4: OK✔️ Caso 5: OK
📊 Resultado: 5/5 (100.0%)
🎉 Parabéns! Todos os testes passaram.
1.17.1.5 ☕ Java
Para executar Java no Colab, você precisa usar uma célula com o prefixo %%writefile para salvar o código em arquivo, compilar e executar.
✔️ EP01_01.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_01.cases
🔍 Testando Java: EP01_01.java
✔️ Caso 1: OK✔️ Caso 2: OK✔️ Caso 3: OK✔️ Caso 4: OK✔️ Caso 5: OK
📊 Resultado: 5/5 (100.0%)
🎉 Parabéns! Todos os testes passaram.
1.17.1.6 💻 C
De forma similar, use %%writefile para salvar o código, depois compile e execute. Para C, utilizamos o compilador GCC.
# Instalar o compilador GCC e ferramentas de build# O build-essential inclui gcc, g++, make, etc.import platform, shutil, subprocessdef instalar_gcc():if shutil.which("gcc"):print("✅ GCC já disponível.");returnif platform.system() !="Linux":print("⚠️ Mac: xcode-select --install | Windows: WSL ou MinGW");returntry: import google.colab; cmd = ["apt-get", "install", "-y", "build-essential"]exceptImportError: cmd = ["sudo", "apt-get", "install", "-y", "build-essential"] subprocess.run(cmd, check=True)print("✅ build-essential instalado!")instalar_gcc()
# Compila o arquivo .c gerando o executável EP01_01# -lm é usado para linkar a biblioteca matemática (math.h) se necessário!gcc EP01_01.c -o EP01_01 -lm!echo -e "0\n0\n4\n4"| ./EP01_01
5.66
8.00
4.00
TestSuite("EP01_01.c").run()
✔️ EP01_01.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_01.cases
🔍 Testando C: EP01_01.c
✔️ Caso 1: OK✔️ Caso 2: OK✔️ Caso 3: OK✔️ Caso 4: OK✔️ Caso 5: OK
📊 Resultado: 5/5 (100.0%)
🎉 Parabéns! Todos os testes passaram.
1.17.1.7 💻 C++
De forma similar ao Java, use %%writefile para salvar o código, depois compile e execute. Lembre-se de que, no Colab, também é necessário instalar o seguinte:
# Instalar o compilador G++ para C++# O build-essential inclui o g++, make, etc.import platform, shutil, subprocessdef instalar_gpp():if shutil.which("g++"):print("✅ G++ já disponível.");returnif platform.system() !="Linux":if platform.system() =="Darwin":print("⚠️ Mac: xcode-select --install")else:print("⚠️ Windows: use WSL ou MinGW (https://www.mingw-w64.org)")returntry: import google.colab; cmd = ["apt-get", "install", "-y", "build-essential"]exceptImportError: cmd = ["sudo", "apt-get", "install", "-y", "build-essential"] subprocess.run(cmd, check=True)print("✅ Compilador C++ pronto.")instalar_gpp()
✔️ EP01_01.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_01.cases
🔍 Testando C++: EP01_01.cpp
✔️ Caso 1: OK✔️ Caso 2: OK✔️ Caso 3: OK✔️ Caso 4: OK✔️ Caso 5: OK
📊 Resultado: 5/5 (100.0%)
🎉 Parabéns! Todos os testes passaram.
1.17.1.8 🌐 JavaScript (Node.js)
Para JavaScript, use %%writefile para criar o arquivo e execute com Node:
✔️ EP01_01.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_01.cases
🔍 Testando Node.js: EP01_01.js
✔️ Caso 1: OK✔️ Caso 2: OK✔️ Caso 3: OK✔️ Caso 4: OK✔️ Caso 5: OK
📊 Resultado: 5/5 (100.0%)
🎉 Parabéns! Todos os testes passaram.
1.17.1.9 📊 R
No Colab, pode-se executar código R diretamente utilizando a mágica %%R.
O programa deve ler quatro números (x1, y1, x2, y2) e exibir a distância euclidiana com duas casas decimais.
# Instalar o R e o Rscript# O r-base instala o ambiente R completo, incluindo o Rscriptimport platform, shutil, subprocessdef instalar_r():if shutil.which("R"):print("✅ R já disponível.");returnif platform.system() !="Linux":if platform.system() =="Darwin":print("⚠️ Mac: https://cran.r-project.org/bin/macosx/")else:print("⚠️ Windows: https://cran.r-project.org/bin/windows/base/")returntry: import google.colab; cmd = ["apt-get", "install", "-y", "r-base"]exceptImportError: cmd = ["sudo", "apt-get", "install", "-y", "r-base"] subprocess.run(cmd, check=True)print("✅ Ambiente R pronto.")instalar_r()
✅ R já disponível.
Para testar da mesma forma que nos exemplos anteriores, deve-se usar a entrada padrão (stdin) no terminal ou adaptar o código conforme abaixo:
✔️ EP01_01.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_01.cases
🔍 Testando R: EP01_01.r
✔️ Caso 1: OK✔️ Caso 2: OK✔️ Caso 3: OK✔️ Caso 4: OK✔️ Caso 5: OK
📊 Resultado: 5/5 (100.0%)
🎉 Parabéns! Todos os testes passaram.
1.17.2 EP01_02 📊 Desempenho Preditivo — Métricas de ML em VC
Nesta atividade, você entrará no mundo do Aprendizado de Máquina (Machine Learning). Seu objetivo é avaliar o desempenho de um classificador binário calculando métricas a partir de uma Matriz de Confusão.
1.17.2.1 🧠 Por que a métrica certa importa?
Imagine um detector de moedas de R$ 1,00. O impacto do erro define a métrica prioritária:
Métrica
Exemplo Prático
Importância em PDI/VC
Acurácia
Contagem de Grãos
Útil quando as classes são equilibradas (ex: metade dos grãos com defeito, metade saudáveis).
Precisão
Segurança/Biometria
Crucial para evitar Falsos Positivos (ex: não permitir que um impostor acesse um sistema por erro de reconhecimento).
Sensibilidade
Saúde (Tumores)
Crucial para evitar Falsos Negativos (ex: não deixar um tumor passar despercebido em um exame de Raio-X).
F1-score
Cédulas de Dinheiro
Ideal para um equilíbrio entre não rejeitar notas verdadeiras e não aceitar notas falsas.
Imprima os resultados formatados com duas casas decimais, um por linha.
📌 Importante:
Use divisão em ponto flutuante para evitar resultados truncados.
A ordem de saída deve ser: Acurácia, Precisão, Sensibilidade e F1-score.
Veja a simulação interativa deste EP na Figura 1.12.
1.17.2.3 📌 Exemplo de Execução
Entrada
Saída
Observação
40
0.75
Acurácia
10
0.73
Precisão
15
0.80
Sensibilidade
35
0.76
F1-score
(Nota: VP=40, FN=10, FP=15, VN=35. Total de casos = 100)
🎮 Simulador EP01_02: Desempenho Preditivo — Métricas de MLMatriz de Confusão & Métricas
Selecione um cenário pré-definido ou ajuste os controles deslizantes para observar o impacto em tempo real na matriz de confusão e nas métricas de avaliação.
ENTRADAS (PARÂMETROS DA MATRIZ)
40
10
15
35
MATRIZ DE CONFUSÃO DISCRETA
Pred +
Pred −
Real +
VP
40
FN
10
Real −
FP
15
VN
35
MÉTRICAS CALCULADAS EM TEMPO REAL
Acurácia0.75
(VP + VN) / Total
Precisão0.73
VP / (VP + FP)
Sensibilidade (Recall)0.80
VP / (VP + FN)
F1-Score (Média Harmônica)0.76
2 · (P · R) / (P + R)
Total de Amostras: 100
Figura 1.12: Simulador EP01_02: Desempenho Preditivo — Métricas de ML
%%writefile EP01_02.py# sua solução
Writing EP01_02.py
TestSuite("EP01_02.py").run()
✔️ EP01_02.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_02.cases
🔍 Testando Python: EP01_02.py
⚠️ EP01_02.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
1.17.3 EP01_03 📈 Mean Average Precision (mAP) — Curva Precisão‑Sensibilidade
Na EP01_02 você viu que a escolha do limiar altera significativamente a Precisão e a Sensibilidade. O mAP (Mean Average Precision) resolve isso: avalia o modelo em vários limiares (cada limiar deve gerar uma matriz de confusão diferente) e resume o desempenho pela área sob a curva Precisão‑Sensibilidade (P‑S).
Enquanto o F1‑Score olha para um único ponto de equilíbrio, o mAP considera a curva inteira. Quanto mais próximo de 1,0, melhor o detector em todos os limiares e classes (ex.: moedas de 25, 50 e 1 real).
Para cada limiar (t), classifique as amostras: predito = 1 se confiança ≥ t, senão 0.
Calcule VP, FP, FN, VN e obtenha Precisão((t)) e Sensibilidade((t)).
Monte a curva P‑S: pares (Sensibilidade((t)), Precisão((t))), ordenados por Sensibilidade crescente.
Monotonize a Precisão: \[P_{\text{mono}}[i] = \max_{j \ge i} P[j]\]
Calcule a AP (área sob a curva monotônica) usando a regra do trapézio (aproximação mais precisa que a simples soma de Riemann): \[AP = \sum_{i=1}^{m-1} \frac{P_{\text{mono}}[i-1] + P_{\text{mono}}[i]}{2} \cdot (S[i] - S[i-1])\]
mAP = média das APs de todas as classes. Neste EP há apenas 1 classe, portanto mAP = AP.
Nota
📐 Diferença resumida:
A soma de Riemann aproxima a área por retângulos, podendo subestimar ou superestimar. A regra do trapézio usa trapézios, reduzindo o erro ao considerar a média entre os valores nos extremos do intervalo, sendo geralmente mais precisa para funções suaves por partes, como a curva Precisão‑Sensibilidade.
1.17.3.3 📋 Tarefa
Leia um inteiro n (quantidade de amostras). Em seguida leia n linhas, cada uma com: verdade (0 ou 1) e confiança (float 0.0–1.0).
Calcule e imprima, para o limiar 0.85 (índice 9 da lista):
Matriz de Confusão (VP, FN, FP, VN)
Acurácia, Precisão, Sensibilidade e F1‑Score
Em seguida, para todos os limiares, imprima:
Precisões cruas, Precisões monotônicas e Sensibilidades, separadas por ,
mAP final
1.17.3.4 📌 Importante
Limiar fixo para as métricas individuais: 0.85
Divisão segura: se denominador for zero, use 0
Formatação: duas casas decimais
Monotonize de trás para frente
A Figura 1.13 apresenta uma simulação desta questão
1.17.3.6 🐍 Dica para calcular o AP (com regra do trapézio)
def calcular_AP(verdades, confiancas, limiares): m =len(limiares) precisoes = [0.0] * m sensibilidades = [0.0] * mfor i inrange(m): p, s = calcular_metricas(verdades, confiancas, limiares[i]) precisoes[m-1-i] = p sensibilidades[m-1-i] = s prec_mono = precisoes.copy()for i inrange(m-2, -1, -1):if prec_mono[i] < prec_mono[i+1]: prec_mono[i] = prec_mono[i+1] AP =0.0for i inrange(1, m):# Regra do trapézio: média das alturas vezes a base area_trapezio = (prec_mono[i-1] + prec_mono[i]) /2.0 AP += area_trapezio * (sensibilidades[i] - sensibilidades[i-1])return precisoes, prec_mono, sensibilidades, AP
Edite as amostras (classe real e confiança) ou escolha um cenário pré-definido para visualizar a matriz de confusão, a curva P-S e o valor de mAP em tempo real.
Figura 1.13: Simulador EP01_03: Mean Average Precision (mAP) e Curva P-S
%%writefile EP01_03.py# sua solução
Writing EP01_03.py
TestSuite("EP01_03.py").run()
✔️ EP01_03.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_03.cases
🔍 Testando Python: EP01_03.py
⚠️ EP01_03.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
1.17.4 EP01_04 🖼️ Leitura e Informações de uma Imagem em Matriz
Nesta atividade, você deve escrever um programa que processe uma imagem digital representada como uma matriz de pixels em tons de cinza.
Leia dois inteiros L e C, representando o número de linhas e colunas.
Leia os L * C valores inteiros que compõem a matriz da imagem (cada valor entre 0 e 255).
Calcule e imprima as seguintes informações:
O número de linhas.
O número de colunas.
O valor do maior pixel (Máximo).
O valor do menor pixel (Mínimo).
A Média aritmética de todos os pixels.
📌 Importante:
A saída deve seguir exatamente o formato rotulado (ex: Linhas: X).
O valor da média deve ser formatado com duas casas decimais.
Ver um simulador interativo para esta questão na Figura 1.14 (grade interativa para visualização de intensidades e cálculos em tempo real).
1.17.4.1 🧠 Por que isso importa? – A Imagem como Dados
Toda imagem digital é, no fundo, uma estrutura de dados. Em tons de cinza de 8 bits, cada pixel é um valor escalar. Extrair estatísticas básicas é o primeiro passo para:
Operação
Utilidade Prática
Máximo/Mínimo
Identificar se a imagem está “lavada” (baixo contraste) ou saturada.
Média
Calcular o brilho global da cena para ajustes de exposição.
Normalização
Redimensionar os valores para intervalos como \([0, 1]\) em redes neurais.
1.17.4.2 📋 Tarefa (especificação para VPL)
Entrada:
A primeira linha contém o inteiro L (linhas).
A segunda linha contém o inteiro C (colunas).
As linhas seguintes contêm os elementos da matriz.
📊 Simulador EP01_04: Estatísticas Locais de PixelsMatriz 5x5
Clique sobre qualquer pixel da matriz para incrementar seu nível de cinza (passo de +51) ou utilize as ações pré-definidas abaixo para observar os limites e a média global.
Média Global (µ)
0.00
Valor Máx
0
Valor Mín
0
Figura 1.14: Simulador EP01_04: Estatísticas de Pixels em Matriz Discreta 5x5
%%writefile EP01_04.py# sua solução
Writing EP01_04.py
TestSuite("EP01_04.py").run()
✔️ EP01_04.cases já existe em casos/
📋 5 caso(s) carregado(s) de casos/EP01_04.cases
🔍 Testando Python: EP01_04.py
⚠️ EP01_04.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
1.17.5 EP01_05 🔄 Negativo de uma Imagem em Tons de Cinza
Nesta atividade, deve-se escrever um programa que calcule o negativo de uma imagem digital.
Leia dois inteiros L e C, representando o número de linhas e colunas.
Leia os valores inteiros que compõem a matriz da imagem.
Para cada pixel, aplique a transformação de inversão:
\[pixel_{negativo} = 255 - pixel_{original}\]
Imprima a matriz resultante, mantendo o formato original (L linhas e C colunas).
📌 Importante:
Os valores de cada linha na saída devem ser separados por um espaço em branco.
A saída deve conter apenas os números da matriz resultante.
Ver um simulador interativo para esta questão na Figura 1.15 (comparação em tempo real entre a matriz original e seu negativo).
1.17.5.1 🧠 Por que isso importa? – Inversão de Intensidade
O negativo é uma transformação linear básica que inverte a escala de brilho. É uma ferramenta essencial para o olho humano identificar detalhes claros que estão “escondidos” em fundos mais escuros, sendo amplamente utilizada em:
Aplicação
Utilidade
Imagens Médicas
Melhora a visualização de anomalias em tecidos densos (ex: Raios-X).
Astronomia
Destacar galáxias e nebulosas tênues contra o vazio do espaço.
Artes Digitais
Efeitos estéticos e preparação de máscaras de seleção.
1.17.5.2 📋 Tarefa (especificação para VPL)
Entrada:
A primeira linha contém o inteiro L.
A segunda linha contém o inteiro C.
As linhas seguintes contêm os elementos da matriz.
Saída:
A matriz invertida com L linhas e C colunas.
1.17.5.3 📌 Exemplos
Entrada
Saída
Observação
2
3
0 128 255
50 100 255
255 127 0
205 155 55
Onde era 0 (preto) vira 255 (branco)
🌓 Simulador EP01_05: Transformação de Negativo de Imagemp' = 255 - p
Clique nos pixels da matriz Original (p) para alterar seus níveis de cinza (passo de +51) e observe o efeito da inversão complementar na matriz Negativo (255 - p).
ORIGINAL (p)
NEGATIVO (255 - p)
💡A transformação de negativo mapeia tons escuros (próximos de 0) para tons claros (próximos de 255) e vice-versa, sendo útil para destacar detalhes escuros em fundos claros.
Figura 1.15: Simulador EP01_05: Transformação de Negativo de Imagem (Inversão de Intensidade)
%%writefile EP01_05.py# sua solução
Writing EP01_05.py
TestSuite("EP01_05.py").run()
✔️ EP01_05.cases já existe em casos/
📋 7 caso(s) carregado(s) de casos/EP01_05.cases
🔍 Testando Python: EP01_05.py
⚠️ EP01_05.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
Nesta atividade, você deve escrever um programa que converta pixels coloridos (RGB) para tons de cinza utilizando a ponderação fisiológica do padrão ITU-R BT.601.
Leia dois inteiros L e C, representando o número de linhas e colunas.
Leia L × C triplas de inteiros, onde cada tripla representa os canais R (Red), G (Green) e B (Blue) de um pixel.
Para cada pixel, calcule o valor de cinza (\(g\)) usando a fórmula:
\[g = \text{round}(0.299 \times R + 0.587 \times G + 0.114 \times B)\]
Imprima a matriz resultante (L linhas e C colunas) contendo os valores inteiros convertidos.
📌 Importante:
Utilize a função round() da sua linguagem para garantir o arredondamento correto para o inteiro mais próximo.
A saída deve conter apenas os valores de cinza, mantendo a estrutura de matriz (separados por espaço na linha).
Ver um simulador interativo para esta questão na Figura 1.16 (ajuste os sliders para ver como cada cor contribui para o brilho final).
1.17.6.1 🧠 Por que não usar apenas a média?
O olho humano não percebe todas as cores com a mesma intensidade. Somos muito mais sensíveis ao Verde do que ao Azul devido à nossa evolução biológica. O padrão ITU-R BT.601 utiliza pesos específicos para criar uma imagem em cinza que pareça naturalmente correta para nossa visão:
Canal
Peso
Percepção Humana
🟢 Verde
58.7%
Máxima sensibilidade (distinção de folhagens).
🔴 Vermelho
29.9%
Sensibilidade média.
🔵 Azul
11.4%
Baixa sensibilidade (tons mais escuros).
1.17.6.2 📋 Tarefa (especificação para VPL)
Entrada:
A primeira linha contém o inteiro L.
A segunda linha contém o inteiro C.
As linhas seguintes contêm triplas de inteiros R G B para cada pixel.
Saída:
A matriz de cinzas com L linhas e C colunas.
1.17.6.3 📌 Exemplos
Entrada
Saída
Observação
1
3
255 0 0 0 255 0 0 0 255
76 150 29
Note como o Verde (150) é mais brilhante que o Azul (29)
Ajuste a intensidade dos canais Vermelho (R), Verde (G) e Azul (B) para observar como cada componente contribui ponderadamente para o valor final de luminância em níveis de cinza.
AJUSTE DOS CANAIS DE COR
80
180
30
Original RGB
Tons de Cinza
💡O canal Verde (G) possui o maior peso (0.587) devido à maior sensibilidade espectral do sistema visual humano aos comprimentos de onda do verde.
Figura 1.16: Simulador EP01_06: Conversão RGB para Grayscale (Ponderação Perceptual ITU-R BT.601)
%%writefile EP01_06.py# sua solução
Writing EP01_06.py
TestSuite("EP01_06.py").run()
✔️ EP01_06.cases já existe em casos/
📋 7 caso(s) carregado(s) de casos/EP01_06.cases
🔍 Testando Python: EP01_06.py
⚠️ EP01_06.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
Nesta atividade, você deve escrever um programa que realize a segmentação de uma imagem através da limiarização (thresholding).
Leia dois inteiros L e C, representando as dimensões da matriz.
Leia um inteiro T, que será o valor do limiar (corte).
Leia os valores inteiros da matriz.
Para cada pixel \(p\), aplique a seguinte regra de binarização:
\[\text{resultado} = \begin{cases} 255 & \text{se } p > T \\ 0 & \text{se } p \le T \end{cases}\]
Imprima a matriz resultante contendo apenas os valores 0 ou 255.
📌 Importante:
Preste atenção ao operador: o pixel só vira branco (255) se for estritamente maior que \(T\).
A saída deve manter a estrutura de matriz (L linhas e C colunas).
Ver um simulador interativo para esta questão na Figura 1.17 (ajuste o slider de \(T\) para observar como os objetos são isolados do fundo).
1.17.7.1 🧠 O que é Segmentação?
A limiarização é o método mais simples de separar objetos de interesse do fundo da imagem. Ao transformar tons de cinza em preto e branco puro, criamos um mapa binário que facilita a contagem de objetos ou a identificação de formas:
Valor do Pixel (\(p\))
Condição
Resultado Final
Escuro (\(p \le T\))
Fundo/Ruído
0 (Preto)
Claro (\(p > T\))
Objeto/Destaque
255 (Branco)
1.17.7.2 📋 Tarefa (especificação para VPL)
Entrada:
A primeira linha contém o inteiro L.
A segunda linha contém o inteiro C.
A terceira linha contém o inteiro T (limiar).
As linhas seguintes contêm os elementos da matriz.
Saída:
A matriz binarizada (0 ou 255) com L linhas e C colunas.
1.17.7.3 📌 Exemplos
Entrada
Saída
Observação
2
4
128
0 100 128 200
50 129 255 64
0 0 0 255
0 255 255 0
Note que o valor 128 virou 0 (pois \(128 \le 128\))
🎛️ Simulador EP01_07: Limiarização Interativa de ImagemBinário: 0 ou 255
128
Entrada (Tons de Cinza)
Saída (Máscara Binária)
Pixels com intensidade maior que 128 (p > 128) tornam-se brancos (255); caso contrário, tornam-se pretos (0).
Figura 1.17: Simulador EP01_07: Limiarização Global Interativa (Binarização p > T)
%%writefile EP01_07.py# sua solução
Writing EP01_07.py
TestSuite("EP01_07.py").run()
✔️ EP01_07.cases já existe em casos/
📋 7 caso(s) carregado(s) de casos/EP01_07.cases
🔍 Testando Python: EP01_07.py
⚠️ EP01_07.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
1.17.8 EP01_08 🎨 Remapeamento por Faixa de Intensidade
Nesta atividade, você deve escrever um programa que aplique transformações lineares distintas a diferentes regiões de intensidade da imagem.
Leia dois inteiros L e C, representando as dimensões da matriz.
Leia os inteiros T (limiar), δ₁ (delta 1) e δ₂ (delta 2).
Leia os valores inteiros da matriz.
Para cada pixel \(p\), aplique a regra de remapeamento condicional:
\[\text{resultado} = \begin{cases} p + \delta_1 & \text{se } p < T \\ p + \delta_2 & \text{se } p \ge T \end{cases}\]
Imprima a matriz resultante com os novos valores de intensidade.
📌 Importante:
δ₁ é o offset aplicado aos pixels escuros (abaixo do limiar).
δ₂ é o offset aplicado aos pixels claros (maior ou igual ao limiar).
Os casos de teste garantem que o resultado estará sempre no intervalo válido de 0 a 255, portanto, não é necessário tratar saturação ou arredondamentos.
A saída deve manter a estrutura de matriz (L linhas e C colunas).
1.17.8.1 🧠 Transformação Condicional de Pixels
Em Processamento Digital de Imagens (PDI), frequentemente precisamos tratar regiões de forma independente. Esta técnica permite, por exemplo, clarear apenas as sombras de uma fotografia (aumentando os pixels escuros) sem estourar o brilho das áreas já claras, ou vice-versa.
Faixa de Intensidade
Condição
Operação
Pixels Escuros
\(p < T\)
\(p + \delta_1\)
Pixels Claros
\(p \ge T\)
\(p + \delta_2\)
1.17.8.2 📋 Tarefa (especificação para VPL)
Entrada:
A primeira linha contém os inteiros L e C.
A segunda linha contém os inteiros T, δ₁ e δ₂.
As linhas seguintes contêm os elementos da matriz.
Saída:
A matriz transformada com L linhas e C colunas, com valores separados por espaço.
1.17.8.3 📌 Exemplos
Entrada
Saída
Observação
2 4 128 60 -40 0 100 150 255 80 128 200 30
60 160 110 215 140 88 160 90
Pixels < 128 somam 60. Pixels ≥ 128 subtraem 40.
🎛️ Simulador EP01_08: Remapeamento por Faixa Condicionalp < T → p + δ₁ | p ≥ T → p + δ₂
Ajuste o limiar de separação (T) e os deslocamentos de brilho (δ₁ e δ₂) para aplicar transformações de intensidade diferenciadas nas regiões escuras e claras da imagem.
128
+60
-40
Entrada Original (p)
Resultado Transformado
Regra ativa: p < 128 → p + (+60) | p ≥ 128 → p + (-40)
Figura 1.18: Simulador EP01_08: Remapeamento por Faixa Condicional (Brilho e Contraste por Limiar)
%%writefile EP01_08.py# sua solução
Writing EP01_08.py
TestSuite("EP01_08.py").run()
✔️ EP01_08.cases já existe em casos/
📋 8 caso(s) carregado(s) de casos/EP01_08.cases
🔍 Testando Python: EP01_08.py
⚠️ EP01_08.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
1.17.9 EP01_09 🏁 Padrão de Tabuleiro: Matriz de Xadrez
Nesta atividade, você deve escrever um programa que gere uma imagem sintética no padrão de um tabuleiro de xadrez.
Leia dois inteiros L (linhas) e C (colunas).
Gere uma matriz onde os valores alternam entre 0 (preto) e 1 (branco).
A lógica de preenchimento deve seguir a regra da paridade:
O elemento na posição \((0,0)\) é sempre 0.
Um pixel na posição \((i, j)\) será 1 se a soma dos índices \((i + j)\) for ímpar.
Um pixel na posição \((i, j)\) será 0 se a soma dos índices \((i + j)\) for par.
📌 Importante:
As cores devem alternar corretamente tanto horizontalmente quanto verticalmente.
A saída deve ser a matriz impressa linha por linha, com os elementos separados por um espaço.
Ver um simulador interativo para esta questão na Figura 1.19 (ajuste as dimensões para visualizar a construção da malha e a saída textual correspondente).
1.17.9.1 🧠 Padrões Sintéticos
Criar padrões geométricos é um exercício fundamental para dominar a lógica de índices em matrizes. Em Processamento Digital de Imagens, o padrão de xadrez não é apenas estético; ele é amplamente utilizado para:
Aplicação
Utilidade
Calibração de Câmera
Estimar parâmetros intrínsecos e extrínsecos da lente.
Correção de Distorção
Identificar e corrigir o efeito de “barril” ou “almofada” em lentes grande-angulares.
Mapeamento 3D
Projetar padrões conhecidos para reconstruir superfícies em sistemas de luz estruturada.
1.17.9.2 📋 Tarefa (especificação para VPL)
Entrada:
Uma linha contendo o inteiro L (linhas).
Uma linha contendo o inteiro C (colunas).
Saída:
A matriz de xadrez com L linhas e C colunas, impressa com espaços entre os elementos.
1.17.9.3 📌 Exemplos
Entrada
Saída
Observação
3
4
0 1 0 1
1 0 1 0
0 1 0 1
Note que cada linha começa com o inverso da anterior
Altere o número de linhas (L) e colunas (C) para observar como a alternância de paridade das coordenadas da grade constrói a matriz binária de xadrez.
×
GRADE GRÁFICA
SAÍDA ESPERADA (VALORES)
Figura 1.19: Simulador EP01_09: Gerador de Padrão Xadrez (Lógica de Paridade (i + j) % 2)
%%writefile EP01_09.py# sua solução
Writing EP01_09.py
TestSuite("EP01_09.py").run()
✔️ EP01_09.cases já existe em casos/
📋 7 caso(s) carregado(s) de casos/EP01_09.cases
🔍 Testando Python: EP01_09.py
⚠️ EP01_09.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
1.17.10 EP01_10 📄 Metadados: Leitura de Arquivo PGM
Nesta atividade, você deve ler um arquivo de imagem no formato PGM (Portable Gray Map) e extrair suas dimensões a partir do cabeçalho.
O formato PGM (P2) é um arquivo de texto simples (ASCII) que armazena imagens em tons de cinza.
O arquivo possui um cabeçalho estruturado da seguinte forma:
Versão: O identificador P2.
Comentários: Linhas opcionais que começam com # (devem ser ignoradas).
Dimensões: Dois inteiros representando Largura e Altura.
Máximo: Um inteiro representando a intensidade máxima (geralmente 255).
Após o cabeçalho, seguem-se os dados dos pixels.
📌 Importante:
Leitura de Arquivo: Você deve abrir o arquivo indicado no exemplo utilizando a função open() do Python.
Ordem de Saída: Ao contrário da ordem presente no arquivo, a saída esperada deve estar no formato de tupla: (Altura, Largura, Canais).
Como arquivos PGM são em tons de cinza, o número de Canais é sempre 1.
Ver um simulador interativo para esta questão na Figura 1.20 (ajuste as dimensões para ver como o cabeçalho ASCII é gerado).
1.17.10.1 🧠 Entendendo o formato PGM
O formato PGM é um dos mais simples para processamento de imagens. Por ser em texto puro, ele permite visualizar os metadados e até os valores dos pixels abrindo o arquivo em um bloco de notas:
Componente
Exemplo
Significado
Magic Number
P2
Identifica que é um PGM em formato de texto (ASCII).
Comentário
# CREATOR...
Linha informativa ignorada pelo processador.
Dimensões
397 343
397 colunas (Largura) e 343 linhas (Altura).
Intensidade
255
Define o valor do branco puro (escala de 0 a 255).
1.17.10.2 📋 Tarefa (especificação para VPL)
Entrada:
Nenhuma entrada via teclado. O programa deve ler o arquivo "aula01fig03b.pgm" presente no diretório de execução.
Saída:
Uma tupla contendo (Altura, Largura, 1).
1.17.10.3 📌 Exemplos
Nome do Arquivo
Saída Esperada
Observação
“aula01fig03b.pgm”
(343, 397, 1)
Note a inversão da ordem: Altura primeiro
📄 Simulador EP01_10: Estrutura do Arquivo PGMASCII P2 & Format (H, W, C)
Altere as dimensões de largura (W) e altura (H) para observar a montagem dinâmica do cabeçalho PGM e o formato da tupla do array resultante em Python (Linhas × Colunas × Canais).
DEFINIÇÕES DA IMAGEM
💡 Atenção à convenção: O cabeçalho PGM declara primeiramente W H (Largura × Altura), enquanto o array em Python/NumPy reporta a tupla como (H, W, C) (Linhas × Colunas × Canais).
CONTEÚDO DO ARQUIVO (.PGM)
P2
# CREATOR: UFABC PDI / EP01_10
397343
255
120 134 210 0 85 255 ...
Saída da Função mm.readImg (Formato do Array):
(343, 397, 1)
Figura 1.20: Simulador EP01_10: Estrutura de Arquivo PGM (Cabeçalho ASCII P2 e Mapeamento para Tupla Python)
NotaNota
O arquivo necessário para este EP será baixado automaticamente do repositório por meio do seguinte código:
import os, urllib.requestBASE_URL ="https://raw.githubusercontent.com/fzampirolli/pdi-vc/master/all/cap01/imagens"file="aula01fig03b.pgm"opener = urllib.request.build_opener()opener.addheaders = [('User-agent', 'Mozilla/5.0')]urllib.request.install_opener(opener)for f in [file]:ifnot os.path.exists(f): url =f"{BASE_URL}/{f}"try: urllib.request.urlretrieve(url, f)print(f"✅ Arquivo baixado: {f}")except urllib.error.HTTPError as e:print(f"❌ Erro {e.code}: Não encontrado em {url}")
%%writefile EP01_10.py# sua solução
Writing EP01_10.py
TestSuite("EP01_10.py").run()
✔️ EP01_10.cases já existe em casos/
📋 1 caso(s) carregado(s) de casos/EP01_10.cases
🔍 Testando Python: EP01_10.py
⚠️ EP01_10.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
1.17.11 EP01_11 📈 Análise de Vizinhança: Filtro de Máximo 1D
Nesta atividade, você deve implementar um filtro morfológico simples de máximo operando em um sinal unidimensional (vetor).
Leia um inteiro n, representando o tamanho do vetor.
Leia os n elementos inteiros que compõem o vetor original v1.
Crie um novo vetor v2, onde cada posição \(i\) é o resultado da comparação entre o elemento atual e seus vizinhos imediatos:
\[v2[i] = \max(v1[i-1],\; v1[i],\; v1[i+1])\]
📌 Importante:
Bordas: Nas extremidades do vetor (índices \(0\) e \(n-1\)), a vizinhança possui apenas dois elementos (o próprio e o único vizinho disponível). No índice \(0\), compare apenas \(v1[0]\) e \(v1[1]\). No último índice, compare apenas \(v1[n-2]\) e \(v1[n-1]\).
Saída: Imprima o cabeçalho “v2:” seguido pelos valores do vetor resultante, um por linha.
Ver um simulador interativo para esta questão na Figura 1.21 (passe o mouse sobre os resultados para visualizar a janela de vizinhança utilizada no cálculo).
1.17.11.1 🧠 Por que analisar vizinhos?
Em processamento de imagens, o valor de um pixel raramente é isolado; ele depende do contexto ao seu redor. O Filtro de Máximo é a base da operação de Dilatação em morfologia matemática, servindo para:
Função
Efeito Visual
Realce
Expande estruturas brilhantes e “engorda” objetos claros.
Remoção de Ruído
Elimina pequenos pontos pretos (ruído de “sal e pimenta” escuro).
Preenchimento
Fecha pequenos buracos ou lacunas em formas binárias.
1.17.11.2 📋 Tarefa (especificação para VPL)
Entrada:
Um inteiro n.
Nas linhas seguintes, os n elementos inteiros do vetor.
Saída:
A stringv2: na primeira linha.
Nas linhas seguintes, cada elemento de v2 (um por linha).
1.17.11.3 📌 Exemplos
Entrada
Saída
Observação
5
10
20
5
30
15
v2:
20
20
30
30
30
No índice 1: max(10, 20, 5) = 20
📈 Simulador EP01_11: Filtro de Máximo Local 1DJanela 1x3
Clique nos elementos de v1 (Entrada) para gerar novos valores individuais ou passe o mouse sobre as células de v2 (Resultado) para inspecionar a janela local de vizinhança.
Vetor v1 (Entrada)
⬇️
Vetor v2 (Resultado do Máximo)
Passe o cursor do mouse sobre uma célula do vetor v2 para analisar a janela de máximo local.
Figura 1.21: Simulador EP01_11: Filtro de Máximo Local 1D (Vizinhança 1x3 com Condição de Borda)
%%writefile EP01_11.py# sua solução
Writing EP01_11.py
TestSuite("EP01_11.py").run()
✔️ EP01_11.cases já existe em casos/
📋 7 caso(s) carregado(s) de casos/EP01_11.cases
🔍 Testando Python: EP01_11.py
⚠️ EP01_11.py: Arquivo sem conteúdo (menos de 3 linhas). Testes ignorados.
BRADSKI, Gary; KAEHLER, Adrian. Learning OpenCV: Computer vision with the OpenCV library. [S.l.]: " O’Reilly Media, Inc.", 2008.
GONZALEZ, R. C.; WOODS, R. E. Digital Image Processing. 4th. ed. New York: Pearson, 2018.
ITU-R. Recommendation BT.601: Studio encoding parameters of digital television for standard 4:3 and wide screen 16:9 aspect ratios. https://www.itu.int/rec/R-REC-BT.601/, 2011.
KNUTH, Donald E. Literate Programming. The Computer Journal, v. 27, n. 2, p. 97–111, 1984.
LEWIS, Patrick et al. Retrieval-augmented generation for knowledge-intensive nlp tasks. Advances in neural information processing systems, v. 33, p. 9459–9474, 2020.