Você está na página 1de 149

Máquina Prática

Aprendizagem e
Processamento de
imagem
Para reconhecimento facial, detecção de
objetos,
e reconhecimento de padrões usando
Python
-
Himanshu Singh
Máquina Prática
Aprendizagem e
Imagem
Em
processamento
Para reconhecimento facial,
objeto
Detecção e padrão
Reconhecimento usando
Python

Aprendizado de máquina prático e processamento de imagens


Himanshu Singh
Allahabad, Uttar Pradesh, Índia

ISBN-13 (pbk): 978-1-4842-4148-6 \ 琀 ISBN-13 (eletrônico): 978-1-4842-4149-3


https://doi.org/10.1007/978-1-4842-4149-3
 

Número de controle da Biblioteca do Congresso: 2019933848


 

Copyright © 2019 por Himanshu Singh


Este trabalho está sujeito a direitos autorais. Todos os direitos são reservados pela
Editora, seja todo ou parte do material em questão, especificamente os direitos de
tradução, reimpressão, reutilização de ilustrações, recitação, transmissão, reprodução
em microfilmes ou de qualquer outra forma física, e transmissão ou armazenamento de
informações e recuperação, adaptação eletrônica, software de computador ou por
metodologia semelhante ou diferente agora conhecida ou desenvolvida no futuro.
Nomes de marcas registradas, logotipos e imagens podem aparecer neste livro. Em vez
de usar um símbolo de marca registrada em todas as ocorrências de um nome,
logotipo ou imagem de marca registrada, usamos os nomes, logotipos e imagens
apenas de forma editorial e para o benefício do proprietário da marca registrada, sem
intenção de violação da marca registrada.
O uso nesta publicação de nomes comerciais, marcas registradas, marcas de serviço e
termos semelhantes, mesmo que não sejam identificados como tal, não deve ser
interpretado como uma expressão de opinião se estão ou não sujeitos a direitos de
propriedade.
Embora os conselhos e informações neste livro sejam considerados verdadeiros e
precisos na data de publicação, nem os autores, nem os editores, nem a editora podem
aceitar qualquer responsabilidade legal por quaisquer erros ou omissões que possam
ser cometidos. O editor não oferece nenhuma garantia, expressa ou implícita, com
relação ao material aqui contido.
Diretor administrativo, Apress Media LLC: Welmoed Spahr
Editor de aquisições: Celestin Suresh John
Editor de Desenvolvimento: Matthew Moodie
Editor Coordenador: Aditee Mirashi
Capa desenhada por eStudioCalamar
Imagem da capa desenhada por Freepik (www.freepik.com)
Distribuído para o comércio de livros em todo o mundo pela Springer Science + Business Media New
York,
233 Spring Street, 6th Floor, New York, NY 10013. Telefone 1-800-SPRINGER, fax (201)
348-4505, e-mail orders-ny@springer-sbm.com ou visite www.springeronline.com.
Apress Media, LLC é uma Califórnia LLC e o único membro (proprietário) é Springer
Science + Business Media Finance Inc (SSBM Finance Inc). SSBM Finance Inc é uma
empresa de Delaware .
Para obter informações sobre traduções, envie um e-mail para rights@apress.com ou
visite http: //www.apress. com / permissões-direitos.
Os títulos da Apress podem ser adquiridos a granel para uso acadêmico, corporativo ou
promocional. Versões e licenças de e-books também estão disponíveis para a maioria
dos títulos. Para obter mais informações, consulte nossa página de vendas em massa de
impressão e e-book em http://www.apress.com/bulk-sales.
Qualquer código-fonte ou outro material suplementar citado pelo autor neste livro está
disponível para leitores no GitHub por meio da página do produto do livro, localizada
em www.apress.com/978-1-4842-4148-6. Para obter informações mais detalhadas, visite
http://www.apress.com/source-code.
Impresso em papel sem ácido

Índice
Sobre o autor \ ix

Sobre o Revisor Técnico \ xi

Agradecimentos \ xiii

Introdução \ xv

Capítulo 1: Ambiente de configuração 1

Instale o Anaconda 1 Windows \ 2 macOS \ 4 Ubuntu \ 4 Instalar OpenCV 4 Instalar Keras \ 5 Teste as instalações \ 5 Ambientes

Virtuais 6

Capítulo 2: Introdução ao Processamento de Imagens 7

Imagens \ 8

Pixels \ 8

Resolução da imagem \ 9

PPI e DPI 10

I bit \ 10
Imagens bitmap \ 10

Compressão sem perdas \ 11

Compressão com perdas \ 12

iii

Índice
Formatos de arquivo de imagem \ 12

Espaços de cores \ 13

RGB 14

XYZ \ 15

HSV / HSL \ 17

LAB \ 18

LCH \ 18

YPbPr \ 19

YUV \ 20

YIQ \ 21

Conceitos de imagem avançados 21

Curva de Bezier \ 22

Elipsóide \ 23

Correção gama 24

Índice de similaridade estrutural \ 25

Deconvolução 25

Homografia \ 26

Convolução 27

Capítulo 3: Noções básicas de Python e Scikit Image \ 29

Noções básicas de Python \ 30

Variáveis e tipos de dados 30

Estruturas de dados \ 33

Declarações de fluxo de controle \ 34

Declarações condicionais \ 37

Funções 38

Imagem Scikit \ 40

Carregando e visualizando uma imagem \ 41

Obtendo resolução da imagem \ 42

iv

Índice
Olhando para os valores de pixel 43
C t d E d C \ 43
Convertendo Espaço de Cores \ 43

Salvando uma imagem 53

Criação de desenhos básicos \ 53

Fazendo Correção Gama \ 57

Rotação, deslocamento e dimensionamento de imagens \ 59

Determinando a similaridade estrutural \ 60

Capítulo 4: Processamento de imagem avançado usando OpenCV 63

Combinando duas imagens \ 64 Mudança de contraste e brilho \ 66 Adicionando Texto às Imagens 68 Suavização de imagens \

71 Filtro de mediana \ 71 Filtro Gaussiano 71 Filtro Bilateral \ 72 Mudando o formato das imagens \ 75 Efetuando a limitação da

imagem \ 80 Calculando Gradientes 84 Executando Equalização de Histograma \ 87

Capítulo 5: Processamento de Imagem com Aprendizado de Máquina \ 89


Mapeamento de características usando o algoritmo SIFT 90

Etapa 1: construção do espaço \ 91

Etapa 2: Diferença entre as Gaussianas 91

Etapa 3: Pontos importantes \ 92

Etapa 4: Pontos-chave sem importância 92

Etapa 5: Orientação dos pontos-chave \ 92

Etapa 6: Principais recursos \ 93

Índice
     

Registro de imagem usando o algoritmo RANSAC \ 98


105
estimativa_afina \ 

105
residual_lengths 

Processando as Imagens \
106

O Código Completo \
106

Classificação de imagensUsando Redes Neurais Artificiais \


110
Classificação de imagensUsando CNNs \
118
Classificação de imagensUsando Abordagens de aprendizado de máquina \ 125

Árvores de Decisão \ 126


 

Support Vector Machines \


127

Regressão Logística \
127
127
Código \ 

Termos importantes 130


 
Capítulo 6: Casos de uso em tempo real \ 133

Encontrando Palm Lines 133

Detectando faces \ 135

Reconhecendo rostos \ 138

Rastreando Movimentos 141


D t ã d i t \ 143
Detecção de pistas \ 143

Apêndice: Conceitos e Terminologia Importantes 151

Adaboost \ 151

XGBoost 152

Pulso acoplado Redes Neurais \ 153

Gradiente descendente \ 154

Gradiente Descendente Estocástico 155

AdaDelta 156

Canny Edge Detector 156

vi

Índice
Transformação Sobel \ 157

Haar Cascade 158

Reconhecimento facial LBPH \ 158

Momentos da imagem \ 158

Contornos de imagem 159

Função Chessboard Corners 160

Calibrar função da câmera 161

Função de transformação de perspectiva \ 162

Índice 165
vii

Sobre o autor
Himanshu Singh tem mais de 6 anos
de experiência como profissional de
ciência de dados. Atualmente, ele é um
cientista de dados sênior no V-Soft
Labs. Ele oferece treinamento
corporativo em ciência de dados,
aprendizado de máquina e
aprendizado profundo. Ele também é
um membro do corpo docente
visitante em análises no Narsee
Monjee Institute of Management
Studies, considerado um dos
institutos de gestão de prêmios na Índia. Ele é o fundador da
Black Feathers Analytics e Rise of Literati Clubs.  

ix
Sobre o Revisor Técnico
Santanu Pattanayak atualmente
trabalha na GE, Digital, como cientista
de dados da equipe e é autor de Pro Deep
Learning with TensorFlow: A
Mathematical Approach to Advanced
Artificial Intelligence in Python . Ele tem
aproximadamente
12 anos de experiência geral de
trabalho, com oito anos de experiência
no campo de análise de dados / ciência
de dados, e também tem experiência em
desenvolvimento e tecnologias de banco
de dados. Antes de ingressar na GE,
Santanu trabalhou em empresas como
RBS, Capgemini e IBM. Ele se formou em
engenharia elétrica pela Universidade
de Jadavpur, Calcutá,
e é um ávido entusiasta da matemática. Santanu está atualmente
cursando um mestrado em ciência de dados no Instituto Indiano
de Tecnologia de Hyderabad. Ele também dedica seu tempo a
hackathons de ciência de dados e competições Kaggle, nas quais
está entre os 500 melhores do mundo. Santanu nasceu e foi
criado em West Bengal, Índia, e atualmente reside em Bangalore,
Índia, com sua esposa.  

XI

Agradecimentos
Em primeiro lugar, agradeço à Equipe Apress, Celestian John e
Aditee Mirashi, por me fornecerem uma plataforma para
contribuir com meu conhecimento de processamento de
imagens e compartilhá-lo com os leitores. Em segundo lugar,
agradeço a meus colegas, sem os quais este livro não teria sido
possível: Aravind Kota, Yamuna e meu chefe e mentor, Yunis
Ahmad Lone. Agradeço também aos meus alunos. Eles me
ajudaram a ver quais questões são desafiadoras para eles e me
possibilitaram criar um meio específico de explicar os conceitos
a eles de uma maneira que facilite seu aprendizado.

Por último, mas não menos importante, agradeço a minha esposa,


Shikha Singh. A constante dela
o apoio e a ajuda permitiram que este projeto se tornasse
realidade. Ela me ajudou em todos os aspectos da escrita deste
livro, às vezes revisando e escrevendo detalhes técnicos ela
mesma.
Muito obrigado a todos pelo apoio constante.

xiii

Introdução
Aprendizado de máquina prático e processamento de imagem
oferece aos leitores uma visão profunda dos fundamentos do
processamento de imagem e várias metodologias e algoritmos
de processamento de imagem, aplicativos que usam várias
bibliotecas Python e implementação de caso de uso em
tempo real usando abordagens de aprendizado de máquina.

O livro começa com uma discussão do ambiente de


configuração para diferentes sistemas operacionais, apresenta a
terminologia básica de processamento de imagem e explora
conceitos úteis do Python para a aplicação de algoritmo. isto
em seguida, investiga vários algoritmos de processamento de
imagem e implementação prática deles em Python usando duas
bibliotecas: Scikit Image e OpenCV. A seguir, métodos avançados
de aprendizado de máquina e aprendizado profundo são
apresentados para processamento e classificação de imagens.
São explicados conceitos como Adaboost, XG Boost, redes neurais
convolucionais e mais, para aplicativos específicos de imagem.
Posteriormente, é descrito o processo para fazer modelos em
tempo real e, em seguida, implantá-los.
Todos os conceitos do livro são explicados usando cenários
da vida real . Ao final do livro, os leitores devem ser capazes de
aplicar técnicas de processamento de imagem e fazer modelos
de aprendizado de máquina para aplicativos personalizados.

xv

CAPÍTULO 1

Ambiente de
Configuração
Neste capítulo, preparamos nosso sistema para executar o
código incluído neste livro. Vejamos como instalar o
seguinte:
• \ Anaconda
• \ OpenCV
• \ Keras

Além dos dois últimos pacotes da lista, a maior parte do que


precisamos vem pré-instalado com o Anaconda. Vamos
começar com o Anaconda e, a seguir, com o OpenCV e o Keras.

Instale o Anaconda
A página de instalação do Anaconda proclama que é “A
plataforma de ciência de dados mais popular do Python”. Usar o
Anaconda, instalar software de suporte, configurar ambientes
virtuais e assim por diante, são todos muito fáceis, e o pacote
vem com um dos melhores ambientes de desenvolvimento
integrado (IDEs) para ciência de dados Python: Jupyter
Notebook. O Jupyter não apenas ajuda você a escrever código
Python, mas também torna seu código bonito e apresentável.
Então, vamos começar com a instalação do Anaconda.

© Himanshu Singh 2019 1


H. Singh, Practical Machine Learning and Image
Processing ,
https://doi.org/10.1007/978-1-4842-4149-3_1

Capítulo 1 Ambiente de configuração

janelas
Se você estiver usando o Windows, este é o processo:
\ 1. \ Vá para www.anaconda.com .

\ 2. \ No lado superior direito da tela, está o


botão Downloads. Clique.

\ 3. \ Role para baixo e você verá duas versões


do Anaconda: Python versão 3.7 e Python
versão 2.7. Na caixa da versão Python 3.7,
selecione Instalador gráfico de 64 bits
(selecione a opção de 32 bits , se o seu
sistema for de 32 bits ).

\ 4. \ Aguarde o download terminar


e clique duas vezes no arquivo de
instalação.
\ 5. \ Conclua a instalação e reinicie o sistema.
\ 6. \ Agora, abra o menu Iniciar, procure o
prompt do Anaconda e selecione-o. Um shell
chamado Anaconda Prompt aparece. Digite
Jupyter Notebook dentro do shell e você verá
uma tela como a exibida na Figura 1-1 .

Capítulo 1 Ambiente de configuração

Figura 1-1.  Tela de abertura

\ 7. \ No canto superior direito da guia Arquivos,


você verá o menu suspenso Novo. Clique na
seta apontando para baixo e selecione
Python 3. Agora você está pronto para
codificar (Figura 1-2 )!

Figura 1-2.  Um novo script Python


3

Capítulo 1 Ambiente de configuração

Mac OS
Se você estiver usando macOS, aqui está o processo de instalação do
Anaconda:

\ 1. \ Baixe o Anaconda para macOS como faria


para o Windows.

\ 2. \ Clique duas vezes no arquivo .pkg e siga os


procedimentos de instalação.

\ 3. \ Abra seu terminal e digite Jupyter


Notebook. Você verá a mesma tela
mostrada na Figura 1-1 .

Ubuntu
O processo de download do Anaconda no Ubuntu é o seguinte:

\ 1. \ Baixe o Anaconda para Linux como


fez para o Windows.

\ 2. \ Vá para a pasta de instalação e


digite bash
Anaconda-latest-Linux-x86_64.sh.

\ 3. \ Siga os procedimentos de instalação, abra


seu terminal e digite Jupyter Notebook.
Você verá a mesma tela mostrada na Figura
1-1 .

Instale OpenCV
Agora que instalamos o Anaconda e o Jupyter Notebook. A
próxima coisa a fazer é instalar o software de suporte. Para
OpenCV, faça o seguinte:
\ 1. \ Abra o prompt do Anaconda.
\ 2. \ Digite conda install -c conda-forge opencv.

Capítulo 1 Ambiente de configuração


\ 3. \ Você também pode digitar conda install
-c condaforge / label / broken opencv.
\ 4. \ Após alguns minutos, o OpenCV será
instalado em seu ambiente.

Instale Keras
Para instalar o Keras, siga estes procedimentos:
\ 1. \ Abra o prompt do Anaconda.

Digite conda install -c conda-forge keras.

\ 2. \ Após alguns minutos, o Keras será


instalado em seu ambiente.

Teste as instalações
Antes de prosseguir, você precisa testar as instalações da seguinte
maneira:
\ 1. \ Abra o Jupyter Notebook.
\ 2. \ Abra um novo bloco de notas Python 3.

\ 3. \ Digite import cv2. Se não receber uma


mensagem de erro, o OpenCV foi instalado
perfeitamente. Se ocorrer um erro, você fez
algo errado durante a instalação ou pode
haver um problema de compatibilidade. Para
retificação, reinicie o processo de instalação
ou consulte a página de documentação do
OpenCV.

Capítulo 1 Ambiente de Configuração

\ 4. \ Digite import keras. Se você não receber um erro,


 
então Keras foi instalado perfeitamente. Se um erro
 
vem, ou você fez algo errado durante
 
a instalação, ou pode haver uma compatibilidade
 
questão. Para retificação, reinicie o processo de
 
instalação ou consulte a página de documentação do Keras.
Ambientes Virtuais
Agora que instalamos o software de que precisamos, vamos dar
uma olhada nos ambientes virtuais. Os ambientes virtuais são
muito importantes quando você deseja desenvolver vários
projetos. O que devemos fazer se estamos desenvolvendo
um produto usando Python 3, mas queremos criar outro projeto
usando Python 2.7? Se fizermos isso diretamente, podemos
encontrar problemas porque diferentes versões do Python estão
instaladas. Ou podemos criar um ambiente virtual, instalar o
Python 2.7 e desenvolver o produto dentro desse ambiente.
Independentemente do que você desenvolve dentro de um
ambiente virtual, isso nunca influencia nenhum código fora do
ambiente. Vamos ver como podemos criar um ambiente virtual:

\ 1. \ Digite conda create -n environment_name


python = version anaconda. No lugar de
environment_name, digite qualquer nome
que deseja dar ao seu ambiente. No lugar da
versão, digite qualquer versão do Python
que deseja usar (por exemplo, 2.7, 3.5, 3.6 e
assim por diante).
\ 2. \ Agora que criamos o ambiente, temos que
ativá-lo. Fazemos isso digitando source
activate environment_name.
\ 3. \ Agora podemos abrir o Jupyter
Notebook e começar a trabalhar neste
ambiente.
\ 4. \ Para desativar o ambiente, digite source deactivate.

CAPÍTULO 2

Introdução ao
processamento de
imagens
Neste capítulo, examinamos exatamente o que é uma imagem e
suas propriedades relacionadas. Ao final do capítulo, você deve
ter uma compreensão dos seguintes conceitos:
• \Imagens
• \Píxeis
• \Resolução de imagem
• \Pixels por polegada (PPI) e pontos por polegada (DPI)
• \Imagens bitmap
• \Compressão sem perdas e compressão com perdas
• \Diferentes formatos de arquivo de imagem
• \Diferentes tipos de espaços de cores

• \Conceitos avançados de imagem

© Himanshu Singh 2019 7


H. Singh, Practical Machine Learning and Image
Processing ,
https://doi.org/10.1007/978-1-4842-4149-3_2

Capítulo 2 Introdução ao processamento de imagens

Imagens
A representação visual de um objeto da vida real (uma pessoa
ou qualquer outro objeto) em uma forma bidimensional é
chamada de imagem . Uma imagem nada mais é que uma
coleção de pixels em diferentes espaços de cores. A Figura 2-1 é
um exemplo de imagem normal.

Figura 2-1.  Imagem normal


Píxeis
Você pode pensar em uma imagem completa como um conjunto
que consiste em pequenas amostras. Essas amostras são
chamadas de pixels . Eles são os menores elementos em qualquer
imagem digital. Você já aumentou o zoom em uma imagem a
ponto de ver pequenos quadrados? Esses são pixels. Portanto,
pixels são subamostras de uma imagem que, quando
combinados, nos dão a imagem completa. A Figura 2-2 mostra
como os pixels, com várias cores, podem ficar.

Capítulo 2 Introdução ao processamento de imagens

Figura 2-2.  Pixels de várias cores (Fonte: www.freeimages.co.uk )

Resolução de imagem
A resolução da imagem é o número de pixels presentes em
uma imagem. Quanto maior o número de pixels, melhor será a
qualidade. As resoluções de imagem são descritas, por
exemplo, como 320 × 240, 640 × 480, 800 × 600, 1024 × 768 e
assim por diante.
Isso significa, por exemplo, que existem 1024 colunas de pixels e
768 linhas de pixels. O número total de pixels é obtido
multiplicando os dois números, o que nos dá 786.432 pixels. A
Figura 2-3 mostra representações comparativas de diferentes
resoluções de imagem.
1920 x 1080

1280 x 720
640x480

Figura 2-3.  Resolução comparativa da imagem


(Fonte: www. Freeimages.co.uk )

Capítulo 2 Introdução ao processamento de imagens

PPI e DPI
Conforme observado no início do capítulo, PPI significa
"pixels por polegada", enquanto DPI significa "pontos por
polegada". Eles são as unidades para medir a resolução da
imagem.
Se considerarmos uma polegada de uma imagem, o número
de pixels quadrados que podemos ver no seu interior é
representado por PPI. O DPI, por outro lado, está relacionado à
impressão. Quando imprimimos uma imagem e olhamos para
uma polegada da impressão, o número de pontos de tinta usados
é representado por DPI.
Conforme mostrado na Figura 2-4 , o PPI parece mais suave, enquanto o
DPI é mais nítido.

Figura 2-4.  Representações PPI e DPI

Imagens bitmap
Em geral, quando olhamos para os valores dos pixels, eles são
um intervalo de inteiros. Mas, quando convertemos o intervalo
de inteiros em bytes, temos uma imagem bitmap.
Um tipo de bitmap é uma imagem binária em que cada pixel
tem um de dois números: zero ou um. Eles representam preto
ou branco e costumam ser usados para armazenar imagens de
maneira eficiente. A Figura 2-5 mostra uma imagem de bitmap
binário.

10

Capítulo 2 Introdução ao processamento de imagens

Figura 2-5.  Representação de bitmap binário da Figura 2-1

Compressão sem perdas


Quando queremos reduzir o tamanho de um arquivo (que pode
ser uma imagem), mas não queremos comprometer a qualidade,
esse tipo de compressão é chamada de compressão sem perdas .
O arquivo compactado pode ser salvo, mas quando necessário,
durante o processo de descompactação, todas as informações são
restauradas e obtemos a imagem real (Figura 2-6 ). Esse primeiro
tipo de compactação dá prioridade às informações contidas no
arquivo - especialmente ao compactar texto, onde não podemos
perder nem mesmo uma única informação.

Original Comprimido Restaurado

Figura 2-6.  Processo de compressão sem perdas

11
Capítulo 2 Introdução ao processamento de imagens

Compressão com perda


Com a compactação com perdas, por outro lado, alguns dados
podem ser perdidos. A compactação com perdas prioriza a
economia de espaço, em vez da precisão do arquivo
recuperado. Alguns arquivos, como aqueles que contêm música
ou imagens, podem ser cortados e ainda assim não serão
afetados pela compactação. Pode haver alguma perda, mas não
é preocupante (Figura 2-7 ).

