Uma visão geral dos métodos de segmentação de imagens na biblioteca scikit-image

Limiar


Essa é a maneira mais fácil de separar objetos do plano de fundo selecionando pixels acima ou abaixo de um determinado limite. Isso geralmente é útil quando vamos segmentar objetos por segundo plano. Você pode ler mais sobre o limite aqui .

Pessoas familiarizadas com o filme Terminator provavelmente concordarão que foi o maior filme de ficção científica da época. No filme, James Cameron introduziu um conceito interessante de efeitos visuais, que permitia que os espectadores se escondessem atrás dos olhos de um ciborgue chamado Terminator. Esse efeito ficou conhecido como "Terminator Vision" (inglês Terminator Vision). Em certo sentido, ele separou as silhuetas de pessoas do fundo. Isso pode parecer completamente inapropriado, mas a segmentação de imagem é uma parte importante de muitas técnicas de processamento de imagem atualmente.

Segmentação de imagem


Existem várias bibliotecas escritas para análise de imagem. Neste artigo, discutiremos detalhadamente o scikit-image, uma biblioteca de processamento de imagens Python.

Scikit-imagem


imagem

Scikit-image é uma biblioteca Python para processamento de imagens.

Instalação


O scikit-image é instalado da seguinte maneira:

pip install -U scikit-image(Linux and OSX) pip install scikit-image(Windows) # For Conda-based distributions conda install scikit-image 



Visão geral da imagem do Python


Antes de abordarmos os aspectos técnicos da segmentação de imagens, é importante familiarizar-se com o ecossistema de imagens Scikit e como ele processa imagens.

Importar imagem do GrayScale da biblioteca do skimage


O módulo de dados do skimage contém vários exemplos internos de conjuntos de dados, que geralmente são armazenados no formato jpeg ou png.

 from skimage import data import numpy as np import matplotlib.pyplot as plt image = data.binary_blobs() plt.imshow(image, cmap='gray') 

Importar imagem colorida da biblioteca do skimage


 from skimage import data import numpy as np import matplotlib.pyplot as plt image = data.astronaut() plt.imshow(image) 



Importar uma imagem de uma fonte externa


 # The I/O module is used for importing the image from skimage import data import numpy as np import matplotlib.pyplot as plt from skimage import io image = io.imread('skimage_logo.png') plt.imshow(image); 



Baixar várias imagens


 images = io.ImageCollection('../images/*.png:../images/*.jpg') print('Type:', type(images)) images.files Out[]: Type: <class 'skimage.io.collection.ImageCollection'> 

Salvando Imagens


 #Saving file as 'logo.png' io.imsave('logo.png', logo) 

Segmentação de imagem


Agora que temos uma idéia da imagem-scikit, oferecemos a consideração dos detalhes da segmentação da imagem. A segmentação de imagem é o processo de dividir uma imagem digital em vários segmentos, a fim de simplificar e / ou alterar a representação da imagem para algo mais significativo e fácil de analisar.

Neste artigo, consideraremos algoritmos para modelos ensinados com um professor (supervisionado) e sem um professor (sem supervisão).


Alguns dos algoritmos de segmentação estão disponíveis na biblioteca de imagens scikit.

Segmentação com um professor: algum conhecimento preliminar, possivelmente proveniente de contribuições humanas, é usado para guiar o algoritmo.

Segmentação sem professor: não é necessário conhecimento prévio. Esses algoritmos tentam dividir automaticamente as imagens em áreas significativas. O usuário ainda pode configurar certos parâmetros para obter os resultados desejados.

Vamos tentar isso em uma imagem de tutorial que vem com um conjunto de dados predefinido de imagem scikit.

Importação regular


 import numpy as np import matplotlib.pyplot as plt import skimage.data as data import skimage.segmentation as seg import skimage.filters as filters import skimage.draw as draw import skimage.color as color 

Recurso de imagem simples

 def image_show(image, nrows=1, ncols=1, cmap='gray'): fig, ax = plt.subplots(nrows=nrows, ncols=ncols, figsize=(14, 14)) ax.imshow(image, cmap='gray') ax.axis('off') return fig, ax 

Imagem


 text = data.page() image_show(text) 



Essa imagem é um pouco mais escura, mas talvez ainda possamos escolher um valor que nos permita uma segmentação razoável sem algoritmos complicados. Agora, para nos ajudar a escolher esse valor, usaremos um histograma.

Nesse caso, o histograma mostra o número de pixels na imagem com diferentes intensidades encontradas nessa imagem. Simplificando, um histograma é um gráfico no qual o eixo X mostra todos os valores que estão na imagem e o eixo Y mostra a frequência desses valores.

 fig, ax = plt.subplots(1, 1) ax.hist(text.ravel(), bins=32, range=[0, 256]) ax.set_xlim(0, 256); 



Nosso exemplo acabou sendo uma imagem de 8 bits, portanto, temos 256 valores possíveis ao longo do eixo X. O histograma mostra que há uma concentração de pixels razoavelmente brilhantes (0: preto, 255: branco). Este é provavelmente o nosso plano de fundo de texto bastante claro, mas o resto é um pouco embaçado. Um histograma de segmentação ideal seria bimodal para que possamos selecionar um número bem no meio. Agora vamos tentar criar algumas imagens segmentadas com base em um valor limite simples.

Limiar controlado


Como nós mesmos escolhemos um valor limite, chamamos de um valor limite controlado.

 text_segmented = text > (value concluded from histogram ie 50,70,120 ) image_show(text_segmented); 


Esquerda: texto> 50 | Meio: texto> 70 | Direita: texto> 120

Não obtivemos resultados perfeitos, pois a sombra da esquerda cria problemas. Vamos tentar com o limiar sem supervisão agora.

Limite não controlado


Limite não controlado O Scikit-image possui vários métodos de determinação automática de limite que não requerem entrada na escolha do limite ideal. Aqui estão alguns dos métodos: otsu, li, local.

 text_threshold = filters.threshold_ # Hit tab with the cursor after the underscore to get all the methods. image_show(text < text_threshold); 


Otsu esquerdo || Direita: li

No caso de local, também precisamos especificar block_size. O deslocamento ajuda a ajustar a imagem para obter melhores resultados.

 text_threshold = filters.threshold_local(text,block_size=51, offset=10) image_show(text > text_threshold); 



Este método fornece um efeito muito bom. Em grande medida, pode-se livrar das regiões barulhentas.

Segmentação com um algoritmo para um modelo com um professor


O limiar é um processo de segmentação muito simples e não funcionará corretamente em uma imagem de alto contraste, para a qual precisaremos de ferramentas mais avançadas.

Nesta seção, usaremos uma imagem de exemplo disponível gratuitamente e tentaremos segmentar a parte principal usando métodos com o professor.

 # import the image from skimage import io image = io.imread('girl.jpg') plt.imshow(image); 



Antes de fazer qualquer segmentação de imagem, é recomendável remover o ruído usando alguns filtros.

No entanto, no nosso caso, a imagem não possui ruído significativo; portanto, aceitamos como está. O próximo passo é converter a imagem em escala de cinza usando rgb2gray.

 image_gray = color.rgb2gray(image) image_show(image_gray); 



Usaremos dois métodos de segmentação que funcionam com princípios completamente diferentes.

Segmentação de contorno ativa


A segmentação do caminho ativo também é chamada de cobra e é inicializada usando um caminho ou linha definida pelo usuário em torno de uma região de interesse e, em seguida, esse caminho é lentamente compactado e atraído ou repelido por luz e bordas.

Para o nosso exemplo de imagem, vamos desenhar um círculo ao redor da cabeça humana para inicializar a cobra.

 def circle_points(resolution, center, radius): """ Generate points which define a circle on an image.Centre refers to the centre of the circle """ radians = np.linspace(0, 2*np.pi, resolution) c = center[1] + radius*np.cos(radians)#polar co-ordinates r = center[0] + radius*np.sin(radians) return np.array([c, r]).T # Exclude last point because a closed path should not have duplicate points points = circle_points(200, [80, 250], 80)[:-1] 

Os cálculos acima calculam as coordenadas x e y dos pontos na periferia do círculo. Como demos uma resolução de 200, ele calculará 200 desses pontos.

 fig, ax = image_show(image) ax.plot(points[:, 0], points[:, 1], '--r', lw=3) 



O algoritmo então segmenta o rosto da pessoa do resto da imagem, ajustando uma curva fechada às bordas da face.



Podemos configurar parâmetros chamados alfa e beta. Valores alfa mais altos fazem com que a curva se contraia mais rapidamente, enquanto o beta torna a curva mais suave.

 snake = seg.active_contour(image_gray, points,alpha=0.06,beta=0.3) fig, ax = image_show(image) ax.plot(points[:, 0], points[:, 1], '--r', lw=3) ax.plot(snake[:, 0], snake[:, 1], '-b', lw=3); 



Segmentação aleatória de andadores


Nesse método, a segmentação é realizada usando rotulagem interativa, denominada rótulos. Ao desenhar cada pixel no rótulo para o qual a probabilidade mais alta é calculada, você pode obter uma segmentação de imagem de alta qualidade. Mais detalhes sobre esse método podem ser encontrados neste trabalho.

