Commit 9006f304 authored by aschwarzkopf's avatar aschwarzkopf

[ADD #309] Added Simplistic Building detection feature.

Some simplistic algorithms are done in order to group buildings. Evaluation:
  + Important data structures as Octree and Quadtree are implemented with important functions
  + Grouping neighbor voxels runs even if still not 26-neighborship (few steps required)
     + Algorithm can easily be transferred to the quadtree set.
  + First new plugin for cutting of outlier points using the algorithm that calculates
    figuring out cube connectivity. Other points are cut off that don't belong to the
    largest voxel group
  - Possibly vaporized effort
      - The algorithm that takes relative elevation minimums has problems detecting low buildings.
          - Probably the algorithm won't be taken
          + The great deal of the code amount used for it can be very useful for the final code.
      + It's easy to differ trees from buildings
          - Not thought to the end to differ trees that are very close to buildings
parent d28ad199
......@@ -15,4 +15,4 @@
#VERSION=1.2.0
#VERSION=1.2.0+hgX -> replaces the X with the current revision number
#VERSUIB=1.2.0+hg1234 -> here, revision number was set by the build scripts for example.
VERSION=0.0.1+hgX
VERSION=0.0.2+hgX
......@@ -31,6 +31,7 @@
#include <core/kernel/WModule.h>
#include "buildingsDetection/WMBuildingsDetection.h"
#include "pointsCutOutliers/WMPointsCutOutliers.h"
#include "readLAS/WMReadLAS.h"
// #include "WToolkit.h"
......@@ -48,6 +49,7 @@
extern "C" void WLoadModule( WModuleList& m ) // NOLINT
{
m.push_back( boost::shared_ptr< WModule >( new WMBuildingsDetection ) );
m.push_back( boost::shared_ptr< WModule >( new WMPointsCutOutliers ) );
m.push_back( boost::shared_ptr< WModule >( new WMReadLAS ) );
}
......
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2013 OpenWalnut Community, BSV-Leipzig and CNCF-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#include <iostream>
#include "WBuildingDetector.h"
WBuildingDetector::WBuildingDetector()
{
m_detailDepth = 0.5;
m_minSearchDetailDepth = 8.0;
m_minSearchCutUntilAbove = 4.0;
m_detailDepthBigHeights = 32.0;
m_minSearchCutUntilAboveBigHeights = 20.0;
}
WBuildingDetector::~WBuildingDetector()
{
}
void WBuildingDetector::detectBuildings( boost::shared_ptr< WDataSetPoints > points )
{
WDataSetPoints::VertexArray verts = points->getVertices();
size_t count = verts->size()/3;
WOctree* zones3d = new WOctree( m_detailDepth );
WQuadTree* zones2d = new WQuadTree( m_detailDepth );
WQuadTree* minimalMaxima = new WQuadTree( m_minSearchDetailDepth );
WQuadTree* targetShowables = new WQuadTree( m_detailDepth );
m_targetGrouped3d = new WOctree( m_detailDepth );
for( size_t vertex = 0; vertex < count; vertex++ )
{
float x = verts->at( vertex*3 );
float y = verts->at( vertex*3+1 );
float z = verts->at( vertex*3+2 );
zones3d->registerPoint( x, y, z );
zones2d->registerPoint( x, y, z );
}
initMinimalMaxima( zones2d->getRootNode(), minimalMaxima );
projectDrawableAreas( zones2d->getRootNode(), minimalMaxima, targetShowables );
fetchBuildingVoxels( zones3d->getRootNode(), targetShowables, m_targetGrouped3d );
m_targetGrouped3d->groupNeighbourLeafs();
//targetShowables->setExportElevationImageSettings( -16.0, 8.0 );
//targetShowables->exportElevationImage( "/home/renegade/Dokumente/Projekte/OpenWalnut@Eclipse/elevation images/targetShowables.bmp", 1 );
}
void WBuildingDetector::setDetectionParams( int detailDepth, int minSearchDetailDepth,
double minSearchCutUntilAbove )
{
m_detailDepth = pow( 2.0, detailDepth );
m_minSearchDetailDepth = pow( 2.0, minSearchDetailDepth );
m_minSearchCutUntilAbove = minSearchCutUntilAbove;
}
WOctree* WBuildingDetector::getBuildingGroups()
{
return m_targetGrouped3d;
}
void WBuildingDetector::initMinimalMaxima( WQuadNode* sourceNode, WQuadTree* targetTree )
{
if ( sourceNode->getRadius() <= m_detailDepth )
{
double coordX = sourceNode->getCenter( 0 );
double coordY = sourceNode->getCenter( 1 );
double d = m_minSearchDetailDepth / 2.0;
double height = sourceNode->getElevationMax();
targetTree->registerPoint( coordX - d, coordY - d, height );
targetTree->registerPoint( coordX - d, coordY + d, height );
targetTree->registerPoint( coordX + d, coordY - d, height );
targetTree->registerPoint( coordX + d, coordY + d, height );
}
else
{
for( int child = 0; child < 4; child++ )
if( sourceNode->getChild( child ) != 0 )
initMinimalMaxima( sourceNode->getChild( child ), targetTree );
}
}
void WBuildingDetector::projectDrawableAreas( WQuadNode* sourceNode,
WQuadTree* minimalMaxima, WQuadTree* targetTree )
{
if( sourceNode->getRadius() <= m_detailDepth )
{
double coordX = sourceNode->getCenter( 0 );
double coordY = sourceNode->getCenter( 1 );
WQuadNode* minimalNode = minimalMaxima->getLeafNode( coordX, coordY );
WQuadNode* minimalNodeBigHeight = minimalMaxima->getLeafNode( coordX, coordY,
m_minSearchCutUntilAboveBigHeights );
if( minimalNode == 0 || minimalNodeBigHeight == 0 ) return;
double minimalHeight = m_minSearchCutUntilAbove + minimalNode->getElevationMin();
double minimalHeightBigHeights = m_minSearchCutUntilAbove + minimalNodeBigHeight->getElevationMin();
if( sourceNode->getElevationMax() < minimalHeight
&& sourceNode->getElevationMax() < minimalHeightBigHeights ) return;
targetTree->registerPoint( coordX, coordY, sourceNode->getElevationMax() );
}
else
{
for( int child = 0; child < 4; child++ )
if( sourceNode->getChild( child ) != 0 )
projectDrawableAreas( sourceNode->getChild( child ), minimalMaxima, targetTree );
}
}
void WBuildingDetector::fetchBuildingVoxels( WOctNode* sourceNode, WQuadTree* buildingPixels,
WOctree* targetTree )
{
if( sourceNode->getRadius() <= m_detailDepth )
{
double coordX = sourceNode->getCenter( 0 );
double coordY = sourceNode->getCenter( 1 );
double coordZ = sourceNode->getCenter( 2 );
WQuadNode* buildingArea = buildingPixels->getLeafNode( coordX, coordY );
if( buildingArea != 0 )
targetTree->registerPoint( coordX, coordY, coordZ );
}
else
{
for( int child = 0; child < 8; child++ )
if( sourceNode->getChild( child ) != 0 )
fetchBuildingVoxels( sourceNode->getChild( child ), buildingPixels, targetTree );
}
}
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2013 OpenWalnut Community, BSV-Leipzig and CNCF-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#ifndef WBUILDINGDETECTOR_H
#define WBUILDINGDETECTOR_H
#include <vector>
#include "core/graphicsEngine/WTriangleMesh.h"
#include "core/dataHandler/WDataSetPoints.h"
#include "structure/WOctNode.h"
#include "structure/WQuadNode.h"
#include "structure/WQuadTree.h"
#include "structure/WOctree.h"
/**
* Class that detects buildings using the WDataSetPoints
*/
class WBuildingDetector
{
public:
/**
* Constructor of the building detector instance.
*/
explicit WBuildingDetector();
/**
* Destructor of the building detector instance.
*/
virtual ~WBuildingDetector();
/**
* Starts the routine detecting buildings using the point data.
* Don't forget to execute initMinimalMaxima() before using that method.
* After executing you can extract the building number from each node
* using the method getBuildingGroups().
* \param points Point data to extract buildings from
*/
void detectBuildings( boost::shared_ptr< WDataSetPoints > points );
/**
* Sets setting params for building recognition
* \param m_detailDepth data grid resolution of cube group data. The number must
* be an element of 2^n. n can be smaller than 0.
* \param minSearchDetailDepth The area width to search a corresponding minimum.
* This number must be an element of 2^n. n can be
* smaller than 0. Finally this parameter determines the
* search area of the relative minimum.
* \param minSearchCutUntilAbove The minimal height above the relative minimal
* height in order to recognize as a building.
*/
void setDetectionParams( int m_detailDepth, int minSearchDetailDepth,
double minSearchCutUntilAbove );
/**
* Returns the 3D node tree. Each node has a group ID number. Many nodes can pe
* pointed by an equal parameter to a single building area.
* \return The octree of existing buildings. Buildings are grouped by an equal
* group number parameter of WOctNode.
*/
WOctree* getBuildingGroups();
private:
/**
* Inits the image of minimals which is used to determine a relative minimum of
* a X/Y coordinate.
* \param sourceNode Input image with fine-grain maximal geights to calculate a
* better image of relative minimums. It removes outliers below
* the ground.
* \param targetTree The grifty image to calculate relative minimums.
*/
void initMinimalMaxima( WQuadNode* sourceNode, WQuadTree* targetTree );
/**
* Calculates 2D-areas which cover buildings.
* \param sourceImage Input image, maximal points.
* \param minimalMaxima Image of relative minimums calculated by initMinimalMaxima();
* \param targetTree Output image containing elevation data. Areas covering no
* buildings should contain no data.
*/
void projectDrawableAreas( WQuadNode* sourceImage, WQuadTree* minimalMaxima,
WQuadTree* targetTree );
/**
* This is one of the last steps. It sorts out voxels that belong to buildings.
* \param sourceNode Source octree contatining data that represent to contain any
* point data altogether.
* \param buildingPixels Image that depicts areas covered by buildings in order to map
* the source 3D cubes on them.
* \param targetTree Not grouped voxels that represent building point areas.
*/
void fetchBuildingVoxels( WOctNode* sourceNode, WQuadTree* buildingPixels,
WOctree* targetTree );
/**
* Resolution of input/output data in meters. Use only numbers depictable by 2^n
* where n can also be 0 or below.
*/
double m_detailDepth;
/**
* Resolution of the relative minimum search image. Use only numbers depictable by 2^n
* where n can also be 0 or below. The bigger the pixels the greater are the areas
* searched from an examined X/Y area.
*/
double m_minSearchDetailDepth;
/**
* Height that must exceed above an relative minimum to recognize it as a building pixel.
*/
double m_minSearchCutUntilAbove;
/**
* The same as m_minSearchDetailDepth. But it's used to still be able tu use smaller
* areas not having big proglems with larger buildings.
*/
double m_detailDepthBigHeights;
/**
* The corresponding height setting for m_detailDepthBigHeights.
*/
double m_minSearchCutUntilAboveBigHeights;
/**
* The Octree that depicts the set of all buildings. Each node represents a cube
* within X/Y/Z. Every cube has a group id which corresponds to a building number.
* This field is calculated by detectBuildings().
*/
WOctree* m_targetGrouped3d;
};
#endif // WBUILDINGDETECTOR_H
......@@ -228,12 +228,49 @@ private:
* 2: Point count of each X/Y bin coordinate.
*/
WPropSelection m_elevImageMode;
/**
* Elevation image export setting.
* All areas below that elevation are depicted using the black color.
*/
WPropDouble m_minElevImageZ;
/**
* Elevation image export setting.
* Count of intensity increases per meter.
*/
WPropDouble m_intensityIncreasesPerMeter;
/**
* Path of the exportable elevation image *.bmp file.
*/
WPropFilename m_elevationImageExportablePath; //!< The mesh will be read from this file.
WPropTrigger m_exportTriggerProp; //!< This property triggers the actual reading,
/**
* This trigger button reloads the data to output.
*/
WPropTrigger m_reloadData; //!< This property triggers the actual reading,
/**
* Main building detection setting.
* Resolution of the relative minimum search image. Use only numbers depictable by 2^n
* where n can also be 0 or below. The bigger the pixels the greater are the areas
* searched from an examined X/Y area.
*/
WPropInt m_minSearchDetailDepth;
/**
* Main building detection setting.
* Height that must exceed above an relative minimum to recognize it as a building pixel.
*/
WPropDouble m_minSearchCutUntilAbove;
/**
* Property to choose an output building of a voxel group number. Currently 0 is
* cutting nothing and 1 is is showing all buildings altogether.
*/
WPropInt m_selectedShowableBuilding;
/**
* Instance for applying drawable geoms.
*/
......
......@@ -33,6 +33,7 @@ const size_t WOctNode::vZ[] = {0, 0, 0, 0, 1, 1, 1, 1};
WOctNode::WOctNode( double centerX, double centerY, double centerZ, double radius )
{
m_groupNr = 0;
m_center[0] = centerX;
m_center[1] = centerY;
m_center[2] = centerZ;
......@@ -118,7 +119,14 @@ double WOctNode::getCenter( size_t dimension )
return m_center[dimension];
}
size_t WOctNode::getGroupNr()
{
return m_groupNr;
}
void WOctNode::setGroupNr( size_t groupNr )
{
m_groupNr = groupNr;
}
void WOctNode::setChild( WOctNode* child, size_t drawer )
......
......@@ -97,6 +97,16 @@ public:
* \return the center coordinate to the corresponding dimension parameter
*/
double getCenter( size_t dimension );
/**
* Returns the Node group ID that is calculated regarding the node neighbors.
* \return The voxel group ID.
*/
size_t getGroupNr();
/**
* Sets the octree node group ID. It's usually calculated regarding the voxel neighbors.
* \param groupNr The voxel group ID.
*/
void setGroupNr( size_t groupNr );
/**
* Determines which X coordinate axis case a m_child has.
......@@ -134,5 +144,9 @@ private:
* node standing next to the current.
*/
double m_radius;
/**
* The node group ID. This number usually corresponds to its voxel neighborship
*/
size_t m_groupNr;
};
#endif // WOCTNODE_H
......@@ -28,7 +28,7 @@
WOctree::WOctree( double detailDepth )
{
m_root = new WOctNode( 0.0, 0.0, 0.0, 1.0 );
m_root = new WOctNode( 0.0, 0.0, 0.0, detailDepth );
m_detailLevel = detailDepth;
}
......@@ -39,7 +39,7 @@ WOctree::~WOctree()
void WOctree::registerPoint( double x, double y, double z )
{
// std::cout << "Inflating point: " << x << ", " << y << ", " << z << std::endl;
while ( !m_root->fitsIn( x, y, z ) )
while ( !m_root->fitsIn( x, y, z ) || m_root->getRadius() <= m_detailLevel )
m_root->expand();
WOctNode* node = m_root;
......@@ -50,6 +50,129 @@ void WOctree::registerPoint( double x, double y, double z )
node = node->getChild( drawer );
}
}
WOctNode* WOctree::getLeafNode( double x, double y, double z )
{
if( !m_root->fitsIn( x, y, z ) )
return 0;
WOctNode* node = m_root;
while ( node->getRadius() > m_detailLevel )
{
size_t drawer = node->getFittingCase( x, y, z );
if( node->getChild( drawer ) == 0 )
return 0;
node = node->getChild( drawer );
}
return node;
}
WOctNode* WOctree::getRootNode()
{
return m_root;
}
size_t WOctree::getGroupCount()
{
return m_groupEquivs.size();
}
void WOctree::groupNeighbourLeafs()
{
resizeGroupList( 0 );
groupNeighbourLeafs( m_root );
size_t currentFinalGroup = 0;
for( size_t groupID = 0; groupID < m_groupEquivs.size(); groupID++ )
{
bool idUsed = false;
for( size_t index = 0; index < m_groupEquivs.size(); index++ )
{
if( m_groupEquivs[index] == groupID )
{
m_groupEquivs[index] = currentFinalGroup;
idUsed = true;
}
}
if( idUsed ) currentFinalGroup++;
}
refreshNodeGroup( m_root );
resizeGroupList( currentFinalGroup );
for( size_t index = 0; index < currentFinalGroup; index++ )
m_groupEquivs[index] = index;
std::cout << "Found " << currentFinalGroup << " groups." << std::endl;
}
void WOctree::refreshNodeGroup( WOctNode* node )
{
if ( node->getRadius() <= m_detailLevel )
{
size_t oldGroupNr = node->getGroupNr();
node->setGroupNr( m_groupEquivs[oldGroupNr] );
}
else
{
for ( int child = 0; child < 8; child++ )
if ( node->getChild( child ) != 0 )
refreshNodeGroup( node->getChild( child ) );
}
}
void WOctree::groupNeighbourLeafs( WOctNode* node )
{
if ( node->getRadius() <= m_detailLevel )
{
size_t group = m_groupEquivs.size();
double x = node->getCenter( 0 );
double y = node->getCenter( 1 );
double z = node->getCenter( 2 );
double d = node->getRadius() * 2.0;
WOctNode* neighbors[3] = {
// getLeafNode( x-d, y-d, z ),
getLeafNode( x , y-d, z ),
// getLeafNode( x+d, y-d, z ),
getLeafNode( x-d, y , z ),
// getLeafNode( x-d, y-d, z-d ),
// getLeafNode( x , y-d, z-d ),
// getLeafNode( x+d, y-d, z-d ),
// getLeafNode( x-d, y , z-d ),
getLeafNode( x , y , z-d ),
// getLeafNode( x+d, y , z-d ),
// getLeafNode( x-d, y+d, z-d ),
// getLeafNode( x , y+d, z-d )
// getLeafNode( x+d, y+d, z-d )
};
for( size_t index = 0; index < 3; index++ )
{
WOctNode* neighbor = neighbors[index];
size_t neighborID = neighbor == 0 ?group :m_groupEquivs[neighbor->getGroupNr()];
if( group > neighborID && m_detailLevel == neighbor->getRadius() )
group = neighborID;
}
for( size_t index = 0; index < 3; index++ )
{
WOctNode* neighbor = neighbors[index];
size_t neighborID = neighbor == 0 ?group :m_groupEquivs[neighbor->getGroupNr()];
if( group < neighborID && m_detailLevel == neighbor->getRadius() )
{
for( size_t newIdx = 0; newIdx < m_groupEquivs.size(); newIdx++ )
if( m_groupEquivs[newIdx] == neighborID )
m_groupEquivs[newIdx] = group;
}
}
if( group >= m_groupEquivs.size() )
{
resizeGroupList( group + 1 );
m_groupEquivs[group] = group;
}
node->setGroupNr( group );
}
else
{
for ( int child = 0; child < 8; child++ )
if ( node->getChild( child ) != 0 )
groupNeighbourLeafs( node->getChild( child ) );
}
}
void WOctree::resizeGroupList( size_t listLength )
{
m_groupEquivs.resize( listLength );
m_groupEquivs.reserve( listLength );
}
boost::shared_ptr< WTriangleMesh > WOctree::getOutline()
{
......
......@@ -22,12 +22,13 @@
//
//---------------------------------------------------------------------------
#include "core/graphicsEngine/WTriangleMesh.h"
#include "WOctNode.h"
#ifndef WOCTREE_H
#define WOCTREE_H
#include <vector>
#include "core/graphicsEngine/WTriangleMesh.h"
#include "WOctNode.h"
/**
* Octree structure for analyzing buildings point data
*/
......@@ -53,6 +54,37 @@ public:
* \param z Z coordinate of the registerable point
*/
void registerPoint( double x, double y, double z );
/**
* Returns the leaf octree node of the finest detail level that covers a X/Y/Z
* coordinate
* \param x X coordinate covering the voxel.
* \param y Y coordinate covering the voxel.
* \param z Z coordinate covering the voxel.
* \return Octree node that corresponds to the X/Y/Z coordinate. If no node is found,
* then 0 will be returnd.
*/
WOctNode* getLeafNode( double x, double y, double z );
/**
* Returns the root node of the octree.
* \return The octree root noce.
*/
WOctNode* getRootNode();
/**
* Adjusts group numbers of all leaf nodes so that nodes have the same ID that
* represent altogether a single block.
*/
void groupNeighbourLeafs();
/**
* Refreshs group IDs using the temporary id mapping vector m_groupEquivs.
* \param node Node to refresh.
*/
void refreshNodeGroup( WOctNode* node );
/**
* Returns the voxel neighbor group count. Execute groupNeighbourLeafs() befor
* acquiring that parameter.
* \return The voxel neighbor group count.
*/
size_t getGroupCount();
/**
* Returns a WTriangleMesh which outlines the octree.
* \return Triangle mesh that represents the outline.
......@@ -66,6 +98,17 @@ private: