コード例 #1
0
ファイル: metashape.py プロジェクト: friedrichknuth/hsfm
def oc32dem(project_name, output_path, split_in_blocks=False, resolution=2):

    import Metashape

    doc = Metashape.Document()
    doc.open(output_path + project_name + ".psx")
    doc.read_only = False

    chunk = doc.chunk

    chunk.buildDem(source_data=Metashape.DenseCloudData,
                   interpolation=Metashape.DisabledInterpolation)

    doc.save()

    chunk.exportRaster(output_path + project_name + "_DEM.tif",
                       source_data=Metashape.ElevationData,
                       image_format=Metashape.ImageFormatTIFF,
                       format=Metashape.RasterFormatTiles,
                       nodata_value=-32767,
                       save_kml=False,
                       save_world=False,
                       split_in_blocks=split_in_blocks,
                       resolution=resolution)
コード例 #2
0
def align_cameras(chunk, min_latitude, min_longitude):
    if chunk.transform.scale is None:
        chunk.transform.scale = 1
        chunk.transform.rotation = ps.Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
        chunk.transform.translation = ps.Vector([0, 0, 0])

    i, j, k = get_chunk_vectors(min_latitude, min_longitude)  # i || North
    estimate_rotation_matrices(chunk, i, j)

    for c in chunk.cameras:
        if c.transform is not None:
            continue

        location = c.reference.location
        if location is None:
            continue
        chunk_coordinates = wgs_to_chunk(chunk, location)
        fi = c.reference.rotation.x + 90
        fi = math.radians(fi)
        roll = math.radians(c.reference.rotation.z)
        pitch = math.radians(c.reference.rotation.y)

        roll_mat = ps.Matrix([[1, 0, 0], [0,
                                          math.cos(roll), -math.sin(roll)],
                              [0, math.sin(roll),
                               math.cos(roll)]])
        pitch_mat = ps.Matrix([[math.cos(pitch), 0,
                                math.sin(pitch)], [0, 1, 0],
                               [-math.sin(pitch), 0,
                                math.cos(pitch)]])
        yaw_mat = ps.Matrix([[math.cos(fi), -math.sin(fi), 0],
                             [math.sin(fi), math.cos(fi), 0], [0, 0, 1]])

        r = roll_mat * pitch_mat * yaw_mat

        ii = r[0, 0] * i + r[1, 0] * j + r[2, 0] * k
        jj = r[0, 1] * i + r[1, 1] * j + r[2, 1] * k
        kk = r[0, 2] * i + r[1, 2] * j + r[2, 2] * k

        c.transform = ps.Matrix([[ii.x, jj.x, kk.x, chunk_coordinates[0]],
                                 [ii.y, jj.y, kk.y, chunk_coordinates[1]],
                                 [ii.z, jj.z, kk.z, chunk_coordinates[2]],
                                 [0, 0, 0, 1]])
コード例 #3
0
		doc.save()
		chunk = doc.chunk
		chunk.buildDem(source_data=Metashape.DenseCloudData, interpolation=Metashape.EnabledInterpolation)
			
		doc.save()
	if chunk.orthomosaic is None:
		chunk.buildOrthomosaic(surface_data=Metashape.ElevationData, blending_mode=Metashape.MosaicBlending)

	doc.save()


if __name__ == '__main__':


	app = Metashape.Application()
	doc = Metashape.app.document
	
	project_path = Metashape.app.getSaveFileName()
	if project_path[-4:].lower() != ".psx":
		project_path += ".psx"
	doc.save(project_path)

	chunk_list = doc.chunks
	for chunk in chunk_list:
		doc.chunk = chunk

		out_crs = Metashape.app.getCoordinateSystem( 'choose CRS like your GCP', chunk.crs )
		convertCRS(chunk, out_crs)

		GCP = Metashape.app.getBool(label='If yot have GCP select "yes", program will BREAK to give you import GCP after align photo done. Select "No" program will run buil all finish.')
コード例 #4
0
ファイル: Activate.py プロジェクト: UWStout/PARSECScripts
import Metashape

# Use this to create a metashape.lic file for running headless python scripts
Metashape.License().activate("AAAAA-BBBBB-CCCCC-DDDDD-EEEEE")
コード例 #5
0
import Metashape
import os
print(Metashape.app.enumGPUDevices())
Metashape.app.gpu_mask = 1
if not Metashape.app.activated:
    raise ('No license')
doc = Metashape.Document()
todo_list = range(20, 30)
pic_dir = ''

for num in todo_list:
    chunk = doc.addChunk()
    chunk.label = "pic_{}".format(num)
    path_photos = pic_dir + "pic_{}".format(num)
    print(path_photos)

    image_list = os.listdir(path_photos)
    chunk.addPhotos([
        path_photos + '/' + x for x in image_list
        if x.split('.')[-1].lower() == 'jpg'
    ])

    for carmera in chunk.cameras:
        if "GOPR" in carmera.label:
            carmera.sensor.type = Metashape.Sensor.Type.Fisheye

    for frame in chunk.frames:
        frame.matchPhotos(accuracy=Metashape.HighestAccuracy,
                          keypoint_limit=40000,
                          tiepoint_limit=40000)  # HighestAccuracy
コード例 #6
0
def main() -> bool:
    """Main function for handling align and delete

    This will create a new chunk, add and align photos, create tie points, estimate camera locations and delete all tie points outside bounding box.
    Then it will delete pixels above a 0.5 reprojection error.

    Returns
    -------
    bool
        False if issue with photo paths and True if align and delete completed
    """

    path_photos, path_export = prompt_path()

    if path_photos == "" or path_export == "":
        return False

    # logging
    logger = logging.getLogger()
    logger.handlers.clear()
    f_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
    f_handler = logging.FileHandler(
        filename=path_photos + divider + "align_and_delete.log", mode="a"
    )
    f_handler.setFormatter(f_formatter)
    logger.addHandler(f_handler)
    logger.setLevel(logging.DEBUG)

    # get rid of pesky files like .DS_Store
    fold_list = filter(
        lambda x: x[0] != "." and x[-3::] != "log", os.listdir(path_photos)
    )

    logger.info("starting align_and_delete")

    for folder in fold_list:
        logger.info(folder)
        if not os.path.isfile(folder):
            # loading images
            folderPath = path_photos + divider + folder
            if os.path.isfile(folderPath):  # skip because it should be folder not file
                continue
            photo_list = []
            # getting rid of pesky files like .DS_Store
            image_list = filter(lambda x: x[0] != ".", os.listdir(folderPath))
            for photo in image_list:
                if ("jpg" or "jpeg") in photo.lower():
                    photo_list.append(os.path.join(folderPath, photo))
            # only runs program on folder with photos in it (photo_list in chunk.addPhotos(photo_list) can't be empty)
            if not photo_list:
                print("found non photo folder")
                logger.info("found non photo folder")
                continue
            doc = meta.Document()
            doc.save(path_export + divider + folder + ".psx")

            try:
                chunk = doc.addChunk()
                chunk.addPhotos(photo_list)
                # align photos
                chunk.matchPhotos(
                    downscale=DOWNSCALE,
                    generic_preselection=GENERIC_PRESELECTION,
                    filter_mask=False,
                    keypoint_limit=KEYPOINTS,
                    tiepoint_limit=TIEPOINTS,
                )
                chunk.alignCameras()
                chunk = doc.chunks[-1]

                # delete points outside bounding box
                # https://www.agisoft.com/forum/index.php?topic=9030.0
                R = chunk.region.rot  # Bounding box rotation matrix
                C = chunk.region.center  # Bounding box center vertor
                size = chunk.region.size
                if not (chunk.point_cloud and chunk.enabled):
                    continue
                elif not chunk.point_cloud.points:
                    continue
                for point in chunk.point_cloud.points:
                    if point.valid:
                        v = point.coord
                        v.size = 3
                        v_c = v - C
                        v_r = R.t() * v_c
                        if abs(v_r.x) > abs(size.x / 2.0):
                            point.valid = False
                        elif abs(v_r.y) > abs(size.y / 2.0):
                            point.valid = False
                        elif abs(v_r.z) > abs(size.z / 2.0):
                            point.valid = False
                        else:
                            continue

                # Points outside the region were removed. Read reprojection error and delete any 0.5 or greater
                f = meta.PointCloud.Filter()
                f.init(chunk, criterion=meta.PointCloud.Filter.ReprojectionError)
                f.removePoints(THRESHOLD)
                doc.save()
                print("completed align_and_delete for:", folder)
                logger.info(folder + ": completed align_and_delete")

            except RuntimeError as r_err:
                logger.error(folder + ": " + str(r_err))
    return True
