easyidp.pix4d.Pix4D#
- class easyidp.pix4d.Pix4D(project_path=None, raw_img_folder=None, param_folder=None)#
A Pix4D class, contains information of 3D reconstruction.
- __init__(project_path=None, raw_img_folder=None, param_folder=None)#
The method to initialize the Pix4D class
- パラメータ:
project_path (str, optional) -- The pix4d project file to open, like "xxxx.p4d", by default None, means create an empty class
raw_img_folder (str, optional) -- the original UAV image folder, by default None
param_folder (str, optional) -- the folder of pix4d project parameters, just in case user changed the default folder structure (
...\project_name\1_initial\params\
), by default None
サンプル
>>> import easyidp as idp >>> test_data = idp.data.TestData()
Then open the demo pix4d project:
>>> p4d = idp.Pix4D(test_data.pix4d.maize_folder)
Or manual specify parameters if the project folder structure has been changed.
>>> 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 ... )
Or you can create an empty project, and the open a given path:
>>> 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 ... )
注意
In previous case, the manager reorganized the project structure and outputs of
test_data.pix4d.lotus_folder
(e.g., moved the
\project_name\1_initial\params\
to\project_name\params\
, as well as other outputs, the following folder is no more a standard pix4d project)$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
The default loading doesn't work, because it is not a standard pix4d project:
>>> 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
In this case, must manual specfiy the
param_folder
Methods
__init__
([project_path, raw_img_folder, ...])The method to initialize the Pix4D class
back2raw
(roi[, save_folder])Projects several GIS coordintates ROIs (polygons) to all images
back2raw_crs
(points_xyz[, distort_correct, ...])Projects one GIS coordintates ROI (polygon) to all images
get_photo_position
([to_crs, refresh])Get all photos' center geo position (on given CRS)
load_dom
(geotiff_path)Manual load the DOM file generated by this Pix4D project
load_dsm
(geotiff_path)Manual load the DSM file generated by this Pix4D project
load_pcd
(pcd_path)Manual load the point cloud file generated by this Pix4D project
open_project
(project_path[, raw_img_folder, ...])Open a new 3D reconstructin project to overwritting current project.
show_roi_on_img
(img_dict, roi_name[, img_name])Visualize the specific backward projection results for given roi on the given image.
sort_img_by_distance
(img_dict_all, roi[, ...])Advanced wrapper of sorting back2raw img_dict results by distance from photo to roi
Attributes
crs
the geographic coordinates (often the same as the export DOM and DSM),
<class 'pyproj.crs.crs.CRS'>
dom
The output digitial orthomosaic map (DOM),
easyidp.GeoTiff
dsm
The output digitial surface map (DSM),
easyidp.GeoTiff
pcd
The output point cloud,
easyidp.PointCloud
the 3D reconstruction project software, in ['pix4d', 'metashape'],
<class 'str'>
pix4d point cloud offset
project / chunk name
project meta information
whether this project is activated, (often for Metashape),
<class 'bool'>
the container for all sensors in this project (camera model),
<class 'easyidp.Container'>
the container for all photos used in this project (images),
<class 'easyidp.Container'>
- back2raw(roi, save_folder=None, **kwargs)#
Projects several GIS coordintates ROIs (polygons) to all images
- パラメータ:
roi (easyidp.ROI | dict) -- the <ROI> object created by easyidp.ROI() or dictionary
save_folder (str, optional) -- the folder to save projected preview images and json files, by default ""
distortion_correct (bool, optional) -- Whether do distortion correction, by default True (back to raw image); If back to software corrected images without len distortion, set it to True. Pix4D support do this operation, seems metashape not supported yet.
ignore (str | None, optional) --
Whether tolerate small parts outside image, check
easyidp.reconstruct.Sensor.in_img_boundary()
for more details.None
: strickly in image area;x
: only y (vertical) in image area, x can outside image;y
: only x (horizontal) in image area, y can outside image.
log (bool, optional) -- whether print log for debugging, by default False
サンプル
Data prepare
>>> 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)
Then using this function to do backward projection:
>>> 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)#
Projects one GIS coordintates ROI (polygon) to all images
- パラメータ:
points_hv (ndarray (nx3)) -- The 3D coordinates of polygon vertexm, in CRS coordinates
distortion_correct (bool, optional) -- Whether do distortion correction, by default True (back to raw image); If back to software corrected images without len distortion, set it to True. Pix4D support do this operation, seems metashape not supported yet.
ignore (str | None, optional) --
Whether tolerate small parts outside image, check
easyidp.reconstruct.Sensor.in_img_boundary()
for more details.None
: strickly in image area;x
: only y (vertical) in image area, x can outside image;y
: only x (horizontal) in image area, y can outside image.
log (bool, optional) -- whether print log for debugging, by default False
- 戻り値:
a dictionary like
{img_name: pixel coordinate, ... }
- 戻り値の型:
dict,
サンプル
Data preparation
>>> 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] ... ])
Then use this function to find the previous ROI positions on the raw images:
>>> 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#
whether this project is activated, (often for Metashape),
<class 'bool'>
- get_photo_position(to_crs=None, refresh=False)#
Get all photos' center geo position (on given CRS)
- パラメータ:
to_crs (pyproj.CRS, optional) -- Transformed to another geo coordinate, by default None, the project.crs
refresh (bool, optional) --
False
: Use cached results (if have), by defaultTrue
: recalculate the photo position
- 戻り値:
The dictionary contains "photo.label": [x, y, z] coordinates
- 戻り値の型:
dict
サンプル
Data prepare
>>> import easyidp as idp >>> lotus = idp.data.Lotus() >>> p4d = idp.Pix4D(lotus.pix4d.project,lotus.photo, lotus.pix4d.param)
Then use this function to get the photo position in 3D world:
>>> 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#
project / chunk name
- load_dom(geotiff_path)#
Manual load the DOM file generated by this Pix4D project
- パラメータ:
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)#
Manual load the DSM file generated by this Pix4D project
- パラメータ:
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)#
Manual load the point cloud file generated by this Pix4D project
- パラメータ:
pcd_path (str) -- The path to point cloud file
注意
The pix4d produced point cloud is offsetted (xyz-offset). This function already handle adding offset back to point cloud. If you need manual specify
idp.PointCloud()
by yourself, please do:>>> 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#
project meta information
- offset_np#
pix4d point cloud offset
- open_project(project_path, raw_img_folder=None, param_folder=None)#
Open a new 3D reconstructin project to overwritting current project.
- パラメータ:
project_path (str) -- The pix4d project file to open, like "xxxx.p4d", or "xxxx" without suffix
raw_img_folder (str, optional) -- the original UAV image folder, by default None
param_folder (str, optional) -- the folder of pix4d project parameters, just in case user changed the default folder structure (
...\project_name\1_initial\params\
), by default None
サンプル
>>> import easyidp as idp >>> test_data = idp.data.TestData()
Then using this function to open a new project:
>>> 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#
the container for all photos used in this project (images),
<class 'easyidp.Container'>
- sensors#
the container for all sensors in this project (camera model),
<class 'easyidp.Container'>
- show_roi_on_img(img_dict, roi_name, img_name=None, **kwargs)#
Visualize the specific backward projection results for given roi on the given image.
- パラメータ:
img_dict (dict) -- The backward results from back2raw()
roi_name (str) -- The roi name to show
img_name (str) -- the image file name. by default None, plotting all available images
corrected_poly_coord (np.ndarray, optional) -- the corrected 2D polygon pixel coordiante on the image (if have), by default None
title (str, optional) -- The image title displayed on the top, by default None ->
ROI [roi_name] on [img_name]
save_as (str, optional) -- file path to save the output figure, by default None
show (bool, optional) -- whether display (in jupyter notebook) or popup (in command line) the figure, by default False
color (str, optional) -- the polygon line color, by default 'red'
alpha (float, optional) -- the polygon transparency, by default 0.5
dpi (int, optional) -- the dpi of produced figure, by default 72
サンプル
>>> img_dict_ms = roi.back2raw(ms)
Check the "N1W1" ROI on image "DJI_0479.JPG":
>>> ms.show_roi_on_img(img_dict_ms, "N1W1", "DJI_0479")
Check the "N1W1" ROI on all available images:
>>> ms.show_roi_on_img(img_dict_ms, "N1W1")
For more details, please check in this example
- software#
the 3D reconstruction project software, in ['pix4d', 'metashape'],
<class 'str'>
- sort_img_by_distance(img_dict_all, roi, distance_thresh=None, num=None, save_folder=None)#
Advanced wrapper of sorting back2raw img_dict results by distance from photo to roi
- パラメータ:
img_dict_all (dict) -- All output dict of roi.back2raw(...) e.g. img_dict = roi.back2raw(...) -> img_dict
roi (idp.ROI) -- Your roi variable
num (None or int) -- Keep the closest {x} images
distance_thresh (None or float) -- Keep the images closer than this distance to ROI.
save_folder (str, optional) -- the folder to save json files and parts of ROI on raw images, by default None
- 戻り値:
the same structure as output of roi.back2raw(...)
- 戻り値の型:
dict
サンプル
In the previous
back2raw()
results :>>> 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()` }
The image are in chaos order, in most application cases, probable only 1-3 closest images (to ROI in real world) are required, so this function is provided to sort/filter out.
In the following example, it filtered 3 images whose distance from camera to ROI in real world smaller than 10m:
>>> 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]]) } }
Or pick the closest one image:
>>> 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]]) } }
You can use
list(dict.keys())[0]
to get the image name automatically to iterate each plot: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]