From b87bb90a79579c04f14cb0cc128dc708929e4403 Mon Sep 17 00:00:00 2001 From: Julien Michel <julien.michel@orfeo-toolbox.org> Date: Thu, 2 Sep 2010 17:32:41 +0200 Subject: [PATCH] ENH: Adding a disparity map estimation command line tool --- DisparityMap/otbFineRegistration.cxx | 320 +++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100644 DisparityMap/otbFineRegistration.cxx diff --git a/DisparityMap/otbFineRegistration.cxx b/DisparityMap/otbFineRegistration.cxx new file mode 100644 index 0000000000..83cf6e1655 --- /dev/null +++ b/DisparityMap/otbFineRegistration.cxx @@ -0,0 +1,320 @@ +/*========================================================================= + + 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 "otbImage.h" +#include "otbImageFileReader.h" +#include "otbStreamingImageFileWriter.h" +#include "otbFineRegistrationImageFilter.h" +#include "otbStandardWriterWatcher.h" +#include "otbCommandLineArgumentParser.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" + +int main(int argc, char * argv[]) +{ + // Parse command line parameters + typedef otb::CommandLineArgumentParser ParserType; + ParserType::Pointer parser = ParserType::New(); + + parser->SetProgramDescription("Estimate disparity map between two images. Output image contain x offset, y offset and metric value."); + parser->AddOption("--Reference","The reference image","-ref", 1,true); + parser->AddOption("--Secondary","The secondary image","-sec", 1,true); + parser->AddOutputImage(); + parser->AddOption("--ExplorationRadius","Radius (in pixels) of the exploration window","-er",2,true); + parser->AddOption("--MetricRadius","Radius (in pixels) of the metric computation window","-mr",2,true); + parser->AddOption("--CoarseOffset","(optional) Coarse offset (in physical space) between the two images. Default is [0,0].","-co",2,false); + parser->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); + parser->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); + parser->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); + parser->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); + parser->AddOption("--SubPixelAccuracy","(optional) Metric extrema location will be refined up to the given accuracy. Default is 0.01","-spa",1,false); + parser->AddOption("--ValidityMask","(optional) Threshold to obtain a validity mask. Params are lowerThan or greaterThan and a threshold","-vm",2,true); + + typedef otb::CommandLineArgumentParseResult ParserResultType; + ParserResultType::Pointer parseResult = ParserResultType::New(); + + try + { + parser->ParseCommandLine(argc,argv,parseResult); + } + catch ( itk::ExceptionObject & err ) + { + std::string descriptionException = err.GetDescription(); + if (descriptionException.find("ParseCommandLine(): Help Parser") != std::string::npos) + { + return EXIT_SUCCESS; + } + if (descriptionException.find("ParseCommandLine(): Version Parser") != std::string::npos) + { + return EXIT_SUCCESS; + } + return EXIT_FAILURE; + } + + 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