コード例 #7
0
def align_two_point_clouds(points1_source,
                           points2_target,
                           scale_ratio=None,
                           target_resolution=None,
                           no_global_alignment=False,
                           preview_intermidiate_alignment=True):
    # For example let:
    #  - points2_target - tree with height1=10 and resolution1=0.1 (in its coordinates system)
    #  - points2_target - the same tree but with height2=50 (because of another coordinates system) and resolution2=1.0
    # Then:
    #  - scale_ratio should be height2/height1=50/10=5 or (if scale_ratio=None) it will be guessed based on convex hulls (this works good only for closed objects without noise - like furniture object or house without ground surface around it)
    #  - target_resolution=resolution2=1.0 or (if target_resolution=None) it will be guessed as rough average distance between two points
    #
    # So if you want to align two models/point clouds with not-100% overlap (or for not closed objects) - you should measure and specify scale ratio
    # (note that between LIDAR point clouds scale ratio is mostly 1.0)

    assert (isinstance(points1_source, np.ndarray)
            and isinstance(points2_target, np.ndarray))
    assert (points1_source.shape[1] == points2_target.shape[1] == 3)
    v1, v2 = points1_source, points2_target

    c1 = np.mean(v1, axis=0)
    c2 = np.mean(v2, axis=0)
    if no_global_alignment:
        c1[:] = 0.0
        c2[:] = 0.0

    v1 = v1 - c1
    v2 = v2 - c2

    if scale_ratio is None:
        if no_global_alignment:
            scale_ratio = 1.0
        else:
            print("Warning! No scale ratio!")
            print(
                "It will be estimated based on convex hulls of point clouds/models and so alignment may fail if object is not closed!"
            )
            print(
                "So if alignment will fail - please manually measure and specify scale ratio!"
            )
            start = time.time()
            v1_subsampled = subsample_points(v1, 100000)
            v2_subsampled = subsample_points(v2, 100000)
            hull_size1 = estimate_convex_hull_size(v1_subsampled)
            hull_size2 = estimate_convex_hull_size(v2_subsampled)
            scale_ratio = hull_size2 / hull_size1
            print("    scale_ratio={} (size1={}, size2={})".format(
                scale_ratio, hull_size1, hull_size2))
            print("    estimated in {} s".format(time.time() - start))
    if target_resolution is None:
        print("Warning! No target resolution!")
        print(
            "It will be estimated based on rough average distance between points!"
        )
        start = time.time()
        v1_subsampled = subsample_points(v1, 1000)
        source_resolution = 1.5 * estimate_resolution(v1_subsampled) / np.sqrt(
            len(v1) / len(v1_subsampled)) * scale_ratio
        v2_subsampled = subsample_points(v2, 1000)
        target_resolution = 1.5 * estimate_resolution(v2_subsampled) / np.sqrt(
            len(v2) / len(v2_subsampled))
        resolution = np.max([source_resolution, target_resolution])
        print(
            "    target_resolution={} (resolution1={}, resolution2={})".format(
                resolution, source_resolution, target_resolution))
        print("    estimated in {} s".format(time.time() - start))
        target_resolution = resolution

    print("scale_ratio={} target_resolution={}".format(scale_ratio,
                                                       target_resolution))
    Metashape.app.update()

    v1 = v1 * scale_ratio

    stage = 0
    total_stages = 2 if no_global_alignment else 3

    if no_global_alignment:
        transformation = np.eye(4)
        if preview_intermidiate_alignment:
            print("Initial objects shown!")
            draw_registration_result(v1, v2, title="Initial alignment")
    else:
        stage += 1
        print("{}/{}: Global registration...".format(stage, total_stages))
        start = time.time()
        source_down1, target_down1, global_registration_result = global_registration(
            v1, v2, global_voxel_size=64.0 * target_resolution)
        print("    estimated in {} s".format(time.time() - start))
        Metashape.app.update()
        if preview_intermidiate_alignment:
            print("{}/{}: Global registration shown!".format(
                stage, total_stages))
            draw_registration_result(source_down1,
                                     target_down1,
                                     global_registration_result.transformation,
                                     title="Initial global alignment")
        transformation = global_registration_result.transformation

    downscale1 = 8.0
    stage += 1
    print("{}/{}: Coarse ICP registration...".format(stage, total_stages))
    start = time.time()
    icp_voxel_size1 = downscale1 * target_resolution
    source_down1 = downscale_point_cloud(to_point_cloud(v1), icp_voxel_size1)
    target_down1 = downscale_point_cloud(to_point_cloud(v2), icp_voxel_size1)
    icp_result1 = icp_registration(source_down1,
                                   target_down1,
                                   voxel_size=icp_voxel_size1,
                                   transform_init=transformation,
                                   max_iterations=100)
    print("    estimated in {} s".format(time.time() - start))
    Metashape.app.update()
    if preview_intermidiate_alignment:
        print("{}/{}: Coarse ICP registration shown!".format(
            stage, total_stages))
        draw_registration_result(source_down1,
                                 target_down1,
                                 icp_result1.transformation,
                                 title="Intermidiate ICP alignment")
    transformation = icp_result1.transformation

    downscale2 = 1.0
    stage += 1
    print("{}/{}: Fine ICP registration...".format(stage, total_stages))
    start = time.time()
    icp_voxel_size2 = downscale2 * target_resolution
    icp_result2 = icp_registration(to_point_cloud(v1),
                                   to_point_cloud(v2),
                                   voxel_size=icp_voxel_size2,
                                   transform_init=transformation,
                                   max_iterations=100)
    print("    estimated in {} s".format(time.time() - start))
    Metashape.app.update()
    if preview_intermidiate_alignment:
        print("{}/{}: Fine ICP registration shown!".format(
            stage, total_stages))
        draw_registration_result(v1,
                                 v2,
                                 icp_result2.transformation,
                                 title="Resulting alignment")
    transformation = icp_result2.transformation

    T1 = np.diag([1.0, 1.0, 1.0, 1.0])
    T1[:3, 3] = -c1.reshape(3)

    S = np.diag([scale_ratio, scale_ratio, scale_ratio, 1.0])

    T2 = np.diag([1.0, 1.0, 1.0, 1.0])
    T2[:3, 3] = c2.reshape(3)

    M = np.dot(T2, np.dot(transformation, np.dot(S, T1)))
    M = Metashape.Matrix(M)
    print("Estimated transformation matrix:")
    print(M)
    Metashape.app.update()
    return M
コード例 #8
0
def removeLicense():   
    Metashape.License().deactivate(key)
    for licence in licenses:
        os.remove(licence.as_posix())   
コード例 #9
0
def update_boundbox():
    doc = Metashape.app.document
    chunk = doc.chunk  # This points to the first chunk!

    print(banner1, "Updating boundbox using markers...")

    #ground targets for finding horizontal center
    '''p0 = "target 1"  
    p1 = "target 8"  '''
    #below: special case for strawberry
    p0 = "target 1"
    p1 = "target 19"

    fp0 = fp1 = 0

    #setting for Y up, Z forward -> needed for mixamo/unity

    c = 0

    #find center points (my way)
    c1 = 0
    c2 = 0

    print(p0, p1)
    for m in chunk.markers:
        if m.label == p0:
            c1 = c
            fp0 = 1
        elif m.label == p1:
            c2 = c
            fp1 = 1
        c = c + 1

    #check markers found (my way)
    if fp0 and fp1:
        print("Found all markers")
        print(c1, c2)
        chunk.updateTransform()
    else:
        print("Error: not all markers found")
        print(fp0, fp1)

    newregion = chunk.region

    T = chunk.transform.matrix
    #v_t = T * Metashape.Vector( [0,0,0,1] )
    m = Metashape.Matrix().Diag([1, 1, 1, 1])

    m = m * T
    s = math.sqrt(m[0, 0]**2 + m[0, 1]**2 + m[0, 2]**2)  #scale factor
    R = Metashape.Matrix([[m[0, 0], m[0, 1], m[0, 2]],
                          [m[1, 0], m[1, 1], m[1, 2]],
                          [m[2, 0], m[2, 1], m[2, 2]]])
    R = R * (1. / s)
    newregion.rot = R.t()

    # Calculate center point of the bounding box, by taking the average of 2 left and 2 right markers
    mx = (chunk.markers[c1].position + chunk.markers[c2].position) / 2  #my way

    print('x', chunk.markers[c2].position[0], chunk.markers[c1].position[0])
    print('y', chunk.markers[c2].position[1], chunk.markers[c1].position[1])
    box_X_length = math.fabs(
        chunk.markers[c2].position[0] - chunk.markers[c1].position[0]
    )  #x-axis. may return negative number, so absoluted
    box_Y_length = math.fabs(
        chunk.markers[c2].position[1] - chunk.markers[c1].position[1]
    )  #y-axis. may return negative number, so absoluted

    print('lengths:', box_X_length, box_Y_length)

    '''if box_Y_length > box_X_length:
        swap_length = box_X_length
        box_X_length = box_Y_length
        box_Y_length = swap_length''' #disabled for strawberry

    box_Z_length = box_Y_length  #same as Y length #chunk.markers[c2].position[2] - chunk.markers[c1].position[2] #z-axis

    print('lengths:', box_X_length, box_Y_length, box_Z_length)

    Z_ratio = 4  #my way. Change this to adjust box height (default=1)

    mx = Metashape.Vector(
        [mx[0], mx[1], mx[2] + (0 * Z_ratio * box_Z_length / 2)]
    )  #adjusting the zratio thing shifts the whole boundbox on a diagonal. not so easy. set to 0 for now.
    newregion.center = mx

    newregion.size = Metashape.Vector(
        [box_X_length, box_Y_length, box_Z_length * Z_ratio])  #my way

    chunk.region = newregion
    chunk.updateTransform()  #disabled for strawberry

    print("Bounding box should be aligned now")
import os
import Metashape, sys

#####################################################################################

##this line is pointing to the path of drone photos that you specified in the Command Prompt code
path_photos = sys.argv[1]

# set new working directory to be in working folder set in command line
os.chdir(path_photos)

##Define: PhotoScan Project File
MetashapeProjectFile = ".\\Metashape_Project.psx"
##get main app objects
doc = Metashape.Document()
doc.save(MetashapeProjectFile)

app = Metashape.Application()

## create chunk
chunk = doc.addChunk()

## loading images
image_list = os.listdir(path_photos)
photo_list = list()
for photo in image_list:
    if ("jpg" or "jpeg" or "JPG" or "JPEG") in photo.lower():
        photo_list.append(path_photos + "/" + photo)
chunk.addPhotos(photo_list)
コード例 #11
0
    def align(self):
        print("Script started...")

        (key1, isModel1, label1) = self.objects[self.fromObject.currentIndex()]
        (key2, isModel2, label2) = self.objects[self.toObject.currentIndex()]

        print("Aligning {} to {}...".format(label1, label2))

        region_size = Metashape.app.document.chunk.region.size
        try:
            tmp1 = tempfile.NamedTemporaryFile(delete=False)
            tmp1.close()
            tmp2 = tempfile.NamedTemporaryFile(delete=False)
            tmp2.close()
            Metashape.app.document.chunk.region.size = Metashape.Vector([0.0, 0.0, 0.0])
            for (key, isModel, filename) in [(key2, isModel2, tmp2.name), (key1, isModel1, tmp1.name)]:
                if isModel:
                    self.chunk.model = None
                    for model in self.chunk.models:
                        if model.key == key:
                            self.chunk.model = model
                    assert(self.chunk.model is not None)
                    self.chunk.exportModel(path=filename, binary=True,
                                      save_texture=False, save_uv=False, save_normals=False, save_colors=False,
                                      save_cameras=False, save_markers=False, save_udim=False, save_alpha=False,
                                      save_comment=False,
                                      format=Metashape.ModelFormatPLY)
                else:
                    self.chunk.dense_cloud = None
                    for dense_cloud in self.chunk.dense_clouds:
                        if dense_cloud.key == key:
                            self.chunk.dense_cloud = dense_cloud
                    assert(self.chunk.dense_cloud is not None)
                    self.chunk.exportPoints(path=filename,
                                       source_data=Metashape.DenseCloudData, binary=True,
                                       save_normals=False, save_colors=False, save_classes=False, save_confidence=False,
                                       save_comment=False,
                                       format=Metashape.PointsFormatPLY)

            v1 = read_ply(tmp1.name)
            v2 = read_ply(tmp2.name)
            os.remove(tmp1.name)
            os.remove(tmp2.name)
            Metashape.app.document.chunk.region.size = region_size
        except:
            os.remove(tmp1.name)
            os.remove(tmp2.name)
            Metashape.app.document.chunk.region.size = region_size
            raise

        print("Vertices number: {}, {}".format(len(v1), len(v2)))

        scale_ratio = None if self.edtScaleRatio.text() == '' else float(self.edtScaleRatio.text())
        target_resolution = None if self.edtTargetResolution.text() == '' else float(self.edtTargetResolution.text())
        no_global_alignment = self.chkUseInitialAlignment.isChecked()
        preview_intermidiate_alignment = self.chkPreview.isChecked()

        M12 = align_two_point_clouds(v1, v2, scale_ratio, target_resolution, no_global_alignment, preview_intermidiate_alignment)

        if isModel1:
            assert(self.chunk.model.key == key1)
            try:
                matrix = self.chunk.transform.matrix
                if self.chunk.model.transform is not None:
                    matrix = self.chunk.model.transform * matrix
                self.chunk.model.transform = Metashape.Matrix(M12) * matrix
            except AttributeError:
                nvertices = len(self.chunk.model.vertices)
                matrix = Metashape.Matrix(M12)
                vertices = self.chunk.model.vertices
                for i in range(nvertices):
                    vertices[i].coord = matrix.mulp(vertices[i].coord)
                vertices.resize(nvertices)
            self.chunk.model = None
        else:
            assert(self.chunk.dense_cloud.key == key1)
            matrix = self.chunk.transform.matrix
            if self.chunk.dense_cloud.transform is not None:
                matrix = self.chunk.dense_cloud.transform * matrix
            self.chunk.dense_cloud.transform = Metashape.Matrix(M12) * matrix
            self.chunk.dense_cloud = None

        print("Script finished!")
        self.reject()
コード例 #12
0
def create_footprints():
    """
    Creates four-vertex shape for each aligned camera (footprint) in the active chunk
    and puts all these shapes to a new separate shape layer
    """

    doc = Metashape.app.document
    if not len(doc.chunks):
        raise Exception("No chunks!")

    print("Script started...")
    chunk = doc.chunk

    if not chunk.shapes:
        chunk.shapes = Metashape.Shapes()
        chunk.shapes.crs = chunk.crs
    T = chunk.transform.matrix
    footprints = chunk.shapes.addGroup()
    footprints.label = "Footprints"
    footprints.color = (30, 239, 30)

    if chunk.model:
        surface = chunk.model
    elif chunk.dense_cloud:
        surface = chunk.dense_cloud
    else:
        surface = chunk.point_cloud

    for camera in chunk.cameras:
        if not camera.transform:
            continue  # skipping NA cameras

        sensor = camera.sensor
        corners = list()
        for i in [[0, 0], [sensor.width - 1, 0],
                  [sensor.width - 1, sensor.height - 1],
                  [0, sensor.height - 1]]:
            corners.append(
                surface.pickPoint(
                    camera.center,
                    camera.transform.mulp(
                        sensor.calibration.unproject(Metashape.Vector(i)))))
            if not corners[-1]:
                corners[-1] = chunk.point_cloud.pickPoint(
                    camera.center,
                    camera.transform.mulp(
                        sensor.calibration.unproject(Metashape.Vector(i))))
            if not corners[-1]:
                break
            corners[-1] = chunk.crs.project(T.mulp(corners[-1]))

        if not all(corners):
            print("Skipping camera " + camera.label)
            continue

        if len(corners) == 4:
            shape = chunk.shapes.addShape()
            shape.label = camera.label
            shape.attributes["Photo"] = camera.label
            shape.type = Metashape.Shape.Type.Polygon
            shape.group = footprints
            shape.vertices = corners
            shape.has_z = True

    Metashape.app.update()
    print("Script finished!")
コード例 #13
0
argumentFile.close()

# Strip new line character at the end of the path name
addrDir = addrDir.strip('\n')
# Combine the path with the name of the address folder containing the IP Address (address.txt) and store it in the variable (addrDir)
# It will always be named address.txt becuase that is the name designated in initServerNode2.sh
addrDir = (addrDir + "/address.txt")

# Opens the address file
addr_file = open(addrDir, "r")
# Reads the IP Address and stores it in a variable
addr_str = addr_file.readline()
# Closes the address file
addr_file.close()
# Create a Metashape Network Client object named 'client'
client = Metashape.NetworkClient()

