MASK-RCNN用于从无人机图像中查找屋顶


在一条白色街道上的白色城市中,有白色和白色的房屋……而这张照片中您能很快找到房屋的所有屋顶吗?

人们越来越多地听到政府计划对房地产进行完整清点以澄清地籍数据的计划。对于此问题的主要解决方案,可以基于从航空照片计算出的首都建筑物屋顶面积并与地籍数据进行进一步比较的基础上,采用一种简单的方法。不幸的是,手动搜索和计算需要大量时间,并且由于新房屋的拆除和建造是连续的,因此需要一次又一次地重复计算。立刻出现了一个假设,即可以使用机器学习算法(特别是Computer Vision)来自动执行此过程。在本文中,我将讨论我们在NORBIT的情况 解决了这个问题,以及他们遇到了什么困难。

剧透-我们做到了开发的ML服务基于基于卷积神经网络的深度机器学习模型。该服务接受来自无人机的图像作为输入;在输出处,该服务生成一个GeoJSON文件,该文件带有参考地理坐标的找到的基本建设对象的标记。

结果,它看起来像这样:


问题


让我们从遇到的技术问题开始:

  • 冬季和夏季航空照片之间存在显着差异(仅在夏季照片中训练的模型在冬季完全无法找到屋顶);
  • , ;
  • , ( ), ( ) , ;
  • , , ( ). ;
  • (, ) .

无人机有时会带来以下照片:


我还想指出可能存在的问题,但它们与我们无关:

  • 我们没有任务可以在有限的时间内执行推理(例如,在飞行时直接进行推理),这可以立即解决所有可能的性能问题;
  • 在进行处理时,我们立即从客户Shakhty公司收到了高质量的高分辨率图像(使用焦距为21 mm的镜头,高度为250 m,即5 cm / px),可以使用他们的专业知识对地图上的对象进行地理定位,并且他们还有机会对未来的无人机飞行制定一套特定的要求,这最终大大降低了训练集中没有的非常独特的瓷砖的可能性;

第一个解决问题的方法是使用“边界框”描边 


关于我们用来创建解决方案的工具的几句话。

  • Anaconda是用于Python和R的便捷软件包管理系统。
  • Tensorflow是Google开发的开源机器学习软件库。
  • Keras是Deeplearning4j,TensorFlow和Theano框架的附加组件。
  • OpenCV是用于计算机视觉,图像处理和通用开源数值算法的算法库。
  • Flask是用于使用Python编程语言创建Web应用程序的框架。

由于操作系统使用Ubuntu 18.04。借助Ubuntu中GPU(NVIDIA)上的驱动程序,一切都井井有条,因此通常可以通过以下命令来解决此任务:

> sudo apt install nvidia-cuda-toolkit

瓷砖准备


我们面临的第一个任务是将飞越图像拆分为图块(2048x2048 px)。您可以编写自己的脚本,但是随后您必须考虑维护每个图块的地理位置。使用现成的解决方案(例如GeoServer)更容易,它是一种开放源代码软件,可让您在服务器上发布地理数据。此外,GeoServer为我们解决了另一个问题-在地图上方便地显示自动标记的结果。这可以在本地完成,例如在qGIS中,但是对于分布式命令和演示,Web资源更加方便。

要执行平铺,您需要在设置中指定所需的比例和大小。


对于坐标系之间的转换,我们使用了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)
...

结果,可以容易地从所有多边形形成一个大的层并将其放置在基板的顶部上。 


要安装GeoServer软件,您必须完成以下步骤。
  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并不是使我们能够解决问题的唯一应用程序。例如,可以选择使用ArcGIS for Server,但该产品是专有的,因此我们没有使用它。

接下来,每个瓷砖都必须找到所有可见的屋顶。解决问题的第一种方法是使用来自模型/研究Tensorflow集object_detection通过这种方式,可以找到图像上的类并使用矩形选择(边界框)定位它们。 

训练数据标记 


显然,要训练模型,您需要标记的数据集。幸运的巧合是,除了盘旋之外,在我们的垃圾箱中,保存了过去美好时光的5万个屋顶的数据集,当时所有用于训练的数据集仍然处在公共领域。

要获得可接受的模型精度所需的训练样本的确切大小很难事先预测。它可以根据图像的质量,它们彼此之间的不相似程度以及在生产中使用该模型的条件而有所不同。我们有200块足够的情况,有时也遗漏了5万个标记样品。在缺少标记图像的情况下,我们通常会添加增强方法:转向,镜面反射,颜色分级等。

现在,有许多服务可用来标记图像-既可以使用开放源代码进行标记以在计算机/服务器上安装,也可以使用公司解决方案,包括外部评估人员的工作,例如Yandex.Toloka。在此项目中,我们使用了最简单的VGG图像注释器另外,您可以尝试coco-annotatorlabel-studio我们通常使用后者来标记文本和音频文件。


为了训练各种注释器的标记,通常需要对字段进行一小部分转换,例如VGG

为了正确计算落入矩形分配区域的屋顶面积,有必要注意以下几个条件:

  • / . :


  • , , :


要解决第二个问题,您可以尝试训练一个单独的模型,该模型可以确定用于标记的瓷砖的正确旋转角度,但事实证明一切都更加容易。人们自己努力减少熵,因此他们将所有人造结构相互对齐,尤其是与密集的建筑物对齐。如果从上方看,则在局部区域中,篱笆,人行道,植物,温室,乔木将平行于或垂直于屋顶的边界。剩下的只是找到所有清晰的线条并计算最常见的垂直倾斜角度。为此,OpenCV具有出色的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()])

找到角度后,我们使用仿射变换旋转图像:


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)

完整的示例代码在这里看起来是这样的:



