Você está na página 1de 71

Visão Computacional e Reconhecimento de Imagens para Sistemas Embarcados

Prof.

AULA 6

Msc. Francisco Fambrini

Vídeo no Youtube

Uma excelente apresentação dos recursos do SimpleCV pode ser vista aqui, na apresentação da

PyCon2013:

https://www.youtube.com/watch?v=O4_kWEDd52o

Sugestão de Projeto: Robô que fala

Que tal fazer um projeto de um robô que fala o nome das cores que ele enxerga através da câmera ?

Para fazer o robô falar, basta carregar o programa "Espeak" na sua raspberry PI

http://espeak.sourceforge.net/

Veja o tutorial em anexo !

Usar OpenCV ao invés de usar SimpleCV

Ao invés de usar o SimpleCV, você pode usar o OpenCV. Experimente o script Python a seguir, que utiliza OpenCV:

import numpy as np import cv2 # Carrega uma imagem em escala de cinza img = cv2.imread('lenaTest3.jpg',0)

cv2.imshow('image',img)

cv2.waitKey(0)

cv2.destroyAllWindows()

Obs: De modo geral, usar OpenCv é bem mais complexo do que SimpleCV, a estrutura de programação é bem mais complicada.

OUTRAS OPERAÇÕES MORFOLÓGICAS

Além da Dilatação, Erosão, Abertura e Fechamento, existem muitas outras Transformações Morfológicas. Todas elas dependem de se definir um Elemento Estruturante adequado. Os principais formatos dos elementos estruturantes são mostrados a seguir:

se definir um Elemento Estruturante adequado. Os principais formatos dos elementos estruturantes são mostrados a seguir:

OUTRAS OPERAÇÕES MORFOLÓGICAS

Image.logicalAND() Faz um AND da imagem binária com a máscara

Image.logicalOR() Faz um OR da imagem binária com a máscara

Image.logicalXOR() Faz um XOR da imagem binária com a máscara

Image.logicalNAND() Faz um NAND da imagem binária com a máscara

Image.floodFill() funciona como a ferramenta balde de tinta em um editor de imagens. Ela pode apagar determinadas áreas da imagem.

Transformada TopHat

Em morfologia matemática e processamento de imagens digitais, top-hat é uma operação morfológica que extrai pequenos elementos e detalhes de imagens dadas. Existem dois tipos: Transformada top-hat branca é definida como a diferença entre a imagem de entrada e sua abertura por algum elemento estruturante:

de entrada e sua abertura por algum elemento estruturante: A transformação top-hat preta é definida como

A transformação top-hat preta é definida como a diferença entre a imagem de entrada e a imagem após o Fechamento Morfologico.

imagem de entrada e a imagem após o Fechamento Morfologico. São usados para diversas tarefas de

São usados para diversas tarefas de processamento de imagem, como a extração de características, equalização fundo, realce de imagem, entre outros.

Transformada Top-Hat

Exemplo em MATLAB

BW = imread('aviao.jpg');

se = strel('disk',20);

tophatFiltered = imtophat(BW,se); % faz a transformada TopHat

figure, imshow(tophatFiltered)

% define o elemento estruturante redondo

= imtophat(BW,se); % faz a transformada TopHat figure, imshow(tophatFiltered) % define o elemento estruturante redondo
= imtophat(BW,se); % faz a transformada TopHat figure, imshow(tophatFiltered) % define o elemento estruturante redondo

Transformada Watershed

A palavra Watershed significa “Bacia Hidrográfica”. É uma ferramenta morfológica que serve para ajudar na segmentação das imagens. Fazemos uma analogia de certas regiões na imagem com um Poço dágua (Catchment basins). A linha divisória entre dois poços dágua é o Watershed Line.

na imagem com um Poço dágua (Catchment basins). A linha divisória entre dois poços dágua é

Watershed no SimpleCV Só funciona na versão SimpleCV 1.7 em diante !

from SimpleCV import Image img = Image(“lena.jpg") img1 = img.watershed()

img1.show()

img2 = img.watershed(color=True)

img2.show()

#OBS: Só funciona na versão SimpleCV 1.7 em diante !

#--------------------------------------------------------------------------------------------------------

Alguns parâmetros que podem ser usados na função watersheds( )

def

watershed(self , color = False)

def

watershed( self, mask=None,

erode=2,

dilate=2,

useMyMask=False )

#---------------------------------------------------------------------------------------------------------

color-

Seleciona o tipo de imagem de saida, colorida ou em escala de cinzas.

mask-

Uma máscara binária opcional. Se nenhuma é fornecida, o programa binariza e inverte a imagem

erode-

o numero de vezes que a mascara vai erodir até encontrar o primeiro plano

dilate-

o numero de vezes que a mascara vai dilatar até encontrar o plano de fundo da imagem

useMyMask- se for "True" a mascara não será modificada pelo programa

Criando uma máscara com a transformada Watershed

from SimpleCV import Image img = Image('lenna') myMask = Image((img.width,img.height)) myMask = myMask.floodFill((0,0),color=Color.WATERSHED_BG) mask = img.threshold(128) myMask = ( myMask - mask.dilate(2) + mask.erode(2) ) result = img.watershed(mask=myMask,useMyMask=True)

Não funciona no SimpleCV versão 1.3, somente nas versoes 1.7 em diante !

Obs:

Color.WATERSHED_FG - Transf. watershed para a cor de primeiro plano Color.WATERSHED_BG – Transf. watershed para a cor de fundo Color.WATERSHED_UNSURE – plano não é definido

mais alguns Parâmetros para a função watershed()

Esqueletos

O processo morfológico que reduz todos os objetos de uma imagem em linhas, sem alterar a estrutura essencial da imagem é conhecido como esqueletização (skeleton). O termo esqueleto é geralmente utilizado para denotar uma representação de um padrão através de uma coleção de arcos e curvas finas. Alguns autores usam termos diferentes, por exemplo, "eixo médio" (medial axis) e "thinned image". Contudo, "thinning" e "esqueletonização" tornaram-se os mais utilizados na literatura. Além disso, o termo "esqueleto" é utilizado para referir-se ao resultado do algoritmo.

utilizados na literatura. Além disso, o termo "esqueleto" é utilizado para referir-se ao resultado do algoritmo.

Esqueletos para várias figuras

Esqueletos para várias figuras

Esqueleto para uma letra “H”

Esqueleto para uma letra “H”
Observe que o esqueleto de um círculo é um único ponto no seu centro

Observe que o esqueleto de um círculo é um único ponto no seu centro

A obtenção do esqueleto geralmente exige grande

esforço computacional. Em 1984, Zhang e Suen publicaram um trabalho chamado "A fast parallel Algorithm for thinning digital patterns", no qual propuseram um novo algoritmo paralelo de

esqueletonização. Este trabalho trouxe resultados muito superiores quando comparados a outros da sua época. Mais tarde, muitos pesquisadores, incluindo o próprio Zhang, propuseram nosvos testes e metodologias que melhoraram ainda mais o algoritmo de Zhang e Suen. Contudo, mesmo nos dias de hoje (2001), trabalhos recentes ainda comparam os resultados conseguidos com o trabalho original de Zhang e Suen. Por isso, esse algoritmo é o mais usado na prática. É também chamado

de algoritmo “Transformada Distância”.

Por isso, esse algoritmo é o mais usado na prática. É também chamado de algoritmo “Transformada
Observe o esqueleto do avião na última foto

Observe o esqueleto do avião na última foto

Espaço de Cores HSV

HSV é a abreviatura para o sistema de cores formadas pelas componentes hue (matiz), saturation (saturação) e value (valor). O HSV também é conhecido como HSB (hue, saturation e brightness — matiz, saturação e brilho, respectivamente). Esse sistema de cores define o espaço de cor conforme descrito abaixo, utilizando seus três parâmetros:

Matiz (tonalidade): Verifica o tipo de cor, abrangendo todas as cores do espectro, desde o vermelho até o violeta, mais o magenta. Atinge valores de 0 a 360, mas para algumas aplicações, esse valor é normalizado de 0 a

100%.

Saturação: Também chamado de "pureza". Quanto menor esse valor, mais com tom de cinza aparecerá a imagem. Quanto maior o valor, mais "pura" é a imagem. Atinge valores de 0 a 100%.

Valor (brilho): Define o brilho da cor. Atinge valores de 0 a 100%.

Transformando RGB para HSV

Transformando RGB para HSV

Espaço de cores HSV

Algumas vezes é útil transformar a imagem (R,G,B) para o espaço de cores HSV onde temos:

a) Hue: (matiz) ou “cor pura”, varia entre 0 e 180 graus;

b) Saturação: conta-nos o quão longe a cor está do branco;

c) Value: nos informa o quanto escura a cor é;

Este sistema apresenta a vantagem de ser “imune” com relação a iluminação: um sistema RGB, apesar de mais simples de trabalhar, é muito suscetível à variações na cor da luz que ilumina o objeto.

sistema RGB, apesar de mais simples de trabalhar, é muito suscetível à variações na cor da

Como trabalhar em cores HSV no SimpleCV

Image.toHSV(): converte a imagem de RGB para HSV

Image.toBGR(): converte de volta para BGR

Image.toGray(): converte a imagem para escalas de cinza

Image.toRGB(): converte a imagem de volta para RGB

Image.huePeaks(): encontra os picos de matiz na sua imagem

