Skip to content
Browse files

stereo: session: Added support for Digital Globe XML

This allows the user to triangulate images with a generic linescan
camera model. No support for RPC, but oh well.
  • Loading branch information...
1 parent a256287 commit e71c8f54ce92af3c6665c03e7794b3bb49a60281 Zack Moratto committed Feb 7, 2012
View
8 .gitignore
@@ -12,6 +12,7 @@
*.pb.cc
*.pb.h
*.pyc
+*.match
*#
/.vimrc.local
/.project.root
@@ -92,6 +93,9 @@ src/asp/IsisIO/tests/TestEphemerisEquations
src/asp/IsisIO/tests/TestIsisAdjustCameraModel
src/asp/IsisIO/tests/TestIsisCameraModel
+# Sessions
+src/asp/Sessions/tests/TestStereoSessionDG
+
# Tools
src/asp/MPI/isis_mpi_adjust
src/asp/Tools/aligndem
@@ -156,9 +160,13 @@ src/asp/ControlNetTK/render_gcp
src/asp/ControlNetTK/vw_position_extract
# Data
+data/*/nonmap
+data/*/map
data/MOC/*cub
data/MOC/*prt
data/MOC/results
+data/CTX/*.cub
+data/CTX/*.IMG
# Doc
docs/book/*aux
View
2 configure.ac
@@ -478,6 +478,8 @@ AC_CONFIG_FILES([ \
src/asp/Sessions/Keypoint/Makefile \
src/asp/Sessions/Pinhole/Makefile \
src/asp/Sessions/RMAX/Makefile \
+ src/asp/Sessions/DG/Makefile \
+ src/asp/Sessions/tests/Makefile \
src/asp/MPI/Makefile \
src/asp/Tools/Makefile \
src/asp/ControlNetTK/Makefile \
View
95 src/asp/Sessions/DG/LinescanDGModel.h
@@ -0,0 +1,95 @@
+// __BEGIN_LICENSE__
+// Copyright (C) 2006-2011 United States Government as represented by
+// the Administrator of the National Aeronautics and Space Administration.
+// All Rights Reserved.
+// __END_LICENSE__
+
+
+/// \file LinescanModel.h
+///
+/// A generic linescan camera model object
+///
+///
+#ifndef __STEREO_SESSION_DG_LINESCAN_DG_MODEL_H__
+#define __STEREO_SESSION_DG_LINESCAN_DG_MODEL_H__
+
+#include <vw/Math/Quaternion.h>
+#include <vw/Camera/CameraModel.h>
+
+namespace asp {
+
+ // This is potentially a more generic line scan camera model that
+ // allows a offset ine the line direction. This also allows a
+ // different function for evaluating time for a specific line
+ // location.
+ //
+ // This expects the pose to be a rotation from the camera frame to
+ // the world frame. The position is a the camera's location in the
+ // world frame.
+ //
+ // The intrinisic model expects +Z to be point out the camera. +X is
+ // the column direction of the image and is perpendicular to
+ // direction of flight. +Y is the row direction of the image (down
+ // the image); it is also the flight direction. This is different
+ // from Digital Globe model, but you can rotate pose beforehand.
+
+ template <class PositionFuncT, class PoseFuncT, class TimeFuncT>
+ class LinescanDGModel : public vw::camera::CameraModel {
+
+ // Extrinsics
+ PositionFuncT m_position_func; // Function of time
+ PoseFuncT m_pose_func;
+ TimeFuncT m_time_func; // Function of line number
+
+ // Intrinsics
+ vw::Vector2i m_image_size; // px
+ vw::Vector2 m_detector_origin; // px
+ double m_focal_length; // px
+
+ public:
+ //------------------------------------------------------------------
+ // Constructors / Destructors
+ //------------------------------------------------------------------
+ LinescanDGModel( PositionFuncT const& position,
+ PoseFuncT const& pose,
+ TimeFuncT const& time,
+ vw::Vector2i const& image_size,
+ vw::Vector2 const& detector_origin,
+ double focal_length ) :
+ m_position_func(position), m_pose_func(pose), m_time_func(time),
+ m_image_size(image_size), m_detector_origin(detector_origin),
+ m_focal_length(focal_length) {}
+
+ virtual ~LinescanDGModel() {}
+ virtual std::string type() const { return "LinescanDG"; }
+
+ //------------------------------------------------------------------
+ // Interface
+ //------------------------------------------------------------------
+ virtual vw::Vector2 point_to_pixel(vw::Vector3 const& /*point*/) const {
+ vw_throw( vw::NoImplErr() << "LinescanModel::point_to_pixel is not yet implemented." );
+ return vw::Vector2(); // never reached
+ }
+
+ // Gives a pointing vector in the world coordinates.
+ virtual vw::Vector3 pixel_to_vector(vw::Vector2 const& pix) const {
+ double t = m_time_func( pix.y() );
+ return normalize(m_pose_func( t ).rotate( vw::Vector3(pix[0]+m_detector_origin[0],
+ m_detector_origin[1],
+ m_focal_length) ) );
+ }
+
+ // Gives a position in world coordinates.
+ virtual vw::Vector3 camera_center(vw::Vector2 const& pix ) const {
+ return m_position_func( m_time_func( pix.y() ) );
+ }
+
+ // Gives a pose vector which represents the rotation from camera to world units
+ virtual vw::Quat camera_pose(vw::Vector2 const& pix) const {
+ return m_pose_func( m_time_func( pix.y() ) );
+ }
+ };
+
+} // namespace asp
+
+#endif//__STEREO_SESSION_DG_LINESCAN_DG_MODEL_H__
View
23 src/asp/Sessions/DG/Makefile.am
@@ -0,0 +1,23 @@
+# __BEGIN_LICENSE__
+# Copyright (C) 2006-2011 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+
+#########################################################################
+# sources
+#########################################################################
+
+include_HEADERS = StereoSessionDG.h XMLBase.h XML.h
+
+#########################################################################
+# general
+#########################################################################
+
+AM_CPPFLAGS = @ASP_CPPFLAGS@
+AM_LDFLAGS = @ASP_LDFLAGS@ -version-info @LIBTOOL_VERSION@
+
+includedir = $(prefix)/include/asp/Sessions/DG
+
+include $(top_srcdir)/config/rules.mak
View
127 src/asp/Sessions/DG/StereoSessionDG.cc
@@ -0,0 +1,127 @@
+// __BEGIN_LICENSE__
+// Copyright (C) 2006-2011 United States Government as represented by
+// the Administrator of the National Aeronautics and Space Administration.
+// All Rights Reserved.
+// __END_LICENSE__
+
+/// \file StereoSessionDG.cc
+///
+
+// Ames Stereo Pipeline
+#include <asp/Core/StereoSettings.h>
+#include <asp/Sessions/DG/LinescanDGModel.h>
+#include <asp/Sessions/DG/StereoSessionDG.h>
+#include <asp/Sessions/DG/XML.h>
+
+// Vision Workbench
+#include <vw/Camera/Extrinsics.h>
+#include <vw/Math/EulerAngles.h>
+
+// Std
+#include <iostream>
+#include <string>
+
+// Other
+#include <xercesc/dom/DOM.hpp>
+#include <xercesc/parsers/XercesDOMParser.hpp>
+#include <xercesc/sax/HandlerBase.hpp>
+#include <xercesc/util/PlatformUtils.hpp>
+#include <xercesc/util/XMLString.hpp>
+
+// Boost
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+
+using namespace vw;
+using namespace asp;
+namespace pt = boost::posix_time;
+
+// Allows FileIO to correctly read/write these pixel types
+namespace vw {
+ template<> struct PixelFormatID<Vector3> { static const PixelFormatEnum value = VW_PIXEL_GENERIC_3_CHANNEL; };
+}
+
+// Helper class for converting to floating point seconds based on a
+// given reference.
+class SecondsFrom {
+ pt::ptime m_reference;
+public:
+ SecondsFrom( pt::ptime const& time ) : m_reference(time) {}
+
+ double operator()( pt::ptime const& time ) const {
+ return double( (time - m_reference).total_microseconds() ) / 1e6;
+ }
+};
+
+// Xerces-C initialize
+asp::StereoSessionDG::StereoSessionDG() {
+ xercesc::XMLPlatformUtils::Initialize();
+}
+
+// Provide our camera model
+boost::shared_ptr<camera::CameraModel>
+asp::StereoSessionDG::camera_model( std::string const& /*image_file*/,
+ std::string const& camera_file ) {
+ GeometricXML geo;
+ AttitudeXML att;
+ EphemerisXML eph;
+ ImageXML img;
+ read_xml( camera_file, geo, att, eph, img );
+
+ // Convert measurements in millimeters to pixels.
+ geo.principal_distance /= geo.detector_pixel_pitch;
+ geo.detector_origin /= geo.detector_pixel_pitch;
+
+ // Convert all time measurements to something that boost::date_time can read.
+ boost::replace_all( eph.start_time, "T", " " );
+ boost::replace_all( img.tlc_start_time, "T", " " );
+ boost::replace_all( img.first_line_start_time, "T", " " );
+ boost::replace_all( att.start_time, "T", " " );
+
+ // Convert UTC time measurements to line measurements. Ephemeris
+ // start time will be our reference frame to calculate seconds
+ // against.
+ SecondsFrom convert( pt::time_from_string( eph.start_time ) );
+
+ // I'm going make the assumption that EPH and ATT are sampled at the
+ // same rate and time.
+ VW_ASSERT( eph.position_vec.size() == att.quat_vec.size(),
+ MathErr() << "Ephemeris and Attitude don't have the same number of samples." );
+ VW_ASSERT( eph.start_time == att.start_time && eph.time_interval == att.time_interval,
+ MathErr() << "Ephemeris and Attitude don't seem to sample with the same t0 or dt." );
+
+ // I also don't support optical distortion yet.
+ VW_ASSERT( geo.optical_polyorder == 0,
+ NoImplErr() << "Cameras with optical distortion are not supported currently." );
+
+ // Convert ephemeris to be position of camera. Change attitude to be
+ // be the rotation from camera frame to world frame. We also add an
+ // additional rotation to the camera frame so X is the horizontal
+ // direction to the picture and +Y points down the image (in the
+ // direction of flight).
+ Quat sensor_coordinate = math::euler_xyz_to_quaternion(Vector3(0,0,geo.detector_rotation * M_PI/180.0 - M_PI/2));
+ for ( size_t i = 0; i < eph.position_vec.size(); i++ ) {
+ eph.position_vec[i] += att.quat_vec[i].rotate( geo.perspective_center );
+ att.quat_vec[i] = att.quat_vec[i] * geo.camera_attitude * sensor_coordinate;
+ }
+
+ typedef LinescanDGModel<camera::PiecewiseAPositionInterpolation, camera::SLERPPoseInterpolation, camera::TLCTimeInterpolation> camera_type;
+ typedef boost::shared_ptr<camera::CameraModel> result_type;
+
+ return result_type( new camera_type( camera::PiecewiseAPositionInterpolation( eph.position_vec, eph.velocity_vec,
+ convert( pt::time_from_string( eph.start_time ) ),
+ eph.time_interval ),
+ camera::SLERPPoseInterpolation( att.quat_vec,
+ convert( pt::time_from_string( att.start_time ) ),
+ att.time_interval ),
+ camera::TLCTimeInterpolation( img.tlc_vec,
+ convert( pt::time_from_string( img.tlc_start_time ) ) ),
+ img.image_size, subvector(inverse(sensor_coordinate).rotate(Vector3(geo.detector_origin[0],
+ geo.detector_origin[1], 0 ) ), 0, 2 ),
+ geo.principal_distance ) );
+}
+
+// Xerces-C terminate
+asp::StereoSessionDG::~StereoSessionDG() {
+ xercesc::XMLPlatformUtils::Terminate();
+}
View
35 src/asp/Sessions/DG/StereoSessionDG.h
@@ -0,0 +1,35 @@
+// __BEGIN_LICENSE__
+// Copyright (C) 2006-2011 United States Government as represented by
+// the Administrator of the National Aeronautics and Space Administration.
+// All Rights Reserved.
+// __END_LICENSE__
+
+/// \file StereoSessionDG.h
+///
+/// This a session to hopefully support Digital Globe images from
+/// Quickbird and World View.
+
+#ifndef __STEREO_SESSION_DG_H__
+#define __STEREO_SESSION_DG_H__
+
+#include <asp/Sessions/Pinhole/StereoSessionPinhole.h>
+
+namespace asp {
+
+ class StereoSessionDG : public StereoSessionPinhole {
+
+ public:
+ StereoSessionDG();
+ virtual ~StereoSessionDG();
+
+ // Produces a camera model from the images
+ virtual boost::shared_ptr<vw::camera::CameraModel>
+ camera_model( std::string const& image_file,
+ std::string const& camera_file = "" );
+
+ static StereoSession* construct() { return new StereoSessionDG; }
+ };
+
+}
+
+#endif//__STEREO_SESSION_DG_H__
View
316 src/asp/Sessions/DG/XML.cc
@@ -0,0 +1,316 @@
+// __BEGIN_LICENSE__
+// Copyright (C) 2006-2011 United States Government as represented by
+// the Administrator of the National Aeronautics and Space Administration.
+// All Rights Reserved.
+// __END_LICENSE__
+
+#include <asp/Sessions/DG/XML.h>
+
+#include <xercesc/parsers/XercesDOMParser.hpp>
+#include <xercesc/sax/HandlerBase.hpp>
+#include <xercesc/util/PlatformUtils.hpp>
+
+using namespace vw;
+using namespace xercesc;
+
+void asp::ImageXML::parse_meta( xercesc::DOMElement* node ) {
+ cast_xmlch( get_node<DOMElement>( node, "TLCTIME" )->getTextContent(),
+ tlc_start_time );
+ cast_xmlch( get_node<DOMElement>( node, "FIRSTLINETIME" )->getTextContent(),
+ first_line_start_time );
+ size_t num_tlc;
+ cast_xmlch( get_node<DOMElement>( node, "NUMTLC" )->getTextContent(),
+ num_tlc );
+ tlc_vec.resize( num_tlc );
+}
+
+void asp::ImageXML::parse_tlc_list( xercesc::DOMElement* node ) {
+ DOMNodeList* children = node->getChildNodes();
+ size_t count = 0;
+
+ for ( XMLSize_t i = 0; i < children->getLength(); i++ ) {
+ if ( children->item(i)->getNodeType() == DOMNode::ELEMENT_NODE ) {
+ DOMElement* element = dynamic_cast<DOMElement*>( children->item(i) );
+ std::string buffer;
+ cast_xmlch( element->getTextContent(), buffer );
+
+ std::istringstream istr( buffer );
+ istr >> tlc_vec[count].first >> tlc_vec[count].second;
+
+ count++;
+ }
+ }
+
+ VW_ASSERT( count == tlc_vec.size(),
+ IOErr() << "Read incorrect number of TLC." );
+}
+
+void asp::ImageXML::parse_image_size( xercesc::DOMElement* node ) {
+ cast_xmlch( get_node<DOMElement>( node, "NUMROWS" )->getTextContent(),
+ image_size[1] );
+ cast_xmlch( get_node<DOMElement>( node, "NUMCOLUMNS" )->getTextContent(),
+ image_size[0] );
+}
+
+asp::ImageXML::ImageXML() : XMLBase(3) {}
+
+void asp::ImageXML::parse( xercesc::DOMElement* node ) {
+ DOMElement* image = get_node<DOMElement>( node, "IMAGE" );
+ parse_meta( image );
+ check_argument(0);
+
+ parse_tlc_list( get_node<DOMElement>( image, "TLCLISTList" ) );
+ check_argument(1);
+
+ parse_image_size( node );
+ check_argument(2);
+}
+
+void asp::GeometricXML::parse_principal_distance( xercesc::DOMElement* node ) {
+ cast_xmlch( get_node<DOMElement>( node, "PD" )->getTextContent(),
+ principal_distance );
+}
+
+void asp::GeometricXML::parse_optical_distortion( xercesc::DOMElement* node ) {
+ cast_xmlch( get_node<DOMElement>( node, "POLYORDER" )->getTextContent(),
+ optical_polyorder );
+ if ( optical_polyorder > 0 )
+ vw_throw( NoImplErr() << "We don't support optical distortion.\n" );
+}
+
+void asp::GeometricXML::parse_perspective_center( xercesc::DOMElement* node ) {
+ cast_xmlch( get_node<DOMElement>( node, "CX" )->getTextContent(),
+ perspective_center[0] );
+ cast_xmlch( get_node<DOMElement>( node, "CY" )->getTextContent(),
+ perspective_center[1] );
+ cast_xmlch( get_node<DOMElement>( node, "CZ" )->getTextContent(),
+ perspective_center[2] );
+}
+
+void asp::GeometricXML::parse_camera_attitude( xercesc::DOMElement* node ) {
+ Vector4 buffer; // We buffer since Q will want to constantly
+ // renormalize itself.
+ cast_xmlch( get_node<DOMElement>( node, "QCS4" )->getTextContent(),
+ buffer[0] );
+ cast_xmlch( get_node<DOMElement>( node, "QCS1" )->getTextContent(),
+ buffer[1] );
+ cast_xmlch( get_node<DOMElement>( node, "QCS2" )->getTextContent(),
+ buffer[2] );
+ cast_xmlch( get_node<DOMElement>( node, "QCS3" )->getTextContent(),
+ buffer[3] );
+ camera_attitude = Quat(buffer[0],buffer[1],buffer[2],buffer[3]);
+}
+
+void asp::GeometricXML::parse_detector_mounting( xercesc::DOMElement* node ) {
+ // Assumes only one band
+ DOMNodeList* children = node->getChildNodes();
+
+ // Do one pass counting the number of bands
+ XMLSize_t band_count = 0;
+ XMLSize_t band_index = 0;
+ for ( XMLSize_t i = 0; i < children->getLength(); i++ ) {
+ DOMNode* current = children->item(i);
+ if ( current->getNodeType() == DOMNode::ELEMENT_NODE ) {
+ DOMElement* element =
+ dynamic_cast< DOMElement* > (current);
+
+ std::string tag( XMLString::transcode(element->getTagName()) );
+ if (boost::starts_with( tag, "BAND_" )) {
+ band_index = i;
+ band_count++;
+ }
+ }
+ }
+
+ // Sanity check that there is just one band
+ VW_ASSERT( band_count == 1,
+ NoImplErr() << "We only expect to see one band data.\n" );
+
+ // Load up the single band
+ DOMElement* band = dynamic_cast<DOMElement*>(children->item(band_index));
+ DOMElement* detector = get_node<DOMElement>( band, "DETECTOR_ARRAY" );
+ cast_xmlch( get_node<DOMElement>(detector,"DETORIGINX")->getTextContent(),
+ detector_origin[0] );
+ cast_xmlch( get_node<DOMElement>(detector,"DETORIGINY")->getTextContent(),
+ detector_origin[1] );
+ cast_xmlch( get_node<DOMElement>(detector,"DETROTANGLE")->getTextContent(),
+ detector_rotation );
+ cast_xmlch( get_node<DOMElement>(detector,"DETPITCH")->getTextContent(),
+ detector_pixel_pitch );
+}
+
+asp::GeometricXML::GeometricXML() : XMLBase(5) {}
+
+void asp::GeometricXML::parse( xercesc::DOMElement* node ) {
+ DOMNodeList* children = node->getChildNodes();
+ const XMLSize_t nodeCount = children->getLength();
+
+ for ( XMLSize_t i = 0; i < nodeCount; ++i ) {
+ DOMNode* current = children->item(i);
+ if ( current->getNodeType() == DOMNode::ELEMENT_NODE ) {
+ DOMElement* element =
+ dynamic_cast< xercesc::DOMElement* >( current );
+
+ std::string tag( XMLString::transcode(element->getTagName()) );
+ if ( tag == "PRINCIPAL_DISTANCE" ) {
+ parse_principal_distance( element );
+ check_argument(0);
+ } else if ( tag == "OPTICAL_DISTORTION" ) {
+ parse_optical_distortion( element );
+ check_argument(1);
+ } else if ( tag == "PERSPECTIVE_CENTER" ) {
+ parse_perspective_center( element );
+ check_argument(2);
+ } else if ( tag == "CAMERA_ATTITUDE" ) {
+ parse_camera_attitude( element );
+ check_argument(3);
+ } else if ( tag == "DETECTOR_MOUNTING" ) {
+ parse_detector_mounting( element );
+ check_argument(4);
+ }
+ }
+ }
+}
+
+void asp::EphemerisXML::parse_meta( xercesc::DOMElement* node ) {
+ cast_xmlch( get_node<DOMElement>( node, "STARTTIME" )->getTextContent(),
+ start_time );
+ cast_xmlch( get_node<DOMElement>( node, "TIMEINTERVAL" )->getTextContent(),
+ time_interval );
+ size_t num_points;
+ cast_xmlch( get_node<DOMElement>( node, "NUMPOINTS" )->getTextContent(),
+ num_points );
+ position_vec.resize( num_points );
+ velocity_vec.resize( num_points );
+ covariance_vec.resize( num_points );
+}
+
+void asp::EphemerisXML::parse_eph_list( xercesc::DOMElement* node ) {
+ DOMNodeList* children = node->getChildNodes();
+ size_t count = 0;
+
+ for ( XMLSize_t i = 0; i < children->getLength(); i++ ) {
+ if ( children->item(i)->getNodeType() == DOMNode::ELEMENT_NODE ) {
+ DOMElement* element = dynamic_cast<DOMElement*>( children->item(i) );
+ std::string buffer;
+ cast_xmlch( element->getTextContent(), buffer );
+
+ std::istringstream istr( buffer );
+ std::string index_b;
+ istr >> index_b;
+ size_t index = size_t(boost::lexical_cast<float>(index_b) + 0.5) - 1;
+ istr >> position_vec[index][0] >> position_vec[index][1]
+ >> position_vec[index][2] >> velocity_vec[index][0]
+ >> velocity_vec[index][1] >> velocity_vec[index][2];
+ istr >> covariance_vec[index][0] >> covariance_vec[index][1]
+ >> covariance_vec[index][2] >> covariance_vec[index][3]
+ >> covariance_vec[index][4] >> covariance_vec[index][5];
+
+ count++;
+ }
+ }
+
+ VW_ASSERT( count == position_vec.size(),
+ IOErr() << "Read incorrect number of points." );
+}
+
+asp::EphemerisXML::EphemerisXML() : XMLBase(2) {}
+
+void asp::EphemerisXML::parse( xercesc::DOMElement* node ) {
+ parse_meta( node );
+ check_argument(0);
+
+ parse_eph_list( get_node<DOMElement>( node, "EPHEMLISTList" ) );
+ check_argument(1);
+}
+
+void asp::AttitudeXML::parse_meta( xercesc::DOMElement* node ) {
+ cast_xmlch( get_node<DOMElement>( node, "STARTTIME" )->getTextContent(),
+ start_time );
+ cast_xmlch( get_node<DOMElement>( node, "TIMEINTERVAL" )->getTextContent(),
+ time_interval );
+ size_t num_points;
+ cast_xmlch( get_node<DOMElement>( node, "NUMPOINTS" )->getTextContent(),
+ num_points );
+ quat_vec.resize( num_points );
+ covariance_vec.resize( num_points );
+}
+
+void asp::AttitudeXML::parse_att_list( xercesc::DOMElement* node ) {
+ DOMNodeList* children = node->getChildNodes();
+ size_t count = 0;
+ Vector4 qbuf;
+
+ for ( XMLSize_t i = 0; i < children->getLength(); i++ ) {
+ if ( children->item(i)->getNodeType() == DOMNode::ELEMENT_NODE ) {
+ DOMElement* element = dynamic_cast<DOMElement*>( children->item(i) );
+ std::string buffer;
+ cast_xmlch( element->getTextContent(), buffer );
+
+ std::istringstream istr( buffer );
+ std::string index_b;
+ istr >> index_b;
+ size_t index = size_t(boost::lexical_cast<float>(index_b) + 0.5) - 1;
+ istr >> qbuf[0] >> qbuf[1] >> qbuf[2] >> qbuf[3];
+ quat_vec[index] = Quat(qbuf[3], qbuf[0], qbuf[1], qbuf[2] );
+ istr >> covariance_vec[index][0] >> covariance_vec[index][1]
+ >> covariance_vec[index][2] >> covariance_vec[index][3]
+ >> covariance_vec[index][4] >> covariance_vec[index][5]
+ >> covariance_vec[index][6] >> covariance_vec[index][7]
+ >> covariance_vec[index][8] >> covariance_vec[index][9];
+
+ count++;
+ }
+ }
+
+ VW_ASSERT( count == quat_vec.size(),
+ IOErr() << "Read incorrect number of points." );
+}
+
+asp::AttitudeXML::AttitudeXML() : XMLBase(2) {}
+
+void asp::AttitudeXML::parse( xercesc::DOMElement* node ) {
+ parse_meta( node );
+ check_argument(0);
+
+ parse_att_list( get_node<DOMElement>( node, "ATTLISTList" ) );
+ check_argument(1);
+}
+
+// Helper functions to allow us to fill the objects
+void asp::read_xml( std::string const& filename,
+ GeometricXML& geo,
+ AttitudeXML& att,
+ EphemerisXML& eph,
+ ImageXML& img ) {
+
+ boost::scoped_ptr<XercesDOMParser> parser( new XercesDOMParser() );
+ parser->setValidationScheme(XercesDOMParser::Val_Always);
+ parser->setDoNamespaces(true);
+ boost::scoped_ptr<ErrorHandler> errHandler( new HandlerBase() );
+ parser->setErrorHandler(errHandler.get());
+
+ parser->parse( filename.c_str() );
+ DOMDocument* xmlDoc = parser->getDocument();
+ DOMElement* elementRoot = xmlDoc->getDocumentElement();
+
+ DOMNodeList* children = elementRoot->getChildNodes();
+ for ( XMLSize_t i = 0; i < children->getLength(); ++i ) {
+ DOMNode* curr_node = children->item(i);
+ if ( curr_node->getNodeType() == DOMNode::ELEMENT_NODE ) {
+ DOMElement* curr_element =
+ dynamic_cast<DOMElement*>( curr_node );
+
+ std::string tag( XMLString::transcode(curr_element->getTagName()) );
+
+ if ( tag == "GEO" )
+ geo.parse( curr_element );
+ else if ( tag == "EPH" )
+ eph.parse( curr_element );
+ else if ( tag == "ATT" )
+ att.parse( curr_element );
+ else if ( tag == "IMD" )
+ img.parse( curr_element );
+ }
+ }
+}
View
106 src/asp/Sessions/DG/XML.h
@@ -0,0 +1,106 @@
+// __BEGIN_LICENSE__
+// Copyright (C) 2006-2011 United States Government as represented by
+// the Administrator of the National Aeronautics and Space Administration.
+// All Rights Reserved.
+// __END_LICENSE__
+
+// These are objects that relate directly to block in XML that we need
+// to read. They only read and then store the raw values. Other
+// objects will interpret the results.
+
+#ifndef __STEREO_SESSION_DG_XML_H__
+#define __STEREO_SESSION_DG_XML_H__
+
+#include <asp/Sessions/DG/XMLBase.h>
+#include <vector>
+#include <vw/Math/Vector.h>
+#include <vw/Math/Quaternion.h>
+
+namespace asp {
+
+ // Objects that represent read data from XML. These also provide a
+ // storage structure for modification later on.
+ class ImageXML : public XMLBase {
+
+ void parse_meta( xercesc::DOMElement* node );
+ void parse_tlc_list( xercesc::DOMElement* node );
+ void parse_image_size( xercesc::DOMElement* node );
+
+ public:
+ ImageXML();
+
+ void parse( xercesc::DOMElement* node );
+
+ std::string tlc_start_time;
+ std::string first_line_start_time;
+ std::vector<std::pair<double,double> > tlc_vec; // Line -> time offset pairings
+ vw::Vector2i image_size;
+ };
+
+ class GeometricXML : public XMLBase {
+
+ void parse_principal_distance( xercesc::DOMElement* node );
+ void parse_optical_distortion( xercesc::DOMElement* node );
+ void parse_perspective_center( xercesc::DOMElement* node );
+ void parse_camera_attitude( xercesc::DOMElement* node );
+ void parse_detector_mounting( xercesc::DOMElement* node );
+
+ public:
+ GeometricXML();
+
+ void parse( xercesc::DOMElement* node );
+
+ double principal_distance; // mm
+ vw::int32 optical_polyorder;
+ vw::Vector<double> optical_a, optical_b; // Don't currently support these
+ vw::Vector3 perspective_center; // meters in spacecraft frame
+ vw::Quat camera_attitude;
+ vw::Vector2 detector_origin; // mm
+ double detector_rotation; // degrees about Z+ in camera frame
+ double detector_pixel_pitch; // mm
+ };
+
+ class EphemerisXML : public XMLBase {
+
+ void parse_meta( xercesc::DOMElement* node );
+ void parse_eph_list( xercesc::DOMElement* node );
+
+ public:
+ EphemerisXML();
+
+ void parse( xercesc::DOMElement* node );
+
+ std::string start_time; // UTC
+ double time_interval; // seconds
+ std::vector<vw::Vector3> position_vec, velocity_vec; // ECEF
+ std::vector<vw::Vector<double,6> > covariance_vec; // Tri-diagonal
+ };
+
+ class AttitudeXML : public XMLBase {
+
+ void parse_meta( xercesc::DOMElement* node );
+ void parse_att_list( xercesc::DOMElement* node );
+
+ public:
+ AttitudeXML();
+
+ void parse( xercesc::DOMElement* node );
+
+ std::string start_time;
+ double time_interval;
+ std::vector<vw::Quat> quat_vec;
+ std::vector<vw::Vector<double,10> > covariance_vec;
+ };
+
+ // Helper functions to allow us to fill the objects. This doesn't
+ // really help with code reuse but I think it makes it easer to
+ // read.
+ void read_xml( std::string const& filename,
+ GeometricXML& geo,
+ AttitudeXML& att,
+ EphemerisXML& eph,
+ ImageXML& img );
+
+} //end namespace asp
+
+#endif//__STEREO_SESSION_DG_XML_H__
View
30 src/asp/Sessions/DG/XMLBase.cc
@@ -0,0 +1,30 @@
+// __BEGIN_LICENSE__
+// Copyright (C) 2006-2011 United States Government as represented by
+// the Administrator of the National Aeronautics and Space Administration.
+// All Rights Reserved.
+// __END_LICENSE__
+
+#include <asp/Sessions/DG/XMLBase.h>
+
+using namespace vw;
+
+void asp::XMLBase::check_argument( vw::uint8 arg ) {
+ m_checksum |= 0x1 << arg;
+}
+
+asp::XMLBase::XMLBase( vw::uint8 num_arguments ) : m_num_arguments(num_arguments), m_checksum(0) {
+ VW_ASSERT( num_arguments != 0,
+ ArgumentErr() << "There must be at least one thing you read.\n");
+ VW_ASSERT( num_arguments <= 32,
+ ArgumentErr() << "You can only have up to 32 checks.\n" );
+ int32 x = 0x1;
+ for ( uint8 i = num_arguments - 1; i != 0; --i ) {
+ x <<= 1;
+ x |= 0x1;
+ }
+ m_good = x;
+}
+
+bool asp::XMLBase::is_good() const {
+ return (m_good ^ m_checksum) == 0;
+}
View
64 src/asp/Sessions/DG/XMLBase.h
@@ -0,0 +1,64 @@
+// __BEGIN_LICENSE__
+// Copyright (C) 2006-2011 United States Government as represented by
+// the Administrator of the National Aeronautics and Space Administration.
+// All Rights Reserved.
+// __END_LICENSE__
+
+// This is header shouldn't be included by any other headers since it
+// brings in a bunch of Xerces Headers. This way I can limit the
+// spread of those headers and objects.
+
+#ifndef __STEREO_SESSION_DG_XMLBASE_H__
+#define __STEREO_SESSION_DG_XMLBASE_H__
+
+#include <vw/Core.h>
+#include <boost/lexical_cast.hpp>
+
+#include <xercesc/dom/DOM.hpp>
+#include <xercesc/util/XMLString.hpp>
+
+namespace asp {
+
+ // XML parsing base that provides tools for verifying that we read
+ // everything we were expecting.
+ class XMLBase {
+ vw::uint8 m_num_arguments;
+ vw::int32 m_checksum;
+ vw::int32 m_good;
+
+ protected:
+ // Used to check off that one of the arguments has been read.
+ void check_argument( vw::uint8 arg );
+
+ // Helper function to convert XML text to binary value we want.
+ template <class T>
+ void cast_xmlch( const XMLCh* ch, T& dst) {
+ char* text = xercesc::XMLString::transcode(ch);
+ dst = boost::lexical_cast<T>( text );
+ xercesc::XMLString::release( &text );
+ }
+
+ // Helper function to retreive a node via string and verify that
+ // only one exists.
+ template <class T>
+ T* get_node( xercesc::DOMElement* element, std::string const& tag ) {
+ XMLCh* tag_c =
+ xercesc::XMLString::transcode(tag.c_str());
+ xercesc::DOMNodeList* list = element->getElementsByTagName( tag_c );
+ VW_ASSERT( list->getLength() != 0,
+ vw::IOErr() << "Couldn't find \"" << tag << "\" tag." );
+ VW_ASSERT( list->getLength() == 1,
+ vw::IOErr() << "Found multiple \"" << tag << "\" tags." );
+ xercesc::XMLString::release(&tag_c);
+ return dynamic_cast<T*>(list->item(0));
+ }
+
+ public:
+ XMLBase( vw::uint8 num_arguments );
+
+ bool is_good() const;
+ };
+
+} // end namespace asp
+
+#endif//__STEREO_SESSION_DG_XMLBASE_H__
View
5 src/asp/Sessions/Makefile.am
@@ -16,13 +16,14 @@ include_HEADERS = StereoSession.h
libaspSessions_la_SOURCES = StereoSession.cc \
Keypoint/StereoSessionKeypoint.cc \
Pinhole/StereoSessionPinhole.cc RMAX/RMAX.cc \
- RMAX/StereoSessionRmax.cc
+ RMAX/StereoSessionRmax.cc DG/StereoSessionDG.cc \
+ DG/XMLBase.cc DG/XML.cc
libaspSessions_la_LIBADD = @MODULE_SESSIONS_LIBS@
lib_LTLIBRARIES = libaspSessions.la
-SUBDIRS = . Keypoint Pinhole RMAX
+SUBDIRS = . Keypoint Pinhole RMAX DG tests
if HAVE_PKG_ISIS
SUBDIRS += ISIS
View
4 src/asp/Sessions/Pinhole/StereoSessionPinhole.cc
@@ -259,7 +259,7 @@ void asp::StereoSessionPinhole::pre_pointcloud_hook(std::string const& input_fil
vw::Matrix<double> align_matrix;
try {
read_matrix(align_matrix, m_out_prefix + "-align.exr");
- vw_out(DebugMessage) << "Alignment Matrix: " << align_matrix << "\n";
+ vw_out(DebugMessage,"asp") << "Alignment Matrix: " << align_matrix << "\n";
} catch ( vw::IOErr const& e ) {
vw_out() << "\nCould not read in alignment matrix: " << m_out_prefix
<< "-align.exr. Exiting. \n\n";
@@ -276,7 +276,7 @@ void asp::StereoSessionPinhole::pre_pointcloud_hook(std::string const& input_fil
right_disk_image.rows()) );
block_write_gdal_image( output_file, result, m_options,
- TerminalProgressCallback("asp", "\t Saving: ") );
+ TerminalProgressCallback("asp", "\tRemoving H: ") );
} else {
output_file = input_file;
}
View
5 src/asp/Sessions/StereoSession.cc
@@ -10,6 +10,7 @@
#include <asp/Sessions/StereoSession.h>
#include <asp/Sessions/Pinhole/StereoSessionPinhole.h>
+#include <asp/Sessions/DG/StereoSessionDG.h>
#include <vw/Core/Exception.h>
@@ -50,7 +51,9 @@ static void register_default_session_types() {
if ( already ) return;
already = true;
asp::StereoSession::register_session_type( "pinhole",
- &asp::StereoSessionPinhole::construct);
+ &asp::StereoSessionPinhole::construct );
+ asp::StereoSession::register_session_type( "dg",
+ &asp::StereoSessionDG::construct );
}
asp::StereoSession* asp::StereoSession::create( std::string const& session_type ) {
View
30 src/asp/Sessions/tests/Makefile.am
@@ -0,0 +1,30 @@
+# __BEGIN_LICENSE__
+# Copyright (C) 2006-2011 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+
+########################################################################
+# sources
+########################################################################
+
+if MAKE_MODULE_SESSIONS
+
+TestStereoSessionDG_SOURCES = TestStereoSessionDG.cxx
+
+TESTS = TestStereoSessionDG
+
+endif
+
+########################################################################
+# general
+########################################################################
+
+AM_CPPFLAGS = @ASP_CPPFLAGS@
+AM_LDFLAGS = @ASP_LDFLAGS@ @PKG_SESSIONS_LIBS@
+
+check_PROGRAMS = $(TESTS)
+
+include $(top_srcdir)/config/rules.mak
+include $(top_srcdir)/config/tests.am
View
107 src/asp/Sessions/tests/TestStereoSessionDG.cxx
@@ -0,0 +1,107 @@
+// __BEGIN_LICENSE__
+// Copyright (C) 2006-2011 United States Government as represented by
+// the Administrator of the National Aeronautics and Space Administration.
+// All Rights Reserved.
+// __END_LICENSE__
+
+#include <asp/Sessions/DG/StereoSessionDG.h>
+#include <asp/Sessions/DG/XML.h>
+#include <boost/scoped_ptr.hpp>
+#include <test/Helpers.h>
+
+#include <vw/Stereo/StereoModel.h>
+
+using namespace vw;
+using namespace asp;
+using namespace xercesc;
+
+TEST(StereoSessionDG, XMLReading) {
+ XMLPlatformUtils::Initialize();
+
+ GeometricXML geo;
+ AttitudeXML att;
+ EphemerisXML eph;
+ ImageXML img;
+
+ EXPECT_FALSE( geo.is_good() );
+ EXPECT_FALSE( att.is_good() );
+ EXPECT_FALSE( eph.is_good() );
+ EXPECT_FALSE( img.is_good() );
+
+ read_xml( "dg_example1.xml",
+ geo, att, eph, img );
+
+ EXPECT_TRUE( geo.is_good() );
+ EXPECT_TRUE( att.is_good() );
+ EXPECT_TRUE( eph.is_good() );
+ EXPECT_TRUE( img.is_good() );
+
+ // Checking GEO
+ EXPECT_NEAR( 7949.165, geo.principal_distance, 1e-6 );
+ EXPECT_EQ( 0, geo.optical_polyorder );
+ EXPECT_VECTOR_NEAR( Vector3(), geo.perspective_center, 1e-6 );
+ EXPECT_NEAR( 0, geo.camera_attitude.x(), 1e-6 );
+ EXPECT_NEAR( 1, geo.camera_attitude.w(), 1e-6 );
+ EXPECT_VECTOR_NEAR( Vector2(.05372,140.71193), geo.detector_origin, 1e-6 );
+ EXPECT_NEAR( 0, geo.detector_rotation, 1e-6 );
+ EXPECT_NEAR( .008, geo.detector_pixel_pitch, 1e-6 );
+
+ // Checking ATT
+ EXPECT_FALSE( att.start_time.empty() );
+ EXPECT_NEAR( .02, att.time_interval, 1e-6 );
+ EXPECT_EQ( 840, att.quat_vec.size() );
+ EXPECT_EQ( 840, att.covariance_vec.size() );
+ for ( size_t i = 0; i < 840; i++ ) {
+ EXPECT_NE( 0, att.quat_vec[i].w() );
+ EXPECT_NE( 0, att.covariance_vec[i][5] );
+ }
+ EXPECT_VECTOR_NEAR( Vector3(3.72e-12, 3.51e-12, 1.12e-13),
+ subvector(att.covariance_vec[0],0,3), 1e-20 );
+
+ // Checking EPH
+ EXPECT_FALSE( eph.start_time.empty() );
+ EXPECT_NEAR( .02, eph.time_interval, 1e-6 );
+ EXPECT_EQ( 840, eph.position_vec.size() );
+ EXPECT_EQ( 840, eph.velocity_vec.size() );
+ EXPECT_EQ( 840, eph.covariance_vec.size() );
+ for ( size_t i = 0; i < 840; i++ ) {
+ EXPECT_NE( 0, eph.position_vec[i].x() ) << i;
+ EXPECT_NE( 0, eph.velocity_vec[i].x() );
+ EXPECT_NE( 0, eph.covariance_vec[i][3] );
+ }
+ EXPECT_VECTOR_NEAR( Vector3(-1.150529111070304e+06,
+ -4.900037170821411e+06,
+ 4.673402253879593e+06),
+ eph.position_vec[0], 1e-6 );
+
+ // Checking IMG
+ EXPECT_FALSE( img.tlc_start_time.empty() );
+ EXPECT_FALSE( img.first_line_start_time.empty() );
+ EXPECT_EQ( 2, img.tlc_vec.size() );
+ EXPECT_NEAR( 0, img.tlc_vec[0].first, 1e-8 );
+ EXPECT_NEAR( 0, img.tlc_vec[0].second, 1e-8 );
+ EXPECT_NEAR( 23708, img.tlc_vec[1].first, 1e-8 );
+ EXPECT_NEAR( 1.975667, img.tlc_vec[1].second, 1e-8 );
+ EXPECT_EQ( 23708, img.image_size.y() );
+ EXPECT_EQ( 35170, img.image_size.x() );
+
+ XMLPlatformUtils::Terminate();
+}
+
+TEST(StereoSessionDG, CreateCamera) {
+ StereoSessionDG session;
+
+ boost::shared_ptr<camera::CameraModel> cam1( session.camera_model("", "dg_example1.xml") ),
+ cam2( session.camera_model("", "dg_example2.xml") );
+ ASSERT_TRUE( cam1.get() != 0 );
+ ASSERT_TRUE( cam2.get() != 0 );
+
+ Vector2 m1(13864, 5351), m2(15045, 5183);
+ m1 *= 2; m2 *= 2;
+
+ stereo::StereoModel sm(cam1.get(), cam2.get());
+ double error;
+ Vector3 pos = sm(m1,m2,error);
+ EXPECT_LT( error, 5.0 ); // Triangulation should be better than 5 meters.
+ EXPECT_LT( norm_2(pos), norm_2(cam1->camera_center(m1)) ); // Point should be below camera.
+}
View
1,803 src/asp/Sessions/tests/dg_example1.xml
1,803 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
2,015 src/asp/Sessions/tests/dg_example2.xml
2,015 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
11 src/asp/Tools/stereo.cc
@@ -77,15 +77,20 @@ void vw::handle_arguments( int argc, char *argv[], Options& opt ) {
vw_out() << "\t--> Detected pinhole camera files. "
<< "Executing pinhole stereo pipeline.\n";
opt.stereo_session_string = "pinhole";
- } else if (boost::iends_with(opt.in_file1, ".cub") &&
- boost::iends_with(opt.in_file2, ".cub")) {
+ } else if (boost::iends_with(boost::to_lower_copy(opt.in_file1), ".cub") &&
+ boost::iends_with(boost::to_lower_copy(opt.in_file2), ".cub")) {
vw_out() << "\t--> Detected ISIS cube files. "
<< "Executing ISIS stereo pipeline.\n";
opt.stereo_session_string = "isis";
+ } else if (boost::iends_with(boost::to_lower_copy(opt.cam_file1), ".xml") &&
+ boost::iends_with(boost::to_lower_copy(opt.cam_file2), ".xml")) {
+ vw_out() << "\t--> Detected likely Digital Globe XML files. "
+ << "Executing DG stereo pipeline.\n";
+ opt.stereo_session_string = "dg";
} else {
vw_throw( ArgumentErr() << "Could not determine stereo session type. "
<< "Please set it explicitly.\n"
- << "using the -t switch. Options include: [pinhole isis].\n" );
+ << "using the -t switch. Options include: [pinhole isis dg].\n" );
}
}
View
2 src/asp/Tools/stereo.in
@@ -43,7 +43,7 @@ if __name__ == '__main__':
p.add_option('--threads', dest='threads', type='int',
help='Select the number of processors (threads) to use.')
p.add_option('-t', '--session-type', dest='session',
- help='Select the stereo session type to use for processing. [options: pinhole isis]')
+ help='Select the stereo session type to use for processing. [options: pinhole isis dg]')
p.add_option('-s', '--stereo-file', dest='filename', default='./stereo.default',
help='Explicitly specify the stereo.default file to use. [default: ./stereo.default]')
p.add_option('-e', '--entry-point', dest='entry_point', default=0,
View
12 src/asp/Tools/stereo_tri.cc
@@ -137,6 +137,18 @@ int main(int argc, char* argv[]) {
<< "\tmodels as most likely stereo won't be able\n"
<< "\tto triangulate or perform epipolar rectification.\n";
+ // Developer friendly help
+ VW_OUT(DebugMessage,"asp") << "Camera 1 location: " << camera_model1->camera_center(Vector2()) << "\n"
+ << " in Lon Lat Rad: " << cartography::xyz_to_lon_lat_radius(camera_model1->camera_center(Vector2())) << "\n";
+ VW_OUT(DebugMessage,"asp") << "Camera 2 location: " << camera_model2->camera_center(Vector2()) << "\n"
+ << " in Lon Lat Rad: " << cartography::xyz_to_lon_lat_radius(camera_model2->camera_center(Vector2())) << "\n";
+ VW_OUT(DebugMessage,"asp") << "Camera 1 Pointing Dir: " << camera_model1->pixel_to_vector(Vector2()) << "\n"
+ << " dot against pos: " << dot_prod(camera_model1->pixel_to_vector(Vector2()),
+ camera_model1->camera_center(Vector2())) << "\n";
+ VW_OUT(DebugMessage,"asp") << "Camera 2 Pointing Dir: " << camera_model2->pixel_to_vector(Vector2()) << "\n"
+ << " dot against pos: " << dot_prod(camera_model2->pixel_to_vector(Vector2()),
+ camera_model2->camera_center(Vector2())) << "\n";
+
// Can cameras triangulate to point at something in front of them?
stereo::StereoModel model( camera_model1.get(), camera_model2.get() );
double error;

0 comments on commit e71c8f5

Please sign in to comment.
Something went wrong with that request. Please try again.