Este artigo demonstra como programar a extração de fronteiras internas e externas, e esqueletização de imagens binárias apresentadas em Morfologia Matemática – Esqueletização de imagem e Morfologia Matemática – Extração de Fronteiras / Detecção de Bordas. A programação foi realizada na linguagem javascript, utilizando a biblioteca opencv.js e as imagens produzidas com a IDE OpenCV-Flow.
OpenCV
A biblioteca opencv.js disponibiliza diversas funcionalidade prontas para o processamento de imagens e visão computacional. Para utilizá-la, basta incluí-la no script da página conforme descrito em Using OpenCV.js.
Extração de Fronteira Interna
A fronteira interna, como apresentado no outro post citado, é o contorno da imagem binarizada. O cálculo consiste em subtrair da imagem o resultado da erosão da própria imagem, por um elemento estruturante.
A primeira etapa que iremos realizar é criar o núcleo de operação com o formato de quadrado. O elemento estruturante deve ser uma imagem binarizada (preto e branco). Como não temos imagens binárias no OpenCV, criaremos uma imagem em tons de cinza.
Na função nucleoFormatoCruz abaixo, é criada uma matriz de tamanho 3×3, com um canal de cor do tipo uint8, com valores entre 0 e 255:
function nucleoFormatoCruz() { return new cv.matFromArray(3, 3, cv.CV_8UC1, [ 0, 1, 0, 1, 1, 1, 0, 1, 0 ]); }
Com o núcleo criado, basta realiza a erosão da imagem binarizada e depois subtrair a imagem original pelo resultado desta operação. A função abaixo realiza este processo com a biblioteca OpenCV.js:
function extrairFronteiraInterna(imagem, imagemSaida, nucleo) { let imgTemporaria = new cv.Mat(imagem.rows, imagem.cols, imagem.type(), new cv.Scalar(0)); // Realiza a erosão com biblioteca opencv. cv.erode(imagem, imgTemporaria, nucleo); // Realiza a subtração da imagem pela mesma erodida cv.subtract(imagem, imgTemporaria, imagemSaida); }
A primeira operação realizada com a função cv.erode, recebe uma imagem de entrada, uma imagem que receberá o resultado da operação de erosão e o núcleo para a operação.
A segunda e última operação realizada é a subtração com a função cv.subtract, que recebe uma imagem de entrada, uma segunda imagem com os valores das subtrações a serem realizadas, e por fim uma imagem de saída que receberá o resultado desta operação.
É possível simplificar esta função conforme abaixo:
function extrairFronteiraInterna(imagem, imagemSaida, nucleo) { // Realiza a erosão com biblioteca opencv. cv.erode(imagem, imagemSaida, nucleo); // Realiza a subtração da imagem pela mesma erodida cv.subtract(imagem, imagemSaida, imagemSaida); }
Note que, foi informada a variável imagemSaida como segundo e terceiro parâmetro, da segunda operação. Foi realizado isto, pois a variável imagemSaida contém o resultado da erosão, que é preciso para subtrair os valores e para não precisarmos criar uma terceira variável, simplificando a função. Não há problemas em utilizar a mesma variável na operação cv.subtract, pois ela é uma operação não convolucional, que realiza apenas operações que utilizam um único pixel de cada vez.
Extração de Fronteiras Externas
A fronteira externa, na imagem binarizada, cria uma camada como uma vestimenta que cobre toda a imagem. O cálculo consiste em dilatar a imagem por um elemento estruturante e subtrair pela imagem original. De certa forma, o inverso da operação para extração da fronteira interna.
A primeira etapa para realizar esta operação, também consiste em criar um núcleo para a operação de dilatação. Utilizaremos o mesmo da seção anterior. Com o núcleo criado basta realizar as operações conforme a função abaixo:
function extrairFronteiraExterna(imagem, imagemSaida, nucleo) { let imgTemporaria = new cv.Mat(imagem.rows, imagem.cols, imagem.type(), new cv.Scalar(0)); // Realiza a erosão com biblioteca opencv. cv.dilate(imagem, imgTemporaria, nucleo); // Realiza a subtração da imagem pela mesma erodida cv.subtract(imgTemporaria, imagem, imagemSaida); }
Observe que para extração da fronteira externa, foi trocada a operação de erosão pela dilatação com a função cv.dilate, e alterada a ordem dos parâmetros durante a subtração com a função cv.subtract.
Esqueletização de Imagem Binarizada
Pare realizar a esqueletização com morfologia, conforme apresentado em Morfologia Matemática – Esqueletização de imagem, basta realizar quatro operações, dentro de um laço de repetição, e realizar a inicialização de algumas variáveis para este processamento.
Observe na função abaixo, que nas primeiras linhas, antes do laço while, foram inicializados variáveis para o processamento da esqueletização, criando uma imagem temporária chamada clone, com as mesmas informações da imagem original e um núcleo que é utilizado para reduzir a estrutura da imagem original.
function extrairEsqueleto(imagem, imagemSaida) { // Inicializa as matrizes const erosao = new cv.Mat(imagem.rows, imagem.cols, src.type()); const abertura = new cv.Mat(imagem.rows, imagem.cols, src.type()); const subtracao = new cv.Mat(imagem.rows, imagem.cols, src.type()); // Clona a imagem original let clone = imagem.clone(); // Cria o núcleo(3x3) em formato de cruz const nucleo = cv.getStructuringElement(cv.MORPH_CROSS, new cv.Size(3, 3), new cv.Point(-1, -1)); while (cv.countNonZero(clone) !== 0) { cv.erode(clone, erosao, nucleo); cv.dilate(erosao, abertura, nucleo); cv.subtract(clone, abertura, subtracao); cv.bitwise_or(imagemSaida, subtracao, imagemSaida); clone = erosao.clone(); GCStore.add(clone); } // Deleta as variáveis temporárias delete erosao; delete abertura; delete subtracao; delete clone; }
Após a inicialização das variáveis, é realizado um laço que verifica se a imagem clone está vazia (só com valores zero), caso não esteja, o laço fica realizando as seguintes operações:
- As duas primeiras operações realizadas, dentro do laço, consistem em realizar uma erosão, seguida de uma dilatação.
- Como resultado é criado uma abertura da imagem, pelo elemento estruturante em formato cruz;
- Esta abertura remove os pontos das extremidades da imagem clone.
- A terceiraça operação, realiza a subtração da imagem clone pela imagem de abertura.
- Como resultado, na matriz subtração, estão apenas os pontos com as extremidades da imagem clone.
- Por fim, os pontos da extremidades, são salvos na imagem de saída.
O processo é repetido até que a imagem clone seja zerada, durante o processo de erosão.
Note que, em cada laço realizado, é coletado os pontos da extremidades da imagem clone e salvo na imagem de saída, e em cada laço a imagem é diminuída de tamanho. Este processo resulta na criação do esqueleto da imagem.
Cuidados
Caso você vá utilizar a biblioteca opencv, saiba que as funções morfológicas dela também realizam morfologia matemática em imagens em tons de cinza e coloridas, que possuem resultados muito diferentes das operações morfológicas em imagens binarizadas. Lembre-se de converter a imagem em preto e branco, para realizar os experimentos aqui apresentados.
Considerações
As funções programadas neste post possuem caráter de estudo, afim de entender as etapas e os processos morfológicos, pois não foram pensadas em questões como desempenho nestes exemplos. Caso você precise utilizar este tipo de operação, é recomendado que utilize uma biblioteca preparada para isso, como o OpenCV ou similar.
O código aqui apresentado esta disponível no link abaixo:
Source code: https://github.com/visaocomputacionalexemplos/morfologia/blob/main/javascript/base/esqueletizacao.html
Nenhum comentário ainda!