Commit dcc37fa9 authored by Tyler Wilson's avatar Tyler Wilson
Browse files

Added cnet2dem app to control. Fixes #3869

git-svn-id: http://subversion.wr.usgs.gov/repos/prog/isis3/trunk@6627 41f8697f-d340-4b68-9986-7bafba869bb8
parent 90c43741
Loading
Loading
Loading
Loading
+347 −0
Original line number Diff line number Diff line
#ifndef ControlPointCloudPt_h
#define ControlPointCloudPt_h
/**
 * @file
 * $Revision: 1.0 $ 
 * $Date: 2014/02/27 18:49:25 $ 
 *
 *   Unless noted otherwise, the portions of Isis written by the USGS are public
 *   domain. See individual third-party library and package descriptions for
 *   intellectual property information,user agreements, and related information.
 *
 *   Although Isis has been used by the USGS, no warranty, expressed or implied,
 *   is made by the USGS as to the accuracy and functioning of such software
 *   and related material nor shall the fact of distribution constitute any such
 *   warranty, and no responsibility is assumed by the USGS in connection
 *   therewith.
 *
 *   For additional information, launch
 *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html in a browser or see
 *   the Privacy & Disclaimers page on the Isis website,
 *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
 *   http://www.usgs.gov/privacy.html.
 */

#include <cmath>

#include <QExplicitlySharedDataPointer>
#include <QSharedData>
#include <QVector>
#include <QtGlobal>

#include "ControlPoint.h"
#include "ControlMeasure.h"
#include "SpecialPixel.h"


namespace Isis {

/**
 * @brief 3-D ControlPoint class for use in PointCloud datasets
 *  
 *  The ControlPoint container is required to not change its content for the
 *  duration of use of the nanoflann kd-tree built from the points.
 *  
 * @internal 
 * @history 2015-10-11 Kris Becker Original Version 
 */

class ControlPointCloudPt {
  public:
    enum CoordinateType { Image, Ground };
    enum Ownership { Shared,      // Indicates creator of ControlPoint retains
                                  // ownership. take() provides a unique clone
                                  // of ControlPoint.
                     Exclusive    // Creator gives ownership to ControlPointCloudPt.
                                  // Gives up pointer interally to first caller to
                                  // take() invalidating this object.
                   };

    ControlPointCloudPt() : m_xyz(), m_type(Image), m_serialno(),
                            m_data( new ControlPointData() ),
                            m_merged() {
      m_data->getImageCoordinates(m_xyz); 
    }

    ControlPointCloudPt(ControlPoint *point, const CoordinateType &ptype,
                        const Ownership owner,
                        const QString &serialno = "", 
                        const double &weight = 1.0) : 
                        m_xyz( ), m_type(ptype), m_serialno(serialno),
                        m_data(new ControlPointData(point, owner, weight)),
                        m_merged() {
      if ( Image == ptype ) {
        m_data->setReference(serialno);
        if ( !selectImageCoordinates() ) { 
          m_data->disable(); 
        }
      }
      else {  //  ( Ground == ptype )
        if ( !selectGroundCoordinates() ) { 
          m_data->disable(); 
        }
      }
    }

    virtual ~ControlPointCloudPt() { }

    inline bool isValid() const {
      return ( !m_data->isDisabled() );
    }

    inline void disable() {
      return ( m_data->disable() );
    }

    inline int size() const {
      return ( m_data->size() );
    }

    Ownership owner() const {
      return ( m_data->m_owner );
    }

    ControlPoint *take() const {
       return ( m_data->take() );
    }

    inline bool selectGroundCoordinates() {
      m_type = Ground;
      return ( m_data->getGroundCoordinates(m_xyz) );
    }

    inline bool selectImageCoordinates() {
      m_type = Image;
      return ( m_data->getImageCoordinates(m_xyz) );
    }

    inline CoordinateType getCoordinateType() const {
      return ( m_type );
    }