parcialmente
Original compactado
Restaurado

Figura 2-7.  Processo de compressão com perdas

Formatos de arquivo de imagem


A seguir estão alguns dos formatos de imagem mais
amplamente usados, que são explicados na Tabela 2-1 :

• \ JPEG : Joint Photographic Experts Group


• \ JPEG2000 : Novo formato JPEG desenvolvido em 2000
• \ TIFF : Formato de arquivo de imagem marcada
• \ GIF : Formato de intercâmbio de gráficos
• \ BMP : Bitmap
• \ PNG : Portable Network Graphics
• \ WebP : Formato desenvolvido pelo Google

• \ SVG : Gráficos vetoriais escaláveis

12

Capítulo 2 Introdução ao processamento de imagens

Tabela 2-1.  Descrições e usos de diferentes tipos de imagem


Formato de imagemDescrição Usar
     

JPEG Compressão com perdas de imagens brutas Fotografi


   
pinturas
JPEG2000
 
Forma otimizada de JPEG; melhor compressão Vigilânc
 
Razão; compressão sem perdas e com perdas
TIFF Compressão sem perdas; pode ser armazenado e Armazena

 
recuperado sem perder informação  

GIF Formato de imagem bitmap; suporta animação; Jogos e


 
compressão sem perdas animaçã
BMP Independente do dispositivo de exibição; falta deNo Wind
 
compressão  

PNG Compressão de dados sem perdas; apoia Transfer


 
diferentes espaços de cores na intern
WebP Compressão sem perdas e com perdas; tamanho pequeno, Adesivos
 
mas qualidade de imagem comparável com JPEG aplicativ
SVG Para interatividade e animação; comportamentos Local na
 
e imagens definidas em formato XML; eles podem desenvo
 
ser pesquisado, indexado e compactado  
     

Espaços de cores
A organização das cores de uma imagem em um formato
específico é chamada de espaço de cores . A maneira como uma
cor é representada é chamada de modelo de cores . Cada imagem
usa um dos seguintes espaços de cores para uma representação
de imagem eficaz:
• \ RGB : vermelho, verde, azul
• \ XYZ : cor nas dimensões x, y e z

13

Capítulo 2 Introdução ao processamento de imagens

• \ HSV / HSL : matiz, saturação e valor / matiz,


saturação e luminosidade

• \ LAB : luminância e componentes de cor


verde-vermelho e azul-amarelo
• \ LCH : luminosidade, croma e matiz
• \ YPbPr : cabos verdes, azuis e vermelhos

• \ YUV : brilho e croma, ou cor


• \ YIQ : luminância, parâmetro em fase e quadratura
Vamos dar uma olhada em todos esses modelos de cores, um por um.

RGB
Usando o espaço de cores RGB, vermelho, verde e azul são
misturados de maneiras diferentes para fazer combinações de
cores diferentes. Por que usamos RGB? Porque nossos olhos têm
receptores de cores que podem perceber essas três cores e suas
combinações de forma bastante eficaz.
Podemos formar qualquer cor, teoricamente, a partir
dessas três cores. A intensidade de cada cor é definida em um
intervalo de 0 a 255. Esse intervalo é chamado de
profundidade de cor .
O espaço de cores RGB tem mais dois componentes:

\ 1. \ Cromaticidade de ponto branco


\ 2. \ Curva de conexão Gama

14

Capítulo 2 Introdução ao processamento de imagens

A Figura 2-8 mostra um diagrama de Venn do espaço de cores RGB.

Figura 2-8.  Sobreposição de cores RGB

XYZ
As cores RGB têm um limite de saturação. Eles não podem ir
além do que podemos ver. O espaço de cores XYZ nos ajuda a ir
além desse limite. Agora, você pode se perguntar por que
gostaríamos de ir além do limite. Bem, pode não ser possível
para nossos olhos humanos perceberem certas cores, mas no
mundo digital, você pode precisar que essas cores sejam
usadas. Por exemplo, XYZ pode ser usado para correspondência
de cores; podemos inserir um código de cor e depois reproduzi-
lo em diferentes aplicações, como impressão. Usando XYZ,
podemos codificar todas as cores que existem no mundo real.
Esse espaço de cores é denominado XYZ porque extrapola as
cores RGB em três dimensões: x, y e z. A Figura 2-9 apresenta
uma representação XYZ de uma imagem.

15

Capítulo 2 Introdução ao processamento de imagens

Figura 2-9.  O espaço de cores XYZ

Limiar de pixel Um limite é usado para estabelecer


condições. Por exemplo, se a intensidade de um pixel for
maior que 47, torne-o preto ou branco; 47 é chamado de
limite .
Extrapolação Se prevermos ou estimarmos algum valor com base
em sua relação com os valores anteriores, estamos extrapolando.
Um pixel vizinho ao branco pode ser branco (por suposição ou
extrapolação).
16

Capítulo 2 Introdução ao processamento de imagens

HSV / HSL
HSV / HSL é uma representação alternativa do espaço de
cores RGB. Consiste nos seguintes componentes:
• \ Hue
• \ Saturação
•\ Valor
• \ Leveza

Matiz é uma propriedade que descreve três cores: verde,


vermelho e magenta. Também pode ser uma mistura de duas
cores puras: vermelho e amarelo e amarelo e verde
A saturação mede a intensidade de uma imagem. Diz-nos a
que distância uma cor está do cinzento. Um valor mais baixo
significa que a cor está se aproximando do cinza.
A luminosidade se refere à intensidade da cor em relação ao
branco. Diz-nos a que distância uma cor está do branco.
O valor é outra medida de intensidade. Diz-nos a que
distância uma cor está do preto. A Figura 2-10 mostra uma
representação HSV de uma imagem

Figura 2-10.  O espaço de cores HSV

17

Capítulo 2 Introdução ao processamento de imagens

LAB
O espaço de cores LAB tem três componentes:
\ 1. \ Luminância
\ 2. \ a *, que é o componente de cor verde e vermelho
\ 3. \ b *, que é o componente de cor azul e amarelo

As cores que podemos perceber, e as que não podemos, estão


incluídas no espaço de cores LAB. Os humanos são capazes de
perceber um ponto, com coordenadas definidas e a distância até
um ponto. Juntos, um ponto e a distância até ele têm
coordenadas cilíndricas . Qualquer coisa que não tenha
coordenadas cilíndricas não pode ser percebida pelos humanos.
A melhor parte sobre o espaço de cores LAB é que ele não
depende do dispositivo; ele pode ser usado em impressão, têxteis
e uma série de outras aplicações. O espaço de cores LAB é um
dos meios mais exatos de representar uma cor. A Figura 2-11
mostra uma representação LAB de uma imagem.

Figura 2-11.  O espaço de cores LAB

LCH
O LCH é semelhante ao espaço de cores LAB, mas em vez de
usar coordenadas cilíndricas, ele usa coordenadas
retangulares. Isso torna as coordenadas semelhantes a como
nosso olho humano vê, ou seja, descrevendo um ponto com
base

18

Capítulo 2 Introdução ao processamento de imagens

em não apenas suas coordenadas posicionais, mas também


pela distância de um ponto de referência. Portanto, é ideal
para a percepção do olho humano, já que o ponto de
referência neste caso são os nossos olhos.

YPbPr
O espaço de cores YPbPr é usado em eletrônicos de vídeo, como
DVD players. Consiste nos três componentes a seguir:
\ 1. \ Y : o cabo verde
\ 2. \ Pb : o cabo azul
\ 3. \ Pr : o cabo vermelho

Os três componentes são derivados apenas do espaço de


cores RGB. Y refere-se a brilho; Pb e Pr são os dois sinais de cores
diferentes. Em geral, ao usar computadores, os componentes de
cores digitais são derivados do espaço de cores RGB. No entanto,
quando falamos de dispositivos eletrônicos (como DVD players),
precisamos usar a contraparte analógica do espaço de cores
RGB, que é YPbPr. A Figura 2-12 mostra um cabo YPbPr padrão.

Figura 2-12.  Cabos YPbPr

19

Capítulo 2 Introdução ao processamento de imagens

YUV
O espaço de cores YUV é um pouco semelhante ao YPbPr,
porque ambos são usados em eletrônicos de vídeo. A diferença
é que o YUV também suporta televisão em preto e branco .
• \ Y : o brilho presente em uma imagem. Seu
valor pode variar de 0 a 255.

• \ U e V : o componente croma, ou cor. Seu


valor pode variar de –128 a +127 (no caso de
inteiros com sinal) ou de 0 a 255 (no caso de
inteiros sem sinal).
Se removermos o componente U e V, obteremos uma
imagem em tons de cinza. U e V são matrizes de cores (Figura
2-13 ).
Figura 2-13.  O espaço de cores YUV

20

Capítulo 2 Introdução ao processamento de imagens

YIQ
O espaço de cor YIQ (Figura 2-14 ) é usado em televisões em
cores (o modo NTSC: National Television System Committee).
Consiste nos três componentes a seguir:
\ 1. \ Y : a luminância em uma imagem

\ 2. \ I : o parâmetro em fase
\ 3. \ Q : a quadratura que representa as informações de cor

Figura 2-14.  O espaço de cores YIQ

Conceitos de imagem avançados


Agora que examinamos alguns dos conceitos básicos
relacionados à cor, vamos examinar a terminologia e os
conceitos relacionados ao processamento de imagens:
• \Curva de Bézier
• \Elipsóide
• \Correção de gama

21
Capítulo 2 Introdução ao processamento de imagens

• \ Índice de Similaridade Estrutural


• \ Deconvolution
• \ Homografia

• \ Convolução

Curva de Bézier
A curva de Bézier é uma curva que possui vários pontos de
controle. Os pontos de controle são alguns pontos selecionados
em uma tela que podemos usar para ajustar a curva. À medida
que mudamos a posição dos pontos de controle, a forma da
curva muda e é usada para manipular quadros e movimento.
Também pode ser usado para aplicar zoom, selecionar a
posição de uma imagem, alterar ou transformar parte de uma
imagem e muito mais. A Figura 2-15 mostra uma curva Bezier
normal.

P1

P2

P0 P3
Figura 2-15.  Curva de Bézier e pontos de controle

22

Capítulo 2 Introdução ao processamento de imagens

Elipsóide
Um círculo é uma figura bidimensional com diâmetro ou raio
constante. Uma esfera é um círculo tridimensional que também
possui um raio ou diâmetro constante. Mas, se pegarmos uma
esfera e a esmagarmos dos dois lados, ela se tornará um
elipsóide.
Os elipsóides não têm diâmetros constantes. Um lado tem
um diâmetro maior e é chamado de eixo principal ; o lado
menor é chamado de eixo menor . A Figura 2-16 mostra uma
esfera e dois elipsóides.

Figura 2-16.  Uma esfera comparada com dois elipsóides

23

Capítulo 2 Introdução ao processamento de imagens

Correção de gama
A correção de gama, que é usada para exibir uma imagem com
precisão na tela, controla o brilho de uma imagem e pode ser
usada para alterar a proporção de
vermelho para verde para azul .
Se houver um pixel que desejamos exibir em uma
intensidade específica (por exemplo, x), e a tela do computador
tiver um valor de gama de 2,5, a intensidade do pixel em um
monitor de computador será x 2,5 . Como a intensidade é sempre
medida entre zero e um, a imagem no monitor, neste caso, fica
confusa.
Para eliminar esse problema, o valor de entrada deve ter o
gama corrigido. A conexão Gama é feita de forma que a saída
seja quase semelhante à entrada. Por exemplo, se o valor de
entrada for elevado à potência de 1 / 2,5, esse processo é
conhecido como correção de gama de 2,5. A Figura 2-17 mostra
a aparência de uma imagem com diferentes valores gama.

Figura 2-17.  Correção de gama de uma imagem usando valores


diferentes

24

Capítulo 2 Introdução ao processamento de imagens

Índice de similaridade estrutural


O Índice de Similaridade Estrutural, ou SSIM, é usado para
medir a qualidade de uma imagem. Ele informa o quanto uma
imagem é estruturalmente semelhante a outra, o que significa
que precisamos de duas imagens para realizar o cálculo SSIM.
Uma restrição aqui é que devemos saber qual imagem é a
original; caso contrário, o algoritmo não pode diferenciar
entre qual imagem é melhor do que outra. A fórmula SSIM é
ssim (x, y) = (2μ x μ y + c 1 ) × (2σ xy + c 2 ) ∕ (μ x 2 + μ y 2 + c 2 ) (σ x 2 + σ
y + c 2 ),
2

onde μ é a média das imagens, σ é o desvio padrão das


imagens e σ 2 é a variância das imagens.
SSIM (x, y) deve ser igual a SSIM (y, x). Essa é a condição de
semelhança.

Deconvolução
Em geral, a deconvolução é usada para corrigir imagens
borradas, o que ajuda a restaurar o contraste. Com imagens
borradas, é difícil determinar a intensidade do pixel. Para
fazer essa correção, usamos o que é chamado de função de
dispersão de pontos (PSF). Selecionamos um ponto dentro de
uma imagem e, usando o PSF, podemos representar esse ponto
com um padrão de luz (emitida a partir daquele ponto) de
forma tridimensional, o que ajuda a tornar a imagem mais
clara. A Figura 2-18 mostra uma imagem lunar deconvolvida.

25

Capítulo 2 Introdução ao processamento de imagens

Figura 2-18.  Deconvolução de uma imagem lunar

Suponha que capturemos uma imagem em más condições


climáticas. Devido às condições anormais de iluminação, o
contraste da imagem pode não ser ideal. Usamos restauração de
contraste para ajustar o contraste da imagem para obter uma
imagem melhor. No processo de Restauração de contraste,
pixels próximos são analisados, e outros parâmetros também
são considerados, como profundidade da imagem, estrutura
dela, etc. e, em seguida, usando-os a deconvolução define o
melhor contraste para uma imagem.
Homografia
A homografia tem vários usos no processamento de imagens:
geração de imagens em mosaico e panorâmicas, montagem de
imagens, registro de imagens, alinhamento de imagens e muito
mais. É usado para transformar uma imagem de um plano
projetivo para outro. Portanto, ele pode ser usado para alterar o
plano e a perspectiva de uma imagem. Além das coordenadas
xey da imagem (que resulta em uma imagem plana
e bidimensional ), uma terceira dimensão é adicionada: z. A
Figura 2-19 mostra o mesmo ponto após a homografia ser
aplicada, resultando em uma perspectiva alterada.

26

Capítulo 2 Introdução ao processamento de imagens

