¿Qué técnicas de procesamiento de imágenes podrían usarse para implementar una aplicación que detecte los árboles de Navidad que se muestran en las siguientes imágenes?
Estoy buscando soluciones que funcionen en todas estas imágenes. Por lo tanto, los enfoques que requieren capacitación en clasificadores de cascada de haar o coincidencia de plantillas no son muy interesantes.
Estoy buscando algo que pueda escribirse en cualquier lenguaje de programación, siempre que use solo tecnologías de código abierto . La solución debe probarse con las imágenes que se comparten en esta pregunta. Hay 6 imágenes de entrada y la respuesta debe mostrar los resultados del procesamiento de cada una de ellas. Finalmente, para cada imagen de salida debe haber líneas rojas dibujadas para rodear el árbol detectado.
¿Cómo haría para detectar mediante programación los árboles en estas imágenes?
fuente
Respuestas:
Tengo un enfoque que creo que es interesante y un poco diferente del resto. La principal diferencia en mi enfoque, en comparación con algunos de los otros, está en cómo se realiza el paso de segmentación de imagen: utilicé el algoritmo de agrupación DBSCAN del scikit-learn de Python; Está optimizado para encontrar formas algo amorfas que no necesariamente tienen un solo centroide claro.
En el nivel superior, mi enfoque es bastante simple y se puede dividir en unos 3 pasos. Primero aplico un umbral (o en realidad, el "o" lógico de dos umbrales separados y distintos). Al igual que con muchas de las otras respuestas, supuse que el árbol de Navidad sería uno de los objetos más brillantes de la escena, por lo que el primer umbral es solo una simple prueba de brillo monocromático; todos los píxeles con valores superiores a 220 en una escala de 0-255 (donde el negro es 0 y el blanco 255) se guardan en una imagen binaria en blanco y negro. El segundo umbral intenta buscar luces rojas y amarillas, que son particularmente prominentes en los árboles en la parte superior izquierda e inferior derecha de las seis imágenes, y se destacan bien contra el fondo azul-verde que prevalece en la mayoría de las fotos. Convierto la imagen rgb a espacio hsv, y requieren que el tono sea menor que 0.2 en una escala de 0.0-1.0 (que corresponde aproximadamente al borde entre amarillo y verde) o mayor que 0.95 (que corresponde al borde entre púrpura y rojo) y además requiero colores brillantes y saturados: la saturación y el valor deben estar por encima de 0.7. Los resultados de los dos procedimientos de umbral se combinan lógicamente "o", y la matriz resultante de imágenes binarias en blanco y negro se muestra a continuación:
Puede ver claramente que cada imagen tiene un gran grupo de píxeles que corresponde aproximadamente a la ubicación de cada árbol, además de algunas de las imágenes también tienen algunos otros pequeños grupos correspondientes a las luces en las ventanas de algunos de los edificios o a un Escena de fondo en el horizonte. El siguiente paso es lograr que la computadora reconozca que se trata de grupos separados y etiquetar cada píxel correctamente con un número de identificación de miembro del grupo.
Para esta tarea elegí DBSCAN . Hay una comparación visual bastante buena de cómo DBSCAN se comporta típicamente, en relación con otros algoritmos de agrupamiento, disponibles aquí . Como dije antes, le va bien con formas amorfas. La salida de DBSCAN, con cada grupo trazado en un color diferente, se muestra aquí:
Hay algunas cosas a tener en cuenta al mirar este resultado. Primero es que DBSCAN requiere que el usuario establezca un parámetro de "proximidad" para regular su comportamiento, que controla efectivamente qué tan separados deben estar un par de puntos para que el algoritmo declare un nuevo grupo separado en lugar de aglomerar un punto de prueba en un clúster ya preexistente. Establezco este valor en 0.04 veces el tamaño a lo largo de la diagonal de cada imagen. Dado que las imágenes varían en tamaño desde aproximadamente VGA hasta aproximadamente HD 1080, este tipo de definición relativa a la escala es crítica.
Otro punto que vale la pena señalar es que el algoritmo DBSCAN, tal como se implementa en scikit-learn, tiene límites de memoria que son bastante desafiantes para algunas de las imágenes más grandes de esta muestra. Por lo tanto, para algunas de las imágenes más grandes, en realidad tuve que "diezmar" (es decir, retener solo cada 3er o 4to píxel y soltar los demás) cada grupo para permanecer dentro de este límite. Como resultado de este proceso de selección, los píxeles dispersos individuales restantes son difíciles de ver en algunas de las imágenes más grandes. Por lo tanto, solo para fines de visualización, los píxeles codificados por colores en las imágenes anteriores se han "dilatado" de manera efectiva solo ligeramente para que se destaquen mejor. Es puramente una operación cosmética por el bien de la narrativa; aunque hay comentarios que mencionan esta dilatación en mi código,
Una vez que los grupos están identificados y etiquetados, el tercer y último paso es fácil: simplemente tomo el grupo más grande de cada imagen (en este caso, elegí medir el "tamaño" en términos del número total de píxeles de miembros, aunque uno podría en su lugar, he utilizado con facilidad algún tipo de métrica que mide la extensión física) y calcula el casco convexo para ese grupo. El casco convexo se convierte en el borde del árbol. Los seis cascos convexos calculados mediante este método se muestran a continuación en rojo:
El código fuente está escrito para Python 2.7.6 y depende de numpy , scipy , matplotlib y scikit-learn . Lo he dividido en dos partes. La primera parte es responsable del procesamiento real de la imagen:
y la segunda parte es un script de nivel de usuario que llama al primer archivo y genera todos los gráficos anteriores:
fuente
scipy.ndimage.filters.maximum_filter()
en el mismo lugar donde había usado un umbral.NOTA DE EDICIÓN: edité esta publicación para (i) procesar cada imagen de árbol individualmente, según lo solicitado en los requisitos, (ii) para considerar tanto el brillo como la forma del objeto para mejorar la calidad del resultado.
A continuación se presenta un enfoque que toma en consideración el brillo y la forma del objeto. En otras palabras, busca objetos con forma de triángulo y con un brillo significativo. Fue implementado en Java, utilizando el marco de procesamiento de imágenes Marvin .
El primer paso es el umbral de color. El objetivo aquí es enfocar el análisis en objetos con brillo significativo.
imágenes de salida:
código fuente:
En el segundo paso, los puntos más brillantes de la imagen se dilatan para formar formas. El resultado de este proceso es la forma probable de los objetos con brillo significativo. Aplicando segmentación de relleno de inundación, se detectan formas desconectadas.
imágenes de salida:
código fuente:
Como se muestra en la imagen de salida, se detectaron múltiples formas. En este problema, solo hay algunos puntos brillantes en las imágenes. Sin embargo, este enfoque se implementó para lidiar con escenarios más complejos.
En el siguiente paso se analiza cada forma. Un algoritmo simple detecta formas con un patrón similar a un triángulo. El algoritmo analiza la forma del objeto línea por línea. Si el centro de la masa de cada línea de forma es casi la misma (dado un umbral) y la masa aumenta a medida que aumenta, el objeto tiene una forma de triángulo. La masa de la línea de forma es el número de píxeles en esa línea que pertenece a la forma. Imagine que corta el objeto horizontalmente y analiza cada segmento horizontal. Si están centralizados entre sí y la longitud aumenta desde el primer segmento hasta el último en un patrón lineal, es probable que tenga un objeto que se asemeje a un triángulo.
código fuente:
Finalmente, la posición de cada forma similar a un triángulo y con un brillo significativo, en este caso un árbol de Navidad, se resalta en la imagen original, como se muestra a continuación.
imágenes de salida final:
código fuente final:
La ventaja de este enfoque es el hecho de que probablemente funcionará con imágenes que contienen otros objetos luminosos, ya que analiza la forma del objeto.
¡Feliz Navidad!
EDITAR NOTA 2
Hay una discusión sobre la similitud de las imágenes de salida de esta solución y algunas otras. De hecho, son muy similares. Pero este enfoque no solo segmenta objetos. También analiza las formas de los objetos en cierto sentido. Puede manejar múltiples objetos luminosos en la misma escena. De hecho, el árbol de Navidad no necesita ser el más brillante. Solo lo escribo para enriquecer la discusión. Hay un sesgo en las muestras que solo buscando el objeto más brillante, encontrará los árboles. Pero, ¿realmente queremos detener la discusión en este punto? En este punto, ¿hasta qué punto la computadora realmente reconoce un objeto que se parece a un árbol de Navidad? Intentemos cerrar esta brecha.
A continuación se presenta un resultado solo para dilucidar este punto:
imagen de entrada
salida
fuente
Aquí está mi solución simple y tonta. Se basa en la suposición de que el árbol será la cosa más brillante y más grande de la imagen.
El primer paso es detectar los píxeles más brillantes en la imagen, pero tenemos que hacer una distinción entre el árbol en sí y la nieve que refleja su luz. Aquí intentamos excluir la nieve aplicando un filtro realmente simple en los códigos de color:
Luego encontramos cada píxel "brillante":
Finalmente unimos los dos resultados:
Ahora buscamos el objeto brillante más grande:
Ahora casi hemos terminado, pero todavía hay algunas imperfecciones debido a la nieve. Para cortarlos, construiremos una máscara usando un círculo y un rectángulo para aproximar la forma de un árbol para eliminar piezas no deseadas:
El último paso es encontrar el contorno de nuestro árbol y dibujarlo en la imagen original.
Lo siento, pero por el momento tengo una mala conexión, así que no me es posible subir fotos. Intentaré hacerlo más tarde.
Feliz Navidad.
EDITAR:
Aquí algunas fotos de la salida final:
fuente
./christmas_tree ./*.png
. Pueden ser tantos como desee, los resultados se mostrarán uno tras otro presionando cualquier tecla. ¿Esto esta mal?<img src="http://i.stack.imgur.com/nmzwj.png" width="210" height="150">
simplemente cambie el enlace a la imagen;)Escribí el código en Matlab R2007a. Usé k-means para extraer aproximadamente el árbol de navidad. Mostraré mi resultado intermedio solo con una imagen, y los resultados finales con las seis.
Primero, asigné el espacio RGB al espacio Lab, lo que podría mejorar el contraste del rojo en su canal b:
Además de la característica en el espacio de color, también utilicé la característica de textura que es relevante con el vecindario en lugar de cada píxel en sí. Aquí combiné linealmente la intensidad de los 3 canales originales (R, G, B). La razón por la que he formateado de esta manera es porque todos los árboles de navidad en la imagen tienen luces rojas y, a veces, también iluminación verde / azul.
Apliqué un patrón binario local 3X3
I0
, utilicé el píxel central como umbral y obtuve el contraste calculando la diferencia entre el valor medio de intensidad de píxel por encima del umbral y el valor medio por debajo de él.Como tengo 4 funciones en total, elegiría K = 5 en mi método de agrupación. El código para k-means se muestra a continuación (es del curso de aprendizaje automático del Dr. Andrew Ng. Tomé el curso antes y escribí el código yo mismo en su tarea de programación).
Como el programa se ejecuta muy lento en mi computadora, solo ejecuté 3 iteraciones. Normalmente, el criterio de detención es (i) tiempo de iteración de al menos 10, o (ii) ya no hay cambio en los centroides. Para mi prueba, aumentar la iteración puede diferenciar el fondo (cielo y árbol, cielo y edificio, ...) con mayor precisión, pero no mostró cambios drásticos en la extracción del árbol de navidad. También tenga en cuenta que k-means no es inmune a la inicialización del centroide aleatorio, por lo que se recomienda ejecutar el programa varias veces para hacer una comparación.
Después del medio k,
I0
se eligió la región marcada con la intensidad máxima de . Y el trazado de límites se utilizó para extraer los límites. Para mí, el último árbol de Navidad es el más difícil de extraer, ya que el contraste en esa imagen no es lo suficientemente alto como en los primeros cinco. Otro problema en mi método es que utilicé labwboundaries
función en Matlab para trazar el límite, pero a veces los límites internos también se incluyen como se puede observar en los resultados tercero, quinto y sexto. El lado oscuro dentro de los árboles de navidad no solo no se puede agrupar con el lado iluminado, sino que también conduce a tantos pequeños trazados de límites internos (imfill
no mejora mucho). En general, mi algoritmo todavía tiene mucho espacio de mejora.Algunos publicaciones indican que el cambio medio puede ser más robusto que el medio k, y muchos algoritmos basados en corte de gráficos también son muy competitivos en la segmentación de límites complicada. Escribí un algoritmo de cambio medio yo mismo, parece extraer mejor las regiones sin suficiente luz. Pero el cambio medio está un poco sobre segmentado, y se necesita alguna estrategia de fusión. Funcionó mucho más lento que k-means en mi computadora, me temo que tengo que renunciar. Espero ansiosamente ver que otros presenten excelentes resultados aquí con los algoritmos modernos mencionados anteriormente.
Sin embargo, siempre creo que la selección de características es el componente clave en la segmentación de imágenes. Con una selección de funciones adecuada que puede maximizar el margen entre el objeto y el fondo, muchos algoritmos de segmentación definitivamente funcionarán. Diferentes algoritmos pueden mejorar el resultado de 1 a 10, pero la selección de características puede mejorarlo de 0 a 1.
Feliz Navidad !
fuente
Esta es mi publicación final usando los enfoques tradicionales de procesamiento de imágenes ...
Aquí de alguna manera combino mis otras dos propuestas, logrando resultados aún mejores . De hecho, no puedo ver cómo estos resultados podrían ser mejores (especialmente cuando miras las imágenes enmascaradas que produce el método).
La esencia del enfoque es la combinación de tres supuestos clave :
Con estos supuestos en mente, el método funciona de la siguiente manera:
Aquí está el código en MATLAB (nuevamente, el script carga todas las imágenes jpg en la carpeta actual y, nuevamente, esto está lejos de ser un código optimizado):
Resultados
¡Los resultados de alta resolución aún están disponibles aquí!
Incluso más experimentos con imágenes adicionales se pueden encontrar aquí.
fuente
Mis pasos de solución:
Obtener canal R (de RGB): todas las operaciones que realizamos en este canal:
Crear región de interés (ROI)
Umbral del canal R con valor mínimo 149 (imagen superior derecha)
Región de resultado de dilatación (imagen del centro a la izquierda)
Detectar eges en roi calculado. El árbol tiene muchos bordes (imagen central derecha)
Resultado dilatar
Erosión con mayor radio (imagen inferior izquierda)
Seleccione el objeto más grande (por área): es la región resultante
Casco convexo (el árbol es un polígono convexo) (imagen inferior derecha)
Cuadro delimitador (imagen inferior derecha - cuadro grren)
Paso a paso:
El primer resultado, el más simple pero no en el software de código abierto, "Adaptive Vision Studio + Adaptive Vision Library": este no es de código abierto, pero es muy rápido para crear un prototipo:
Algoritmo completo para detectar el árbol de navidad (11 bloques):
Próximo paso. Queremos una solución de código abierto. Cambie los filtros AVL a filtros OpenCV: aquí hice pequeños cambios, p. Ej., La detección de bordes usó el filtro cvCanny, para respetar el aspecto real, multipliqué la imagen de región con la imagen de bordes, para seleccionar el elemento más grande que utilicé findContours + contourArea pero la idea es la misma.
https://www.youtube.com/watch?v=sfjB3MigLH0&index=1&list=UUpSRrkMHNHiLDXgylwhWNQQ
Ahora no puedo mostrar imágenes con pasos intermedios porque solo puedo poner 2 enlaces.
Ok, ahora usamos filtros openSource pero aún no es de código abierto completo. Último paso: puerto a código c ++. Usé OpenCV en la versión 2.4.4
El resultado del código final de c ++ es:
El código c ++ también es bastante corto:
fuente
std::max_element()
llamada? Me gustaría recompensar su respuesta también. Creo que tengo gcc 4.2.... otra solución pasada de moda, basada exclusivamente en el procesamiento HSV :
Una palabra sobre la heurística en el procesamiento de HSV:
Por supuesto, uno puede experimentar con muchas otras posibilidades para afinar este enfoque ...
Aquí está el código de MATLAB para hacer el truco (advertencia: ¡el código está lejos de ser optimizado! Utilicé técnicas no recomendadas para la programación de MATLAB solo para poder rastrear cualquier cosa en el proceso; esto puede optimizarse enormemente):
Resultados:
En los resultados muestro la imagen enmascarada y el cuadro delimitador.
fuente
Algún enfoque de procesamiento de imágenes a la antigua usanza ...
La idea se basa en la suposición de que las imágenes representan árboles iluminados en fondos típicamente más oscuros y suaves (o en primer plano en algunos casos). El área del árbol iluminado es más "enérgica" y tiene mayor intensidad .
El proceso es el siguiente:
Lo que obtienes es una máscara binaria y un cuadro delimitador para cada imagen.
Aquí están los resultados usando esta técnica ingenua:
El código en MATLAB sigue: El código se ejecuta en una carpeta con imágenes JPG. Carga todas las imágenes y devuelve los resultados detectados.
fuente
Usando un enfoque bastante diferente de lo que he visto, creé un phpguión que detecta los árboles de navidad por sus luces. El resultado es siempre un triángulo simétrico y, si es necesario, valores numéricos como el ángulo ("gordura") del árbol.
La mayor amenaza para este algoritmo obviamente son las luces al lado (en grandes cantidades) o frente al árbol (el mayor problema hasta una mayor optimización). Editar (agregado): Lo que no puede hacer: averiguar si hay un árbol de Navidad o no, encontrar varios árboles de Navidad en una imagen, detectar correctamente un árbol de Navidad en el medio de Las Vegas, detectar árboles de Navidad que estén muy doblados, al revés o picado ...;)
Las diferentes etapas son:
Explicación de las marcas:
Código fuente:
Imágenes:
Bono: un alemán Weihnachtsbaum, de Wikipedia http://commons.wikimedia.org/wiki/File:Weihnachtsbaum_R%C3%B6merberg.jpg
fuente
Usé python con opencv.
Mi algoritmo es así:
El código:
Si cambio el núcleo de (25,5) a (10,5) obtengo mejores resultados en todos los árboles, excepto en la parte inferior izquierda,
mi algoritmo asume que el árbol tiene luces, y en el árbol inferior izquierdo, la parte superior tiene menos luz que las demás.
fuente