    inline QString id() const {
      return ( m_data->m_point->GetId() );
    }


    inline bool getGroundCoordinates(double xyzw[4]) const {
      return ( m_data->getGroundCoordinates(xyzw) );
    }

    inline const ControlPoint *getPoint() const {
      return ( m_data->m_point );
    }

    inline ControlPoint &getPoint() {
      return ( *m_data->m_point );
    }

    inline QString getSerialNumber() const {
      return ( m_serialno );
    }

    inline ControlMeasure *getMeasure(const QString &serialno) const {
      if ( !isValid() ) { return (0); }
      if (m_data->m_point->HasSerialNumber(serialno) ) {
        return ( m_data->m_point->GetMeasure(serialno) );
      }

      return (0);
    }

    // Convenient retrieval coordinates
    inline double x() const { return ( m_xyz[0] ); }
    inline double y() const { return ( m_xyz[1] ); }
    inline double z() const { return ( m_xyz[2] ); }
    inline double w() const { return ( m_xyz[3] ); }
    inline const double *array() const { return ( m_xyz ); }

    /** Compute real vector length (radius) from ControlPoint 
     *  
     *  Radius is returned in meters  
     */
    inline double radius() const {
      // compute magnitude
      double coords[4];
      if ( !getGroundCoordinates(coords) ) { return (Null); }
      return ( radius( coords[0], coords[1], coords[2]) );
    }


    /** Compute vector length (radius) - code based upon NAIF
     *  
     *  Radius is returned in units of input parameters
     */
    inline double radius(const double x, const double y, const double z) const {
      double v1max = qMax(qAbs(x), qMax(qAbs(y), qAbs(z)));

      // We're done if its the zero vector
      if ( qFuzzyCompare(v1max+1.0, 1.0) ) { return ( 0.0 ); }

      // Compute magnitude of the vector
      double tmp0( x / v1max );
      double tmp1( y / v1max );
      double tmp2( z / v1max );
      double normsqr = tmp0*tmp0 + tmp1*tmp1 + tmp2*tmp2;
      return ( v1max * std::sqrt(normsqr) );
    }


    inline bool operator==(const ControlPointCloudPt &other) const {
      return ( m_data->m_point == other.m_data->m_point );
    }

    inline bool operator!=(const ControlPointCloudPt &other) const {
      return ( m_data->m_point != other.m_data->m_point );
    }

  private:
    /** Shared ControlPoint data pointer */
    class ControlPointData : public QSharedData {
      typedef ControlPointCloudPt::Ownership Ownership;
      public:
        ControlPointData() : QSharedData(), m_point(0), m_reference(0), 
                             m_weight(1.0), m_initial(0),
                             m_owner(Exclusive){ }
        ControlPointData(ControlPoint *point, const Ownership owner, 
                         const double weight = 1.0) :
                         QSharedData(), m_point(point), 
                         m_reference(0), 
                         m_weight(weight),
                         m_initial(point->GetNumValidMeasures()), 
                         m_owner(owner) { 
          if ( m_point->GetNumMeasures() > 0) {
            m_reference = m_point->GetRefMeasure();
          }
        }
        ControlPointData(const ControlPointData &other) : QSharedData(other),
                         m_point(other.m_point),
                         m_reference(other.m_reference),
                         m_weight(other.m_weight),
                         m_initial(other.m_initial),
                         m_owner(Shared) { }
        ~ControlPointData() { 
          if ( Exclusive == m_owner ) {
             delete (m_point);
             m_point = 0;
          }
        }

        inline int size() const {
          if ( 0 == m_point ) { return (0); }
          return ( m_point->GetNumValidMeasures() );
        }

        inline bool setReference(const QString &serialno) {
          if ( 0 == m_point ) { return ( false); }
          m_reference = 0;

          if ( serialno.isEmpty() ) {
            m_reference = m_point->GetRefMeasure();
          }
          else {
            if ( m_point->HasSerialNumber(serialno) ) {
              m_reference = m_point->GetMeasure(serialno);
            }
          }

          return ( 0 != m_reference );
        }