# Combines the path to where the project is to be saved with the name of the project in order to save, open, and access the project in the script
filePath = (path + docName)
# Create a Metashape Document object and name it 'doc'
doc = Metashape.app.document
# Set the read only variable to false in order to enable editing
doc.read_only = False
# Creates the project because this is the first processing task to be done
doc.save(filePath)
# Add a chunk to the project
chunk = doc.addChunk()
# Create a list to store our photos
photos = []
# Using glob, grab all the photos from the photos folder and add them to the list
for photo in glob.glob(sourceFolderPath):
コード例 #14
0
def mark_Images(chunk, GCPpath, path):
    # Load the GCP Locations
    try:
        chunk.importReference(GCPpath,
                              format=PhotoScan.ReferenceFormatCSV,
                              delimiter=",",
                              columns="nxyz",
                              create_markers=True,
                              crs=PhotoScan.CoordinateSystem("EPSG::32611"))
    #, crs=PhotoScan.CoordinateSystem("EPSG::32611")
    #, threshold = 3
    except:
        print("GCP Coordinate File Not Found.")

    file = open(path, "rt")  #input file
    eof = False
    line = file.readline()
    line = file.readline()
    if not len(line):
        eof = True

    while not eof:
        sp_line = line.rsplit(",", 4)  #splitting read line by five parts
        print(sp_line)
        y = float(
            sp_line[4])  #y- coordinate of the current projection in pixels
        x = float(
            sp_line[3])  #x- coordinate of the current projection in pixels
        path = sp_line[2]  #camera label
        marker_name = sp_line[1]  #marker label

        flag = 0
        for i in range(len(chunk.cameras)):
            if chunk.cameras[i].label == path:  #searching for the camera
                print("found camera")
                for j in range(
                        len(chunk.markers)
                ):  #searching for the marker (comparing with all the marker labels in chunk)
                    if chunk.markers[j].label == marker_name:
                        print("Found Marker")
                        chunk.markers[j].projections[
                            chunk.cameras[i]] = PhotoScan.Marker.Projection(
                                PhotoScan.Vector([x, y]), True
                            )  #setting up marker projection of the correct photo)
                        flag = 1
                        break

                if not flag:
                    print("Not Flag")
                    marker = chunk.addMarker()
                    marker.label = marker_name
                    marker.projections[
                        chunk.cameras[i]] = PhotoScan.Marker.Projection(
                            PhotoScan.Vector([x, y]), True)
                break

        line = file.readline()  #reading the line from input file
        if not len(line):
            eof = True
            break  # EOF

    ## Disable GCP-t and TCPs
    for marker in chunk.markers:
        # Set the Marker Accuracy
        marker.reference.accuracy = PhotoScan.Vector([3, 3, 3])

        if marker.label == 'Hhh':
            marker.reference.enabled = False
        elif marker.label == '100':
            marker.reference.enabled = False
        elif marker.label == 'gcp0t':
            marker.reference.enabled = False
        elif marker.label == 'gcp0-t':
            marker.reference.enabled = False
        elif marker.label == 'gcp1t':
            marker.reference.enabled = False
        elif marker.label == 'gcp2t':
            marker.reference.enabled = False
        elif marker.label == 'gcp3t':
            marker.reference.enabled = False
        elif marker.label == 'gcp4t':
            marker.reference.enabled = False
        elif marker.label == 'tcp1':
            marker.reference.enabled = False
        elif marker.label == 'tcp2':
            marker.reference.enabled = False
        elif marker.label == 'tcp3':
            marker.reference.enabled = False
        elif marker.label == 'tcp4':
            marker.reference.enabled = False
        else:
            pass

    file.close()
    return
コード例 #15
0
def AlignPhoto(locFold, ProcessDate, typeFolder, chunk, Accuracy, Key_Limit,
               Tie_Limit, QualityFilter, QualityCriteria, new_crs):
    # Set the camera and marker projections
    wgs_84 = PhotoScan.CoordinateSystem("EPSG::4326")
    for camera in chunk.cameras:
        if camera.reference.location:
            camera.reference.location = PhotoScan.CoordinateSystem.transform(
                camera.reference.location, wgs_84, new_crs)

    # Filter out poor qualtiy Images
    if QualityFilter:
        if chunk.cameras[0].meta['Image/Quality'] is None:
            try:
                chunk.estimateImageQuality()  # Metashape 1.5
            except:
                chunk.analyzePhotos()  # Metashape 1.6
        for camera in chunk.cameras:
            if float(camera.meta["Image/Quality"]) < QualityCriteria:
                camera.enabled = False
        ## For Multispectral camera
        # for band in [band for camera in chunk.cameras for band in camera.planes]:
        #     if float(band.meta['Image/Quality']) < QualityCriteria:
        #         band.enabled = False

    # Get list of images in chunk
    originalImgList = list()
    for camera in chunk.cameras:
        if not camera.transform:
            originalImgList.append(camera)
    originalCount = len(originalImgList)

    # Metashape 1.5
    try:
        chunk.matchPhotos(accuracy=Accuracy,
                          generic_preselection=True,
                          reference_preselection=False,
                          filter_mask=False,
                          keypoint_limit=Key_Limit,
                          tiepoint_limit=Tie_Limit)
    # Metashape 1.6
    except:
        chunk.matchPhotos(downscale=Accuracy,
                          generic_preselection=True,
                          reference_preselection=False,
                          filter_mask=False,
                          keypoint_limit=Key_Limit,
                          tiepoint_limit=Tie_Limit)
    chunk.alignCameras()

    # Realign non-aligned images
    realign_list = list()
    for camera in chunk.cameras:
        if not camera.transform:
            realign_list.append(camera)
    chunk.alignCameras(cameras=realign_list)

    realign_list = list()
    for camera in chunk.cameras:
        if not camera.transform:
            realign_list.append(camera)
    chunk.alignCameras(cameras=realign_list)

    aligned_list = list()
    for camera in chunk.cameras:
        if camera.transform:
            aligned_list.append(camera)
    align2 = len(aligned_list)

    # if more than 60% of images were aligned
    if align2 >= originalCount * 0.6:
        successAlignment = True
        # enabling rolling shutter compensation
        try:
            for sensor in chunk.sensors:
                sensor.rolling_shutter = True
        except:
            print("Error applying rolling shutter correction")

        chunk.optimizeCameras(fit_f=True,
                              fit_cx=True,
                              fit_cy=True,
                              fit_b1=False,
                              fit_b2=False,
                              fit_k1=True,
                              fit_k2=True,
                              fit_k3=True,
                              fit_k4=False,
                              fit_p1=True,
                              fit_p2=True,
                              fit_p3=False,
                              fit_p4=False,
                              adaptive_fitting=False,
                              tiepoint_covariance=False)
    else:
        successAlignment = False

    #Remove method variables
    realign_list = None
    aligned_list = None
    originalImgList = None
    originalCount = None

    return successAlignment
コード例 #16
0
    # Variable for building 3D mesh
    # Surface: Arbitrary, HeightField
    # SurfaceSource: PointCloudData, DenseCloudData, DepthMapsData
    Surface = PhotoScan.SurfaceType.Arbitrary
    SurfaceSource = PhotoScan.DataSource.DepthMapsData
    #
    # Variable for building orthomosaic
    # Since 1.4.0, users can choose performing color correction (vignetting) and balance separately.
    # Blending: AverageBlending, MosaicBlending, MinBlending, MaxBlending, DisabledBlending
    # Color_correction: True, False
    # Color_balance: True, False
    BlendingMode = PhotoScan.BlendingMode.MosaicBlending
    Color_correction = False
    Color_balance = False
    # Set the project projection
    new_crs = PhotoScan.CoordinateSystem("EPSG::32611")  #UTM11N

    #####--------------------------------------------------Processing---------------------------------------------------------------#######
    ## Define Folders to Process
    mainFold = "/SNOWDATA/SnowDrones_Processing/"
    Loc = "LDP"
    locFold = mainFold + Loc + "/"

    ## Read in the GCP RTK Target GPS coordinates
    GCP_coordFN = locFold + Loc + "_unprocessed.csv"

    ## List of dates to run code
    AllDates = [
        dI for dI in sorted(os.listdir(locFold))
        if os.path.isdir(os.path.join(locFold, dI))
    ]
コード例 #17
0
optimise_k1 = True
optimise_k2 = True
optimise_k3 = True
optimise_k4 = False
optimise_p1 = False
optimise_p2 = False
optimise_p3 = False
optimise_p4 = False

# Points are exported as floats in binary ply files for speed and size, and thus cannot represent very small changes
# in large geographic coordinates. Thus, the offset below can be set to form a local origin and ensure numerical
# precision is not lost when coordinates are saved as floats. The offset will be subtracted from point coordinates.
# [RECOMMENDED] - Leave as NaN; the script will automatically calculate and apply a suitable offset, which will be saved
# as a text file for further processing, OR edit the line to impose a specific offset of your choice -
# e.g.  pts_offset = Metashape.Vector( [266000, 4702000, 0] )
pts_offset = Metashape.Vector([NaN, NaN, NaN])

