Dans une ville blanc-blanc sur une rue blanc-blanc il y avait des maisons blanches-blanches ... Et à quelle vitesse pouvez-vous trouver tous les toits des maisons sur cette photo?De plus en plus, on peut entendre parler des plans du gouvernement de procéder à un inventaire complet des biens immobiliers afin de clarifier les données cadastrales. Pour la solution principale à ce problème, une méthode simple peut être appliquée basée sur le calcul de la surface du toit des bâtiments d'immobilisations à partir de photographies aériennes et une comparaison plus approfondie avec les données cadastrales. Malheureusement, la recherche et le calcul manuels prennent beaucoup de temps, et comme les nouvelles maisons sont démolies et construites en continu, le calcul doit être répété encore et encore. L'hypothèse se pose immédiatement que ce processus peut être automatisé à l'aide d'algorithmes d'apprentissage automatique, en particulier, la vision par ordinateur. Dans cet article, je parlerai de notre situation à NORBIT résolu ce problème et quelles difficultés ils ont rencontrées.Spoiler - nous l'avons fait . Le service ML développé est basé sur un modèle d'apprentissage machine profond basé sur des réseaux de neurones à convolution. Le service accepte en entrée des images de véhicules aériens sans pilote; en sortie, il génère un fichier GeoJSON avec le balisage des objets de construction en capital trouvés en référence aux coordonnées géographiques.En conséquence, cela ressemble à ceci:Problèmes
Commençons par les problèmes techniques que nous avons rencontrés:- il existe une différence significative entre les photographies aériennes d'hiver et d'été (un modèle formé uniquement aux photographies d'été est totalement incapable de trouver des toits en hiver);
- , ;
- , ( ), ( ) , ;
- , , ( ). ;
- (, ) .
Et les drones apportent parfois ces photos:Je voudrais également noter les problèmes qui auraient pu être, mais ils ne nous concernaient pas:- nous n'avons eu aucune tâche pour effectuer l'inférence pendant un temps limité (par exemple, directement au moment du vol), ce qui a immédiatement résolu tous les problèmes de performances possibles;
- à l'entrée pour le traitement, nous avons immédiatement reçu des images haute résolution de haute qualité (en utilisant des objectifs avec une focale de 21 mm à une hauteur de 250 m, soit 5 cm / px) de notre client, la société Shakhty, pourrait utiliser leur expertise dans la géolocalisation des objets sur les cartes, et ils ont également eu l'occasion d'établir un ensemble spécifique d'exigences pour les futurs vols d'UAV, ce qui a finalement considérablement réduit la probabilité de carreaux très uniques qui n'étaient pas dans l'ensemble de formation;
La première solution au problème, l'AVC à l'aide de la boîte Boundary
Quelques mots sur les outils que nous avons utilisés pour créer la solution.- Anaconda est un système de gestion de paquets pratique pour Python et R.
- Tensorflow est une bibliothèque de logiciels d'apprentissage automatique open source développée par Google.
- Keras est un module complémentaire pour les frameworks Deeplearning4j, TensorFlow et Theano.
- OpenCV est une bibliothèque d'algorithmes pour la vision par ordinateur, le traitement d'image et les algorithmes numériques open source à usage général.
- Flask est un framework pour créer des applications web dans le langage de programmation Python.
Comme le système d'exploitation a utilisé Ubuntu 18.04. Avec les pilotes sur le GPU (NVIDIA) dans Ubuntu, tout est en ordre, donc la tâche est généralement résolue avec une seule commande:> sudo apt install nvidia-cuda-toolkit
Préparation des carreaux
La première tâche à laquelle nous avons été confrontés a été de diviser les images de survol en tuiles (2048x2048 px). Vous pouvez écrire votre propre script, mais vous devrez alors penser à maintenir l'emplacement géographique de chaque tuile. Il était plus facile d'utiliser une solution prête à l'emploi, par exemple, GeoServer - c'est un logiciel open source qui vous permet de publier des géodonnées sur le serveur. De plus, GeoServer a résolu un autre problème pour nous: l'affichage pratique du résultat du marquage automatique sur la carte. Cela peut être fait localement, par exemple, dans qGIS, mais pour une commande et une démonstration distribuées, une ressource Web est plus pratique.Pour effectuer le carrelage, vous devez spécifier l'échelle et la taille requises dans les paramètres.Pour les traductions entre les systèmes de coordonnées, nous avons utilisé la bibliothèque 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)
...
En conséquence, il a été possible de former facilement une grande couche à partir de tous les polygones et de la déposer sur le substrat. Pour installer le logiciel GeoServer, vous devez effectuer les étapes suivantes.- Java 8.
- GeoServer.
- , ,
/usr/share/geoserver
-
echo «export GEOSERVER_HOME=/usr/share/geoserver» >> ~/.profile
- :
sudo groupadd geoserver
- , , :
sudo usermod -a -G geoserver <user_name>
- - :
sudo chown -R :geoserver /usr/share/geoserver/
- :
sudo chmod -R g+rwx /usr/share/geoserver/
- GeoServer
cd geoserver/bin && sh startup.sh
GeoServer n'est pas la seule application qui nous permet de résoudre notre problème. Par exemple, vous pouvez envisager ArcGIS for Server, mais ce produit est propriétaire, nous ne l'avons donc pas utilisé.Ensuite, chaque tuile devait trouver tous les toits visibles. La première approche pour résoudre le problème a été d'utiliser la détection d' objet de l'ensemble modèles / recherche Tensorflow. De cette façon, les classes sur les images peuvent être trouvées et localisées avec une sélection rectangulaire (encadré). Balisage des données de formation
Évidemment, pour former le modèle, vous avez besoin d'un ensemble de données étiqueté. Par une heureuse coïncidence, en plus de tourner autour, dans nos bacs, l'ensemble de données pour 50 000 toits a été préservé du bon vieux temps, lorsque tous les ensembles de données pour la formation étaient toujours dans le domaine public partout.La taille exacte de l'échantillon d'apprentissage nécessaire pour obtenir une précision acceptable du modèle est assez difficile à prévoir à l'avance. Il peut varier en fonction de la qualité des images, de leur degré de dissemblance entre elles et des conditions d'utilisation du modèle en production. Nous avons eu des cas où 200 pièces étaient suffisantes, et parfois 50 000 échantillons marqués manquaient également. En cas de pénurie d'images balisées, nous ajoutons généralement des méthodes d'augmentation: virages, reflets miroir, dégradés de couleurs, etc.Il existe maintenant de nombreux services qui vous permettent de baliser des images - à la fois avec du code open source pour l'installation sur votre ordinateur / serveur, et des solutions d'entreprise, y compris le travail d'évaluateurs externes, par exemple Yandex.Toloka. Dans ce projet, nous avons utilisé l' annotateur d'image VGG le plus simple . Alternativement, vous pouvez essayer coco-annotator ou label-studio . Nous utilisons généralement ce dernier pour baliser le texte et les fichiers audio.Pour la formation sur le balisage de divers annotateurs, vous devez généralement effectuer un petit décalage de champs, un exemple pour VGG .Afin de calculer correctement la surface de la toiture tombée dans la zone d'allocation rectangulaire, il est nécessaire de respecter plusieurs conditions:Pour résoudre le deuxième problème, vous pouvez essayer de former un modèle séparé qui déterminerait l'angle de rotation correct de la tuile pour le marquage, mais tout s'est avéré un peu plus facile. Les gens eux-mêmes s'efforcent de réduire l'entropie, ils alignent donc toutes les structures artificielles les unes par rapport aux autres, en particulier avec les bâtiments denses. Si vous regardez d'en haut, alors dans une zone localisée, les clôtures, les allées, les plantations, les serres, les tonnelles seront parallèles ou perpendiculaires aux limites des toits. Il ne reste plus qu'à trouver toutes les lignes nettes et à calculer l'angle d'inclinaison le plus courant par rapport à la verticale. Pour cela, OpenCV dispose d'un excellent outil 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()])
Après avoir trouvé l'angle, nous faisons pivoter l'image en utilisant la transformation affine:
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)
L'exemple de code complet est ici . Voici à quoi ça ressemble:La méthode de retournement des tuiles et de marquage avec des rectangles fonctionne plus rapidement que le marquage avec des masques, presque tous les toits sont trouvés, mais en production, cette méthode n'est utilisée que comme auxiliaire en raison de plusieurs inconvénients:- il y a beaucoup de survols où il y a un grand nombre de toits non rectangulaires, à cause de cela il y a trop de travail manuel pour affiner la zone,
- parfois trouvé à la maison avec des orientations différentes sur la même tuile,
- parfois il y a beaucoup de fausses lignes sur les tuiles, ce qui conduit finalement Ă un mauvais virage. Cela ressemble Ă ceci:
La solution finale basée sur Mask-RCNN
La deuxième tentative a consisté à rechercher et à mettre en évidence les toits par masques pixel par pixel, puis à définir automatiquement les contours des masques trouvés et à créer des polygones vectoriels. Il existe déjà suffisamment de documents sur les principes de fonctionnement, les types et les tâches des réseaux de neurones convolutifs, y compris ceux en russe, nous ne les aborderons donc pas dans cet article. Arrêtons-nous sur une seule implémentation spécifique, Mask-RCNN - une architecture pour localiser et mettre en évidence les contours des objets dans les images. Il existe d'autres excellentes solutions avec leurs avantages et leurs inconvénients, par exemple UNet, mais il était possible d'obtenir une meilleure qualité sur Mask-RCNN.Au cours de son développement, il est passé par plusieurs étapes. La première version de R-CNN a été développée en 2014. Le principe de son travail est de mettre en évidence de petites zones de l'image, pour chacune desquelles est estimée la probabilité de présence d'un objet cible dans cette zone. R-CNN a fait un excellent travail avec la tâche, mais sa vitesse laissait beaucoup à désirer. Le développement logique a été les réseaux Fast R-CNN et Faster R-CNN, qui ont reçu des améliorations dans l'algorithme d'exploration d'image, ce qui a permis d'augmenter considérablement la vitesse. À la sortie de Faster R-CNN, un marquage apparaît avec une sélection rectangulaire indiquant les limites de l'objet, ce qui n'est pas toujours suffisant pour résoudre le problème. Le masque R-CNN ajoute également une superposition de masque pixel par pixel pour obtenir le contour exact de l'objet.Le cadre de délimitation et les masques sont clairement visibles sur le résultat de l'opération du modèle (le filtre par la surface minimale du bâtiment est activé):Classiquement, le fonctionnement de ce réseau se déroule en 4 étapes:- standard pour tous les réseaux de neurones convolutifs, l'allocation de caractéristiques dans l'image, telles que les lignes, les virages, les frontières contrastées et autres;
- Le réseau de propositions de région (RPN) scanne de petits fragments de l'image, appelés ancres (ancres) et détermine si cette ancre contient des signes spécifiques à la classe cible (dans notre cas, le toit);
- Classification des régions d'intérêt et cadre de délimitation. À ce stade, le réseau, sur la base des résultats de l'étape précédente, tente de mettre en évidence de grandes zones rectangulaires dans la photographie, contenant vraisemblablement l'objet cible;
- Masques de segmentation. A ce stade, le masque de l'objet souhaité est obtenu à partir de la zone rectangulaire obtenue en appliquant la boîte frontière.
De plus, la configuration du réseau s'est avérée très flexible et nous avons pu le reconstruire pour traiter des images avec des couches d'informations supplémentaires.L'utilisation d'images exclusivement RVB ne nous a pas permis d'obtenir la précision de reconnaissance nécessaire (le modèle a raté des bâtiments entiers, il y avait une erreur moyenne de 15% dans le calcul de la surface du toit), nous avons donc fourni au modèle des données utiles supplémentaires, par exemple, des cartes de hauteur obtenues par photogrammétrie. Mesures utilisées pour évaluer la qualité du modèle
Pour déterminer la qualité des modèles, nous avons le plus souvent utilisé la métrique Intersection over Union (IoU)Exemple de code pour calculer l'IoU à l'aide de la bibliothèque 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
Le suivi du processus de formation des modèles est facilement contrôlé à l'aide de Tensorboard, un outil de contrôle métrique pratique qui vous permet de recevoir des données en temps réel sur la qualité du modèle et de les comparer avec d'autres modèles.Tensorboard fournit des données sur de nombreuses mesures différentes. Les plus intéressants pour nous sont:- val_mrcnn_bbox_loss - montre à quel point le modèle localise les objets (c.-à -d. impose une boîte frontière);
- val_mrcnn_mask_loss - montre à quel point le modèle segmente les objets (c'est-à -dire impose un masque).
Formation et validation des modèles
Lors de la formation, nous avons utilisé la pratique standard de diviser au hasard un ensemble de données en 3 parties - formation, validation et test. Dans le processus d'apprentissage, la qualité du modèle est évaluée sur un échantillon de validation et, à la fin, réussit le test final sur les données de test qui en ont été fermées dans le processus d'apprentissage. Nous avons fait nos premiers démarrages d'entraînement sur une petite série de clichés d'été et, décidant de vérifier la qualité de notre modèle en hiver, nous nous attendions à un résultat décevant. L'option d'utiliser différents modèles pour différentes saisons, bien sûr, est un excellent moyen de sortir de la situation, mais cela entraînerait un certain nombre d'inconvénients, nous avons donc décidé d'essayer de rendre le modèle universel. En expérimentant différentes configurations des couches et en fermant également le poids des couches individuelles des changements de poids, nous avons trouvé la stratégie optimale pour entraîner le modèle en appliquant alternativement des images d'été et d'hiver à l'entrée.Création d'un service d'arrière-plan pour la reconnaissance
Maintenant que nous avons un modèle fonctionnel, nous pouvons créer un service d'API en arrière-plan à partir d'un script de reconnaissance qui prend une image en entrée et génère json avec des polygones de toit trouvés en sortie. Cela n'affecte pas directement la solution du problème, mais cela peut être utile à quelqu'un. Ubuntu utilise systemd, et un exemple sera donné spécifiquement pour ce système. Le code du service lui-même peut être consulté ici . Les unités utilisateur sont situées dans le répertoire / etc / systemd / system, où nous allons créer notre fichier de service. Modifiez le fichier:cd /etc/systemd/system
sudo touch my_srv.service
sudo vim my_srv.service
L'unité systemd se compose de trois sections:- [Unité] - décrit l'ordre et la condition du démarrage (par exemple, vous pouvez dire au processus d'attendre le démarrage d'un certain service et de le démarrer ensuite vous-même);
- [Service] - décrit les paramètres de démarrage;
- [Installer] - décrit le comportement du service lors de son ajout au démarrage.
En conséquence, notre fichier ressemblera à ceci:[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
Rechargez maintenant la configuration systemd et exécutez notre service:sudo systemctl daemon-reload
sudo systemctl start my_srv.service
Ceci est un exemple simple d'un processus d'arrière-plan, systemd prend en charge de nombreux paramètres différents qui vous permettent de configurer de manière flexible le comportement du service, mais rien de plus compliqué n'est requis pour notre tâche.résultats
Le principal résultat du projet a été la capacité de détecter automatiquement les incohérences dans le développement réel et les informations contenues dans les données cadastrales.À la suite de l'évaluation de la précision du modèle sur les données de test, les valeurs suivantes ont été obtenues: le nombre de toits trouvés - 91%, la précision des polygones de contour de toit - 94%.Il était possible d'obtenir une qualité acceptable des modèles lors des vols d'été et d'hiver, mais la qualité de reconnaissance peut diminuer dans les images immédiatement après une chute de neige.Maintenant, même l'opéra de Sydney ne se dérobera pas aux yeux de notre modèle. Nous prévoyons de mettre ce service avec un modèle formé sur notre demostand. Si vous êtes intéressé à essayer le service sur vos propres photos, envoyez vos candidatures à ai@norbit.ru.