Example #1
0
def main():
    points = load_csv('robust_1.csv')
    polylidar_kwargs = dict(alpha=0.0, lmax=1000.0, min_triangles=1)

    points_mat = MatrixDouble(points)
    polylidar = Polylidar3D(**polylidar_kwargs)

    # Extracts planes and polygons, time
    t1 = time.time()
    # pick large lmax to get the convex hull, no triangles filtered
    mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
    t2 = time.time()
    print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))
    print(
        "If Robust Geometric Predicates is NOT activated, you should see a malformed convex hull"
    )
    print(
        "See README.md to activate if desired. ~20% performance penalty when active."
    )
    print("")
    # Convert to numpy format, no copy with np.asarray()
    triangles = np.asarray(mesh.triangles)

    fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
    # plot points
    ax.scatter(points[:, 0], points[:, 1], c='k')
    # plot all triangles
    plot_triangles(get_triangles_from_list(triangles, points), ax)
    # plot seperated planar triangular segments
    triangle_meshes = get_colored_planar_segments(planes, triangles, points)
    plot_triangle_meshes(triangle_meshes, ax)
    # plot polygons
    plot_polygons(polygons, points, ax)
    plt.axis('equal')
    plt.show()
Example #2
0
def main():

    letter_map = read_alphabet()
    # letters = ['P', 'O', 'L', 'Y', 'L', 'I', 'D', 'A', 'R']
    points = stitch_letters(letter_map)

    # plt.scatter(points[:, 0], points[:, 1], s=0.2)
    # plt.show()
    print(int(points.shape[0] / 2))
    idx = np.random.randint(0, points.shape[0], size=int(points.shape[0] / 2))
    points = np.ascontiguousarray(points[idx, :])
    lmax = 10.0
    polylidar_kwargs = dict(alpha=0.0, lmax=lmax, min_triangles=5)
    # print(polylidar_kwargs)
    # Convert Points and make Polylidar
    points_mat = MatrixDouble(points)
    polylidar = Polylidar3D(**polylidar_kwargs)
    # Extracts planes and polygons, time
    t1 = time.perf_counter()
    mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
    t2 = time.perf_counter()
    triangles = np.asarray(mesh.triangles)
    # print(triangles, triangles.shape)
    triangles = triangles.flatten()
    # print(triangles, triangles.shape)
    planes_np = planes
    # print(planes_np)
    print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))

    # Plot Data
    if points.shape[0] < 100000:
        fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
        ax.set_xticks([])
        ax.set_yticks([])
        plt.axis('equal')
        plt.axis('off')
        # plot points
        plot_points(points, ax)
        fig.savefig('assets/scratch/pl_logo_points.png',
                    bbox_inches='tight',
                    transparent=True)
        # plot all triangles
        plot_triangles(get_triangles_from_list(triangles, points), ax)
        # plot mesh triangles
        triangle_meshes = get_colored_planar_segments(planes_np, triangles,
                                                      points)
        plot_triangle_meshes(triangle_meshes, ax)
        # plot polygons
        plot_polygons(polygons, points, ax, linewidth=4.0)

        plt.subplots_adjust(wspace=0.185,
                            hspace=0.185,
                            left=0.110,
                            top=0.535,
                            right=0.750,
                            bottom=0.110)
        fig.savefig('assets/scratch/pl_logo.png',
                    bbox_inches='tight',
                    transparent=True)
        plt.show()
Example #3
0
    def __init__(self, config):
        super().__init__()
        self.data_folder: str = config['data_folder']
        self.date: str = config['date']
        self.drive: str = config['drive']
        self.frames = config['frames']
        self.view_image = config['view_image']
        self.cam = config['cam']
        self.pointcloud = config['pointcloud']
        self.view_3D = config['view_3D']
        self.polylidar_kwargs = config['polygon']['polylidar']
        self.postprocess = config['polygon']['postprocess']
        self.record = config['record']
        stacked_str = "_stacked" if self.record['stacked'] and self.view_3D[
            'active'] else ""
        self.record['fpath'] = str(
            Path(self.record['directory']) /
            "{}_{}{}.avi".format(self.date, self.drive, stacked_str))
        self.interactive = config['interactive']

        self.load_kitti(self.data_folder,
                        self.date,
                        self.drive,
                        frames=self.frames)
        self.cm = plt.get_cmap(self.pointcloud['color_map'])

        self.frame_iter = range(len(self.data_kitti.velo_files))

        self.polylidar = Polylidar3D(**self.polylidar_kwargs)
def extract_planes_and_polygons_from_mesh(tri_mesh, avg_peaks,
                                          polylidar_kwargs=dict(alpha=0.0, lmax=0.1, min_triangles=2000,
                                                                z_thresh=0.1, norm_thresh=0.95, norm_thresh_min=0.95, min_hole_vertices=50, task_threads=4),
                                          filter_polygons=True, pl_=None, optimized=False,
                                          postprocess=dict(filter=dict(hole_area=dict(min=0.025, max=100.0), hole_vertices=dict(min=6), plane_area=dict(min=0.05)),
                                                           positive_buffer=0.00, negative_buffer=0.00, simplify=0.0)):

    if pl_ is not None:
        pl = pl_
    else:
        pl = Polylidar3D(**polylidar_kwargs)

    avg_peaks_mat = MatrixDouble(avg_peaks)
    t0 = time.perf_counter()
    if optimized:
        all_planes, all_polygons = pl.extract_planes_and_polygons_optimized(tri_mesh, avg_peaks_mat)
    else:
        all_planes, all_polygons = pl.extract_planes_and_polygons(tri_mesh, avg_peaks_mat)
    t1 = time.perf_counter()

    # tri_set = pl.extract_tri_set(tri_mesh, avg_peaks_mat)
    # planes_tri_set = [np.argwhere(np.asarray(tri_set) == i)  for i in range(1, 2)]
    # o3d_mesh_painted = paint_planes(o3d_mesh, planes_tri_set)

    polylidar_time = (t1 - t0) * 1000
    # logging.info("Polygon Extraction - Took (ms): %.2f", polylidar_time)
    all_planes_shapely = []
    all_obstacles_shapely = []
    time_filter = []
    time_geometric_planes = []
    # all_poly_lines = []
    geometric_planes = []
    # all_polygons = [[NORMAL_0_POLY_1, NORMAL_0_POLY_2], [NORMAL_1_POLY_1]]
    if filter_polygons:
        vertices = np.asarray(tri_mesh.vertices)
        for i in range(avg_peaks.shape[0]):
            avg_peak = avg_peaks[i, :]
            rm, _ = R.align_vectors([[0, 0, 1]], [avg_peak])  # Rotating matrix the polygon
            polygons_for_normal = all_polygons[i]
            planes = all_planes[i]
            # print(polygons_for_normal)
            if len(polygons_for_normal) > 0:
                planes_shapely, obstacles_shapely, planes_indices, filter_time = filter_and_create_polygons(
                    vertices, polygons_for_normal, rm=rm, postprocess=postprocess)

                t3 = time.perf_counter()
                geometric_planes_for_normal = [extract_geometric_plane(plane_poly[0], planes[plane_idx], tri_mesh, avg_peak) for (
                    plane_poly, plane_idx) in zip(planes_shapely, planes_indices)]
                geometric_planes.append(geometric_planes_for_normal)
                t4 = time.perf_counter()
                time_geometric_planes.append((t4 - t3) * 1000)

                all_planes_shapely.extend(planes_shapely)
                all_obstacles_shapely.extend(obstacles_shapely)
                time_filter.append(filter_time)
                # all_poly_lines.extend(poly_lines)

    timings = dict(t_polylidar_planepoly=polylidar_time, t_polylidar_filter=np.array(time_filter).mean(), t_geometric_planes=np.array(time_geometric_planes).sum())
    # all_planes_shapely, all_obstacles_shapely, all_poly_lines, timings
    return all_planes_shapely, all_obstacles_shapely, geometric_planes, timings
Example #5
0
def test_100k_array_3d_lmax(benchmark, np_100K_array_3d, params_lmax):
    if Polylidar3D is not None:
        points_mat = MatrixDouble(np_100K_array_3d)
        pl = Polylidar3D(**params_lmax)
        _, _ , _  = benchmark(pl.extract_planes_and_polygons, points_mat)
    else:
        benchmark(extractPolygons, np_100K_array_3d, **params_lmax)
Example #6
0
def extract_polygons(opc, classes, config):
    pl = Polylidar3D(**config['polylidar'])
    ga = GaussianAccumulatorS2Beta(level=config['fastga']['level'])
    ico = IcoCharts(level=config['fastga']['level'])
    # 1. Create mesh
    tri_mesh = create_meshes_cuda(opc, **config['mesh']['filter'])

    mesh_o3d = create_open_3d_mesh_from_tri_mesh(tri_mesh)
    classes = classes.reshape((np.asarray(mesh_o3d.vertices).shape[0], 1))
    classes_mat = MatrixUInt8(classes)
    tri_mesh.set_vertex_classes(classes_mat, True)
    # alg_timings.update(timings)
    # 2. Get dominant plane normals
    avg_peaks, _, _, _, alg_timings = extract_all_dominant_plane_normals(
        tri_mesh, ga_=ga, ico_chart_=ico, **config['fastga'])

    # alg_timings.update(timings)
    # 3. Extract Planes and Polygons
    planes, obstacles, timings = extract_planes_and_polygons_from_classified_mesh(
        tri_mesh,
        avg_peaks,
        pl_=pl,
        filter_polygons=True,
        optimized=True,
        postprocess=config['polygon']['postprocess'])

    return mesh_o3d, planes, obstacles, timings