###################################   END OF SETUP   ###################################
########################################################################################
# Initialisation
filename = os.path.abspath("C:/HG_Projects/CWC_Drone_work/pia_plots/P3E1.psz")

doc = Metashape.app.document
doc.open(filename, read_only=False)
chunk = doc.chunk
# chunk = Metashape.app.document.chunk
point_proj = chunk.point_cloud.projections

# Need CoordinateSystem object, but PS only returns 'None' if an arbitrary coordinate system is being used
# thus need to set manually in this case; otherwise use the Chunk coordinate system.
if chunk.crs == None:
コード例 #18
0
def main(camera_extension, aligned_camera_threshold):

    global start_time
    start_time = time.time()

    project_filepath = get_project_filepath()
    log_file = open(project_filepath.replace('.psx', '.log'), 'w')

    # Enable all GPUs
    Metashape.app.gpu_mask = 2 ** (len(Metashape.app.enumGPUDevices())) - 1

    doc = Metashape.Document()
    if os.path.isfile(project_filepath):
        doc.open(project_filepath)  # Open exisiting project
    else:
        doc.save(project_filepath)  # Create new project

    if not doc.chunk:
        chunk = doc.addChunk()      # Add chunk if it does not already exists
    else:
        chunk = doc.chunk

    if len(chunk.cameras) == 0:
        camera_list = convert_cameras(camera_extension)
        chunk.addPhotos(camera_list)
        doc.save()

    aligned_cameras, non_aligned_cameras = get_aligned_and_non_aligned_cameras(chunk)

    if len(aligned_cameras) == 0:
        start_next_step("Match photos", log_file)
        chunk.matchPhotos(downscale = 1,                    # Image alignment accuracy = High
                          generic_preselection = True,      # Enable generic preselection
                          reference_preselection = False,   # Disable reference preselection
                          filter_mask = False,              # Disable filtering points by mask
                          mask_tiepoints = False,           # Disable applying mask filter to tie points
                          keypoint_limit = 5000,            
                          tiepoint_limit = 0,
                          keep_keypoints = False,           # Do not store keypoints in the project
                          guided_matching = False,          # Disable guided image matching
                          reset_matches = True,             # Resent current matches
                          progress = progress_print)             

        doc.save()
        
        start_time = time.time()
        start_next_step("Align photos", log_file)
        chunk.alignCameras(adaptive_fitting = True,         # Enable adaptive fitting of distortion coefficients
                           reset_alignment = True,          # Reset current alignment
                           progress = progress_print)
        doc.save()
        start_time = time.time()

        aligned_cameras, non_aligned_cameras = get_aligned_and_non_aligned_cameras(chunk)

    if (len(aligned_cameras) / len(chunk.cameras)) < aligned_camera_threshold:
        sys.exit('Unsufficient cameras aligned: {0} aligned out of {1}'.format(len(aligned_cameras),
                                                                            len(chunk.cameras)))

    start_next_step("Build dense maps", log_file)
    chunk.buildDepthMaps(downscale = 2,                 # Depth map quality = High (2)
                         filter_mode = Metashape.MildFiltering,
                         reuse_depth = False,           # Disable reuse depth maps option
                         progress = progress_print)

    doc.save()
    start_time = time.time()
    
    start_next_step("Build dense maps", log_file)
    chunk.buildDenseCloud(point_colors = True,          # Enable point colors calculation
                          point_confidence = True,      # Enable point confidence calculation
                          keep_depth = True,            # Enable store depth maps option
                          progress = progress_print)

    doc.save()
    start_time = time.time()
    
    start_next_step("Export points to PLY file", log_file)
    chunk.exportPoints(path = project_filepath.replace('.psx', '.ply'),
                       source_data = Metashape.DenseCloudData,
                       binary = True, 
                       save_normals = True, 
                       save_colors = True, 
                       save_classes = False,
                       save_confidence = True,
                       raster_transform = Metashape.RasterTransformNone,
                       colors_rgb_8bit = True,
                       format = Metashape.PointsFormatPLY,
                       split_in_blocks = False,
                       progress = progress_print)
    
    start_next_step("Export cameras positions", log_file)
    chunk.exportCameras(project_filepath.replace('.psx', '.cams.xml'))

    start_next_step("Export camera metadata", log_file)
    output_camera_metadata(project_filepath.replace('.psx', '.meta.json'), 
                           chunk)

    doc.save()
    start_time = time.time()

    log_file.close()
コード例 #19
0
global appFile
appFile = [f.as_posix() for f in list (Path(path).glob('Metashape.exe'))]


global licenses
licenses = [Path(f) for f in list (Path(path).glob('*.lic')) if Path(f).stem != 'rlm_roam']
for licence in licenses:
    os.remove(licence.as_posix())

global app
app = QtCore.QCoreApplication.instance()
global key

key = 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX'

if Metashape.app.activated == False:
    locale = QtCore.QLocale.system().name().split('_')[0]
    if locale =='fr':
        print("Activation de la clé de licence...")
    else:
        print(" Activating License Key...")
    Metashape.License().activate(key)

def removeLicense():   
    Metashape.License().deactivate(key)
    for licence in licenses:
        os.remove(licence.as_posix())   


app.aboutToQuit.connect(removeLicense)
コード例 #20
0
def build_dense_cloud(doc, log_file, run_id, cfg):
    '''
    Build depth maps and dense cloud
    '''

    ### Build depth maps

    # get a beginning time stamp for the next step
    timer2a = time.time()

    # build depth maps only instead of also building the dense cloud ##?? what does
    doc.chunk.buildDepthMaps(
        downscale=cfg["buildDenseCloud"]["downscale"],
        filter_mode=cfg["buildDenseCloud"]["filter_mode"],
        reuse_depth=cfg["buildDenseCloud"]["reuse_depth"],
        max_neighbors=cfg["buildDenseCloud"]["max_neighbors"],
        subdivide_task=cfg["subdivide_task"])
    doc.save()

    # get an ending time stamp for the previous step
    timer2b = time.time()

    # calculate difference between end and start time to 1 decimal place
    time2 = diff_time(timer2b, timer2a)

    # record results to file
    with open(log_file, 'a') as file:
        file.write(sep.join(['Build Depth Maps', time2]) + '\n')

    ### Build dense cloud

    # get a beginning time stamp for the next step
    timer3a = time.time()

    # build dense cloud
    doc.chunk.buildDenseCloud(
        max_neighbors=cfg["buildDenseCloud"]["max_neighbors"],
        keep_depth=cfg["buildDenseCloud"]["keep_depth"],
        subdivide_task=cfg["subdivide_task"],
        point_colors=True)
    doc.save()

    # get an ending time stamp for the previous step
    timer3b = time.time()

    # calculate difference between end and start time to 1 decimal place
    time3 = diff_time(timer3b, timer3a)

    # record results to file
    with open(log_file, 'a') as file:
        file.write(sep.join(['Build Dense Cloud', time3]) + '\n')

    ### Classify ground points

    if cfg["buildDenseCloud"]["classify"]:

        # get a beginning time stamp for the next step
        timer_a = time.time()

        doc.chunk.dense_cloud.classifyGroundPoints(
            max_angle=cfg["buildDenseCloud"]["max_angle"],
            max_distance=cfg["buildDenseCloud"]["max_distance"],
            cell_size=cfg["buildDenseCloud"]["cell_size"])
        doc.save()

        # get an ending time stamp for the previous step
        timer_b = time.time()

        # calculate difference between end and start time to 1 decimal place
        time_tot = diff_time(timer_b, timer_a)

        # record results to file
        with open(log_file, 'a') as file:
            file.write(sep.join(['Classify Ground Points', time_tot]) + '\n')

    ### Export points

    if cfg["buildDenseCloud"]["export"]:

        output_file = os.path.join(cfg["output_path"], run_id + '_points.las')

        if cfg["buildDenseCloud"]["classes"] == "ALL":
            # call without classes argument (Metashape then defaults to all classes)
            doc.chunk.exportPoints(path=output_file,
                                   source_data=Metashape.DenseCloudData,
                                   format=Metashape.PointsFormatLAS,
                                   crs=Metashape.CoordinateSystem(
                                       cfg["project_crs"]),
                                   subdivide_task=cfg["subdivide_task"])
        else:
            # call with classes argument
            doc.chunk.exportPoints(path=output_file,
                                   source_data=Metashape.DenseCloudData,
                                   format=Metashape.PointsFormatLAS,
                                   crs=Metashape.CoordinateSystem(
                                       cfg["project_crs"]),
                                   clases=cfg["buildDenseCloud"]["classes"],
                                   subdivide_task=cfg["subdivide_task"])

    return True
