Estou ajudando uma clínica veterinária a medir a pressão sob uma pata de cachorro. Uso Python para minha análise de dados e agora estou tentando dividir as patas em sub-regiões (anatômicas).
Fiz uma matriz 2D de cada pata, que consiste nos valores máximos para cada sensor que foi carregado pela pata ao longo do tempo. Aqui está um exemplo de uma pata, em que usei o Excel para desenhar as áreas que quero 'detectar'. São 2 por 2 caixas ao redor do sensor com máximos locais, que juntos têm a maior soma.
Então, tentei fazer algumas experiências e decido simplesmente procurar o máximo de cada coluna e linha (não consigo olhar em uma direção devido ao formato da pata). Isso parece 'detectar' a localização dos dedos separados razoavelmente bem, mas também marca os sensores vizinhos.
Então, qual seria a melhor maneira de dizer ao Python quais desses máximos são os que eu quero?
Nota: Os quadrados 2x2 não podem se sobrepor, pois precisam ser dedos separados!
Também tomei 2x2 como uma conveniência, qualquer solução mais avançada é bem-vinda, mas sou simplesmente um cientista do movimento humano, por isso não sou um programador de verdade ou um matemático, por isso, mantenha-o 'simples'.
Aqui está uma versão que pode ser carregada comnp.loadtxt
Resultados
Então, tentei a solução do @ jextee (veja os resultados abaixo). Como você pode ver, funciona muito nas patas dianteiras, mas funciona menos bem nas patas traseiras.
Mais especificamente, ele não consegue reconhecer o pequeno pico que é o quarto dedo do pé. Obviamente, isso é inerente ao fato de o loop parecer de cima para baixo em direção ao valor mais baixo, sem levar em consideração onde está.
Alguém saberia como ajustar o algoritmo do @ jextee, para que ele também possa encontrar o quarto dedo do pé?
Como ainda não processei nenhum outro teste, não posso fornecer outras amostras. Mas os dados que forneci antes eram as médias de cada pata. Este arquivo é uma matriz com os dados máximos de 9 patas na ordem em que eles entraram em contato com a placa.
Esta imagem mostra como eles foram espalhados espacialmente sobre o prato.
Atualizar:
Criei um blog para qualquer pessoa interessada e configurei um SkyDrive com todas as medições brutas. Portanto, para quem solicita mais dados: mais poder para você!
Nova atualização:
Então, depois da ajuda que recebi com minhas perguntas sobre detecção e classificação de patas , finalmente pude verificar a detecção de dedos de todas as patas! Acontece que ele não funciona tão bem em nada, mas patas do tamanho de uma no meu próprio exemplo. É claro que, em retrospectiva, a culpa é minha por escolher o 2x2 tão arbitrariamente.
Aqui está um bom exemplo de onde dá errado: uma unha está sendo reconhecida como dedo do pé e o 'calcanhar' é tão largo que é reconhecido duas vezes!
Como a pata é muito grande, o tamanho de 2x2 sem sobreposição faz com que alguns dedos sejam detectados duas vezes. Por outro lado, em cães pequenos, muitas vezes não consegue encontrar o quinto dedo do pé, o que suspeito ser causado pela área 2x2 ser muito grande.
Depois de tentar a solução atual em todas as minhas medidas , cheguei à conclusão impressionante de que, para quase todos os meus cães pequenos, não encontrava o quinto dedo do pé e que em mais de 50% dos impactos para os cães grandes encontrava mais!
Tão claramente que preciso mudar isso. Meu palpite era mudar o tamanho do neighborhood
para algo menor para cães pequenos e maior para cães grandes. Mas generate_binary_structure
não me deixou mudar o tamanho da matriz.
Portanto, espero que alguém tenha uma sugestão melhor para localizar os dedos, talvez com a escala da área dos dedos com o tamanho da pata?
fonte
Respostas:
Detectei os picos usando um filtro máximo local . Aqui está o resultado do seu primeiro conjunto de dados de 4 patas:
Também o executei no segundo conjunto de 9 patas e também funcionou .
Aqui está como você faz isso:
Tudo o que você precisa fazer depois é usar
scipy.ndimage.measurements.label
a máscara para rotular todos os objetos distintos. Então você poderá brincar com eles individualmente.Observe que o método funciona bem porque o plano de fundo não é barulhento. Se fosse, você detectaria vários outros picos indesejados em segundo plano. Outro fator importante é o tamanho do bairro . Você precisará ajustá-lo se o tamanho do pico mudar (o valor deve permanecer aproximadamente proporcional).
fonte
Solução
Arquivo de dados: paw.txt . Código fonte:
Saída sem quadrados sobrepostos. Parece que as mesmas áreas estão selecionadas como no seu exemplo.
Alguns comentários
A parte complicada é calcular somas de todos os quadrados 2x2. Presumi que você precisava de todos eles, então pode haver alguma sobreposição. Usei fatias para cortar as primeiras / últimas colunas e linhas da matriz 2D original e, em seguida, sobrepor todas elas e calcular somas.
Para entender melhor, crie imagens de uma matriz 3x3:
Então você pode pegar as fatias:
Agora imagine que você os empilha um acima do outro e soma elementos nas mesmas posições. Essas somas serão exatamente as mesmas somas nos quadrados 2x2 com o canto superior esquerdo na mesma posição:
Quando você tem somas acima de quadrados 2x2, pode usar
max
para encontrar o máximo, ousort
, ousorted
para encontrar os picos.Para lembrar as posições dos picos, associo cada valor (a soma) à sua posição ordinal em uma matriz achatada (ver
zip
). Depois, calculo a posição da linha / coluna novamente quando imprimo os resultados.Notas
Eu permiti que os quadrados 2x2 se sobrepusessem. A versão editada filtra alguns deles, de modo que apenas quadrados não sobrepostos apareçam nos resultados.
Escolhendo os dedos (uma ideia)
Outro problema é como escolher o que provavelmente será o dedo de todos os picos. Eu tenho uma ideia que pode ou não funcionar. Eu não tenho tempo para implementá-lo agora, então apenas pseudo-código.
Notei que, se os dedos da frente permanecerem quase em um círculo perfeito, o dedo traseiro deverá estar dentro desse círculo. Além disso, os dedos da frente estão mais ou menos igualmente espaçados. Podemos tentar usar essas propriedades heurísticas para detectar os dedos.
Pseudo-código:
Essa é uma abordagem de força bruta. Se N é relativamente pequeno, acho que é factível. Para N = 12, há C_12 ^ 5 = 792 combinações, vezes 5 maneiras de selecionar um dedo traseiro, portanto, 3960 casos para avaliar para cada pata.
fonte
Este é um problema de registro de imagem . A estratégia geral é:
Aqui está uma abordagem grosseira e pronta , "a coisa mais idiota que poderia funcionar":
Para combater o problema de orientação, você pode ter 8 configurações iniciais para as direções básicas (norte, nordeste, etc). Execute cada um individualmente e jogue fora todos os resultados em que dois ou mais dedos do pé terminem no mesmo pixel. Pensarei um pouco mais sobre isso, mas esse tipo de coisa ainda está sendo pesquisado no processamento de imagens - não há respostas corretas!
Ideia um pouco mais complexa: K (ponderado) significa cluster. Não é tão ruim.
Em seguida, itere até a convergência:
Esse método quase certamente dará resultados muito melhores e você obtém a massa de cada cluster, o que pode ajudar na identificação dos dedos dos pés.
(Novamente, você especificou o número de clusters antecipadamente. Com o cluster, você deve especificar a densidade de uma maneira ou de outra: Escolha o número de clusters, apropriado neste caso, ou escolha um raio de cluster e veja quantos você termina Um exemplo deste último é o deslocamento médio .)
Desculpe a falta de detalhes de implementação ou outras especificidades. Eu codificaria isso, mas tenho um prazo. Se nada mais funcionar até a próxima semana, avise-me e eu vou tentar.
fonte
Usando homologia persistente para analisar seu conjunto de dados, obtenho o seguinte resultado (clique para ampliar):
Esta é a versão 2D do método de detecção de pico descrito nesta resposta do SO . A figura acima mostra simplesmente classes de homologia persistente em 0-dimensão classificadas por persistência.
Eu fiz um upscale do conjunto de dados original por um fator 2 usando scipy.misc.imresize (). No entanto, observe que eu considerei as quatro patas como um conjunto de dados; dividi-lo em quatro facilitaria o problema.
Metodologia. A idéia por trás disso é bem simples: considere o gráfico da função que atribui a cada pixel seu nível. Se parece com isso:
Agora considere um nível de água na altura 255 que desce continuamente para níveis mais baixos. Nas ilhas máximas locais, aparece (nascimento). Nos pontos de sela, duas ilhas se fundem; consideramos que a ilha inferior se fundirá à ilha superior (morte). O chamado diagrama de persistência (das classes de homologia da 0ª dimensão, nossas ilhas) mostra os valores de morte por nascimento de todas as ilhas:
A persistência de uma ilha é então a diferença entre o nível de nascimento e a morte; a distância vertical de um ponto para a diagonal principal cinza. A figura rotula as ilhas diminuindo a persistência.
A primeira imagem mostra a localização dos nascimentos das ilhas. Este método não apenas fornece os máximos locais, mas também quantifica seu "significado" pela persistência acima mencionada. Um seria filtrar todas as ilhas com uma persistência muito baixa. No entanto, no seu exemplo, toda ilha (ou seja, todo máximo local) é um pico que você procura.
O código Python pode ser encontrado aqui .
fonte
Este problema foi estudado em profundidade por físicos. Existe uma boa implementação no ROOT . Veja as classes TSpectrum (especialmente TSpectrum2 para o seu caso) e a documentação para elas.
Referências:
... e para aqueles que não têm acesso a uma assinatura do NIM:
fonte
Aqui está uma idéia: você calcula o (discreto) Laplaciano da imagem. Eu esperaria que fosse (negativo e) grande no máximo, de uma maneira mais dramática do que nas imagens originais. Assim, o máximo poderia ser mais fácil de encontrar.
Aqui está outra idéia: se você conhece o tamanho típico dos pontos de alta pressão, pode suavizar sua imagem convoluindo-a com um Gaussiano do mesmo tamanho. Isso pode fornecer imagens mais simples de processar.
fonte
Apenas algumas idéias em cima da minha cabeça:
Você também pode querer dar uma olhada no OpenCV , ele possui uma API Python bastante decente e pode ter algumas funções que você achar úteis.
fonte
Tenho certeza de que você já tem o suficiente para continuar, mas não posso deixar de sugerir o uso do método de agrupamento k-means. O k-means é um algoritmo de cluster não supervisionado que leva os dados (em qualquer número de dimensões - por acaso faço isso em 3D) e os organiza em k clusters com limites distintos. Aqui é legal porque você sabe exatamente quantos dedos esses caninos devem ter.
Além disso, é implementado no Scipy, o que é muito bom ( http://docs.scipy.org/doc/scipy/reference/cluster.vq.html ).
Aqui está um exemplo do que ele pode fazer para resolver espacialmente clusters 3D:
O que você quer fazer é um pouco diferente (2D e inclui valores de pressão), mas ainda acho que você poderia tentar.
fonte
obrigado pelos dados brutos. Estou no trem e isso é o mais longe que consegui (minha parada está chegando). Eu massageei seu arquivo txt com regexps e o coloquei em uma página html com algum javascript para visualização. Estou compartilhando aqui porque alguns, como eu, podem achar mais fácil de hackear do que python.
Eu acho que uma boa abordagem será invariável em escala e rotação, e meu próximo passo será investigar misturas de gaussianos. (cada pata é o centro de um gaussiano).
fonte
Solução do físico:
defina 5 marcadores de pata identificados por suas posições
X_i
e inicie-os com posições aleatórias. Definir alguma função energética que combine algum prêmio pela localização dos marcadores nas posições das patas com alguma punição pela sobreposição de marcadores; Digamos:(
S(X_i)
é a força média em 2x2 quadrado ao redorX_i
,alfa
é um parâmetro a ser atingido experimentalmente)Agora é hora de fazer mágica de Metropolis-Hastings:
1. Selecione um marcador aleatório e mova-o um pixel em direção aleatória.
2. Calcule dE, a diferença de energia que esse movimento causou.
3. Obtenha um número aleatório uniforme de 0-1 e chame-o de r.
4. Se
dE<0
ouexp(-beta*dE)>r
, aceite a mudança e vá para 1; caso contrário, desfaça o movimento e vá para 1.Isso deve ser repetido até que os marcadores converjam em patas. O Beta controla a varredura para otimizar o tradeoff, por isso também deve ser otimizado experimentalmente; também pode ser aumentado constantemente com o tempo da simulação (recozimento simulado).
fonte
Heres outra abordagem que eu usei ao fazer algo semelhante para um grande telescópio:
1) Procure o pixel mais alto. Depois disso, procure em torno dele o melhor ajuste para 2x2 (talvez maximizando a soma de 2x2) ou faça um ajuste gaussiano em 2d dentro da sub-região, por exemplo, 4x4, centrado no pixel mais alto.
Em seguida, defina os pixels 2x2 encontrados como zero (ou talvez 3x3) ao redor do centro do pico
volte para 1) e repita até que o pico mais alto caia abaixo de um limite de ruído, ou você tenha todos os dedos dos pés necessários
fonte
Provavelmente vale a pena tentar com redes neurais se você conseguir criar alguns dados de treinamento ... mas isso precisa de muitas amostras anotadas manualmente.
fonte
um esboço aproximado ...
você provavelmente desejaria usar um algoritmo de componentes conectados para isolar cada região de pata. O wiki tem uma descrição decente disso (com algum código) aqui: http://en.wikipedia.org/wiki/Connected_Component_Labeling
você terá que tomar uma decisão sobre o uso da conexão 4 ou 8. pessoalmente, para a maioria dos problemas, eu prefiro a conexão 6. de qualquer forma, depois de separar cada "impressão de pata" como uma região conectada, deve ser fácil o suficiente percorrer a região e encontrar os máximos. depois de encontrar o máximo, você poderá aumentar iterativamente a região até atingir um limite predeterminado para identificá-lo como um "dedo do pé" determinado.
Um problema sutil aqui é que, assim que você começa a usar as técnicas de visão computacional para identificar algo como uma pata direita / esquerda / dianteira / traseira e começa a observar os dedos dos pés individuais, deve começar a levar em consideração rotações, inclinações e traduções. isso é realizado através da análise dos chamados "momentos". existem alguns momentos diferentes a serem considerados nas aplicações de visão:
momentos centrais: invariantes da tradução momentos normalizados: invariantes da escala e da tradução hu momentos: invariantes da tradução, escala e rotação
mais informações sobre momentos podem ser encontradas pesquisando "momentos da imagem" no wiki.
fonte
Talvez você possa usar algo como Gaussian Mixture Models. Aqui está um pacote Python para fazer GMMs (fiz uma pesquisa no Google) http://www.ar.media.kyoto-u.ac.jp/members/david/softwares/em/
fonte
Parece que você pode trapacear um pouco usando o algoritmo do jetxee. Ele está achando bem os três primeiros dedos, e você deve ser capaz de adivinhar onde o quarto se baseia.
fonte
Problema interessante. A solução que eu tentaria é a seguinte.
Aplique um filtro passa-baixo, como convolução com uma máscara gaussiana 2D. Isso fornecerá vários valores (provavelmente, mas não necessariamente ponto flutuante).
Realize uma supressão 2D não máxima usando o raio aproximado conhecido de cada almofada de pata (ou dedo do pé).
Isso deve fornecer as posições máximas sem ter vários candidatos próximos. Apenas para esclarecer, o raio da máscara na etapa 1 também deve ser semelhante ao raio usado na etapa 2. Esse raio pode ser selecionável ou o veterinário pode explicitamente medi-la de antemão (variará com a idade / raça / etc).
Algumas das soluções sugeridas (mudança média, redes neurais etc.) provavelmente funcionarão até certo ponto, mas são excessivamente complicadas e provavelmente não são ideais.
fonte
Bem, aqui está um código simples e não muito eficiente, mas para esse tamanho de um conjunto de dados está bom.
Basicamente, apenas faço uma matriz com a posição do canto superior esquerdo e a soma de cada quadrado 2x2 e classifico pela soma. Então pego o quadrado 2x2 com a soma mais alta de contenção, coloco-o na
best
matriz e removo todos os outros quadrados 2x2 que usaram qualquer parte dele que acabou de remover o quadrado 2x2.Parece funcionar bem, exceto com a última pata (aquela com a menor soma na extremidade direita da sua primeira foto), verifica-se que existem outros dois quadrados 2x2 elegíveis com uma soma maior (e eles têm uma soma igual a entre si). Um deles ainda seleciona um quadrado do seu quadrado 2x2, mas o outro está à esquerda. Felizmente, por sorte, vemos escolher mais do que você gostaria, mas isso pode exigir que outras idéias sejam usadas para obter o que você realmente quer o tempo todo.
fonte
Só quero dizer a vocês que existe uma boa opção para encontrar local
maxima
em imagens com python:ou para skimage
0.8.0
:http://scikit-image.org/docs/0.8.0/api/skimage.feature.peak.html
fonte
Talvez uma abordagem ingênua seja suficiente aqui: crie uma lista de todos os quadrados 2x2 do seu avião, ordene-os pela soma (em ordem decrescente).
Primeiro, selecione o quadrado de maior valor na sua "lista de patas". Em seguida, escolha iterativamente quatro dos próximos melhores quadrados que não se cruzam com nenhum dos quadrados encontrados anteriormente.
fonte
Existem várias e extensas peças de software disponíveis na comunidade de astronomia e cosmologia - esta é uma área significativa de pesquisa tanto histórica quanto atualmente.
Não se assuste se você não é um astrônomo - alguns são fáceis de usar fora do campo. Por exemplo, você pode usar astropy / photutils:
https://photutils.readthedocs.io/en/stable/detection.html#local-peak-detection
[Parece um pouco grosseiro repetir seu código de exemplo curto aqui.]
Uma lista incompleta e ligeiramente tendenciosa de técnicas / pacotes / links que podem ser interessantes é dada abaixo - adicione mais nos comentários e atualizarei esta resposta conforme necessário. Obviamente, há uma troca de precisão versus recursos de computação. [Honestamente, existem muitos exemplos de códigos em uma única resposta como essa, por isso não tenho certeza se essa resposta será ou não correta.]
Extrator de origem https://www.astromatic.net/software/sextractor
MultiNest https://github.com/farhanferoz/MultiNest [+ pyMultiNest]
Desafio de busca de fonte ASKAP / EMU: https://arxiv.org/abs/1509.03931
Você também pode procurar por desafios de extração de fonte Planck e / ou WMAP.
...
fonte
E se você prosseguir passo a passo: primeiro localize o máximo global, processe, se necessário, os pontos circundantes, considerando seu valor, depois defina a região encontrada para zero e repita para a próxima.
fonte
Não sei se isso responde à pergunta, mas parece que você pode apenas procurar os n picos mais altos que não têm vizinhos.
Aqui está a essência. Observe que está em Ruby, mas a ideia deve ser clara.
fonte