Example #7
0
def extract_planes_and_polygons_from_classified_mesh(
    tri_mesh,
    avg_peaks,
    polylidar_kwargs=dict(alpha=0.0,
                          lmax=0.1,
                          min_triangles=2000,
                          z_thresh=0.1,
                          norm_thresh=0.95,
                          norm_thresh_min=0.95,
                          min_hole_vertices=50,
                          task_threads=4),
    filter_polygons=True,
    pl_=None,
    optimized=True,
    postprocess=dict(filter=dict(hole_area=dict(min=0.025, max=100.0),
                                 hole_vertices=dict(min=6),
                                 plane_area=dict(min=0.05)),
                     positive_buffer=0.00,
                     negative_buffer=0.00,
                     simplify=0.0)):

    if pl_ is not None:
        pl = pl_
    else:
        pl = Polylidar3D(**polylidar_kwargs)

    avg_peaks_mat = MatrixDouble(avg_peaks)
    t0 = time.perf_counter()
    all_planes, all_polygons = pl.extract_planes_and_polygons_optimized_classified(
        tri_mesh, avg_peaks_mat)
    t1 = time.perf_counter()

    polylidar_time = (t1 - t0) * 1000
    all_planes_shapely = []
    all_obstacles_shapely = []
    time_filter = []
    # all_poly_lines = []
    if filter_polygons:
        vertices = np.asarray(tri_mesh.vertices)
        for i in range(avg_peaks.shape[0]):
            avg_peak = avg_peaks[i, :]
            rm, _ = R.align_vectors([[0, 0, 1]], [avg_peak])
            polygons_for_normal = all_polygons[i]
            # print(polygons_for_normal)
            if len(polygons_for_normal) > 0:
                planes_shapely, obstacles_shapely, filter_time = filter_and_create_polygons(
                    vertices,
                    polygons_for_normal,
                    rm=rm,
                    postprocess=postprocess)
                all_planes_shapely.extend(planes_shapely)
                all_obstacles_shapely.extend(obstacles_shapely)
                time_filter.append(filter_time)
                # all_poly_lines.extend(poly_lines)

    timings = dict(t_polylidar_planepoly=polylidar_time,
                   t_polylidar_filter=np.array(time_filter).sum())
    # all_planes_shapely, all_obstacles_shapely, all_poly_lines, timings
    return all_planes_shapely, all_obstacles_shapely, timings
Example #8
0
def get_planes(pl: polylidar.Polylidar3D, lidar_building, class_id, class_label, config_pp, **kwargs):
    np_mat = polylidar.MatrixDouble(lidar_building)
    mesh, planes, polygons = pl.extract_planes_and_polygons(np_mat)
    planes = filter_planes(polygons, lidar_building, config_pp)
    features = []
    for plane in planes:
        features.append(Feature(plane[0], {'class_id': class_id, 'class_label': class_label, 'height': plane[1]}))
    return features
Example #9
0
def verify_all(points, polylidar_kwargs):
    pl = Polylidar3D(**polylidar_kwargs)
    points_mat = MatrixDouble(points)
    mesh, planes, polygons = pl.extract_planes_and_polygons(points_mat)
    # Basic test to ensure no obvious errors occurred
    basic_polylidar_verification(points, mesh, planes, polygons)
    # Ensure that the polygons returned are valid
    verify_all_polygons_are_valid(polygons, points)
Example #10
0
def extract_all_dominant_planes(tri_mesh,
                                vertices,
                                polylidar_kwargs,
                                config_pp,
                                ds=50,
                                min_samples=10000):
    ga = GaussianAccumulatorS2(level=4, max_phi=180)
    ico = IcoCharts(level=4)

    triangle_normals = np.asarray(tri_mesh.triangle_normals)
    num_normals = triangle_normals.shape[0]
    triangle_normals_ds = down_sample_normals(triangle_normals)

    # Get the data
    t0 = time.perf_counter()
    ga.integrate(MatX3d(triangle_normals_ds))
    t1 = time.perf_counter()
    avg_peaks, _, _, timings = get_image_peaks(ico,
                                               ga,
                                               level=4,
                                               with_o3d=False)
    timings['t_fastga_integrate'] = (t1 - t0) * 1000
    timings['t_fastga_total'] = timings['t_fastga_integrate'] + timings[
        't_fastga_peak']

    logging.info("Processing mesh with %d triangles", num_normals)
    logging.info("Dominant Plane Normals")
    print(avg_peaks)

    avg_peaks_selected = np.copy(avg_peaks[[0, 1, 2, 3], :])
    pl = Polylidar3D(**polylidar_kwargs)
    avg_peaks_mat = MatrixDouble(avg_peaks_selected)

    # debugging purposes, ignore
    tri_set = pl.extract_tri_set(tri_mesh, avg_peaks_mat)

    t0 = time.perf_counter()
    all_planes, all_polygons = pl.extract_planes_and_polygons_optimized(
        tri_mesh, avg_peaks_mat)
    t1 = time.perf_counter()
    timings['t_polylidar_planepoly'] = (t1 - t0) * 1000

    all_poly_lines = []
    for i in range(avg_peaks_selected.shape[0]):
        avg_peak = avg_peaks[i, :]
        rm, _ = R.align_vectors([[0, 0, 1]], [avg_peak])
        polygons_for_normal = all_polygons[i]
        # print(polygons_for_normal)
        if len(polygons_for_normal) > 0:
            poly_lines, _ = filter_and_create_open3d_polygons(
                vertices, polygons_for_normal, rm=rm, config_pp=config_pp)
            all_poly_lines.extend(poly_lines)

    return all_planes, tri_set, all_poly_lines, timings
Example #11
0
def test_clusters(benchmark, cluster_groups):
    """
    Will benchmark clusters AND check that python overhead is less than 3ms (ussually <1 ms)
    """
    points = cluster_groups['points']
    polylidar_kwargs = cluster_groups['polylidar_kwargs']
    if Polylidar3D is not None:
        points_mat = MatrixDouble(points)
        pl = Polylidar3D(**polylidar_kwargs)
        _, _ , _  = benchmark(pl.extract_planes_and_polygons, points_mat)
    else:
        _, _ , _  = benchmark(extractPlanesAndPolygons, points, **polylidar_kwargs)
Example #12
0
def extract_all_dominant_planes(tri_mesh,
                                vertices,
                                polylidar_kwargs,
                                ds=50,
                                min_samples=10000):
    ga = GaussianAccumulatorS2(level=4, max_phi=180)
    triangle_normals = np.asarray(tri_mesh.triangle_normals)
    num_normals = triangle_normals.shape[0]

    # Downsample, TODO improve this
    ds_normals = int(num_normals / ds)
    to_sample = max(min([num_normals, min_samples]), ds_normals)
    ds_step = int(num_normals / to_sample)

    triangle_normals_ds = np.ascontiguousarray(
        triangle_normals[:num_normals:ds_step, :])
    # A copy occurs here for triangle_normals......if done in c++ there would be no copy
    ga.integrate(MatX3d(triangle_normals_ds))
    gaussian_normals = np.asarray(ga.get_bucket_normals())
    accumulator_counts = np.asarray(ga.get_normalized_bucket_counts())
    _, _, avg_peaks, _ = find_peaks_from_accumulator(gaussian_normals,
                                                     accumulator_counts)
    logging.info("Processing mesh with %d triangles", num_normals)
    logging.info("Dominant Plane Normals")
    print(avg_peaks)

    avg_peaks_selected = np.copy(avg_peaks[[0, 1, 2, 3], :])
    pl = Polylidar3D(**polylidar_kwargs)
    avg_peaks_mat = MatrixDouble(avg_peaks_selected)

    tri_set = pl.extract_tri_set(tri_mesh, avg_peaks_mat)
    t0 = time.perf_counter()

    all_planes, all_polygons = pl.extract_planes_and_polygons_optimized(
        tri_mesh, avg_peaks_mat)
    t1 = time.perf_counter()
    polylidar_time = (t1 - t0) * 1000

    all_poly_lines = []
    for i in range(avg_peaks_selected.shape[0]):
        avg_peak = avg_peaks[i, :]
        rm, _ = R.align_vectors([[0, 0, 1]], [avg_peak])
        polygons_for_normal = all_polygons[i]
        # print(polygons_for_normal)
        if len(polygons_for_normal) > 0:
            poly_lines, _ = filter_and_create_open3d_polygons(
                vertices, polygons_for_normal, rm=rm)
            all_poly_lines.extend(poly_lines)

    return all_planes, tri_set, all_poly_lines, polylidar_time
def extract_all_dominant_planes(tri_mesh,
                                vertices,
                                polylidar_kwargs,
                                ds=50,
                                min_samples=10000):
    ga = GaussianAccumulatorS2Beta(level=4)
    ico = IcoCharts(level=4)

    fast_ga_kwargs = dict(find_peaks_kwargs=dict(threshold_abs=15,
                                                 min_distance=1,
                                                 exclude_border=False,
                                                 indices=False),
                          cluster_kwargs=dict(t=0.28, criterion='distance'),
                          average_filter=dict(min_total_weight=0.1))

    avg_peaks, _, _, _, alg_timings = extract_all_dominant_plane_normals(
        tri_mesh, ga_=ga, ico_chart_=ico, **fast_ga_kwargs)
    logging.info("Dominant Plane Normals")
    print(avg_peaks)

    avg_peaks_selected = np.copy(avg_peaks[[0, 1, 2, 3, 4], :])
    pl = Polylidar3D(**polylidar_kwargs)
    avg_peaks_mat = MatrixDouble(avg_peaks_selected)

    tri_set = pl.extract_tri_set(tri_mesh, avg_peaks_mat)
    t0 = time.perf_counter()

    all_planes, all_polygons = pl.extract_planes_and_polygons_optimized(
        tri_mesh, avg_peaks_mat)
    t1 = time.perf_counter()
    polylidar_time = (t1 - t0) * 1000
    all_poly_lines = []
    for i in range(avg_peaks_selected.shape[0]):
        avg_peak = avg_peaks[i, :]
        rm, _ = R.align_vectors([[0, 0, 1]], [avg_peak])
        polygons_for_normal = all_polygons[i]
        # print(polygons_for_normal)
        if len(polygons_for_normal) > 0:
            poly_lines, _ = filter_and_create_open3d_polygons(
                vertices, polygons_for_normal, rm=rm)
            all_poly_lines.extend(poly_lines)

    return all_planes, tri_set, all_poly_lines, polylidar_time
Example #14
0
def extract_planes_and_polygons_from_mesh(tri_mesh, avg_peaks,
                                          polylidar_kwargs=dict(alpha=0.0, lmax=0.1, min_triangles=2000,
                                                                z_thresh=0.1, norm_thresh=0.95, norm_thresh_min=0.95, min_hole_vertices=50, task_threads=4),
                                          filter_polygons=True, pl_=None, optimized=True, **kwargs):

    if pl_ is not None:
        pl = pl_
    else:
        pl = Polylidar3D(**polylidar_kwargs)

    avg_peaks_mat = MatrixDouble(avg_peaks)
    t0 = time.perf_counter()
    if optimized:
        all_planes, all_polygons = pl.extract_planes_and_polygons_optimized(tri_mesh, avg_peaks_mat)
    else:
        all_planes, all_polygons = pl.extract_planes_and_polygons(tri_mesh, avg_peaks_mat)

    t1 = time.perf_counter()

    polylidar_time = (t1 - t0) * 1000
    logger.info("Polygon Extraction - Took (ms): %.2f", polylidar_time)
    all_poly_lines = []
    if filter_polygons:
        vertices = np.asarray(tri_mesh.vertices)
        for i in range(avg_peaks.shape[0]):
            avg_peak = avg_peaks[i, :]
            rm, _ = R.align_vectors([[0, 0, 1]], [avg_peak])
            polygons_for_normal = all_polygons[i]
            # print(polygons_for_normal)
            if len(polygons_for_normal) > 0:
                poly_lines, _ = filter_and_create_open3d_polygons(vertices, polygons_for_normal, rm=rm, **kwargs)
                all_poly_lines.extend(poly_lines)

    timings = dict(polylidar=polylidar_time)

    return all_planes, all_polygons, all_poly_lines, timings
Example #15
0
def main():

    kwargs = dict(num_groups=2, group_size=1000, dist=100.0, seed=1)
    # generate random normally distributed clusters of points, 200 X 2 numpy array.
    points = generate_test_points(**kwargs)
    lmax = get_estimated_lmax(**kwargs)
    polylidar_kwargs = dict(alpha=0.0, lmax=lmax, min_triangles=5)
    # print(polylidar_kwargs)
    # Convert Points and make Polylidar
    points_mat = MatrixDouble(points)
    polylidar = Polylidar3D(**polylidar_kwargs)
    # Extracts planes and polygons, time
    t1 = time.perf_counter()
    mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
    t2 = time.perf_counter()
    triangles = np.asarray(mesh.triangles)
    # print(triangles, triangles.shape)
    triangles = triangles.flatten()
    # print(triangles, triangles.shape)
    planes_np = np.asarray(planes)
    # print(planes_np)
    print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))

    # Plot Data
    if points.shape[0] < 100000:
        fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
        # plot points
        print("Plot Points")
        ax.scatter(points[:, 0], points[:, 1], c='k', s=20.0)
        fig.savefig("assets/scratch/Basic2DAlgorithm_pointcloud.png",
                    bbox_inches='tight',
                    pad_inches=-0.6)
        plt.show()

        print("Plot Mesh")
        fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
        # plot points
        ax.scatter(points[:, 0], points[:, 1], c='k', s=0.1)
        # plot all triangles
        plot_triangles(get_triangles_from_list(triangles, points), ax)
        fig.savefig("assets/scratch/Basic2DAlgorithm_mesh.png",
                    bbox_inches='tight',
                    pad_inches=-0.6)
        plt.show()

        print("Planes and Polygons")
        fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1)
        # plot points
        ax.scatter(points[:, 0], points[:, 1], c='k', s=0.1)
        # plot all triangles
        plot_triangles(get_triangles_from_list(triangles, points), ax)
        # plot mesh triangles
        triangle_meshes = get_colored_planar_segments(planes_np, triangles,
                                                      points)
        plot_triangle_meshes(triangle_meshes, ax)
        # plot polygons
        plot_polygons(polygons, points, ax)

        plt.axis('equal')

        plt.show()
def capture(config, video=None):
    # Configure streams
    pipeline, process_modules, filters, proj_mat, t265_device = create_pipeline(
        config)
    t265_pipeline = t265_device['pipeline']
    logging.info("Pipeline Created")

    # Long lived objects. These are the object that hold all the algorithms for surface exraction.
    # They need to be long lived (objects) because they hold state (thread scheduler, image datastructures, etc.)
    ll_objects = dict()
    ll_objects['pl'] = Polylidar3D(**config['polylidar'])
    ll_objects['ga'] = GaussianAccumulatorS2(level=config['fastga']['level'])
    ll_objects['ico'] = IcoCharts(level=config['fastga']['level'])

    if video:
        frame_width = config['color']['width'] * 2
        frame_height = config['color']['height']
        out_vid = cv2.VideoWriter(video,
                                  cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'),
                                  30, (frame_width, frame_height))

    all_records = []
    counter = 0
    try:
        while True:
            t00 = time.perf_counter()
            try:
                color_image, depth_image, meta = get_frames(
                    pipeline, t265_pipeline, process_modules, filters, config)
            except RuntimeError:
                # This only gets thrown when in playback mode from a recoded file when frames "run out"
                logging.info("Out of frames")
                break
            t0 = time.perf_counter()
            if color_image is None or not valid_frames(
                    color_image, depth_image, **
                    config['polygon']['frameskip']):
                logging.debug("Invalid Frames")
                continue
            t1 = time.perf_counter()
            counter += 1
            # if counter < 10:
            #     continue

            try:
                # Get 6DOF Pose at appropriate timestamp
                if config['tracking']['enabled']:
                    euler_t265 = get_pose_matrix(meta['ts'])
                    # logging.info('euler_t265: %r', euler_t265)

                if config['show_polygon']:
                    # planes, obstacles, geometric_planes, timings, o3d_mesh = get_polygon(depth_image, config, ll_objects, **meta)
                    planes, obstacles, geometric_planes, timings = get_polygon(
                        depth_image, config, ll_objects, **meta)
                    timings['t_get_frames'] = (t0 - t00) * 1000
                    timings['t_check_frames'] = (t1 - t0) * 1000
                    all_records.append(timings)

                    curb_height = analyze_planes(geometric_planes)
                    ser.write(("{:.2f}".format(curb_height) + "\n").encode())

                    # Plot polygon in rgb frame
                    plot_planes_and_obstacles(planes, obstacles, proj_mat,
                                              None, color_image, config)

                # Show images
                if config.get("show_images"):
                    # Convert to open cv image types (BGR)
                    color_image_cv, depth_image_cv = colorize_images_open_cv(
                        color_image, depth_image, config)
                    # Stack both images horizontally
                    images = np.hstack((color_image_cv, depth_image_cv))
                    cv2.imshow('RealSense Color/Depth (Aligned)', images)
                    if video:
                        out_vid.write(images)
                    res = cv2.waitKey(1)
                    if res == ord('p'):
                        uid = uuid.uuid4()
                        logging.info("Saving Picture: {}".format(uid))
                        cv2.imwrite(
                            path.join(PICS_DIR, "{}_color.jpg".format(uid)),
                            color_image_cv)
                        cv2.imwrite(
                            path.join(PICS_DIR, "{}_stack.jpg".format(uid)),
                            images)
                    if res == ord('m'):
                        plt.imshow(
                            np.asarray(ll_objects['ico'].image_to_vertex_idx))
                        plt.show()
                        plt.imshow(np.asarray(ll_objects['ico'].mask))
                        plt.show()
                        plt.imshow(np.asarray(ll_objects['ico'].image))
                        plt.show()
                        import ipdb
                        ipdb.set_trace()

                    to_save_frames = config['save'].get('frames')
                    if config['playback'][
                            'enabled'] and to_save_frames is not None and counter in to_save_frames:
                        logging.info("Saving Picture: {}".format(counter))
                        cv2.imwrite(
                            path.join(PICS_DIR,
                                      "{}_color.jpg".format(counter)),
                            color_image_cv)
                        cv2.imwrite(
                            path.join(PICS_DIR,
                                      "{}_stack.jpg".format(counter)), images)

                # logging.info(f"Frame %d; Get Frames: %.2f; Check Valid Frame: %.2f; Laplacian: %.2f; Bilateral: %.2f; Mesh: %.2f; FastGA: %.2f; Plane/Poly: %.2f; Filtering: %.2f; Geometric Planes: %.2f; Curb Height: %.2f",
                #              counter, timings['t_get_frames'], timings['t_check_frames'], timings['t_laplacian'], timings['t_bilateral'], timings['t_mesh'], timings['t_fastga_total'],
                #              timings['t_polylidar_planepoly'], timings['t_polylidar_filter'], timings['t_geometric_planes'] curb_height)
                logging.info(f"Curb Height: %.2f", curb_height)
            except Exception as e:
                logging.exception("Error!")
    finally:
        pipeline.stop()
    if video is not None:
        out_vid.release()
    cv2.destroyAllWindows()
