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

Parameters:
  • 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

Example

>>> 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

Caution

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

Parameters:
  • 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

Example

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

Parameters:
  • 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

Returns:

a dictionary that key = img_name and values= pixel coordinate

Return type:

dict,

Example

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

Parameters:
  • 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)

Parameters:
  • 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

Returns:

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

Return type:

dict

Example

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

Caution

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

Parameters:
  • 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

Example

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.

Parameters:
  • 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.

Example

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

Parameters:
  • 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

Example

>>> 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

Parameters:
  • 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

Returns:

the same structure as output of roi.back2raw(…)

Return type:

dict

Example

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]