Quais técnicas de processamento de imagem podem ser usadas para implementar um aplicativo que detecta as árvores de Natal exibidas nas imagens a seguir?
Estou procurando soluções que funcionem em todas essas imagens. Portanto, abordagens que requerem treinamento de classificadores em cascata de haar ou correspondência de modelos não são muito interessantes.
Estou procurando algo que possa ser escrito em qualquer linguagem de programação, desde que use apenas tecnologias de código aberto . A solução deve ser testada com as imagens compartilhadas nesta pergunta. Existem 6 imagens de entrada e a resposta deve exibir os resultados do processamento de cada uma delas. Finalmente, para cada imagem de saída deve haver linhas vermelhas desenhadas para cercar a árvore detectada.
Como você detectaria programaticamente as árvores nessas imagens?
fonte
Respostas:
Eu tenho uma abordagem que eu acho interessante e um pouco diferente do resto. A principal diferença em minha abordagem, em comparação com algumas outras, está na maneira como a etapa de segmentação da imagem é executada - usei o algoritmo de agrupamento DBSCAN do scikit -learn do Python; é otimizado para encontrar formas um tanto amorfas que podem não ter necessariamente um único centróide claro.
No nível superior, minha abordagem é bastante simples e pode ser dividida em três etapas. Primeiro aplico um limite (ou, na verdade, o lógico "ou" de dois limites separados e distintos). Como em muitas outras respostas, presumi que a árvore de Natal seria um dos objetos mais brilhantes da cena; portanto, o primeiro limiar é apenas um simples teste de brilho monocromático; todos os pixels com valores acima de 220 em uma escala de 0 a 255 (onde preto é 0 e branco é 255) são salvos em uma imagem em preto e branco binária. O segundo limiar tenta procurar luzes vermelhas e amarelas, que são particularmente proeminentes nas árvores no canto superior esquerdo e no canto inferior direito das seis imagens, e se destacam bem contra o fundo azul esverdeado, predominante na maioria das fotos. Eu converto a imagem rgb em espaço hsv, e exija que o matiz seja menor que 0,2 em uma escala de 0,0-1,0 (correspondendo aproximadamente à borda entre amarelo e verde) ou maior que 0,95 (correspondente à borda entre roxo e vermelho) e, além disso, preciso de cores saturadas e brilhantes: a saturação e o valor devem estar acima de 0,7. Os resultados dos dois procedimentos de limite são logicamente "ou" juntos, e a matriz resultante de imagens binárias em preto e branco é mostrada abaixo:
Você pode ver claramente que cada imagem possui um grande agrupamento de pixels, correspondendo aproximadamente à localização de cada árvore, e algumas também têm outros pequenos agrupamentos, correspondendo a luzes nas janelas de alguns dos edifícios ou a um cena de fundo no horizonte. A próxima etapa é fazer com que o computador reconheça que esses são clusters separados e rotule cada pixel corretamente com um número de identificação de associação ao cluster.
Para esta tarefa, escolhi o DBSCAN . Há uma boa comparação visual de como o DBSCAN normalmente se comporta, em relação a outros algoritmos de cluster, disponíveis aqui . Como eu disse anteriormente, ele se dá bem com formas amorfas. A saída do DBSCAN, com cada cluster plotado em uma cor diferente, é mostrada aqui:
Há algumas coisas a serem observadas ao analisar esse resultado. Primeiro, o DBSCAN exige que o usuário defina um parâmetro de "proximidade" para regular seu comportamento, que controla efetivamente a separação de um par de pontos para que o algoritmo declare um novo cluster separado em vez de aglomerar um ponto de teste no um cluster já pré-existente. Defino esse valor como 0,04 vezes o tamanho na diagonal de cada imagem. Como as imagens variam em tamanho, de aproximadamente VGA a HD 1080, esse tipo de definição relativa à escala é crítico.
Outro ponto digno de nota é que o algoritmo DBSCAN, conforme implementado no scikit-learn, possui limites de memória bastante desafiadores para algumas das imagens maiores desta amostra. Portanto, para algumas das imagens maiores, eu realmente tive que "dizimar" (ou seja, reter apenas todos os 3 ou 4 pixels e soltar os outros) cada cluster para permanecer dentro desse limite. Como resultado desse processo de seleção, os pixels esparsos individuais restantes são difíceis de visualizar em algumas das imagens maiores. Portanto, apenas para fins de exibição, os pixels codificados por cores nas imagens acima foram efetivamente "dilatados" apenas um pouco para que se destacem melhor. É uma operação puramente cosmética em prol da narrativa; embora haja comentários mencionando essa dilatação no meu código,
Depois que os clusters são identificados e rotulados, a terceira e última etapa é fácil: eu simplesmente pego o maior cluster de cada imagem (nesse caso, escolhi medir o "tamanho" em termos do número total de pixels de membros, embora se possa com a mesma facilidade, utilizaram algum tipo de métrica que mede a extensão física) e calculam o casco convexo para esse cluster. O casco convexo torna-se então a borda da árvore. Os seis cascos convexos calculados por esse método são mostrados abaixo em vermelho:
O código fonte foi escrito para o Python 2.7.6 e depende do numpy , scipy , matplotlib e scikit-learn . Dividi em duas partes. A primeira parte é responsável pelo processamento real da imagem:
e a segunda parte é um script no nível do usuário que chama o primeiro arquivo e gera todos os gráficos acima:
fonte
scipy.ndimage.filters.maximum_filter()
no mesmo local em que eu havia usado um limite.NOTA DE EDIÇÃO: Editei este post para (i) processar cada imagem da árvore individualmente, conforme solicitado nos requisitos, (ii) para considerar o brilho e a forma do objeto, a fim de melhorar a qualidade do resultado.
A seguir, é apresentada uma abordagem que leva em consideração o brilho e a forma do objeto. Em outras palavras, procura objetos com formato de triângulo e com brilho significativo. Foi implementado em Java, usando Marvin estrutura de processamento de imagem .
O primeiro passo é o limiar de cores. O objetivo aqui é focar a análise em objetos com brilho significativo.
imagens de saída:
Código fonte:
Na segunda etapa, os pontos mais brilhantes da imagem são dilatados para formar formas. O resultado desse processo é a provável forma dos objetos com brilho significativo. Aplicando a segmentação de preenchimento, formas desconectadas são detectadas.
imagens de saída:
Código fonte:
Como mostrado na imagem de saída, várias formas foram detectadas. Nesse problema, existem apenas alguns pontos brilhantes nas imagens. No entanto, essa abordagem foi implementada para lidar com cenários mais complexos.
Na próxima etapa, cada forma é analisada. Um algoritmo simples detecta formas com um padrão semelhante a um triângulo. O algoritmo analisa a forma do objeto linha por linha. Se o centro da massa de cada linha de forma é quase o mesmo (dado um limite) e a massa aumenta à medida que y aumenta, o objeto tem uma forma de triângulo. A massa da linha da forma é o número de pixels nessa linha que pertence à forma. Imagine dividir o objeto horizontalmente e analisar cada segmento horizontal. Se eles estiverem centralizados entre si e o comprimento aumentar do primeiro para o último em um padrão linear, você provavelmente terá um objeto que se assemelha a um triângulo.
Código fonte:
Finalmente, a posição de cada forma semelhante a um triângulo e com brilho significativo, neste caso uma árvore de Natal, é destacada na imagem original, como mostrado abaixo.
imagens finais de saída:
código fonte final:
A vantagem dessa abordagem é o fato de provavelmente funcionar com imagens contendo outros objetos luminosos, uma vez que analisa a forma do objeto.
Feliz Natal!
NOTA DE EDIÇÃO 2
Há uma discussão sobre a semelhança das imagens de saída desta solução e algumas outras. De fato, eles são muito parecidos. Mas essa abordagem não apenas segmenta objetos. Também analisa as formas do objeto em algum sentido. Ele pode lidar com vários objetos luminosos na mesma cena. De fato, a árvore de Natal não precisa ser a mais brilhante. Estou apenas escrevendo para enriquecer a discussão. Há um viés nas amostras de que, apenas procurando o objeto mais brilhante, você encontrará as árvores. Mas, realmente queremos parar a discussão neste momento? Neste ponto, até que ponto o computador está realmente reconhecendo um objeto que se assemelha a uma árvore de Natal? Vamos tentar fechar essa lacuna.
Abaixo é apresentado um resultado apenas para elucidar esse ponto:
imagem de entrada
resultado
fonte
Aqui está a minha solução simples e burra. É baseado na suposição de que a árvore será a coisa mais brilhante e grande da imagem.
O primeiro passo é detectar os pixels mais brilhantes da imagem, mas precisamos fazer uma distinção entre a própria árvore e a neve que reflete sua luz. Aqui tentamos excluir a neve aplicando um filtro realmente simples nos códigos de cores:
Então encontramos todos os pixels "brilhantes":
Finalmente, juntamos os dois resultados:
Agora, procuramos o maior objeto brilhante:
Agora estamos quase terminando, mas ainda há alguma imperfeição devido à neve. Para cortá-los, criaremos uma máscara usando um círculo e um retângulo para aproximar a forma de uma árvore e excluir partes indesejadas:
O último passo é encontrar o contorno da nossa árvore e desenhá-lo na figura original.
Sinto muito, mas no momento tenho uma conexão ruim, portanto não é possível fazer upload de fotos. Vou tentar fazer isso mais tarde.
Feliz Natal.
EDITAR:
Aqui estão algumas fotos da saída final:
fonte
./christmas_tree ./*.png
. Eles podem ser quantos você quiser, os resultados serão mostrados um após o outro pressionando qualquer tecla. Isso está errado?<img src="http://i.stack.imgur.com/nmzwj.png" width="210" height="150">
Basta alterar o link para a imagem;) #Eu escrevi o código no Matlab R2007a. Eu usei k-means para extrair aproximadamente a árvore de natal. Mostrarei meu resultado intermediário apenas com uma imagem e os resultados finais com todas as seis.
Primeiro, mapeei o espaço RGB no espaço Lab, o que poderia aumentar o contraste do vermelho em seu canal b:
Além do recurso no espaço de cores, também usei o recurso de textura relevante para a vizinhança e não para cada pixel. Aqui eu combinei linearmente a intensidade dos 3 canais originais (R, G, B). A razão pela qual eu formatei dessa maneira é porque as árvores de natal na imagem têm luzes vermelhas e, às vezes, iluminação verde / às vezes azul.
Apliquei um padrão binário local 3X3
I0
, usei o pixel central como limite e obtive o contraste calculando a diferença entre o valor médio da intensidade do pixel acima do limite e o valor médio abaixo dele.Como tenho 4 recursos no total, eu escolheria K = 5 no meu método de clustering. O código para k-means é mostrado abaixo (é do curso de aprendizado de máquina do Dr. Andrew Ng. Eu fiz o curso antes e escrevi o código em sua tarefa de programação).
Como o programa está muito lento no meu computador, eu apenas executei 3 iterações. Normalmente, o critério de parada é (i) tempo de iteração de pelo menos 10 ou (ii) nenhuma alteração nos centróides mais. Para o meu teste, aumentar a iteração pode diferenciar o plano de fundo (céu e árvore, céu e construção, ...) com mais precisão, mas não mostrou mudanças drásticas na extração da árvore de Natal. Observe também que o k-means não é imune à inicialização aleatória do centróide, portanto, é recomendável executar o programa várias vezes para fazer uma comparação.
Após as médias k,
I0
foi escolhida a região marcada com a intensidade máxima de . E o rastreamento de limites foi usado para extrair os limites. Para mim, a última árvore de natal é a mais difícil de extrair, já que o contraste nessa imagem não é alto o suficiente, pois são nos cinco primeiros. Outro problema no meu método é que eu usei abwboundaries
função no Matlab para rastrear os limites, mas às vezes os limites internos também são incluídos, como você pode observar nos resultados da 3ª, 5ª e 6ª. O lado escuro dentro das árvores de Natal não apenas deixa de ser agrupado com o lado iluminado, mas também leva a muitos pequenos traçados de limites internos (imfill
não melhora muito). Em todo o meu algoritmo ainda tem muito espaço para melhorias.Algumas publicações s indicam que o deslocamento médio pode ser mais robusto do que o k-means e muitos algoritmos baseados em corte de gráfico também são muito competitivos na segmentação de limites complicada. Eu mesmo escrevi um algoritmo de mudança de média, parece extrair melhor as regiões sem luz suficiente. Mas a mudança na média é um pouco exagerada e é necessária alguma estratégia de fusão. Funcionou ainda mais devagar do que o k-means no meu computador, tenho medo de desistir. Estou ansioso para ver que outros apresentariam excelentes resultados aqui com os algoritmos modernos mencionados acima.
No entanto, eu sempre acredito que a seleção de recursos é o componente principal na segmentação de imagens. Com uma seleção adequada de recursos que pode maximizar a margem entre o objeto e o plano de fundo, muitos algoritmos de segmentação definitivamente funcionarão. Algoritmos diferentes podem melhorar o resultado de 1 para 10, mas a seleção de recursos pode melhorar de 0 para 1.
Feliz Natal !
fonte
Este é o meu post final usando as abordagens tradicionais de processamento de imagem ...
Aqui, de alguma forma, combino minhas duas outras propostas, obtendo resultados ainda melhores . De fato, não consigo ver como esses resultados poderiam ser melhores (especialmente quando você olha para as imagens mascaradas que o método produz).
No centro da abordagem está a combinação de três premissas principais :
Com essas suposições em mente, o método funciona da seguinte maneira:
Aqui está o código no MATLAB (novamente, o script carrega todas as imagens jpg na pasta atual e, novamente, isso está longe de ser um trecho de código otimizado):
Resultados
Resultados de alta resolução ainda disponíveis aqui!
Ainda mais experimentos com imagens adicionais podem ser encontrados aqui.
fonte
Minhas etapas da solução:
Obter canal R (de RGB) - todas as operações que fazemos neste canal:
Criar região de interesse (ROI)
Canal R de limite com valor mínimo 149 (imagem superior direita)
Região de resultado dilatada (imagem do meio à esquerda)
Detecte eges no roi computado. A árvore tem muitas arestas (imagem no meio à direita)
Resultado dilatado
Corroer com raio maior (imagem inferior esquerda)
Selecione o maior objeto (por área) - é a região de resultado
ConvexHull (árvore é polígono convexo) (imagem inferior direita)
Caixa delimitadora (imagem inferior direita - caixa grren)
Passo a passo:
O primeiro resultado - mais simples, mas não no software de código aberto - "Adaptive Vision Studio + Adaptive Vision Library": esse não é um código aberto, mas é muito rápido para prototipar:
Algoritmo inteiro para detectar a árvore de natal (11 blocos):
Próxima Etapa. Queremos uma solução de código aberto. Altere os filtros AVL para os filtros OpenCV: Aqui, fiz pequenas alterações, por exemplo, a detecção de borda usa o filtro cvCanny, para respeitar o roi, multipliquei a imagem da região pela imagem das bordas, para selecionar o maior elemento que usei findContours + contourArea, mas a idéia é a mesma.
https://www.youtube.com/watch?v=sfjB3MigLH0&index=1&list=UUpSRrkMHNHiLDXgylwhWNQQ
Não posso mostrar imagens com etapas intermediárias agora porque posso colocar apenas 2 links.
Ok, agora usamos filtros de código aberto, mas ainda não é todo o código aberto. Última etapa - porta para o código c ++. Eu usei o OpenCV na versão 2.4.4
O resultado do código c ++ final é:
O código c ++ também é bastante curto:
fonte
std::max_element()
ligação? Gostaria de recompensar sua resposta também. Eu acho que tenho o gcc 4.2.... outra solução antiquada - puramente baseada no processamento HSV :
Uma palavra sobre as heurísticas no processamento HSV:
É claro que se pode experimentar inúmeras outras possibilidades para ajustar essa abordagem ...
Aqui está o código do MATLAB para executar o truque (aviso: o código está longe de ser otimizado !!! Eu usei técnicas não recomendadas para a programação do MATLAB apenas para poder rastrear qualquer coisa no processo - isso pode ser bastante otimizado):
Resultados:
Nos resultados, mostro a imagem mascarada e a caixa delimitadora.
fonte
Alguma abordagem antiquada de processamento de imagem ...
A idéia baseia-se no pressuposto de que as imagens mostram árvores iluminadas em fundos tipicamente mais escuros e suaves (ou em primeiro plano em alguns casos). A área da árvore iluminada é mais "energética" e tem maior intensidade .
O processo é como se segue:
O que você obtém é uma máscara binária e uma caixa delimitadora para cada imagem.
Aqui estão os resultados usando esta técnica ingênua:
O código no MATLAB é o seguinte: O código é executado em uma pasta com imagens JPG. Carrega todas as imagens e retorna os resultados detectados.
fonte
Usando uma abordagem bem diferente da que eu já vi, criei um phpscript que detecta árvores de natal por suas luzes. O resultado é sempre um triângulo simétrico e, se necessário, valores numéricos como o ângulo ("gordura") da árvore.
A maior ameaça a esse algoritmo obviamente são as luzes próximas (em grandes números) ou na frente da árvore (o maior problema até uma otimização adicional). Editar (adicionado): o que não pode ser feito: descubra se há uma árvore de Natal ou não, encontre várias árvores de Natal em uma imagem, detecte corretamente uma árvore de Natal no meio de Las Vegas, detecte árvores de Natal muito dobradas, de cabeça para baixo ou picado ...;)
As diferentes etapas são:
Explicação das marcações:
Código fonte:
Imagens:
Bônus: um alemão Weihnachtsbaum, da Wikipedia http://commons.wikimedia.org/wiki/File:Weihnachtsbaum_R%C3%B6merberg.jpg
fonte
Eu usei python com opencv.
Meu algoritmo é assim:
O código:
Se eu mudar o kernel de (25,5) para (10,5), obtive melhores resultados em todas as árvores, exceto no canto inferior esquerdo,
meu algoritmo assume que a árvore tem luzes e, na árvore inferior esquerda, o topo tem menos luz que os outros.
fonte