Example #17
0
def run_test(pcd, rgbd, intrinsics, extrinsics, bp_alg=dict(radii=[0.02, 0.02]), poisson=dict(depth=8), callback=None, stride=2):
    """Demonstrate Polygon Extraction on both unorganized and organized point cloud

    Args:
        pcd (o3d.geometry.PointCloud): Open3D point cloud
        rgbd (np.ndarray): MXN Numpy array
        intrinsics (np.ndarray): 3X3 numpy array of camera intrinsics (assume pin hole model)
        extrinsics (np.ndarray): 4X4 numpy array of extrinsics of camera
        bp_alg (dict, optional): Arguments to Open3D ball pivot alg. Defaults to dict(radii=[0.02, 0.02]).
        poisson (dict, optional): Arguments to Open3D Poisson surface reconstruction. Defaults to dict(depth=8).
        callback (function, optional): Callback function for visualization. Defaults to None.
        stride (int, optional): Skip rows/columns in rgbd. Defaults to 2.

    """
    points = np.asarray(pcd.points)
    polylidar_kwargs = dict(alpha=0.0, lmax=0.10, min_triangles=100,
                            z_thresh=0.04, norm_thresh=0.90, norm_thresh_min=0.90, min_hole_vertices=6)
    pl = Polylidar3D(**polylidar_kwargs)

    ################################################################################
    ##### Treat data as an unorganized point clouds
    ##### Create Surface Mesh using 2.5 Delaunay Triangulation and extract Polygons
    ################################################################################
    points_mat = MatrixDouble(points)
    t1 = time.perf_counter()
    mesh, planes, polygons = pl.extract_planes_and_polygons(points_mat)
    t2 = time.perf_counter()

    # Visualization of mesh and polygons
    all_poly_lines = filter_and_create_open3d_polygons(points, polygons)
    triangles = np.asarray(mesh.triangles)
    mesh_2d_polylidar = extract_mesh_planes(points, triangles, planes, mesh.counter_clock_wise, COLOR_PALETTE[0])
    mesh_2d_polylidar.extend(flatten([line_mesh.cylinder_segments for line_mesh in all_poly_lines]))
    time_mesh_2d_polylidar = (t2 - t1) * 1000
    polylidar_alg_name = 'Treated as **Unorganized** Point Cloud - 2.5D Delaunay Triangulation with Polygon Extraction'
    callback(polylidar_alg_name, time_mesh_2d_polylidar, pcd, mesh_2d_polylidar)

    ################################################################################
    ###### Treat data as an **Organized** 3D Point Cloud #########
    ###### Creates a true 3D mesh and is much master using organized structure of point cloud 
    ################################################################################
    tri_mesh, t_mesh_creation = make_uniform_grid_mesh(np.asarray(
        rgbd.depth), np.ascontiguousarray(intrinsics.intrinsic_matrix), extrinsics, stride=stride)

    # Visualization of only the mesh
    tri_mesh_o3d = create_open_3d_mesh_from_tri_mesh(tri_mesh)
    uniform_alg_name = 'Treated as **Organized** Point Cloud - Right-Cut Triangulation/Uniform Mesh (Mesh only)'
    callback(uniform_alg_name, t_mesh_creation, pcd, tri_mesh_o3d)

    # Exctact Polygons with Polylidar3D using the Uniform Mesh. Dominant Plane normal is 0,0,1.
    t1 = time.perf_counter()
    planes, polygons = pl.extract_planes_and_polygons(tri_mesh)
    t2 = time.perf_counter()

    # Visualization of mesh and polygons
    vertices_np = np.asarray(tri_mesh.vertices)
    triangles_np = np.asarray(tri_mesh.triangles)

    all_poly_lines = filter_and_create_open3d_polygons(vertices_np, polygons)
    mesh_3d_polylidar = extract_mesh_planes(vertices_np, triangles_np, planes, tri_mesh.counter_clock_wise)
    mesh_3d_polylidar.extend(flatten([line_mesh.cylinder_segments for line_mesh in all_poly_lines]))
    time_polylidar3D = (t2 - t1) * 1000
    polylidar_3d_alg_name = 'Polygon Extraction on Uniform Mesh (only one dominant plane normal)'
    callback(polylidar_3d_alg_name, time_polylidar3D,
             create_open3d_pc(vertices_np), mesh_3d_polylidar)
Example #18
0
def main():
    points = generate_point_cloud()
    points_mat = MatrixDouble(points)
    polylidar_kwargs = dict(alpha=0.0,
                            lmax=1.0,
                            min_triangles=20,
                            z_thresh=0.2,
                            norm_thresh=0.98)
    polylidar = Polylidar3D(**polylidar_kwargs)
    t1 = time.perf_counter()
    # Create Pseudo 3D Surface Mesh using Delaunay Triangulation and Polylidar
    mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
    t2 = time.perf_counter()
    print("Point Size: {}".format(points.shape[0]))
    print(
        "2.5D Delaunay Triangulation and Plane Extraction; Mesh Creation {:.2f} milliseconds"
        .format((t2 - t1) * 1000))

    # Visualize
    # Create Open3D Mesh
    mesh_planes = extract_mesh_planes(points, mesh, planes, COLOR_PALETTE[0])
    # Create Open 3D Point Cloud
    pcd = create_open3d_pc(points)
    o3d.visualization.draw_geometries([pcd, *mesh_planes])

    # Estimate Point Cloud Normals
    t0 = time.perf_counter()
    pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(
        radius=1.0, max_nn=8))
    t1 = time.perf_counter()
    # Create True 3D Surface Mesh using Ball Pivot Algorithm
    radii = o3d.utility.DoubleVector([0.4, 0.5])
    mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
        pcd, radii)
    mesh.paint_uniform_color(COLOR_PALETTE[0])
    t2 = time.perf_counter()
    print(
        "3D Triangulation using Ball Pivot: Normal Estimation took: {:.2f}, Mesh Creation: {:.2f}"
        .format((t1 - t0) * 1000, (t2 - t1) * 1000))
    # Visualize
    o3d.visualization.draw_geometries([pcd, mesh])

    # Create True 3D Surface Mesh using Poisson Reconstruction Algorithm
    t3 = time.perf_counter()
    mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
        pcd, depth=6)
    t4 = time.perf_counter()
    print(
        "3D Triangulation using Poisson Reconstruction: Mesh Creation: {:.2f}".
        format((t4 - t3) * 1000))
    # Visualize
    # o3d.visualization.draw_geometries([mesh])
    # print(mesh)
    # print('visualize densities')
    densities = np.asarray(densities)
    density_colors = plt.get_cmap('plasma')(
        (densities - densities.min()) / (densities.max() - densities.min()))
    density_colors = density_colors[:, :3]
    density_mesh = o3d.geometry.TriangleMesh()
    density_mesh.vertices = mesh.vertices
    density_mesh.triangles = mesh.triangles
    density_mesh.triangle_normals = mesh.triangle_normals
    density_mesh.vertex_colors = o3d.utility.Vector3dVector(density_colors)
    # mesh.vertex_colors = o3d.utility.Vector3dVector(density_colors)
    # o3d.visualization.draw_geometries([density_mesh])

    # print('remove low density vertices')
    vertices_to_remove = densities < np.quantile(densities, 0.1)
    mesh.remove_vertices_by_mask(vertices_to_remove)
    mesh.compute_triangle_normals()
    mesh.paint_uniform_color(COLOR_PALETTE[0])
    # print(mesh)
    o3d.visualization.draw_geometries([pcd, mesh])
