From a2456748f2f99ad312a7c8b60d1f46914f49177c Mon Sep 17 00:00:00 2001 From: Julien Michel <julien.michel@orfeo-toolbox.org> Date: Mon, 3 Dec 2012 15:50:32 +0100 Subject: [PATCH] TEST: Adding tests for the new RPCSolverAdapter class --- Testing/Code/UtilitiesAdapters/CMakeLists.txt | 37 ++++ .../otbRPCSolverAdapterTest.cxx | 180 ++++++++++++++++++ .../otbUtilitiesAdaptersTests1.cxx | 1 + 3 files changed, 218 insertions(+) create mode 100644 Testing/Code/UtilitiesAdapters/otbRPCSolverAdapterTest.cxx diff --git a/Testing/Code/UtilitiesAdapters/CMakeLists.txt b/Testing/Code/UtilitiesAdapters/CMakeLists.txt index cd155d1d3b..e7e06978d7 100644 --- a/Testing/Code/UtilitiesAdapters/CMakeLists.txt +++ b/Testing/Code/UtilitiesAdapters/CMakeLists.txt @@ -32,12 +32,49 @@ ADD_TEST(uaTvGeometricSarSensorModelAdapterTest otbGeometricSarSensorModelAdapterTest ${LARGEINPUT}/TERRASARX/2008-03-10_GrandCanyon_SSC/TSX1_SAR__SSC______SM_S_SRA_20080310T133220_20080310T133228/TSX1_SAR__SSC______SM_S_SRA_20080310T133220_20080310T133228.xml ${TEMP}/uaTvGeometricSarSensorModelAdapterTest.txt) + +ADD_TEST(uaTvRPCSolverAdapterValidationTest + ${UtilitiesAdapters_TESTS1} + otbRPCSolverAdapterTest + ${LARGEINPUT}/QUICKBIRD/TOULOUSE/000000128955_01_P001_PAN/02APR01105228-P1BS-000000128955_01_P001.TIF + 10 0.25 0.35 + ${INPUTDATA}/DEM/srtm_directory/ + ${INPUTDATA}/DEM/egm96.grd +) + +ADD_TEST(uaTvRPCSolverAdapterOutGeomTest + ${UtilitiesAdapters_TESTS1} + --compare-ascii ${EPSILON_9} + ${BASELINE_FILES}/uaTvRPCSolverAdapterOutGeomTest.geom + ${TEMP}/uaTvRPCSolverAdapterOutGeomTest.geom + otbRPCSolverAdapterTest + ${LARGEINPUT}/QUICKBIRD/TOULOUSE/000000128955_01_P001_PAN/02APR01105228-P1BS-000000128955_01_P001.TIF + 10 0.25 0.35 + ${INPUTDATA}/DEM/srtm_directory/ + ${INPUTDATA}/DEM/egm96.grd + ${TEMP}/uaTvRPCSolverAdapterOutGeomTest.geom +) + +ADD_TEST(uaTvRPCSolverAdapterNotEnoughPointsTest + ${UtilitiesAdapters_TESTS1} + otbRPCSolverAdapterTest + ${LARGEINPUT}/QUICKBIRD/TOULOUSE/000000128955_01_P001_PAN/02APR01105228-P1BS-000000128955_01_P001.TIF + 4 0.25 0.35 + ${INPUTDATA}/DEM/srtm_directory/ + ${INPUTDATA}/DEM/egm96.grd + ) +SET_TESTS_PROPERTIES(uaTvRPCSolverAdapterNotEnoughPointsTest PROPERTIES WILL_FAIL TRUE) + + + + ENDIF(OTB_DATA_USE_LARGEINPUT) SET(UtilitiesAdapters_SRCS1 otbUtilitiesAdaptersTests1.cxx otbPlatformPositionAdapter.cxx otbGeometricSarSensorModelAdapter.cxx +otbRPCSolverAdapterTest.cxx ) diff --git a/Testing/Code/UtilitiesAdapters/otbRPCSolverAdapterTest.cxx b/Testing/Code/UtilitiesAdapters/otbRPCSolverAdapterTest.cxx new file mode 100644 index 0000000000..61e948f3e9 --- /dev/null +++ b/Testing/Code/UtilitiesAdapters/otbRPCSolverAdapterTest.cxx @@ -0,0 +1,180 @@ +/*========================================================================= + + Program: ORFEO Toolbox + Language: C++ + Date: $Date$ + Version: $Revision$ + + + Copyright (c) Centre National d'Etudes Spatiales. All rights reserved. + See OTBCopyright.txt for details. + + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#include "otbMacro.h" +#include "otbImage.h" +#include "otbImageFileReader.h" +#include "otbGenericRSTransform.h" +#include "otbGeographicalDistance.h" +#include "itkEuclideanDistance.h" +#include "otbDEMHandler.h" +#include "otbSensorModelAdapter.h" +#include "otbRPCSolverAdapter.h" + +typedef otb::Image<double> ImageType; +typedef otb::ImageFileReader<ImageType> ReaderType; +typedef otb::GenericRSTransform<> RSTranformType; +typedef otb::RPCSolverAdapter::Point2DType Point2DType; +typedef otb::RPCSolverAdapter::Point3DType Point3DType; +typedef otb::GenericRSTransform<double,3,3> RSTranform3dType; +typedef itk::Statistics::EuclideanDistance<Point2DType> EuclideanDistanceType; +typedef otb::GeographicalDistance<Point2DType> GeoDistanceType; + +int otbRPCSolverAdapterTest(int argc, char* argv[]) +{ + // This test takes a sensor model (possibly a rpc one), use it to + // generate gcps and estimate a rpc model. It then checks the + // precision of both forward and inverse transform + std::string infname = argv[1]; + const unsigned int gridSize = atoi(argv[2]); + const double geoTol = atof(argv[3]); + const double imgTol = atof(argv[4]); + const std::string demdir = argv[5]; + const std::string geoid = argv[6]; + + std::cout<<"GeoTol: "<<geoTol<<" meters"<<std::endl; + std::cout<<"ImgTol: "<<imgTol<<" pixels"<<std::endl; + + otb::DEMHandler::Instance()->OpenDEMDirectory(demdir); + otb::DEMHandler::Instance()->OpenGeoidFile(geoid); + + ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(infname); + reader->UpdateOutputInformation(); + + RSTranformType::Pointer fwd2dTransform = RSTranformType::New(); + fwd2dTransform->SetInputKeywordList(reader->GetOutput()->GetImageKeywordlist()); + fwd2dTransform->InstanciateTransform(); + + ImageType::SizeType size = reader->GetOutput()->GetLargestPossibleRegion().GetSize(); + + unsigned int stepx = size[0]/gridSize; + unsigned int stepy = size[1]/gridSize; + + otb::RPCSolverAdapter::GCPsContainerType gcps; + + // Generate gcps + for(unsigned int i = 0; i < gridSize;++i) + { + for(unsigned int j = 0; j < gridSize;++j) + { + ImageType::IndexType currentIndex; + currentIndex[0] = i*stepx; + currentIndex[1] = j*stepy; + + Point2DType currentPoint, currentWgs84Point; + reader->GetOutput()->TransformIndexToPhysicalPoint(currentIndex,currentPoint); + + currentWgs84Point = fwd2dTransform->TransformPoint(currentPoint); + + double height = otb::DEMHandler::Instance()->GetHeightAboveEllipsoid(currentWgs84Point); + + Point3DType current3DWgs84Point; + current3DWgs84Point[0] = currentWgs84Point[0]; + current3DWgs84Point[1] = currentWgs84Point[1]; + current3DWgs84Point[2] = height; + + gcps.push_back(std::make_pair(currentPoint,current3DWgs84Point)); + + std::cout<<"Adding gcp: "<<currentPoint<<" <-> " << current3DWgs84Point<<std::endl; + } + } + + // Solve rpc + otb::ImageKeywordlist rpcKwl; + double rmse; + + Point2DType ul = reader->GetOutput()->GetOrigin(); + Point2DType lr; + lr[0] = ul[0] + size[0]*reader->GetOutput()->GetSpacing()[0]; + lr[1] = ul[1] + size[1]*reader->GetOutput()->GetSpacing()[1]; + + // Call solver: either write geom and exit, or evaluate model precision + if(argc==8) + { + const std::string outgeom = argv[7]; + bool success = otb::RPCSolverAdapter::Solve(gcps,ul,lr,rmse,outgeom); + if(success) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } + + otb::RPCSolverAdapter::Solve(gcps,ul,lr,rmse,rpcKwl); + + std::cout<<"Optimization done, RMSE="<<rmse<<std::endl; + + // Build forward and inverse rpc transform + RSTranform3dType::Pointer rpcFwdTransform = RSTranform3dType::New(); + rpcFwdTransform->SetInputKeywordList(rpcKwl); + rpcFwdTransform->InstanciateTransform(); + RSTranformType::Pointer rpcInvTransform = RSTranformType::New(); + rpcInvTransform->SetOutputKeywordList(rpcKwl); + rpcInvTransform->InstanciateTransform(); + + EuclideanDistanceType::Pointer euclideanDistance = EuclideanDistanceType::New(); + GeoDistanceType::Pointer geoDistance = GeoDistanceType::New(); + + bool fail = false; + + for(otb::RPCSolverAdapter::GCPsContainerType::iterator it = gcps.begin(); + it != gcps.end();++it) + { + Point2DType imgPoint,groundPoint, groundPoint2dRef; + Point3DType imgPoint3D,groundPoint3D; + + groundPoint2dRef[0] = it->second[0]; + groundPoint2dRef[1] = it->second[1]; + + // Check forward transform + imgPoint3D[0] = it->first[0]; + imgPoint3D[1] = it->first[1]; + imgPoint3D[2] = it->second[2]; + + groundPoint3D = rpcFwdTransform->TransformPoint(imgPoint3D); + + groundPoint[0] = groundPoint3D[0]; + groundPoint[1] = groundPoint3D[1]; + + double groundRes = geoDistance->Evaluate(groundPoint,groundPoint2dRef); + + if(groundRes>geoTol) + { + fail = true; + std::cerr<<"Imprecise result with forward estimated model: fwd("<<it->first<<") = "<<groundPoint<<", but reference is "<<groundPoint2dRef<<" error: "<<groundRes<<" meters)"<<std::endl; + } + + // Check inverse transform + imgPoint = rpcInvTransform->TransformPoint(groundPoint2dRef); + + double imgRes = euclideanDistance->Evaluate(imgPoint,it->first); + + if(imgRes>imgTol) + { + fail = true; + std::cerr<<"Imprecise result with inverse estimated model: inv("<<groundPoint2dRef<<") = "<<imgPoint<<", but reference is "<<it->first<<" error: "<<imgRes<<" pixels)"<<std::endl; + } + } + + + if(fail) + { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/Testing/Code/UtilitiesAdapters/otbUtilitiesAdaptersTests1.cxx b/Testing/Code/UtilitiesAdapters/otbUtilitiesAdaptersTests1.cxx index 27df316b36..8e1eb88c51 100644 --- a/Testing/Code/UtilitiesAdapters/otbUtilitiesAdaptersTests1.cxx +++ b/Testing/Code/UtilitiesAdapters/otbUtilitiesAdaptersTests1.cxx @@ -27,4 +27,5 @@ void RegisterTests() REGISTER_TEST(otbPlatformPositionComputeBaselineTest); REGISTER_TEST(otbGeometricSarSensorModelAdapterNewTest); REGISTER_TEST(otbGeometricSarSensorModelAdapterTest); + REGISTER_TEST(otbRPCSolverAdapterTest); } -- GitLab