Image.hueDistance(): ajuda a encontrar objetos que tem o matiz que voce deseja;

Image.hueHistogram(): visualiza o histograma no espaço colorido

Exemplo

import pylab as plt from SimpleCV import Image img = Image("flower.jpg") img.show() peaks=img.huePeaks() print peaks hist = img.hueHistogram() plt.plot(hist) plt.draw() hdist = img.hueDistance(peaks[2][0]) binary = img.hueDistance(peaks[2][0]).invert().threshold(220) hdist.show() hdist.save("hflower.png") binary.save("bflower.png")

Descritores

Descritores são parâmetros da imagem que podem ser transformados em números. Um bom descritor deve ser invariante à mudança de escala, à rotação e à translação. Blobs (como já explicado neste curso) são regiões da imagem que se distinguem do background e que possuem pixels similares.

Uma vez que um Blob é identificado, nós podemos medir:

a) A área (quantos pixels tem este blog);

b) Sua largura e sua altura;

c) Achar seu centro (baseado no centro de massa)

d) Pode-se contar o numero de blobs para descobrir diferentes objetos

e) Medir angulos

f) Olhar as cores presentes no blob

g) Podemos descobrir o quanto este blob se aproxima de um círculo, de um quadrado ou de um retângulo.

Encontrando Blobs: findBlobs()

A função findBlobs(): Se você não especificar nenhum parâmetro, ela vai encontrar o Blob mais brilhante e o mais escuro na imagem. Blobs são mais facilmente encontrados em imagens binárias.

O exemplo abaixo encontra Blobs na imagem “moedas.png”.

A cor padrão é o verde, mas a cor no contorno dos blobs() pode ser mudada

facilmente através da função packs.show(Color.RED)

from SimpleCV import Image pennies = Image ("moedas.png") binPen = pennies.binarize() blobs = binPen.findBlobs()

blobs.show(width=5)

(por exemplo)

Encontrando outros descritores na imagem

Você pode encontrar outros descritores na imagem das moedas:

from SimpleCV import Image pennies = Image("moedas.png") binPen = pennies.binarize() blobs = binPen.findBlobs() print "Areas=", blobs.area() print "Angulos=", blobs.angle() print "Centros=", blobs.coordinates()

Encontrando Blobs escuros na imagem

Como a função Blobs() retorna apenas os Blobs mais claros da imagem, se nós queremos detectar Blobs escuros precisamos primeiro inverter a imagem:

from SimpleCV import Image img = Image("chessmen.png") invImg = img.invert() blobs = invImg.findBlobs()

blobs.show(width=2)

img.addDrawingLayer(invImg.dl())

img.show()

Encontrando Blobs de uma cor específica

Encontrando todas as balas azuis na imagem:

cor específica Encontrando todas as balas azuis na imagem : from SimpleCV import Image, Color img

from SimpleCV import Image, Color img = Image("mandms.png") blue_distance = img.colorDistance(Color.BLUE).invert() blobs = blue_distance.findBlobs() blobs.draw(color=Color.PUCE, width=3) blue_distance.show() img.addDrawingLayer(blue_distance.dl()) img.show()

Explicando o programa do Slide anterior

A função colorDistance() retorna uma imagem que mostra o quão longe as cores da imagem original estão da cor passada no parametro Color (no caso, é azul). Para tornar esta função ainda mais precisa, poderiamos encontrar os 3 numeros RGB para a cor azul verdadeira das balas, mas para este exemplo o que fizemos foi suficiente. Uma vez que qualquer cor próxima do azul se tornará preta, e as cores longe do azul se tornarão branca, nós usamos novamente a função invert() para inverter a imagem, tornando brancas as balas azuis e o fundo será preto (demais balas). Usamos então a nova imagem para encontrar os blobs que contornam as balas azuis. Nós também podemos fazer um ajuste fino da função findBlobs() passando para esta função um parâmetro de threshold. O

threshold pode ser um inteiro ou 3 números RGB. Quando um threshold é passado na função, ela irá alterar todos os pixels que são mais escuros do que o threshold para branco, e todos os pixels acima do valor de threshold

para preto.

No final, nós mostramos na tela a imagem blue_distance.

Vantagens de se usar o espaço HSV

Algumas vezes as condições de iluminação podem tornar a detecção de cores mais dificil. Para demosntrar este problema, a imagem seguinte é baseada na mesma imagem “balas” anterior, mas a metade direita da imagem está no escuro. A função hueDistance() é a melhor escolha para este tipo de problema. Mesmo com a metade direita da imagem estando muito escura, a função anterior ainda pode ser usada para encontrar blobs e mostra as vantagens de se trabalhar no espaço Hue ao invés de usar o espaço RGB:

ser usada para encontrar blobs e mostra as vantagens de se trabalhar no espaço Hue ao