Example #19
0
def capture(config, video=None):
    # Configure streams
    pipeline, process_modules, filters, proj_mat, t265_device = create_pipeline(config)
    t265_pipeline = t265_device['pipeline']
    logging.info("Pipeline Created")

    # Long lived objects. These are the object that hold all the algorithms for surface exraction.
    # They need to be long lived (objects) because they hold state (thread scheduler, image datastructures, etc.)
    ll_objects = dict()
    ll_objects['pl'] = Polylidar3D(**config['polylidar'])
    ll_objects['ga'] = GaussianAccumulatorS2(level=config['fastga']['level'])
    ll_objects['ico'] = IcoCharts(level=config['fastga']['level'])

    if video:
        frame_width = config['color']['width'] * 2
        frame_height = config['color']['height']
        out_vid = cv2.VideoWriter(video, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'), 30, (frame_width, frame_height))

    # Create Homogenous Transform from sensor frame to wheel chair frame
    sensor_mount_frame = config['frames']['sensor_mount']
    sensor_frame = config['frames']['sensor']
    sensor_to_wheel_chair_transform = create_transform(np.array(sensor_mount_frame['translation']), sensor_mount_frame['rotation']) \
        @ create_transform(sensor_frame['translation'], sensor_frame['rotation'])

    # print(sensor_to_wheel_chair_transform)
    # sys.exit()
    all_records = []
    counter = 0
    try:
        while True:
            t00 = time.perf_counter()
            try:
                color_image, depth_image, meta = get_frames(pipeline, t265_pipeline, process_modules, filters, config)
            except RuntimeError:
                # This only gets thrown when in playback mode from a recoded file when frames "run out"
                logging.info("Out of frames")
                break
            t0 = time.perf_counter()
            if color_image is None or not valid_frames(color_image, depth_image, **config['polygon']['frameskip']):
                logging.debug("Invalid Frames")
                continue
            t1 = time.perf_counter()
            counter += 1
            if counter < 1:
                continue

            try:
                # Get 6DOF Pose at appropriate timestamp
                if config['tracking']['enabled']:
                    euler_t265 = get_pose_matrix(meta['ts'])
                    logging.info('euler_t265: %r', euler_t265)

                # flag to write results
                have_results = False
                if config['show_polygon']:
                    # planes, obstacles, geometric_planes, timings, o3d_mesh = get_polygon(depth_image, config, ll_objects, **meta)
                    planes, obstacles, geometric_planes, timings = get_polygon(depth_image, config, ll_objects, **meta)
                    timings['t_get_frames'] = (t0 - t00) * 1000
                    timings['t_check_frames'] = (t1 - t0) * 1000
                    all_records.append(timings)

                    curb_height, first_plane, second_plane = analyze_planes_updated(geometric_planes)
                    fname = config['playback']['file'].split('/')[1].split('.')[0]
                    # dump(dict(first_plane=first_plane, second_plane=second_plane), f"data/scratch_test/planes_{fname}_{counter:04}.joblib")
                    
                    # curb height must be greater than 2 cm and first_plane must have been found
                    if curb_height > 0.02 and first_plane is not None:
                        top_plane = choose_plane(first_plane, second_plane)
                        top_points, top_normal = top_plane['all_points'], top_plane['normal_ransac']
                        filtered_top_points = filter_points(top_points)  # <100 us
                        top_points_2d, height, all_fit_lines, best_fit_lines = extract_lines_wrapper(filtered_top_points, top_normal, return_only_one_line=True, **config['linefitting'])
                        if best_fit_lines:
                            #If there are two lines only choose the first one
                            platform_center_sensor_frame = best_fit_lines[0]['hplane_point'] 
                            platform_normal_sensor_frame = best_fit_lines[0]['hplane_normal']
                            # print(platform_center_sensor_frame, platform_normal_sensor_frame)
                            result = get_turning_manuever(platform_center_sensor_frame, platform_normal_sensor_frame, \
                                        sensor_to_wheel_chair_transform, poi_offset=config.get('poi_offset', 0.7), debug=False)
                            plt.show()

                            orthog_dist = result['ortho_dist_platform']
                            distance_of_interest = result['dist_poi']
                            orientation = result['beta']
                            initial_turn = result['first_turn']
                            final_turn= result['second_turn']

                            logging.info("Frame #: %s, Orthogonal Distance to Platform: %.02f m, Orientation: %0.1f Degrees, Distance to POI: %.02f m, " \
                                "First Turn: %.01f degrees, Second Turn: %.01f degrees", 
                                         counter, orthog_dist, orientation, distance_of_interest, initial_turn, final_turn)
                            
                            plot_points(best_fit_lines[0]['square_points'], proj_mat, color_image, config)
                            # plot_points(best_fit_lines[0]['points_3d_orig'], proj_mat, color_image, config)
                            if len(best_fit_lines) > 2: 
                                plot_points(best_fit_lines[1]['square_points'], proj_mat, color_image, config)
                            have_results = True
                        else:
                            logging.warning("Line Detector Failed")
                    else:
                        logging.warning("Couldn't find the street and sidewalk surface")

                    # sys.exit()
                    # Plot polygon in rgb frame
                    plot_planes_and_obstacles(planes, obstacles, proj_mat, None, color_image, config)

                    # import ipdb; ipdb.set_trace()
                # Show images
                if config.get("show_images"):
                    # Convert to open cv image types (BGR)
                    color_image_cv, depth_image_cv = colorize_images_open_cv(color_image, depth_image, config)
                    # Stack both images horizontally
                    images = np.hstack((color_image_cv, depth_image_cv))
                    if have_results:
                        cv2.putText(images,'Curb Height: '"{:.2f}" 'm'.format(curb_height), (20,360), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)
                        cv2.putText(images,'Orthogonal Distance: '"{:.2f}" 'm'.format(orthog_dist), (20,380), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)
                        cv2.putText(images,'Distance to Point of Interest: '"{:.2f}" 'm'.format(distance_of_interest), (20,400), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)
                        cv2.putText(images,'Initial Turn: '"{:.2f}" 'deg'.format(initial_turn), (20,420), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)
                        cv2.putText(images,'Orientation: '"{:.2f}" 'deg'.format(orientation), (20,440), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)
                        cv2.putText(images,'Angle for final turn: '"{:.2f}" 'deg'.format(final_turn), (20,460), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1, cv2.LINE_AA)
                        cv2.imshow('RealSense Color/Depth (Aligned)', images)
                        # visualize_2d(top_points_2d, top_points_2d, all_fit_lines, best_fit_lines)
                    
                    if video:
                        out_vid.write(images)

                    res = cv2.waitKey(1)
                    if res == ord('p'):
                        uid = uuid.uuid4()
                        logging.info("Saving Picture: {}".format(uid))
                        cv2.imwrite(path.join(PICS_DIR, "{}_color.jpg".format(uid)), color_image_cv)
                        cv2.imwrite(path.join(PICS_DIR, "{}_stack.jpg".format(uid)), images)
                    if res == ord('m'):
                        plt.imshow(np.asarray(ll_objects['ico'].image_to_vertex_idx))
                        plt.show()
                        plt.imshow(np.asarray(ll_objects['ico'].mask))
                        plt.show()
                        plt.imshow(np.asarray(ll_objects['ico'].image))
                        plt.show()                  
                    

                    to_save_frames = config['save'].get('frames')
                    if config['playback']['enabled'] and to_save_frames is not None and counter in to_save_frames:
                        logging.info("Saving Picture: {}".format(counter))
                        cv2.imwrite(path.join(PICS_DIR, "{}_color.jpg".format(counter)), color_image_cv)
                        cv2.imwrite(path.join(PICS_DIR, "{}_stack.jpg".format(counter)), images)

                # logging.info(f"Frame %d; Get Frames: %.2f; Check Valid Frame: %.2f; Laplacian: %.2f; Bilateral: %.2f; Mesh: %.2f; FastGA: %.2f; Plane/Poly: %.2f; Filtering: %.2f; Geometric Planes: %.2f",
                #              counter, timings['t_get_frames'], timings['t_check_frames'], timings['t_laplacian'], timings['t_bilateral'], timings['t_mesh'], timings['t_fastga_total'],
                #              timings['t_polylidar_planepoly'], timings['t_polylidar_filter'], timings['t_geometric_planes'])
                logging.info(f"Curb Height: %.2f", curb_height)
                
            except Exception as e:
                logging.exception("Error!")
    finally:
        pipeline.stop()
    if video is not None:
        out_vid.release()
    cv2.destroyAllWindows()
Example #20
0
def main():
    np.random.seed(1)
    # generate random plane with hole
    plane = generate_3d_plane(bounds_x=[0, 10, 0.5], bounds_y=[0, 10, 0.5], holes=[
        [[3, 5], [3, 5]]], height_noise=0.02, planar_noise=0.02)
    # Generate top of box (causing the hole that we see)
    box_top = generate_3d_plane(bounds_x=[3, 5, 0.2], bounds_y=[3, 5, 0.2], holes=[
    ], height_noise=0.02, height=2, planar_noise=0.02)
    # Generate side of box (causing the hole that we see)
    box_side = generate_3d_plane(bounds_x=[0, 2, 0.2], bounds_y=[
        0, 2, 0.2], holes=[], height_noise=0.02, planar_noise=0.02)
    rm = rotation_matrix([0, 1, 0], -math.pi / 2.0)
    box_side = apply_rotation(rm, box_side) + [5, 3, 0]
    # All points joined together
    points = np.concatenate((plane, box_side, box_top))

    points_mat = MatrixDouble(points)
    polylidar_kwargs = dict(alpha=0.0, lmax=1.0, min_triangles=20, z_thresh=0.1, norm_thresh_min=0.94)
    polylidar = Polylidar3D(**polylidar_kwargs)

    elev = 15.0
    azim = -35

    # Show Point Cloud
    print("Should see point raw point cloud")
    fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
                           subplot_kw=dict(projection='3d'))
    # plot points
    ax.scatter(*scale_points(points), s=20.0, c=points[:, 2], cmap=plt.cm.plasma)
    set_axes_equal(ax)
    ax.view_init(elev=elev, azim=azim)
    fig.savefig("assets/scratch/Basic25DAlgorithm_pointcloud.pdf", bbox_inches='tight')
    fig.savefig("assets/scratch/Basic25DAlgorithm_pointcloud.png", bbox_inches='tight', pad_inches=-0.8)
    plt.show()

    # Extracts planes and polygons, time
    t1 = time.time()
    mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
    t2 = time.time()
    print("Polylidar Took {:.2f} milliseconds".format((t2 - t1) * 1000))

    triangles = np.asarray(mesh.triangles)
    all_planes = [np.arange(triangles.shape[0])]

    # Show Triangulation
    fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
                           subplot_kw=dict(projection='3d'))

    plot_planes_3d(points, triangles, all_planes, ax, alpha=0.0, z_value=-4.0)
    plot_planes_3d(points, triangles, all_planes, ax, alpha=0.5)
    # plot points
    ax.scatter(*scale_points(points), s=0.1, c='k')
    set_axes_equal(ax, ignore_z=True)
    ax.set_zlim3d([-4, 6])
    ax.view_init(elev=elev, azim=azim)
    print("Should see triangulation point cloud")
    fig.savefig("assets/scratch/Basic25DAlgorithm_mesh.pdf", bbox_inches='tight')
    fig.savefig("assets/scratch/Basic25DAlgorithm_mesh.png", bbox_inches='tight', pad_inches=-0.8)
    plt.show()

    print("")
    print("Should see two planes extracted, please rotate.")
    fig, ax = plt.subplots(figsize=(10, 10), nrows=1, ncols=1,
                           subplot_kw=dict(projection='3d'))
    # plot all triangles
    plot_polygons_3d(points, polygons, ax)
    plot_planes_3d(points, triangles, planes, ax)
    # plot points
    ax.scatter(*scale_points(points), c='k', s=0.1)
    set_axes_equal(ax)
    ax.view_init(elev=elev, azim=azim)
    fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.pdf", bbox_inches='tight')
    fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.png", bbox_inches='tight', pad_inches=-0.8)
    plt.show()
    print("")