コード例 #21
0
        horizontal = get_marker(horiz[0], chunk).position - get_marker(
            horiz[1], chunk).position
        #!disabled for strawberry vertical = get_marker(vert[0], chunk).position - get_marker(vert[1], chunk).position
        #!special for strawberry
        normal = get_marker(horiz[0], chunk).position
        normal[2] = normal[2] + 1
        '''normal = vect(horizontal, vertical)
        vertical = vertical.normalized()
        horizontal = vect(vertical, normal)'''
        #!disabled for strawberry normal = vect(horizontal, vertical)
        vertical = -vect(horizontal, normal)
        horizontal = -horizontal.normalized()
        #!disabled for strawberry vertical = - vect(horizontal, normal)
        normal = -vect(horizontal, vertical)

        R = Metashape.Matrix([horizontal, vertical, normal
                              ])  #horizontal = X, vertical = Y, normal = Z

        print(R.det())
        region.rot = R.t()
        chunk.region = region
        R = chunk.region.rot  #Bounding box rotation matrix
        C = chunk.region.center  #Bounding box center vector

        if chunk.transform.matrix:
            T = chunk.transform.matrix
            s = math.sqrt(T[0, 0]**2 + T[0, 1]**2 +
                          T[0, 2]**2)  #scaling # T.scale()
            S = Metashape.Matrix().Diag([s, s, s, 1])  #scale matrix
        else:
            S = Metashape.Matrix().Diag([1, 1, 1, 1])
        T = Metashape.Matrix([[R[0, 0], R[0, 1], R[0, 2], C[0]],
コード例 #22
0
def project_setup(cfg):
    '''
    Create output and project paths, if they don't exist
    Define a project ID based on photoset name and timestamp
    Define a project filename and a log filename
    Create the project
    Start a log file
    '''

    # Make project directories (necessary even if loading an existing project because this workflow saves a new project based on the old one, leaving the old one intact
    if not os.path.exists(cfg["output_path"]):
        os.makedirs(cfg["output_path"])
    if not os.path.exists(cfg["project_path"]):
        os.makedirs(cfg["project_path"])

    ### Set a filename template for project files and output files
    ## Get the first parts of the filename (the photoset ID and location string)

    run_name = cfg["run_name"]

    ## Project file example to make: "projectID_YYYYMMDDtHHMM-jobID.psx"
    timestamp = stamp_time()
    run_id = "_".join([run_name, timestamp])
    # TODO: If there is a slurm JobID, append to time (separated with "-", not "_"). This will keep jobs initiated in the same minute distinct

    project_file = os.path.join(cfg["project_path"], '.'.join([run_id, 'psx']))
    log_file = os.path.join(cfg["output_path"],
                            '.'.join([run_id + "_log", 'txt']))
    '''
    Create a doc and a chunk
    '''

    # create a handle to the Metashape object
    doc = Metashape.Document(
    )  #When running via Metashape, can use: doc = Metashape.app.document

    # If specified, open existing project
    if cfg["load_project"] != "":
        doc.open(cfg["load_project"])
    else:
        # Initialize a chunk, set its CRS as specified
        chunk = doc.addChunk()
        chunk.crs = Metashape.CoordinateSystem(cfg["project_crs"])
        chunk.marker_crs = Metashape.CoordinateSystem(
            cfg["addGCPs"]["gcp_crs"])

    # Save doc doc as new project (even if we opened an existing project, save as a separate one so the existing project remains accessible in its original state)
    doc.save(project_file)
    '''
    Log specs except for GPU
    '''

    # log Metashape version, CPU specs, time, and project location to results file
    # open the results file
    # TODO: records the Slurm values for actual cpus and ram allocated
    # https://slurm.schedmd.com/sbatch.html#lbAI
    with open(log_file, 'a') as file:

        # write a line with the Metashape version
        file.write(sep.join(['Project', run_id]) + '\n')
        file.write(
            sep.join([
                'Agisoft Metashape Professional Version', Metashape.app.version
            ]) + '\n')
        # write a line with the date and time
        file.write(sep.join(['Processing started', stamp_time()]) + '\n')
        # write a line with CPU info - if possible, improve the way the CPU info is found / recorded
        file.write(sep.join(['Node', platform.node()]) + '\n')
        file.write(sep.join(['CPU', platform.processor()]) + '\n')
        # write two lines with GPU info: count and model names - this takes multiple steps to make it look clean in the end

    return doc, log_file, run_id
コード例 #23
0
def generate_ply(filepath, output_dir):
    """
    uses metashape to read a professor clipped file, find the 
    filepaths, add the images to a chunk, and generate a dense 
    cloud out of it

    parameters:
        filepath (str): a filepath the a professor clipped csv
        output_dir (str): a directory to where the ply generated from this file will end up

    returns:
        none
    """
    # building path to save to
    basename = os.path.basename(filepath)
    basename = os.path.splitext(basename)[0]
    filename = basename + ".ply"
    save_path = os.path.join(output_dir, filename)
    print("################################################")
    print(save_path)
    print("################################################")

    # creating a chunk to work with
    chunk = Metashape.app.document.addChunk()
    chunk.label = basename

    with open(filepath, "r") as csvfile:
        csvreader = csv.DictReader(csvfile)

        n1_paths = []
        n2_paths = []
        n3_paths = []

        for row in csvreader:
            if "gopro" not in row["filepath"].lower():
                if "n1" in row["filepath"].lower():
                    n1_paths.append(row["filepath"])

                if "n2" in row["filepath"].lower():
                    n2_paths.append(row["filepath"])

                if "n3" in row["filepath"].lower():
                    n3_paths.append(row["filepath"])

    paths = n2_paths + n3_paths + n1_paths

    # add photos from csv here
    chunk.addPhotos(paths)

    # disabling exif gps data
    for camera in chunk.cameras:
        camera.reference.enabled = False

    # creating a local coordinate system
    chunk.crs = Metashape.CoordinateSystem()

    # Perform image matching for the chunk frame.
    chunk.matchPhotos(
        accuracy=Metashape.HighAccuracy,
        generic_preselection=True,
        reference_preselection=False,
        tiepoint_limit=10000,
    )

    chunk.alignCameras()

    print("ALIGNING COORDINATE SYSTEM BOUNDING BOX")
    # https://github.com/agisoft-llc/metashape-scripts/blob/master/src/coordinate_system_to_bounding_box.py
    R = chunk.region.rot  # Bounding box rotation matrix
    C = chunk.region.center  # Bounding box center vector

    if chunk.transform.matrix:
        T = chunk.transform.matrix
        s = math.sqrt(
            T[0, 0] ** 2 + T[0, 1] ** 2 + T[0, 2] ** 2
        )  # scaling # T.scale()
        S = Metashape.Matrix().Diag([s, s, s, 1])  # scale matrix
    else:
        S = Metashape.Matrix().Diag([1, 1, 1, 1])

    T = Metashape.Matrix(
        [
            [R[0, 0], R[0, 1], R[0, 2], C[0]],
            [R[1, 0], R[1, 1], R[1, 2], C[1]],
            [R[2, 0], R[2, 1], R[2, 2], C[2]],
            [0, 0, 0, 1],
        ]
    )

    chunk.transform.matrix = (
        S * T.inv()
    )  # resulting chunk transformation matrix

    print("FINISHED ALIGNING COORDINATE SYSTEM BOUNDING BOX")

    # Generate depth maps for the chunk.
    chunk.buildDepthMaps(
        quality=Metashape.MediumQuality, filter=Metashape.AggressiveFiltering
    )

    # generating the cloud
    chunk.buildDenseCloud()

    # saving
    chunk.exportPoints(save_path)

    print("done processing. point cloud exported to " + output_dir)
コード例 #24
0
ファイル: stitch.py プロジェクト: beelabhmc/flower_map
# SurfaceSource: PointCloudData, DenseCloudData, DepthMapsData
Surface = PhotoScan.SurfaceType.Arbitrary
SurfaceSource = PhotoScan.DataSource.DepthMapsData
#
# Variable for building orthomosaic
# Since 1.4.0, users can choose performing color correction (vignetting) and balance separately.
# Blending: AverageBlending, MosaicBlending, MinBlending, MaxBlending, DisabledBlending
# Color_correction: True, False
# Color_balance: True, False
BlendingMode = PhotoScan.BlendingMode.MosaicBlending
Color_correction = True
Color_balance = False
#
#######################################################

wgs_84 = PhotoScan.CoordinateSystem("EPSG::4326")


def AlignPhoto(chunk,
               Accuracy,
               Key_Limit,
               Tie_Limit,
               QualityFilter=QualityFilter,
               QualityCriteria=QualityCriteria):
    if QualityFilter:
        if chunk.cameras[0].meta['Image/Quality'] is None:
            chunk.estimateImageQuality()
        for band in [
                band for camera in chunk.cameras for band in camera.planes
        ]:
            if float(band.meta['Image/Quality']) < QualityCriteria:
コード例 #25
0
def build_dem(doc, log_file, run_id, cfg):
    '''
    Build end export DEM
    '''

    # get a beginning time stamp for the next step
    timer5a = time.time()

    #prepping params for buildDem
    projection = Metashape.OrthoProjection()
    projection.crs = Metashape.CoordinateSystem(cfg["project_crs"])

    #prepping params for export
    compression = Metashape.ImageCompression()
    compression.tiff_big = cfg["buildDem"]["tiff_big"]
    compression.tiff_tiled = cfg["buildDem"]["tiff_tiled"]
    compression.tiff_overviews = cfg["buildDem"]["tiff_overviews"]

    if (cfg["buildDem"]["type"] == "DSM") | (cfg["buildDem"]["type"]
                                             == "both"):
        # call without classes argument (Metashape then defaults to all classes)
        doc.chunk.buildDem(source_data=Metashape.DenseCloudData,
                           subdivide_task=cfg["subdivide_task"],
                           projection=projection)
        output_file = os.path.join(cfg["output_path"], run_id + '_dsm.tif')
        if cfg["buildDem"]["export"]:
            doc.chunk.exportRaster(path=output_file,
                                   projection=projection,
                                   nodata_value=cfg["buildDem"]["nodata"],
                                   source_data=Metashape.ElevationData,
                                   image_compression=compression)
    if (cfg["buildDem"]["type"] == "DTM") | (cfg["buildDem"]["type"]
                                             == "both"):
        # call with classes argument
        doc.chunk.buildDem(source_data=Metashape.DenseCloudData,
                           classes=Metashape.PointClass.Ground,
                           subdivide_task=cfg["subdivide_task"],
                           projection=projection)
        output_file = os.path.join(cfg["output_path"], run_id + '_dtm.tif')
        if cfg["buildDem"]["export"]:
            doc.chunk.exportRaster(path=output_file,
                                   projection=projection,
                                   nodata_value=cfg["buildDem"]["nodata"],
                                   source_data=Metashape.ElevationData,
                                   image_compression=compression)
    if (cfg["buildDem"]["type"] !=
            "DTM") & (cfg["buildDem"]["type"]
                      == "both") & (cfg["buildDem"]["type"] == "DSM"):
        raise ValueError("DEM type must be either 'DSM' or 'DTM' or 'both'")

    doc.save()

    # get an ending time stamp for the previous step
    timer5b = time.time()

    # calculate difference between end and start time to 1 decimal place
    time5 = diff_time(timer5b, timer5a)

    # record results to file
    with open(log_file, 'a') as file:
        file.write(sep.join(['Build DEM', time5]) + '\n')

    return True
コード例 #26
0
scale = chunk.transform.scale

#############################################################

# Here you can specify the cameras for which you want to export depth
cameraNames = ['T_S03447', 'T_S03448', 'T_S03497']

##############################################################

cameraList = list()
for camera in chunk.cameras:
    thisLabel = camera.label
    cameraList.append(thisLabel)

for counter, value in enumerate(cameraNames):

    cameraLabel = cameraNames[counter]
    index = cameraList.index(cameraLabel)
    camera = chunk.cameras[index]
    depth = chunk.model.renderDepth(camera.transform,
                                    camera.sensor.calibration)
    depth = depth * scale
    depth = depth.convert(" ", "F16")
    compr = Metashape.ImageCompression()
    compr.tiff_compression = Metashape.ImageCompression(
    ).TiffCompressionDeflate

    ####### Remember to update the path below to which you want to export the depth maps ########
    depth.save("/Users/deryaakkaynak/Desktop/depth" + camera.label + ".tif",
               compression=compr)
コード例 #27
0
def project_setup(cfg):
    '''
    Create output and project paths, if they don't exist
    Define a project ID based on photoset name and timestamp
    Define a project filename and a log filename
    Create the project
    Start a log file
    '''

    if not os.path.exists(cfg["output_path"]):
        os.makedirs(cfg["output_path"])
    if not os.path.exists(cfg["project_path"]):
        os.makedirs(cfg["project_path"])

    ### Set a filename template for project files and output files
    ## Get the first parts of the filename (the photoset ID and location string)

    if (cfg["photoset_id"] == "%lookup%") or cfg["location"] == "%lookup%":
        path_parts = cfg["photo_path"].split("/")
        photoset_name = path_parts[-1]
        photoset_parts = photoset_name.split("_")

    if cfg["photoset_id"] == "%lookup%":
        set_id = photoset_parts[0]
    else:
        set_id = cfg["photoset_id"]

    if cfg["location"] == "%lookup%":
        location = photoset_parts[1]
    else:
        location = cfg["location"]

    ## Project file example to make: "01c_ChipsA_YYYYMMDD-jobid.psx"
    timestamp = stamp_time()
    # TODO: allow a nonexistent location string
    run_id = "_".join([timestamp, set_id, location])
    # TODO: If there is a JobID, append to time (separated with "-", not "_"). ##?? This will keep jobs initiated in the same minute distinct
    # TODO: Allow to specify a mnemonic for the end of the project name (from YAML?)

    project_file = os.path.join(cfg["project_path"], '.'.join([run_id, 'psx']))
    log_file = os.path.join(cfg["output_path"],
                            '.'.join([run_id + "_log", 'txt']))
    '''
    Create a doc and a chunk
    '''

    # create a handle to the Metashape object
    doc = Metashape.Document(
    )  #When running via Metashape, can use: doc = Metashape.app.document

    # Save doc (necessary for steps after point cloud because there needs to be a project file)
    doc.save(project_file)

    # Initialize a chunk, set its CRS as specified
    chunk = doc.addChunk()
    chunk.crs = Metashape.CoordinateSystem(cfg["project_crs"])
    '''
    Log specs except for GPU
    '''

    # log Metashape version, CPU specs, time, and project location to results file
    # open the results file
    # TODO: records the Slurm values for actual cpus and ram allocated
    # https://slurm.schedmd.com/sbatch.html#lbAI
    with open(log_file, 'a') as file:

        # write a line with the Metashape version
        file.write(sep.join(['Project', run_id]) + '\n')
        file.write(
            sep.join([
                'Agisoft Metashape Professional Version', Metashape.app.version
            ]) + '\n')
        # write a line with the date and time
        file.write(sep.join(['Processing started', stamp_time()]) + '\n')
        # write a line with CPU info - if possible, improve the way the CPU info is found / recorded
        file.write(sep.join(['Node', platform.node()]) + '\n')
        file.write(sep.join(['CPU', platform.processor()]) + '\n')
        # write two lines with GPU info: count and model names - this takes multiple steps to make it look clean in the end

    return doc, log_file, run_id
コード例 #28
0
def align_ground(path, section='DEFAULT'):
    print(banner1, 'Aligning ground (XY) plane with coded targets...')

    region = chunk.region

    path = path + 'skip/orientation.ini'
    print('path: ', path)

    config = configparser.ConfigParser()
    config.read(path)
    # print(config.sections())

    horiz0 = config[section]['horiz0']
    horiz1 = config[section]['horiz1']
    vert0 = config[section]['vert0']
    vert1 = config[section]['vert1']

    del config

    print('x-axis:', horiz0, 'to', horiz1)
    print('y-axis:', vert0, 'to', vert1)

    H0_pos = get_marker(horiz0, chunk).position
    H1_pos = get_marker(horiz1, chunk).position
    V0_pos = get_marker(vert0, chunk).position
    V1_pos = get_marker(vert1, chunk).position

    print('H0: ', H0_pos)
    print('H1: ', H1_pos)

    print('V0: ', V0_pos)
    print('V1: ', V1_pos)

    horizontal = H1_pos - H0_pos
    vertical = V1_pos - V0_pos

    normal = vect(horizontal, vertical)
    vertical = -vect(horizontal, normal)
    horizontal = horizontal.normalized()

    R = Metashape.Matrix([horizontal, vertical,
                          normal])  #horizontal = X, vertical = Y, normal = Z

    print(R.det())  #should be 1.0
    region.rot = R.t()
    chunk.region = region
    R = chunk.region.rot  #Bounding box rotation matrix
    C = chunk.region.center  #Bounding box center vector

    if chunk.transform.matrix:
        T = chunk.transform.matrix
        s = math.sqrt(T[0, 0]**2 + T[0, 1]**2 +
                      T[0, 2]**2)  #scaling # T.scale()
        S = Metashape.Matrix().Diag([s, s, s, 1])  #scale matrix
    else:
        S = Metashape.Matrix().Diag([1, 1, 1, 1])
    T = Metashape.Matrix([[R[0, 0], R[0, 1], R[0, 2], C[0]],
                          [R[1, 0], R[1, 1], R[1, 2], C[1]],
                          [R[2, 0], R[2, 1], R[2, 2], C[2]], [0, 0, 0, 1]])
    chunk.transform.matrix = S * T.inv(
    )  #resulting chunk transformation matrix

    print("\nGround alignment finished\n")
コード例 #29
0
    def align(self):
        print("Script started...")

        (key1, isModel1, label1) = self.objects[self.fromObject.currentIndex()]
        (key2, isModel2, label2) = self.objects[self.toObject.currentIndex()]

        print("Aligning {} to {}...".format(label1, label2))

        region_size = Metashape.app.document.chunk.region.size
        try:
            tmp1 = tempfile.NamedTemporaryFile(delete=False)
            tmp1.close()
            tmp2 = tempfile.NamedTemporaryFile(delete=False)
            tmp2.close()
            Metashape.app.document.chunk.region.size = Metashape.Vector(
                [0.0, 0.0, 0.0])
            for (key, isModel, filename) in [(key2, isModel2, tmp2.name),
                                             (key1, isModel1, tmp1.name)]:
                if isModel:
                    self.chunk.model = None
                    for model in self.chunk.models:
                        if model.key == key:
                            self.chunk.model = model
                    assert (self.chunk.model is not None)
                    self.chunk.exportModel(path=filename,
                                           binary=True,
                                           save_texture=False,
                                           save_uv=False,
                                           save_normals=False,
                                           save_colors=False,
                                           save_cameras=False,
                                           save_markers=False,
                                           save_udim=False,
                                           save_alpha=False,
                                           save_comment=False,
                                           format=Metashape.ModelFormatPLY)
                else:
                    self.chunk.dense_cloud = None
                    for dense_cloud in self.chunk.dense_clouds:
                        if dense_cloud.key == key:
                            self.chunk.dense_cloud = dense_cloud
                    assert (self.chunk.dense_cloud is not None)
                    self.chunk.exportPoints(
                        path=filename,
                        source_data=Metashape.DenseCloudData,
                        binary=True,
                        save_normals=False,
                        save_colors=False,
                        save_classes=False,
                        save_confidence=False,
                        save_comment=False,
                        format=Metashape.PointsFormatPLY)

            v1 = read_ply(tmp1.name)
            v2 = read_ply(tmp2.name)
            os.remove(tmp1.name)
            os.remove(tmp2.name)
            Metashape.app.document.chunk.region.size = region_size
        except:
            os.remove(tmp1.name)
            os.remove(tmp2.name)
            Metashape.app.document.chunk.region.size = region_size
            raise

        print("Vertices number: {}, {}".format(len(v1), len(v2)))

        scale_ratio = None if self.edtScaleRatio.text() == '' else float(
            self.edtScaleRatio.text())
        target_resolution = None if self.edtTargetResolution.text(
        ) == '' else float(self.edtTargetResolution.text())
        no_global_alignment = self.chkUseInitialAlignment.isChecked()
        preview_intermidiate_alignment = self.chkPreview.isChecked()

        M12 = align_two_point_clouds(v1, v2, scale_ratio, target_resolution,
                                     no_global_alignment,
                                     preview_intermidiate_alignment)

        if isModel1:
            assert (self.chunk.model.key == key1)
            model1 = self.chunk.model
            T1, Tlocal1 = self.get_model_T(model1)
        else:
            assert (self.chunk.dense_cloud.key == key1)
            dense_cloud1 = self.chunk.dense_cloud
            T1, Tlocal1 = self.get_dense_cloud_T(dense_cloud1)

        if isModel2:
            model2 = None
            for model in self.chunk.models:
                if model.key == key2:
                    model2 = model
            assert model2 is not None
            _, Tlocal2 = self.get_model_T(model2)
        else:
            dense_cloud2 = None
            for dense_cloud in self.chunk.dense_clouds:
                if dense_cloud.key == key2:
                    dense_cloud2 = dense_cloud
            assert dense_cloud2 is not None
            _, Tlocal2 = self.get_dense_cloud_T(dense_cloud2)

        shift21 = Tlocal2 * Tlocal1.inv()

        if isModel1:
            assert (self.chunk.model.key == key1)
            self.chunk.model.transform(T1.inv() * shift21.inv() * M12 * T1)
            self.chunk.model = None
        else:
            assert (self.chunk.dense_cloud.key == key1)
            self.chunk.dense_cloud.transform = self.chunk.dense_cloud.transform * T1.inv(
            ) * shift21.inv() * M12 * T1

        print("Script finished!")
        self.reject()
コード例 #30
0
def update_boundbox_by_markers(path, chunk, section='DEFAULT'):

    print(banner1, "Updating boundbox using markers...")

    #ground targets for finding horizontal center
    path = path + 'skip/orientation.ini'
    print('path: ', path)
    config = configparser.ConfigParser()
    config.read(path)
    p0 = config[section]['p0']
    p1 = config[section]['p1']
    buffer = config[section].getint('buffer')
    print(p0, p1, buffer)

    del config

    print('p0:', p0, 'p1:', p1)

    fp0 = fp1 = 0  #initialize binary values for finding the markers to 0

    c = 0

    #find center points
    c1 = 0
    c2 = 0
    for m in chunk.markers:
        if m.label == p0:
            c1 = c
            fp0 = 1
        elif m.label == p1:
            c2 = c
            fp1 = 1
        c = c + 1

    #check markers found
    if fp0 and fp1:
        print("Found all markers")
        print(c1, c2)
        chunk.updateTransform()
    else:
        print("Error: not all markers found")
        print(fp0, fp1)

    newregion = chunk.region

    T = chunk.transform.matrix
    #v_t = T * Metashape.Vector( [0,0,0,1] )
    m = Metashape.Matrix().Diag([1, 1, 1, 1])

    m = m * T
    s = math.sqrt(m[0, 0]**2 + m[0, 1]**2 + m[0, 2]**2)  #scale factor
    R = Metashape.Matrix([[m[0, 0], m[0, 1], m[0, 2]],
                          [m[1, 0], m[1, 1], m[1, 2]],
                          [m[2, 0], m[2, 1], m[2, 2]]])
    R = R * (1. / s)
    newregion.rot = R.t()

    # Calculate center point of the bounding box, by taking the average of 2 left and 2 right markers
    mx = (chunk.markers[c1].position + chunk.markers[c2].position) / 2

    print('x', chunk.markers[c2].position[0], chunk.markers[c1].position[0])
    print('y', chunk.markers[c2].position[1], chunk.markers[c1].position[1])
    box_X_length = math.fabs(
        chunk.markers[c2].position[0] - chunk.markers[c1].position[0]
    ) * (100 + buffer) / 100  #x-axis. may return negative number, so absoluted
    box_Y_length = math.fabs(
        chunk.markers[c2].position[1] - chunk.markers[c1].position[1]
    ) * (100 + buffer) / 100  #y-axis. may return negative number, so absoluted

    print('lengths:', box_X_length, box_Y_length)

    if box_Y_length > box_X_length:
        swap_length = box_X_length
        box_X_length = box_Y_length
        box_Y_length = swap_length
        del swap_length

    Z_ratio = 1  #my way. Change this to adjust box height (default=1)
    box_Z_length = box_Y_length * Z_ratio  #same as Y length #chunk.markers[c2].position[2] - chunk.markers[c1].position[2] #z-axis

    print('lengths:', box_X_length, box_Y_length, box_Z_length)

    mx = Metashape.Vector(
        [mx[0], mx[1], mx[2]]
    )  #adjusting the zratio thing shifts the whole boundbox on a diagonal. not so easy. set to 0 for now.
    newregion.center = mx
    print('cent1', newregion.center)
    newregion.size = Metashape.Vector(
        [box_X_length, box_Y_length, box_Z_length])
    print('nrsize1', newregion.size)
    print('cent[2]', newregion.center[2], 'size[2]', newregion.size[2])
    newregion.center = Metashape.Vector([
        newregion.center[0], newregion.center[1],
        newregion.center[2] + (newregion.size[2] / 2)
    ])
    print('cent2', newregion.center)
    chunk.region = newregion
    chunk.updateTransform()

    print("Bounding box should be aligned now")