( x ' , y'

(x,y

Figura 2-19.  Aplicação de homografia para mudar a


perspectiva de uma imagem

Convolução
A convolução é um processo simples durante o qual aplicamos
uma matriz (também chamada de kernel ou filtro ) a uma
imagem para que possamos reduzi-la ou adicionar várias
camadas de preenchimento para manter o mesmo tamanho. A
convolução também é usada para extrair recursos específicos
de uma imagem, como uma forma, uma aresta e assim por
diante. A convolução é muito usada no processamento de
imagens, especialmente em redes neurais convolucionais e
detecção facial. Falaremos sobre Convolução em detalhes no
Capítulo 6 .
A seguir, no Capítulo 3 , examinamos os conceitos básicos do
Python e implementamos alguns dos conceitos discutidos neste
capítulo escrevendo scripts Python.
27

CAPÍTULO 3

Noções básicas
de Python e Scikit
Image
Fazer o processamento de imagens sem usar uma linguagem de
programação é como contar o número de estrelas enquanto você
olha para o céu noturno. Existem tantas metodologias complexas
que, mesmo que tentemos fazer manualmente, não é de todo
possível. Mas, se usarmos linguagens de programação como
Python, R, C ++, MATLAB e assim por diante, o mesmo trabalho
pode ser feito em um instante.
A questão é que devemos conhecer a linguagem antes de
começar a aplicar qualquer um dos métodos de processamento
de imagem. Este capítulo visa ajudá-lo a atingir os dois objetivos.
A primeira metade do capítulo trata dos conceitos básicos do
Python que são úteis na aplicação de técnicas de processamento
de imagens. A segunda metade do capítulo examina a biblioteca
de processamento de imagens do Python: Scikit Learn. Todos os
conceitos que estudamos no capítulo anterior, junto com alguns
outros, podem ser aplicados em Python usando Scikit Learn. Ao
final deste capítulo, você deve se sentir confortável com os
conceitos Python e os aplicativos básicos de processamento de
imagem.

© Himanshu Singh 2019 29


H. Singh, Practical Machine Learning and Image
Processing ,
https://doi.org/10.1007/978-1-4842-4149-3_3

Capítulo 3 Noções básicas de Python e Scikit Image

Noções básicas de Python


Nesta seção, examinamos brevemente os seguintes conceitos:
• \Variáveis e tipos de dados
• \Estruturas de dados
• \Declarações de fluxo de controle

• \Declarações Condicionais
• \Funções

Variáveis e tipos de dados


A primeira coisa que precisamos entender sobre Python é como
salvar os dados e em qual formato os dados devem ser salvos.
Precisamos definir nossas variáveis de imagem como um
contêiner, dentro do qual armazenamos os dados. Se não
usarmos as variáveis, podemos fazer cálculos, mas não podemos
salvar nossa saída. Vejamos um exemplo:

nome =
'Saurav' idade
= 20 altura =
6,5

Neste exemplo, nome, idade e altura são as variáveis que


armazenam os valores Saurav, 20 e 6,5, respectivamente.
Existem algumas regras que precisamos seguir ao nomear uma
variável:
• \Os nomes das variáveis devem começar com uma letra ou um
 
sublinhado.
• \O resto do nome da variável pode consistir em letras,
 
números e sublinhados.

• \Os nomes diferenciam maiúsculas de minúsculas.


• \Não devemos usar nomes embutidos em Python .

30

Capítulo 3 Noções básicas de Python e Scikit Image

Agora precisamos examinar os tipos de dados. No exemplo


anterior, armazenamos três tipos de dados: texto, que é
colocado entre aspas simples; um inteiro e um valor com um
decimal. Python sabe automaticamente que qualquer coisa
entre aspas é String, qualquer coisa sem decimal é Int e
qualquer coisa com decimal é Float. Esses são os três tipos de
dados em Python.
Agora que sabemos como salvar um valor dentro de
uma variável, podemos precisar imprimi-lo. A impressão
pode ser feita como

nome = 'Saurav' idade


= 20 altura = 6,5
imprimir (nome,
idade, altura)

Saída: 20,6,5

ou como

nome =
'Saurav' idade
= 20 altura =
6,5 imprimir
(nome)
imprimir
(idade)
imprimir
(altura)

Produto:
Saurav
20 6.5

ou como

nome =
'Saurav' idade
= 20 altura =
6,5
imprimir ("o nome é", nome)

31

Capítulo 3 Noções básicas de Python e Scikit Image

print ("a idade é", idade) print


("a altura é", altura)

Resultado:
o nome é Saurav 'a
idade é 20
a altura é 6,5

Outra forma de impressão é usar conectores:

nome =
'Saurav' idade
= 20 altura =
6,5
print ("Meu nome é% s. Minha idade e altura são% d,% f"%
(nome, idade, altura))

Resultado:
Meu nome é Saurav '. Minha idade e altura são 20, 6,5

No exemplo anterior,% s representa String, onde “s”


significa string; % d representa Int, onde “d” significa dígito ou
inteiro; e% f representa decimais, onde “f” significa float.
Assim, o primeiro conector é conectado à primeira variável, o
segundo à segunda e o terceiro à terceira. Tudo é unido
usando “%” (Figura 3-1 ).

print (“Meu nome é% s. Minha idade e altura são% d,% f“% (nome, idade, altura))

Figura 3-1.  Como funcionam os conectores

32

Capítulo 3 Noções básicas de Python e Scikit Image

Estruturas de dados
Na seção anterior, vimos como salvar um valor dentro de uma
variável. Mas, se quisermos salvar mais de um valor, devemos
usar as estruturas de dados Python, que incluem o seguinte:
• \ Listas
• \ Dicionários
• \ Tuplas
Essas estruturas são as mais utilizadas no processamento de
imagens.
Listas
Podemos usar listas para armazenar vários valores dentro de uma
única variável. Por exemplo,

Idade = [24,35,26,42]
Nomes = ["Sachin", "Saurav", "Rahul"]

Como você pode ver, as listas sempre começam e terminam com


colchetes.

Dicionários
Os dicionários são combinações de chaves e valores. Assim
como um dicionário comum tem palavras e significados, você
pode pensar em chaves e valores como palavras e
significados. Por exemplo,

Detalhes = {"Sachin": 24, "Saurav": 35, "Rahul": 42}

Os dicionários sempre começam e terminam com chaves.


Além disso, as chaves e os valores são separados por dois
pontos. O primeiro elemento antes dos dois pontos é a chave; o
elemento após os dois pontos é o valor.

33

Capítulo 3 Noções básicas de Python e Scikit Image

Tuplas
As tuplas também armazenam valores de maneira semelhante às
listas. A diferença é que as tuplas são imutáveis - ou seja, uma
vez que uma tupla é definida, os valores não podem ser
modificados. As tuplas começam e terminam com parênteses.
Por exemplo,
Altura = (6,5, 5,4, 5,11)

Declarações de fluxo de controle


Existem dois tipos de declarações de fluxo de controle:
\ 1. \ um loop while
\ 2. \ a loop for

Se quisermos repetir uma determinada operação várias


vezes, usamos instruções de fluxo de controle. Suponha que
desejemos gerar uma tabuada de multiplicação de dois. Vejamos
como podemos fazer isso usando um loop while e um loop for.
contagem = 1
enquanto
contagem <= 10:
tabela = 2 *
contagem
contagem =
contagem + 1
impressão da
tabela Saída:

2
4
6
8
10
12
14
16
18
20

34

Capítulo 3 Noções básicas de Python e Scikit Image

Vamos examinar a sintaxe de um loop while. A Figura


3-2 descreve o funcionamento de um loop while.

A condição determina quantas


vezes o loop será executado

enquanto (condição):

Código que precisa ser


repetido várias vezes com
base na condição

Se a condição se tornar
falsa, saímos do loop e ele
para

Figura 3-2.  Como funciona um loop while


O loop em nosso trecho de código será executado dez
vezes, porque inserimos a condição de menor ou igual a dez.
Quando a contagem chega a 11, a condição falha e saímos do
loop.
Agora vamos ver como podemos usar o loop for para o mesmo
problema.

para i no intervalo
(10): table = 2 * (i +
1) print (table)
Output:

2
4
6

35

Capítulo 3 Noções básicas de Python e Scikit Image

8
10
12
14
16
18
20

A Figura 3-3 descreve o funcionamento do loop for.

Um intervalo de dez significa que no


primeiro loop, o valor é zero; no segundo
loop, o valor é um e assim por diante. No
último loop, o valor é nove, porque o
intervalo é equivalente a 1 <10. Nossos
valores sempre começam com zero.

para i no intervalo (10):

Código que precisa


ser repetido várias vezes
com base no intervalo

Quando o valor de i se
torna maior do que o
intervalo, o loop para.

Figura 3-3.  Como funciona um loop for

O loop for em nosso exemplo será executado dez vezes


porque estipulamos um intervalo de 10. Quando o valor de i
é igual a dez, o loop para.
Usamos loops while quando queremos um loop
baseado em condições ; usamos loops for quando temos
números predefinidos. Ambos os loops são muito importantes
no campo do processamento de imagens, como você verá mais
tarde.

36

Capítulo 3 Noções básicas de Python e Scikit Image

Declarações Condicionais
As instruções condicionais são usadas para fornecer um
resultado binário com base em uma condição fornecida por
você. Todas as condições discutidas na Tabela 3-1 podem ser
usadas no exemplo a seguir. Se o resultado for verdadeiro ou
1, o bloco de código dentro da instrução condicional será
executado; caso contrário, não. As declarações condicionais
podem ser dos seguintes tipos:
•\ E se
• \ if-else
• \ if-elif-else

Tabela 3-1.  Operadores condicionais

Doença Significado
   

a == b Verifica se a é igual a b
a! = b Verifica se a não é igual a b
a <b Verifica se a é menor que b
a <= b Verifica se a é menor e igual a b

a> b Verifica se a é maior que b


a> = b Verifica se a é maior e igual a b
   

Vejamos todos os três com um exemplo. Suponha que


queremos dar um A para os alunos que obtiveram mais de 80
pontos em um teste, um B para aqueles que obtiveram mais de
60 pontos e menos de 80 pontos e um C para os alunos que
obtiveram 59 pontos ou menos. Aqui está o código:

marcas = 45
se marcas> = 80: print ("Você
obteve uma nota") marcas
elif> = 60 e marcas <80:
37

Capítulo 3 Noções básicas de Python e Scikit Image

print ("Você obteve nota


B") else:
print ("You got C Grade")
Resultado:
Você tem grau C

Este código é executado um por um. Primeiro, as marcas de


variáveis recebem um valor de 45. Em seguida, a primeira
declaração condicional é encontrada. Se o valor das notas for
maior ou igual a 80, é atribuída nota A. Caso contrário, este
não é o caso, a instrução elif é encontrada, que verifica a
segunda condição. Se nenhuma das condições for verdadeira, o
aluno recebe um C.

Funções
As funções são usadas quando você deseja encerrar códigos
complexos dentro de um único invólucro e, em seguida, usar
esse invólucro várias vezes sem escrever o código
repetidamente. É como se dedicássemos um pote para conter
açúcar, e sempre que quisermos tirar o açúcar, usamos apenas
esse pote, não a sacola em que você tem açúcar junto com sal,
vegetais e salgadinhos.
A Figura 3-4 descreve as funções em poucas palavras. Uma
função pode receber um ou mais valores como entradas - I1, I2,
I3 e assim por diante (In) - e fornecer um ou mais resultados
como saída (O).

38

Capítulo 3 Noções básicas de Python e Scikit Image


Linhas de código
complexo que
pegam entradas
e produzem uma
saída

Figura 3-4.  Como funciona uma função

Vejamos como usar funções com base no exemplo a seguir.


Até agora, geramos uma tabuada de multiplicação de dois
usando loops for e while, mas suponha que queiramos gerar
uma tabela com qualquer número que quisermos. Aqui está o
código para fazer exatamente isso:

tabela def (a):


para i no intervalo
(10): tabela = a
* (i + 1)
impressão
(tabela)

A função recebe a como entrada e gera uma tabela com


qualquer valor armazenado. Vamos ver como chamar a
função. Suponha que queremos gerar uma tabuada de 10 e
17. Chamamos a função:

tabela (10)
Resultado:
10
20
30
40
50

39

Capítulo 3 Noções básicas de Python e Scikit Image

60
70
80
90
100
tabela (17)
Resultado:
17
34
51
68
85
102
119
136
153
170

Agora que você já conhece os fundamentos do Python,


vamos passar a uma discussão sobre a imagem Scikit.

Imagem Scikit
Scikit Image é um módulo usado para fazer o processamento
básico de imagens. Antes de começar, vamos dar uma olhada
na definição de um módulo. Um módulo é uma coleção de
arquivos, classes ou funções Python. Podemos salvar códigos
complexos e longos dentro de arquivos diferentes. Para fazer
isso, precisamos importar os arquivos e usá-los em nosso
ambiente. Primeiro, precisamos importar Scikit Image em
nosso ambiente, assim:

importar imagem de esqui

40

Capítulo 3 Noções básicas de Python e Scikit Image

Essa única linha de código importa uma coleção inteira


de classes e funções necessárias para fazer a análise básica
de imagens.
Podemos aplicar todos os conceitos que examinamos
no Capítulo 2 usando o Scikit Image.
Nesta seção, veremos as seguintes operações usando Scikit
Image e Python:
• \Carregando e visualizando uma imagem
• \Obtendo resolução de imagem
• \Olhando para valores de pixel
• \Convertendo Espaço de Cores
• \Salvar uma imagem
• \Criação de desenhos básicos
• \Fazendo Correção Gamma
• \Rotação, deslocamento e dimensionamento de imagens
• \Determinando Similaridade Estrutural

Carregando e visualizando uma imagem


Vamos ver como podemos importar uma imagem para o
ambiente Python e visualizá-la lá. Começamos importando um
módulo denominado skimage, que contém diferentes
algoritmos de processamento de imagem. Para fazer o upload e
visualizar a imagem, usamos uma classe do módulo skimage
chamada io. Dentro desta classe, usamos a função imread para
carregar e ler uma imagem; a função imshow é usada para
visualizar a imagem. Vamos dar uma olhada no código.

41

Capítulo 3 Noções básicas de Python e Scikit Image

da importação de skimage
img = io.imread ('filhote.jpg')
io.imshow (img)

Resultado:

Obtendo resolução de imagem


Para obter a resolução da imagem, usamos um built-in chamado
forma função. Quando uma imagem é lida, todos os valores de
pixel são armazenados em um formato de matriz; esta matriz é
chamada de matriz numpy . Depois de ler a imagem e convertê-
la em array, usamos a função de forma para ver a resolução.
No código a seguir, você pode ver que temos uma imagem com um
resolução de 1536 × 2048, e tem três canais (porque está no
formato de cores RGB).
#Obter resolução de
imagem da importação
de imagem de esqui io
img = io.imread ('filhote.jpg')
img.shape
Resultado:
(1536, 2048, 3)

42

Capítulo 3 Noções básicas de Python e Scikit Image

Olhando para valores de pixel


Agora que sabemos a resolução da imagem, podemos examinar
o valor de cada pixel. Para fazer isso, salvamos a matriz numpy
em uma linha - em outras palavras, usamos uma linha para
armazenar todos os valores de pixel. Quando você olha o código
a seguir, pode ver que estamos importando outro módulo
chamado pandas. O Pandas é usado para ler, escrever e
processar vários formatos de arquivo. Aqui, salvamos os valores
de pixel no formato Excel:

#Getting Pixel Values


from skimage import
io import pandas as
pd
img = io.imread ('filhote.jpg')
df = pd.DataFrame (img.flatten ())
filepath = 'pixel_values1.xlsx'
df.to_excel (filepath, index = False)

Quando olhamos para a linha de importação — import


pandas as pd — significa que estamos renomeando o módulo
importado para pd. A função nivelar é usada para converter as
três dimensões de uma imagem RGB em uma única dimensão.
Em seguida, salvamos essa imagem em um arquivo Excel
denominado pixel_values.xlsx. Para fazer isso, usamos a função
Pandas chamada to_excel. A função DataFrame converte uma
matriz unidimensional em um formato semelhante ao do Excel ,
com linhas e colunas. Você pode imprimir a variável df para
examinar a estrutura do quadro de dados.

Convertendo Espaço de Cores


Suponha que nossa imagem esteja no espaço de cores RGB.
Podemos querer convertê-lo para formatos de cores diferentes,
conforme discutido no Capítulo 2 . Nesta seção, examinamos
diferentes conversões e, a seguir, convertemos a imagem de
volta ao formato RGB original.

Antes de examinar o código, devemos examinar as funções


que usaremos. Para converter uma imagem em diferentes
formatos de cores, precisamos usar o

43

Capítulo 3 Noções básicas de Python e Scikit Image

classe color, que está presente no módulo skimage. Dentro


desta classe, podemos usar as seguintes funções:
• \ rgb2hsv • \ yuv2rgb
• \ hsv2rgb • \ rgb2lab
• \ rgb2xyz • \ lab2rgb
• \ xyz2rgb • \ rgb2yiq
• \ rgb2grey • \ yiq2rgb
• \ grey2rgb • \ rgb2ypbpr
• \ rgb2yuv • \ ypbpr2rgb

Além disso, temos que usar mais um módulo, chamado


pylab. Importamos todas as classes presentes no pylab usando
*. Usamos pylab para ver diferentes figuras em diferentes
blocos. Em seguida, usamos a figura de função para exibir mais
de uma imagem de uma vez. Vamos agora examinar todo o
código e sua saída.

RGB para HSV e Vice Versa


#Import library from
skimage import io from
skimage importar cor de
skimage importar dados
de pylab import * #Read
image

img = io.imread ('filhote.jpg')


#Converter para HSV
img_hsv = color.rgb2hsv (img)

#Converter para RGB


img_rgb = color.hsv2rgb (img_hsv)

#Mostrar ambos
os valores figura
(0)

44

Capítulo 3 Noções básicas de Python e Scikit Image

io.imshow
(img_hsv) figura
(1) io.imshow
(img_rgb)

Resultado:

RGB para XYZ e vice-versa


#Import library from
skimage import io from
skimage import cor de
skimage import data
#Read image

img = io.imread ('filhote.jpg')

#Converter para XYZ


img_xyz = color.rgb2xyz (img)

45

Capítulo 3 Noções básicas de Python e Scikit Image

#Converter para RGB


img_rgb = color.xyz2rgb (img_xyz)

#Mostrar ambas
as figuras figura
(0) io.imshow
(img_xyz) figura
(1) io.imshow
(img_rgb)

Resultado:

46

Capítulo 3 Noções básicas de Python e Scikit Image

RGB para LAB e vice-versa


#Import library from
skimage import io from
skimage import color
#Read image

img = io.imread ('filhote.jpg')

#Converter para LAB


img_lab = color.rgb2lab (img)
#Converter para RGB
img_rgb = color.lab2rgb (img_lab)

#Mostrar ambas
as figuras figura
(0) io.imshow
(img_lab) figura
(1) io.imshow
(img_rgb)

Resultado:

47

Capítulo 3 Noções básicas de Python e Scikit Image


RGB para YUV e vice-versa
#Import library from
skimage import io from
skimage import color
#Read image

img = io.imread ('filhote.jpg')

#Convert para YUV


img_yuv = color.rgb2yuv (img)

48

Capítulo 3 Noções básicas de Python e Scikit Image

#Converter para RGB


img_rgb = color.yuv2rgb (img_yuv)

#Mostrar ambas
as figuras figura
(0) io.imshow
(img_yuv) figura
(1) io.imshow
(img_rgb)

Resultado:
49

Capítulo 3 Noções básicas de Python e Scikit Image

RGB para YIQ e vice-versa


#Import library from
skimage import io from
skimage import color
#Read image

img = io.imread ('filhote.jpg')

#Converter para YIQ


img_yiq = color.rgb2yiq (img)

#Converter para RGB


img_rgb = color.yiq2rgb (img_yiq)

#Mostrar ambas
as figuras figura
(0) io.imshow
(img_yiq) figura
(1) io.imshow
(img_rgb)

Resultado:

50

Capítulo 3 Noções básicas de Python e Scikit Image


RGB para YPbPr e vice-versa
#Import library from
skimage import io from
skimage import color
#Read image

img = io.imread ('filhote.jpg')

#Convert para YPbPr


img_ypbpr = color.rgb2ypbpr (img)

51

Capítulo 3 Noções básicas de Python e Scikit Image

#Converter para RGB


img_rgb = color.ypbpr2rgb (img_ypbpr)

#Mostrar ambas as
figuras figura (0)
io.imshow
(img_ypbpr) figura
(1) io.imshow
(img_rgb)

Resultado:
52

Capítulo 3 Noções básicas de Python e Scikit Image

Salvando uma imagem


Após cada análise de imagem, podemos querer salvar a
imagem. Para fazer isso, usamos a função skimage io chamada
imsave. No código a seguir, o primeiro argumento inclui o
nome do arquivo no qual você deseja salvar a imagem; a
segunda é a variável que contém a imagem.

#Import library from


skimage import io from
skimage import color
from pylab import *
#Read image

img = io.imread ('filhote.jpg')

#Convert para YPbPr


img_ypbpr = color.rgb2ypbpr (img)

#Converter para RGB


img_rgb = color.ypbpr2rgb (img_ypbpr)

io.imsave ("filhote_ypbpr.jpg", img_ypbpr)

Criação de desenhos básicos


Dentro de uma imagem, podemos querer desenhar certas
figuras. Essas figuras podem ser simples, como uma linha, ou
complexas, como um elipsóide. Vejamos alguns desenhos
básicos usando a classe de desenho skimage chamada draw.

Linhas
A função de linha é usada para desenhar uma linha simples em
uma imagem. No código a seguir, os primeiros dois parâmetros
indicam o primeiro ponto; os dois últimos parâmetros indicam o
segundo ponto. Uma linha é então desenhada usando esses
pontos. Podemos então alterar os valores de pixel da linha para
que possamos ver a linha na imagem.

53

Capítulo 3 Noções básicas de Python e Scikit Image


de skimage import io de
skimage import draw

img = io.imread ('filhote.jpg')


x, y = draw.line (0,0,1000,1000)
img [x, y] = 0 io.imshow (img)

Resultado:

Retângulos
Para desenhar retângulos, usamos a função polígono. Podemos
desenhar não apenas retângulo, mas qualquer tipo de polígono
que quisermos. Tudo o que precisamos fazer é fornecer as
coordenadas xey e, em seguida, definir a largura e a altura.
No código a seguir, uso o retângulo de função. Ele retorna
uma forma com valores de pixel que alteramos, como no
exemplo anterior de uma linha.

de skimage import io de
skimage import draw

img = io.imread ('filhote.jpg')


retângulo def (x, y, w, h):
rr, cc = [x, x + w, x + w, x], [y, y, y + h, y + h]

54

Capítulo 3 Noções básicas de Python e Scikit Image

return (draw.polygon (rr, cc)) rr, cc


= retângulo (10, 10, 500.500) img [rr,
cc] = 1

io.imshow (img)

Resultado:
Círculos
A função de círculo é usada para desenhar um círculo. No
código a seguir, os dois primeiros argumentos indicam a posição
do círculo dentro da imagem; o último argumento indica o raio.

#Importar bibliotecas
de skimage import io de
skimage import draw

#Load image
img = io.imread ('filhote.jpg')

#Define as coordenadas do círculo e o


raio x, y = draw.circle (500,500, 100)

#Desenhar
círculo img
[x, y] = 1

55

Capítulo 3 Noções básicas de Python e Scikit Image

#Show image
io.imshow
(img)

Resultado:

Curva de Bézier
Para desenhar uma curva de Bezier, usamos a função
bezier_curve. Precisamos indicar a posição de três ou mais
pontos de controle que então moldam a curva. Os primeiros seis
argumentos no código a seguir definem três pontos; o último
argumento define a tensão presente na linha. Jogue com valores
diferentes para alterar a curva.

#Importar bibliotecas
de skimage import io de
skimage import draw

#Load image
img = io.imread ('filhote.jpg')

#Defina as coordenadas da curva de Bézier


x, y = draw.bezier_curve (0,0, 500, 500, 900,1200,100)
#Draw Bezier curve
img [x, y] = 1

56

Capítulo 3 Noções básicas de Python e Scikit Image

#Show image
io.imshow
(img)

Resultado:

Fazendo Correção Gamma


Para realizar a correção de gama de uma imagem, com base
no instrumento de exibição, usamos a classe de exposição na
imagem de esqui. A classe de exposição contém uma função
chamada ajustar_gamma, que usamos para fornecer uma
imagem como entrada e o valor final de gama que desejamos.
Desta forma, obtemos uma imagem com correção de gama .

de exposição de importação
de skimage de importação
de skimage de importação
de pylab *
img = io.imread ('filhote.jpg')
gamma_corrected1 = exposição.adjust_gamma
(img, 0,5) gamma_corrected2 =
exposição.adjust_gamma (img, 5) figura (0)
io.imshow
(gamma_corrected1) figura
(1)

57

Capítulo 3 Noções básicas de Python e Scikit Image

io.imshow (gamma_corrected2)

Resultado:

58

Capítulo 3 Noções básicas de Python e Scikit Image


Rotação, deslocamento e
dimensionamento de imagens
Às vezes, podemos querer girar uma imagem ou alterar seu
tamanho. Para fazer isso, usamos a classe transform no
módulo skimage. transform tem duas funções: girar e
redimensionar. rotate usa o grau de rotação como parâmetro;
resize usa o novo tamanho como parâmetro.

da importação de skimage
de skimage.transform import rotate
img = io.imread ('puppy.jpg') img_rot
= rotate (img, 20) io.imshow
(img_rot)

Resultado:

da importação de skimage
from skimage.transform import
resize img = io.imread ('puppy.jpg')
img_res = resize (img, (100,100))
io.imshow (img_res) io.imsave
("ss.jpg", img_res)

59

Capítulo 3 Noções básicas de Python e Scikit Image

Resultado:
Determinando Similaridade Estrutural
Como expliquei anteriormente, a similaridade estrutural é usada
para encontrar o índice que indica o quanto duas imagens são
semelhantes. Um valor mais próximo de um significa que as
imagens são muito semelhantes; um valor mais próximo de zero
significa que eles são menos semelhantes. No código a seguir,
para a primeira comparação de imagens semelhantes, obtemos
uma saída SSIM de 1.0. No segundo bit de código, no qual
comparamos a imagem com sua contraparte YPbPr, obtemos
um SSIM de 0,43, o que indica menos similaridade.

da importação de skimage
de skimage.measure import compare_ssim as
ssim img_original = io.imread ('puppy.jpg')
img_modified = io.imread ('puppy_ypbpr.jpg')

ssim_original = ssim (img_original, img_original, data_range =


img_ original.max () - img_original.min (), multicanal = True)
ssim_different = ssim (img_original, img_modified, data_range =
img_original.max (), multicanal = True) ssim_different = ssim
(img_original, img_modified, data_range = img_original.max () -
img_modified) multicanal = verdadeiro) imprimir (ssim_original,
ssim_different)

60

Capítulo 3 Noções básicas de Python e Scikit Image

Resultado:
1,0 0,4348875243670361

O SSIM leva três argumentos. O primeiro se refere à imagem; a


segundo indica o intervalo dos pixels (o valor de cor de pixel
mais alto menos o valor de cor de pixel mais baixo). O terceiro
argumento é multicanal. Um valor True significa que a
imagem contém mais de um canal, como RGB. Falso significa
que há apenas um canal, como escala de cinza.
No próximo capítulo, veremos os conceitos avançados de
processamento de imagem usando uma biblioteca de visão
computacional chamada OpenCV.
61

CAPÍTULO 4

Imagem Avançada
Em processamento
Usando OpenCV
Agora que examinamos as técnicas básicas de processamento
de imagem usando a biblioteca de imagens Scikit, podemos
passar para seus aspectos mais avançados. Neste capítulo,
usamos uma das bibliotecas de visão computacional mais
abrangentes: OpenCV e examinamos os seguintes conceitos:
• \Combinando duas imagens
• \Alterar o contraste e o brilho de uma imagem
• \Adicionar texto a imagens
• \Suavizando imagens

• \Mudando a forma das imagens


• \Efetuando limite de imagem
• \Calculando gradientes para detectar bordas

• \Executando a equalização do histograma

© Himanshu Singh 2019 63


H. Singh, Practical Machine Learning and Image
Processing ,
https://doi.org/10.1007/978-1-4842-4149-3_4

Capítulo 4 Processamento de imagem avançado usando OpenCV

Combinando Duas Imagens


Suponha que você tenha duas imagens e queira mesclá-las para
que os recursos de ambas as imagens fiquem visíveis. Usamos
técnicas de registro de imagem para mesclar uma imagem sobre
a segunda e determinar se há alguma alteração. Vejamos o
código:

#import pacotes
necessários import cv2

#Read image 1
img1 = cv2.imread ('cat_1.jpg')
#Leia a imagem 2
img2 = cv2.imread ('cat_2.jpg')

#Defina alfa e beta


alfa = 0,30
beta = 0,70

#Blend images
imagem_final = cv2.addWeighted (img1, alfa, img2, beta, 0,0)

#Show image
io.imshow
(final_image)

Vejamos algumas das funções usadas neste código:

• \ import cv2: A biblioteca OpenCV completa está


presente no pacote cv2. No Capítulo 1 ,
aprendemos como instalar o OpenCV. Agora
tudo o que precisamos fazer é importar este
pacote para usar as classes e funções
armazenadas nele.

• \ cv2.imread (): Semelhante a


skimage.io.imread (), temos cv2.imread (),
que é usado para ler a imagem de um
determinado destino.

64

Capítulo 4 Processamento de imagem avançado usando OpenCV

• \ cv2.addWeighted (): esta função combina as


duas imagens. Os parâmetros alfa e beta
indicam a transparência em ambas as
imagens. Existem algumas fórmulas que
ajudam a determinar a mistura final. O último
parâmetro é denominado gama . Atualmente,
ele tem um valor zero. É apenas um escalar,
que é adicionado às fórmulas, para
transformar as imagens de forma mais eficaz.
Em geral, gama é zero.

• \ cv2.imshow (): Semelhante a


skimage.io.imshow (), cv2. imshow () ajuda a
exibir a imagem em uma nova janela.
• \ cv2.waitKey (): waitKey () é usado para que a
janela exibindo a saída permaneça até
clicarmos em Fechar ou pressionar Escape. Se
não incluirmos esta função após cv2.imshow (),
as imagens não serão exibidas.

• \ cv2.DestroyAllWindows (): Depois de clicar em


Fechar ou pressionar Escape, esta função
destrói todas as janelas que foram abertas e
salvas na memória.
As seguintes imagens são o resultado do código anterior:

65

Capítulo 4 Processamento de imagem avançado usando OpenCV

Mudança de contraste e brilho


Para alterar o contraste e o brilho em uma imagem,
devemos entender o que esses dois termos significam:
• \ Contraste : o contraste é a diferença entre a
intensidade máxima e mínima do pixel.

• \ Brilho : O brilho se refere à claridade ou


escuridão de uma imagem. Para tornar uma
imagem mais brilhante, adicionamos um
número constante a todos os pixels presentes
nela.
Vamos examinar o código e a saída para ver a diferença
entre contraste e brilho.

#import pacotes
necessários import cv2
importar numpy como np

#Read image
image = cv2.imread ("cat_1.jpg")

#Crie uma imagem fictícia que armazene diferentes


contrastes e brilho

new_image = np.zeros (image.shape, image.dtype)

#Contraste dos parâmetros de


brilho e contraste = 3,0
brilhante = 2

# Altere o contraste e o brilho para


y na faixa (image.shape [0]):
para x no intervalo (image.shape
[1]): para c no intervalo
(image.shape [2]):
nova_imagem [y, x, c] = np.clip (contraste * imagem
[y, x, c] + brilho, 0, 255)

66

Capítulo 4 Processamento de imagem avançado usando OpenCV

figura (0)
io.imshow (imagem)
figura (1) io.imshow
(nova_imagem)

Neste código, não usamos nenhuma função cv2 para alterar


o brilho ou contraste. Usamos a biblioteca numpy e um conceito
de fatiamento para alterar os parâmetros. A primeira coisa que
fizemos foi definir os parâmetros. Demos ao contraste um valor
de 3 e ao brilho um valor de 2. O primeiro loop for deu a largura
da imagem, o segundo deu a altura da imagem e o terceiro deu
os canais da imagem. Portanto, o primeiro loop executa a
largura várias vezes, o segundo loop executa a altura várias
vezes e o último loop executa o número de canais de cor várias
vezes. Se a imagem RGB estiver lá, o loop será executado três
vezes para os três canais.
np.clip () limita os valores em um intervalo particular. No anterior
código, o intervalo é de 0 a 255, que nada mais é do que os
valores de pixel de cada canal. Portanto, uma fórmula é
derivada:
(Valor específico do pixel × Contraste) + Brilho.

Usando esta fórmula, podemos alterar cada valor de pixel, e


np.clip () garante que o valor de saída não vai além de 0 a 255.
Portanto, os loops atravessam cada pixel, para cada canal , e faz
a transformação.

67

Capítulo 4 Processamento de imagem avançado usando OpenCV

Aqui estão as imagens de saída:

Adicionar texto a imagens


cv2.putText () é uma função presente no módulo cv2 que nos
permite adicionar texto às imagens. A função leva os
seguintes argumentos:

• \Imagem, onde você deseja escrever o texto


• \O texto que você quer escrever
• \Posição do texto na imagem

68

Capítulo 4 Processamento de imagem avançado usando OpenCV


 

• \Tipo de fonte
• \Escala de fonte

• \Cor do texto
• \Espessura do texto

• \Tipo de linha usada

Como você pode ver no código a seguir, a fonte usada é


FONT_HERSHEY_ SIMPLEX. cv2 também suporta as seguintes
fontes:
• \ FONT_HERSHEY_SIMPLEX
• \ FONT_HERSHEY_PLAIN
• \ FONT_HERSHEY_DUPLEX
• \ FONT_HERSHEY_COMPLEX
• \ FONT_HERSHEY_TRIPLEX
• \ FONT_HERSHEY_COMPLEX_SMALL
• \ FONT_HERSHEY_SCRIPT_SIMPLEX

•\
FONT_HERSHEY_SCRIPT_COMPLEX
• \ FONT_ITALIC

O tipo de linha usado no código é cv2.LINE_AA. Outros


tipos de linhas que são suportados são
• \ FILLED: uma linha completamente preenchida
• \ LINE_4: quatro linhas conectadas
• \ LINE_8: oito linhas conectadas

• \ LINE_AA: uma linha anti-aliasing


69

Capítulo 4 Processamento de imagem avançado usando OpenCV

Você pode experimentar usando todos os diferentes


argumentos e verificar os resultados. Vejamos o código e sua
saída.

#import pacotes
necessários import cv2
importar numpy como np

#Read image
image = cv2.imread ("cat_1.jpg")

#Define fonte
font = cv2.FONT_HERSHEY_SIMPLEX

#Escreva na imagem
cv2.putText (imagem, "Eu sou um gato", (230, 50), fonte, 0,8, (0,
255, 0), 2, cv2.LINE_AA)

io.imshow (imagem)

Resultado:

70

Capítulo 4 Processamento de imagem avançado usando OpenCV

Suavizando Imagens
Nesta seção, daremos uma olhada em três filtros usados para
suavizar imagens. Esses filtros são os seguintes:
• \ O filtro mediano (cv2.medianBlur)

• \ o filtro gaussiano (cv2.GaussianBlur)


• \ o filtro bilateral (cv2.bilateralFilter)

Filtro Mediano
O filtro de mediana é um dos filtros de suavização de imagem
mais básicos . É um filtro não linear que remove o ruído
preto e branco presente em uma imagem ao encontrar a
mediana usando pixels vizinhos.
Para suavizar uma imagem usando o filtro mediano, olhamos para o
primeiro
Matriz 3 × 3, encontre a mediana dessa matriz e remova o valor
central dessa mediana. Em seguida, movemos um passo para a
direita e repetimos este processo até que todos os pixels tenham
sido cobertos. A imagem final é uma imagem suavizada. Se você
deseja preservar as bordas da imagem enquanto desfoca, o filtro
mediano é sua melhor opção.
cv2.medianBlur é a função usada para obter o desfoque
médio. Possui dois parâmetros:
\ 1. \ A imagem que queremos suavizar
\ 2. \ O tamanho do kernel, que deve ser estranho. Portanto, um valor
  
de 9 significa uma matriz 9 × 9.

Filtro Gaussiano
O filtro gaussiano depende do desvio padrão da imagem
(distribuição) e assume que a média é zero (podemos definir
uma média diferente de zero também). Os filtros gaussianos
não cuidam das bordas.

71

Capítulo 4 Processamento de imagem avançado usando OpenCV

O valor de certo parâmetro estatístico define a preservação. É


usado para desfocar a imagem básica. Geralmente funciona
definindo um kernel. Suponha que definamos um kernel 3 × 3.
Aplicamos esse kernel a cada pixel presente na imagem e
calculamos a média do resultado, o que resulta em uma imagem
borrada. Aqui está um exemplo:
cv2.GaussianBlur () é a função usada para aplicar um filtro
gaussiano. Possui três parâmetros:
\ 1. \ A imagem, que precisa ser desfocada
\ 2. \ O tamanho do kernel (3 × 3 neste caso)

\ 3. \ O desvio padrão

Filtro Bilateral
Se quisermos suavizar uma imagem e manter as bordas intactas,
usamos um filtro bilateral. Sua implementação é simples:
substituímos o valor do pixel pela média de seus vizinhos. Esta é
uma abordagem de suavização não linear que obtém a média
ponderada dos pixels vizinhos. “Vizinhos” são definidos das
seguintes maneiras:

• \Dois valores de pixel estão próximos um do outro


• \Dois valores de pixel são semelhantes um ao outro

72

Capítulo 4 Processamento de imagem avançado usando OpenCV

cv2.bilateralFilter tem quatro parâmetros:


\ 1. \ A imagem que queremos suavizar

\ 2. \ O diâmetro da vizinhança do pixel


(definindo o diâmetro da vizinhança para
procurar vizinhos)

\ 3. \ O valor sigma da cor (para encontrar os


pixels que são semelhantes)

\ 4. \ O valor sigma para o espaço (para


encontrar os pixels que estão mais
próximos)
Vamos dar uma olhada no código:

#import pacotes
necessários import cv2
importar numpy como np

#Leia imagens para diferentes fins de


desfoque image_Original = cv2.imread
("cat_1.jpg") image_MedianBlur = cv2.imread
("cat_1.jpg") image_GaussianBlur =
cv2.imread ("cat_1.jpg") image_BilateralBlur =
cv2.imread (" cat_1.jpg ")

#Blur images
image_MedianBlur = cv2.medianBlur (image_MedianBlur, 9)
image_GaussianBlur = cv2.GaussianBlur (image_GaussianBlur,
(9,9), 10) image_BilateralBlur = cv2.bilateralFilter
(image_BilateralBlur, 9, 100,75)

#Show images figure (0)


io.imshow (image_Original)
figure (1) io.imshow
(image_MedianBlur) figure
(2)

73

Capítulo 4 Processamento de imagem avançado usando OpenCV

io.imshow
(image_GaussianBlur) figure
(3) io.imshow
(image_BilateralBlur)
Resultado:
74

Capítulo 4 Processamento de imagem avançado usando OpenCV

Mudando o formato das imagens


Nesta seção, examinamos a erosão e a dilatação, que são as
duas operações usadas para alterar a forma das imagens. A
dilatação resulta na adição de pixels ao limite de um objeto;
a erosão leva à remoção de pixels do limite.
Dois corroem ou dilatam uma imagem, primeiro definimos o
kernel da vizinhança, o que pode ser feito de três maneiras:
\ 1. \ MORPH_RECT: para fazer um kernel retangular

\ 2. \ MORPH_CROSS: para fazer um kernel em forma de cruz


\ 3. \ MORPH_ELLIPS: para fazer um kernel elíptico

O kernel encontra os vizinhos de um pixel, o que nos ajuda


a erodir ou dilatar uma imagem. Para dilatação, o valor
máximo gera um novo valor de pixel. Para erosão, o valor
mínimo em um kernel gera um novo valor de pixel.
Na Figura 4-1 , aplicamos uma matriz 3 × 1 para encontrar o
mínimo para cada linha. Para o primeiro elemento, o kernel
começa em uma célula anterior. Como o valor não está presente
na nova célula à esquerda, o consideramos em branco. Esse
conceito é chamado de preenchimento . Portanto, o primeiro
mínimo é verificado entre nenhum, 141 e 157. Portanto, 141 é o
mínimo e você vê 141 como o primeiro valor na matriz certa.
Então, o kernel muda para a direita. Agora, as células a serem
consideradas são 141, 157 e 65. Desta vez, 65 é o mínimo, então o
segundo valor na nova matriz é 65. Na terceira vez, o kernel
compara 157, 65 e nenhum, porque não há terceiro célula.
Portanto, o mínimo é 65 e esse se torna o último valor. Essa
operação é realizada para cada célula e você obtém a nova
matriz mostrada na Figura 4-1 .

75

Capítulo 4 Processamento de imagem avançado usando OpenCV

141 157 65 141 65 65


       
21 21 21

240 21 98  
 

        102 100 100

102 195 100


 

Figura 4-1.  Dilatação

A operação de erosão é feita de forma semelhante à


dilatação, exceto que em vez de encontrar o mínimo,
encontramos o máximo. A Figura 4-2 mostra a operação.

141 157 65 157 157 157


 

       

240 21 98  
  240 240 98

       

102 195 100 195 195 195


 

Figura 4-2.  Erosão

O tamanho do kernel é, como na dilatação, um retângulo 3


× 1. cv2.getStructuringElement () é a função usada para definir
o kernel e passá-lo para a função erodir ou dilatar. Vamos ver
seus parâmetros:
• \Tipo de erosão / dilatação
• \Tamanho do kernel
• \Ponto em que o kernel deve iniciar

76

Capítulo 4 Processamento de imagem avançado usando OpenCV

Depois de aplicar cv2.getStructuringElement () e obter o


kernel final, usamos cv2.erode () e cv2.dilate () para realizar
as operações específicas. Vejamos o código e sua saída:

# CÓDIGO DE DILAÇÃO:

#Import
package
import cv2

#Read image
image = cv2.imread ("cat_1.jpg")
#Defina o tamanho
da erosão s1 = 0
s2 = 10
s3 = 10

#Defina o tipo de
erosão t1 =
cv2.MORPH_RECT
t2 = cv2.MORPH_CROSS
t3 = cv2.MORPH_ELLIPSE

#Defina e salve o modelo de erosão


tmp1 = cv2.getStructuringElement (t1, (2 * s1 + 1, 2 * s1 + 1), (s1,
s1)) tmp2 = cv2.getStructuringElement (t2, (2 * s2 + 1, 2 * s2 + 1),
(s2, s2)) tmp3 = cv2.getStructuringElement (t3, (2 * s3 + 1, 2 * s3 +
1), (s3, s3))

#Aplicar o modelo de erosão à imagem e salvar em diferentes


variáveis

final1 = cv2.erode (imagem,


tmp1) final2 = cv2.erode
(imagem, tmp2) final3 =
cv2.erode (imagem, tmp3)

#Mostrar todas as imagens com diferentes


figuras de erosões (0)

io.imshow (final1)

77

Capítulo 4 Processamento de imagem avançado usando OpenCV

figura 1)
io.imshow (final2)
Figura 2)
io.imshow (final3)

# CÓDIGO DE EROSÃO:

#Import
packages
import cv2

#Read images
image = cv2.imread ("cat_1.jpg")

#Defina o tamanho
da dilatação d1 = 0
d2 = 10
d3 = 20
#Defina o tipo de
dilatação t1 =
cv2.MORPH_RECT
t2 = cv2.MORPH_CROSS
t3 = cv2.MORPH_ELLIPSE

#Armazene os modelos de dilatação


tmp1 = cv2.getStructuringElement (t1, (2 * d1 + 1, 2 * d1 + 1), (d1,
d1)) tmp2 = cv2.getStructuringElement (t2, (2 * d2 + 1, 2 * d2 + 1),
(d2, d2)) tmp3 = cv2.getStructuringElement (t3, (2 * d3 + 1, 2 * d3
+ 1), (d3, d3))

# Aplicar dilatação às imagens


final1 = cv2.dilate (imagem,
tmp1) final2 = cv2.dilate
(imagem, tmp2) final3 =
cv2.dilate (imagem, tmp3)

#Mostrar a figura
das imagens (0)
io.imshow
(final1)

78

Capítulo 4 Processamento de imagem avançado usando OpenCV

figura 1)
io.imshow (final2)
Figura 2)
io.imshow (final3)

Resultado:
79

Capítulo 4 Processamento de imagem avançado usando OpenCV

Efetuando a limitação de imagem


O principal motivo pelo qual você faria o limiar de imagem é
segmentar imagens. Tentamos retirar um objeto da imagem
removendo o fundo e focalizando o objeto. Para fazer isso,
primeiro convertemos a imagem em tons de cinza e, em seguida,
em um formato binário - ou seja, a imagem contém preto ou
branco apenas.

Fornecemos um valor de pixel de referência, e todos os


valores acima ou abaixo dele são convertidos em preto ou
branco. Existem cinco tipos de limite:

\ 1. \ Binário : se o valor do pixel for maior do que


o valor do pixel de referência (o valor do
limiar), converta para branco (255); caso
contrário, converta para preto (0).
\ 2. \ Binário invertido : Se o valor do pixel for
maior que o valor do pixel de referência (o
valor do limite), converta para preto (0);
caso contrário, converta para branco (255).
Exatamente o oposto do tipo binário.

80

Capítulo 4 Processamento de imagem avançado usando OpenCV


\ 3. \ Truncado : se o valor do pixel for maior
que o valor do pixel de referência (o
valor limite), converta para o valor
limite; caso contrário, não altere o valor.
\ 4. \ Limite para zero : Se o valor do pixel for
maior que o valor do pixel de referência (o
valor do limite), não altere o valor; caso
contrário, converta para preto (0).

\ 5. \ Limite para zero invertido : se o valor do


pixel for maior que o valor do pixel de
referência (o valor do limite), converta para
preto (0); caso contrário, não mude.
Usamos a função cv2.threshold () para fazer o limite de
imagem, que usa os seguintes parâmetros:
• \A imagem a ser convertida

• \O valor limite
• \O valor máximo do pixel

• \O tipo de limite (conforme listado anteriormente)


Vejamos o código e sua saída.

#Import
packages
import cv2

#Read image
image = cv2.imread ("cat_1.jpg")

#Defina os tipos de
limite "'

0 - binário
1 - Binário Invertido

81

Capítulo 4 Processamento de imagem avançado usando OpenCV

2 - truncado
3 - Limiar para zero
4 - Limiar para zero invertido
"'

# Aplique limites diferentes e salve em variáveis diferentes


_, img1 = cv2.threshold (imagem, 50, 255, 0)
_, img2 = cv2.threshold (image, 50, 255, 1) _,
img3 = cv2.threshold (image, 50, 255, 2) _,
img4 = cv2.threshold (image, 50, 255, 3) _,
img5 = cv2.threshold (imagem, 50, 255, 4)

#Mostrar a figura das diferentes


imagens de limite (0)
io.imshow (img1) #Prints Figura de
imagem binária (1)
io.imshow (img2) #Prints Figura binária
invertida (2)
io.imshow (img3) #Prints Figura da
imagem truncada (3)
io.imshow (img4) #Prints Threshold to Zero
Image figure (4)
io.imshow (img5) #Prints Threshold to Zero Inverted Image

82

Capítulo 4 Processamento de imagem avançado usando OpenCV

Resultado:
83

Capítulo 4 Processamento de imagem avançado usando OpenCV

Calculando gradientes
Nesta seção, veremos a detecção de bordas usando derivados de
Sobel. As arestas são encontradas em duas direções: a direção
vertical e a direção horizontal. Com este algoritmo, enfatizamos
apenas as regiões que possuem uma frequência espacial muito
alta, que pode corresponder a arestas. A frequência espacial é o
nível de detalhe presente em uma área de importância.
No código a seguir, lemos a imagem, aplicamos desfoque
gaussiano para que o ruído seja removido e, em seguida,
convertemos a imagem em tons de cinza. Usamos a função
cv2.cvtColor () para converter a imagem em tons de cinza.
Também podemos usar as funções de imagem de esqui para
fazer o mesmo. Por último, fornecemos a saída da escala de
cinza para a função cv2.Sobel (). Vejamos os parâmetros da
função Sobel:
• \Imagem de entrada
• \Profundidade da imagem de saída. Quanto maior for a profundidade
 
imagem, menores são as chances de você perder alguma fronteira. V
 
pode experimentar todos os parâmetros listados abaixo,
 
para ver se eles capturam as fronteiras de forma eficaz, por
seus requerimentos. A profundidade pode ser dos seguintes tipos:

84

Capítulo 4 Processamento de imagem avançado usando OpenCV


 

• \-1 (a mesma profundidade da imagem original)


• \cv2.CV_16S

• \cv2.CV_32F
• \cv2.CV_64F
• \Ordem da derivada x (define o ordem derivada para
 
encontrando bordas horizontais)  

• \Ordem da derivada y (define o ordem derivada para


 
encontrando bordas verticais)  

• \Tamanho do kernel  

• \Fator de escala a ser aplicado aos derivados


• \Valor delta a ser adicionado como um escalar na fórmula

• \Tipo de borda para extrapolação de pixels

A função cv2.convertScaleAbs () é usada para converter os


valores em um número absoluto, com um tipo de 8 bits sem
sinal . Em seguida, mesclamos os gradientes xey que
encontramos para encontrar as bordas gerais da imagem.
Vejamos o código e sua saída.

#Import
packages
import cv2

#Read image
src = cv2.imread ("cat_1.jpg")

#Apply gaussian blur


cv2.GaussianBlur (src, (3, 3), 0)

#Converter imagem em tons de cinza


cinza = cv2.cvtColor (src, cv2.COLOR_BGR2GRAY)

85

Capítulo 4 Processamento de imagem avançado usando OpenCV

# Aplicar o método Sobel à imagem em tons de cinza


grad_x = cv2.Sobel (cinza, cv2.CV_16S, 1, 0, ksize = 3, scale = 1,
delta = 0, borderType = cv2.BORDER_DEFAULT) # Derivação
horizontal de Sobel

grad_y = cv2.Sobel (cinza, cv2.CV_16S, 0, 1, ksize = 3, scale = 1,


delta = 0, borderType = cv2.BORDER_DEFAULT) # Derivação
vertical de Sobel
abs_grad_x = cv2.convertScaleAbs
(grad_x) abs_grad_y =
cv2.convertScaleAbs (grad_y)
grad = cv2.addWeighted (abs_grad_x, 0,5, abs_grad_y, 0,5, 0)
#Aplicar ambos

#Mostrar a imagem io.imshow


(grad) #Ver a imagem

Resultado:

86

Capítulo 4 Processamento de imagem avançado usando OpenCV

Executando Equalização de Histograma


A equalização do histograma é usada para ajustar o contraste de
uma imagem. Primeiro, traçamos o histograma da distribuição
da intensidade do pixel e depois o modificamos. Existe uma
função de probabilidade cumulativa associada a cada imagem. A
equalização do histograma fornece uma tendência linear para
essa função. Devemos usar uma imagem em tons de cinza para
realizar a equalização do histograma.
A função cv2.equalizeHist () é usada para equalização do
histograma. Vejamos um exemplo.

#Import
packages
import cv2

#Read image
src = cv2.imread ("cat_1.jpg")
#Converter para escala de cinza
src = cv2.cvtColor (src, cv2.COLOR_BGR2GRAY)

#Apply equalize histogram


src_eqlzd = cv2.equalizeHist (src) #Performs Histograma Equalização

#Mostrar as duas
imagens figura (0)
io.imshow (src)
figura (1) io.imshow
(src_eqlzd) figura (2)
io.imshow
(src_eqlzd)

87

Capítulo 4 Processamento de imagem avançado usando OpenCV

Resultado:
Agora conhecemos os algoritmos básicos de processamento
de imagem usando skimage e algumas das operações
avançadas usando OpenCV. No próximo capítulo, avançamos e
aplicamos algoritmos de aprendizado de máquina para fazer o
processamento de imagens.

88

CAPÍTULO 5

Processamento de
imagem
Usando Máquina
Aprendendo
Começamos este capítulo examinando alguns dos algoritmos de
processamento de imagem mais amplamente usados e, em
seguida, passamos para a implementação de aprendizado de
máquina no processamento de imagens. O capítulo em resumo é
o seguinte:
• \Mapeamento de recursos usando o recurso invariável de escala
 
algoritmo de transformação (SIFT)

• \Registro de imagem usando o consenso de amostra aleatória


 
(RANSAC) algoritmo
• \Classificação de imagens usando redes neurais artificiais
• \Classificação de imagens usando neural convolucional
 
redes (CNNs)
• \Classificação de imagens usando aprendizado de máquina
• \Termos importantes
© Himanshu Singh 2019 89
H. Singh, Practical Machine Learning and Image
Processing ,
https://doi.org/10.1007/978-1-4842-4149-3_5

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Mapeamento de características usando o


algoritmo SIFT
Suponha que temos duas imagens. Uma imagem é de um banco
em um parque. A segunda imagem é de todo o parque, que
também inclui o banco. Agora, suponha que queiramos
escrever um código que nos ajude a encontrar o banco dentro
da imagem do parque. Você pode achar que esta é uma tarefa
fácil, mas deixe-me adicionar alguma complexidade. E se a
imagem da bancada for uma imagem ampliada? Ou e se for
girado? Ou ambos? Como você vai lidar com isso agora?
A resposta está na transformação de recurso
invariante de escala , ou algoritmo SIFT. Como o nome sugere, é
invariante de escala, o que significa que não importa o quanto
ampliamos (ou afastamos) a imagem, ainda podemos encontrar
semelhanças. Outra característica desse algoritmo é que ele é
invariante à rotação. Independentemente do grau de rotação,
ele ainda funciona bem. O único problema com esse algoritmo é
que ele é patenteado, o que significa que é bom para fins
acadêmicos, mas para fins comerciais, pode haver muitos
problemas legais envolvidos em usá-lo. No entanto, isso não nos
impedirá de aprender e aplicar esse algoritmo por enquanto.
Primeiro, devemos entender os fundamentos do algoritmo.
Então, podemos aplicá-lo para encontrar semelhanças entre
duas imagens usando Python e, em seguida, examinaremos o
código linha por linha.
Vejamos os recursos da imagem que o algoritmo SIFT tenta
fatorar durante o processamento:
• \ Escala (imagem ampliada ou reduzida )
• \ Rotação

• \ Iluminação
• \ Perspectiva
90

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Como você pode ver, não apenas a escala e a rotação são


acomodadas, o algoritmo SIFT também cuida da iluminação
presente na imagem e da perspectiva da qual estamos olhando.
Mas como ele faz tudo isso? Vamos dar uma olhada no processo
passo a passo de uso do algoritmo SIFT:

\ 1. \ Encontre e construa um espaço para


garantir invariância de escala
\ 2. \ Encontre a diferença entre as gaussianas
\ 3. \ Encontre os pontos importantes presentes dentro da imagem

\ 4. \ Remova os pontos sem importância para


fazer comparações eficientes

\ 5. \ Fornece orientação para os pontos


importantes encontrados na etapa 3
\ 6. \ Identificando os principais recursos de forma exclusiva.

Etapa 1: construção do espaço


Na primeira etapa, pegamos a imagem original e executamos o
desfoque gaussiano, para que possamos remover alguns dos
pontos sem importância e o ruído extra presente na imagem.
Feito isso, redimensionamos a imagem e repetimos o processo.
Existem vários fatores dos quais o redimensionamento e o
desfoque dependem, mas não entraremos em detalhes
matemáticos aqui.

Etapa 2: diferença entre as gaussianas


Na segunda etapa, pegamos as imagens da etapa 1 e
encontramos a diferença entre seus valores. Isso torna a escala
da imagem invariável.

91

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Etapa 3: pontos importantes


Durante a terceira etapa, identificamos pontos importantes
(também chamados de pontos-chave ). A diferença entre a
imagem gaussiana que encontramos na etapa 3 é usada para
determinar os máximos e mínimos locais. Pegamos cada pixel e
verificamos seus vizinhos. O pixel é marcado como ponto-chave
se for o maior (máximo) ou o menor (mínimo) entre todos os
seus vizinhos.
O próximo passo é encontrar os máximos e / ou mínimos dos
subpixel. Encontramos subpixels usando um conceito
matemático denominado expansão de Taylor . Quando os
subpixels são encontrados, tentamos encontrar os máximos e os
mínimos novamente, usando o mesmo processo. Além disso,
para pegar apenas os cantos e considerá-los como pontos-chave,
usamos um conceito matemático denominado matriz de Hessian
. Os cantos são sempre considerados os melhores pontos-chave.

Etapa 4: Pontos-chave sem importância


Nesta etapa, primeiro determinamos um valor limite. Na
imagem gerada pelos pontos- chave e na imagem de subpixels,
verificamos a intensidade do pixel com o valor limite. Se for
menor do que o valor limite, consideramos um ponto-chave sem
importância e o rejeitamos.

Etapa 5: Orientação dos pontos-chave


Encontramos a direção do gradiente e a magnitude de cada
ponto-chave e seus vizinhos, então olhamos para a orientação
predominante em torno do ponto-chave e atribuímos a mesma
a ele. Usamos histogramas para encontrar essas orientações e
obter a última.

92

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Etapa 6: Principais recursos


Para tornar os pontos-chave únicos, extraímos os principais
recursos deles. Além disso, garantimos que, ao comparar esses
pontos-chave com a segunda imagem, eles não devem ser
exatamente semelhantes, mas quase semelhantes.
Agora que sabemos o básico do algoritmo, vamos examinar
o código ao qual o algoritmo é aplicado a um par de imagens.

import cv2
importar numpy como np
import matplotlib.pyplot as plt
de Sift_Operations import *