Example #21
0
def capture(config, video=None):
    # Configure streams
    pipeline, process_modules, filters, proj_mat, t265_device = create_pipeline(
        config)
    t265_pipeline = t265_device['pipeline']
    logging.info("Pipeline Created")

    # Long lived objects. These are the object that hold all the algorithms for surface exraction.
    # They need to be long lived (objects) because they hold state (thread scheduler, image datastructures, etc.)
    ll_objects = dict()
    ll_objects['pl'] = Polylidar3D(**config['polylidar'])
    ll_objects['ga'] = GaussianAccumulatorS2(level=config['fastga']['level'])
    ll_objects['ico'] = IcoCharts(level=config['fastga']['level'])

    if video:
        frame_width = config['color']['width'] * 2
        frame_height = config['color']['height']
        out_vid = cv2.VideoWriter(video,
                                  cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'),
                                  30, (frame_width, frame_height))

    all_records = []
    counter = 0
    try:
        while True:
            t00 = time.perf_counter()
            try:
                color_image, depth_image, meta = get_frames(
                    pipeline, t265_pipeline, process_modules, filters, config)
            except RuntimeError:
                # This only gets thrown when in playback mode from a recoded file when frames "run out"
                logging.info("Out of frames")
                break
            t0 = time.perf_counter()
            if color_image is None or not valid_frames(
                    color_image, depth_image, **
                    config['polygon']['frameskip']):
                logging.debug("Invalid Frames")
                continue
            t1 = time.perf_counter()
            counter += 1
            if counter < 1:
                continue

            try:
                # Get 6DOF Pose at appropriate timestamp
                if config['tracking']['enabled']:
                    euler_t265 = get_pose_matrix(meta['ts'])
                    logging.info('euler_t265: %r', euler_t265)

                if config['show_polygon']:
                    # planes, obstacles, geometric_planes, timings, o3d_mesh = get_polygon(depth_image, config, ll_objects, **meta)
                    planes, obstacles, geometric_planes, timings = get_polygon(
                        depth_image, config, ll_objects, **meta)
                    timings['t_get_frames'] = (t0 - t00) * 1000
                    timings['t_check_frames'] = (t1 - t0) * 1000
                    all_records.append(timings)

                    curb_height, first_plane, second_plane = analyze_planes(
                        geometric_planes)

                    # curb height must be greater than 2 cm and first_plane must have been found
                    if curb_height > 0.02 and first_plane is not None:
                        square_points, normal_svm, center = hplane(
                            first_plane, second_plane)
                        dist, theta = get_theta_and_distance(
                            normal_svm, center, first_plane['normal_ransac'])
                        logging.info(
                            "Frame #: %s, Distance: %.02f meters, Theta: %.01f degrees",
                            counter, dist, theta)
                        plot_points(square_points, proj_mat, color_image,
                                    config)
                        # dump(dict(first_plane=first_plane, second_plane=second_plane), 'data/planes.joblib')
                    else:
                        logging.warning(
                            "Couldn't find the street and sidewalk surface")

                    # Send arduino curb height, distance to curb, and Angle to the curb
                    # ser.write(("{:.2f}".format(curb_height)+"\n").encode())
                    # ser.write(("{:.2f}".format(dist)+"\n").encode())
                    # ser.write(("{:.2f}".format(theta)+"\n").encode())

                    # Send RPi through Socket curb height, distance to curb, and Angle to the curb
                    s = socket.socket()
                    host = "169.254.41.103"  #This is your Server IP!
                    port = 2345
                    s.connect((host, port))
                    data = struct.pack('!d', ("{:.2f}".format(curb_height)))
                    s.send(data)
                    rece = s.recv(1024)
                    print("Received", rece)
                    s.close()

                    # sys.exit()
                    # Plot polygon in rgb frame
                    plot_planes_and_obstacles(planes, obstacles, proj_mat,
                                              None, color_image, config)

                    # import ipdb; ipdb.set_trace()
                # Show images
                if config.get("show_images"):
                    # Convert to open cv image types (BGR)
                    color_image_cv, depth_image_cv = colorize_images_open_cv(
                        color_image, depth_image, config)
                    # Stack both images horizontally
                    images = np.hstack((color_image_cv, depth_image_cv))
                    cv2.putText(
                        images, 'Curb Height: '
                        "{:.2f}"
                        'm'.format(curb_height), (10, 200),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1,
                        cv2.LINE_AA)
                    cv2.putText(
                        images, 'Distance from Curb: '
                        "{:.2f}"
                        'm'.format(dist), (10, 215), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, (0, 255, 0), 1, cv2.LINE_AA)
                    cv2.putText(
                        images, 'Angle to the Curb: '
                        "{:.2f}"
                        'deg'.format(theta), (10, 230),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1,
                        cv2.LINE_AA)
                    cv2.imshow('RealSense Color/Depth (Aligned)', images)

                    if video:
                        out_vid.write(images)

                    res = cv2.waitKey(1)
                    if res == ord('p'):
                        uid = uuid.uuid4()
                        logging.info("Saving Picture: {}".format(uid))
                        cv2.imwrite(
                            path.join(PICS_DIR, "{}_color.jpg".format(uid)),
                            color_image_cv)
                        cv2.imwrite(
                            path.join(PICS_DIR, "{}_stack.jpg".format(uid)),
                            images)
                    if res == ord('m'):
                        plt.imshow(
                            np.asarray(ll_objects['ico'].image_to_vertex_idx))
                        plt.show()
                        plt.imshow(np.asarray(ll_objects['ico'].mask))
                        plt.show()
                        plt.imshow(np.asarray(ll_objects['ico'].image))
                        plt.show()

                        # import ipdb; ipdb.set_trace()

                    to_save_frames = config['save'].get('frames')
                    if config['playback'][
                            'enabled'] and to_save_frames is not None and counter in to_save_frames:
                        logging.info("Saving Picture: {}".format(counter))
                        cv2.imwrite(
                            path.join(PICS_DIR,
                                      "{}_color.jpg".format(counter)),
                            color_image_cv)
                        cv2.imwrite(
                            path.join(PICS_DIR,
                                      "{}_stack.jpg".format(counter)), images)

                # logging.info(f"Frame %d; Get Frames: %.2f; Check Valid Frame: %.2f; Laplacian: %.2f; Bilateral: %.2f; Mesh: %.2f; FastGA: %.2f; Plane/Poly: %.2f; Filtering: %.2f; Geometric Planes: %.2f; Curb Height: %.2f",
                #              counter, timings['t_get_frames'], timings['t_check_frames'], timings['t_laplacian'], timings['t_bilateral'], timings['t_mesh'], timings['t_fastga_total'],
                #              timings['t_polylidar_planepoly'], timings['t_polylidar_filter'], timings['t_geometric_planes'] curb_height)
                logging.info(f"Curb Height: %.2f", curb_height)

            except Exception as e:
                logging.exception("Error!")
    finally:
        pipeline.stop()
    if video is not None:
        out_vid.release()
    cv2.destroyAllWindows()
