Backward Projection

这个例子解释了如何使用 EasyIDP 在原始无人机图像上找到 ROI 的对应位置。

荷花池

包和数据准备

导入 easyidp 包的最常见方式是:

[1]:
import easyidp as idp

lotus = idp.data.Lotus()

如果您是第一次运行,它将自动从 Google Drive 下载 3.3GB 的数据集,请参考 Data 了解更多详情。

从 Shapefile 读取 ROI

然后打开 shapefile plot.shp,shp 文件看起来像这样(红色多边形):

荷花池

[2]:
roi = idp.ROI(lotus.shp, name_field='plot_id')
[shp][proj] Use projection [WGS 84] for loaded shapefile [plots.shp]
Read shapefile [plots.shp]: 100%|██████████| 112/112 [00:00<00:00, 2406.33it/s]

然后检查它是否按预期加载:

[3]:
roi
[3]:
<easyidp.ROI> with 112 items
[0]     N1W1
array([[139.54052962,  35.73475194],
       [139.54055106,  35.73475596],
       [139.54055592,  35.73473843],
       [139.54053438,  35.73473446],
       [139.54052962,  35.73475194]])
[1]     N1W2
array([[139.54053488,  35.73473289],
       [139.54055632,  35.73473691],
       [139.54056118,  35.73471937],
       [139.54053963,  35.73471541],
       [139.54053488,  35.73473289]])
...
[110]   S4E6
array([[139.54090456,  35.73453742],
       [139.540926  ,  35.73454144],
       [139.54093086,  35.7345239 ],
       [139.54090932,  35.73451994],
       [139.54090456,  35.73453742]])
[111]   S4E7
array([[139.54090986,  35.73451856],
       [139.54093129,  35.73452258],
       [139.54093616,  35.73450504],
       [139.54091461,  35.73450107],
       [139.54090986,  35.73451856]])

从 DSM 获取高度(z)值

shapefile 中的 ROI 只有 2D 坐标,然而,要进行逆向投影,ROI 应该是 3D 坐标,缺失的高度值可以从 DSM 获取。

未来工作

未来将支持从点云获取 Z 值,在这种情况下,如果只需要逆向投影(在原始图像上进行图像分析而不是低质量的 geotiff),3D 重建可以在制作密集点云时停止,无需运行后续的 DOM 和 DSM 步骤。

[4]:
roi.get_z_from_dsm(lotus.metashape.dsm)
Read z values of roi from DSM [170531.Lotus_dsm.tif]: 100%|██████████| 112/112 [00:01<00:00, 100.17it/s]

并检查 ROI 的值

[5]:
roi
[5]:
<easyidp.ROI> with 112 items
[0]     N1W1
array([[ 368017.7565143 , 3955511.08102276,      97.39836121],
       [ 368019.70190232, 3955511.49811902,      97.39836121],
       [ 368020.11263046, 3955509.54636219,      97.39836121],
       [ 368018.15769062, 3955509.13563382,      97.39836121],
       [ 368017.7565143 , 3955511.08102276,      97.39836121]])
[1]     N1W2
array([[ 368018.20042946, 3955508.96051697,      97.31330109],
       [ 368020.14581791, 3955509.37761334,      97.31330109],
       [ 368020.55654627, 3955507.42585654,      97.31330109],
       [ 368018.601606  , 3955507.01512806,      97.31330109],
       [ 368018.20042946, 3955508.96051697,      97.31330109]])
...
[110]   S4E6
array([[ 368051.31139629, 3955486.78103425,      97.61438751],
       [ 368053.25678767, 3955487.19813795,      97.61438751],
       [ 368053.66752456, 3955485.24638299,      97.61438751],
       [ 368051.71258131, 3955484.83564713,      97.61438751],
       [ 368051.31139629, 3955486.78103425,      97.61438751]])
[111]   S4E7
array([[ 368051.75902187, 3955484.68169527,      97.59178925],
       [ 368053.70441367, 3955485.09879908,      97.59178925],
       [ 368054.11515079, 3955483.14704415,      97.59178925],
       [ 368052.16020711, 3955482.73630818,      97.59178925],
       [ 368051.75902187, 3955484.68169527,      97.59178925]])

我们可以注意到,roi 的 x 和 y 值也发生了变化。因为 ROI shp 地理坐标是 EPSG::4326,而 DSM 是 EPSG::32654。

另见

有关此功能控制的更多详细信息,请参考 从 DSM 获取高度(z)值

Read 3D reconstuction project and backward projection

对于 metashape 项目:

[6]:
ms = idp.Metashape(lotus.metashape.project, chunk_id=0)

对于 pix4d 项目:

[7]:
p4d = idp.Pix4D(project_path=lotus.pix4d.project,
                raw_img_folder=lotus.photo,
                param_folder=lotus.pix4d.param)

And then do the backward projection by:

[8]:
img_dict_ms = roi.back2raw(ms)
img_dict_p4d = roi.back2raw(p4d)
Backward roi to raw images: 100%|██████████| 112/112 [00:01<00:00, 56.75it/s]
Backward roi to raw images: 100%|██████████| 112/112 [00:01<00:00, 79.72it/s]

>>> img_dict_ms = ms.back2raw(roi)
>>> img_dict_p4d = p4d.back2raw(roi)