        /** This approach allows use of 2 and 3 dimensional Euclidean
         *  distances */
        inline bool getImageCoordinates(double coords[4]) const {
          (void) getNoPointData(coords);
          if ( 0 == m_reference ) { return (false); }
          if ( 0 == m_point ) { return ( false ); }

          // ControlMeasure *refm = m_point->GetRefMeasure();
          coords[0] = m_reference->GetSample();
          coords[1] = m_reference->GetLine();
          coords[2] = 0.0;
          coords[3] = m_weight;
          return ( true );
        }


        /** This approach assumes 3 dimensional Euclidean distances */
        inline bool getGroundCoordinates(double coords[4]) const {
          (void) getNoPointData(coords);
          if ( 0 == m_point ) { return ( false ); }

          // Always get the best surface point (2015-10-27)
          SurfacePoint surfpt = m_point->GetBestSurfacePoint();
          if ( !surfpt.Valid() ) { return (false); }

          // Get the location and convert to meters!
          surfpt.ToNaifArray(&coords[0]);
          coords[0] *= 1000.0;
          coords[1] *= 1000.0;
          coords[2] *= 1000.0;
          coords[3] = m_weight;
          return (true);
        }

        inline bool isDisabled() const {
          if ( 0 == m_point ) return ( true );
          return (
                   m_point->IsInvalid()  || 
                   m_point->IsIgnored() || 
                   m_point->IsRejected() ||
                   m_point->IsEditLocked()  ||
                   ( 0 == m_reference )
                   ); 
        }

        inline void disable() {
          if ( !isDisabled() ) {
            m_point->SetIgnored( true );
          }
          return;
        }

        // The first to call this method will take ownership of the 
        // ControlPoint if Exclusive. Cloned otherwise.
        ControlPoint *take() {
          ControlPoint *p(m_point); 
          if ( 0 == m_point ) {  return (p); }

          // If someone else owns the point, clone it
          if ( Shared == m_owner ) {
            p = new ControlPoint(*m_point);
          }
          else {
            // Exclusive == Ownership
            // Relinquish ownership
            m_point = 0;
          }

          return ( p );
        }

        // Data....
        ControlPoint          *m_point; 
        ControlMeasure        *m_reference;
        double                m_weight;
        int                   m_initial;
        Ownership             m_owner;


      private:
        inline bool getNoPointData(double coords[4]) const {
          coords[0] = coords[1] = coords[2] = 0.0; 
          coords[3] = m_weight;
          return ( false );
        }
    };

    // Variables...
    double                                  m_xyz[4];
    CoordinateType                          m_type;
    QString                                 m_serialno;
    QExplicitlySharedDataPointer<ControlPointData> m_data;
    QList<ControlPointCloudPt>              m_merged;
};

};  // namespace Isis
#endif
+211 −0
Original line number Diff line number Diff line
/**                                                                       
 * @file                                                                  
 * $Revision$
 * $Date$
 * $Id$
 * 
 *   Unless noted otherwise, the portions of Isis written by the USGS are 
 *   public domain. See individual third-party library and package descriptions 
 *   for intellectual property information, user agreements, and related  
 *   information.                                                         
 *                                                                        
 *   Although Isis has been used by the USGS, no warranty, expressed or   
 *   implied, is made by the USGS as to the accuracy and functioning of such 
 *   software and related material nor shall the fact of distribution     
 *   constitute any such warranty, and no responsibility is assumed by the
 *   USGS in connection therewith.                                        
 *                                                                        
 *   For additional information, launch                                   
 *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html                
 *   in a browser or see the Privacy &amp; Disclaimers page on the Isis website,
 *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
 *   http://www.usgs.gov/privacy.html.                                    
 */ 
#include <cstdarg>
#include <cstdio>
#include <string>
#include <vector>
#include <numeric>
#include <iostream>
#include <sstream>