imprimir ("'Certifique-se de que as duas imagens estão na mesma


pasta"')

x = input ("Insira o nome da primeira


imagem:") Image1 = cv2.imread (x)
y = input ("Digite o nome da segunda
imagem:") Image2 = cv2.imread (y)

Image1_gray = cv2.cvtColor (Image1,


cv2.COLOR_BGR2GRAY) Image2_gray = cv2.cvtColor
(Image2, cv2.COLOR_BGR2GRAY)

Image1_key_points, Image1_descriptors = extract_sift_


features (Image1_gray)
Image2_key_points, Image2_descriptors = extract_sift_
features (Image2_gray)

imprimir ('Exibindo recursos SIFT')


mostrando características_de_sift (Image1_gray, Image1,
Image1_key_points);

norma = cv2.NORM_L2
bruteForce = cv2.BFMatcher (norma)

match = bruteForce.match (Image1_descriptors, Image2_descriptors)

93

Capítulo 5 Processamento de imagens usando aprendizado de máquina

correspondências = classificado (correspondências, chave =


correspondência lambda: correspondência.distância)

matched_img = cv2.drawMatches
(Image1, Image1_key_points,
Image2, Image2_key_points,
corresponde a [: 100],
Image2.copy ())

plt.figure (figsize = (100,300))


plt.imshow (matched_img)

O código acima aplica todo o algoritmo SIFT a um par de


imagens. Porém, o algoritmo é salvo em um arquivo Python
denominado Sift_Operations.py no mesmo diretório deste
código. Vejamos o código dentro disso também.

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

def extract_sift_features (img): sift_initialize =


cv2.xfeatures2d.SIFT_create () key_points,
descriptors = sift_initialize. detectAndCompute
(img, Nenhum)

retornar pontos-chave, descritores

def showing_sift_features (img1, img2, key_points): return


plt.imshow (cv2.drawKeypoints (img1, key_points,
img2.copy ()))

Agora vamos examinar o código, pulando de um


arquivo para outro conforme necessário:

\ 1. \ No código principal, importamos as


bibliotecas importantes: OpenCV, Numpy,
Matplotlib e o módulo personalizado
Sift_Operations. import * significa importar
tudo o que está presente no arquivo Python.

94

Capítulo 5 Processamento de imagens usando aprendizado de máquina

\ 2. \ A seguir lemos duas imagens, às quais


devemos aplicar as operações SIFT. A Figura
5-1 mostra as imagens que importei.

Figura 5-1.  Imagens originais

\ 3. \ Em seguida, convertemos a imagem em


tons de cinza. SIFT precisa de imagens cinza
para realizar suas operações. Usamos a
função OpenCV cv2.cvtColor para conversão
do formato de cores.

Image1_gray = cv2.cvtColor (Image1, cv2.COLOR_BGR2GRAY)

Image2_gray = cv2.cvtColor (Image2, cv2.COLOR_BGR2GRAY)

\ 4. \ Agora passamos essas duas imagens para a


função extract_sift_features, que está
armazenada no arquivo Sift_Operations.py.
Esta função retorna os pontos-chave
encontrados na imagem e as características
desses pontos com o nome dos descritores.
Vejamos essa função por dentro:

sift_initialize = cv2.xfeatures2d.SIFT_create ()
\ a. \ A linha de código anterior armazena
todo o SIFT dentro da variável
sift_initialize.

95

Capítulo 5 Processamento de imagens usando aprendizado de máquina

\ b. \ O método detectAndCompute é usado


para aplicar o algoritmo às imagens, w
retorna os principais pontos e
descritores:

pontos-chave, descritores = sift_initialize.detectAndCompute


(img, Nenhum)

\ c. \ Os valores são então retornados:

retornar pontos-chave, descritores


\ d. \De volta ao código de chamada, esses valores são armazenados
   
em diferentes variáveis específicas das imagens:

Image1_key_points, Image1_descriptors = extract_sift_


features (Image1_gray)

Image2_key_points, Image2_descriptors = extrair _sift_


features (Image2_gray)

\ 5. \ As características são então mostradas


para que possamos olhar os pontos-chave
e as semelhanças. O método
showing_sift_features é usado para fazer
isso.
\ 6. \ Vejamos esse método por dentro.
cv2.drawKeypoints é usado para desenhar
os pontos-chave encontrados nas duas
imagens.

\ 7. \ A variável norm é então inicializada e usada


para encontrar a distância entre os pontos-
chave. cv2.Norm_L2 é usado para calcular a
distância de Manhattan (Figura 5-2 ), que é a
distância entre dois pontos medidos ao longo
de eixos em ângulos retos - 90 graus. Não é
uma distância em linha reta; segue uma
abordagem de grade.

96

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Manhattan

Figura 5-2.  A distância de Manhattan

\ 8. \ Em seguida, a função cv2.BFMatcher é


inicializada. É usado para encontrar a
correspondência entre os descritores dos
pontos-chave. Em seguida, a variável norm
é passada como um argumento. Diz ao
BFMatcher para usar a distância de
Manhattan para realizar a correspondência.
O algoritmo inicializado é salvo em uma
variável chamada bruteForce.

\ 9. \ Os dois descritores são bruteForce


combinados. correspondência, e então as
correspondências são classificadas com base
na distância de Manhattan:

match = bruteForce.match (Image1_descriptors, Image2 _descriptors)

correspondências = classificado (correspondências, chave =


correspondência lambda: correspondência.distância)
\ 10. \ Os pontos-chave das duas imagens são
conectados com base nas 100 melhores
correspondências:

matched_img = cv2.drawMatches (

Image1, Image1_key_points,

Image2, Image2_key_points,

corresponde a [: 100], Image2.copy ())

97

Capítulo 5 Processamento de imagens usando aprendizado de máquina

\ 11. \ Por último, as imagens correspondentes são mostradas:

plt.figure (figsize = (100.300))

plt.imshow (matched_img)

A saída de todo o código é fornecida na Figura 5-3 .

Figura 5-3.  Semelhanças encontradas usando o algoritmo SIFT

Registro de imagem usando o


algoritmo RANSAC
Suponha que temos duas imagens de um único lugar de uma
vista aérea. Uma imagem mostra o local usando satélites,
enquanto a segunda mostra parte da mesma imagem usando
drones. As imagens de satélite são atualizadas em termos de
anos, enquanto as imagens de drones são obtidas com muito
mais frequência. Portanto, pode haver uma situação em que a
imagem do drone capture desenvolvimentos não vistos na
imagem de satélite. Neste cenário, podemos querer colocar a
imagem do drone exatamente no mesmo lugar onde ela
pertence na imagem de satélite, mas também mostrar as
atualizações mais recentes. Esse processo de colocar uma
imagem sobre a outra, exatamente no mesmo local onde ela está
presente, é denominado registro de imagem .

98

Capítulo 5 Processamento de imagens usando aprendizado de máquina

O RANSAC é um dos melhores algoritmos para registro de


imagens, que consiste em quatro etapas:
\ 1. \ Detecção e extração de recursos

\ 2. \ Correspondência de recursos
\ 3. \ Ajuste da função de transformação
\ 4. \ Transformação de imagem e reamostragem de imagem

O algoritmo RANSAC é usado na terceira etapa para


encontrar a função de transformação. Pegamos duas imagens e
então, usando o algoritmo RANSAC, encontramos a homografia
(similaridade) entre essas imagens. Vejamos o algoritmo
resumidamente:
\ 1. \ Encontre quatro pontos de característica
comuns das duas imagens aleatoriamente e,
em seguida, encontre a matriz de
homografia *.

\ 2. \ Repita este passo várias vezes até que


tenhamos uma matriz de homografia com o
número máximo de inliers

Vamos aplicar esse algoritmo usando Python. O código


consiste em três módulos personalizados: Ransac.py, Affine.py e
Align.py. ransac contém todo o algoritmo RANSAC, Affine é
usado para aplicar a rotação, translação e operação de escala às
imagens. Alinhar serve para alinhar a imagem de forma que
fique perfeitamente registrada na imagem original.
Vejamos o código linha por linha:

\ 1. \ Primeiro, importamos as bibliotecas


importantes, bem como os módulos
personalizados que acabamos de
mencionar.

import numpy
como np Import
cv2
da importação
Ransac * da
importação Affine *
da importação Align
*

99

Capítulo 5 Processamento de imagens usando aprendizado de máquina

\ 2. \ Em seguida, carregamos a imagem que


queremos registrar sobre a segunda
imagem (a imagem alvo). Em seguida,
carregamos a imagem de destino.

img_source = cv2.imread
("source.jpg") img_target =
cv2.imread ("target.jpg")

\ 3. \ Agora, usamos a função extract_SIFT,


armazenada no módulo Align para extrair
os pontos-chave e descritores relacionados
(eu expliquei este código na seção
anterior).

keypoint_source, descriptor_source = extract_SIFT


(img_source) keypoint_target, descriptor_target = extract_SIFT
(img_target)

\ 4. \ Em seguida, usamos a função match_SIFT


para obter a posição de todos os pontos
encontrados na etapa anterior:

pos = match_SIFT (descriptor_source, descriptor_target)

\ 5. \ Dentro do método match_SIFT, tentamos


obter as duas melhores
correspondências, entre todos os
descritores correspondidos. Para fazer
isso, usamos as funções BFMatcher e
knnMatch. Vejamos este snippet de
código, salvo dentro do módulo Align:

bf = cv2.BFMatcher ()
corresponde = bf.knnMatch (descriptor_source, descriptor_target, k = 2)

\ 6. \ Temos que criar um array numpy vazio


para armazenar as posições dos pontos-
chave. Vamos chamá-lo de pos. Colocamos
apenas os pontos dentro de pos que têm uma
proporção menor ou igual a 0,8, com base no
teste de proporção de D. Lowe (consulte os
termos importantes no final do capítulo).

100

Capítulo 5 Processamento de imagens usando aprendizado de máquina

para i no intervalo (match_num):


se corresponde a [i] [0] .distance <= 0,8 * corresponde a
[i] [1] .distance: temp = np.array ([corresponde a [i]
[0] .queryIdx,
corresponde a [i] [0] .trainIdx])
pos = np.vstack ((pos, temp))

\ 7. \ trainIdx retorna o índice do descritor na


fonte, enquanto queryIdx retorna o índice do
descritor no destino. Estas são as posições
reais que empilhamos verticalmente na
variável pos e depois a retornamos.

retorno pos

\ 8. \ Agora que temos a posição dos descritores,


usamos a função affine_matrix no módulo
Align para obter a matriz de homografia, que
usamos no registro de imagens.

H = affine_matrix (keypoint_source, keypoint_target, pos)

\ 9. \ Vamos dar uma olhada dentro da função:


\ a. \ Primeiro, armazenamos todos os pontos-chave em s e
 
variáveis t, com base no melhor descritor
 
posições armazenadas na variável pos.

s = s [:, pos [:, 0]] t =


t [:, pos [:, 1]]

\ b. \ Em seguida, precisamos encontrar os


inliers. Inliers são os pontos nas duas
imagens que mostram similaridade
máxima e, portanto, podem ser usados
para desenhar modelos RANSAC. Usamos
a função ransac_fit, armazenada no
módulo ransac para obter esses pontos-
chave.

_, _, inliers = ransac_fit (s, t)

101
Capítulo 5 Processamento de imagens usando aprendizado de máquina

Dentro de ransac_fit, inicializamos algumas


variáveis básicas: o número de inliers, as
matrizes necessárias para fazer a
transformação afim e uma variável que
armazena a posição dos inliers:

inliers_num =
0 A = Nenhum
t = Nenhum
inliers =
Nenhum

Em seguida, precisamos encontrar matrizes


temporárias que nos ajudem a fazer a
transformação afim. Para isso, geramos
índices aleatoriamente para nossos pontos, a
seguir extraímos pontos desses índices para
passar como parâmetro para a função
estimativa_afino.

idx = np.random.randint (0, pts_s.shape [1], (K, 1))


A_tmp, t_tmp = estimativa_afino (pts_s [:, idx], pts_t [:, idx])

Agora que temos essas matrizes temporárias,


nós as passamos para a função
residual_lengths, que calcula o erro, o que
nos ajuda a decidir a matriz final.

residual = comprimentos_ residual (A_tmp, t_tmp, pts_s, pts_t)

Uma explicação das funções


residual_lengths e estimativa_afina é
fornecida nas seções a seguir. Agora que
sabemos o residual / erro, verificamos com
o limite. Atribuímos um limite mínimo de
um. Se o residual for menor que o limite,
contamos o número dessas instâncias.

inliers_tmp = np.where (residual <limite)

102

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Em seguida, comparamos os inliers com


residual com o total de inliers definido (que
é zero). Se os inliers residuais forem
maiores do que os inliers predefinidos,
atualizamos o inlier predefinido com o novo
valor de inlier e, em seguida, atualizamos as
matrizes de transformação afins com as
matrizes temporárias A e t. Além disso,
armazenamos os índices desses inliers.

inliers_tmp = np.where (residual <limite)


inliers_num_tmp = len (inliers_tmp [0])
if inliers_num_tmp> inliers_num: inliers_num
= inliers_num_tmp inliers =
inliers_tmp
A=
A_tmp t =
t_tmp

Repetimos esse processo 2.000 vezes para


obter as melhores matrizes possíveis e,
em seguida, as retornamos.

para i no intervalo (ITER_NUM


= 2000) r retorna A, t, inliers

\ c. \ Agora que temos os números internos,


nós os usamos para extrair os melhores
pontos-chave de origem e de destino.

s = s [:, inliers [0]] t =


t [:, inliers [0]]

\ d. \ Usamos esses pontos-chave e os


enviamos para a função
estimativa_afina, que nos dá nossas
matrizes de transformação finais.
A, t = estimativa_afino (s, t)

103

Capítulo 5 Processamento de imagens usando aprendizado de máquina

\ e. \ Finalmente, empilhamos as matrizes


horizontalmente e as retornamos como
uma matriz: a matriz de homografia.

M = np.hstack ((A, t))


return M

\ 10. \ Agora que temos nossa matriz de


homografia, tudo o que resta fazer é o
registro da imagem. Para isso, primeiro
extraímos o número de linhas e colunas da
imagem de destino:

linhas, cols, _ = img_target.shape

\ 11. \ Em seguida, usaremos nossa imagem de


origem, aplicará a matriz de homografia
e escalonará para a linha e altura da
imagem de destino:

warp = cv2.warpAffine (img_source, H, (colunas, linhas))

\ 12. \ Agora todos nós mesclamos as duas


imagens. Para isso, damos um peso de 50%
à imagem alvo e um peso de 50% à imagem
distorcida (a imagem sobre a qual estamos
mesclando a segunda):

merge = np.uint8 (img_target * 0,5 + warp * 0,5)

\ 13. \ Agora, basta mostrar o cadastro. Também


podemos salvar a imagem, com base em
nossos requisitos.

cv2.imshow ('img',
merge) cv2.waitKey (0)
cv2.destroyAllWindows
()

104

Capítulo 5 Processamento de imagens usando aprendizado


de máquina

estimativa_afina
A função estimativa_afina leva o número total de pontos-chave
da imagem de origem e da imagem de destino como uma
entrada e retorna as matrizes de transformação afins como uma
saída. Com base na dimensão dos pontos-chave de origem,
inicializamos uma matriz fictícia e, em seguida, a preenchemos
com os pontos-chave de origem para serem usados como um
loop. Em seguida, pegamos os pontos-chave de destino,
remodelamos a dimensão para 2.000 × 1, após encontrar sua
transposição. Finalmente, fazemos uma regressão linear em
ambas as matrizes e obtemos a inclinação e a interceptação da
linha. Usando isso, calculamos as matrizes finais X e Y. Aqui está
o código:

def estimativa_fino (s, t):


num = s.shape [1]
M = np.zeros ((2 * num, 6))

para i no intervalo (num):


temp = [[s [0, i], s [1, i], 0, 0, 1, 0], [0, 0, s
[0, i], s [1, i], 0, 1] ] M [2 * i: 2 * i + 2,:] =
np.matriz (temp)
b = tTreformar ((2 * num, 1)) teta
= np.linalg.lstsq (M, b) [0] X =
teta [: 4]. remodelar ((2, 2))
Y = theta [4:]
retorna X, Y

residual_lengths
A função residual_lengths é usada para determinar os erros
presentes em nosso modelo e para garantir que as matrizes
afins que geramos, ou os descritores que combinamos, tenham
o mínimo de erros possível. Primeiro, fazemos um modelo
linear entre as matrizes afins e os pontos-chave da fonte, que
nos dá os pontos estimados para a imagem de destino. Nós os
comparamos com os pontos de destino reais para determinar os
erros finais. Em seguida, subtraímos o

105

Capítulo 5 Processamento de imagens usando aprendizado de máquina

almeje pontos com esses pontos estimados, tire o quadrado


deles e, em seguida, encontre a raiz quadrada para remover o
efeito dos valores negativos. Esta é a estimativa do erro
quadrático médio ou estimativa de resíduos. Por último,
retornamos o valor. O código desta operação é o seguinte:

def comprimentos_resíduos (X, Y, s,


t): e = np.dot (X, s) + Y
diff_square = np.power (e - t, 2)

residual = np.sqrt (np.sum (diff_square, axis =


0)) residual de retorno

Processando as imagens
Vejamos nossa imagem de destino (Figura 5-4 ):
Figura 5-4.  Imagem alvo

O Código Completo
Aqui está o código completo para registro de imagem:
Código principal:

import numpy
como np import
cv2
da importação Ransac *

106

Capítulo 5 Processamento de imagens usando aprendizado de máquina

da importação
Affine * da
importação Align *

img_source = cv2.imread ("2.jpg")


img_target = cv2.imread ("target.jpg")
keypoint_source, descriptor_source = extract_SIFT
(img_source) keypoint_target, descriptor_target = extract_SIFT
(img_target) pos = match_SIFT (descriptor_source,
descriptor_target)
H = affine_matrix (keypoint_source, keypoint_target, pos)

linhas, cols, _ = img_target.shape


warp = cv2.warpAffine (img_source, H, (cols,
linhas)) merge = np.uint8 (img_target * 0,5 + warp *
0,5) cv2.imshow ('img', merge)
cv2.waitKey (0)
cv2.destroyAllWindows ()

Ransac.py:

importar numpy
como np de
importação afim * K
=3

limiar = 1
ITER_NUM =
2000
def comprimentos_resíduos (X, Y, s,
t): e = np.dot (X, s) + Y
diff_square = np.power (e - t, 2)
residual = np.sqrt (np.sum (diff_square, axis =
0)) residual de retorno
def ransac_fit (pts_s, pts_t):
inliers_num = 0
A = Nenhum
t = nenhum

107

Capítulo 5 Processamento de imagens usando aprendizado de máquina

inliers = Nenhum
para i no intervalo (ITER_NUM):
idx = np.random.randint (0, pts_s.shape [1], (K, 1))
A_tmp, t_tmp = estimativa_afino (pts_s [:, idx], pts_t [:,
idx]) residual = comprimentos_de_ residual (A_tmp,
t_tmp, pts_s, pts_t) se não (residual é Nenhum):

inliers_tmp = np.where (residual <limite)


inliers_num_tmp = len (inliers_tmp [0])
if inliers_num_tmp> inliers_num:
inliers_num =
inliers_num_tmp inliers =
inliers_tmp
A=
A_tmp t =
t_tmp
else:
pass

return A, t, inliers

Affine.py:

importar numpy como np

def estimativa_fino (s, t):


num = s.shape [1]
M = np.zeros ((2 * num, 6))

para i no intervalo (num):


temp = [[s [0, i], s [1, i], 0, 0, 1, 0], [0, 0, s
[0, i], s [1, i], 0, 1] ] M [2 * i: 2 * i + 2,:] =
np.matriz (temp)
b = tTreformar ((2 * num, 1)) teta
= np.linalg.lstsq (M, b) [0] X =
teta [: 4]. remodelar ((2, 2))
Y = theta [4:]
retorna X, Y
108

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Align.py:

import numpy como


np de Ransac
import * import cv2
de importação afim *

def extract_SIFT (img):


img_gray = cv2.cvtColor (img,
cv2.COLOR_BGR2GRAY) sift =
cv2.xfeatures2d.SIFT_create ()
kp, desc = sift.detectAndCompute (img_gray,
None) kp = np.array ([p.pt para p em kp]). T
return kp, desc
def match_SIFT (descriptor_source, descriptor_target):
bf = cv2.BFMatcher ()
corresponde = bf.knnMatch (descriptor_source,
descriptor_target, k = 2)

pos = np.array ([], dtype = np.int32) .reshape ((0, 2))


match_num = len (match)
para i no intervalo (match_num):
se corresponde a [i] [0] .distance <= 0,8 * corresponde a
[i] [1] .distance: temp = np.array ([corresponde a [i]
[0] .queryIdx,
corresponde a [i] [0] .trainIdx])
pos = np.vstack ((pos, temp))
retorno pos

def affine_matrix (s, t, pos): s


= s [:, pos [:, 0]]
t = t [:, pos [:, 1]]
_, _, inliers = ransac_fit (s, t) s = s
[:, inliers [0]]
t = t [:, inliers [0]]

109

Capítulo 5 Processamento de imagens usando aprendizado de máquina

A, t = estimativa_afino (s, t)
M = np.hstack ((A, t)) return
M

Classificação de imagens usando


redes neurais artificiais
Antes de aplicar redes neurais artificiais em um conjunto de
imagens para classificação, vamos primeiro examinar o que
são redes neurais. Por exemplo, o que acontece quando nos
machucamos? Um sinal é enviado imediatamente ao nosso
cérebro, e então o cérebro responde com base na intensidade
do sinal. A transferência do sinal ocorre por meio de
neurônios. Os neurônios transferem o sinal, na forma de uma
sinapse, para outro neurônio, e o processo continua até que o
sinal chegue ao cérebro. A estrutura de um neurônio é
apresentada na Figura 5-5 .

Soma

Axon Synapse

Dendrites

Figura 5-5.  Neurônio biológico

110

Capítulo 5 Processamento de imagens usando aprendizado de máquina

As informações na Figura 5-5 que são importantes para nós


incluem os dendritos e o axônio. Os dendritos recebem o sinal
de outro neurônio, e o axônio transmite o sinal para o
próximo neurônio. Essa cadeia pára no último nó, que é o
cérebro.
Redes neurais artificiais usam a mesma analogia e
processam informações usando neurônios artificiais. A
informação é transferida de um neurônio artificial para outro,
o que finalmente leva a uma função de ativação, que atua
como um cérebro e toma uma decisão. A estrutura de uma
rede neural artificial simples é mostrada na Figura 5-6 .

Escondido

Entrada

Resultado

Figura 5-6.  Rede neural artificial simples

111

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Portanto, a informação a ser processada é armazenada em


nós de entrada. Em seguida, vêm as camadas ocultas, onde as
informações são realmente processadas. Por último, existe a
saída. A função de ativação está entre a camada oculta e a
camada de saída. Chamamos a camada de oculta porque não
podemos ver o que acontece dentro da camada oculta. Pode
haver uma ou mais camadas ocultas na arquitetura da rede
neural artificial. Quanto maior o número de camadas ocultas,
mais profunda é a rede (em comparação com uma rede rasa).
Agora, se nos aprofundarmos nos detalhes completos das
redes neurais, este livro se tornará muito longo e vamos nos
desviar de nosso tópico principal: processamento de imagens.
Portanto, em vez de entrar nos detalhes das redes neurais,
aconselho você a estudá-las por conta própria. Dito isso, agora
vamos passar à aplicação de redes neurais artificiais para
reconhecimento de escrita. Eu recomendo fortemente que você
não prossiga sem ter uma boa base de conhecimento de redes
neurais.
O banco de dados do Instituto Nacional de Padrões e
Tecnologia Modificado (MNIST) inclui um conjunto de dados que
contém aproximadamente 60.000 imagens de treinamento e
10.000 imagens de teste de dígitos manuscritos. Usaremos o
conjunto de dados de treinamento * para treinar nossa rede
neural e, em seguida, usaremos o conjunto de dados de teste *
para verificar sua precisão. Finalmente, você pode fornecer seu
próprio dígito manuscrito para verificar as previsões por nosso
modelo treinado.
Primeiro, vejamos um fluxograma de como proceder com as
redes neurais (Figura 5-7 ).

112

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Carregar conjunto de dados

Dados de pré-processamento

Encontre treinamento e
Conjuntos de teste

Adicionar Camada de Entrada,


Camadas ocultas,
e camada de saída
Treine o modelo

Teste o modelo

Prever

Figura 5-7.  Fluxograma do processo

113

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Primeiro, temos que baixar os conjuntos de dados de trem e


teste do banco de dados MNIST. Podemos baixá-los do site do
kaggle ( https: // www. Kaggle.com/c/digit-recognizer/data# ).
Depois que os dados são baixados, precisamos fazer o
upload dos dados para nosso ambiente Python.

importar pandas como pd


input_data = pd.read_csv ("train.csv")

Agora vamos dar uma olhada em nosso conjunto de


treinamento. Consiste em 785 colunas. Cada imagem tem
resolução de 28 × 28. Portanto, 784 colunas contêm os valores
de pixel de cada dígito. O último indica o número real
representado pelos valores de pixel. Vamos dar uma olhada na
visualização das imagens e no conjunto de dados (Figura 5-8 ).

Figura 5-8.  Conjunto de dados MNIST

Devemos criar dois quadros de dados em Python. Um


armazenará todos os valores de pixel X; o outro armazenará o
número real y.
y = input_data ['rótulo']
input_data.drop ('label', axis = 1, inplace = True)
X = input_data

114

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Agora, convertemos os rótulos presentes em y em dummies


(consulte Termos importantes).

y = pd.get_dummies (y)

Agora que temos nossos dados em X e y, podemos começar


com nossa rede neural. Vamos criar quatro camadas ocultas,
uma camada de entrada e uma camada de saída usando Keras
.

classificador = Sequencial ()
classifier.add (Dense (unidades = 600, kernel_initializer
= 'uniforme', ativação = 'relu', input_dim = 784))
classifier.add (Denso (unidades = 400, kernel_initializer
= 'uniforme', ativação = 'relu')) classifier.add (Dense
(unidades = 200, kernel_initializer = 'uniforme',
ativação = 'relu')) classifier.add (Denso (unidades = 10,
kernel_initializer = 'uniforme', ativação = 'sigmóide'))

A primeira camada oculta tem 600 neurônios, a segunda


tem 400, a terceira tem 200 e a última tem dez neurônios. Em
seguida, inicializamos os parâmetros w e b no formato
normalizado, fornecendo kernel_initializer = 'uniforme'. Nas
primeiras três camadas, fornecemos a função de ativação relu;
a última camada contém a função sigmóide. Além disso, a
primeira camada contém a dimensão de entrada de 784. Nossa
rede agora se parece com a representada na Figura 5-9 .

115
Capítulo 5 Processamento de imagens usando aprendizado de máquina

Sigmóide
Ativação
Função

784 600 400 200 10

Figura 5-9.  Rede neural profunda

Nota Na Figura 5-9 , todo e qualquer nó está conectado a


todos os nós da próxima camada.

Agora precisamos calcular o algoritmo de descida gradiente


estocástico * para minimizar a perda:

classifier.compile (optimizer = 'sgd', loss = 'mean_squared_


error', metrics = ['precisão'])

Finalmente, começamos o treinamento dando um tamanho


de lote * de dez e épocas * de dez:

classifier.fit (X_train, y_train, batch_size = 10, epochs = 10)

Isso nos dá uma precisão de 98,95. Vejamos a saída:

116

Capítulo 5 Processamento de imagens usando aprendizado de máquina


Finalmente, fazemos previsões no conjunto de dados de
teste. Primeiro, fazemos o upload e, em seguida, fazemos as
previsões:

test_data = pd.read_csv ("test.csv")


y_pred = classifier.predict (test_data)

Todas as previsões são salvas dentro da variável y_pred.


Vejamos o código completo:

import pandas as
pd import keras
from keras.models import
Sequential from keras.layers
import Dense

input_data = pd.read_csv ("train.csv")

y = input_data ['rótulo']
input_data.drop ('label', axis = 1, inplace =
True) X = input_data
y = pd.get_dummies (y)

classificador = Sequencial ()

117

Capítulo 5 Processamento de imagens usando aprendizado de máquina

classifier.add (Dense (unidades = 600, kernel_initializer =


'uniforme', ativação = 'relu', input_dim = 784)) classifier.add
(Denso (unidades = 400, kernel_initializer = 'uniforme',
ativação = 'relu')) classifier.add (Dense (unidades = 200,
kernel_initializer = 'uniforme', ativação = 'relu'))
classifier.add (Dense (unidades = 10, kernel_initializer =
'uniforme', ativação = 'sigmoide')) classifier.compile (
optimizer = 'sgd', loss = 'mean_squared_ error', metrics =
['precisão'])

classifier.fit (X, y, batch_size = 10, epochs = 10)

test_data = pd.read_csv ("test.csv")


y_pred = classifier.predict (test_data)

Classificação de imagens usando CNNs


CNNs são usados para problemas de classificação e
processamento de imagens. Na seção anterior, vimos como usar
uma rede neural artificial e aplicá-la a um conjunto de dados
MNIST. Nesta seção, daremos uma olhada nas CNNs e sua
aplicação ao mesmo conjunto de dados.
Com CNNs, existem algumas camadas extras, além das
camadas normais da rede neural. Na seção anterior, vimos que
todo e qualquer nó está conectado a todos os nós da próxima
camada. Isso pode ser demorado e também levar ao problema
de sobreajuste *. CNNs são usados para corrigir esse problema.
Com CNNs, não temos uma conexão com cada um dos nós. Com
CNNs, aplicamos filtragem seletiva. A Figura 5-10 mostra a
estrutura básica de uma CNN simples.

118

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Figura 5-10.  Resumo de uma CNN

Um breve resumo de como funcionam as CNNs começa com


a camada de convolução, onde aplicamos um filtro (também
conhecido como kernel ) à imagem de entrada. Este kernel
avança sobre a imagem, bloco a bloco, onde cada bloco é uma
coleção de células de pixel. Durante este processo, realizamos a
multiplicação da matriz, o que resulta em uma imagem de
menor resolução. Na camada de subamostragem (também
chamada de camada de redução da resolução ) encontramos o
valor médio do pixel (denominado pooling médio ) ou o valor
máximo do pixel (denominado pooling máximo ) e obtemos uma
imagem de resolução ainda mais baixa. Por último, a saída é
conectada à camada totalmente conectada, onde cada saída
máxima do pooling é conectada a cada nó na camada
totalmente conectada. Depois disso, uma arquitetura de rede
neural é seguida. Para obter uma explicação detalhada de como
funcionam os CNNs, consulte o curso de Andrew Ng no
Coursera
( https://www.coursera.org/learn/neural-networks-deep-learning ).
Vejamos como aplicar um CNN ao conjunto de dados MNIST.
Primeiro, criamos um arquivo Python com o nome
load_and_preprocess e o importamos em nosso código para
fazer o pré-processamento de dados. Em seguida, encontramos
os conjuntos de dados de treinamento e teste. Neste código, não
uso o conjunto de dados de teste fornecido pelo Kaggle; em vez
de,
Mostro como bifurcar os dados em um conjunto de dados de
treinamento e um conjunto de dados de teste e, em seguida,
verificar a precisão do conjunto de dados de teste. Primeiro,
vamos analisar o código do módulo load_and_preprocess.

119

Capítulo 5 Processamento de imagens usando aprendizado de máquina

\ 1. \ Primeiro, definimos a dimensão da


imagem. Cada imagem no conjunto de
dados é 28 × 28. Salvaremos o número de
linhas de pixel e o número de colunas de
pixel dentro das variáveis r e c:

r, c = 28, 28

\ 2. \ Definimos então o número de classes usando rótulos de


0 a 9, o que significa que há um total de dez classes:

num_classes = 10

\ 3. \ Dentro do módulo keras, temos todo o


conjunto de dados MNIST. Portanto, em vez
de usar a planilha .csv baixada do Kaggle,
usamos o conjunto de dados do Keras
diretamente:

de keras.datasets import mnist

\ 4. \ Em seguida, extraímos os conjuntos de


treinamento e teste chamando o método
integrado dentro de Keras chamado
load_data ():

(x_train, y_train), (x_test, y_test) = mnist.load_data ()


\ 5. \ Em seguida, remodelamos a funcionalidade
do numpy. Atualmente, os dados estão em
um formato de matriz sem estrutura
adequada. Usando reshape, damos a
estrutura de dados. Fazemos isso dizendo ao
Python para converter a matriz de forma que
tenha todos os valores de pixel em apenas
uma coluna:

x_train = x_train.reshape (x_train.shape [0], r, c, 1)


x_test = x_test.reshape (x_test.shape [0], r, c, 1)

120

Capítulo 5 Processamento de imagens usando aprendizado de máquina

\ 6. \ Atualmente, o tipo de dados de x_train e


x_test é Int (inteiro). Queremos convertê-lo
para Float, para que possamos aplicar o pré-
processamento sobre ele facilmente:

x_train = x_train.astype ('float32')


x_test = x_test.astype ('float32')

\ 7. \ Agora precisamos fazer a normalização.


Dividimos cada pixel com o valor de
intensidade de pixel mais alto de 255, de
modo que os dados resultem em uma faixa
inferior de zero a um. Isso ajuda no
treinamento do modelo de forma eficiente.

x_train / = 255
x_test / = 255

\ 8. \ Agora que cuidamos das variáveis


independentes *, precisamos cuidar das
variáveis dependentes *, que são os rótulos
de número reais. Para fazer isso,
convertemos os valores, que atualmente
estão no formato inteiro, em valores
categóricos *:

y_train = keras.utils.to_categorical (y_train, num_classes)


y_test = keras.utils.to_categorical (y_test, num_classes)

\ 9. \ Por último, devolvemos todos os dados


processados ao nosso código original:

return (x_train, x_test, y_train, y_test, input_shape)


Vejamos o código completo:

de keras.datasets import mnist


import keras

def load_and_preprocess
(): r, c = 28, 28

121

Capítulo 5 Processamento de imagens usando aprendizado de máquina

num_classes = 10
x_train, y_train, x_test, y_test = mnist.load_data ()
x_train = x_train.reshape (x_train.shape [0], r, c, 1)
x_test = x_test.reshape (x_test.shape [0], r, c, 1 )
input_shape = (r, c, 1)

x_train = x_train.astype ('float32')


x_test = x_test.astype ('float32')

x_train / = 255
x_test / = 255

# converter vetores de classe em matrizes de classe binárias


y_train = keras.utils.to_categorical (y_train, num_classes)
y_test = keras.utils.to_categorical (y_test, num_classes)
return (x_train, x_test, y_train, y_test, input_shape)

Chegando ao nosso código principal, vamos agora declarar


nossas camadas de convolução e subamostragem:

modelo = Sequencial ()
model.add (Conv2D (32, kernel_size = (3, 3), activation =
'relu', input_shape = input_shape))
model.add (Conv2D (64, (3, 3), activation = 'relu'))
model.add (MaxPooling2D (pool_size = (2, 2)))
model.add (Dropout (0,25)) model.add (Flatten ())

model.add (Dense (128, ativação = 'relu')) model.add


(Dropout (0,5)) model.add (Denso (num_classes,
ativação = 'softmax'))

No código anterior, definimos duas camadas de convolução


usando a função Conv2D. A saída do segundo é dada à camada
de subamostragem. O dropout é usado para fazer com que nosso
treinamento evite overfitting. Seu valor está entre zero e um, e
podemos experimentar diferentes valores
122

Capítulo 5 Processamento de imagens usando aprendizado de máquina

para encontrar uma melhor precisão. A função Denso ajuda a


fornecer a saída das funções de ativação relu ou softmax
(consulte os Termos importantes).
Em seguida, minimizamos os erros usando o
algoritmo AdaDelta (consulte Termos importantes):

model.compile (loss = keras.losses.categorical_crossentropy,


optimizer = keras.optimizers.Adadelta (), metrics =
['precisão'])

Finalmente, começamos o treinamento com um tamanho de


lote de 128 e épocas de 12. Fornecemos os dados de validação
dos parâmetros, para que ele possa aplicar o modelo treinado ao
conjunto de dados de teste e nos permitir ver a precisão disso
também.

model.fit (x_train, y_train, batch_size = 128, épocas =


12, validação_data = (x_test, y_test))

Para imprimir a precisão, usamos o seguinte código:

score = model.evaluate (x_test, y_test, verbose = 0)


print ('Perda do teste:', pontuação [0])
imprimir ('Teste de precisão:', pontuação [1])

Vejamos o código completo. A saída é apresentada na


Figura 5-11 . Código principal

importar keras
de keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
de Load_and_Preprocess import *

x_train, x_test, y_train, y_test, input_shape =


load_and_preprocess () num_classes = 10
model = Sequential () model.add (Conv2D (32,
kernel_size = (3, 3), activation = 'relu',
input_shape = input_shape)) model.add (Conv2D
(64, (3, 3), activation = 'relu '))

123

Capítulo 5 Processamento de imagens usando aprendizado de máquina


model.add (MaxPooling2D (pool_size = (2,
2))) model.add (Dropout (0.25)) model.add
(Flatten ()) model.add (Dense (128,
ativação = 'relu'))

model.add (Dropout (0,5))

model.add (Denso (num_classes, ativação = 'softmax'))

model.compile (loss =
keras.losses.categorical_crossentropy,
optimizer = keras.optimizers.Adadelta (),
metrics = ['precisão'])

model.fit (x_train, y_train, batch_size = 128,


épocas = 12, validação_data =
(x_test, y_test))

score = model.evaluate (x_test, y_test, verbose = 0)


print ('Perda do teste:', pontuação [0])
imprimir ('Teste de precisão:', pontuação [1])

Load_and_Preprocess

de keras.datasets import mnist


import keras

def load_and_preprocess
(): r, c = 28, 28
num_classes = 10

(x_train, y_train), (x_test, y_test) = mnist.load_data ()


x_train = x_train.reshape (x_train.shape [0], r, c, 1) x_test
= x_test.reshape (x_test.shape [0], r , c, 1) input_shape =
(r, c, 1)

124

Capítulo 5 Processamento de imagens usando aprendizado de máquina

x_train = x_train.astype ('float32')


x_test = x_test.astype ('float32')

x_train / = 255
x_test / = 255
y_train = keras.utils.to_categorical (y_train, num_classes)
y_test = keras.utils.to_categorical (y_test, num_classes)
return (x_train, x_test, y_train, y_test, input_shape)

Resultado

Figura 5-11.  Saída de código

Classificação de imagens
usando abordagens de
aprendizado de máquina
Nesta seção, veremos a aplicação de três algoritmos de
aprendizado de máquina famosos:
• \Árvores de decisão
• \Máquinas de vetor de suporte (SVMs)

• \Regressão logística

125

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Primeiro, vamos examinar os fundamentos desses três


algoritmos. Em seguida, os aplicamos ao conjunto de dados
MNIST, que - como mencionado anteriormente - é um enorme
banco de dados de dígitos manuscritos. Usamos este conjunto
de dados para reconhecimento de escrita.

Árvores de decisão
Quando queremos tomar uma grande decisão em nossas vidas,
fazemos uma análise de prós e contras. As árvores de decisão são
semelhantes a este método. Com base em um certo limite
estatístico, determinamos se uma determinada coisa pertence a
uma classe ou a outra. Suponha que desejemos descobrir se uma
pessoa é indiana ou estrangeira. O primeiro limite pode ser olhar
para a cor da pele. O próximo limite pode ser o tom de voz. Outro
limite pode ser físico. Depois de aplicar todos esses limites,
fazemos uma árvore que nos ajuda a determinar a categoria a
que essa pessoa pertence. Não estudamos os detalhes estatísticos,
mas alguns dos termos importantes relacionados às árvores de
decisão são os seguintes:
• \ Nó : Um bloco na árvore

• \ Nó puro : um nó que contém elementos de


uma única classe, como pessoas que são
estrangeiras
• \ Pureza : O grau dos mesmos elementos de classe em um nó

• \ Entropia : Um método estatístico usado para


determinar um limite

• \ Ganho de informação : A diferença entre


entropias de dois níveis de nós; usado para
decidir quando parar a geração de árvores

126

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Máquinas de vetor de suporte


Os SVMs usam o conceito de planos matemáticos ( hiperplanos
de margem máxima ) para distinguir entre várias classes.
Portanto, usando nosso exemplo anterior, os SVMs desenham
um plano entre duas classes - em nosso caso, índios e
estrangeiros. Tentamos maximizar a distância desse plano de
ambas as classes, por isso é chamado de hiperplano de
margem máxima . Para construir este hiperplano, usamos o
conceito de vetores de suporte, que são os pontos mais externos
de cada classe. No caso de classificação linear (ver termos
importantes), esta margem é desenhada diretamente. Mas,
quando se trata de classificações não lineares, os SVMs usam o
truque do kernel para converter não linear em linear e, em
seguida, encontrar o hiperplano.

Regressão Logística
A regressão logística é um dos algoritmos mais famosos em
aprendizado de máquina. É uma forma modificada de
regressão linear em que usamos logits para determinar a
probabilidade de um elemento pertencer a uma classe
particular. Isso nos dá uma saída entre zero e um. Se a saída for
maior que 0,5, diz-se que o elemento pertence a uma classe;
caso contrário, pertence ao outro. Também podemos desenhar
uma curva para testar a eficiência do nosso modelo.

Código
Agora que sabemos o básico de todos os três algoritmos, vamos
aplicá-los ao nosso conjunto de dados. A primeira etapa é ler o
conjunto de dados usando a biblioteca pandas e armazená-lo nos
dados variáveis:
importar pandas como pd
data = pd.read_csv ("train.csv")

127

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Em seguida, encontramos nossas variáveis dependentes e


independentes. Armazenamos nossa variável dependente na
variável y e nossa variável independente em X:

y = data ['label'] data.drop ('label', axis =


1, inplace = True) X = data

y = pd.Categorical (y)

Depois de fazer isso, importamos nossos três algoritmos,


que são salvos no módulo sklearn:

de sklearn.linear_model import LogisticRegression


de sklearn.tree import DecisionTreeClassifier de
sklearn.svm import LinearSVC
Com as bibliotecas importadas, temos que fazer uma instância delas:

logreg = LogisticRegression ()
dt = DecisionTreeClassifier ()
svc = LinearSVC ()

Agora, todo o nosso algoritmo é armazenado em três


variáveis respectivas: logreg, dt e svc. Em seguida, temos
que treinar nosso modelo. Chamamos a função de ajuste,
que inicia o treinamento diretamente:
model_logreg = logreg.fit (X, y)
model_dt = dt.fit (X, y)
model_svc = svc.fit (X, y)

Depois de salvar nossos modelos treinados em nossas


variáveis, tentamos prever os novos valores presentes no
conjunto de dados de teste:

X_test = pd.read_csv ("test.csv")


pred_logreg = model_logreg.predict
(X_test) pred_dt = model_logreg.predict
(X_test) pred_svc = model_logreg.predict
(X_test)

128

Capítulo 5 Processamento de imagens usando aprendizado de máquina

Também podemos verificar a precisão do nosso modelo treinado no


conjunto de dados do trem:

de sklearn.accuracy import precision_score


pred1 = model_logreg.predict (X)
pred2 = model_dt.predict (X)
pred3 = model_svc.predict
(X)
print ("A precisão da árvore de decisão é:", exatidão_score (pred1, y) *
100)
print ("A precisão da regressão logística é:", precisão_
pontuação (pred2, y) * 100)
print ("A precisão da máquina de vetor de suporte é:",
exatidão_ pontuação (pred3, y) * 100)

Vejamos o código completo e a saída:

importar pandas como pd


data = pd.read_csv ("train.csv") y = data
['label'] data.drop ('label', axis = 1,
inplace = True) X = data

y = pd.Categorical (y)
de sklearn.linear_model import LogisticRegression
de sklearn.tree import DecisionTreeClassifier de
sklearn.svm import LinearSVC
logreg = LogisticRegression ()
dt = DecisionTreeClassifier ()
svc = LinearSVC ()
model_logreg = logreg.fit (X, y)
model_dt = dt.fit (X, y)
model_svc = svc.fit (X, y)

X_test = pd.read_csv ("test.csv")


pred_logreg = model_logreg.predict
(X_test) pred_dt = model_dt.predict (X_test)
pred_svc = model_svc.predict (X_test)

129

Capítulo 5 Processamento de imagens usando aprendizado de máquina

de sklearn.accuracy import precision_score


pred1 = model_logreg.predict (X)
pred2 = model_dt.predict (X)
pred3 = model_svc.predict
(X)
print ("A precisão da árvore de decisão é:", exatidão_score
(pred_dt, y) * 100)

print ("A precisão da regressão logística é:", exatidão_score


(pred_ logreg, y) * 100)
print ("A precisão da máquina de vetor de suporte é:",
exatidão_ pontuação (pred_svc, y) * 100)

Resultado:

A precisão da árvore de decisão é: 100,0


A precisão da regressão logística é: 93.8547619047619
A precisão da máquina de vetores de suporte é: 88.26190476190476

Termos importantes
O algoritmo AdaDelta é uma alternativa
ao algoritmo de descida gradiente; o
modelo aprende com o conjunto de dados
automaticamente sem predefinir a taxa de
aprendizado (obrigatório no algoritmo de
descida de gradiente); ajuda a eliminar os
problemas de overfitting e underfitting

partes do tamanho do lote de dados (lotes)


passados um de cada vez dentro de um
modelo até que o conjunto de dados termine;
depois que todos os lotes são processados, o
resultado é uma época

rótulos de valores categóricos que não são


numéricos (por exemplo, gatos, cães, alto,
médio, baixo); oposto de uso no conjunto de
dados MNIST, onde rótulos são números, o
que torna as previsões mais eficazes

130

Capítulo 5 Processamento de imagens usando aprendizado de máquina

variável dependente um elemento que


estamos realmente prevendo

dummies quando convertemos variáveis


categóricas em números binários atribuídos
a cada categoria

épocas o número de etapas que um


modelo executa para minimizar o erro

algoritmo de descida gradiente usado para


minimizar o erro com base nos conceitos de
retropropagação e diferenciação; o modelo
aprende com esse algoritmo todos os
recursos importantes presentes no conjunto
de dados, o que o ajuda a fazer previsões
eficientes

matriz de homografia uma forma de


mapear duas imagens para encontrar os
padrões comuns entre elas; usado para
registro de imagem
elemento de variável independente
usado para prever variáveis
dependentes

classificação linear uma forma de


classificar elementos com base na
bifurcação em linha reta
overfitting quando um modelo leva em
consideração todos os recursos, incluindo
recursos desnecessários; dá os resultados
errados
teste de proporção pelo Dr. Lowe um teste
usado para determinar se os recursos
extraídos das imagens podem ser usados
para encontrar a similaridade entre eles

softmax uma função de ativação usada


para fazer classificações depois que um
modelo é feito com o treinamento; ajuda a
decidir a qual categoria o elemento
pertence; tem um valor entre zero e um

131

Capítulo 5 Processamento de imagens usando aprendizado de máquina

conjunto de teste parte de um


conjunto de dados em que testamos a
eficiência do nosso modelo

conjunto de treinamento parte de um


conjunto de dados usado para treinar e fazer
um modelo

underfitting quando um modelo é incapaz


de cuidar de todos os recursos importantes
presentes e, portanto, dá resultados errados

132
CAPÍTULO 6

Casos de uso em
tempo real
Agora que examinamos os conceitos básicos e avançados do
processamento de imagens, é hora de examinar alguns casos
de uso em tempo real . Neste capítulo, examinamos cinco POCs
diferentes, que podem ser ajustados com base em seus
próprios requisitos:
\ 1. \ Encontrando linhas de palmeira
\ 2. \ Detectando Faces
\ 3. \ Reconhecendo Faces
\ 4. \ Movimentos de rastreamento
\ 5. \ Faixas de detecção

Encontrando Palm Lines


Usaremos Python e a biblioteca OpenCV para determinar as
principais linhas de palmas presentes em nossa palma.
Primeiro, precisamos ler a imagem original:

import cv2
image = cv2.imread ("palm.jpg") cv2.imshow ("palm",
imagem) #para visualizar a palma em python
cv2.waitKey (0)

Agora, convertemos a imagem em tons de cinza:

cinza = cv2.cvtColor (imagem, cv2.COLOR_BGR2GRAY)


© Himanshu Singh 2019 133
H. Singh, Practical Machine Learning and Image
Processing ,
https://doi.org/10.1007/978-1-4842-4149-3_6

Capítulo 6 Casos de uso em tempo real

Em seguida, usamos o algoritmo de filtro Canny Edge


Detector para encontrar as linhas da palma. Para imagens
diferentes, precisamos alterar os parâmetros de acordo.
bordas = cv2.Canny (cinza, 40,55, apertureSize
= 3) cv2.imshow ("bordas na palma", bordas)
cv2.waitKey (0)

Agora, reverteremos as cores para que as linhas reconhecidas sejam


pretas:

bordas = cv2.bitwise_not (bordas)

Resultado:

Em seguida, mesclamos a imagem anterior com a imagem original:

cv2.imwrite ("palmlines.jpg", bordas)


palmlines = cv2.imread
("palmlines.jpg")
img = cv2.addWeighted (palmlines, 0.3, imagem, 0.7, 0)

134

Capítulo 6 Casos de uso em tempo real

Resultado final:
Podemos alterar os parâmetros para obter uma saída mais eficaz.

Detectando rostos
Nesta seção, aplicamos o código de reconhecimento facial a
uma imagem que contém um rosto e, em seguida, aplicamos o
mesmo código a uma imagem com vários rostos. A primeira
coisa que devemos fazer é importar as bibliotecas
importantes:

import cv2
import matplotlib.pyplot as plt

Em seguida, lemos a imagem contendo um rosto.


Depois de lê-lo, o convertemos para tons de cinza e o
mostramos em uma nova janela:

img1 = cv2.imread ("single_face.jpg")


gray_img = cv2.cvtColor (img11,
cv2.COLOR_BGR2GRAY) cv2.imshow
("Original_grayscale_image", gray_img)
cv2.waitKey (0)

135

Capítulo 6 Casos de uso em tempo real

Agora precisamos aplicar Haar Cascade sobre a imagem.


Temos várias Haar Cascades para detectar várias coisas já
armazenadas no projeto OpenCV. Por conveniência, anexei as
cascatas necessárias, armazenadas no formato de arquivo XML,
no sharepoint deste livro. A seguir está uma lista de cascatas:

• \ haarcascade_eye.xml
• \ haarcascade_eye_tree_eyeglasses.xml
• \ haarcascade_frontalcatface.xml
• \ haarcascade_frontalface_alt.xml
• \ haarcascade_frontalface_alt2.xml
• \ haarcascade_frontalface_alt_tree.xml

• \ haarcascade_frontalface_default.xml
• \ haarcascade_fullbody.xml
• \ haarcascade_lefteye_2splits.xml
• \ haarcascade_lowerbody.xml
• \ haarcascade_profileface.xml
• \ haarcascade_reciationye_2splits.xml
• \ haarcascade_smile.xml
• \ haarcascade_upperbody.xml
Em nosso caso, usaremos haarcascade_frontalface_alt.xml:

haar_face_cascade = cv2.CascadeClassifier
('haarcascade_ frontalface_alt.xml')
faces = haar_face_cascade.detectMultiScale
(gray_img, scaleFactor = 1.1, minNeighbors = 5)
para (x, y, w, h) nas faces:
cv2.rectângulo (img1, (x, y), (x + w, y + h), (0, 255, 0), 2)

136

Capítulo 6 Casos de uso em tempo real

O código anterior carrega o algoritmo em cascata em uma


variável. Então, usando esse algoritmo, ele tenta detectar os
rostos e desenhar um círculo sobre o rosto detectado. scaleFactor
é usado para cuidar de rostos grandes e pequenos. Se você
estiver mais perto da câmera, parece ter um rosto grande; caso
contrário, parece menor. minNeighbors olha para o rosto
detectado dentro de um retângulo e decide o que incluir e o que
rejeitar. Agora, vamos mostrar a imagem detectada:

cv2.imshow ("Final_detected_image", cv2.COLOR_BGR2RGB


(img1)) cv2.waitKey (0)

As linhas de código anteriores fornecem a seguinte saída:


137

Capítulo 6 Casos de uso em tempo real

Se usarmos outra imagem contendo várias faces, em vez de


uma, o código fornecerá esta saída:

Reconhecendo rostos
Detectamos com sucesso os rostos presentes na imagem, mas
como reconhecemos qual rosto pertence a quem? Para
descobrir isso, usaremos métodos OpenCV avançados.
O primeiro passo é detectar o rosto. Incorporamos
exatamente o mesmo código da seção anterior dentro do
método detect_face ():

def detect_face (img):


cinza = cv2.cvtColor (img, cv2.COLOR_BGR2GRAY)
face_cascade = cv2.CascadeClassifier
('haarcascade_ frontalface_alt.xml')
faces = face_cascade.detectMultiScale
(cinza, scaleFactor = 1,2, minNeighbors = 5)
(x, y, w, h) = faces [0]
retornar cinza [y: y + w, x: x + h], faces [0]

138

Capítulo 6 Casos de uso em tempo real

Em vez de desenhar um retângulo ao redor da face, o


código retorna as coordenadas.
A próxima etapa é fornecer dados suficientes para que o
sistema possa aprender que vários rostos pertencem a uma
pessoa específica. Da próxima vez, será capaz de identificar a
pessoa a partir de uma nova imagem.

def prepare_training_data
(data_folder_path): dirs = os.listdir
(data_folder_path) faces = []

rótulos = []
para dir_name em dirs:
se não dir_name.startswith ("s"):
continue

rótulo = int (dir_name.replace ("s", ""))


subject_dir_path = data_folder_path + "/" + dir_name
subject_images_names = os.listdir (subject_dir_path)
para image_name em subject_images_names:
image_path = subject_dir_path + "/" +
image_name image = cv2.imread (image_path)
face, rect = detect_face
(imagem) se a face não for
Nenhum:
faces.append (face)
labels.append (label)
cv2.waitKey (0)
cv2.destroyAllWindows
() retorna faces, rótulos

O código anterior primeiro lê cada uma das imagens


presentes em uma pasta específica, depois tenta detectar o rosto
e armazenar as coordenadas do rosto nas listas de faces [] e
rótulos []. Nesse código, o nome da pessoa deve ser o nome da
pasta, e todas as imagens dessa pessoa devem ser mantidas na
pasta. A função acima retorna todas as coordenadas e rótulos
de face, que posteriormente ajudam a treinar os dados.

139

Capítulo 6 Casos de uso em tempo real

Em seguida, vem a parte do treinamento. Para isso,


usaremos a função LBPHFaceRecognizer. Vamos aplicar a
função de treinamento aos rostos e rótulos:

face_recognizer = cv2.face.LBPHFaceRecognizer_create
() face_recognizer.train (faces, np.array (rótulos))

Este código treina o modelo, olhando para as


coordenadas da face e os rótulos.
A próxima coisa que precisamos fazer é prever. Suponha
que tentemos reconhecer duas faces. Eu peguei as imagens de
Ranveer e Sachin Tendulkar. O modelo foi treinado nessas
imagens e tentará prever novas imagens.

assuntos = ["", "Sachin Tendulkar", "Ranveer"]


img = "ranveer.jpg"
face, rect = detect_face (img)
label = face_recognizer.predict (face) [0]
label_text = subject [label]
(x, y, w, h) = rect
cv2.rectangle (img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText (img, label_text, (rect [0], rect [1] -5), cv2.FONT_
HERSHEY_PLAIN, 1,5, (0, 255, 0), 2)

Este código lê a imagem e tenta prever se a imagem é de


Ranveer. Vamos ver a saída deste código. Todo o código está
presente no sharepoint.

140

Capítulo 6 Casos de uso em tempo real

Movimentos de rastreamento
Suponha que você tenha uma caneta, mas em vez de escrever
em um pedaço de papel, você escreve no ar e as coisas são
escritas automaticamente. Parece mágica? Bem, é possível
fazer isso usando métodos avançados de processamento de
imagem.
Suponha que eu tenha um fabricante com uma ponta azul.
Quero movê-lo no ar e ter uma câmera rastreando a ponta azul e
desenhando exatamente os mesmos movimentos na tela. Vamos
ver como podemos fazer isso acontecer.
Primeiro, temos que capturar apenas a ponta azul. Isso
significa que todas as outras coisas - a mão, o fundo e assim
por diante - não passam de ruído. Temos que remover o
ruído, e fazemos isso usando erosão e dilatação para.
Vejamos o código:

mask = cv2.inRange (hsv, Lower_green, Upper_green) mask =


cv2.erode (mask, kernel, iterations = 2) mask =
cv2.morphologyEx (mask, cv2.MORPH_OPEN, kernel) mask =
cv2.dilate (mask, kernel, iterações = 1) res = cv2.bitwise_and
(img, img, máscara = máscara) cnts, herdeiro =
cv2.findContours (mask.copy (), cv2.RETR_EXTERNAL, cv2.
CHAIN_APPROX_SIMPLE) [- 2:]

141

Capítulo 6 Casos de uso em tempo real

Em seguida, temos que definir a gama de cores de azul que queremos:

Inferior_verde = np.array ([110,50,50])


Superior_verde = np.array ([130.255.255])

Agora precisamos rastrear o movimento. Para fazer isso,


encontramos os contornos e, em seguida, os momentos da
imagem, que são usados posteriormente para desenhar as linhas.
Para saber mais sobre os momentos e contornos da imagem,
consulte o Apêndice.

se len (cnts)> 0:
c = max (cnts, key = cv2.contourArea)
((x, y), raio) = cv2.minEnclosingCircle © M
= cv2.moments ©
center = (int (M ["m10"] / M ["m00"]), int (M ["m01"]
/ M ["m00"]))
se raio> 5:
cv2.circle (img, (int (x), int (y)), int (raio), (0, 255,
255), 2)

cv2.circle (img, center, 5, (0,0,255), -1)


pts.appendleft (center)
para i no intervalo (1, len (pts)):
se pts [i-1] for Nenhum ou pts [i] for
Nenhum: continue
espessura = int (np.sqrt (len (pts) / float (i + 1)) * 2.5)
cv2.line (img, pts [i-1], pts [i], (0,0,248), espessura)

Finalmente, nosso código está pronto. Vamos ver se nosso código


rastreia a caneta.

cv2.imshow ("Frame",
img) cv2.imshow
("mask", mask)
cv2.imshow ("res", res)

142

Capítulo 6 Casos de uso em tempo real

Obtemos a seguinte saída:

Detecção de pistas
Todos nós sabemos que os carros autônomos são uma das
maiores novidades na indústria automotiva atualmente. Os
carros sabem quando virar à esquerda, quando parar, como ler
os sinais de trânsito e assim por diante. Nesta seção,
aprendemos como um carro olha para uma pista em uma
rodovia e entende seu significado, definindo assim seus
limites - em outras palavras, não saindo da pista. Se você quiser
se tornar um especialista em programação de carros autônomos
, existe um programa de nanodegree da Udacity que você pode
assistir.
Para começar, primeiro precisamos calibrar nossa câmera.
Como todo o conceito de carro autônomo depende da precisão
da câmera, devemos calibrá-la. Para fazer isso, usamos uma
função no OpenCV chamada findChessboardCorners (), que
(como você pode imaginar) encontra os cantos internos de
uma determinada imagem de um tabuleiro de xadrez.
def convert3D_to_2D (caminho, x, y):
rwp = np.zeros ((y * x, 3), np.float32)
tmp = np.mgrid [0: x, 0: y] .T.reshape (-1,
2) rwp [:,: 2] = tmp
rwpoints = []

143

Capítulo 6 Casos de uso em tempo real

imgpoints = []
images = glob.glob
(path) para fname em
imagens:
img = cv2.imread (fname)
cinza = cv2.cvtColor (img, cv2.COLOR_BGR2GRAY)
corner_found, corners = cv2.findChessboardCorners
(cinza, (x, y), Nenhum)
if corner_found == True: rwpoints.append (rwp)
imgpoints.append (corners)
cv2.drawChessboardCorners (img, (x, y), corners,
corner_ found)

return (rwpoints, imgpoints)

Uma explicação completa linha por linha do código é fornecida no


Sharepoint. O código anterior lê diferentes imagens de tabuleiro
de xadrez presentes em um diretório, encontra seus cantos
internos e salva os pontos finais em duas listas: rwpoints e
imgpoints. rwpoints contêm pontos de espaço real em três
dimensões; Os pontos imgpoints estão em duas dimensões. A
função retorna ambas as listas.

Em seguida, usamos essas duas listas para calibrar a câmera


e, em seguida, não distorcer a imagem. Sem distorção significa
remover o ruído e suavizar
a imagem. Em termos mais simples, dizemos que
convertemos a imagem de três dimensões em duas
dimensões. Vejamos o código:

def calibrate_camera (test_img_path, rwpoints,


imgpoints): img = mpimg.imread (test_img_path)
img_size = (img.shape [1], img.shape
[0]) ret, mtx, dist, rvecs, tvecs = cv2.
calibrateCamera (rwpoints, imgpoints, img_size, None,
None) undst_img = cv2.undistort (img, mtx, dist, None,
mtx) f, (ax1, ax2) = plt.subplots (1, 2, figsize = (20, 10 ))

144

Capítulo 6 Casos de uso em tempo real

ax1.set_title ("Imagem Original")


ax1.imshow (img) ax2.set_title
("Imagem Não distorcida")
ax2.imshow (undst_img)
retorno (mtx, dist)

A função calibrateCamera () primeiro tenta tornar a


câmera eficiente usando as listas rwpoints e imgpoints. Isso
nos dá duas matrizes importantes - mtx e dst - para nos
ajudar a não distorcer a imagem. A saída da não distorção é
assim:

Como a não distorção está funcionando bem, vamos


aplicar o mesmo a uma imagem de estrada:

def undistort_test_img (mtx, dist):


test_image = mpimg.imread
("road.jpg")
undistorted_img = cv2.undistort (test_image, mtx, dist,
None, mtx)

f, (ax1, ax2) = plt.subplots (1, 2, figsize = (20, 10))


ax1.set_title ("Imagem original") ax1.imshow
(test_image) ax2.set_title ("Imagem não distorcida")
ax2. imshow (undistorted_img)

145
Capítulo 6 Casos de uso em tempo real

Depois de aplicar o código acima a uma imagem


de teste, obtemos a seguinte saída:

Agora que temos a imagem em duas dimensões (uma


imagem não distorcida), precisamos realizar a transformação da
perspectiva. Perspectiva, em termos leigos, é o ângulo e a
direção em que você vê uma coisa particular. Portanto, duas
pessoas olhando para uma determinada coisa sempre têm uma
perspectiva diferente.
No caso de carros autônomos , as câmeras sempre observam
as linhas das estradas. Essas linhas de estradas nunca são
constantes, portanto, sua perspectiva muda continuamente. Para
garantir que a perspectiva da câmera sempre permaneça
constante, usamos a transformação de perspectiva.
A primeira coisa que precisamos fazer é selecionar
quatro pontos na pista, que nos guiam durante a
transformação da perspectiva. Selecionamos esses pontos
aleatoriamente. I codificado esses pontos, de modo que elas
coincidam com as posições exatas na imagem.

src = np.float32 ([[203,


720], [585,
460], [695,
460], [1127,
720]])

dst = np.float32 ([[270,


720], [310, 0],
[960, 0],
[1010, 720]])

146

Capítulo 6 Casos de uso em tempo real

Agora, damos esses dois pontos à função


getPerspectiveTransform () para encontrar a matriz de
transformação de perspectiva e a matriz de transformação
de perspectiva inversa:
M = cv2.getPerspectiveTransform (src, dst)
Minv = cv2.getPerspectiveTransform (dst, src)

Usamos essas matrizes para transformação de perspectiva e,


mais tarde, para retornar à nossa imagem original. Primeiro,
vamos ver como fazer a transformação:

test_image = mpimg.imread ("road.jpg")


img_size = (test_image.shape [1], test_image.shape [0])
undistorted_img = cv2.undistort (test_image, mtx, dist, None,
mtx) i = draw_polygon (undistorted_img)
warped = cv2.warpPerspective (undistorted_img, M,
img_size) f, (ax1, ax2) = plt.subplots (1, 2, figsize = (20, 10))
ax1.set_title ("Imagem Original")

ax1.imshow (i)
ax2.set_title ("Imagem distorcida
distorcida") ax2.imshow (distorcida)

Quando executamos este código, obtemos a seguinte saída:

Finalmente, temos a imagem transformada. Convertemos a


imagem para o formato binário, para que a câmera seja capaz de
encontrar e entender as linhas da estrada. Para fazer isso,
usamos a transformação Sobel. Este algoritmo de detecção de
bordas nos ajuda a rastrear as linhas presentes na estrada.

147

Capítulo 6 Casos de uso em tempo real

Portanto, como mencionado, removemos a distorção e


alteramos a perspectiva da imagem, depois a convertemos
em uma imagem binária (uma combinação de preto e
branco). Depois disso, aplicamos outras transformações.

img = cv2.undistort (test_image, mtx, dist, None, mtx)


color_binary, edge_img = find_edges (img)
img_size = (bordas_img.shape [1], bordas_img.shape [0])
warped_img = cv2.warpPerspectiva (bordas_img, M,
img_size, bandeiras = cv2.INTER_LINEAR)
Para encontrar as bordas, criamos uma função chamada
find_edges, que é usada para detectar bordas e obter imagens
coloridas e binárias contendo essas bordas. Em seguida,
aplicamos a função warpPerspective () para usar a matriz M
para a transformação da perspectiva da imagem gerada.
Quando aplicamos o código anterior à imagem original,
obtemos os seguintes resultados:

Por último, voltamos as imagens que criamos à imagem


original. Só então seremos capazes de determinar se a câmera
está detectando as linhas da estrada corretamente. Para fazer
isso, usamos a segunda matriz: Minv.

148

Capítulo 6 Casos de uso em tempo real

Novamente usamos a função warpPerspective (),


mas com Minv em vez de M:

newwarp = cv2.warpPerspective (color_warp, Minv,


(warped_img. shape [1], warped_img.shape [0])

color_warp é usado para criar a imagem que contém as


linhas da estrada que o código detectou e as preenche com
cores. Mantemos nossa perspectiva original usando o Minv.
Uma explicação detalhada do código é fornecida no
Sharepoint. Vejamos a saída:
Portanto, detectamos com sucesso as linhas rodoviárias. O
carro detectou a estrada e o caminho que deve percorrer.

149

APÊNDICE

Conceitos e
terminologia
importantes
Adaboost
Adaboost é uma técnica que usa vários algoritmos de
classificação em todos os dados de treinamento. Primeiro,
pegamos uma amostra aleatória do conjunto de dados e a
chamamos de conjunto de treinamento . Adaboost atribui pesos a
cada um dos exemplos no conjunto de treinamento. Depois
disso, o primeiro classificador é executado. Quando a
classificação é concluída, o Adaboost analisa os exemplos
classificados incorretamente no conjunto de treinamento. Em
seguida, são atribuídos pesos maiores, assim como os exemplos
classificados corretamente. Isso ocorre para que, ao rodar o
segundo classificador, leve em consideração exemplos com pesos
maiores.
Além disso, os pesos também são atribuídos aos
classificadores. Quando o classificador dá uma saída, seu erro é
calculado. Com base na intensidade do erro, são atribuídos
pesos. Se o erro for inferior a 50% de precisão, o classificador
recebe um peso negativo; caso contrário, recebe um peso
positivo. A precisão de cinquenta por cento equivale a um peso
zero.
© Himanshu Singh 2019 151
H. Singh, Practical Machine Learning and Image
Processing , https://doi.org/10.1007/978-1-4842-4149-3

Apêndice Conceitos e terminologia importantes


     

Conjunto de dados original, D 1 Pesos de atualização, D 2 Pesos de atualização, D 3


     
   
_        
_
   
_  
      _ _       _ _     _ _ 
_ + + _ + + _ + +
      Classificador combinado
_ + _ + _ + _
 
   
 
  +  
   
 
  +  
      +  
    _ _
    +         +        +     _   +
           
 
+   
Classificador treinado Classificador treinado Classificador treinado +
      _    
 
_ _ _     +
         
_ _          _ _      
_ _    +    
_ + + _ + + _ + +
 
_ +  
_      +   _  
                          +      
+ + +
               
+ + +
                                

Figura A-1.  (Crédito: Research Gate)

Adaboost é usado mais amplamente no reconhecimento


de rosto. Já vimos como usamos as cascatas Haar, ou
cascatas LBPH, para detectar as faces. Adaboost desempenha
um papel muito importante nisso.

XGBoost
XGBoost significa Extreme Gradient Boosting. É um dos
algoritmos de classificação e regressão mais usados. Você
descobrirá que os vencedores de quase todas as competições de
análise, como kaggle, KDNuggets, Hackerearth e assim por
diante, usam esse conceito. O XGBoost usa a abordagem de
gradiente descendente para encontrar a melhor solução para
um problema. Seu poder computacional, bem como a precisão
de seus resultados, o tornam tão poderoso. Ele usa vários
núcleos de CPU para fornecer cálculos mais rápidos.
Em vez de atribuir pesos (como feito com Adaboost), o
XGBoost analisa os erros incompatíveis (também chamados de
resíduos ) para cada exemplo de treinamento e tenta criar um
modelo de regressão sobre eles. Assim, para cada iteração, um
novo modelo é criado, que nada mais é do que gradientes. Sua
saída minimiza o erro tanto quanto possível.

152

Apêndice Conceitos e terminologia importantes


Figura A-2. 

Redes Neurais Acopladas a Pulso


Redes neurais acopladas a pulso (PCNNs) foram concebidas
depois que a indústria adquiriu uma compreensão do córtex
visual de um gato, que é a área do cérebro de um gato que
processa informações visuais. Um PCNN pode ser usado para
segmentar uma imagem, reduzir o ruído, gerar recursos e muito
mais. Quando aplicamos PCNN às imagens, cada neurônio
corresponde a um pixel em uma imagem. Além disso, todos os
neurônios estão conectados uns aos outros para receber
estímulos. As informações fornecidas a cada neurônio e as
informações passadas de um neurônio a outro são
posteriormente combinadas usando uma função de ativação,
que resulta em uma saída de pulso. Os PCNNs são mais eficazes
do que outros modelos de processamento de imagem porque são
robustos ao ruído, cuidam das variações geométricas presentes
dentro de uma imagem e assim por diante. Para estudar uma
implementação de PCNNs no reconhecimento de padrões,
consulte https: // www. raulmuresan.ro/Papers/PCNN.pdf .

153

Apêndice Conceitos e terminologia importantes

Figura A-3.  (Crédito: Research Gate)

Gradiente descendente
O gradiente descendente é uma das melhores abordagens
usadas para otimização. Quando falamos sobre aprendizado de
máquina, sempre há alguns erros de previsão. Esse erro é
denotado pela função de custo. Se o valor da função de custo for
zero, nossa precisão de previsão é 100%. Para manter o valor da
função de elenco baixo, usamos uma abordagem de gradiente
descendente.
Por exemplo, vamos definir a função de custo como f (x) =
ax + b. a e b são os parâmetros. Imagine que a curva desta
função é semelhante
para uma tigela. Primeiro, fornecemos alguns valores aleatórios
para aeb, o que coloca nossa função de custo em uma posição
particular na curva. Nosso objetivo é alterar os valores de aeb de
forma que a função de custo alcance a parte inferior da tigela /
curva. Para fazer isso, usamos uma taxa de aprendizado. No
final da descida do gradiente, obtemos valores para a e b que são
zero ou muito, muito próximos disso.

154

Apêndice Conceitos e terminologia importantes


 

J (w) Inicial Gradiente


  peso  

Custo global mínimo


J min (w)

Figura A-4.  (Crédito: Hackernoon)

Descida gradiente estocástico


A descida gradiente estocástica é a versão mais rápida do
algoritmo de descida gradiente normal. Usando a descida
normal do gradiente, aeb são atualizados para cada registro
presente no conjunto de dados, o que torna o processo muito
demorado se o conjunto de dados for grande. A descida
gradiente estocástica, por outro lado, atualiza os dados após um
treinamento completo
a instância é concluída, não em registros de treinamento
individuais. Isso torna o processo mais rápido.
Com esse algoritmo, você pode descobrir que a função de
custo salta na curva, mas finalmente atinge a parte inferior da
curva.
Figura A-5.  (Crédito: Rumo à ciência de dados)

155

Apêndice Conceitos e terminologia importantes

AdaDelta
AdaDelta pertence à família das abordagens de descida
gradiente estocástica. Usando este método, além de seus
recursos de descida gradiente estocástico, é dada importância
especial ao valor dos coeficientes. Isso é chamado de ajuste de
parâmetro . Além disso, a taxa de aprendizado não é inicializada
com nenhum tipo de valor padrão; ele é atualizado
automaticamente. Portanto, envolve o mínimo de intervenção
manual e, portanto, melhor precisão.

Figura A-6.  Análise comparativa de várias


técnicas de otimização (Crédito:
akyrillidis.github.io)

Canny Edge Detector


Como o próprio nome sugere, o Canny Edge Detector é usado
para localizar as bordas presentes dentro de uma imagem.
Depois de encontrar as bordas, podemos usá-lo para diferentes
fins, como segmentação de imagem, extração de recursos,
detecção e assim por diante. Usando este algoritmo, a primeira
etapa é remover o ruído presente
na imagem. Se não for removida, a imagem errada pode ser
detectada e a análise pode ser afetada negativamente.

156
Apêndice Conceitos e terminologia importantes

Depois que a imagem é suavizada, aplicamos a


transformação de Sobel a ela, para encontrar os gradientes da
imagem. Este é um passo importante, porque nos ajuda a
encontrar os limites. Obtemos a magnitude dos gradientes, bem
como sua direção. A próxima etapa é remover todas as partes da
imagem que não são necessárias. Esta etapa finalmente nos
fornece vantagens, mas não temos certeza se elas estão corretas.
A etapa final determina se a imagem final contém arestas
que são realmente arestas. Para fazer isso, os valores limite são
usados. Qualquer borda com um valor maior que o limite é
considerada uma borda; caso contrário, não é.

Figura A-7.  (Crédito: pyimagesearch)

Transformação Sobel
Um dos principais usos da transformação de Sobel é detectar bordas.
Uma matriz 3 × 3 é usada para percorrer os pixels de uma
imagem horizontal e verticalmente. É semelhante aos kernels
em CNSNs. A saída de Sobel inclui derivadas.
Você pode estar se perguntando: Por que usamos gradientes?
Suponha que selecionamos o retrato de uma menina. Se um
pouco de seu cabelo está em seu rosto, a cor

157

Apêndice Conceitos e terminologia importantes

o tom muda repentinamente da cor da pele para o preto. Isso


deve ser representado como uma borda - e para isso
precisamos de gradientes. Quanto maior o valor dos gradientes,
menor será a diferença de tom. Portanto, para detectar bordas,
precisamos saber todas as localizações de pixels com um valor
de gradiente maior que um limite específico.

Figura A-8.  (Crédito: Research Gate)

Haar Cascade
Reconhecimento facial LBPH
Momentos de imagem
Os momentos da imagem são pesos especiais atribuídos a
pixels específicos com base nas propriedades que exigimos. Em
geral, determinamos os momentos da imagem após termos
concluído a segmentação da imagem.

158

Apêndice Conceitos e terminologia importantes

Figura A-9.  (Crédito: OpenCV)


Contornos de imagem
Suponha que temos dois pontos. Se traçarmos uma linha - uma
que não seja necessariamente reta - para conectar esses dois
pontos, esse cal será considerado um contorno. Assim, os
contornos são usados para detectar e analisar formas, para
realizar diferentes tipos de reconhecimento e assim por diante.

159

Apêndice Conceitos e terminologia importantes

Figura A-10.  (Crédito: Pyimagesearch)

Função Chessboard Corners


Quando tiramos fotos usando a câmera, a imagem pode
ficar distorcida por dilatação, erosão e assim por diante.
Além disso, se escalarmos a imagem para cima ou para
baixo, o fator de escala pode não ser o mesmo.
Portanto, para eliminar esses problemas, devemos calibrar a
câmera. Mas, primeiro, precisamos encontrar alguns pontos que
nos informam sobre a precisão. Para encontrar esses pontos,
recebemos uma imagem de tabuleiro de xadrez para referência.
Nosso código encontra os cantos internos presentes na placa. Se
usarmos um tabuleiro 8 × 8, o código precisa encontrar os cantos
internos 7 × 7. Quando acertamos, obtemos uma matriz de
dois pontos que usamos para calibrar a câmera.

160

Apêndice Conceitos e terminologia importantes

Figura A-11.  (Crédito: OpenCV)

Calibrar função de câmera


Quando determinamos os cantos internos do tabuleiro de
xadrez, e sempre conseguimos nosso ponto, nós os fornecemos
para calibrar a função da câmera. Essa função nos ajuda a
cuidar da distorção, rotação, dimensionamento e assim por
diante. Ele também retorna uma matriz de câmera, matriz de
distorção, matriz rotacional e assim por diante. Nós os
usaremos posteriormente em nosso código.

161

Apêndice Conceitos e terminologia importantes

Figura A-12.  (Crédito: OpenCV)

Função de transformação de perspectiva


Suponha que temos uma imagem, mas queremos vê-la de um
ponto de vista diferente. Digamos que selecionamos uma
imagem de satélite de um local e queremos sobrepô-la com uma
imagem de drone que é exatamente do mesmo lugar e escala da
imagem de satélite. Para fazer isso, usamos a transformação de
perspectiva.
A função que usamos é chamada getPerspectiveTransform
() e está presente no OpenCV. Fornecemos a imagem para a qual
queremos mudar a perspectiva e, em seguida, fornecemos a
matriz usada para a transformação. A imagem final que
recebemos tem uma perspectiva alterada.
Para retornar esta imagem ao original, usamos a função
warpPerspective (), que registra a imagem na imagem
original.
162

Apêndice Conceitos e terminologia importantes

Figura A-13.  (Crédito: Pyimagesearch)

163

Índice
UMA
AdaBoost, 151-152
Algoritmo AdaDelta, 123,
130, 156 Adjust_gamma, 57
Imagem bitmap
Instalação do
binária, 11
Anaconda
neurônios
Jupyter biológicos, 110
Notebook, 1 imagens bitmap,
macOS, 4 10-11
Ubuntu, 4
Windows, 2–3
C
Redes neurais artificiais
Calibre a função da
(ANN) neurônios
câmera, 161 Canny Edge
artificiais, 111
Detector, 156–157 cantos
quadros de dados, do tabuleiro de xadrez
criação, 114-115 camada função, 160-161
oculta, 112, 115
Função de círculo,
processamento de conversão de 55
imagem, banco de dados cores
112 MNIST, fluxograma de RGB para HSV, 44
processo 114 , descida RGB para LAB, 47
gradiente estocástica 113 RGB para XYZ,
algoritmo *, estrutura 45-46 RGB para
116 , conjunto de dados de YIQ, 50 RGB para
treinamento 111 , conjunto YPbPr, 51-52 RGB
de treinamento 112 , 114 para YUV, 48-49
predições, variável y_pred, Espaços de
117 cores, 13
HSV / HSL,
Pooling médio, 119
17 LAB, 18
LCH, 18
B RGB, 14–15
Curva de Bezier, 22, 56 XYZ, 15–16
Filtro bilateral , 72-73 YIQ, 21
YPbPr, 19
YUV, 20
Declarações condicionais, 37
© Himanshu Singh 2019 165
H. Singh, Practical Machine Learning and Image
Processing , https://doi.org/10.1007/978-1-4842-4149-3

Índice

Declarações de fluxo Curva de


de controle para Bézier, 56
loop, 36 círculos, 55
while loop, 35 linhas, 53
convolução, 27
convolucional retângulos,
neural 54
precisão de redes
(CNNs), 123 problemas
de classificação, 118
E
codificação, 121-125
camada totalmente Elipsoides, 23
conectada, 119 estimativa_função afina,
função Conv2D, 122 105 Extrapolação, 16
processamento de
imagem, 118
F
load_and_preprocess
módulo, 119, 121 Código de reconhecimento
conjunto de dados facial, 135-138 mapeamento
de recursos, algoritmo SIFT
MNIST, estrutura 119 ,
camada de bruteForce.match, 97
subamostragem 118 , 119, code, 93-94
122 conjuntos de dados cv2.BFMatcher
de treinamento e teste, function, 97
119 cv2.drawKeypoints, 96
direction of gradiente,
função cv2.convertScaleAbs 92 gaussians, 91
(), 85 função cv2.equalizeHist imagem em escala
(), 87 função de cinza, 95 pontos-
cv2.GaussianBlur (), 72 chave, 92-93, 97
função cv2.medianBlur, 71 distância de
função cv2.putText (), 68 Manhattan, 97
escala e rotação, 91
Sift_Operations.py,
D 95 construção
Função DataFrame, 43 espacial, 91
Estruturas de dados, 33-34
Árvores de decisão, 126
G
Deconvolução, imagem lunar,
25-26 Rede neural profunda, Correção gama, filtro
116 Função densa, 123 Gaussiano 24 , algoritmo
descendente de gradiente
Pontos por 71-72 , 116,
polegada (DPI), 10 130–131, 154
Criação de desenho

166

Índice

H pixels, 8-9
PPI e DPI,
Haar Cascade, 158
resolução 10
Hessian matrix, 92
, 9 SSIM, 25
técnicas, 29
Histogram equalization, Resolução de imagem,
87-88 Homography, 26-27 9 Limite de imagem,
matriz, 99, 101, 104, 80-82
131 espaço de cor HSV,
17
K
Keras, 5
EU J Kernel, 119
Classificação de imagem
ANN ( consulte Redes eu
neurais artificiais
(ANN)) Espaço de cor LAB,
CNNs ( consulte Redes detecção de 18 pistas,
neurais reconhecimento de
convolucionais face 143 LBPH, espaço
(CNNs)) de cor 158 LCH, 18
algoritmos de classificação linear, 127
aprendizado função de linha, 53
de máquina, regressão logística, 127
125 compressão sem
Contornos de perdas, 11 compressão
imagem, com perdas, 12
159 formatos de
arquivo de imagem
, 12–13 momentos de
M
imagem, 158 Codificação de
processamento de algoritmos de
imagem aprendizado de
Curva de máquina, 127, 129-130
Bezier, 22 árvores de decisão,
bitmap, 10-11 126 regressão
espaços de cores ( ver logística, 127 SVMs,
espaços de cores) 127
convolução, 27
deconvolução, 25-26 Pooling
elipsóides, 23 máximo, 119
filtro
formatos de arquivo mediano, 71
, 12–13 correção
gama, 24
homografia, 26–27
compressão sem
perdas, 11
compressão com
perdas, 12 imagem
normal, 8

167

Índice
Instituto Nacional de Padrões
e Tecnologia
Modificado (MNIST), Limiar de pixel, 16
112 polígonos, 54 redes
neurais acopladas a pulso
(PCNNs), 153
N Pitão
Redes neurais, declarações condicionais,
110 imagem 37-38 declarações de fluxo
normal, 8 matriz de controle, 34-36
numpy, 42 estruturas de dados, 33-34
funções, 38-40
variáveis e tipos de dados, 30-32
O
OpenCV, 61 R
combinação de duas
Algoritmo de consenso
imagens, contraste e
de amostra
brilho de 64-65 , cálculo
aleatória
de gradientes de 66-67 ,
(RANSAC), registro
equalização de
de imagem
histograma 84-86 , limiar
Affine.py, 99, 108
de imagem 87-88 ,
instalação de 80 , 4-5 Align.py, 99, 109 módulos
forma das imagens ( personalizados, função
consulte Forma das 99 estimativa_afino,
imagens) detecção de 105 recursos
suavizando imagens e
( consulte extração, 99
Suavização de correspondência de
imagens) recursos, 99
texto para imagens, 68-70 homografia
(similaridade)
entre imagens, 99
P, Q codificação Python,
Preenchimento, 75 99, 106 Ransac.py, 107
Linhas da palma, função
achado, 133-135 função residual_lengths,
Pandas, 43 Ajuste de 105-106
parâmetro, 156 imagem de satélite, 98
Transformação de imagem alvo, 106
perspectiva função de
função, transformação, 99
162 pixels, 8-9 Casos de uso em
Pixels por polegada (PPI), 10 tempo real
detectando faces,
detecção de 135-138
pistas, 143
168

 
linhas da palma, encontrar, 133-135 Descida do gradiente est
reconhecendo rostos, 138-140 Similaridade estrutural,
movimentos de rastreamento, 141-143Índice de similaridade e
função residual_lengths, 105-106 (SSIM), 25
Espaço de cor RGB, 14-15 Máquinas de vetor de su
Girar e dimensionar imagem, 59 (SVMs), 127

S T
Algoritmo de transformação Expansão Taylor,
de característica 92 to_excel, 43
invariante de escala Movimentos de
(SIFT), 89-90 rastreamento, 141-143
Imagem Scikit Conjunto de treinamento,
conversão de espaço de 114
cor ( consulte
Conversão de espaço
de cor) você
criação de desenho ( ver Ubuntu, 4
criação de desenho)
correção de gama,
resolução de 57 V, W
imagens, economia de Ambientes virtuais, 6
42 imagens, valor de
53 pixels, envio e
visualização de 43 X
XGBoost, 152-153
imagem, 41 espaço de cor XYZ,
Forma de 15-16
dilatação das
imagens,
erosão 76-77 , Y, Z
76, 78-79 Espaço de cor YIQ, 21
preenchimento, Cabos YPbPr, 19
75 Espaço de cor YPbPr, 19
Função sigmoide, Espaço de cor YUV, 20
módulo 115
Skimage, 59
imagens de
suavização
filtro bilateral
, filtro gaussiano
72-73 , filtro
mediano 71-72 ,
71
Transformação de Sobel, 157-158

169

Você também pode gostar