easyidp.metashape.Metashape#

class easyidp.metashape.Metashape(project_path=None, chunk_id=None, raw_img_folder=None, check_img_existance=True)#

the object for each chunk in Metashape 3D reconstruction project

__init__(project_path=None, chunk_id=None, raw_img_folder=None, check_img_existance=True)#

The method to initialize the Metashape class

参数:
  • project_path (str, optional) – The metashape project file to open, like “xxxx.psx”,, by default None, means create an empty class

  • chunk_id (int or str, optional) – The chunk id or name(label) want to open, by default None, open the first chunk.

  • raw_img_folder (str, optional) – the original UAV image folder, by default None

  • check_img_existance (bool) – Ignore the missing photos when set to False, suitable for testing project with just a few images, to avoid the FileNotFoundError

示例

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

Then open the demo Metashape project:

>>> ms = idp.Metashape(test_data.metashape.lotus_psx)
<'Lotus.psx' easyidp.Metashape object with 1 active chunks>

  id  label
----  -------
-> 0  Chunk 1

Or you can create an empty class and then open project:

>>> ms = idp.Metashape()
>>> ms.open_project(test_data.metashape.lotus_psx)
<'Lotus.psx' easyidp.Metashape object with 1 active chunks>

id  label
----  -------
-> 0  Chunk 1

小心

One metashape project may have several chunks, and each easyidp.Metashape project could only handle with only one chunk at once.

The arrow before ID shows which chunk has been opened

<'multichunk.psx' easyidp.Metashape object with 2 active chunks>

  id  label
----  ------------
-> 1  multiple_bbb
   2  miltiple_aaa

Methods

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

The method to initialize the Metashape class

back2raw(roi[, save_folder])

Projects several GIS coordintates ROIs (polygons) to all images

back2raw_crs(points_xyz[, ignore, log])

Projs one GIS coordintates ROI (polygon) to all images

change_photo_folder(raw_img_folder[, ...])

Change the folder path of raw images

get_photo_position([to_crs, refresh])

Get all photos' center geo position (on given CRS)

open_chunk(chunk_id[, project_path])

switch to the other chunk, or chunk in a new project

open_project(project_path[, chunk_id])

Open a new 3D reconstructin project to overwritting current project.

show_photo_folder()

A function to check the original photo path

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

project_folder

the folder contains the metashape project (.psx) files

project_name

the metashape project (file) name.

chunk_id

the chunk that be picked as for easyidp (this class, only deal with one chunk in metashape project)

raw_img_folder

the folder that contains the origial images

label

project / chunk name

meta

project meta information

enabled

whether this project is activated, (often for Metashape), <class 'bool'>

sensors

the container for all sensors in this project (camera model), <class 'easyidp.Container'>, a dict-like object consisted by Sensor

photos

the container for all photos used in this project (images), <class 'easyidp.Container'>, a dict-like object consisted by Photo

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 json files and parts of ROI on raw images, by default None

  • 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()

>>> ms = idp.Metashape(project_path=lotus.metashape.project)

>>> 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 = ms.back2raw(roi)
{
    'N1W1': {
        'DJI_0478.JPG':
            array([[  14.96726711, 1843.13937997],
                   [  38.0361733 , 1568.36113526],
                   [ 320.25420037, 1584.28772847],
                   [ 297.16110119, 1859.05913936],
                   [  14.96726711, 1843.13937997]])
        'DJI_0479':
            array([...])
        ...
    }
    'N1W2': {...}   # output of `back2raw_crs()`
}
back2raw_crs(points_xyz, ignore=None, log=False)#

Projs one GIS coordintates ROI (polygon) to all images

参数:
  • points_hv (ndarray (nx3)) – The 3D coordinates of polygon vertexm, in CRS coordinates

  • 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 that key = img_name and values= pixel coordinate

返回类型:

dict,

示例

Data preparation

>>> import easyidp as idp

>>> ms = idp.Metashape(test_data.metashape.lotus_psx)
>>> dsm = idp.GeoTiff(test_data.metashape.lotus_dsm)
>>> ms.crs = dsm.crs

>>> 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]
... ])

..caution:: specifying the CRS of metashape project (ms.crs = dsm.crs) is required before doing backward projection calculation

Then use this function to find the previous ROI positions on the raw images:

>>> out_dict = ms.back2raw_crs(plot)

>>> out_dict["DJI_0478"]
array([[   2.03352333, 1474.90817792],
       [  25.04572582, 1197.08827224],
       [ 311.99971438, 1214.81701547],
       [ 288.88669685, 1492.59824542],
       [   2.03352333, 1474.90817792]])
