potree / potree

WebGL point cloud viewer for large datasets
http://potree.org
Other
4.63k stars 1.19k forks source link

How to Convert Potree Octree data to LAS (Reverse of PotreeConverter) (version 1.6) #1349

Open AdamAtamnia opened 1 year ago

AdamAtamnia commented 1 year ago

Hello,

I want to write a program that does the reverse of the PotreeConverter in Go. I want the input to be the Potree Octree data and the output to be a LAS file. I would appreciate it if you could point me in the right direction.

pierraug31 commented 10 months ago

Hello, I have the same problem, do you have updates?

adam-atamnia commented 10 months ago

It has been a very long time since I worked on this project so I dont remember the details but I think I was able to find a solution. Here are my notes that should explain everything. I hope you find them useful:

I was able to code my own solution for point cloud extraction from volume using code from here https://github.com/potree/potree/issues/549#issuecomment-1176157523, and by using some of the code for point cloud extraction from profiles found in Potree itself (profile.js).

Here is what I did:

I added this in the following files:

VolumePanel.js


// imports
import {CSVExporter} from "../../exporter/CSVExporter.js";
import { LASExporter } from "../../exporter/LASExporter.js";
import {Points} from "../../Points.js";

...

export class VolumePanel extends MeasurePanel {

    constructor(...){
        this.elContent = $(`
            <div ...>
                ...
                <li style="margin-top: 10px">
                    <a id="download_volume_csv" href="#" download="volume.csv">
                        Download CSV
                    </a>
                </li>

                <li style="margin-top: 10px">
                    <a id="download_volume_las" href="#" download="volume.las">
                        Download LAS
                    </a>
                </li>
                ...
            </div>

        `)
    }

    ...

    let csvBtn = this.elContent.find('#download_volume_csv')
    csvBtn.click(() => {
        let points = this.getVolumePoints();
        let string = CSVExporter.toString(points);

        let blob = new Blob([string], { type: "text/string" });
        csvBtn.attr('href', URL.createObjectURL(blob));
    })

    let lasBtn = this.elContent.find('#download_volume_las')
    lasBtn.click(() => {
        let points = this.getVolumePoints();
        let buffer = LASExporter.toLAS(points);

        let blob = new Blob([buffer], {type: "application/octet-binary"});
        lasBtn.attr('href', URL.createObjectURL(blob));
    })

    getVolumePoints() {
        for (let pointcloud of this.viewer.scene.pointclouds) {
            console.log(pointcloud);
        }

        const pointCloud = window.viewer.scene.pointclouds[0];
        const measurement = this.measurement;

        const pointNodes = pointCloud.visibleNodes.map((node, index) => {
            return node.getPointsInBox(measurement);
        });

        const flatNodes = pointNodes.flat();

        let points = new Points();
        points.boundingBox = measurement.boundingBox;
        points.numPoints = flatNodes.length/3;//3 because xyz
        points.data["position"] = new Float32Array(flatNodes);

        return points;

    }

    ...

}

PointCloudOctree.js

export class PointCloudOctreeNode extends PointCloudTreeNode {
    ...

    // modified similarly to: https://github.com/potree/potree/issues/549#issuecomment-1176164943
    getPointsInBox(boxNode) {
        if (!this.sceneNode) {
            return null;
        }

        const attributes = this.geometryNode.geometry.attributes;
        const position = attributes.position;
        const worldToBox = boxNode.matrixWorld.clone().invert();
        const objectToBox = new THREE.Matrix4().multiplyMatrices(
            worldToBox,
            this.sceneNode.matrixWorld
        );
        let inBox = [];

        console.log(attributes)

        for (let i = 0; i < position.count; i++) {
            const pos = new THREE.Vector3();
            const arr = position.array;
            const x = arr[3 * i];
            const y = arr[3 * i + 1];
            const z = arr[3 * i + 2];
            pos.set(x, y, z);
            pos.applyMatrix4(objectToBox);

            if (-0.5 < pos.x && pos.x < 0.5) {
                if (-0.5 < pos.y && pos.y < 0.5) {
                    if (-0.5 < pos.z && pos.z < 0.5) {
                        //const classification = attributes.classification.array[i];
                        let point = new THREE.Vector3(x, y, z);
                        point.applyMatrix4(this.sceneNode.matrixWorld);
                        position.array[3 * i] = point.x;
                        position.array[3 * i + 1] = point.y;
                        position.array[3 * i + 2] = point.z;
                        // instead of returning and array of vectors, each xyz is a separate element in the array
                        // this is because LASConverter.js and CSVConverter.js expect this kind of format in the data field
                        inBox.push(position.array[3 * i]);
                        inBox.push(position.array[3 * i + 1]);
                        inBox.push(position.array[3 * i + 2]);
                    }
                }
            }
        }

        return inBox;
    }
    ...
}

Important findings

Issue

The bounding box of the Points object passed to the LasConverter is always this:

boundingBox: Box3

which it should not be (it should be the same bounding box as the selected volume on the web interface)

This is because the this.mesurments field in VolumePanel class (which is the box representing the selected volume in the web interface) has always this (the one shown above) bounding box. It might be a bug or I might have missed something.

Implications:

ZHUYUEHAN1 commented 1 month ago

how to find all the positions? I have 720000 point data, pointCloud.pcoGeometry.root.children[].geometry.attributes.position.array only have 200000 pointCloud.visibleNodes.geometryNode.geometry.attributes.position.array have 600000