Aplicando na imagem metade- escura

from SimpleCV import Image, Color img = Image("mandms-dark.png") blue_distance = img.colorDistance(Color.BLUE).invert() blobs = blue_distance.findBlobs() blobs.draw(color=Color.PUCE, width=3) img.addDrawingLayer(blue_distance.dl()) img.show()

Note que somente as balas azuis do lado esquerdo da imagem são detectadas.

Usando hueDistance()

Teste agora o mesmo programa do slide anterior, porém usando a função hueDistance() no lugar da função colorDistance()

Observe que no espaço de cores Hue (HSV) as mudanças na luz não criam problemas !

Função findLines()

Usa Transformada de Hough para encontrar as linhas em uma imagem, mas retorna o FeatureSet (conjunto de caracteristicas) destas linhas:

coordinates(): retorna as coordenadas (X, Y) do ponto inicial da linha achada.

width(): retorna a diferença entre o ponto X final e o ponto X inicial da linha

height(): retorna a diferença entre a coordenada Y final e a coordenada Y inicial da linha;

length(): retorna o comprimento da linha em pixels

Usando a função findLines()

from SimpleCV import Image img = Image("block.png") lines = img.findLines()

lines.draw(width=3)

img.show()

A função findLines() i nclui 5 diferentes parâmetros para ajudar a melhorar a qualidade do resultado:

Threshold: determina quão forte deve ser a borda antes de ser reconhecida como uma linha reta; Minlinelength: qual é o comprimento minimo das linhas que serão reconhecidas Maxlinegap: qual é o comprimento do buraco (lacuna) que será tolerado em uma linha. Cannyth1: um parâmetro de limiar utilizado com o passo de detecção de borda que define a mínima "resistência da borda“.

Cannyth2:

um segundo parâmetro para a detecção de borda que define “a

persistência da borda“.

findLines()

O parâmetro threshold para findLines () funciona quase da mesma maneira que o parâmetro limiar para a função findBlobs (). Se você não passar um valor, o valor padrão que ele usa é definido para 80. Para valores menores mais linhas serão encontradas pela função. Para valores maiores, menos linhas serão encontrados. Usando ao imagem do bloco de madeira novo, experimente um limiar baixo:

from SimpleCV import

img = Image("block.png") # Ajusta um valor baixo de threshold

Image

lines = img.findLines(threshold=10)

lines.draw(width=3)

img.show()

# Ajusta um valor baixo de threshold Image lines = img.findLines(threshold=10) lines.draw(width=3) img.show()

Eliminando linhas muito curtas

Uma maneira de se livrar de pequenas linhas é usar o parâmetro minlinelength para eliminar linhas curtas. O comprimento da linha é medida em pixels, e o valor padrão da função findLines() é 30 pixels de comprimento. No exemplo abaixo, o comprimento mínimo é ajustado, eliminando algumas linhas não desejadas:

from SimpleCV import Image img = Image("block.png") lines = img.findLines(threshold=10, minlinelength=50)

lines.draw(width=3)

img.show()

O resultado é mostrado na figura a seguir.

O resultado é mostrado na figura a seguir. Observe que eliminou um par de linhas extras,

Observe que eliminou um par de linhas extras, mas a um custo de eliminar as linhas laterais novamente. Alterar o comprimento da linha não resolve o problema. As duas extremidades da imagem ainda não foram achadas. Na verdade, ele poderia criar o problema oposto. Às vezes, o algoritmo pode achar linhas muito pequenas e ele precisa saber se esses pequenos segmentos de linha, na verdade, representam um maior linha contínua. A função findLines () pode ignorar pequenos espaços em uma linha, e reconhecê-lo como uma linha geral maior.

Por padrão, a função irá combinar dois segmentos de uma linha se a distância entre eles é de 10 pixels ou menos. Você pode usar o parâmetro maxlinegap para ajustar como isso funciona.

O exemplo que se segue permite um maior buraco entre linhas, potencialmente permitindo-lhe descobrir algumas pequenas linhas que constituem a borda.

Exemplo: Ajustando a função findLines()

from SimpleCV import Image img = Image("block.png") lines = img.findLines(threshold=10, maxlinegap=20)

lines.draw(width=3)

img.show() O resultado restaura a linha de borda direita novamente, mas, mais uma vez, ele encontra um monte de linhas indesejadas, como demonstrado na Figura ao lado. Note-se que ao definir o comprimento mínimo da linha diminuiu o número de linhas encontradas na figura, a adição de um intervalo mais longo, em seguida, aumentou dramaticamente o número de linhas.

encontradas na figura, a adição de um intervalo mais longo, em seguida, aumentou dramaticamente o número

Com o buraco maior (maior gap), os segmentos de linha podem ser combinados para satisfazer os requisitos de comprimento da linha, e mais linhas são então reconhecidas. Os dois últimos

parâmetros, cannyth1 e cannyth2, são limiares para o detector de bordas Canny.

modo, as bordas são detectados pela procura de mudanças no brilho. O primeiro destes parâmetros de limiar controla quanto o brilho tem de mudar para detectar uma borda. O segundo parâmetro controla o threshold para a ligação em conjunto várias bordas. Ambos estes parâmetros agem da mesma maneira que os três parâmetros anteriores: valores menores significam que mais linhas serão detectadas, o que poderia ser apenas a adição de ruído. Por outro lado, valores maiores resultará em menos linhas detectadas, mas pode significar que algumas linhas válidas não estão sendo detectadas. O truque é trabalhar com os parâmetros até que você esteja em um intervalo que faz mais sentido para a sua aplicação. Claro que, por vezes é mais fácil do que simplesmente modificar a imagem para reduzir o ruído, em vez de afinar os parâmetros. O exemplo a seguir, encontra as linhas desejadas no bloco de madeira:

A grosso

from SimpleCV import Image img = Image('block.png') dist = img.colorDistance((150, 90, 50)) bin = dist.binarize(70).morphClose() lines = bin.findLines(threshold=10, minlinelength=15)

lines.draw(width=3)

# Move as linhas desenhadas na imagem binária de volta para a imagem principal img.addDrawingLayer(bin.dl()) img.show()

Nosso objetivo final

Nosso objetivo final É segmentar o bloco de madeira nas 4 arestas, conforme a foto acima,

É segmentar o bloco de madeira nas 4 arestas, conforme a foto acima, usando

a função findLines()

corretamente parametrizada.

Encontrando Círculos: findCircle()

Além de linhas retas, você também pode trabalhar com circulos. O método para encontrar caracteristicas circulares é chamado findCircle(), e ele funciona da mesma maneira que findLines(). Ele retorna um conjunto de caracteristicas [ FeatureSet() ] da figura circular encontrada e também tem parametros para ajudar a ajustar a sensibilidade: Alguns parametros são Canny: Este é um parâmetro de limite para o detector de bordas Canny. O valor padrão é 100. Se você definir um valor mais baixo, um maior número de círculos será reconhecido, enquanto um valor maior, ao contrário, significam menos círculos sendo detectados. Thresh: Este é o equivalente do parâmetro threshold para findLines (). estabelece quão perto uma borda tem que estar de um circulo tem que ser antes de ser reconhecido. O valor padrão para este parâmetro é 350. Distance: Similar ao parâmetro maxlinegap para findLines (). Ele determina o quanto o blob pode estar proximo de uma circulo , antes de serem tratados como círculo. Se deixado indefinido, o sistema tenta encontrar o melhor valor, com base na imagem que está sendo analisado.

Tal como acontece com as linhas, existem funções

apropriados quando se lida com círculos, também. raio (): Como o nome sugere, este é o raio do círculo. diâmetro (): O diâmetro do círculo.

perímetro (): retorna o perímetro da funcionalidade, que, no caso de um círculo,

é o comprimento da circunferência.

featureset que são mais

Estas funções incluem:

Encontrando Círculos: findCircle()

from SimpleCV import Image img = Image("pong.png") circles = img.findCircle(canny=200, thresh=250, distance=15) circles = circles.sortArea()

circles.draw(width=4)

circles[0].draw( width=4) img_with_circles = img.applyLayers() edges_in_image = img.edges(t2=200)

final = img.sideBySide(edges_in_image.sideBySide(img_with_circles)).scale(0.5)

final.show()

Corners (cantos)

A grosso modo, os cantos são lugares em uma imagem onde duas linhas se encontram.

Os cantos são interessante em termos de visão computacional porque cantos, ao contrário arestas, são relativamente únicos e bons para a identificação de partes de uma imagem.

Por exemplo, se você estiver tentando analisar um quadrado, se você tentou encontrar as linhas que representam os lados de um quadrado, você poderia encontrar duas linhas horizontais e duas linhas verticais para cada um dos lados.

Contudo, você não seria capaz de dizer qual das duas linhas horizontais pertence a parte superior ou inferior, ou qual das duas linhas verticais era o lado esquerdo ou direito.

Cada canto, por outro, é único, e é possivel identificar facilmente onde ele está localizado na imagem.

findCorners()

A função findCorners() analisa a imagem e retorna os locais onde todos os cantos possam

ser encontrados. Note-se que um canto não precisa ser um ângulo reto em 90 graus. Duas linhas que se cruzam em qualquer ângulo pode constituir um canto. Tal como acontece com findLines() e findCircle (), a função findCorners() retorna uma featureset de todas as caracteristicas do canto que foi achado.

Apesar do featureset dos cantos compartilhar as mesmas funções que qualquer outro FeatureSet, existem funções que não fazem muito sentido no contexto de um canto. Por exemplo, tentar encontrar a largura, comprimento ou área de um canto realmente não faz muito sentido.

Tecnicamente, as funções usadas para encontrar essas coisas ainda funcionam, mas

o que eles vão voltar são valores padrão e dados não verdadeiros sobre os cantos.

Similar às funções findLines() e findCircle(), a função findCorners() também tem parâmetros para ajudar a determinar quais cantos são encontrados em uma imagem.

Vamos explorar os parâmetros disponíveis usando uma imagem de um suporte como um exemplo.

from

img = Image('corners.png') img.findCorners.show()

SimpleCV import Image

Os pequenos círculos verdes representam os cantos detectados. Observe que o exemplo encontra muitas curvas. Por padrão, ele procura por 50 cantos, que é, obviamente, muitos falsos positivos. Com base na inspeção visual, verifica-se que existem quatro cantos principais. Para restringir o número de cantos voltou, podemos usar o parâmetro MAXNUM.

from SimpleCV import Image img = Image('corners.png')

img.findCorners.(maxnum=9).show()

O método findCorners( )

classifica todos os cantos antes de retornar seus resultados,

então se ele encontra mais do que o número máximo de cantos determinados, ele irá retornar apenas os melhores. Alternativamente, o parâmetro minquality define a qualidade mínima de um canto antes de que ele seja mostrado. Esta abordagem filtra o ruído sem ter que codificar um máximo número de cantos de um objeto. Isso já melhora a performance, mas o algoritmo encontrou dois cantos na parte inferior esquerda e nenhum na parte superior direita. Os dois cantos na parte inferior esquerda são realmente parte do mesmo canto, mas a

iluminação e as cores estão confundindo o algoritmo. Para evitar cantos próximos de serem contados como dois cantos separados, definimos o parâmetro mindistance, que define o mínimo número de pixels entre dois cantos.

from SimpleCV import Image img = Image('corners.png') img.findCorners.(maxnum=9, mindistance=10).show()

Exemplo: Contando Moedas

Neste exemplo, vamos usar uma foto de algumas moedas norte-americanas e calcular o seu valor total em dinheiro. Para fazer isso, encontramos as bolhas que representam cada moeda, e depois comparamos os seus diâmetros para saber o valor de cada moeda. No exemplo, usamos como referência a moeda de 25 cents para comparar com as demais.

from SimpleCV import Image, Blob import numpy as np img = Image("coins.png") coins = img.invert().findBlobs(minsize = 500) value = 0.0 # O valor das moedas é determinado pelo seu tamanho

# http://www.usmint.gov/about_the_mint/?action=coin_specifications coin_diameter_values = np.array([

[

19.05, 0.10],

[

21.21, 0.01],

[

17.91, 0.05],

[

24.26, 0.25]]);

#use uma moeda de 25cents para calibrar (tem que existir pelo menos uma)

px2mm = coin_diameter_values[3,0] / max([c.radius()*2 for c in coins]) for c in coins:

diameter_in_mm = c.radius() * 2 * px2mm distance = np.abs(diameter_in_mm - coin_diameter_values[:,0]) index = np.where(distance == np.min(distance))[0][0] value += coin_diameter_values[index, 1]

Ao executar este código usando o nosso exemplo da foto, o valor total das moedas exibido pelo programa deve ser de US $ 0,91.

Neste exemplo, a detecção de base era bastante simples, mas passamos a maior parte do tempo analisando os dados que se reuniram a partir da imagem.

Uma idéia interessante de projeto é fazer um programa que reconhece e fala o valor de um conjunto de moedas, para ajudar pessoas com deficiencia de visão (cegos) que não podem contar o dinheiro.

CLASSIFICADORES

Etapas para o Reconhecimento de Imagens

Etapas para o Reconhecimento de Imagens

Classificadores: separam as imagens por classes semelhantes

Classificadores: separam as imagens por classes semelhantes

Classificadores

São programas baseados no conceito de “Machine Learning” (aprendizado de máquina) que podem ser usados para classificar imagens baseado nos “Vetores de Características”. Cita-se:

KNNClassifier (K-Nearest-Neighbors ou K-vizinhos mais próximos): Retorna a classe do conjunto de características dentro do conjunto de treinamento que está mais perto do vetor da imagem de entrada.

SVMClassifier (Support Vector Machine): Encontra a função matemática que define o hiperplano que melhora separa as classes de objetos.

NaiveBayesClassifier (baseado em probabilidade Bayesiana): Pesa a probabilidade de cada classe comparando-a com a probabilidade de uma característica que aparece na figura.