change_photo_folder(raw_img_folder, check_img_existance=True)#

Change the folder path of raw images

参数:
  • raw_img_folder (str | dict) –

    The new folder path contains raw image folder.

    If type == str :

    replace the root string directly.

    if type == dict(not implemented)

    e.g. {‘path/to/flight1/’: ‘new/path/to/flight1’, }

  • check_img_existance (bool) – Ignore the missing photos when set to False, suitable for testing project with just a few images, to avoid the FileNotFoundError

chunk_id#

the chunk that be picked as for easyidp (this class, only deal with one chunk in metashape project)

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 default

    • True : recalculate the photo position

返回:

The dictionary contains “photo.label”: [x, y, z] coordinates

返回类型:

dict

示例

Data prepare

>>> import numpy as np
>>> np.set_printoptions(suppress=True)

>>> import easyidp as idp

>>> lotus = idp.data.Lotus()
>>> ms = idp.Metashape(project_path=lotus.metashape.project)

Then use this function to get the photo position in 3D world:

>>> out = ms.get_photo_position()
{
    'DJI_0422': array([139.54053245,  35.73458169, 130.09433649]),
    'DJI_0423': array([139.54053337,  35.73458315, 129.93437641]),
    ...
}

小心

by default, if not specifying the CRS of metashape project, it will return in default CRS (epsg: 4326) -> (lon, lat, height), if need turn to the same coordinate like DOM/DSM, please specify the CRS first

>>> dom = idp.GeoTiff(lotus.metashape.dom)
>>> ms.crs = dom.crs

>>> out = ms.get_photo_position()
{
    'DJI_0422': array([ 368017.73174354, 3955492.1925972 ,     130.09433649]),
    'DJI_0423': array([ 368017.81717717, 3955492.35300323,     129.93437641]),
    ...
}
label#

project / chunk name

meta#

project meta information

open_chunk(chunk_id, project_path=None)#

switch to the other chunk, or chunk in a new project

参数:
  • chunk_id (int or str) – The chunk id or name(label) want to open

  • project_path (str, optional) – The new metashape project file to open, like “xxxx.psx”,, by default None, means swtich inside current metashape project

示例

Data prepare

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

>>> ms = idp.Metashape(test_data.metashape.multichunk_psx)
<'multichunk.psx' easyidp.Metashape object with 4 active chunks>

  id  label
----  --------------
-> 1  multiple_bbb
   2  multiple_aaa
   3  multiple_aaa_1
   4  multiple_aaa_2

Then switch from chunk 1 to chunk 4, by id or by label:

>>> ms.open_chunk('4')
# or
>>> ms.open_chunk('multiple_aaa_2')

>>> ms
<'multichunk.psx' easyidp.Metashape object with 4 active chunks>

  id  label
----  --------------
   1  multiple_bbb
   2  multiple_aaa
   3  multiple_aaa_1
-> 4  multiple_aaa_2
open_project(project_path, chunk_id=None)#

Open a new 3D reconstructin project to overwritting current project.

参数:
  • project_path (str, optional) – The pix4d project file to open, like “xxxx.psx”, or “xxxx” without suffix.

  • chunk_id (int or str, optional) – The chunk id or name(label) want to open, by default None, open the first chunk.

示例

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

>>> ms = idp.Metashape()
<Empty easyidp.Metashape object>

>>> ms.open_project(test_data.metashape.lotus_psx)
<'Lotus.psx' easyidp.Metashape object with 1 active chunks>

  id  label
----  -------
-> 0  Chunk 1
photos#

the container for all photos used in this project (images), <class 'easyidp.Container'>, a dict-like object consisted by Photo

project_folder#

the folder contains the metashape project (.psx) files

project_name#

the metashape project (file) name.

raw_img_folder#

the folder that contains the origial images

sensors#

the container for all sensors in this project (camera model), <class 'easyidp.Container'>, a dict-like object consisted by Sensor

show_photo_folder()#

A function to check the original photo path

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

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 = ms.back2raw(roi)
{
    'N1W1': {
        'DJI_0478.JPG':
            array([[  14.96726711, 1843.13937997],
                   [  38.0361733 , 1568.36113526],
                   [ 320.25420037, 1584.28772847],
                   [ 297.16110119, 1859.05913936],
                   [  14.96726711, 1843.13937997]])
        'DJI_0479':
            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 = ms.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 = ms.sort_img_by_distance(
...     out_all, roi,
...     num=1   # only keep the 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]