Example #22
0
def main():
    np.random.seed(1)
    # generate random plane with hole
    plane = generate_3d_plane(bounds_x=[0, 10, 0.5],
                              bounds_y=[0, 10, 0.5],
                              holes=[[[3, 5], [3, 5]]],
                              height_noise=0.02,
                              planar_noise=0.02)
    # Generate top of box (causing the hole that we see)
    box_top = generate_3d_plane(bounds_x=[3, 5, 0.2],
                                bounds_y=[3, 5, 0.2],
                                holes=[],
                                height_noise=0.02,
                                height=2,
                                planar_noise=0.02)
    # Generate side of box (causing the hole that we see)
    box_side = generate_3d_plane(bounds_x=[0, 2, 0.2],
                                 bounds_y=[0, 2, 0.2],
                                 holes=[],
                                 height_noise=0.02,
                                 planar_noise=0.02)
    rm = rotation_matrix([0, 1, 0], -math.pi / 2.0)
    box_side = apply_rotation(rm, box_side) + [5, 3, 0]
    # All points joined together
    points = np.concatenate((plane, box_side, box_top))

    points_mat = MatrixDouble(points)
    polylidar_kwargs = dict(alpha=0.0,
                            lmax=1.0,
                            min_triangles=20,
                            z_thresh=0.1,
                            norm_thresh_min=0.94)
    polylidar = Polylidar3D(**polylidar_kwargs)

    fig, ax = plt.subplots(figsize=(10, 10),
                           nrows=1,
                           ncols=1,
                           subplot_kw=dict(projection='3d'))
    # plot points
    ax.scatter(*scale_points(points),
               s=2.5,
               c=points[:, 2],
               cmap=plt.cm.plasma)
    set_axes_equal(ax)
    ax.view_init(elev=15., azim=-35)
    plt.show()

    # Extracts planes and polygons, time
    t1 = time.time()
    mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
    t2 = time.time()
    print("Took {:.2f} milliseconds".format((t2 - t1) * 1000))
    print("Should see two planes extracted, please rotate.")

    triangles = np.asarray(mesh.triangles)
    fig, ax = plt.subplots(figsize=(10, 10),
                           nrows=1,
                           ncols=1,
                           subplot_kw=dict(projection='3d'))
    # plot all triangles
    plot_planes_3d(points, triangles, planes, ax)
    plot_polygons_3d(points, polygons, ax)
    # plot points
    ax.scatter(*scale_points(points), c='k', s=0.1)
    set_axes_equal(ax)
    ax.view_init(elev=15., azim=-35)
    plt.show()
    print("")
Example #23
0
def evaluate_with_params(param_set,
                         param_index,
                         variance,
                         counter=None,
                         data='train'):
    """Evaluates polylidar against a set of parameters and writes results to a csv file

    Arguments:
        param_set {List[Dict]} -- A list of parameters dictionaries
        param_index {int} -- A unique index of the param_set if it was split from a master list
        variance {int} -- The level of variance of files to pull from SynPEB

    Keyword Arguments:
        counter {Value} -- Multiprocessing Value for incementing progress in safe manner (default: {None})
        data {str} -- To pull from training or testing (default: {'train'})
    """
    from polylidar_plane_benchmark.utility.helper import (
        convert_planes_to_classified_point_cloud, load_pcd_file,
        predict_loops_laplacian, extract_all_dominant_plane_normals,
        extract_planes_and_polygons_from_mesh)
    from polylidar_plane_benchmark.utility.evaluate import evaluate
    from polylidar_plane_benchmark.utility.helper_mesh import create_meshes_cuda

    from polylidar import Polylidar3D
    from fastga import GaussianAccumulatorS2, IcoCharts

    all_fpaths = get_fpaths(variance, data=data)
    optimized = data == 'test'

    # Create Long Lived Objects Only Once
    ga = GaussianAccumulatorS2(
        level=level_default)  # Fast Gaussian Accumulator
    ico_chart = IcoCharts(
        level=level_default
    )  # Used for unwrapping S2 to Image for peak detection
    pl = Polylidar3D(
        **polylidar_kwargs_default)  # Region Growing and Polygons Extraction

    # logger_train.warn("Working on variance %r, param index %r with %r elements", variance, param_index, len(param_set))
    csv_fpath = get_csv_fpath(variance, param_index, data=data)
    with open(csv_fpath, 'w', newline='') as csv_file:
        fieldnames = [
            'variance', 'fname', 'tcomp', 'kernel_size', 'loops_bilateral',
            'loops_laplacian', 'sigma_angle', 'norm_thresh_min', 'stride',
            'min_triangles', 'predict_loops_laplacian', 'n_gt', 'n_ms_all',
            'f_weighted_corr_seg', 'f_corr_seg', 'n_corr_seg', 'n_over_seg',
            'n_under_seg', 'n_missed_seg', 'n_noise_seg', 'rmse', 'laplacian',
            'bilateral', 'mesh', 'fastga_total', 'fastga_integrate',
            'fastga_peak', 'polylidar'
        ]
        writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
        writer.writeheader()
        try:
            for fpath in all_fpaths:
                fname = pathlib.Path(fpath).name
                logger_train.info("Working on file %s", fpath)

                mesh_kwargs_previous = dict()
                tri_mesh = None
                timings_mesh = None

                prev_stride = 0
                pc_raw = None
                pc_image = None
                for params in param_set:
                    stride = params.get('stride', 2)
                    if stride != prev_stride:
                        pc_raw, pc_image = load_pcd_file(str(fpath),
                                                         stride=stride)
                        prev_stride = stride
                    else:
                        logger_train.debug(
                            "Reusing previously loaded point cloud")

                    # Predict laplacian loops needed by noisiness of point cloud
                    if params.get('predict_loops_laplacian'):
                        params['loops_laplacian'] = predict_loops_laplacian(
                            pc_image, stride)
                        logger_train.info("Set laplacian loops to :%r",
                                          params['loops_laplacian'])

                    # Update Polylidar Kwargs
                    pl.norm_thresh = params['norm_thresh_min']
                    pl.norm_thresh_min = params['norm_thresh_min']
                    pl.min_triangles = params.get('min_triangles', 250)

                    t1 = time.perf_counter()
                    mesh_kwargs = {
                        k: params[k]
                        for k in ('loops_laplacian', 'kernel_size',
                                  'loops_bilateral', 'sigma_angle')
                    }
                    # Create Smoothed TriMesh
                    if mesh_kwargs_previous != mesh_kwargs:
                        tri_mesh, mesh_timings = create_meshes_cuda(
                            pc_image, **mesh_kwargs)
                        mesh_kwargs_previous = mesh_kwargs
                    else:
                        logger_train.debug("Reusing previously created mesh!")

                    # Extract Dominant Plane Peaks
                    avg_peaks, _, _, _, fastga_timings = extract_all_dominant_plane_normals(
                        tri_mesh,
                        level=level_default,
                        with_o3d=False,
                        ga_=ga,
                        ico_chart_=ico_chart)
                    # Extact Planes and Polygons
                    try:
                        all_planes, all_polygons, all_poly_lines, polylidar_timings = extract_planes_and_polygons_from_mesh(
                            tri_mesh,
                            avg_peaks,
                            filter_polygons=False,
                            pl_=pl,
                            optimized=optimized)
                    except Exception:
                        logger_train.exception(
                            "Error in Polylidar, File: %s, Variance: %d, Param Index: %d, Params: %r",
                            fname, variance, param_index, params)
                        all_planes = []
                        all_polygons = []
                        all_poly_lines = []
                        polylidar_timings = dict()
                        logger_train.exception(
                            "Error in Polylidar, Params: %r", params)

                    # Aggregate timings
                    all_timings = dict(**mesh_timings, **fastga_timings,
                                       **polylidar_timings)

                    # Convert planes to format used for evaluation
                    all_planes_classified = convert_planes_to_classified_point_cloud(
                        all_planes, tri_mesh, avg_peaks)
                    # Evaluate the results. This actually takes the longest amount of time!
                    misc = dict(fname=fname,
                                variance=variance,
                                tcomp=0.80,
                                **params)
                    results_080, auxiliary_080 = evaluate(
                        pc_image, all_planes_classified, tcomp=0.80, misc=misc)
                    # print(results)
                    logger_train.info("Finished %r", params)

                    full_record_080 = dict(**params,
                                           **results_080,
                                           **all_timings,
                                           fname=fname,
                                           variance=variance,
                                           tcomp=0.80)
                    # Save the record
                    writer.writerow(full_record_080)
                    csv_file.flush()

                    t2 = time.perf_counter()
                    elapsed = (t2 - t1) * 1000
                    logger_train.info("One Param Set Took: %.1f", elapsed)
                    # Don't forget to clear the GA
                    ga.clear_count()
                    if counter is not None:
                        with counter.get_lock():
                            counter.value += 1

        except Exception as e:
            logger_train.exception("Something went wrong")
