Após minha pergunta anterior sobre como encontrar dedos dentro de cada pata , comecei a carregar outras medidas para ver como ela se sustentaria. Infelizmente, rapidamente encontrei um problema com uma das etapas anteriores: reconhecer as patas.
Veja bem, minha prova de conceito basicamente tomou a pressão máxima de cada sensor ao longo do tempo e começaria a procurar a soma de cada linha, até encontrar nela! = 0,0. Em seguida, faz o mesmo para as colunas e assim que encontrar mais de 2 linhas com zero novamente. Ele armazena os valores mínimo e máximo de linhas e colunas em algum índice.
Como você pode ver na figura, isso funciona muito bem na maioria dos casos. No entanto, existem muitas desvantagens nessa abordagem (além de serem muito primitivas):
Os seres humanos podem ter "pés ocos", o que significa que existem várias linhas vazias na própria pegada. Como eu temia que isso pudesse acontecer com cães (grandes) também, esperei pelo menos 2 ou 3 linhas vazias antes de cortar a pata.
Isso cria um problema se outro contato feito em uma coluna diferente antes de atingir várias linhas vazias, expandindo a área. Eu acho que poderia comparar as colunas e ver se elas excedem um determinado valor, elas devem ser patas separadas.
O problema piora quando o cão é muito pequeno ou anda em um ritmo mais alto. O que acontece é que os dedos da pata dianteira ainda estão em contato, enquanto os dedos da pata traseira começam a fazer contato na mesma área da pata dianteira!
Com o meu script simples, ele não será capaz de dividir esses dois, porque teria que determinar quais quadros dessa área pertencem a qual pata, enquanto atualmente eu só precisaria examinar os valores máximos em todos os quadros.
Exemplos de onde começa a dar errado:
Portanto, agora estou procurando uma maneira melhor de reconhecer e separar as patas (depois disso, chegarei ao problema de decidir qual é a pata!).
Atualizar:
Estou mexendo na implementação da resposta de Joe (incrível!), Mas estou tendo dificuldades para extrair os dados reais das patas dos meus arquivos.
As coded_paws me mostram todas as patas diferentes, quando aplicadas à imagem de pressão máxima (veja acima). No entanto, a solução passa por cada quadro (para separar as patas sobrepostas) e define os quatro atributos do retângulo, como coordenadas ou altura / largura.
Não consigo descobrir como pegar esses atributos e armazená-los em alguma variável que eu possa aplicar aos dados de medição. Desde que eu preciso saber para cada pata, qual é a sua localização durante quais quadros e associá-lo a qual pata é (frontal / traseira, esquerda / direita).
Então, como posso usar os atributos Retângulos para extrair esses valores para cada pata?
Tenho as medidas que usei na configuração da pergunta na minha pasta pública do Dropbox ( exemplo 1 , exemplo 2 , exemplo 3 ). Para quem estiver interessado, também criei um blog para mantê-lo atualizado :-)
fonte
Respostas:
Se você está apenas querendo regiões (semi) contíguas, já existe uma implementação fácil no Python: o módulo ndimage.morphology do SciPy . Esta é uma operação morfológica de imagem bastante comum .
Basicamente, você tem 5 etapas:
Desfoque um pouco os dados de entrada para garantir que as patas tenham uma pegada contínua. (Seria mais eficiente usar apenas um kernel maior (o
structure
kwarg para as váriasscipy.ndimage.morphology
funções), mas isso não está funcionando corretamente por algum motivo ...)Limite a matriz para que você tenha uma matriz booleana de locais onde a pressão está acima de algum valor limite (ou seja
thresh = data > value
)Preencha os orifícios internos para ter regiões mais limpas (
filled = sp.ndimage.morphology.binary_fill_holes(thresh)
)Encontre as regiões contíguas separadas (
coded_paws, num_paws = sp.ndimage.label(filled)
). Isso retorna uma matriz com as regiões codificadas por número (cada região é uma área contígua de um número inteiro único (1 até o número de patas) com zeros em qualquer outro lugar)).Isole as regiões contíguas usando
data_slices = sp.ndimage.find_objects(coded_paws)
. Isso retorna uma lista de tuplas deslice
objetos, para que você possa obter a região dos dados de cada pata[data[x] for x in data_slices]
. Em vez disso, desenharemos um retângulo com base nessas fatias, o que exige um pouco mais de trabalho.As duas animações abaixo mostram os dados de exemplo "Patas sobrepostas" e "Patas agrupadas". Este método parece estar funcionando perfeitamente. (E para o que vale a pena, isso é executado de maneira muito mais suave do que as imagens GIF abaixo na minha máquina, então o algoritmo de detecção de pata é bastante rápido ...)
Aqui está um exemplo completo (agora com explicações muito mais detalhadas). A grande maioria disso está lendo a entrada e fazendo uma animação. A detecção real da pata é de apenas 5 linhas de código.
Atualização: Quanto à identificação de qual pata está em contato com o sensor em que horários, a solução mais simples é fazer a mesma análise, mas usar todos os dados de uma só vez. (ou seja, empilhe a entrada em uma matriz 3D e trabalhe com ela, em vez dos períodos de tempo individuais.) Como as funções ndimage do SciPy são destinadas a trabalhar com matrizes n-dimensionais, não precisamos modificar a função original de localização de patas em absoluto.
fonte
convert *.png output.gif
. Certamente, já tive o imagemagick colocando minha máquina de joelhos antes, embora funcionasse bem neste exemplo. No passado, eu usei este script: svn.effbot.python-hosting.com/pil/Scripts/gifmaker.py para escrever diretamente um gif animado de python sem salvar os quadros individuais. Espero que ajude! Vou postar um exemplo na pergunta @unutbu mencionada.bbox_inches='tight'
noplt.savefig
, o outro era impaciência :)Eu não sou especialista em detecção de imagens e não conheço Python, mas vou tentar ...
Para detectar patas individuais, você deve primeiro selecionar apenas tudo com uma pressão maior que um pequeno limiar, muito próximo de nenhuma pressão. Todo pixel / ponto acima disso deve ser "marcado". Em seguida, todos os pixels adjacentes a todos os pixels "marcados" ficam marcados e esse processo é repetido algumas vezes. Massas totalmente conectadas seriam formadas, para que você tenha objetos distintos. Então, cada "objeto" possui um valor mínimo e máximo de x e y, para que as caixas delimitadoras possam ser agrupadas em torno delas.
Pseudo-código:
(MARK) ALL PIXELS ABOVE (0.5)
(MARK) ALL PIXELS (ADJACENT) TO (MARK) PIXELS
REPEAT (STEP 2) (5) TIMES
SEPARATE EACH TOTALLY CONNECTED MASS INTO A SINGLE OBJECT
MARK THE EDGES OF EACH OBJECT, AND CUT APART TO FORM SLICES.
Isso deveria fazê-lo.
fonte
Nota: digo pixel, mas isso pode ser regiões usando uma média dos pixels. A otimização é outra questão ...
Parece que você precisa analisar uma função (pressão ao longo do tempo) para cada pixel e determinar onde a função gira (quando ela muda> X na outra direção, é considerada uma vez para combater erros).
Se você souber em que quadros ele gira, você saberá o quadro em que a pressão foi mais difícil e saberá onde foi o menos difícil entre as duas patas. Em teoria, você conheceria os dois quadros em que as patas pressionavam mais e pode calcular uma média desses intervalos.
Este é o mesmo passeio de antes, saber quando cada pata aplica mais pressão ajuda a decidir.
fonte