easyidp.pix4d.Pix4D

class easyidp.pix4d.Pix4D(project_path=None, raw_img_folder=None, param_folder=None)

一个Pix4D类,包含3D重建的信息。

__init__(project_path=None, raw_img_folder=None, param_folder=None)

初始化Pix4D类的方法

参数:
  • project_path (str, optional) -- 要打开的pix4d项目文件,如“xxxx.p4d”,默认值为None,表示创建一个空类

  • raw_img_folder (str, optional) -- 原始无人机图像文件夹,默认值为 None

  • param_folder (str, optional) -- pix4d项目参数的文件夹,以防用户更改了默认文件夹结构(...\project_name\1_initial\params\),默认值为None

示例

>>> import easyidp as idp
>>> test_data = idp.data.TestData()

然后打开演示pix4d项目:

>>> p4d = idp.Pix4D(test_data.pix4d.maize_folder)

或者,如果项目文件夹结构已更改,请手动指定参数。

>>> p4d = idp.Pix4D(
...     project_path   = test_data.pix4d.lotus_folder,
...     raw_img_folder = test_data.pix4d.lotus_photos,
...     param_folder   = test_data.pix4d.lotus_param
... )

或者您可以创建一个空项目,然后打开给定路径:

>>> p4d = idp.Pix4D()
>>> p4d.open_project(
...     project_path   = test_data.pix4d.lotus_folder,
...     raw_img_folder = test_data.pix4d.lotus_photos,
...     param_folder   = test_data.pix4d.lotus_param
... )

小心

在之前的情况下,经理重新组织了``test_data.pix4d.lotus_folder``的项目结构和输出

(例如,将``project_name1_initialparams``移动到``project_nameparams``,以及其他输出,以下文件夹不再是标准的pix4d项目)

$ls '/Users/<user>/Library/Application Support/easyidp.data/data_for_tests/pix4d/lotus_tanashi_full'

params/
photos/
hasu_tanashi_20170525_Ins1RGB_30m_dsm.tif
hasu_tanashi_20170525_Ins1RGB_30m_group1_densified_point_cloud.ply
hasu_tanashi_20170525_Ins1RGB_30m_transparent_mosaic_group1.tif
plot_dom.tif
plot_dsm.tif
plot_pcd.ply

默认加载不起作用,因为它不是标准的pix4d项目:

>>> p4d = idp.Pix4D(test_data.pix4d.lotus_folder)

Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/Users/hwang/OneDrive/Program/GitHub/EasyIDP/easyidp/pix4d.py", line 48, in __init__
    File "/Users/hwang/OneDrive/Program/GitHub/EasyIDP/easyidp/pix4d.py", line 56, in open_project
        hasu_tanashi_20170525_Ins1RGB_30m_group1_densified_point_cloud.ply
    File "/Users/hwang/OneDrive/Program/GitHub/EasyIDP/easyidp/pix4d.py", line 829, in parse_p4d_project
        sub_folder = os.listdir(project_path)
FileNotFoundError: Can not find pix4d parameter in given project folder

在这种情况下,必须手动指定 param_folder

Methods

__init__([project_path, raw_img_folder, ...])

初始化Pix4D类的方法

back2raw(roi[, save_folder])

将多个 GIS 坐标 ROI(多边形)投影到所有图像上

back2raw_crs(points_xyz[, distort_correct, ...])

将一个GIS坐标ROI(多边形)投影到所有图像

get_photo_position([to_crs, refresh])

获取所有照片的中心地理位置(在给定的 CRS 上)

load_dom(geotiff_path)

手动加载此Pix4D项目生成的DOM文件

load_dsm(geotiff_path)

手动加载此Pix4D项目生成的DSM文件

load_pcd(pcd_path)

手动加载此Pix4D项目生成的点云文件

open_project(project_path[, raw_img_folder, ...])

打开一个新的 3D 重建项目以覆盖当前项目。

show_roi_on_img(img_dict, roi_name[, img_name])

在给定图像上可视化给定 ROI 的特定反向投影结果。

sort_img_by_distance(img_dict_all, roi[, ...])

按照片到 ROI 的距离排序 back2raw img_dict 结果的高级包装器

Attributes

crs

the geographic coordinates (often the same as the export DOM and DSM), <class 'pyproj.crs.crs.CRS'>

dom

输出的数字正射影像图 (DOM),easyidp.GeoTiff

dsm

输出的数字表面模型 (DSM),easyidp.GeoTiff

pcd

输出的点云,easyidp.PointCloud

software

3D重建项目软件,在['pix4d', 'metashape']中,<class 'str'>

offset_np

pix4d point cloud offset

label

项目 / 块名称

meta

项目元信息

enabled

该项目是否被激活,(通常用于Metashape),<class 'bool'>

sensors