#include <QtGlobal>
#include <QCoreApplication>
#include <QScopedPointer>
#include <QString>
#include <QStringList>

#include <boost/foreach.hpp>

#include "DatumFunctoid.h"
#include "FileName.h"
#include "IString.h"
#include "IException.h"
#include "PvlKeyword.h"
#include "PvlObject.h"

using namespace std;

namespace Isis {

DatumFunctoidFactory *DatumFunctoidFactory::m_maker = 0;

DatumFunctoidFactory::DatumFunctoidFactory() {
//  This ensures this singleton is shut down when the application exists
   qAddPostRoutine(DieAtExit);
   init();
   return;
}

DatumFunctoidFactory::~DatumFunctoidFactory() { } 

DatumFunctoidFactory *DatumFunctoidFactory::getInstance() { 
  if (!m_maker) {
    m_maker = new DatumFunctoidFactory();
  }
  return (m_maker);
}



QStringList DatumFunctoidFactory::algorithms() const {
  QStringList functoids;

  BOOST_FOREACH ( SharedDatumFunctoid d, m_functoids ) {
    functoids.push_back( d->name() );
  }
  return (functoids);
}

DatumFunctoidList DatumFunctoidFactory::create(const QString &specs,
                                               const bool &errorIfEmpty) 
                                               const {
  DatumFunctoidList functoids; 

  // Individual algorithm specifications are separated by vertical bars
  QStringList algorithms = specs.split(",", QString::SkipEmptyParts);
  // cout << "create: got " << algorithms.size() << " algorithms...\n";
  if ( algorithms.size() == 0 ) {
    if ( errorIfEmpty ) {
      QString mess = "No functoid algorithm specifictions found!"; 
      throw IException(IException::User, mess, _FILEINFO_);
    }
    return ( functoids );
  }

  IException ie;
  int nerrors(0);
  BOOST_FOREACH ( QString functoid, algorithms ) {
    SharedDatumFunctoid f( make(functoid) );
    if ( f.isNull() ) {
      QString mess = "Algorithm \"" + functoid + "\" was not found!";
      ie.append(IException(IException::User, mess, _FILEINFO_));
      nerrors++;
    }
    else {
      functoids.append(f); 
    }
  }

  // Toss errors if they occurred
  if ( nerrors > 0 ) { throw ie; }

  // return the list
  return ( functoids );
}


DatumFunctoid *DatumFunctoidFactory::make(const QString &funcspec) const {
  PvlFlatMap parms = parseParameters(funcspec);
  QString algorithm = parms.get("Functoid","").toLower();
  BOOST_FOREACH ( SharedDatumFunctoid d, m_functoids ) {
    if ( d->name().toLower() == algorithm ) {
      return ( d->clone(parms) );
    }
  }

  // Did not find a functoid
  return ( 0 );
}

/**
 * @brief Parse a parameter string for values and return in parameter map
 * 
 * @author 2015-09-08 Kris Becker
 * 
 * @param parameters 
 * 
 * @return PvlFlatMap 
 */
PvlFlatMap DatumFunctoidFactory::parseParameters(const QString &parameters) 
                                                    const {
  PvlFlatMap pmap;

  // If the string is empty, return an empty paramter list
  QStringList parts = parameters.split("@");
  if ( parts.size() == 0 ) { return (pmap); }

  // Pull the first value and name it the Functoid
  QString parmtag = parts.takeFirst();
  pmap.add("Functoid", parmtag);

  // All good so far, parse each parameter string
  BOOST_FOREACH ( QString specs, parts ) {

    // Is it valid?
    QStringList parmvaltag = specs.split(":");
    if ( 2 != parmvaltag.size() ) {
      QString mess = "Invalid parameter at or near [" + specs + "] in \"" +
                     parameters + "\" - must be of the form "
                     "\"name@parm1:value1@parm2:value2...\"";
      throw IException(IException::User, mess, _FILEINFO_);
    }

    // Looks good, set the parameters
    pmap.add(parmvaltag[0], parmvaltag[1]);
  }

  // All done...
  return ( pmap );
}


 /**
   * @brief Exit termination routine
   *
   * This (static) method ensure this object is destroyed when Qt exits.  
   *
   * Note that it is error to add this to the system _atexit() routine because
   * this object utilizes Qt classes.  At the time the atexit call stack is
   * executed, Qt is long gone resulting in Very Bad Things.  Fortunately, Qt has
   * an exit stack function as well.  This method is added to the Qt exit call
   * stack.
   */
  void DatumFunctoidFactory::DieAtExit() {
    delete  m_maker;
    m_maker = 0;
    return;
  }


void DatumFunctoidFactory::init() {
  m_functoids.clear();

  m_functoids.append( SharedDatumFunctoid(new AverageRadius()) );
  m_functoids.append( SharedDatumFunctoid(new MedianRadius()) );
  m_functoids.append( SharedDatumFunctoid(new StandardDeviationRadius()) );
  m_functoids.append( SharedDatumFunctoid(new MaximumRadius()) );
  m_functoids.append( SharedDatumFunctoid(new MinimumRadius()) );
  m_functoids.append( SharedDatumFunctoid(new AverageDistance()) );
  m_functoids.append( SharedDatumFunctoid(new MedianDistance()) );
  m_functoids.append( SharedDatumFunctoid(new StandardDeviationDistance()) );
  m_functoids.append( SharedDatumFunctoid(new MaximumDistance()) );
  m_functoids.append( SharedDatumFunctoid(new MinimumDistance()) );
  m_functoids.append( SharedDatumFunctoid(new TypeCount()) );
  m_functoids.append( SharedDatumFunctoid(new ShepardsRadius()) );
  m_functoids.append( SharedDatumFunctoid(new FrankeNelsonRadius()) );
  m_functoids.append( SharedDatumFunctoid(new NaturalNeighborRadius()) );
  return;
}

} // namespace Isis
+592 −0

File added.

Preview size limit exceeded, changes collapsed.

+12 −0
Original line number Diff line number Diff line
include $(ISISROOT)/make/isismake.apps

ifeq ($(HOST_ARCH),Darwin)
ALLINCDIRS += -I/work/users/kbecker/NaturalNeighbors/include
ALLLIBDIRS += -L/work/users/kbecker/NaturalNeighbors/lib
else
ALLINCDIRS += -I$(ISISROOT)/3rdParty/extras/include
ALLLIBDIRS += -L$(ISISROOT)/3rdParty/extras/lib
endif

ALLLIBS += -lnn
+172 −0
Original line number Diff line number Diff line
#ifndef MapPointCollector_h
#define MapPointCollector_h
/**
 * @file
 * $Revision: 1.0 $ 
 * $Date: 2014/02/27 18:49:25 $ 
 *
 *   Unless noted otherwise, the portions of Isis written by the USGS are public
 *   domain. See individual third-party library and package descriptions for
 *   intellectual property information,user agreements, and related information.
 *
 *   Although Isis has been used by the USGS, no warranty, expressed or implied,
 *   is made by the USGS as to the accuracy and functioning of such software
 *   and related material nor shall the fact of distribution constitute any such
 *   warranty, and no responsibility is assumed by the USGS in connection
 *   therewith.
 *
 *   For additional information, launch
 *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html in a browser or see
 *   the Privacy &amp; Disclaimers page on the Isis website,
 *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
 *   http://www.usgs.gov/privacy.html.
 */

#include <QList>
#include <QPair>
#include <QString>
#include <QSharedPointer>

#include <opencv2/opencv.hpp>

#include "ControlPointCloudPt.h"
#include "Statistics.h"