Example #24
0
def evaluate_with_params_visualize(params):
    """Similar to above but just for one set of parameters. Used for debugging and visualization

    Arguments:
        params {dict} -- Dict of hyperparameters
    """

    from polylidar_plane_benchmark.utility.helper import (
        convert_planes_to_classified_point_cloud, load_pcd_file, paint_planes,
        predict_loops_laplacian, extract_all_dominant_plane_normals,
        extract_planes_and_polygons_from_mesh)
    from polylidar_plane_benchmark.utility.evaluate import evaluate, evaluate_incorrect
    from polylidar_plane_benchmark.utility.helper_mesh import create_meshes_cuda
    from polylidar_plane_benchmark.utility.o3d_util import create_open_3d_pcd, plot_meshes, create_open_3d_mesh_from_tri_mesh, mark_invalid_planes

    from polylidar import Polylidar3D
    from fastga import GaussianAccumulatorS2, IcoCharts
    import open3d as o3d

    logger.setLevel(logging.INFO)
    variance = params['variance']
    all_fpaths = get_fpaths(params['variance'])
    fpaths = [fpath for fpath in all_fpaths if params['fname'] in fpath]
    fpath = fpaths[0]

    # Create Long Lived Objects Only Once
    ga = GaussianAccumulatorS2(
        level=level_default)  # Fast Gaussian Accumulator
    ico_chart = IcoCharts(
        level=level_default
    )  # Used for unwrapping S2 to Image for peak detection
    pl = Polylidar3D(
        **polylidar_kwargs_default)  # Region Growing and Polygons Extraction

    fname = pathlib.Path(fpath).name
    logger_train.info("Working on file %s", fpath)

    stride = params.get('stride', 2)
    pc_raw, pc_image = load_pcd_file(fpath, stride=stride)
    pc_points = np.ascontiguousarray(pc_raw[:, :3])
    # Create Open3D point cloud
    cmap = cc.cm.glasbey_bw
    pcd_raw = create_open_3d_pcd(pc_raw[:, :3], pc_raw[:, 3], cmap=cmap)

    pl.norm_thresh = params['norm_thresh_min']
    pl.norm_thresh_min = params['norm_thresh_min']
    pl.min_triangles = params.get('min_triangles', 250)

    # Predict laplacian loops needed by noisiness of point cloud
    if params.get('predict_loops_laplacian'):
        params['loops_laplacian'] = predict_loops_laplacian(pc_image, stride)
        logger_train.info("Set laplacian loops to :%r",
                          params['loops_laplacian'])

    t1 = time.perf_counter()
    mesh_kwargs = {
        k: params[k]
        for k in ('loops_laplacian', 'kernel_size', 'loops_bilateral',
                  'sigma_angle')
    }

    tri_mesh, mesh_timings = create_meshes_cuda(pc_image, **mesh_kwargs)
    mesh_kwargs_previous = mesh_kwargs

    # Extract Dominant Plane Peaks
    avg_peaks, pcd_all_peaks, arrow_avg_peaks, colored_icosahedron, fastga_timings = extract_all_dominant_plane_normals(
        tri_mesh,
        level=level_default,
        with_o3d=True,
        ga_=ga,
        ico_chart_=ico_chart)

    # Extact Planes and Polygons
    try:
        all_planes, all_polygons, all_poly_lines, polylidar_timings = extract_planes_and_polygons_from_mesh(
            tri_mesh,
            avg_peaks,
            filter_polygons=False,
            pl_=pl,
            optimized=False)
    except Exception:
        all_planes = []
        all_polygons = []
        all_poly_lines = []
        polylidar_timings = dict()
        logger_train.exception("Error in Polylidar, Params: %r", params)

    # Aggregate timings
    all_timings = dict(**mesh_timings, **fastga_timings, **polylidar_timings)

    # Convert planes to format used for evaluation
    all_planes_classified = convert_planes_to_classified_point_cloud(
        all_planes, tri_mesh, avg_peaks)
    # Evaluate the results. This actually takes the longest amount of time!
    misc = dict(**params)
    # results_080, auxiliary_080 = evaluate(pc_image, all_planes_classified, tcomp=0.80, misc=misc)
    results_080, auxiliary_080 = evaluate(pc_image,
                                          all_planes_classified,
                                          tcomp=params['tcomp'],
                                          misc=misc)
    # print(results)
    logger_train.info("Finished %r", params)

    # create invalid plane markers, green = gt_label_missed, red=ms_labels_noise, blue=gt_label_over_seg, gray=ms_label_under_seg
    invalid_plane_markers = mark_invalid_planes(pc_raw, auxiliary_080,
                                                all_planes_classified)
    tri_mesh_o3d = create_open_3d_mesh_from_tri_mesh(tri_mesh)
    tri_mesh_o3d_painted = paint_planes(all_planes_classified, tri_mesh_o3d)

    # plot_meshes([pcd_raw, tri_mesh_o3d], [colored_icosahedron, *arrow_avg_peaks])
    plot_meshes([pcd_raw, tri_mesh_o3d_painted, *invalid_plane_markers])
def main():
    # np.random.seed(5)
    np.random.seed(9)
    # generate random plane with hole
    plane = generate_3d_plane(bounds_x=[0, 10, 0.5],
                              bounds_y=[0, 10, 0.5],
                              holes=[[[3, 5], [3, 5]]],
                              height_noise=0.05,
                              planar_noise=0.10)
    # Generate top of box (causing the hole that we see)
    box_top = generate_3d_plane(bounds_x=[3, 5, 0.2],
                                bounds_y=[3, 5, 0.2],
                                holes=[],
                                height_noise=0.02,
                                height=2,
                                planar_noise=0.02)
    # Generate side of box (causing the hole that we see)
    box_side = generate_3d_plane(bounds_x=[0, 2, 0.2],
                                 bounds_y=[0, 2, 0.2],
                                 holes=[],
                                 height_noise=0.02,
                                 planar_noise=0.02)
    rm = rotation_matrix([0, 1, 0], -math.pi / 2.0)
    box_side = apply_rotation(rm, box_side) + [5, 3, 0]
    # box_side = r.apply(box_side) + [5, 3, 0]
    # All points joined together
    points = np.concatenate((plane, box_side, box_top))

    points_mat = MatrixDouble(points)
    polylidar_kwargs = dict(alpha=0.0,
                            lmax=1.0,
                            min_triangles=20,
                            z_thresh=0.1,
                            norm_thresh_min=0.94)
    polylidar = Polylidar3D(**polylidar_kwargs)

    elev = 15.0
    azim = -125

    # Extracts planes and polygons, time
    t1 = time.time()
    mesh, planes, polygons = polylidar.extract_planes_and_polygons(points_mat)
    t2 = time.time()
    print("Polylidar Took {:.2f} milliseconds".format((t2 - t1) * 1000))

    triangles = np.asarray(mesh.triangles)
    all_planes = [np.arange(triangles.shape[0])]

    print("")
    print("Should see a mesh segments")
    fig, ax = plt.subplots(figsize=(10, 10),
                           nrows=1,
                           ncols=1,
                           subplot_kw=dict(projection='3d'))
    # plot all triangles
    # plot_polygons_3d(points, polygons, ax)
    rm_45 = rotation_matrix([0, 1, 0], -math.pi / 4.0)
    points_rot = apply_rotation(rm_45, points)
    plot_planes_3d(points_rot, triangles, [planes[1]], ax, alpha=[0.85])
    # plot points
    # ax.scatter(*scale_points(points), c='k', s=0.1)
    set_axes_equal(ax)
    ax.view_init(elev=elev, azim=azim)
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    fig.savefig("assets/scratch/PolygonExtraction_a.pdf", bbox_inches='tight')
    # fig.savefig("assets/scratch/Basic25DAlgorithm_polygons.png", bbox_inches='tight', pad_inches=-0.8)
    plt.show()

    # Show 2D Projection and Polygon Extraction
    print("")
    print("Should see projected vertices to geometric plane")
    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))
    simplices_plane = triangles[planes[1], :]
    plot_polygons([polygons[1]],
                  points[:, :2],
                  ax,
                  shell_color=COLOR_PALETTE[4],
                  hole_color=COLOR_PALETTE[4])
    ax.triplot(points[:, 0], points[:, 1], simplices_plane, c='k', alpha=0.75)
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    fig.savefig("assets/scratch/PolygonExtraction_b1.pdf", bbox_inches='tight')
    plt.show()

    # Show 2D Projection and Polygon Extraction
    print("")
    print("Should see projected vertices to geometric plane")
    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))
    simplices_plane = triangles[planes[1], :]
    plot_polygons([polygons[1]], points[:, :2], ax)
    ax.triplot(points[:, 0], points[:, 1], simplices_plane, c='k', alpha=0.75)
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    fig.savefig("assets/scratch/PolygonExtraction_b2.pdf", bbox_inches='tight')
    plt.show()

    poly = polygons[1]
    shell_coords = get_points(poly.shell, points)
    hole_coords = [get_points(hole, points) for hole in poly.holes]
    poly_shape = Polygon(shell=shell_coords, holes=hole_coords)

    # Show Simplification
    print("")
    print("Should see simplified Polygon")
    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))

    patch = PolygonPatch(poly_shape, fill=True, alpha=0.5)
    ax.add_patch(patch)
    # plot_polygons([polygons[1]], points[:, :2], ax)
    poly_simplify = poly_shape.simplify(.2, preserve_topology=True)
    patch = PolygonPatch(poly_simplify,
                         color='k',
                         fill=False,
                         linewidth=1.5,
                         linestyle='--',
                         zorder=1)
    ax.add_patch(patch)
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)
    print(f"Before Vertices: {len(poly_shape.exterior.coords)}")
    print(f"After Vertices: {len(poly_simplify.exterior.coords)}")
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    fig.savefig("assets/scratch/PolygonExtraction_c.pdf", bbox_inches='tight')
    plt.show()

    # Show Positive Buffer
    print("")
    print("Should see positive buffer Polygon")
    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))

    patch = PolygonPatch(poly_shape, fill=True, alpha=0.5)
    ax.add_patch(patch)
    # plot_polygons([polygons[1]], points[:, :2], ax)
    poly_buffer = poly_simplify.buffer(.2)
    patch = PolygonPatch(poly_buffer,
                         color='k',
                         fill=False,
                         linewidth=1.5,
                         linestyle='--',
                         zorder=1)
    ax.add_patch(patch)
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)
    print(f"After Vertices: {len(poly_buffer.exterior.coords)}")
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    fig.savefig("assets/scratch/PolygonExtraction_d.pdf", bbox_inches='tight')
    plt.show()

    # Show Negative Buffer
    print("")
    print("Should see negative buffer Polygon")
    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))

    patch = PolygonPatch(poly_shape, fill=True, alpha=0.5)
    ax.add_patch(patch)
    # plot_polygons([polygons[1]], points[:, :2], ax)
    poly_buffer = poly_buffer.buffer(-.2)
    patch = PolygonPatch(poly_buffer,
                         color='k',
                         fill=False,
                         linewidth=1.5,
                         linestyle='--',
                         zorder=1)
    ax.add_patch(patch)
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)
    print(f"After Vertices: {len(poly_buffer.exterior.coords)}")
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    fig.savefig("assets/scratch/PolygonExtraction_e.pdf", bbox_inches='tight')
    plt.show()