该项目中所有传感器的容器(相机模型),<class 'easyidp.Container'>

photos

该项目中所有照片的容器(图像),<class 'easyidp.Container'>

back2raw(roi, save_folder=None, **kwargs)

将多个 GIS 坐标 ROI(多边形)投影到所有图像上

参数:
  • roi (easyidp.ROI | dict) -- 由easyidp.ROI()或字典创建的<ROI>对象

  • save_folder (str, optional) -- 用于保存投影预览图像和json文件的文件夹,默认值为""

  • distortion_correct (bool, optional) -- 是否进行畸变校正,默认值为True(返回原始图像);如果返回没有镜头畸变的软件校正图像,请将其设置为True。Pix4D支持此操作,Metashape似乎尚不支持。

  • ignore (str | None, optional) -- 是否容忍图像外的小部分,详见 easyidp.reconstruct.Sensor.in_img_boundary()。 - None:严格在图像区域内; - x:仅y(垂直)在图像区域内,x可以在图像外; - y:仅x(水平)在图像区域内,y可以在图像外。

  • log (bool, optional) -- 是否打印日志以进行调试,默认值为False

示例

数据准备

>>> import easyidp as idp

>>> lotus = idp.data.Lotus()

>>> p4d = idp.Pix4D(project_path=lotus.pix4d.project,
                    raw_img_folder=lotus.photo,
                    param_folder=lotus.pix4d.param)

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

>>> roi.get_z_from_dsm(lotus.pix4d.dsm)

然后使用此函数进行反向投影:

>>> out_all = p4d.back2raw(roi)
{
    'N1W1': {
        'DJI_0479':
            array([[  52.9824393 , 1253.05643133],
                   [  79.20465849,  979.90831093],
                   [ 363.27656888, 1000.07501881],
                   [ 337.25115499, 1273.15285336],
                   [  52.9824393 , 1253.05643133]]),
        'DJI_0480':
            array([...])
        ...
    }
    'N1W2': {...}   # output of `back2raw_crs()`
}
back2raw_crs(points_xyz, distort_correct=True, ignore=None, log=False)

将一个GIS坐标ROI(多边形)投影到所有图像

参数:
  • points_hv (ndarray (nx3)) -- 多边形顶点的3D坐标,CRS坐标

  • distortion_correct (bool, optional) -- 是否进行畸变校正,默认值为True(返回原始图像);如果返回没有镜头畸变的软件校正图像,请将其设置为True。Pix4D支持此操作,Metashape似乎尚不支持。

  • ignore (str | None, optional) -- 是否容忍图像外的小部分,详见 easyidp.reconstruct.Sensor.in_img_boundary()。 - None:严格在图像区域内; - x:仅y(垂直)在图像区域内,x可以在图像外; - y:仅x(水平)在图像区域内,y可以在图像外。

  • log (bool, optional) -- 是否打印日志以进行调试,默认值为False

返回:

一个类似``{img_name: pixel coordinate, ... }``的字典

返回类型:

dict,

示例

数据准备

>>> import easyidp as idp

>>> p4d = idp.Pix4D(
...     test_data.pix4d.lotus_folder,
...     raw_img_folder=test_data.pix4d.lotus_photos,
...     param_folder=test_data.pix4d.lotus_param
... )

>>> plot =  np.array([   # N1E1 plot geo coordinate
...     [ 368020.2974959 , 3955511.61264302,      97.56272272],
...     [ 368022.24288365, 3955512.02973983,      97.56272272],
...     [ 368022.65361232, 3955510.07798313,      97.56272272],
...     [ 368020.69867274, 3955509.66725421,      97.56272272],
...     [ 368020.2974959 , 3955511.61264302,      97.56272272]
... ])

然后使用此函数在原始图像上找到先前的ROI位置:

>>> out_dict = p4d.back2raw_crs(plot, distort_correct=True)

>>> out_dict["DJI_0177"]
array([[ 137.10982937, 2359.55887614],
       [ 133.56116243, 2107.13954299],
       [ 384.767746  , 2097.05639105],
       [ 388.10993307, 2350.41225998],
       [ 137.10982937, 2359.55887614]])
enabled

该项目是否被激活,(通常用于Metashape),<class 'bool'>

get_photo_position(to_crs=None, refresh=False)

获取所有照片的中心地理位置(在给定的 CRS 上)

参数:
  • to_crs (pyproj.CRS, optional) -- 转换为另一个地理坐标,默认值为None,即项目的CRS

  • refresh (bool, optional) --

    • False:使用缓存的结果(如果有),默认值

    • True:重新计算照片位置

返回:

字典包含"photo.label":[x, y, z]坐标

返回类型:

dict

示例

数据准备

>>> import easyidp as idp