namespace Isis {

/**
 * @brief Gather ControlPoints for generation of an output map (radius) pixel
 *  
 * This class will collect ControlPoint candidates that are within 
 * a tolerance generation of an output radius map pixel
 *  
 * @internal 
 * @history 2015-11-16 Kris Becker Original Version 
 */

class MapPointCollector {
  public:
    enum SearchType { UnDefined, Radius, NearestNeighbor };
    MapPointCollector() : m_source(), m_candidates(),
                          m_radius_stats(), m_distance_stats() { }
    virtual ~MapPointCollector() { }

    int size() const {
      return ( m_candidates.size() );
    }

    const Statistics &getRadiusStatistics() const {
      return ( m_radius_stats );
    }

    const Statistics &getDistanceStatistics() const {
      return ( m_distance_stats );
    }

    const ControlPointCloudPt &getSource() const {
      return ( m_source );
    }

    const ControlPointCloudPt &getPoint(const int index) const {
      Q_ASSERT (index >= 0);
      Q_ASSERT ( index < m_candidates.size() );
      return ( m_candidates[index].first );
    }

    double getDistance(const int index) const {
      Q_ASSERT (index >= 0);
      Q_ASSERT ( index < m_candidates.size() );
      return ( m_candidates[index].second );
    }

    SearchType getSearchType() const {
      return ( m_search_type );
    }

    void setSearchType (const SearchType stype) {
      m_search_type = stype;
    }

    void apply(ControlPointCloudPt &source, ControlPointCloudPt &candidate,
               const double distance)  {
#if 0
      if ( !source.isValid() ) { return; }
      if ( !candidate.isValid() ) { return; }
#endif

      m_source = source;
      m_candidates.push_back( qMakePair(candidate, distance));
      m_radius_stats.AddData(candidate.radius());
      m_distance_stats.AddData(distance);
      return;
    }

  /**
   * @brief Noise remove using standard deviations with radius
   * 
   * @author 2015-12-10 Kris Becker
   * 
   * @param sigma Number of standard deviations from the median to filter as 
   *              noise
   * 
   * @return int number removed
   */
    int removeNoise(const double sigma = 3.0) {
       if ( size() < 3 ) return (0);  // Can't really do with less

       double v_median = computeMedian(m_candidates);
       double v_stddev = m_radius_stats.StandardDeviation();
       double v_tolerance = v_stddev * sigma;

       PointDistPair v_outpairs;
       Statistics    v_radiusStats, v_distanceStats;
       for (int i = 0 ; i < m_candidates.size() ; i++) {
         double v_radius = m_candidates[i].first.radius();
         double v_range = qAbs( v_radius - v_median);
         if ( v_range <= v_tolerance)  {
           v_outpairs.append(m_candidates[i]);
           v_radiusStats.AddData(v_radius);
           v_distanceStats.AddData(m_candidates[i].second);
         }
       }

       // Recompute statistics
       int v_removed = m_candidates.size() - v_outpairs.size();
       m_candidates = v_outpairs;
       m_radius_stats = v_radiusStats;
       m_distance_stats = v_distanceStats;
      return ( v_removed );
    }

  private:
    typedef QList< QPair<ControlPointCloudPt, double> >  PointDistPair;
    ControlPointCloudPt  m_source;
    PointDistPair        m_candidates;
    Statistics           m_radius_stats;
    Statistics           m_distance_stats;
    SearchType           m_search_type;

    double computeMedian(const PointDistPair &points) const {
      double median = Null;
      if ( points.size() == 0 )  return ( median );
      if ( points.size() == 1 )  return ( points[0].first.radius() );

      QVector<double> v_radii;
      for (int i = 0 ; i < points.size() ; i++) {
        v_radii.push_back(points[i].first.radius() );
      }

      qSort(v_radii);
      if ( (v_radii.size() % 2) == 0 ) {
        int ndx0 = (v_radii.size() - 1) / 2;
        median = (v_radii[ndx0] + v_radii[ndx0+1]) / 2.0;
      }
      else {
        median = v_radii[v_radii.size()/2];
      }
      return ( median ); 
    }
};

}  // namespace Isis
#endif
Loading