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
crsthe geographic coordinates (often the same as the export DOM and DSM),
<class 'pyproj.crs.crs.CRS'>dom输出的数字正射影像图 (DOM),
easyidp.GeoTiffdsm输出的数字表面模型 (DSM),
easyidp.GeoTiffpcd输出的点云,
easyidp.PointCloud3D重建项目软件,在['pix4d', 'metashape']中,
<class 'str'>pix4d point cloud offset
项目 / 块名称
项目元信息
该项目是否被激活,(通常用于Metashape),
<class 'bool'>该项目中所有传感器的容器(相机模型),
<class 'easyidp.Container'>该项目中所有照片的容器(图像),
<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]