def estimate_rotation_matrices(chunk, i, j): groups = copy.copy(chunk.camera_groups) groups.append(None) for group in groups: group_cameras = list(filter(lambda c: c.group == group, chunk.cameras)) if len(group_cameras) == 0: continue if len(group_cameras) == 1: if group_cameras[0].reference.rotation is None: group_cameras[0].reference.rotation = ps.Vector([0, 0, 0]) continue for idx, c in enumerate(group_cameras[0:-1]): next_camera = group_cameras[idx + 1] if c.reference.rotation is None: if c.reference.location is None or next_camera.reference.location is None: continue direction = delta_vector_to_chunk( c.reference.location, next_camera.reference.location) cos_yaw = direction * j yaw = math.degrees( math.acos(cos_yaw)) + 90 # TODO not sure about this offset if direction * i > 0: yaw = -yaw c.reference.rotation = ps.Vector([yaw, 0, 0]) group_cameras[-1].reference.rotation = group_cameras[ -2].reference.rotation
def setup_camera(self): # Imported camera coordinates projection self.chunk.crs = self.WGS_84 # Accuracy for camera position in m self.chunk.camera_location_accuracy = PhotoScan.Vector([1, 1, 1]) # Accuracy for camera orientations in degree self.chunk.camera_rotation_accuracy = PhotoScan.Vector([1, 1, 1])
def getSVGObject(self): photo = I3_Photo() photo.add_point(p1) photo.add_point(p2) photo.label = "test_Label" p_bottom_left = PhotoScan.Vector((0, 2000)) p_bottom_right = PhotoScan.Vector((2000, 2000)) p_upper_left = PhotoScan.Vector((0, 0)) p_upper_right = PhotoScan.Vector((2000, 0)) photo.add_point(I3_Point(measurement_I=p_bottom_left, projection_I=p_bottom_left)) photo.add_point(I3_Point(measurement_I=p_bottom_right, projection_I=PhotoScan.Vector((2001, 2001)))) photo.add_point(I3_Point(measurement_I=p_upper_left, projection_I=p_upper_left)) photo.add_point(I3_Point(measurement_I=p_upper_right, projection_I=p_upper_right)) class psSensor(): height = 2000 width = 2000 class psCamera(): sensor = psSensor() cam_dummy = psCamera() photo.photoScan_camera = cam_dummy return SVG_Photo_Representation([photo], 700)
def getPhotoforRasterTest(cls): photo = I3_Photo() photo.add_point(p1) photo.add_point(p2) photo.label = "test_Label" p_bottom_left = PhotoScan.Vector((0, 2000)) p_bottom_right = PhotoScan.Vector((2000, 2000)) p_upper_left = PhotoScan.Vector((0, 0)) p_upper_right = PhotoScan.Vector((2000, 0)) photo.add_point(I3_Point(measurement_I=p_bottom_left, projection_I=PhotoScan.Vector((-1, 2001)))) photo.add_point(I3_Point(measurement_I=p_bottom_right, projection_I=PhotoScan.Vector((2001, 2001)))) photo.add_point(I3_Point(measurement_I=p_upper_left, projection_I=PhotoScan.Vector((-1, -1)))) photo.add_point(I3_Point(measurement_I=p_upper_right, projection_I=PhotoScan.Vector((2001, -1)))) class psSensor(): height = 2000 width = 1990 class psCamera(): sensor = psSensor() cam_dummy = psCamera() photo.photoScan_camera = cam_dummy return photo
def project_features(camera, points, features): """ Project feature values from 3D points onto an image using the camera matrix. Requires PhotoScan library. Returns: projected_features - an array of (image_height, image_width, nfeatures) of feature values corresponding to pixels in the image """ import PhotoScan image_height = int(camera.meta['File/ImageHeight']) image_width = int(camera.meta['File/ImageWidth']) _, nfeatures = features.shape projected_features = np.zeros_like((image_height, image_width)) for i, P in enumerate(points): P = PhotoScan.Vector(tuple(P)) x, y = camera.project(P) x_in_image = x >= 0 and x < image_width y_in_image = y >= 0 and y < image_height if x_in_image and y_in_image: projected_features[y, x] = features[i] return projected_features.reshape((image_height, image_width, nfeatures))
def reset_view(self, magnification=80): doc.chunk = self.chunk chunk = self.chunk T = chunk.transform.matrix viewpoint = PhotoScan.app.viewpoint cx = viewpoint.width cy = viewpoint.height region = chunk.region r_center = region.center r_rotate = region.rot r_size = region.size r_vert = list() for i in range(8): # bounding box corners r_vert.append( PhotoScan.Vector([ 0.5 * r_size[0] * ((i & 2) - 1), r_size[1] * ((i & 1) - 0.5), 0.25 * r_size[2] * ((i & 4) - 2) ])) r_vert[i] = r_center + r_rotate * r_vert[i] height = T.mulv(r_vert[1] - r_vert[0]).norm() width = T.mulv(r_vert[2] - r_vert[0]).norm() if width / cx > height / cy: scale = cx / width else: scale = cy / height PhotoScan.app.viewpoint.coo = T.mulp(chunk.region.center) PhotoScan.app.viewpoint.mag = magnification
def bbox_to_cs(): print("Script started...") doc = PhotoScan.app.document chunk = doc.chunk T = chunk.transform.matrix v_t = T.mulp(PhotoScan.Vector([0, 0, 0])) if chunk.crs: m = chunk.crs.localframe(v_t) else: m = PhotoScan.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 # s = m.scale() R = PhotoScan.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 = m.rotation() R = R * (1. / s) reg = chunk.region reg.rot = R.t() chunk.region = reg print("Script finished!")
class TestMyProject(unittest.TestCase): pho1 = I3_Photo() pho1.sigma_I = PhotoScan.Vector((2, 13)) pho2 = I3_Photo() pho2.sigma_I = PhotoScan.Vector((5, 11)) project = I3_Project() project.photos = [pho1, pho2] def test_calcGlobalSigma(self): rms_x, rms_y = self.project._get_RMS_4_all_photos() self.assertAlmostEqual(rms_x, 3.807886553, 6) self.assertAlmostEqual(rms_y, 12.04159458, 6) def test_createProjectSVG(self): pass
def photoscan_alignphotos(images, reference_eo, sequence): start_time = time.time() doc = PhotoScan.app.document chunk = doc.addChunk() chunk.addPhotos(images) for i in range(len(chunk.cameras)): chunk.cameras[i].reference.location = (float(reference_eo[6 * i]), float(reference_eo[6 * i + 1]), float(reference_eo[6 * i + 2])) chunk.cameras[i].reference.rotation = (float(reference_eo[6 * i + 5]), float(reference_eo[6 * i + 4]), float(reference_eo[6 * i + 3])) chunk.camera_location_accuracy = PhotoScan.Vector([0.001, 0.001, 0.001]) chunk.camera_rotation_accuracy = PhotoScan.Vector([0.01, 0.01, 0.01]) # chunk.cameras[-1].reference.location_accuracy = PhotoScan.Vector([10, 10, 10]) # chunk.cameras[-1].reference.rotation_accuracy = PhotoScan.Vector([10, 10, 10]) chunk.cameras[-1].reference.accuracy = PhotoScan.Vector([10, 10, 10]) chunk.cameras[-1].reference.accuracy_ypr = PhotoScan.Vector([10, 10, 10]) chunk.matchPhotos(accuracy=PhotoScan.MediumAccuracy) chunk.alignCameras() # doc.save("test_" + str(int(sequence)+1) + ".psz") camera = chunk.cameras[-1] if not camera.transform: print("There is no transformation matrix") estimated_coord = chunk.crs.project( chunk.transform.matrix.mulp(camera.center)) # estimated XYZ in coordinate system units T = chunk.transform.matrix m = chunk.crs.localframe( T.mulp(camera.center)) # transformation matrix to the LSE coordinates in the given point R = (m * T * camera.transform * PhotoScan.Matrix().Diag([1, -1, -1, 1])).rotation() estimated_ypr = PhotoScan.utils.mat2ypr(R) # estimated orientation angles - yaw, pitch, roll estimated_opk = PhotoScan.utils.mat2opk(R) # estimated orientation angles - omega, phi, kappa print(estimated_coord[0]) print(estimated_coord[1]) print(estimated_coord[2]) print(estimated_ypr[0]) print(estimated_ypr[1]) print(estimated_ypr[2]) print(estimated_opk[0]) print(estimated_opk[1]) print(estimated_opk[2])
def get_photos_delta(chunk): mid_idx = int(len(chunk.cameras) / 2) if mid_idx == 0: return ps.Vector([0, 0, 0]) c1 = chunk.cameras[:mid_idx][-1] c2 = chunk.cameras[:mid_idx][-2] offset = c1.reference.location - c2.reference.location for i in range(len(offset)): offset[i] = math.fabs(offset[i]) return offset
def dbg_test1(): point2d = PhotoScan.Vector([1448, 1186]) sensor = camera.sensor calibration = sensor.calibration p_x = camera.transform.mulp(sensor.calibration.unproject(point2d)) print('p_x', p_x) print('camera center', camera.center) X = chunk.dense_cloud.pickPoint(camera.center, p_x) print('X', X) chunk.addMarker(point=X)
def get_chunk_vectors(lat, lon): z = delta_vector_to_chunk(ps.Vector([lon, lat, 0]), ps.Vector([lon, lat, 1])) y = delta_vector_to_chunk(ps.Vector([lon, lat, 0]), ps.Vector([lon + 0.001, lat, 0])) x = delta_vector_to_chunk(ps.Vector([lon, lat, 0]), ps.Vector([lon, lat + 0.001, 0])) return x, y, -z
def test_createSVG(self): photo = I3_Photo() photo.add_point(p1) photo.add_point(p2) photo.label = "test_Label" p_bottom_left = PhotoScan.Vector((0, 2000)) p_bottom_right = PhotoScan.Vector((2000, 2000)) p_upper_left = PhotoScan.Vector((0, 0)) p_upper_right = PhotoScan.Vector((2000, 0)) photo.add_point(I3_Point(measurement_I=p_bottom_left, projection_I=p_bottom_left)) photo.add_point(I3_Point(measurement_I=p_bottom_right, projection_I=PhotoScan.Vector((2001, 0)))) photo.add_point(I3_Point(measurement_I=p_upper_left, projection_I=p_upper_left)) photo.add_point(I3_Point(measurement_I=p_upper_right, projection_I=p_upper_right)) class psSensor(): height = 2000 width = 2000 class psCamera(): sensor = psSensor() cam_dummy = psCamera() photo.photoScan_camera = cam_dummy
def build_mesh(output_file, min_latitude, max_latitude, min_longitude, max_longitude, lat_step, long_step): hgts_folder = get_hgts_folder() downloader_tasks = ['convert'] downloader = hgt_downloader.HGTDownloader(min_latitude, min_longitude, max_latitude, max_longitude, hgts_folder, downloader_tasks) if not run_downloader(downloader): return def get_height(x, y): return downloader.elevation_data.get_elevation(x, y, approximate=True) mesh_points = np.array([ [longitude, latitude, get_height(latitude, longitude)] for latitude in util.frange(min_latitude, max_latitude, lat_step) for longitude in util.frange(min_longitude, max_longitude, long_step) ]) last_ok = 0 for p in mesh_points: if p[2] is None: p[2] = last_ok else: last_ok = p[2] points = [ps.Vector(p) for p in mesh_points] faces = np.array(computeDelaunayTriangulation(points)) norms = np.zeros(mesh_points.shape, dtype=mesh_points.dtype) tris = mesh_points[faces] # normals for all triangles n = np.cross(tris[::, 1] - tris[::, 0], tris[::, 2] - tris[::, 0]) normalize_v3(n) norms[faces[:, 0]] += n norms[faces[:, 1]] += n norms[faces[:, 2]] += n normalize_v3(norms) with open(output_file, "w") as f: write_model_file(f, mesh_points, norms, faces) print("Successfully built mesh") ps.app.document.chunk.importModel(output_file) print("Mesh imported")
def center_bbox_xyz(): """Centers bounding box to XYZ center.""" chunk = PhotoScan.app.document.chunk transform_matrix = chunk.transform.matrix if chunk.crs: vect_tm = transform_matrix * PhotoScan.Vector([0, 0, 0, 1]) vect_tm.size = 3 locfrm = chunk.crs.localframe(vect_tm) else: locfrm = PhotoScan.Matrix().diag([1, 1, 1, 1]) locfrm = locfrm * transform_matrix sqrt = math.sqrt(locfrm[0, 0]**2 + locfrm[0, 1]**2 + locfrm[0, 2]**2) mat = PhotoScan.Matrix([[locfrm[0, 0], locfrm[0, 1], locfrm[0, 2]], [locfrm[1, 0], locfrm[1, 1], locfrm[1, 2]], [locfrm[2, 0], locfrm[2, 1], locfrm[2, 2]]]) mat = mat * (1. / sqrt) reg = chunk.region reg.rot = mat.t() chunk.region = reg
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]])
def cam_poly(cam_index, chunk): model = chunk.model faces = model.faces vertices = model.vertices camera = chunk.cameras[cam_index] sensor = camera.sensor steps = [(0, 0), (sensor.width - 1, 0), (sensor.width - 1, sensor.height - 1), (0, sensor.height - 1)] respoly = np.array([]) for x, y in steps: point = PhotoScan.Vector([x, y]) point = sensor.calibration.unproject(point) point = camera.transform.mulv(point) vect = point p = PhotoScan.Vector(camera.center) for face in faces: v = face.vertices E1 = PhotoScan.Vector(vertices[v[1]].coord - vertices[v[0]].coord) E2 = PhotoScan.Vector(vertices[v[2]].coord - vertices[v[0]].coord) D = PhotoScan.Vector(vect) T = PhotoScan.Vector(p - vertices[v[0]].coord) P = cross(D, E2) Q = cross(T, E1) result = PhotoScan.Vector([Q * E2, P * T, Q * D]) / (P * E1) if (0 < result[1]) and (0 < result[2]) and (result[1] + result[2] <= 1): t = (1 - result[1] - result[2]) * vertices[v[0]].coord u = result[1] * vertices[v[1]].coord v_ = result[2] * vertices[v[2]].coord res = chunk.transform.matrix.mulp(u + v_ + t) res = chunk.crs.project(res) thisvert = np.r_[ res[0], res[1]] #(x,y) cordimates for a corner (defined in steps) break #finish when the first intersection is found respoly = np.concatenate([thisvert, respoly]) return (respoly)
def add_altitude(): """ Adds user-defined altitude for camera instances in the Reference pane """ doc = PhotoScan.app.document if not len(doc.chunks): raise Exception("No chunks!") alt = PhotoScan.app.getFloat("Please specify the height to be added:", 100) print("Script started...") chunk = doc.chunk for camera in chunk.cameras: if camera.reference.location: coord = camera.reference.location camera.reference.location = PhotoScan.Vector([coord.x, coord.y, coord.z + alt]) print("Script finished!")
def create_roi(): """ Attempts to create the region ROI. This places the center of the ROI region """ # """at the midpoint of all of the scale bar markers. """ # """NOTE: This doesn't actually do anything yet.""" x_axis, y_axis, z_axis = 0, 0, 0 for chunk in PhotoScan.app.document.chunks: if chunk.label == 'Aligned Side A': x_axis, y_axis, z_axis = 0, 0, 0 num_markers = chunk.markers.__len__() for marker in chunk.markers: x_axis += marker.position.x y_axis += marker.position.y z_axis += marker.position.z cent_x = x_axis / num_markers cent_y = y_axis / num_markers cent_z = z_axis / num_markers newregion = PhotoScan.Region() newregion.size = chunk.region.size newregion.rot = chunk.region.rot newregion.center = PhotoScan.Vector([cent_x, cent_y, cent_z]) chunk.region = newregion
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]) same_yaw_bound = 40 # within this bound all yaws are considered to be for same direction flights yaws_deltas, first_class_yaw = get_camera_calibration(chunk, min_latitude, min_longitude, same_yaw_bound=40) print('Estimated yaw offsets {}'.format(yaws_deltas)) i, j, k = get_chunk_vectors(min_latitude, min_longitude) # i || North for c in chunk.cameras: group_index = chunk.camera_groups.index( c.group) if c.group is not None else -1 location = c.reference.location if location is None: continue chunk_coordinates = wgs_to_chunk(chunk, location) fi = c.reference.rotation.x + 90 idx = 0 if math.fabs( c.reference.rotation.x - first_class_yaw[group_index]) < same_yaw_bound else 1 fi += yaws_deltas[group_index][idx] fi = math.radians(fi) ii, jj = i * math.cos(fi) + j * math.sin(fi), j * math.cos( fi) - i * math.sin(fi) c.transform = ps.Matrix([[ii.x, jj.x, k.x, chunk_coordinates[0]], [ii.y, jj.y, k.y, chunk_coordinates[1]], [ii.z, jj.z, k.z, chunk_coordinates[2]], [0, 0, 0, 1]])
marker_2D = (2000, 1000) # projections of marker on the given photo marker = chunk.addMarker() marker.projections[camera] = marker_2D point_2D = marker.projections[camera].coord vect = camera.sensor.calibration.unproject(point_2D) vect = camera.transform.mulv(vect) center = camera.center # estimating ray and surface intersection for face in model.faces: v = face.vertices E1 = PhotoScan.Vector(vertices[v[1]].coord - vertices[v[0]].coord) E2 = PhotoScan.Vector(vertices[v[2]].coord - vertices[v[0]].coord) D = PhotoScan.Vector(vect) T = PhotoScan.Vector(center - vertices[v[0]].coord) P = cross(D, E2) Q = cross(T, E1) result = PhotoScan.Vector([Q * E2, P * T, Q * D]) / (P * E1) if (0 < result[1]) and (0 < result[2]) and (result[1] + result[2] <= 1): t = (1 - result[1] - result[2]) * vertices[v[0]].coord u = result[1] * vertices[v[1]].coord v_ = result[2] * vertices[v[2]].coord point_3D = T0.mulp(u + v_ + t) if chunk.crs: point_3D = chunk.crs.project(point_3D)
def offsetsTest(document): chunk = document.chunk a_set = [1, 1.25, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 9, 11, 13, 15] zaccscale = [0.5, 1, 1.5, 2] xyacc = 0.03 chunk.marker_projection_accuracy = 3 x_list = [] y_list = [] z_list = [] x_med = 0 y_med = 0 z_med = 0 x_min = 100 x_max = -100 y_min = 100 y_max = -100 z_min = 100 z_max = -100 adj_r = 0 adj_p = 0 adj_y = 0 f = 0 meansq_x = 0 meansq_y = 0 meansq_z = 0 pathCSV = document.path + "_offsets_test.csv" # accuracy # median x y z error min max # adj rpy #focal length with open(pathCSV, "w") as test: test.write( "t.p.acc z.acc x.med y.med z.med x.min y.min z.min x.max y.max z.max adj.r adj.p adj.y f meansqx meansqy meansqz \n" ) #load markers path = os.path.dirname(PhotoScan.app.document.path) chunk.importMarkers(path + '/markers.xml') print("Markers path: " + path + '/markers.xml') for scale in zaccscale: z_acc = xyacc * scale chunk.camera_location_accuracy = PhotoScan.Vector( (float(xyacc), float(xyacc), float(z_acc))) for a in a_set: print(str(a) + ' ' + str(z_acc)) #set parameters chunk.tiepoint_accuracy = a # uncheck markers #for m in chunk.markers: #m.reference.enabled = False chunk.remove(chunk.markers) #optimise chunk.optimizeCameras(fit_f=True, fit_cx=True, fit_cy=True, fit_b1=True, fit_b2=True, fit_k1=True, fit_k2=True, fit_k3=True, fit_k4=True, fit_p1=True, fit_p2=True, fit_p3=False, fit_p4=False) # check markers #for m in chunk.markers: #m.reference.enabled = True chunk.importMarkers(path + '/markers.xml') #export errors for m in chunk.markers: if len(m.projections.items()) > 2: r = chunk.crs.unproject(m.reference.location) e = chunk.transform.matrix.mulp(m.position) l = chunk.crs.localframe( chunk.transform.matrix.mulp(m.position)) v = l.mulv(e - r) print("Errors: " + str(v[0]) + " " + str(v[1]) + " " + str(v[2]) + " \n") if (v[0] < x_min): x_min = v[0] if (v[0] > x_max): x_max = v[0] if (v[1] < y_min): y_min = v[1] if (v[1] > y_max): y_max = v[1] if (v[2] < z_min): z_min = v[2] if (v[2] > z_max): z_max = v[2] x_list.append(v[0]) y_list.append(v[1]) z_list.append(v[2]) x_med = median(x_list) y_med = median(y_list) z_med = median(z_list) meansq_x = meansq(x_list) meansq_y = meansq(y_list) meansq_z = meansq(z_list) f = chunk.sensors[0].calibration.f with open(pathCSV, "a") as test: test.write( str(a) + ' ' + str(z_acc) + ' ' + str(x_med) + ' ' + str(y_med) + ' ' + str(z_med) + ' ' + str(x_min) + ' ' + str(y_min) + ' ' + str(z_min) + ' ' + str(x_max) + ' ' + str(y_max) + ' ' + str(z_max) + ' ' + str(chunk.sensors[0].antenna.rotation[2]) + ' ' + str(chunk.sensors[0].antenna.rotation[1]) + ' ' + str(chunk.sensors[0].antenna.rotation[0]) + ' ' + str(f) + ' ' + str(meansq_x) + ' ' + str(meansq_y) + ' ' + str(meansq_z) + '\n')
def processscan(scanfile): configfile = MonitorDirectory + scanfile log("JSON file: " + configfile) config = json.loads(open(configfile).read()) scanid = config["scanid"] normaldir = config["normaldir"] projectdir = config["projectdir"] savedir = config["savedir"] try: SKETCHFAB_ENABLE = config["SKETCHFAB_ENABLE"] log("Taking JSON setting for sketchfab enable") except: log("Taking default sketchfab setting from main script") try: SKETCHFAB_DESCRIPTION = config["SKETCHFAB_DESCRIPTION"] log("Taking JSON setting for sketchfab description") except: log("Taking sketchfab description from main script") # STEP 1 - Load Images doc = PhotoScan.app.document doc.clear() chunk = doc.addChunk() photos = os.listdir(normaldir) # Get the photos filenames photos = [os.path.join(normaldir, p) for p in photos] # Make them into a full path log("Found {} photos in {}".format(len(photos), normaldir)) if not chunk.addPhotos(photos): log("ERROR: Failed to add photos: " + str(photos)) # STEP 2 - Detect Markers log("Dectecting markers on non-projected images") chunk.detectMarkers(PhotoScan.TargetType.CircularTarget12bit, 50) # STEP 3 - Create auto mask, if empty directory is specified in JSON file try: emptydir = config["emptydir"] log("Mask directory found, going to create auto mask") except: emptydir = "" log("No mask directory set, no auto making will take place") if (emptydir != ""): log("Creating auto mask based on non-projected images") maskpath = emptydir + "{filename}.jpg" chunk.importMasks(maskpath, method='background', tolerance=MaskTolerence) # STEP 4 - Change images to projection images log("Switching to projection images") for i in range(len(chunk.cameras)): camera = chunk.cameras[i] photo = camera.photo.copy() photo.path = projectdir + camera.label camera.photo = photo # STEP 5 - Align Images chunk.matchPhotos(accuracy=PhotoScan.HighAccuracy, preselection=PhotoScan.NoPreselection, filter_mask=True, keypoint_limit=keypointLimit, tiepoint_limit=tiepointLimit) chunk.alignCameras() # STEP 6 - Create Auto Bounding box mp0 = 0 mpy = 0 mpx = 0 fp0 = 0 fpy = 0 fpx = 0 #setting for Y up, Z forward -> needed for mixamo/unity vector0 = PhotoScan.Vector((0, 0, 0)) vectorY = PhotoScan.Vector((0, 0, distancepy)) # Specify Y Distance vectorX = PhotoScan.Vector((distancepx, 0, 0)) # Specify X Distance c1 = 0 c2 = 0 c3 = 0 c4 = 0 c = 0 for m in chunk.markers: if m.label == c1target: log("Center 1 point found") c1 = c if m.label == c2target: log("Center 2 point found") c2 = c if m.label == c3target: log("Center 3 point found") c3 = c if m.label == c4target: log("Center 4 point found") c4 = c if m.label == p0: mp0 = c fp0 = 1 m.reference.location = vector0 m.reference.enabled = 1 log("Found floormat center point") if m.label == py: mpy = c fpy = 1 m.reference.location = vectorY m.reference.enabled = 1 log("found floormat Y point") if m.label == px: mpx = c fpx = 1 m.reference.location = vectorX m.reference.enabled = 1 log("found floormat X point") c = c + 1 if fp0 and fpx and fpy: log("Found all markers") chunk.updateTransform() else: log("Error: not all markers found") newregion = chunk.region T = chunk.transform.matrix v_t = T * PhotoScan.Vector([0, 0, 0, 1]) m = PhotoScan.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 = PhotoScan.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 + chunk.markers[c3].position + chunk.markers[c4].position) / 4 mx = PhotoScan.Vector([mx[0], mx[1], mx[2]]) newregion.center = mx dist = chunk.markers[mp0].position - chunk.markers[mpy].position dist = dist.norm() ratio = dist / distancepy newregion.size = PhotoScan.Vector( [boxwidth * ratio, boxheight * ratio, boxdepth * ratio]) chunk.region = newregion chunk.updateTransform() log("Bounding box should be aligned now") # STEP 7 - Create Dense Cloud chunk.buildDenseCloud(quality=PhotoScan.HighQuality, filter=PhotoScan.AggressiveFiltering) # STEP 8 - Create MESH chunk.buildModel(surface=PhotoScan.Arbitrary, interpolation=PhotoScan.EnabledInterpolation, face_count=PhotoScan.HighFaceCount) # STEP 9 - Switch projection images back to normal images for i in range(len(chunk.cameras)): camera = chunk.cameras[i] photo = camera.photo.copy() photo.path = normaldir + camera.label camera.photo = photo # STEP 10 - Do some basic clean up operations try: chunk.model.removeComponents(removecomponentsmax) except: log("Error Removing small components") try: chunk.model.fixTopology() except: log("Error Fix topology") try: chunk.model.closeHoles(100) except: log("Error closing holes") try: if smoothmodellevel > 1: chunk.smoothModel(smoothmodellevel) except: log("Error smoothing model") # STEP 11 - Create UVmap and Texture chunk.buildUV(mapping=PhotoScan.GenericMapping) chunk.buildTexture(blending=PhotoScan.MosaicBlending, size=8196) # STEP 12 - Save files doc.save(savedir + scanid + ".psz") modelpath = savedir + scanid + ".obj" chunk.exportModel(modelpath, binary=True, precision=6, texture_format="jpg", texture=True, normals=True, colors=True, cameras=False, format="obj") # STEP 13 - Zip files if SKETCHFAB_ENABLE: log("Zipping files to upload to sketchfab") zf = zipfile.ZipFile(savedir + scanid + ".zip", mode="w") try: zf.write(savedir + scanid + ".mtl") zf.write(savedir + scanid + ".obj") zf.write(savedir + scanid + ".jpg") finally: zf.close() zip_file = savedir + scanid + ".zip" # STEP 14 - Upload to sketchfab data = { 'token': SKETCHFAB_API_TOKEN, 'name': scanid, 'description': SKETCHFAB_DESCRIPTION, 'tags': SKETCHFAB_TAGS, 'private': SKETCHFAB_PRIVATE, 'password': SKETCHFAB_PASSWORD } f = open(zip_file, 'rb') files = {'modelFile': f} try: log("Uploading.. agisoft will pretend to hang while uploading, please wait" ) PhotoScan.app.update() model_url = upload(data, files) sfile = open(savedir + scanid + "_sketchfabURL.txt", "w") sfile.write(model_url) sfile.close() log("Uploaded to Sketchfab") finally: f.close() log("==============================================================================" ) log(" Completeted processing: " + scanid) log("==============================================================================" )
def cross(a, b): result = PhotoScan.Vector( [a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x]) return result
logging.warning("Found no images in %s" % args.input) exit(-1) logging.info("Adding %d images" % len(images)) chunk.addPhotos(images, PhotoScan.FlatLayout) ## Assign chunk to our station group for c in chunk.cameras: c.group = group if center_paths and os.path.basename(c.photo.path) in center_paths: print("Found camera for center path at %s, fixing to the origin" % c.photo.path) c.reference.rotation = PhotoScan.Vector([0, 0, 0]) c.reference.accuracy_ypr = PhotoScan.Vector([5, 5, 5]) chunk.matchPhotos(accuracy=PhotoScan.HighAccuracy, generic_preselection=True, reference_preselection=False) chunk.alignCameras() chunk.transform.rotation = PhotoScan.Utils.ypr2mat( PhotoScan.Vector([180, -20, 180])) # for c in chunk.cameras: # print(c.transform) # ## Find first aligned image # first = next(c for c in chunk.cameras if c.transform)
def check_error(self): # Compatibility - Agisoft PhotoScan Professional 1.2.4 # saves reprojection error for the tie points in the sparse cloud chunk = self.chunk M = chunk.transform.matrix point_cloud = chunk.point_cloud projections = point_cloud.projections points = point_cloud.points n_points = len(points) points_errors = {} error_list = [] for photo in chunk.cameras: if not photo.transform: continue T = photo.transform.inv() calib = photo.sensor.calibration point_index = 0 for proj in projections[photo]: track_id = proj.track_id while point_index < n_points and points[ point_index].track_id < track_id: point_index += 1 if point_index < n_points and points[ point_index].track_id == track_id: if points[point_index].valid == False: continue coord = T * points[point_index].coord coord.size = 3 dist = calib.error(coord, proj.coord).norm()**2 v = M * points[point_index].coord v.size = 3 if point_index in points_errors.keys(): point_index = int(point_index) points_errors[point_index].x += dist points_errors[point_index].y += 1 else: points_errors[point_index] = PhotoScan.Vector( [dist, 1]) for point_index in range(n_points): if points[point_index].valid is False: continue if chunk.crs: w = M * points[point_index].coord w.size = 3 X, Y, Z = chunk.crs.project(w) else: X, Y, Z, w = M * points[point_index].coord error = math.sqrt(points_errors[point_index].x / points_errors[point_index].y) error_list.append(error) error_list.sort() ave_error = sum(error_list) / len( error_list) # for original photos, no point manipulation recorded valid_pt_list = [] for pt in chunk.point_cloud.points: # Makes a list of points that are NOT deleted if pt.valid is True: valid_pt_list.append(pt) else: continue count = 0 print(ave_error) print(max(error_list)) self.error = ave_error if self.start_error is None: self.start_error = ave_error return ave_error
steps.extend( list( zip([camera.width - 1] * ((camera.height - 1) // step), list(range(0, camera.height - 1, step))))) steps.extend( list( zip(list(range((camera.width - 1), 0, -step)), [camera.height - 1] * ((camera.width - 1) // step)))) steps.extend( list( zip([0] * ((camera.height - 1) // step), list(range(camera.height - 1, 0, -step))))) for x, y in steps: point = PhotoScan.Vector([x, y]) point = camera.calibration.unproject(point) point = camera.transform.mulv(point) vect = point p = PhotoScan.Vector(camera.center) for face in faces: v = face.vertices E1 = PhotoScan.Vector(vertices[v[1]].coord - vertices[v[0]].coord) E2 = PhotoScan.Vector(vertices[v[2]].coord - vertices[v[0]].coord) D = PhotoScan.Vector(vect) T = PhotoScan.Vector(p - vertices[v[0]].coord) P = cross(D, E2) Q = cross(T, E1)
def get_shape_inf(): chunk = PhotoScan.app.document.chunk region = chunk.region Mat = chunk.transform.matrix shapes = chunk.shapes chunk.optimizeCameras(fit_f=True, fit_cx=True, fit_cy=True, fit_b1=True, fit_b2=True, fit_k1=True, fit_k2=True, fit_k3=True, fit_k4=True, fit_p1=True, fit_p2=True, fit_p3=True, fit_p4=True) #optimize camera alignment chunk.matchPhotos(accuracy=PhotoScan.HighAccuracy, generic_preselection=True, reference_preselection=False) chunk.alignCameras() xmin = 10E+10 ymin = 10E+10 zmin = 10E+10 xmax = -xmin ymax = -ymin zmax = -zmin padding = 1 + 0.05 for shape in chunk.shapes: print(shape.vertices) #creates an array to hold all of the vertices for the shapes verts = [[] for _ in range(len(shape.vertices))] a = 0 for v in shape.vertices: b = 3 ver = [[] for _ in range(b)] ver = [[v.x], [v.y], [v.z]] if v.x > xmax: xmax = v.x if v.x < xmin: xmin = v.x if v.y > ymax: ymax = v.y if v.y < ymin: ymin = v.y if v.z > zmax: zmax = v.z if v.z < zmin: zmin = v.z #places the vertice in the array holding all of the vertices verts[a] = ver a = a + 1 # find polar coordinates of selected region Maxvert = PhotoScan.Vector([xmax, ymax, zmax]) Minvert = PhotoScan.Vector([xmin, ymin, zmin]) #Scale Maxvert *= padding Minvert *= padding #find center of region Center = (Maxvert + Minvert) / 2 #find size of region Size = Maxvert - Minvert # resize bounding area to be slightly larger than the coordinates region.center = Mat.inv().mulp(chunk.crs.unproject(Center)) region.size = Size * (1 / Mat.scale()) print(region.size) v_t = Mat * PhotoScan.Vector([0, 0, 0, 1]) v_t.size = 3 R = chunk.crs.localframe(v_t) * Mat region.rot = R.rotation().t() chunk.region = region #build depth maps chunk.buildDepthMaps(quality=PhotoScan.MediumQuality, filter=PhotoScan.AggressiveFiltering) #build dense cloud chunk.buildDenseCloud() #build mesh chunk.buildModel(surface=PhotoScan.Arbitrary, interpolation=PhotoScan.EnabledInterpolation) print("Importing masks") chunk.importMasks(path='', source=PhotoScan.MaskSourceModel, operation=PhotoScan.MaskOperationReplacement, tolerance=10, cameras=chunk.cameras) print("Script finished!")
sp_line = line.rsplit(",", 6) #splitting read line by four parts camera_name = sp_line[0] #camera label marker_name = sp_line[1] #marker label x = int(sp_line[2]) #x- coordinate of the current projection in pixels y = int(sp_line[3]) #y- coordinate of the current projection in pixels cx = float(sp_line[4]) #world x- coordinate of the current marker cy = float(sp_line[5]) #world y- coordinate of the current marker cz = float(sp_line[6]) #world z- coordinate of the current marker flag = 0 for i in range(0, photos_total): if chunk.cameras[i].label == camera_name: for marker in chunk.markers: #searching for the marker (comparing with all the marker labels in chunk) if marker.label == marker_name: marker.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: marker = chunk.addMarker() marker.label = marker_name marker.projections[ chunk.cameras[i]] = PhotoScan.Marker.Projection( PhotoScan.Vector([x, y]), True) # print(marker) marker.reference.location = PhotoScan.Vector([cx, cy, cz]) break line = markerList.readline() #reading the line in input file # print (line) if len(line) == 0:
def error(_CurrentChunk): # Compatibility - Agisoft PhotoScan Professional 1.2.4 # saves reprojection error for the tie points in the sparse cloud import PhotoScan import math doc = PhotoScan.app.document M = _CurrentChunk.transform.matrix crs = _CurrentChunk.crs point_cloud = _CurrentChunk.point_cloud projections = point_cloud.projections points = point_cloud.points npoints = len(points) tracks = point_cloud.tracks points_coords = {} points_errors = {} errorList = [] for photo in _CurrentChunk.cameras: if not photo.transform: continue T = photo.transform.inv() calib = photo.sensor.calibration point_index = 0 for proj in projections[photo]: track_id = proj.track_id while point_index < npoints and points[ point_index].track_id < track_id: point_index += 1 if point_index < npoints and points[ point_index].track_id == track_id: if points[point_index].valid == False: continue coord = T * points[point_index].coord coord.size = 3 dist = calib.error(coord, proj.coord).norm()**2 v = M * points[point_index].coord v.size = 3 if point_index in points_errors.keys(): point_index = int(point_index) points_errors[point_index].x += dist points_errors[point_index].y += 1 else: points_errors[point_index] = PhotoScan.Vector([dist, 1]) for point_index in range(npoints): if points[point_index].valid == False: continue if _CurrentChunk.crs: w = M * points[point_index].coord w.size = 3 X, Y, Z = _CurrentChunk.crs.project(w) else: X, Y, Z, w = M * points[point_index].coord error = math.sqrt(points_errors[point_index].x / points_errors[point_index].y) errorList.append(error) errorList.sort() avgError = sum(errorList) / len( errorList) ###for original photos, no point manipulation recorded validPtList = [] for pt in _CurrentChunk.point_cloud.points: ##Makes a list of points that are NOT deleted if pt.valid == True: validPtList.append(pt) else: continue count = 0 #for err in errorList: ## loops through error values in error list # if err > 1.5*avgError: # validPtList[count].selected = True # count += 1 print(avgError) print(max(errorList)) return avgError