TreeClassifier (Árvore de Decisão): É basicamente uma enorme e longa lista de if/else para tomar uma decisão. Existe diversos tipos de árvores e florestas para analisar cada característica.

(existem outros algoritmos, mas que não estão implementados no SimpleCV e

nem no Orange: http://orange.biolab.si/ )

Funções úteis para Treinamento dos Classificadores

Conforme já foi dito, para que um algoritmo classificador funcione, é necessário termos um Banco de Imagens de Treinamento (para poder extrair o conjunto de vetores de características necessário para fazer a identificação da imagem). Algumas funções do SimpleCV podem ajudar no Treinamento (ou seja, na hora de obter os Conjuntos de Classes).

Treinar e testar seus programas de Machine Learning é mais fácil se você salvar seus dados em um arquivo, geralmente classificados dentro de diretórios por Classes.

TurkingModule pode ajudar a separar manualmente seus dados e salvá-los.

ConfusionMatriz (Matriz de Confusão) : Pode ser usado para ajudar você a perceber o quão bom seu Classificador funciona. Nenhum classificador é 100% perfeito. Imprimir quais classes são “confundidas” com quais outras classes ajuda a melhorar o software.

Ilustração básica de como funciona o SVM

O algoritmo Support Vector Machine separa as classes através do melhor hiperplano de separação (é bom para classes não- linearmente separáveis):

separa as classes através do melhor hiperplano de separação (é bom para classes não- linearmente separáveis):

FeatureExtractors

FeatureExtractors pode ser usado para extrair os vetores de caracteristicas de imagens e aramazena-los na forma de uma lista de numeros;

HueHistogramFeatureExtractor - devolve um histograma de matizes.

EdgeHistogramFeatureExtractor - Calcula ângulos da linha em uma imagem.

HaarLikeFeatureExtractor - medidas ásperas de simetria e forma.

BOFFeatureExtracor - Pequenas e minúsculas características do gradiente das bordas.

MorphologyFeatureExtractor – extrai momentos de Hu dos Blobs, comprimento,largura, relação de aspecto, perímetro, etc.

Muitos mais - escrever o seu próprio extrator de descritores !

Momentos de Hu

Os momentos invariantes de uma imagem (também chamados Momentos de Hu), permitem o cálculo da área de um objeto (conjunto de pixels), centróide de um objeto ou também permite identificar um determinado objeto mesmo que tenha sofrido mudança de tamanho ou mesmo que seja rotacionado. Esta teoria é muito utilizada em reconhecimento de padrões. Geralmente utiliza-se algum tipo de software que extrai os referidos momentos de uma imagem binarizada. Os 7 momentos de Hu podem ser calculados e colocados numa tabela e terão valores muito aproximados. Sendo assim, os momentos de Hu servem para identificar um objeto, mesmo que tenha sofrido mudança de tamanho ou seja rotacionado.

que tenha sofrido mudança de tamanho ou seja rotacionado. Mais sobre este assunto:

Mais sobre este assunto:

http://www.sci.utah.edu/~gerig/CS7960-S2010/handouts/Hu.pdf

de tamanho ou seja rotacionado. Mais sobre este assunto: http://www.sci.utah.edu/~gerig/CS7960-S2010/handouts/Hu.pdf

Orange: Machine Learning Tools

Para testar estes softwares de Aprendizado de Máquina, você precisará instalar também o

pacote Orange:

http://orange.biolab.si/

https://github.com/biolab/orange

Os passos para instalação podem ser seguidos a partir deste site:

http://jonahgroup.github.io/SnackWatcher/Raspberry-Pi-3-for-Computer-Vision

Instalando e Compilando o Orange

1- Clone o Orange

(são 129,56 Mb) .

No terminal do Raspbian, digite:

$ git clone https://github.com/biolab/orange.git

Isso vai carregar o Orange a partir da internet.

2 – Compile o Orange na sua Raspberry ou no seu PC:

$

cd

orange

 

$

python

setup.py build

$

sudo

python setup.py

install

OBS: O processo todo pode demorar mais de 8 horas para compilar na Raspberry PI, no PC é bem mais rápido !

Todas as instruções estão detalhadas neste site:

http://jonahgroup.github.io/SnackWatcher/Raspberry-Pi-3-for-Computer-Vision

Alguns exemplos na Internet:

1- Um software para classificação e reconhecimento de imagens de frutas:

http://jmgomez.me/a-fruit-image-classifier-with-python-and-simplecv/

2- Um software para classificar LEGOs

https://www.youtube.com/watch?v=yupShUY5WS4

3-Acompanhamento facial com camera

https://www.youtube.com/watch?v=GD8ArIM-5BQ

4- Sistema de detecção de esquilos nos jardins:

https://www.youtube.com/watch?v=QPgqfnKG_T4

Para testar se o Orange foi corretamente instalado

Digite no terminal de linha de comando:

$ cd

$ python

orange

setup.py

test

Depois, experimente o seguinte programa Python:

img = Image('cookie_001.png') hue_feature_vec = extractors[0].extract(img) print hue_feature_vec

Programa SnackClassifier

Como exemplo de como se trabalha com classificadores, depois de instalado o pacote

Orange, abra a

pasta

SnackClassifier

Software.

Este programa classifica vários tipos de “lanches” em diversas classes:

"brownie“ (bolo)

"candy",

(bala)

"cookie",

(bolacha)

"package“

(pacote)

(bolo) • "candy", (bala) • "cookie", (bolacha) • "package“ (pacote)
(bolo) • "candy", (bala) • "cookie", (bolacha) • "package“ (pacote)
(bolo) • "candy", (bala) • "cookie", (bolacha) • "package“ (pacote)
(bolo) • "candy", (bala) • "cookie", (bolacha) • "package“ (pacote)

Exemplo Prático:

SnackClassifier

1 – O programa

snack-trainer.py

extrai as caracteristicas dos objetos:

class SnackTrainer():

def

init

(self):

self.classifier = None

def createExtractor(self, extractorName, trainPaths=[]):

if (extractorName == 'hue'):

extractor = HueHistogramFeatureExtractor(10) elif (extractorName == 'edge'):

extractor = EdgeHistogramFeatureExtractor(10) elif (extractorName == 'haar'):

extractor = HaarLikeFeatureExtractor(fname='haar.txt') elif (extractorName == 'bof'):

extractor = BOFFeatureExtractor() extractor.generate(trainPaths, imgs_per_dir=40)

# need to build the vocabuary (feature words) for bag of feature

# extractor.generate(trainPaths, imgs_per_dir=40) return extractor

Pasta

SnackClassifier

A

pasta contendo todas as imagens de treinamento é “train”,

A

pasta contendo todas as imagens de teste é “test” e

O

caminho padrão onde os resultados são escritos é “result”

O

Tipo de classe também deve estar de acordo com os

diretórios. Todas as classes de imagem deve ser de layout para os diretórios separados. Os rótulos de classe devem estar alinhados aos diretórios.

Por exemplo, se temos 2 classes ” cookie` e other” , a chamada deve ser:

python

snack-trainer.py

-g

-c

"cookie,other"

-s

tree

Sobre o slide anterior

Neste exemplo, estamos classificando “cookie” e “other”.

Nós especificamos o uso do classificador “tree” para

classificar as imagens

na pasta “test”.

As imagens do resultado serão guardadas na pasta "result"

Além disso, os parâmetros de classificadores treinados são salvos por padrão, com o nome do classificador, por exemplo: "Tree.dat". Podemos usar o programa classificador "Snack-classify.py" para a classificação de outras imagens após o treinamento.

Snack-Classify.py

import sys from optparse import OptionParser from SimpleCV import *

class SnackClassify():

def

pass def load(self, classifierFile):

self.classifier = TreeClassifier.load(classifierFile) def classify(self, imageFile):

image = Image(imageFile) return self.classifier.classify(image) def classNames(self):

return self.classifier.mClassNames def parse_options(self, args):

init

(self):

# Parse command-line options usage = "%prog [options] -c <classifier_file> -i <image>" parser = OptionParser(usage=usage) parser.add_option("-g", "--debug", action="store_true", dest="debug", default=False, help="debugging mode"), parser.add_option("-c", "--classifier", action="store", dest="classifier_file", default="",

help="load classifier from file"), parser.add_option("-i", "--image", action="store", dest="image_file", default="", help="classify this image file"),

(self.options, args) = parser.parse_args(args)

if not self.options.classifier_file or not self.options.image_file:

parser.print_help()

exit(0)

def process():

snack_bot = SnackClassify() snack_bot.parse_options(sys.argv)

classifierFile = snack_bot.options.classifier_file imageFile = snack_bot.options.image_file

snack_bot.load(classifierFile) class_name = snack_bot.classify(imageFile) print class_name

""" main program """ if

name

== "

process()

main

":

Descrição completa do exemplo

https://github.com/jonahgroup/SnackClassifier/

commit/31851b5483664e55975576e444a272

6dda5fed61

Desafio

1- Rode o exemplo “SnackClassifier” na sua máquina;

2 – Adaptar este exemplo para classificar folhas de árvores, usando as imagens da pasta: “Folhas de Árvores”

usando as imagens da pasta: “Folhas de Árvores” G l a b r u m Circinatum

Glabrum

Circinatum
Circinatum

Garryana

kelloggii
kelloggii
Macrophyllum
Macrophyllum
Negundo
Negundo