0

I used libLAS library to read the cloud points of a .las file. Then I stored the points in a PCL point cloud variable in order to process and display the point cloud using the Point Cloud Library.

This is the code that I used:

class PointCloud
{
    public:
        //PointCloud(const std::string& path);

        uint32_t getVertsCount();
        float4* getVertsData();

        template<typename PointT>
        typename pcl::PointCloud<PointT>::Ptr read(const std::string& path);//void read(const std::string &path);
}

template<typename PointT>
typename pcl::PointCloud<PointT>::Ptr PointCloud::read(const string& path)
{
    typename pcl::PointCloud<PointT>::Ptr lasCloud(new pcl::PointCloud<PointT>);

    std::ifstream ifs;
    ifs.open(path, std::ios::in | std::ios::binary);
    //std::ifstream inf(path, std::ios::in | std::ios::binary);

    liblas::ReaderFactory f;
    liblas::Reader reader = f.CreateWithStream(ifs);
    liblas::Header const& header = reader.GetHeader();

    std::cout << "Compressed: " << (header.Compressed() == true) ? "true" : "false";
    std::cout << "Signature: " << header.GetFileSignature() << '\n';
    std::cout << "Points count: " << header.GetPointRecordsCount() << '\n';

    while (reader.ReadNextPoint())
    {
        liblas::Point const& p = reader.GetPoint();

        PointT cloudPoint;
        cloudPoint.x = float(p.GetX()) * 0.001 + 590284.000; // (double)(x * scaleX) + offsetX;
        cloudPoint.y = float(p.GetY()) * 0.001 + 4339456.000; // (double)(y * scaleY) + offsetY;
        cloudPoint.z = float(p.GetZ()) * 0.001 + 157.000; // (double)(z * scaleZ) + offsetZ;
        std::cout << p.GetX() << ", " << p.GetY() << ", " << p.GetZ() << "\n";
        //cloudPoint.intensity = p.GetIntensity(); // (double)(intensity) / 65536.0;
        lasCloud->points.push_back(cloudPoint);
    }

    if (!ifs.good())
        throw runtime_error("Reading went wrong!");
        
    
    lasCloud->width = lasCloud->points.size();
    lasCloud->height = 1;
    lasCloud->is_dense = true;
    std::cout << "Cloud size = " << lasCloud->points.size() << endl;
    return lasCloud;
    

}

int main (int argc, char** argv)
{
    
    std::cout << "starting enviroment" << std::endl;
    pcl::visualization::PCLVisualizer::Ptr viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
    CameraAngle setAngle = FPS; //XY, FPS, Side, TopDown
    initCamera(setAngle, viewer);
    pcl::PointCloud<pcl::PointXYZ>::Ptr inputCloudI; //
    inputCloudI = pcd.read<pcl::PointXYZ>("C:/Users/hedey/OneDrive/Documents/Research_papers/STDF/10_4231_MFQF-Q141/I-65/LiDAR/RoadSurface/NB/20180524_I65_NB_RoadSurface_1_50.5.las");
    std::cout << "Cloud size = " << inputCloudI->points.size() << endl;
    renderPointCloud(viewer, inputCloudI, "lasCloud");
    while (!viewer->wasStopped())
    {
        viewer->spinOnce();
    }
}

However, the displayed cloud using the PCL viewer looks like one point. What I noticed is that when I printed out the coordinates that I read using libLAS, the x & y coordinates don't have decimal values, which is inaccurate compared to the actual coordinates stored in the las file. I got the actual point coordinates using las2txt from the command prompt. This is the txt file containing the actual coordinates. And here is an image showing the cout results: enter image description here

Also, this is how the point cloud looks like when I opened it using CloudCompare. I look forward to geting the same displayed when I read it into a PCL Point Cloud variable and display the results using the PCL viewer, because I'll need to do further processing in order to make sensor fusion (Camera and Lidar). enter image description here

1 Answers1

0

The default precision of std::cout is 6 decimal digits. Please add something like

    std::cout.precision(12);

before the while loop.

Also, casting p.GetX() etc. to float is pointless: if you multiply it by 0.001, the left hand side of the argument of operator* will naturally by promoted to at least double. However, float has only about 7 digits of precision, so for 9-digit integers stored inside doubles (yes!) such truncation is catastrophic.

Also there's another (minor) error, the correct line reads

    std::cout << "Compressed: " << ((header.Compressed() == true) ? "true\n" : "false\n");

Please notice the () braces surrounding the conditional expression (and \n). Please use standard compiler options that will warn against such simple issues.

Please read also https://stackoverflow.com/help/minimal-reproducible-example

zkoza
  • 1,369
  • 1
  • 6
  • 14
  • Thanks a lot. I have been googling and researching this subject. Now, what I understand is that the read coordinates already have the scale and offset applied, so I don't have to apply them. I just need to use the read coordinates as is. Also, I found a solution for rendering the point cloud. It's just by simply pressing the 'R' key, so the visualizer adjusts to the coordinates. My only concern now is that p.GetX() for example is a double. When I assign it to cloudPoint.x, which is a float, the precision is affected. E.g., p.GetX()=600209.172 but then cloudPoint.x=600209.1875. How to solve it? – Mohamed Hedeya Feb 06 '21 at 13:33
  • 1
    You can't. If you use PCL only for visualization, then before transferring the data to PCL, change the coordinate system so that the magnitude of the values gets considerably smaller. For example, move it so that the center of mass of the cloud is at (0.0.0). – zkoza Feb 06 '21 at 16:03
  • I will use PCL not only for visualization, but for other point cloud processing (such as segmentation, clustering, ... ), and based on that will make sensor fusion with camera images. What I understand is that the PCL coordinates are in meters, and the decimal values add centimeter precision. So, will this result in less accurate measurements (in centimeters)? I’m new to the field, so I’m still discovering. – Mohamed Hedeya Feb 06 '21 at 16:52
  • 1
    if all values start with the same digits, say, 678, then you can safely ignore them. Actually, what is PCL for? I do not know. Segmentation, clustering has sth to do with images, like scenes in computer games? Then not only you can, but you *should* use a local coordinate system. – zkoza Feb 06 '21 at 18:43
  • Thanks a lot. Removing the same digits from the coordinates (to get small coordinate values) almost solved the precision problem. I think this is what's referred to as offsetting the coordinates. Now after assigning the double 'p.GetX()' to the float 'cloudPoint.x', the value of cloudPoint.x is 9.17199993134, which makes the decimal part very close to 0.172. Thanks again sir. – Mohamed Hedeya Feb 07 '21 at 20:31