Skip to content

Blob Colours 2

Mateus de Assis Silva edited this page Sep 22, 2020 · 2 revisions

Funções encontradas nesse arquivo

  1. blob_colours_2

Introdução

Até antes da existência do reconhecimento via proximidade de cor, o Zebtrack reconhecia os peixes ao localizar os blobs e associá-los com os indivíduos mais próximos. De forma a refinar essa associação, utilizava-se uma "previsão" da posição dos peixes no próximo momento. Isso é passível de falha, pois os animais podem se movimentar de forma a confundir o software.

Um desses movimentos é quando os peixes se deslocam em retas concorrentes. No ponto de encontro das retas eles não prosseguem, mas se desviam. Isso faz com que o software troque as labels relativas a cada animal.

Assim se torna necessário uma forma de diferenciar os indivíduos. Dentre algumas possibilidades, utilizaremos um critério de cor.

Ao longo do monitoramento, se o usuário quiser acompanhar os indivíduos (peixes), o blob_colours irá fornecer as informações de cor para a distância de Mahalanobis, de forma a melhorar a associação blob-peixe.

O código blob_colours_2 é uma alternativa ao blob_colours por partir da premissa que a média será feita entre os pixels em RGB.

Como usar

O usuário define o frame a ser analizado, além de passar algumas saídas da função extractnblobs (são essas cx, cy, radius, boundingbox, ndetect - a qual é a quantidade de blobs detectados- e wframe_log - máscara binária). As variáveis l e cindicam as dimensões da tela (largura e comprimento). Por fim, a variável value_threshold indica limiares para Value. Caso deseje saber mais, a página Observações Gerais trata-se do porquê limitar o Value.

Para o vídeo de calibração utilizado nos testes (favor pedir ao responsável pelo projeto), recomenda-se utilizar: value_threshold = 0.15; saturation_threshold = 0.5; how_many_replicates = 5. Os valores dos limiares foram definidos via inspeção com uma imagem-teste, e o parâmetro restante foi utilizado conforme um exemplo.

A função retorna as médias das cores encontradas para cada blob no frame inserido. Reiterando: essa média vem na forma de um vetor RGB para cada blob detectado no frame.

Como funciona

A premissa do funcionamento do código é utilizar a função soma do MATLAB para somar os pixels na dimensão dos canais. Houveram problemas em realizar a soma de vetores iterativamente, e a função soma busca driblar essa dificuldade. Não se pode, entretanto, somar a esmo: apenas pixels não nulos (de acordo com a máscara binária), que possuem Value (no espaço HSV) acima de certo threshold definido pelo usuário (value_threshold) devem ser considerados. De forma a se executar isso, os pixels que não se adequam aos requerimentos são anulados ao longo do processo utilizando-se as multiplicações elemento-a-elemento adequadas.

O primeiro passo é alocar um cell array com dimensões ndetect por 1. Cada célula irá receber o vetor RGB médio (um pra cada blob).

Iteração para cada blob detectado

Recorta-se, no frame, o polígono relativo ao blob iterado (se o iterador está em k, será "recortado" o k-ésimo retângulo). Esse recorte é alocado em frame_retalho.

Recorta-se, no wframe_log (a máscara binária), o retângulo relativo ao k-ésimo blob detectado. Esse recorte é alocado em imdif_retalho.

Aplica-se a máscara imdif_retalho em todos os canais de frame_retalho utilizando a função do MATLAB bsxfun (para mais informações, cheque a referência Como aplicar máscara lógica em todos canais RGB). Agora, a imagem resultante será alocada em retalho_mascarado. Isso faz com que os pontos indicados nulos pela imagem binária se tornem nulos no respectivo recorte.

Transforma-se a imagem previamente processada (retalho_mascarado) para o espaço de cores HSV. Aloca-se o resultado em r_m_hsv. Define-se, em seguida, uma matriz binária bidimensional com as duas primeiras dimensões semelhantes às de retalho_mascarado, denominada mask_value. Ora, se a coordenada Value (3ª dimensão no espaço HSV) não estiver acima do limiar threshold_value, a respectiva posição em mask_value deve ser anulada; se estiver, se torna unitária (máscara binária, muito embora testes realizados tenha indicado o formato uint8).

Nota a respeito da função bsxfun do MATLAB: supõe-se que as máscaras utilizadas estão em formato binário (logical), mas é necessário multiplicar formatos de dados semelhantes. Para isso, se realiza um cast, tornando a máscara no mesmo formato de dado da imagem em processamento.

Agora aplica-se a matriz mask_value no retalho_mascarado, fazendo com que os pixels que não atenderam ao threshold se tornem nulos (todos os canais se zeram). O resultado é alocado em p_image.

Todo o processamento necessário para se eliminar pixels inadequados já foi concluído. Agora, basta somar a imagem na dimensão dos canais (3ª) para se fazer o somatório.Isso implica que cada matriz de canal (exite uma matriz bidimensional por canal de imagem) será colapsada em um valor escalar (a soma dos pixels).

Dado que o desenvolvimento dos códigos está sendo feito na versão R2015a do MATLAB, não se pode "somar direto". Realiza-se, portanto, a soma nas outras dimensões, colapsando a primeira e segunda dimensão, respectivamente. Remodela-se o vetor resultante, que estava com dimensões (1,1,3), para dimensão (1,3).

Para concluir o processo de média, conta-se quantos pixels foram utilizados pra soma. Ora, os pixels que foram utilizados para a soma são aqueles que não foram anulados durante os processamentos. Dado que cada processo foi feito utilizando máscaras binárias, os pixels não nulos do produto elemento-a-elemento entre ambas as máscaras foram os contabilizados. Como cada pixel não nulo é unitário, basta somar os elementos da matriz produto. Novamente utiliza-se o "truque" das somas consecutivas, delineado no parágrafo anterior. A quantidade de pixels utilizados é alocada em how_many_pixels.

Num caso extremo, é possível que nenhum pixel tenha sido contabilizado para a soma. Logo, supondo que how_many_pixels guarde um valor nulo, não faz sentido dividir avg_p_i por essa variável. Caso não seja nulo, a divisão é executada.

Por fim, o valor avg_p_i é salvo na k-ésima posição do cell array avg_vector, o qual é retornado.

Referências: