MASK-RCNN para encontrar techos a partir de imágenes de drones


En una ciudad blanca y blanca en una calle blanca y blanca había casas blancas y blancas ... ¿Y qué tan rápido puedes encontrar todos los techos de las casas en esta foto?

Cada vez más, uno puede escuchar acerca de los planes del gobierno para llevar a cabo un inventario completo de bienes raíces con el fin de aclarar los datos catastrales. Para la solución principal a este problema, se puede aplicar un método simple basado en el cálculo del área del techo de los edificios capitales a partir de fotografías aéreas y una comparación adicional con los datos catastrales. Desafortunadamente, la búsqueda y el cálculo manuales requieren mucho tiempo, y dado que las casas nuevas se demuelen y construyen continuamente, el cálculo debe repetirse una y otra vez. La hipótesis surge inmediatamente de que este proceso puede automatizarse utilizando algoritmos de aprendizaje automático, en particular, la visión por computadora. En este artículo hablaré sobre cómo estamos en NORBIT resolvió este problema y las dificultades que encontraron.

Spoiler: lo hicimos . El servicio ML desarrollado se basa en un modelo de aprendizaje automático profundo basado en redes neuronales de convolución. El servicio acepta imágenes de vehículos aéreos no tripulados como entrada; en la salida, genera un archivo GeoJSON con el marcado de objetos de construcción de capital encontrados con referencia a coordenadas geográficas.

Como resultado, se ve así:


Problemas


Comencemos con los problemas técnicos que encontramos:

  • existe una diferencia significativa entre las fotografías aéreas de invierno y verano (un modelo entrenado solo en fotografías de verano es completamente incapaz de encontrar techos en invierno);
  • , ;
  • , ( ), ( ) , ;
  • , , ( ). ;
  • (, ) .

Y los drones a veces traen estas fotos:


También me gustaría señalar los problemas que podrían haber sido, pero no nos preocuparon:

  • no tuvimos la tarea de realizar inferencia durante un tiempo limitado (por ejemplo, directamente en el momento del vuelo), lo que resolvió de inmediato todos los posibles problemas con el rendimiento;
  • En la entrada para el procesamiento, recibimos de inmediato imágenes de alta resolución y alta calidad (usando lentes con una longitud focal de 21 mm a una altura de 250 m, que es 5 cm / px) de nuestro cliente, la compañía Shakhty, podría usar su experiencia en geolocalización de objetos en mapas, y También tuvieron la oportunidad de establecer un conjunto específico de requisitos para futuros vuelos de vehículos aéreos no tripulados, que en última instancia redujo en gran medida la probabilidad de fichas muy únicas que no estaban en el conjunto de entrenamiento;

La primera solución al problema, trazo usando el cuadro Límite 


Algunas palabras sobre las herramientas que utilizamos para crear la solución.

  • Anaconda es un conveniente sistema de gestión de paquetes para Python y R.
  • Tensorflow es una biblioteca de software de aprendizaje automático de código abierto desarrollada por Google.
  • Keras es un complemento para los marcos Deeplearning4j, TensorFlow y Theano.
  • OpenCV es una biblioteca de algoritmos de visión por computadora, procesamiento de imágenes y algoritmos numéricos de código abierto de uso general.
  • Flask es un marco para crear aplicaciones web en el lenguaje de programación Python.

Como el sistema operativo utilizaba Ubuntu 18.04. Con los controladores en la GPU (NVIDIA) en Ubuntu, todo está en orden, por lo que la tarea generalmente se resuelve con un comando:

> sudo apt install nvidia-cuda-toolkit

Preparación de azulejos


La primera tarea que enfrentamos fue dividir las imágenes de sobrevuelo en mosaicos (2048x2048 px). Podrías escribir tu propio guión, pero luego tendrías que pensar en mantener la ubicación geográfica de cada mosaico. Era más fácil usar una solución preparada, por ejemplo, GeoServer: es un software de código abierto que le permite publicar geodatos en el servidor. Además, GeoServer resolvió otro problema para nosotros: la visualización conveniente del resultado del marcado automático en el mapa. Esto se puede hacer localmente, por ejemplo, en qGIS, pero para un comando distribuido y una demostración, un recurso web es más conveniente.

Para realizar el mosaico, debe especificar la escala y el tamaño necesarios en la configuración.


Para las traducciones entre sistemas de coordenadas, utilizamos la biblioteca pyproj:

from pyproj import Proj, transform

class Converter:
    P3857 = Proj(init='epsg:3857')
    P4326 = Proj(init='epsg:4326')