旋转瓷砖和用矩形标记的方法比用掩模标记的方法更快,几乎可以找到所有屋顶,但是在生产中,由于以下几个缺点,该方法仅用作辅助方法:

  • 有很多飞越的地方都有大量的非矩形屋顶,因此需要太多的手工工作来完善该区域,
  • 有时会在同一瓷砖上以不同方向在家中找到
  • 有时,图块上有许多错误的线条,最终会导致错误的转弯。看起来像这样:



基于Mask-RCNN的最终解决方案


第二种尝试是通过逐像素遮罩来搜索并突出显示屋顶,然后自动为找到的遮罩轮廓绘制轮廓并创建矢量多边形。  

关于卷积神经网络的操作原理,类型和任务的材料已经足够,包括俄语,因此在本文中我们不再赘述。让我们仅讨论一种特定的实现,即Mask-RCNN-一种用于定位和突出显示图像中对象轮廓的体系结构。还有其他各有优缺点的出色解决方案,例如UNet,但是可以在Mask-RCNN上获得更好的质量。

在其发展过程中,它经历了几个阶段。 R-CNN的第一版于2014年开发。它的工作原理是突出显示图像中的小区域,对于每个小区域都将估计目标物体在该区域中的存在概率。 R-CNN在这项任务上做得很出色,但是其速度仍然有很多不足之处。合理的发展是Fast R-CNN和Faster R-CNN网络,它们在图像抓取算法上得到了改进,从而显着提高了速度。在Faster R-CNN的出口处,出现带有矩形选择的标记,指示对象的边界,但这并不总是足以解决问题。 

遮罩R-CNN还添加了逐像素遮罩叠加层,以获取对象的精确轮廓。

在模型的操作结果上可以清楚地看到边界框和蒙版(打开最小建筑面积的过滤器):


通常,此网络的运行分为四个阶段:

  • 所有卷积神经网络的标准,是图像中特征的分配,例如线条,折弯,对比边界等。
  • 区域提议网(RPN)扫描图像的小片段,称为锚点(anchor),并确定该锚点是否包含特定于目标类别的标志(在我们的情况下为屋顶);
  • 兴趣区域分类和边界框。在此阶段,网络根据上一阶段的结果,试图突出显示照片中可能包含目标对象的矩形区域。
  • 细分蒙版。在此阶段,从通过应用边界框获得的矩形区域中获得所需对象的蒙版。

此外,该网络的配置非常灵活,我们能够对其进行重建以处理带有附加信息层的图像。

仅使用RGB图像无法使我们达到必要的识别精度(该模型错过了整个建筑物,在计算屋顶面积时平均误差为15%),因此我们为模型提供了更多有用的数据,例如通过摄影测量获得的高度图 


用于评估模型质量的指标


在确定模型的质量时,我们最经常使用“交集相交”(IoU)度量


使用geometry.shapely库计算IoU的示例代码:

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

使用Tensorboard可以方便地控制跟踪模型的训练过程,Tensorboard是一种方便的度量控制工具,可让您接收有关模型质量的实时数据并将其与其他模型进行比较。


Tensorboard提供有关许多不同指标的数据。对我们来说最有趣的是:

  • val_mrcnn_bbox_loss-显示模型对对象的本地化程度(即强加边界框);
  • val_mrcnn_mask_loss-显示模型对对象的分割程度(即施加遮罩)。

模型训练与验证


训练时,我们采用将数据集随机分为3个部分的标准做法-训练,验证和测试。在学习过程中,将基于验证样本评估模型的质量,并在完成后通过对在学习过程中关闭的测试数据的最终测试。 

我们从一小撮夏天的镜头开始了我们的第一次训练,然后决定检查我们的模型在冬天的状态如何,我们预期会收到令人失望的结果。当然,可以选择在不同的季节使用不同的模型,这是摆脱情况的绝佳方法,但这会带来许多不便,因此我们决定尝试使模型通用。通过对图层的不同配置进行实验,并通过权重变化来关闭各个图层的权重,我们找到了通过交替使用夏季和冬季图片作为输入来训练模型的最佳策略。

创建用于识别的后台服务


现在我们有了一个可以正常运行的模型,我们可以从识别脚本中提供后台API服务,该脚本将图像作为输入并生成在输出中找到带有屋顶多边形的json。这不会直接影响问题的解决,但对某人可能有用。 

Ubuntu使用systemd,并将为此系统专门给出一个示例。服务本身的代码可以在这里查看用户单元位于/ etc / systemd / system目录中,我们将在其中创建我们的服务文件。 编辑文件:

cd /etc/systemd/system

sudo touch my_srv.service




sudo vim my_srv.service

系统单元包括三个部分:

  • [Unit]-描述启动的顺序和条件(例如,您可以告诉进程等待某个服务的启动,然后自己启动);
  • [服务]-描述启动参数;
  • [安装]-描述将服务添加到启动时的行为。

结果,我们的文件将如下所示:

[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

现在重新加载systemd配置并运行我们的服务:

sudo systemctl daemon-reload
sudo systemctl start my_srv.service

这是一个后台进程的简单示例,systemd支持许多不同的参数,这些参数使您可以灵活地配置服务的行为,但是我们的任务并不需要更复杂。

发现


该项目的主要结果是能够自动检测实际开发中的不一致性以及地籍数据中包含的信息。

根据测试数据评估模型的准确性后,获得了以下值:发现的屋顶数量-91%,屋顶轮廓多边形的准确性-94%。

在夏季和冬季的飞行中可以达到可接受的模型质量,但是降雪后图像中的识别质量可能会下降。

现在,即使是悉尼歌剧院也不会从我们模特的视线中溜走。 


我们计划在我们的演示台上以经过培训的模型提供此服务。如果您想在自己的照片上尝试使用该服务,请将应用程序发送至ai@norbit.ru。

All Articles