Em seguida, usaremos novamente os valores anteriores do nosso exemplo. Poderíamos ter feito inicializações diferentes, mas, por simplicidade, vamos nos ater ao princípio dos círculos.

 image_labels = np.zeros(image_gray.shape, dtype=np.uint8) 

O algoritmo de passagem aleatória aceita rótulos como entrada. Assim, teremos um círculo grande cobrindo toda a face da pessoa e outro círculo menor próximo ao meio da face.

 indices = draw.circle_perimeter(80, 250,20)#from here image_labels[indices] = 1 image_labels[points[:, 1].astype(np.int), points[:, 0].astype(np.int)] = 2 image_show(image_labels); 


Agora vamos usar o Random Walker e ver o que acontece.

 image_segmented = seg.random_walker(image_gray, image_labels) # Check our results fig, ax = image_show(image_gray) ax.imshow(image_segmented == 1, alpha=0.3); 



O resultado não é o melhor, as bordas do rosto permaneceram inalcançadas. Para corrigir essa situação, podemos ajustar o parâmetro de passagem até obter o resultado desejado. Após várias tentativas, definimos o valor como 3000, o que funciona muito bem.

 image_segmented = seg.random_walker(image_gray, image_labels, beta = 3000) # Check our results fig, ax = image_show(image_gray) ax.imshow(image_segmented == 1, alpha=0.3); 



Isso é tudo para segmentação com o professor, onde tivemos que fornecer certos dados de entrada, além de configurar alguns parâmetros. No entanto, nem sempre é possível para uma pessoa olhar para a imagem e depois decidir qual contribuição dar e por onde começar. Felizmente, para tais situações, temos métodos de segmentação não controlados.

Segmentação sem professor


A segmentação sem um professor não requer conhecimento prévio. Considere uma imagem tão grande que seja impossível ver todos os pixels de uma só vez. Portanto, nesses casos, a segmentação sem um professor pode dividir a imagem em várias sub-regiões; portanto, em vez de milhões de pixels, você tem dezenas ou centenas de áreas. Vejamos dois desses algoritmos:

Clustering Iterativo Linear Simples


O método (cluster simples iterativo linear inglês ou SLIC) usa um algoritmo de aprendizado de máquina chamado K-Means. Ele pega todos os valores de pixel da imagem e tenta dividi-los em um determinado número de subdomínios. Leia este trabalho para obter informações detalhadas.

O SLIC funciona com cores diferentes, portanto, usaremos a imagem original.

 image_slic = seg.slic(image,n_segments=155) 

Tudo o que precisamos fazer é simplesmente definir um valor médio para cada segmento encontrado, o que faz com que pareça mais uma imagem.

 # label2rgb replaces each discrete label with the average interior color image_show(color.label2rgb(image_slic, image, kind='avg')); 



Reduzimos esta imagem de 512 * 512 = 262.000 pixels para 155 segmentos.

Felzenszwalb


Esse método também usa um algoritmo de aprendizado de máquina chamado cluster de árvores de abrangência mínima. Felzenszwaib não nos diz o número exato de agrupamentos nos quais a imagem será dividida. Ele irá gerar tantos clusters quanto achar adequado.

 image_felzenszwalb = seg.felzenszwalb(image) image_show(image_felzenszwalb); 



Existem muitas regiões na imagem. Vamos contar o número de segmentos únicos.

 np.unique(image_felzenszwalb).size 3368 

Agora vamos recolori-los usando o valor médio do segmento, como fizemos no algoritmo SLIC.

 image_felzenszwalb_colored = color.label2rgb(image_felzenszwalb, image, kind='avg') image_show(image_felzenszwalb_colored); 

Agora temos menos segmentos. Se quiséssemos ainda menos segmentos, poderíamos alterar o parâmetro de escala. Essa abordagem às vezes é chamada de segmentação excessiva.



É mais como uma imagem posterizada, que é essencialmente apenas uma redução no número de cores. Para combiná-los novamente (RAG).

Conclusão


A segmentação de imagens é uma etapa muito importante no processamento de imagens. Este é um campo ativo de pesquisa com várias aplicações, desde visão computacional a imagens médicas, tráfego e vigilância por vídeo. O Python fornece uma biblioteca robusta de imagem scikit com um grande número de algoritmos de processamento de imagem. Está disponível gratuitamente e sem restrições, apoiado por uma comunidade ativa. Eu recomendo que você leia a documentação deles. O artigo original pode ser encontrado aqui .

Source: https://habr.com/ru/post/pt441006/


All Articles