...
    def from_3857_to_GPS(self, point):
        x, y = point
        return transform(self.P3857, self.P4326, x, y)
    def from_GPS_to_3857(self, point):
        x, y = point
        return transform(self.P4326, self.P3857, x, y)
...

Como resultado, fue posible formar fácilmente una capa grande de todos los polígonos y colocarla sobre el sustrato. 


Para instalar el software GeoServer, debe completar los siguientes pasos.
  1. Java 8.
  2. GeoServer.
  3. , , /usr/share/geoserver
  4.  

    echo «export GEOSERVER_HOME=/usr/share/geoserver» >> ~/.profile
  5. :

    sudo groupadd geoserver
  6. , , :

    sudo usermod -a -G geoserver <user_name>
  7. - :

    sudo chown -R :geoserver /usr/share/geoserver/
  8. :

    sudo chmod -R g+rwx /usr/share/geoserver/
  9. GeoServer 

    cd geoserver/bin && sh startup.sh

GeoServer no es la única aplicación que nos permite resolver nuestro problema. Como alternativa, por ejemplo, puede considerar ArcGIS for Server, pero este producto es propietario, por lo que no lo usamos.

Luego, cada teja tenía que encontrar todos los techos visibles. El primer enfoque para resolver el problema fue utilizar la detección de objetos del conjunto de modelos / investigación Tensorflow. De esta forma, las clases de imágenes se pueden encontrar y localizar con una selección rectangular (cuadro de límite). 

Marcado de datos de entrenamiento 


Obviamente, para entrenar el modelo necesita un conjunto de datos etiquetado. Por una coincidencia afortunada, además de dar vueltas, en nuestros contenedores, el conjunto de datos para 50 mil techos se conservó de los viejos tiempos, cuando todos los conjuntos de datos para capacitación todavía estaban en el dominio público en todas partes.

El tamaño exacto de la muestra de entrenamiento requerida para obtener una precisión aceptable del modelo es bastante difícil de predecir de antemano. Puede variar según la calidad de las imágenes, su grado de diferencia entre sí y las condiciones en que se utilizará el modelo en la producción. Tuvimos casos en los que 200 piezas eran suficientes y, a veces, también faltaban 50 mil muestras marcadas. En caso de escasez de imágenes marcadas, generalmente agregamos métodos de aumento: giros, reflejos de espejo, gradación de color, etc.

Ahora hay muchos servicios disponibles que le permiten marcar imágenes, tanto con código fuente abierto para la instalación en su computadora / servidor como con soluciones corporativas, incluido el trabajo de asesores externos, por ejemplo Yandex.Toloka. En este proyecto, utilizamos el Anotador de imagen VGG más simple . Alternativamente, puedes probar coco-annotator o label-studio . Usualmente usamos este último para marcar archivos de texto y audio.


Para la capacitación sobre el marcado de varios anotadores, generalmente debe realizar un pequeño cambio de campo, un ejemplo para VGG .

Para calcular correctamente el área del techo que cayó en el área de asignación rectangular, es necesario observar varias condiciones:

  • / . :


  • , , :


Para resolver el segundo problema, puede intentar entrenar un modelo separado que determine el ángulo de rotación correcto de la baldosa para marcar, pero todo resultó un poco más fácil. Las personas se esfuerzan por reducir la entropía, por lo que alinean todas las estructuras hechas por el hombre en relación entre sí, especialmente con edificios densos. Si mira desde arriba, entonces en un área localizada, las cercas, los pasillos, la plantación, los invernaderos, los cenadores serán paralelos o perpendiculares a los límites de los techos. Solo queda encontrar todas las líneas claras y calcular el ángulo de inclinación más común a la vertical. Para esto, OpenCV tiene una gran herramienta HoughLinesP. 

...

lines = cv2.HoughLinesP(edges, 1, np.pi/180, 50, minLineLength=minLineLength, maxLineGap=5)
if lines is not None:
    length = image.shape[0]
    angles = []
    for x1, y1, x2, y2 in lines[0]:
        angle = math.degrees(math.atan2(y2 — y1, x2 - x1))
        angles.append(angle)
    parts_angles.append(angles)
    median_angle = np.median(angles)
...

#     

for x in range(0, image.shape[0]-1, image.shape[0] // count_crops):
    for y in range(0, image.shape[1]-1, image.shape[1] // count_crops):
        get_line(image[x:x+image.shape[0]//count_crops, y:y+image.shape[1]//count_crops, :])
...

#      

np.median([a if a>0 else 90+a for a in np.array(parts_angles).flatten()])

Después de encontrar el ángulo, rotamos la imagen usando la transformación afín:


h, w = image.shape[:2]
image_center = (w/2, h/2)

if size is None:
    radians = math.radians(angle)
    sin = math.sin(radians)
    cos = math.cos(radians)
    size = (int((h * abs(sin)) + (w * abs(cos))), int((h * abs(cos)) + (w * abs(sin))))
    rotation_matrix = cv2.getRotationMatrix2D(image_center, angle, 1)
    rotation_matrix[0, 2] += ((size[0] / 2) — image_center[0])
    rotation_matrix[1, 2] += ((size[1] / 2) — image_center[1])
else:
    rotation_matrix = cv2.getRotationMatrix2D(image_center, angle, 1)

cv2.warpAffine(image, rotation_matrix, size)

El código de ejemplo completo está aquí . Así es como se ve:



El método de torneado de tejas y marcado con rectángulos funciona más rápido que el marcado con máscaras, se encuentran casi todos los techos, pero en la producción este método se usa solo como auxiliar debido a varios inconvenientes:

  • hay muchos sobrevuelos donde hay una gran cantidad de techos no rectangulares, debido a esto hay demasiado trabajo manual para refinar el área,
  • a veces se encuentra en casa con diferentes orientaciones en el mismo mosaico,
  • a veces hay muchas líneas falsas en las fichas, lo que finalmente conduce a un giro equivocado. Se parece a esto:



La solución final basada en Mask-RCNN


El segundo intento fue buscar y resaltar los tejados por máscaras píxel por píxel, y luego delinear automáticamente los contornos de las máscaras encontradas y crear polígonos vectoriales.  

Ya hay suficientes materiales sobre los principios de operación, tipos y tareas de las redes neuronales convolucionales, incluidas las de Rusia, por lo que no los abordaremos en este artículo. Detengámonos solo en una implementación específica, Mask-RCNN, una arquitectura para localizar y resaltar los contornos de los objetos en las imágenes. Existen otras soluciones excelentes con sus ventajas y desventajas, por ejemplo, UNet, pero fue posible lograr una mejor calidad en Mask-RCNN.

En el proceso de su desarrollo, pasó por varias etapas. La primera versión de R-CNN se desarrolló en 2014. El principio de su trabajo es resaltar pequeñas áreas en la imagen, para cada una de las cuales se realiza una estimación de la probabilidad de la presencia de un objeto objetivo en esta área. R-CNN hizo un excelente trabajo con la tarea, pero su velocidad dejó mucho que desear. El desarrollo lógico fue las redes Fast R-CNN y Faster R-CNN, que recibieron mejoras en el algoritmo de rastreo de imágenes, lo que permitió aumentar significativamente la velocidad. A la salida de Faster R-CNN, aparece una marca con una selección rectangular que indica los límites del objeto, lo que no siempre es suficiente para resolver el problema. 

Máscara R-CNN también agrega una superposición de máscara píxel por píxel para obtener el contorno exacto del objeto.

El cuadro de límite y las máscaras se pueden ver claramente en el resultado de la operación del modelo (el filtro por el área mínima de construcción está activado):


Convencionalmente, hay 4 etapas en la operación de esta red:

  • estándar para todas las redes neuronales convolucionales, la asignación de características en la imagen, como líneas, curvas, límites contrastantes y otros;
  • La Red de Propuesta de Región (RPN) escanea pequeños fragmentos de la imagen, llamados anclajes (anclajes) y determina si este ancla contiene signos específicos de la clase objetivo (en nuestro caso, el techo);
  • Región de interés Clasificación y cuadro delimitador. En esta etapa, la red, basada en los resultados de la etapa anterior, está tratando de resaltar grandes áreas rectangulares en la fotografía, presumiblemente conteniendo el objeto objetivo;
  • Máscaras de segmentación. En esta etapa, la máscara del objeto deseado se obtiene del área rectangular obtenida aplicando el cuadro de límite.

Además, la red resultó tener una configuración muy flexible y pudimos reconstruirla para procesar imágenes con capas de información adicionales.

Usar solo imágenes RGB por sí solas no nos permitió lograr la precisión de reconocimiento necesaria (el modelo perdió edificios enteros, hubo un error promedio de 15% al ​​calcular el área del techo), por lo que proporcionamos al modelo datos útiles adicionales, por ejemplo, mapas de altura obtenidos por fotogrametría. 


Métricas utilizadas para evaluar la calidad del modelo.


Al determinar la calidad de los modelos, con mayor frecuencia usamos la métrica Intersección sobre Unión (IoU)


Código de muestra para calcular IoU usando la biblioteca geometry.shapely:

from shapely.geometry import Polygon

true_polygon = Polygon([(2, 2), (2, 6), (5, 6), (5, 2)])
predicted_polygon = Polygon([(3, 3), (3, 7), (6, 7), (6, 3)])
print(true_polygon.intersection(predicted_polygon).area / true_polygon.union(predicted_polygon).area)

>>> 0.3333333333333333

El seguimiento del proceso de capacitación de los modelos se controla convenientemente utilizando Tensorboard, una herramienta de control métrica conveniente que le permite recibir datos en tiempo real sobre la calidad del modelo y compararlos con otros modelos.


Tensorboard proporciona datos sobre muchas métricas diferentes. Los más interesantes para nosotros son:

  • val_mrcnn_bbox_loss: muestra qué tan bien el modelo ubica los objetos (es decir, impone un cuadro de límite);
  • val_mrcnn_mask_loss: muestra qué tan bien segmenta el modelo los objetos (es decir, impone una máscara).

Modelo de entrenamiento y validación


Durante el entrenamiento, utilizamos la práctica estándar de dividir aleatoriamente un conjunto de datos en 3 partes: entrenamiento, validación y prueba. En el proceso de aprendizaje, la calidad del modelo se evalúa en una muestra de validación y, una vez finalizada, pasa la prueba final sobre los datos de prueba que se cerraron en el proceso de aprendizaje. 

Hicimos nuestro primer inicio de entrenamiento en un pequeño conjunto de fotos de verano y, al decidir comprobar qué tan bueno será nuestro modelo en invierno, se esperaba un resultado decepcionante. La opción de usar diferentes modelos para diferentes estaciones, por supuesto, es una excelente manera de salir de la situación, pero implicaría una serie de inconvenientes, por lo que decidimos intentar que el modelo sea universal. Al experimentar con diferentes configuraciones de las capas, y también al cerrar el peso de las capas individuales de los cambios de peso, encontramos la estrategia óptima para entrenar el modelo al aplicar alternativamente imágenes de verano e invierno a la entrada.

Crear un servicio en segundo plano para el reconocimiento


Ahora que tenemos un modelo funcional, podemos hacer un servicio API en segundo plano a partir de un script de reconocimiento que toma una imagen como entrada y genera json con polígonos de techo encontrados en la salida. Esto no afecta directamente la solución del problema, pero puede ser útil para alguien. 

Ubuntu usa systemd, y se dará un ejemplo específicamente para este sistema. El código del servicio en sí se puede ver aquí . Las unidades de usuario se encuentran en el directorio / etc / systemd / system, donde crearemos nuestro archivo de servicio. Edite el archivo:

cd /etc/systemd/system

sudo touch my_srv.service




sudo vim my_srv.service

La unidad systemd consta de tres secciones:

  • [Unidad]: describe el orden y la condición del inicio (por ejemplo, puede indicarle al proceso que espere el inicio de un determinado servicio y solo luego lo inicie usted mismo);
  • [Servicio]: describe los parámetros de inicio;
  • [Instalar]: describe el comportamiento del servicio al agregarlo al inicio.

Como resultado, nuestro archivo se verá así:

[Unit]
Description=my_test_unit

[Service]
WorkingDirectory=/home/user/test_project
User=root
ExecStart=/home/user/test_project/venv/bin/python3 /home/user/test_project/script.py

[Install]
WantedBy=multi-user.target

Ahora vuelva a cargar la configuración systemd y ejecute nuestro servicio:

sudo systemctl daemon-reload
sudo systemctl start my_srv.service

Este es un ejemplo simple de un proceso en segundo plano, systemd admite muchos parámetros diferentes que le permiten configurar de manera flexible el comportamiento del servicio, pero no se requiere nada más complicado para nuestra tarea.

recomendaciones


El principal resultado del proyecto fue la capacidad de detectar automáticamente inconsistencias en el desarrollo real y la información contenida en los datos catastrales.

Como resultado de la evaluación de la precisión del modelo en los datos de prueba, se obtuvieron los siguientes valores: el número de cubiertas encontradas - 91%, la precisión de los polígonos del contorno de la cubierta - 94%.

Fue posible lograr una calidad aceptable de los modelos en vuelos de verano e invierno, pero la calidad de reconocimiento puede disminuir en las imágenes inmediatamente después de una nevada.

Ahora, incluso la Ópera de Sydney no se escapará de los ojos de nuestra modelo. 


Planeamos poner este servicio con un modelo entrenado en nuestro demostand. Si está interesado en probar el servicio en sus propias fotos, envíe las solicitudes a ai@norbit.ru.

All Articles