Skip to main content
Balneário Camboriú - SC +55 (47) 99725 1117
Siga-nos:

Este artigo trata de como programar as operações morfológicas binárias apresentadas no post Morfologia Matemática para Processamento de Imagens. 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.

Utilizaremos a classe Mat do OpenCV para realizar a manipulação da imagem, com esta classe conseguimos construir de forma fácil imagens (coloridas e binárias) e manipular seus pixels.

Erosão

A erosão como apresentado no outro post citado, consiste em testar se o elemento estruturante (núcleo) se encaixa na imagem de origem, gerando uma nova imagem de destino, onde cada teste realizado com sucesso, é identificado na imagem de destino com o valor 1 e identificado com o valor 0 caso a estrutura não tenha sido localizada. 

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 binárizada (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, [
    1, 1, 1,
    1, 1, 1,
    1, 1, 1
  ]);
}

Além do núcleo das operações, vamos precisar da posição central das operações, sendo que no OpenCV é utilizada a classe cv.Point para indicar posições específicas, com o objetivo de seguir o padrão, nosso código também ira utilizá-la. Para inicializar a classe, é preciso apenas informar as posições das coordenadas x e y, conforme abaixo:

const centro = new cv.Point(1, 1);

Agora que temos nosso núcleo e sua posição, nossa função de erosão foi programada com as seguintes etapas:

  • Percorre todas as posições dos pixels da imagem;
  • Para cara pixel da imagem, percorre todos os elementos do elemento estruturante e testa a estrutura na imagem conforme as etapas:
    • Verifica-se se o elemento do núcleo possui valor e:
      • Se o elemento possuir valor, verifica a posição correspondente na imagem também possui valor e:
        • Se o pixel possuir valor, verifica o próximo elemento;
        • Se o pixel não possuir, marca o teste como negado;
    • Se o elemento não possui valor, verifica o próximo elemento;
    • Ao finalizar todos os testes dos elementos, do núcleo na região do pixel, e caso nenhum for negado, marca na imagem de destino o valor 255, do contrário marca como 0;
function erosao(nucleo, centro, imagem, imagemSaida) {
  //Percorre a imagem
  for (let x = centro.x; x < imagem.cols; x++) {
    for (let y = centro.y; y < imagem.rows; y++) {

      let hasNucleo = true;
      //Percorre o elemento estruturante (núcleo)
      for (let j = 0; j < nucleo.cols; j++) {
        for (let k = 0; k < nucleo.rows; k++) {
          
          //Verifica-se se o elemento do núcleo deve ser checado
          const nucleoTemValor = nucleo.ucharPtr(k, j)[0] > 0;
          if (nucleoTemValor) {
            const col = x + j - centro.x;
            const row = y + k - centro.y;

            //Verifica-se se a imagem tem valor na mesma posição do núcleo
            const imagemTemValor = imagem.ucharPtr(row, col)[0] > 0;
            if (!imagemTemValor) {
              hasNucleo = false;
              break;
            }
          }
        }
      }

      imagemSaida.ucharPtr(y, x)[0] = hasNucleo ? 255 : 0;
    }
  }
}

Dilatação

A dilatação consiste em testar cada elemento da imagem de origem e verificar se possui valor 1, caso exista na imagem de destino, então é adicionado os valores do elemento estruturante a partir da posição central do elemento estruturante.

A programação da dilatação é menos complexa que a da erosão, nossa função realiza apenas as seguintes etapas:

  • Percorre todas as posições dos pixeis da imagem;
  • Para cara pixel da imagem:
    • Verifica-se se o pixel possui valor e:
      • Caso possuir, percorre os elementos do núcleo e os projeta na imagem de destino;
      • Caso não possuir, não realiza nenhuma operação;
function dilatacao(nucleo, centro, imagem, imagemSaida) {
  //Percorre a imagem
  for (let x = centro.x; x < imagem.cols; x++) {
    for (let y = centro.y; y < imagem.rows; y++) {

      //Verifica-se se o pixel da imagem possui valor positivo
      const pixelComValor = imagem.ucharPtr(y, x)[0] > 0;
      if (pixelComValor) {

        //Percorre o elemento estruturante (núcleo)
        for (let j = 0; j < nucleo.cols; j++) {
          for (let k = 0; k < nucleo.rows; k++) {
            
            //Verifica-se se o elemento do núcleo tem valor positivo
            const nucleoTemValor = nucleo.ucharPtr(k, j)[0] > 0;
            if (nucleoTemValor) {
              const col = x + j - centro.x;
              const row = y + k - centro.y;

              imagemSaida.ucharPtr(row, col)[0] = 255;
            }
          }
        }
      }
    }
  }
}

Abertura

A abertura de uma imagem A, por um elemento estruturante B, é simplesmente a operação de erosão de A por B, seguida da dilatação de A por B. Como a abertura é apenas o encadeamento de duas operações, nosso código de exemplo faz apenas isto.

function abertura(nucleo, centro, imagem, imagemSaida) {
  let imgTemporaria = new cv.Mat( imagem.rows, imagem.cols, imagem.type(), new cv.Scalar(0));
  
  erosao(nucleo, centro, imagem, imgTemporaria);
  dilatacao(nucleo, centro, imgTemporaria, imagemSaida);

  imgTemporaria.delete();
}

Fechamento

O fechamento de uma imagem A, por um elemento estruturante B, é simplesmente a operação de dilatação de A por B, seguida da erosão de A por B. Como o fechamento também é apenas o encadeamento de duas operações, nosso código de exemplo faz apenas isto também.

function fechamento(nucleo, centro, imagem, imagemSaida) {
  let imgTemporaria = new cv.Mat( imagem.rows, imagem.cols, imagem.type(), new cv.Scalar(0));

  dilatacao(nucleo, centro, imagem, imgTemporaria);
  erosao(nucleo, centro, imgTemporaria, imagemSaida);

  imgTemporaria.delete();
}

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/morfologia.html

Nenhum comentário ainda!

Seu endereço de e-mail não será publicado