注意

您可以通过以下方式将结果(json 和裁剪的 png)保存到指定文件夹:

img_dict_sort = roi.back2raw(ms, ..., save_folder="folder_to_save")

并将在文件夹中获得以下结果:

荷花池

输出 img_dict 的结构是两层字典。第一层是 roi id,第二层是图像名称 (out_dict['roi_id']['image_name'])。

You can find all available images with specified roi (plot) (e.g. roi named ‘N1W1’) by:

>>> img_dict_ms['N1W1']
{'DJI_0479': array([[  43.91987253, 1247.04066872],
                    [  69.0221046 ,  972.89938018],
                    [ 353.25370817,  993.30409359],
                    [ 328.10701394, 1267.40353364],
                    [  43.91987253, 1247.04066872]]),
 'DJI_0480': array([[ 655.3678591 , 1273.01418098],
                    [ 681.18303761,  996.4866665 ],
                    [ 965.60719523, 1019.55346144],
                    [ 939.89408896, 1296.05588162],
                    [ 655.3678591 , 1273.01418098]]),
...
}

For example, find the roi named ‘N1W1’ on image ‘IMG_3457’ by:

[9]:
img_dict_ms["N1W1"]['DJI_0479']
[9]:
array([[  43.91987253, 1247.04066872],
       [  69.0221046 ,  972.89938018],
       [ 353.25370817,  993.30409359],
       [ 328.10701394, 1267.40353364],
       [  43.91987253, 1247.04066872]])

这是图像像素上 roi 的 2D 坐标

The recommended ‘for loops’ for itering items:

for roi_id, img_dict in out_dict.items():
    # roi_id = 'N1W1'
    # img_dict = out_dict[roi_id]
    for img_name, coords in img_dict.items():
        # img_name = "IMG_3457"
        # coords = out_dict[roi_id][img_name]
        print(coords)

预览结果:

[10]:
ms.show_roi_on_img(img_dict_ms, "N1W1", "DJI_0479")
../_images/jupyter_backward_projection_28_0.png
<Figure size 432x288 with 0 Axes>

或检查所有结果:

[11]:
ms.show_roi_on_img(img_dict_ms, "N1W1")
Reading image files for plotting: 100%|██████████| 17/17 [00:05<00:00,  2.88it/s]
Image data loaded, drawing figures, this may cost a few seconds...
../_images/jupyter_backward_projection_30_2.png
<Figure size 432x288 with 0 Axes>

推荐使用 ms.show_roi_on_img(..., save_as="preview_all.png") 保存到本地磁盘并检查清晰图像。

找到最佳逆向图像

您可以注意到,对于每个 ROI,它将逆向投影到几个原始图像:

[12]:
len(img_dict_ms["N1W1"])
[12]:
17

如何找到最佳的 3 或 5 张图像?在这里您可以计算图像到 ROI 的距离,这里我们假设距离越短越好(理想情况下,无人机图像刚好在 ROI 区域上方,ROI 位于图像中心)。

[13]:
img_dict_sort = ms.sort_img_by_distance(
    img_dict_ms, roi,
    distance_thresh=10,  # distance threshold is 2m
    num=3   # only keep 3 closest images
)
Getting photo positions: 100%|██████████| 151/151 [00:01<00:00, 126.79it/s]
Filter by distance to ROI: 100%|██████████| 112/112 [00:00<00:00, 1404.14it/s]

注意

您可以通过以下方式将结果(json 和裁剪的 png)保存到指定文件夹:

img_dict_sort = ms.sort_img_by_distance(..., save_folder="folder_to_save")

并将在文件夹中获得以下结果:

荷花池

[14]:
img_dict_sort["N1W1"]
[14]:
{'DJI_0500': array([[1922.56317403, 2186.55026212],
        [1931.87582434, 1925.40596027],
        [2192.1757749 , 1934.43920442],
        [2182.74834419, 2195.88054775],
        [1922.56317403, 2186.55026212]]),
 'DJI_0517': array([[2865.83794784, 2151.98114332],
        [2592.43855364, 2169.39562183],
        [2574.82262634, 1895.17496101],
        [2848.48455635, 1878.83422193],
        [2865.83794784, 2151.98114332]]),
 'DJI_0501': array([[1964.74098137, 1514.46208287],
        [1974.95313868, 1258.05047894],
        [2233.01889145, 1269.92769931],
        [2222.63045448, 1526.62893437],
        [1964.74098137, 1514.46208287]])}

Here is the best 3 image that match “distance from ROI to image” < 10m, and the first one is the closest.

检查结果:

[15]:
ms.show_roi_on_img(img_dict_ms, "N1W1", "DJI_0500")
../_images/jupyter_backward_projection_39_0.png
<Figure size 432x288 with 0 Axes>

或检查所有结果:

[16]:
ms.show_roi_on_img(img_dict_sort, "N1W1")
Reading image files for plotting: 100%|██████████| 3/3 [00:00<00:00,  3.12it/s]
Image data loaded, drawing figures, this may cost a few seconds...
../_images/jupyter_backward_projection_41_2.png
<Figure size 432x288 with 0 Axes>

推荐使用 ms.show_roi_on_img(..., save_as="preview_all.png") 保存到本地磁盘并检查清晰图像。