>>> lotus = idp.data.Lotus()
>>> p4d = idp.Pix4D(lotus.pix4d.project,lotus.photo, lotus.pix4d.param)

然后使用此函数获取照片在3D世界中的位置:

>>> out = p4d.get_photo_position()
{
    'DJI_0422.JPG': array([ 368016.23334752, 3955491.97729229,     138.25541246]),
    'DJI_0423.JPG': array([ 368016.33261375, 3955492.15845851,     138.05762001]),
    ...
}
label

项目 / 块名称

load_dom(geotiff_path)

手动加载此Pix4D项目生成的DOM文件

参数:

geotiff_path (str) -- The path to DOM file

示例

>>> import easyidp as idp
>>> ... # define your project_path and param_folder path
>>> p4d = idp.Pix4D(project_path, param_folder)
>>> p4d.dom
None
>>> p4d.load_dom(dom_path)
>>> p4d.dom
<easyidp.GeoTiff> object
load_dsm(geotiff_path)

手动加载此Pix4D项目生成的DSM文件

参数:

geotiff_path (str) -- The path to DSM file

示例

>>> import easyidp as idp
>>> ... # define your project_path and param_folder path
>>> p4d = idp.Pix4D(project_path, param_folder)
>>> p4d.dsm
None
>>> p4d.load_dsm(dsm_path)
>>> p4d.dsm
<easyidp.GeoTiff> object
load_pcd(pcd_path)

手动加载此Pix4D项目生成的点云文件

参数:

pcd_path (str) -- The path to point cloud file

小心

pix4d生成的点云是偏移的(xyz-offset)。此函数已经处理了将偏移添加回点云。如果您需要手动指定 idp.PointCloud() ,请执行以下操作:

>>> p4d = idp.Pix4D(project_path, param_folder)

# wrong
>>> pcd = idp.PointCloud(lotus_full_pcd)

# correct
>>> pcd = idp.PointCloud(lotus_full_pcd, offset=p4d.meta['p4d_offset'])
# or
>>> pcd = idp.PointCloud(lotus_full_pcd, offset=p4d.offset_np)

示例

>>> import easyidp as idp
>>> ... # define your project_path and param_folder path
>>> p4d = idp.Pix4D(project_path, param_folder)
>>> p4d.pcd
None
>>> p4d.load_pcd(pcd_path)
>>> p4d.pcd
<easyidp.PointCloud> object
meta

项目元信息

offset_np

pix4d point cloud offset

open_project(project_path, raw_img_folder=None, param_folder=None)

打开一个新的 3D 重建项目以覆盖当前项目。

参数:
  • project_path (str) -- 要打开的pix4d项目文件,如“xxxx.p4d”,或不带后缀的“xxxx”

  • raw_img_folder (str, optional) -- 原始无人机图像文件夹,默认值为 None

  • param_folder (str, optional) -- pix4d项目参数的文件夹,以防用户更改了默认文件夹结构(...\project_name\1_initial\params\),默认值为None

示例

>>> import easyidp as idp
>>> test_data = idp.data.TestData()

然后使用此函数打开一个新项目:

>>> p4d = idp.Pix4D()
>>> p4d.open_project(
...     project_path   = test_data.pix4d.lotus_folder,
...     raw_img_folder = test_data.pix4d.lotus_photos,
...     param_folder   = test_data.pix4d.lotus_param
... )
photos

该项目中所有照片的容器(图像),<class 'easyidp.Container'>

sensors

该项目中所有传感器的容器(相机模型),<class 'easyidp.Container'>

show_roi_on_img(img_dict, roi_name, img_name=None, **kwargs)

在给定图像上可视化给定 ROI 的特定反向投影结果。

参数:
  • img_dict (dict) -- 来自back2raw()的反向结果

  • roi_name (str) -- 要显示的ROI名称

  • img_name (str) -- 图像文件名,默认值为None,绘制所有可用图像

  • corrected_poly_coord (np.ndarray, optional) -- 图像上校正的2D多边形像素坐标(如果有),默认值为None

  • title (str, optional) -- 显示在顶部的图像标题,默认值为None -> ROI [roi_name] on [img_name]

  • save_as (str, optional) -- 保存输出图形的文件路径,默认值为None

  • show (bool, optional) -- 是否显示(在jupyter notebook中)或弹出(在命令行中)图形,默认值为False

  • color (str, optional) -- 多边形线条颜色,默认值为'红色'

  • alpha (float, optional) -- 多边形透明度,默认值为0.5

  • dpi (int, optional) -- 生成图形的dpi,默认值为72

示例

>>> img_dict_ms = roi.back2raw(ms)

检查图像"DJI_0479.JPG"上的"N1W1" ROI:

>>> ms.show_roi_on_img(img_dict_ms, "N1W1", "DJI_0479")

检查所有可用图像上的"N1W1" ROI:

>>> ms.show_roi_on_img(img_dict_ms, "N1W1")

有关更多详细信息,请参见 此示例

software

3D重建项目软件,在['pix4d', 'metashape']中,<class 'str'>

sort_img_by_distance(img_dict_all, roi, distance_thresh=None, num=None, save_folder=None)

按照片到 ROI 的距离排序 back2raw img_dict 结果的高级包装器

参数:
  • img_dict_all (dict) -- roi.back2raw(...)的所有输出字典,例如 img_dict = roi.back2raw(...) -> img_dict

  • roi (idp.ROI) -- 您的ROI变量

  • num (None or int) -- 保留最近的{x}张图像

  • distance_thresh (None or float) -- 保留距离ROI比此距离更近的图像。

  • save_folder (str, optional) -- 保存json文件和原始图像上ROI部分的文件夹,默认值为None

返回:

与roi.back2raw(...)的输出结构相同

返回类型:

dict

示例

在之前的 back2raw() 结果中:

>>> out_all = p4d.back2raw(roi)
{
    'N1W1': {
        'DJI_0479':
            array([[  52.9824393 , 1253.05643133],
                   [  79.20465849,  979.90831093],
                   [ 363.27656888, 1000.07501881],
                   [ 337.25115499, 1273.15285336],
                   [  52.9824393 , 1253.05643133]]),
        'DJI_0480':
            array([...])
        ...
    }
    'N1W2': {...}   # output of `back2raw_crs()`
}

图像顺序混乱,在大多数应用情况下,可能只需要1-3张最接近(现实世界中的ROI)的图像,因此提供了此函数进行排序/过滤。

在以下示例中,它过滤了3张相机到现实世界中的ROI距离小于10米的图像:

>>> img_dict_sort = p4d.sort_img_by_distance(
...     out_all, roi,
...     distance_thresh=10,  # distance threshold is 10m
...     num=3   # only keep 3 closest images
... )

>>> img_dict_sort
{
    'N1W1': {
        'DJI_0500': array([[1931.09279469, 2191.59919979],
                           [1939.92139124, 1930.65101348],
                           [2199.9439422 , 1939.32128527],
                           [2191.19230849, 2200.557026  ],
                           [1931.09279469, 2191.59919979]]),
        'DJI_0517': array([[2870.94915401, 2143.3570243 ],
                           [2596.8790503 , 2161.04730612],
                           [2578.87033498, 1886.89058023],
                           [2853.13891851, 1869.99769984],
                           [2870.94915401, 2143.3570243 ]]),
        'DJI_0518': array([[3129.43264924, 1984.91814896],
                           [2856.71879306, 2002.03817639],
                           [2838.71418138, 1730.00287388],
                           [3111.73360179, 1713.76233134],
                           [3129.43264924, 1984.91814896]])
    },
    'N1W2': {
        'DJI_0500': array([[2214.36789052, 2200.35979344],
                           [2221.8996575 , 1940.70687713],
                           [2479.9825464 , 1949.3909589 ],
                           [2472.52171907, 2209.40355333],
                           [2214.36789052, 2200.35979344]]),
        'DJI_0517': array([[2849.82108263, 1845.6733702 ],
                           [2577.37309441, 1863.60741328],
                           [2559.80046778, 1592.07656949],
                           [2832.52942622, 1574.92640413],
                           [2849.82108263, 1845.6733702 ]]),
        'DJI_0516': array([[2891.61686486, 2542.98979632],
                           [2616.06780032, 2559.41601014],
                           [2598.43900454, 2282.36641612],
                           [2874.23023492, 2266.71552931],
                           [2891.61686486, 2542.98979632]])
    }
}

或选择最近的一张图像:

>>> img_dict_sort = p4d.sort_img_by_distance(
...     out_all, roi,
...     num=1   # only keep 1 closest images
... )

>>> img_dict_sort
{
    'N1W1': {
        'DJI_0500': array([[1931.09279469, 2191.59919979],
                           [1939.92139124, 1930.65101348],
                           [2199.9439422 , 1939.32128527],
                           [2191.19230849, 2200.557026  ],
                           [1931.09279469, 2191.59919979]])
    },
    'N1W2': {
        'DJI_0500': array([[2214.36789052, 2200.35979344],
                           [2221.8996575 , 1940.70687713],
                           [2479.9825464 , 1949.3909589 ],
                           [2472.52171907, 2209.40355333],
                           [2214.36789052, 2200.35979344]])
    }
}

您可以使用``list(dict.keys())[0]``自动获取图像名称以迭代每个绘图:

for plot_name, plot_value in img_dict_sort.items():
    img_name = list(plot_value.key())[0]

    coord = plot_value[img_name]
    # or
    coord = img_dict_sort[plot_name][img_name]