From 67c8dba300fa5ce17f68f367c62c97e21ed40fdc Mon Sep 17 00:00:00 2001 From: Aurelien Bricier <aurelien.bricier@c-s.fr> Date: Fri, 3 Dec 2010 17:18:48 +0100 Subject: [PATCH] ENH: wrapped otbFineRegistration application --- DisparityMap/otbFineRegistration.cxx | 322 +++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 DisparityMap/otbFineRegistration.cxx diff --git a/DisparityMap/otbFineRegistration.cxx b/DisparityMap/otbFineRegistration.cxx new file mode 100644 index 0000000000..bab11675dd --- /dev/null +++ b/DisparityMap/otbFineRegistration.cxx @@ -0,0 +1,322 @@ +/*========================================================================= + + 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 "otbFineRegistration.h" + +#include <iostream> +#include "otbCommandLineArgumentParser.h" + +#include "otbImage.h" +#include "otbImageFileReader.h" +#include "otbStreamingImageFileWriter.h" +#include "otbFineRegistrationImageFilter.h" +#include "otbStandardWriterWatcher.h" +#include "otbVectorImage.h" +#include "otbImageList.h" +#include "otbImageListToVectorImageFilter.h" + +#include "itkTimeProbe.h" +#include "itkFixedArray.h" +#include "itkNormalizedCorrelationImageToImageMetric.h" +#include "itkMeanReciprocalSquareDifferenceImageToImageMetric.h" +#include "itkMeanSquaresImageToImageMetric.h" +#include "itkMattesMutualInformationImageToImageMetric.h" +#include "itkDiscreteGaussianImageFilter.h" +#include "itkAbsImageFilter.h" +#include "itkVectorIndexSelectionCastImageFilter.h" +#include "itkBinaryThresholdImageFilter.h" + +namespace otb +{ + +int FineRegistration::Describe(ApplicationDescriptor* descriptor) +{ + descriptor->SetName("FineRegistration"); + descriptor->SetDescription("Estimate disparity map between two images. Output image contain x offset, y offset and metric value."); + descriptor->AddOption("Reference", "The reference image", + "ref", 1, true, ApplicationDescriptor::InputImage); + descriptor->AddOption("Secondary", "The secondary image", + "sec", 1, true, ApplicationDescriptor::FileName); + descriptor->AddOption("Output", "The output image", + "out", 1, true, ApplicationDescriptor::OutputImage); + descriptor->AddOption("ExplorationRadius","Radius (in pixels) of the exploration window", + "er",2,true, ApplicationDescriptor::Integer); + descriptor->AddOption("MetricRadius","Radius (in pixels) of the metric computation window", + "mr",2,true, ApplicationDescriptor::Integer); + descriptor->AddOption("CoarseOffset","(optional) Coarse offset (in physical space) between the two images. Default is [0,0].", + "co",2,false, ApplicationDescriptor::Real); + descriptor->AddOption("SubSamplingRate","(optional) Generate a result at a coarser resolution with a given sub-sampling rate in each direction (in pixels). Default is no sub-sampling", + "ssr",2,false, ApplicationDescriptor::Real); + descriptor->AddOption("ReferenceGaussianSmoothing","(optional) Perform a gaussian smoothing of the reference image. Parameters are gaussian sigma (in pixels) in each direction. Default is no smoothing.", + "rgs",2,false, ApplicationDescriptor::Real); + descriptor->AddOption("SecondaryGaussianSmoothing","(optional) Perform a gaussian smoothing of the secondary image. Parameters are gaussian sigma (in pixels) in each direction. Default is no smoothing.", + "sgs",2,false, ApplicationDescriptor::Real); + descriptor->AddOption("Metric","(optional) Choose the metric used for block matching. Available metrics are cross-correlation (CC), cross-correlation with subtracted mean (CCSM), mean-square difference (MSD), mean reciprocal square difference (MRSD) and mutual information (MI). Default is cross-correlation", + "m",1,false, ApplicationDescriptor::String); + descriptor->AddOption("SubPixelAccuracy","(optional) Metric extrema location will be refined up to the given accuracy. Default is 0.01", + "spa",1,false, ApplicationDescriptor::Real); + descriptor->AddOption("ValidityMask","(optional) Threshold to obtain a validity mask. Params are lowerThan or greaterThan and a threshold", + "vm",2,false, ApplicationDescriptor::String); + return EXIT_SUCCESS; +} + + +int FineRegistration::Execute(otb::ApplicationOptionsResult* parseResult) +{ + const unsigned int Dimension = 2; + typedef double PixelType; + + typedef itk::FixedArray<PixelType,Dimension> DeformationValueType; + typedef otb::Image< PixelType, Dimension > ImageType; + typedef otb::VectorImage<PixelType,Dimension> VectorImageType; + typedef otb::ImageList<ImageType> ImageListType; + typedef otb::ImageListToVectorImageFilter<ImageListType,VectorImageType> IL2VIFilterType; + typedef otb::Image<DeformationValueType,Dimension> FieldImageType; + typedef otb::ImageFileReader< ImageType > ReaderType; + typedef otb::StreamingImageFileWriter< VectorImageType > WriterType; + typedef otb::FineRegistrationImageFilter<ImageType,ImageType,FieldImageType> RegistrationFilterType; + typedef itk::DiscreteGaussianImageFilter<ImageType,ImageType> GaussianFilterType; + typedef itk::VectorIndexSelectionCastImageFilter<FieldImageType,ImageType> VectorImageToImageFilterType; + typedef itk::AbsImageFilter<ImageType,ImageType> AbsFilterType; + typedef itk::BinaryThresholdImageFilter<ImageType,ImageType> BinaryThresholdImageFilterType; + + // Read reference image + ReaderType::Pointer freader = ReaderType::New(); + freader->SetFileName(parseResult->GetParameterString("Reference")); + + // Read secondary image + ReaderType::Pointer mreader = ReaderType::New(); + mreader->SetFileName(parseResult->GetParameterString("Secondary")); + + // Retrieve main registration parameters + RegistrationFilterType::SizeType radius, sradius; + ImageType::OffsetType ssrate; + sradius[0] = parseResult->GetParameterULong("ExplorationRadius",0); + sradius[1] = parseResult->GetParameterULong("ExplorationRadius",1); + radius[0] = parseResult->GetParameterULong("MetricRadius",0); + radius[1] = parseResult->GetParameterULong("MetricRadius",1); + + double accuracy = 0.01; + if(parseResult->IsOptionPresent("SubPixelAccuracy")) + { + accuracy = parseResult->GetParameterDouble("SubPixelAccuracy"); + } + ssrate.Fill(1); + if(parseResult->IsOptionPresent("SubSamplingRate")) + { + ssrate[0] = parseResult->GetParameterDouble("SubSamplingRate",0); + ssrate[1] = parseResult->GetParameterDouble("SubSamplingRate",1); + } + RegistrationFilterType::SpacingType initialOffset; + initialOffset.Fill(0); + if(parseResult->IsOptionPresent("CoarseOffset")) + { + initialOffset[0] = parseResult->GetParameterDouble("CoarseOffset",0); + initialOffset[1] = parseResult->GetParameterDouble("CoarseOffset",1); + } + + // Display info + std::cout<<"Reference image : "<<freader->GetFileName()<<std::endl; + std::cout<<"Secondary image : "<<mreader->GetFileName()<<std::endl; + + std::cout<<"Exploration radius: "<<sradius<<" (pixels)"<<std::endl; + std::cout<<"Metric radius : "<<radius<<" (pixels)"<<std::endl; + std::cout<<"Sub-sampling rate : "<<ssrate<<" (pixels)"<<std::endl; + std::cout<<"Coarse offset : "<<initialOffset<<" (physical unit)"<<std::endl; + std::cout<<"Accuracy : "<<accuracy<<" (physical unit)"<<std::endl; + + + RegistrationFilterType::Pointer registration = RegistrationFilterType::New(); + registration->SetRadius(radius); + registration->SetSearchRadius(sradius); + registration->SetSubPixelAccuracy(accuracy); + registration->SetGridStep(ssrate); + registration->SetInitialOffset(initialOffset); + + GaussianFilterType::Pointer refGaussianFilter,secGaussianFilter; + + if(parseResult->IsOptionPresent("ReferenceGaussianSmoothing")) + { + refGaussianFilter = GaussianFilterType::New(); + refGaussianFilter->SetInput(freader->GetOutput()); + GaussianFilterType::ArrayType sigma; + sigma[0] = parseResult->GetParameterDouble("ReferenceGaussianSmoothing",0); + sigma[1] = parseResult->GetParameterDouble("ReferenceGaussianSmoothing",1); + refGaussianFilter->SetVariance(sigma); + refGaussianFilter->SetUseImageSpacingOff(); + std::cout<<"Reference image gaussian smoothing on."<<std::endl; + std::cout<<"variance : "<<sigma<<" (pixels)"<<std::endl; + registration->SetFixedInput(refGaussianFilter->GetOutput()); + } + else + { + std::cout<<"Reference image gaussian smoothing off."<<std::endl; + registration->SetFixedInput(freader->GetOutput()); + } + + if(parseResult->IsOptionPresent("SecondaryGaussianSmoothing")) + { + secGaussianFilter = GaussianFilterType::New(); + secGaussianFilter->SetInput(mreader->GetOutput()); + GaussianFilterType::ArrayType sigma; + sigma[0] = parseResult->GetParameterDouble("SecondaryGaussianSmoothing",0); + sigma[1] = parseResult->GetParameterDouble("SecondaryGaussianSmoothing",1); + secGaussianFilter->SetVariance(sigma); + secGaussianFilter->SetUseImageSpacingOff(); + std::cout<<"Secondary image gaussian smoothing on."<<std::endl; + std::cout<<"variance : "<<sigma<<" (pixels)"<<std::endl; + registration->SetMovingInput(secGaussianFilter->GetOutput()); + } + else + { + std::cout<<"Secondary image gaussian smoothing off."<<std::endl; + registration->SetMovingInput(mreader->GetOutput()); + } + + // Retrieve metric name + std::string metricId = "CC"; + if(parseResult->IsOptionPresent("Metric")) + { + metricId = parseResult->GetParameterString("Metric"); + } + + if(metricId == "CC") + { + std::cout<<"Metric : Cross-correlation"<<std::endl; + typedef itk::NormalizedCorrelationImageToImageMetric<ImageType,ImageType> NCCType; + NCCType::Pointer metricPtr = NCCType::New(); + metricPtr->SubtractMeanOff(); + registration->SetMetric(metricPtr); + registration->MinimizeOn(); + } + else if(metricId == "CCSM") + { + std::cout<<"Metric : Cross-correlation (mean subtracted)"<<std::endl; + typedef itk::NormalizedCorrelationImageToImageMetric<ImageType,ImageType> NCCType; + NCCType::Pointer metricPtr = NCCType::New(); + metricPtr->SubtractMeanOn(); + registration->SetMetric(metricPtr); + registration->MinimizeOn(); + } + else if(metricId == "MSD") + { + std::cout<<"Metric : Mean square difference"<<std::endl; + typedef itk::MeanSquaresImageToImageMetric<ImageType,ImageType> MeanSquareType; + MeanSquareType::Pointer metricPtr = MeanSquareType::New(); + registration->SetMetric(metricPtr); + registration->MinimizeOn(); + } + else if(metricId == "MRSD") + { + std::cout<<"Metric : Mean reciprocal square difference"<<std::endl; + typedef itk::MeanReciprocalSquareDifferenceImageToImageMetric<ImageType,ImageType> MRSDType; + MRSDType::Pointer metricPtr = MRSDType::New(); + registration->SetMetric(metricPtr); + registration->MinimizeOff(); + } + else if(metricId == "MI") + { + std::cout<<"Metric : Mutual information"<<std::endl; + typedef itk::MattesMutualInformationImageToImageMetric<ImageType,ImageType> MIType; + MIType::Pointer metricPtr = MIType::New(); + registration->SetMetric(metricPtr); + registration->MinimizeOn(); + } + else + { + std::cerr<<"Metric "<<metricId<<" not recognized."<<std::endl; + std::cerr<<"Possible choices are: CC, CCMS, MSD, MRSD, MI"<<std::endl; + return EXIT_FAILURE; + } + VectorImageToImageFilterType::Pointer xExtractor = VectorImageToImageFilterType::New(); + xExtractor->SetInput(registration->GetOutputDeformationField()); + xExtractor->SetIndex(0); + + VectorImageToImageFilterType::Pointer yExtractor = VectorImageToImageFilterType::New(); + yExtractor->SetInput(registration->GetOutputDeformationField()); + yExtractor->SetIndex(1); + + ImageListType::Pointer il = ImageListType::New(); + il->PushBack(xExtractor->GetOutput()); + il->PushBack(yExtractor->GetOutput()); + + AbsFilterType::Pointer absFilter; + + // Invert correlation to get classical rendering + if(metricId == "CC" || metricId == "CCSM") + { + absFilter = AbsFilterType::New(); + absFilter->SetInput(registration->GetOutput()); + il->PushBack(absFilter->GetOutput()); + } + else + { + il->PushBack(registration->GetOutput()); + } + + BinaryThresholdImageFilterType::Pointer threshold; + if(parseResult->IsOptionPresent("ValidityMask")) + { + threshold = BinaryThresholdImageFilterType::New(); + + if(metricId == "CC" || metricId == "CCSM") + { + threshold->SetInput(absFilter->GetOutput()); + } + else + { + threshold->SetInput(registration->GetOutput()); + } + if(parseResult->GetParameterString("ValidityMask",0)=="greaterThan") + { + threshold->SetLowerThreshold(parseResult->GetParameterDouble("ValidityMask",1)); + } + else if(parseResult->GetParameterString("ValidityMask",0)=="lowerThan") + { + threshold->SetUpperThreshold(parseResult->GetParameterDouble("ValidityMask",1)); + } + else + { + std::cerr<<"Arguments of --ValidityMask option should begin with lowerThan or greaterThan"<<std::endl; + return EXIT_FAILURE; + } + + std::cout<<"A validity mask will be produced as the 4th band (valid pixels = 1.0, not valid = 0.0)."<<std::endl; + std::cout<<"Pixels are considered valid if metric is "<<parseResult->GetParameterString("ValidityMask",0)<<" "; + std::cout<<parseResult->GetParameterDouble("ValidityMask",1)<<std::endl; + + threshold->SetInsideValue(1.0); + threshold->SetOutsideValue(0.); + il->PushBack(threshold->GetOutput()); + } + + IL2VIFilterType::Pointer il2vi = IL2VIFilterType::New(); + il2vi->SetInput(il); + + WriterType::Pointer writer = WriterType::New(); + writer->SetFileName(parseResult->GetOutputImage()); + writer->SetInput(il2vi->GetOutput()); + + std::cout<<std::endl; + otb::StandardWriterWatcher watcher(writer,registration,"Fine Registration"); + + writer->Update(); + + return EXIT_SUCCESS; +} + +} -- GitLab