阅读我们的文章

.

逐步指导如何为机器学习算法收集和预处理卫星图像 data

本文是两部分系列文章的第一部分,探讨机器学习算法在卫星图像上的应用。在这里,我们将重点介绍 data 的采集和预处理步骤,而第二部分将展示各种机器学习算法的使用。.

简要说明
本文将

  • 让您更好地了解卫星图像
  • 向您展示哪些类型的图像可以开源收集
  • 指导您完成必要的步骤,以获得 “机器学习就绪 ”的图像

    我们将以农田检测和分类为例。预处理步骤使用 Python 和 jupyter notebook。. 用于重现步骤的 jupyter 笔记本可在 github 上获取.
    本文假定 data 科学和 python 具有基本的基础知识。.

  • 计算机视觉是机器学习的一个迷人分支,近年来发展迅速。随着公众可以访问许多现成的算法库(如......张力流, OpenCv, Pytorch, Fastai ...)以及开源 datasets (CIFAR -10, Imagenet, IMDB ......)对于 data 科学家来说,获得这方面的实践经验非常容易。.

    但您知道您也可以获取开源卫星图像吗?这些类型的图像可用于大量用例(农业、物流、能源......)。然而,由于其大小和复杂性,它们给 data 科学家带来了额外的挑战。我撰写了这篇文章,与大家分享我在工作中的一些主要心得。.

    步骤 1 - 选择卫星打开 data 信号源

    首先,您需要选择要使用的卫星。这里需要注意的主要功能有 :

    • 空间分辨率分辨率:每个像素覆盖多少米,更高的分辨率将解锁一些用例,并为大多数算法提供更好的性能。.

    • 可提供的仪器光谱:仪器将捕捉不同的光谱波段,包括可见光谱和不可见光光谱。不同波段的作用因使用情况而异。.

    • 时间分辨率 时间分辨率:两次访问地球上某一地点之间的时间。请注意,许多卫星系统实际上包括多颗卫星,在这种情况下,全球时间分辨率通常等于其中一颗卫星的时间分辨率除以卫星数量。.

    • 辐射分辨率 :仪器捕捉到的可能数值的数量。数值越大,测量越精确。.

    以下是免费提供 data 的卫星:

    卫星系统的基准

    由于我们的目标是检测和识别农作物,因此可见光谱(用于提供标准彩色图像)应该非常有用。此外,农作物的氮含量与 NIR(近红外)光谱波段之间也存在相关性。就免费选项而言,哨兵 2 号目前在这些光谱波段的分辨率方面是最好的,达到了 10 米,因此我们将使用这颗卫星。.

    当然,与商业卫星达到 30 厘米的卫星图像分辨率相比,10 米的分辨率要低得多。获取商业卫星 data 可以解锁许多用例,但尽管进入这一市场的新公司竞争日益激烈,它们的价格仍然很高,因此很难集成到规模用例中。.

    步骤 2 - 从哨兵 2 号收集卫星图像 data

    既然我们已经选择了卫星来源,新的步骤就是下载图像。哨兵 2 号是哥白尼计划的一部分。 欧洲航天局. .我们可以使用 SentinelSat python 应用程序接口.

    我们首先需要在 哥白尼开放存取中心. .另外请注意,该网站还提供了使用图形用户界面查询图像的方法。.

    向应用程序接口提供 geojson 文件是指定要探索的区域的一种方法,该文件是一个包含区域 GPS 坐标位置(纬度和经度)的 json 文件。可以通过以下方式快速创建 Geojson 文件 网站.

    这样,您就可以查询与所提供区域相交的所有卫星图像。.

    在此,我们列出了在给定区域和时间范围内的所有可用图像:

    api = SentinelAPI(
    凭证[“用户名”]、,
    凭据[“密码”]、,
    “https://scihub.copernicus.eu/dhus”
    )

    shape = geojson_to_wkt(read_geojson(geojson_path))

    图像 = api.query(
    形状,
    date=(date(2020, 5, 1), date(2020, 5, 10))、,
    platformname=”Sentinel-2″、,
    处理级别 = “2A级”、,
    cloudcoverpercentage=(0, 30)
    )

    images_df = api.to_dataframe(images)

    注意参数的使用:

    • 日期时间分辨率:设定这一范围时应考虑到 6 天的时间分辨率。由于我们要检测的是农作物,因此将其设置为收获前的时间段

    • 处理级 :2A 级是最先进的预处理级别。例如,已经对图像进行了大气校正 (更深入地了解处理水平)

    • cloud 覆盖率 :云层会使某些图像难以利用。我们会过滤掉超过 30 % 的图像,因为它们很可能无法使用。

    图像

    Images_df 是一个 pandas dataframe,其中包含与我们的查询相匹配的所有图像。我们选择一张 cloud 覆盖率较低的图片,并使用 api .NET 下载:

    uuid = “01d97f6b-8eb5-4edc-9261-10e3f4d437d0”
    api.download(uuid)

    步骤 3 - 了解使用哪些频谱带并管理量化

    下载压缩文件后,我们可以看到它实际上包含了多个图像。.

    这是因为正如我们在步骤 1 中所说的,哨兵 2 号捕捉的光谱波段很多,而不仅仅是红绿蓝,而且每个波段都捕捉在一张图像中。.

    此外,其仪器的空间分辨率也不尽相同,因此为 10 米、20 米和 60 米的不同分辨率分别设置了一个子文件夹。.

    如果我们检查 GRANULE//IMG_DATA 在文件夹中,我们可以看到每个不同分辨率都有一个子文件夹:10、20 和 60 米的光谱波段。我们将使用最大分辨率为 10 米的波段,因为它们提供了蓝光、绿光、红光和近红外波段 2、3、4 和 8。第 7、8 和 9 波段是植被红边(介于红色和近红外之间的光谱波段,标志着植被反射率值的过渡),也可用于探测农作物,但由于它们的分辨率较低,我们现在暂不使用。

    在这里,我们将每个频段加载为二维 numpy 矩阵:

    def get_band(image_folder, band, resolution=10):
    subfolder = [f for f in os.listdir(image_folder + "/GRANULE") if f[0] == "L"][0] image_folder_path = f"/GRANULE//IMG_DATA/Rm"
    image_files = [im for im in os.listdir(image_folder_path) if im[-4:] == ".jp2"] 选择的文件 = [im for im in image_files if im.split("_")[2] == band][0 selected_file = [im for im in image_files if im.split("_")[2] == band][0]

    使用 rasterio.open(f"/") 作为 infile:
    img = infile.read(1)

    返回 img

    band_dict =

    for band in ["B02", "B03", "B04", "B08"]:
    band_dict[band] = get_band(图像文件夹,band,10)

    为确保一切正常,我们可以尝试使用 opencv 和 matplotlib 重新创建真彩色图像(即 RGB)并将其显示出来:

    img = cv2.merge((band_dict[“B04”], band_dict[“B03”], band_dict[“B02”]))
    plt.imshow(img)

    卫星图像 - 色彩强度问题(哥白尼哨兵 data 2020)

    不过,在颜色强度方面存在一个问题。这是因为量化(可能值的数量)不同于 matplotlib 的标准,后者假定为 0-255 int 值或 0-1 float 值。标准图像通常使用 8 位量化(256 个可能值),但哨兵 2 号图像使用 12 位量化,并通过重新处理将值转换为 16 位整数(65536 个值)。.

    如果我们要将 16 位整数缩放为 8 位整数,理论上应该除以 256,但这实际上会导致图像非常暗,因为没有使用整个可能的数值范围。我们图像中的最大值实际上是 65536 中的 16752,而实际上很少有像素的值超过 4000,因此除以 8 实际上会得到一个对比度不错的图像。

    img_processed = img / 8
    img_processed = img_processed.astype(int)
    plt.imshow(img_processed)

    卫星图像 - 右侧颜色强度(哥白尼哨兵 data 2020)

    现在,我们有了一个以 numpy 数组形式存在的三维张量,其形状为 (10980, 10980, 3) 的 8 位整数

    注意到 :

    • 我们可以进一步采用非线性缩放方法来提高对比度。.

    • 转换为整数相当于对值进行四舍五入,因此我们实际上会丢失一些信息。这在很多使用情况下都没问题,而且还能提高处理速度,但如果精度是关键,最好将反射率值转换为 0-1 浮点数(大多数 ML 库也管理这种格式),而不是 0-255 整数。.

    • 目前,我们暂不考虑近红外光谱波段。这将有助于我们更快地进行测试,因为许多现成的算法都需要 3 个颜色通道的图像。不过,如果能在某个时候重新整合近红外光谱带,无论是通过替换其他通道之一,还是通过定制算法使其具有 4 个色彩通道,都会非常有趣。.

    步骤 4 - 将图像分割成机器学习所需的大小

    我们可以尝试将算法应用到当前图像中,但实际上,由于图像的大小,大多数技术都无法做到这一点。即使拥有强大的计算能力,大多数算法(尤其是深度学习)也无法实现。.

    因此,我们必须将图像分割成片段。一个问题是如何设置片段的大小。.

    • 小片段可以提高性能。.

    • 较大的片段可以减少因被分割成多个图像而难以检测到的物体数量。不过,对于某些重缩放图像或无法很好地缩放给定图像中一定数量物体的 ML 算法来说,过大的片段可能会造成问题。.

    我们决定将每幅图像分割成 45 * 45 的网格(45 正好是 10980 的分隔线)。

    在完成参数固定后,这一步就可以直接使用 numpy 数组操作了。我们使用 (x, y) 元组作为键,将值存储在一个 dict 中。.

    frag_count = 45
    frag_size = int(img_processed.shape[0] / frag_count)
    frag_dict =

    for y, x in itertools.product(range(frag_count), range(frag_count)):
    frag_dict[(x, y)] = img_processed[y*frag_size: (y+1)*frag_size、,
    x*frag_size: (x+1)*frag_size, :]

    plt.imshow(frag_dict[(10, 10)])

    卫星图像片段(哥白尼哨兵 data 2020)

    第 5 步 - 使用 GPS 至 UTM 转换将 data 与卫星图像连接起来

    最后,我们需要将图像与农田上的 data 相连接,以实现有监督的机器学习方法。在我们的案例中,农田上的 data 是以 GPS 坐标(纬度和经度)列表的形式存在的,与我们在第 2 步中使用的 geojson 类似,我们希望能够找到它们的像素位置。不过,为了清楚起见,我将首先展示像素转换为 GPS 坐标的过程,然后再展示反向过程

    在请求 SentinelSat API 时收集的 metadata 提供了图像角落(足迹列)的 GPS 坐标,而我们需要的是特定像素的坐标。纬度和经度是角度值,不会在图像内线性变化,因此我们无法使用像素位置进行简单的比例运算。有一些方法可以用数学公式来解决这个问题,但在 python 中已经存在现成的解决方案。.

    一种快速的解决方案是将像素位置转换为 UTM(Universal Transverse Mercator,通用横轴默卡托),在 UTM 中,位置由区域以及(x、y)位置(以米为单位)定义,而后者与图像呈线性变化。因此,我们可以使用 utm 库提供的 UTM 到经度/纬度的转换。为此,我们首先需要获取 UTM 区域和图像左上角的位置,这可以在真彩色图像的 metadata 中找到。.

    #Obtaining meatadata
    transform = rasterio.open(tci_file_path, driver=’JP2OpenJPEG’).transform
    zone_number = int(tci_file_path.split(“/”)[-1][1:3])
    zone_letter = tci_file_path.split(“/”)[-1][0] utm_x, utm_y = transform[2], transform[5]

    # 将像素位置转换为 utm
    east = utm_x + pixel_column * 10
    north = utm_y + pixel_row * - 10

    # 将 UTM 转换为经纬度
    纬度、经度 = utm.to_latlon(east, north, zone_number, zone_letter)

    GPS 定位到像素的转换使用相反的公式完成。在这种情况下,我们必须确保获得的区域与我们的图像相匹配。这可以通过指定区域来实现。如果我们的对象不在图像中,这将导致像素值超出边界。.

    # 将经纬度转换为 UTM
    east, north, zone_number, zone_letter = utm.from_latlon(
    纬度, 经度, force_zone_number=zone_number
    )

    # 将 UTM 转换为列和行
    pixe_column = round((east - utm_x) / 10)
    pixel_row = round((north - utm_y) / -10)
    查看原始

    结论

    现在,我们可以将卫星图像用于机器学习了!

    在本系列 2 部分的下一篇文章中,我们将了解如何利用这些图像,通过有监督和无监督的机器学习来检测字段表面并对其进行分类。.

    感谢您的阅读,请随时 关注 Artefact 技术博客 如果您希望在下一篇文章发布时收到通知 !

    您可以在我们的 Medium 博客上找到更多关于我们和我们项目的信息