Tuesday 26 July 2016

How to add metrics to DASOS

DASOS is our open source software for managing full-waveform (FW) LiDAR data [1] (http://miltomiltiadou.blogspot.co.uk/2015/03/las13vis.html).  Nevertheless it has a limited number of metrics, but it's design make it easy for people to add their own. So this blogpost aims to explain how to add your own metrics derived from the voxelised FW LiDAR data. It is advised to forward me (mmiltoo(at)gmail(dot)com) the new metrics classes in order to add the to future releases of DASOS and your name will also be added to the contributors.

There are two steps for generating your own metrics:
Step 1. Create a new Class for your new metric. To avoid confusion let's name our new class Metric. Please save the Metric.h file into the director ./include/Maps and the Metric.cpp file into the ./src/Maps directory to keep the files tidy. The Metrics class will inherit from the the base class Map. The header file (Metric.h) is standard and it should always be as the following (please replace all METRIC/Metric with the name of your actual new metric):

#ifndef METRIC_H
#define METRIC_H
#include "Map.h"
//-------------------------------------------------------------------------
/// @file Metric.h
/// @author <your name>
/// @version 1.0
/// @date <date generated>
/// @class Metric
/// @brief
//-------------------------------------------------------------------------


class Metric: public Map
{
public:
    //-------------------------------------------------------------------------
    /// @brief default constructor
    //-------------------------------------------------------------------------
    Metric(
            const std::string i_name,
            Volume *i_obj
            );
    //-------------------------------------------------------------------------
    /// @brief default destructor
    //-------------------------------------------------------------------------
    ~Metric();

private:
    //-------------------------------------------------------------------------
    /// @brief method that creates the Map
    //-------------------------------------------------------------------------
    void createMap();
};

#endif // METRIC_H


The only method that needs to be implemented is the createMap() which is a compulsory virtual function. The .cpp file should be as follow:

#include "Metric.h"

//-----------------------------------------------------------------------------
Metric::Metric(
        const std::string i_name,
        Volume *i_obj
        ):
    Map(i_name,i_obj)
{
}


//-----------------------------------------------------------------------------
void Metric::createMap()
{
   // Loop through all the voxels and generate the metric of interest
   // the variable m_noOfPixelsX, m_noOfPixelsY and m_noOfPixelsZ gives you 
   // the the number of voxels in x,y,z axis respectively
   for(unsigned int x=0; x<m_noOfPixelsX; ++x)
   {
      for(unsigned int y=0; y<m_noOfPixelsY; ++y)
      {
         for(unsigned int z=0; z<m_noOfPixelsZ; ++z)
         {
            // in m_mapValues all the values of the metrics are saved
            // the following command assigns the value 0 at the (x,y) position 
            // of the map
            m_mapValues[getIndex(x,y)]=-0.0f;

            // the voxelised 3D volume is the m_object variable and some 
            // useful examples of using it are shown below:

            // get the length of the voxel
            float voxelLength = m_object->getVoxelLen(); 
            // get the intesity at voxel (x,y,z)
            float intensity = m_object->getIntensity(x,y,z);
            // check whether the value of the voxel at (x,y,z) is considered to 
            // be inside or outside the scanned object. This checks whether the 
            // intensity value is above the boundary threshold. 
            bool isInside = m_object->isInside(x,y,z);            
         }
      }
   }
}

//-----------------------------------------------------------------------------
Metric::~Metric()
{}



Step 2: Link the new Metric Class with the rest of the program. This is done by modifying the .cpp file of MapsManager class.  Here it is shown the 4 additions that needs to be done in order to link your new metric with the rest of the program.


#include "MapsManager.h"
#include "ThicknessMap.h"
#include "FirstPatch.h"
#include "LastPatch.h"
// 1st ADDITION: include the header file of the new class
#include "Metric.h
// end of 1st ADDITION
#include <map>
#include <algorithm>

//-----------------------------------------------------------------------------
MapsManager::MapsManager():m_map(0),
    m_FWMetrics({"THICKNESS",
                 "LOWEST_RETURN",
                 "LAST_PATCH",
                 "FIRST_PATCH"
   // 2nd ADDITION: add the a name for your new metric
   // please do not forget the comma at the beginning
                 , "METRIC"
   // end of 2nd ADDITION
                })
{
   // The types should aggree with the fw metrics list
   m_types =
   {
      {"THICKNESS",3},
      {"FIRST_PATCH",9},
      {"LAST_PATCH",11}
    // 3rd ADDITION: give a number to your metric. This number must be unique
    // please do not forget the comma at the beginning
      , {"METRIC", 12}
   // end of 3rd ADDITION
   };
}

//-----------------------------------------------------------------------------
const std::vector<std::string> MapsManager::getNamesOfFWMetrics()const
{
   return m_FWMetrics;
}

//-----------------------------------------------------------------------------
void MapsManager::createMap(
        mapInfo *m_infoOfMap
        )
{
   if (m_map!=0)
   {
      delete m_map;
      m_map=0;
   }

   std::string s(m_infoOfMap->type);
   std::transform(s.begin(), s.end(), s.begin(), toupper);
   switch (m_types[s])
   {
  
   case 3:
      std::cout << "Density map\n";
      m_map = new DensityMap(m_infoOfMap->name,m_infoOfMap->obj);
      break;
  
  
   case 9:
      std::cout << "Length of first continues patch of non empty voxels\n";
      m_map = new FirstPatch(m_infoOfMap->name,m_infoOfMap->obj);
      break;
 
   case 11:
      std::cout << "Length of last continues patch of non empty voxels\n";
      m_map = new LastPatch(m_infoOfMap->name,m_infoOfMap->obj);
      break;

   // 4th ADDITION: link your metric class with the rest of the program
   case 12:
       std::cout << "Brief Discreption of the new Metric\n";
       m_map = new Metric(m_infoOfMap->name,m_infoOfMap->obj);
       break;
   // end of 4th ADDITION
   default:
      std::cout << std::string (s) << " is not a valid type of map";
      break;
   }
   // create and save the map
   if(m_map!=0)
   {
      m_map->createAndSave(m_infoOfMap->thres,m_infoOfMap->samp);
      delete m_map;
      m_map=0;
   }
}


//-----------------------------------------------------------------------------
MapsManager::~MapsManager()
{
   if(m_map!=0)
   {
      delete m_map;
   }
}


Once those steps are done you should be able to call your new metrics from the main program:

 ./DASOS -las myLasFile.LAS -map METRIC metric.asc


If you add the above class you should get a black asc file for all LAS files because all the values of the map are set to zero.

I hope you find that useful and if you have any questions please contact us at the following Google group:
https://groups.google.com/forum/#!forum/dasos---the-native-full-waveform-fw-lidar-software



Work Cited:
[1] Miltiadou, M., Grant, M. G., Campbell, N. D., Warren, M., Clewley, D., & Hadjimitsis, D. G. (2019, June). Open source software DASOS: Efficient accumulation, analysis, and visualisation of full-waveform lidar. In Seventh International Conference on Remote Sensing and Geoinformation of the Environment (RSCy2019) (Vol. 11174, p. 111741M). International Society for Optics and Photonics.