diff --git a/Modules/Adapters/OSSIMAdapters/include/otbSarSensorModelAdapter.h b/Modules/Adapters/OSSIMAdapters/include/otbSarSensorModelAdapter.h index 3c1321e04a0de9af05693b0c78a7252be5b33303..0e63717b7f1a080563235e9edc4e8442f2d5dcc3 100644 --- a/Modules/Adapters/OSSIMAdapters/include/otbSarSensorModelAdapter.h +++ b/Modules/Adapters/OSSIMAdapters/include/otbSarSensorModelAdapter.h @@ -82,7 +82,9 @@ public: bool IsValidSensorModel() const; /** Deburst metadata if possible and return lines to keep in image file */ - bool Deburst(std::vector<std::pair<unsigned long, unsigned long> > & lines); + bool Deburst(std::vector<std::pair<unsigned long, unsigned long> > & lines, + std::pair<unsigned long,unsigned long> & samples, bool onlyValidSample=false); + /** Transform world point (lat,lon,hgt) to input image point (col,row) and YZ frame */ diff --git a/Modules/Adapters/OSSIMAdapters/src/otbSarSensorModelAdapter.cxx b/Modules/Adapters/OSSIMAdapters/src/otbSarSensorModelAdapter.cxx index 5e351bea4b17591fcb9a55966878836dabf46c2e..8e2dd04c56017d1d9da73819cc4f578cd88648b4 100644 --- a/Modules/Adapters/OSSIMAdapters/src/otbSarSensorModelAdapter.cxx +++ b/Modules/Adapters/OSSIMAdapters/src/otbSarSensorModelAdapter.cxx @@ -89,11 +89,13 @@ bool SarSensorModelAdapter::IsValidSensorModel() const return m_SensorModel.get() != nullptr; } -bool SarSensorModelAdapter::Deburst(std::vector<std::pair<unsigned long, unsigned long> > & lines) +bool SarSensorModelAdapter::Deburst(std::vector<std::pair<unsigned long, unsigned long> > & lines, + std::pair<unsigned long,unsigned long> & samples, + bool onlyValidSample) { if(m_SensorModel.get() != nullptr) { - return m_SensorModel->deburst(lines); + return m_SensorModel->deburst(lines, samples, onlyValidSample); } return false; diff --git a/Modules/Applications/AppSARCalibration/app/otbSARDeburst.cxx b/Modules/Applications/AppSARCalibration/app/otbSARDeburst.cxx index 0858e7e7146997530a067cb32929850e749e06f9..849f91de97313dabacb4533fcd18354c23c7e567 100644 --- a/Modules/Applications/AppSARCalibration/app/otbSARDeburst.cxx +++ b/Modules/Applications/AppSARCalibration/app/otbSARDeburst.cxx @@ -60,7 +60,10 @@ private: "Note that the output sensor model is updated accordingly. This deburst" " operation is the perfect preprocessing step to orthorectify S1 IW SLC" " product with OTB [2] without suffering from artifacts caused by" - " bursts separation."); + " bursts separation.\n" + + "Two modes are available for the output image : with all samples and" + "with only valid samples "); SetDocLimitations("Only Sentinel1 IW SLC products are supported for now. Processing of" " other Sentinel1 modes or TerrasarX images will result in no changes in" @@ -80,6 +83,9 @@ private: AddParameter(ParameterType_OutputImage, "out", "Output Image"); SetParameterDescription("out", "Deburst image, with updated geom file that can be further used by Orthorectification application. If the input image is a raw Sentinel1 product, uint16 output type should be used (encoding of S1 product). Otherwise, output type should match type of input image."); + AddParameter(ParameterType_Bool, "onlyvalidsamples", "Select the modes for output image"); + SetParameterDescription("onlyvalidsamples", "If true, the selected mode is with only valid samples."); + AddRAMParameter(); SetDocExampleParameterValue("in","s1_iw_slc.tif"); @@ -100,6 +106,12 @@ private: m_DeburstFilter = DeburstFilterType::New(); m_DeburstFilter->SetInput(in); + if (IsParameterEnabled("onlyvalidsamples")) + { + m_DeburstFilter->SetOnlyValidSample(true); + } + + // Set the output image SetParameterOutputImage("out", m_DeburstFilter->GetOutput()); } diff --git a/Modules/Radiometry/SARCalibration/include/otbSarDeburstImageFilter.h b/Modules/Radiometry/SARCalibration/include/otbSarDeburstImageFilter.h index 6b771c9dbfa6052d49104cc93303a85d3572a800..5489259e10fd06dfdf67a5a0620c9f174a6c0527 100644 --- a/Modules/Radiometry/SARCalibration/include/otbSarDeburstImageFilter.h +++ b/Modules/Radiometry/SARCalibration/include/otbSarDeburstImageFilter.h @@ -62,8 +62,11 @@ public: typedef typename ImageType::RegionType RegionType; typedef typename ImageType::PointType PointType; - typedef std::pair<unsigned long, unsigned long> LinesRecordType; - typedef std::vector<LinesRecordType> LinesRecordVectorType; + typedef std::pair<unsigned long, unsigned long> RecordType; + typedef std::vector<RecordType> LinesRecordVectorType; + + // Setter + itkSetMacro(OnlyValidSample, bool); protected: // Constructor @@ -81,6 +84,9 @@ protected: // Actual processing virtual void ThreadedGenerateData(const RegionType& outputRegionForThread, itk::ThreadIdType threadId) override; + void ThreadedGenerateDataWithAllSamples(const RegionType& outputRegionForThread, itk::ThreadIdType threadId); + void ThreadedGenerateDataWithOnlyValidSamples(const RegionType& outputRegionForThread, itk::ThreadIdType threadId); + RegionType OutputRegionToInputRegion(const RegionType& outputRegion) const; private: @@ -89,7 +95,12 @@ private: // Vector of line records LinesRecordVectorType m_LinesRecord; - + + // Pair for sample valid selection + RecordType m_SamplesRecord; + + bool m_OnlyValidSample; + }; } // End namespace otb diff --git a/Modules/Radiometry/SARCalibration/include/otbSarDeburstImageFilter.hxx b/Modules/Radiometry/SARCalibration/include/otbSarDeburstImageFilter.hxx index d9de620dba6c6643f22426cb45f4ca74bed45232..e15425f09236a28391fdb72105a316d6caa09c3a 100644 --- a/Modules/Radiometry/SARCalibration/include/otbSarDeburstImageFilter.hxx +++ b/Modules/Radiometry/SARCalibration/include/otbSarDeburstImageFilter.hxx @@ -27,12 +27,14 @@ #include "otbImageKeywordlist.h" #include "itkImageScanlineIterator.h" #include "itkImageScanlineConstIterator.h" +#include "itkImageRegionIterator.h" +#include "itkImageRegionConstIterator.h" namespace otb { // Constructor template <class TImage> SarDeburstImageFilter<TImage>::SarDeburstImageFilter() - : m_LinesRecord() + : m_LinesRecord(), m_SamplesRecord(), m_OnlyValidSample(false) {} // Needs to be re-implemented since size of output is modified @@ -66,7 +68,7 @@ SarDeburstImageFilter<TImage>::GenerateOutputInformation() itkExceptionMacro(<<"Input image does not contain a valid SAR sensor model."); // Try to call the deburst function - bool deburstOk = sarSensorModel->Deburst(m_LinesRecord); + bool deburstOk = sarSensorModel->Deburst(m_LinesRecord, m_SamplesRecord, m_OnlyValidSample); if(!deburstOk || m_LinesRecord.empty()) itkExceptionMacro(<<"Could not deburst SAR sensor model from input image"); @@ -83,9 +85,6 @@ SarDeburstImageFilter<TImage>::GenerateOutputInformation() if(!saveOk) itkExceptionMacro(<<"Could not export deburst SAR sensor model to keyword list"); - // Set new keyword list to output image - outputPtr->SetImageKeywordList(newKwl); - // Now, filter the LinesRecord so as to account for possible // extracts on input image long firstInputLine = static_cast<long>(origin[1]-0.5); @@ -111,7 +110,7 @@ SarDeburstImageFilter<TImage>::GenerateOutputInformation() // If record is inside input image region if((long)it->first<=lastInputLine && (long)it->second>=firstInputLine) { - LinesRecordType filteredRecord = *it; + RecordType filteredRecord = *it; filteredRecord.first = std::max((long)filteredRecord.first,firstInputLine); filteredRecord.second = std::min((long)filteredRecord.second,lastInputLine); filteredRecords.push_back(filteredRecord); @@ -134,10 +133,21 @@ SarDeburstImageFilter<TImage>::GenerateOutputInformation() deburstSize[1]+=it->second-it->first+1; } + if (m_OnlyValidSample) + { + deburstSize[0] = m_SamplesRecord.second - m_SamplesRecord.first + 1; + } + // Set largest possible region typename ImageType::RegionType outputLargestPossibleRegion = largestPossibleRegion; largestPossibleRegion.SetSize(deburstSize); outputPtr->SetLargestPossibleRegion(largestPossibleRegion); + + newKwl.AddKey("support_data_number_samples", std::to_string(deburstSize[0])); + newKwl.AddKey("support_data_number_lines", std::to_string(deburstSize[1])); + + // Set new keyword list to output image + outputPtr->SetImageKeywordList(newKwl); } template<class TImage> @@ -172,6 +182,12 @@ SarDeburstImageFilter<TImage>::OutputRegionToInputRegion(const RegionType& outpu typename RegionType::SizeType size = inputRegion.GetSize(); typename RegionType::IndexType index = inputRegion.GetIndex(); + if (m_OnlyValidSample) + { + index[0]+= m_SamplesRecord.first; + //size[0]+= m_SamplesRecord.first; + } + index[1]=inputUpperLeftLine; size[1]=inputLowerLeftLine-inputUpperLeftLine+1; @@ -194,7 +210,26 @@ template <class TImage> void SarDeburstImageFilter<TImage>::GenerateInputRequest } // Actual processing -template <class TImage> void SarDeburstImageFilter<TImage>::ThreadedGenerateData(const RegionType& outputRegionForThread, itk::ThreadIdType itkNotUsed(threadId)) +template <class TImage> +void +SarDeburstImageFilter<TImage>::ThreadedGenerateData(const RegionType& outputRegionForThread, + itk::ThreadIdType itkNotUsed(threadId)) +{ + if (m_OnlyValidSample) + { + this->ThreadedGenerateDataWithOnlyValidSamples(outputRegionForThread, 0); + } + else + { + this->ThreadedGenerateDataWithAllSamples(outputRegionForThread, 0); + } +} + +// Actual processing with all samples +template <class TImage> +void +SarDeburstImageFilter<TImage>::ThreadedGenerateDataWithAllSamples(const RegionType& outputRegionForThread, + itk::ThreadIdType itkNotUsed(threadId)) { // Compute corresponding input region RegionType inputRegionForThread = OutputRegionToInputRegion(outputRegionForThread); @@ -239,6 +274,59 @@ template <class TImage> void SarDeburstImageFilter<TImage>::ThreadedGenerateData } } +// Actual processing with only valid samples +template <class TImage> +void +SarDeburstImageFilter<TImage>::ThreadedGenerateDataWithOnlyValidSamples(const RegionType& outputRegionForThread, + itk::ThreadIdType itkNotUsed(threadId)) +{ + // Compute corresponding input region + RegionType inputRegionForThread = OutputRegionToInputRegion(outputRegionForThread); + + itk::ImageRegionConstIterator<ImageType> inputIt(this->GetInput(),inputRegionForThread); + itk::ImageRegionIterator<ImageType> outputIt(this->GetOutput(),outputRegionForThread); + + inputIt.GoToBegin(); + outputIt.GoToBegin(); + + while(!inputIt.IsAtEnd()&&!outputIt.IsAtEnd()) + { + typename ImageType::IndexType currentInputIndex = inputIt.GetIndex(); + PointType currentInputPoint; + this->GetInput()->TransformIndexToPhysicalPoint(currentInputIndex,currentInputPoint); + + bool lineToKeep = false; + bool sampleToKeep = false; + + for(LinesRecordVectorType::const_iterator it = m_LinesRecord.begin(); + it!=m_LinesRecord.end();++it) + { + if(currentInputPoint[1]-0.5>=it->first && currentInputPoint[1]-0.5<=it->second) + { + lineToKeep = true; + break; + } + } + + if (currentInputIndex[0] >= static_cast<int>(m_SamplesRecord.first) && + currentInputIndex[0] <= static_cast<int>(m_SamplesRecord.second)) + { + sampleToKeep = true; + } + + if(lineToKeep && sampleToKeep) + { + outputIt.Set(inputIt.Get()); + + ++outputIt; + } + + + ++inputIt; + } +} + + } // End namespace otb #endif diff --git a/Modules/ThirdParty/OssimPlugins/include/ossim/ossimSarSensorModel.h b/Modules/ThirdParty/OssimPlugins/include/ossim/ossimSarSensorModel.h index 7bbc7f2df4e4939f9b66d123886abce9bef52da8..c3f6731c11f6c59effec84e1c101a476cc33c752 100644 --- a/Modules/ThirdParty/OssimPlugins/include/ossim/ossimSarSensorModel.h +++ b/Modules/ThirdParty/OssimPlugins/include/ossim/ossimSarSensorModel.h @@ -288,12 +288,19 @@ public: * Note that the deburst operation has no effect if theBurstRecords * contains a single burst. Otherwise it will merge burst together * into a single burst, and update GCPs accordingly. + * Two modes are available for the output image : with all samples and + * with only valid samples. A pair of samples specifies first and last samples * \return true if the deburst operation succeeded. No changes is * made to the object if the operation fails. * \param lines A container for the lines ranges to keep in the * deburst image. + * \param samples A container for the samples to keep in the + * deburst image. + * \param onlyValidSample If true, the selected mode is with only valid sample. */ - bool deburst(std::vector<std::pair<unsigned long,unsigned long> >& lines); + bool deburst(std::vector<std::pair<unsigned long,unsigned long> >& lines, + std::pair<unsigned long,unsigned long> & samples, bool onlyValidSample=false); + /** * This is a helper function to convert image line to deburst image @@ -453,6 +460,11 @@ protected: double theRangeTimeOffset; // Offset in seconds, computed bool theRightLookingFlag; + TimeType theFirstLineTime; + TimeType theLastLineTime; + + bool redaptMedataAfterDeburst; + static const double C; static const unsigned int thePluginVersion; // version of the SarSensorModel plugin diff --git a/Modules/ThirdParty/OssimPlugins/src/ossim/ossimSarSensorModel.cpp b/Modules/ThirdParty/OssimPlugins/src/ossim/ossimSarSensorModel.cpp index 31c046fe6baed47cf66876f07021e43d2c7a5fd5..505bc70c2c1bd3bf0f6539be0949f39953d09f9a 100644 --- a/Modules/ThirdParty/OssimPlugins/src/ossim/ossimSarSensorModel.cpp +++ b/Modules/ThirdParty/OssimPlugins/src/ossim/ossimSarSensorModel.cpp @@ -139,7 +139,8 @@ namespace ossimplugins theBistaticCorrectionNeeded(false), theAzimuthTimeOffset(seconds(0)), theRangeTimeOffset(0.), - theRightLookingFlag(true) + theRightLookingFlag(true), + redaptMedataAfterDeburst(false) {} ossimSarSensorModel::GCPRecordType const& @@ -1359,6 +1360,14 @@ bool ossimSarSensorModel::worldToAzimuthRangeTime(const ossimGpt& worldPt, TimeT add(kwl, HEADER_PREFIX, "version", thePluginVersion); + if (redaptMedataAfterDeburst) + { + add(kwl, SUPPORT_DATA_PREFIX, "first_line_time", theFirstLineTime); + add(kwl, SUPPORT_DATA_PREFIX, "last_line_time", theLastLineTime); + add(kwl, HEADER_PREFIX, "first_line_time", theFirstLineTime); + add(kwl, HEADER_PREFIX, "last_line_time", theLastLineTime); + } + return ossimSensorModel::saveState(kwl, prefix); } @@ -1425,7 +1434,8 @@ bool ossimSarSensorModel::worldToAzimuthRangeTime(const ossimGpt& worldPt, TimeT return false; } -bool ossimSarSensorModel::deburst(std::vector<std::pair<unsigned long, unsigned long> >& lines) +bool ossimSarSensorModel::deburst(std::vector<std::pair<unsigned long, unsigned long> >& lines, + std::pair<unsigned long,unsigned long> & samples, bool onlyValidSample) { if(theBurstRecords.empty()) return false; @@ -1452,6 +1462,8 @@ bool ossimSarSensorModel::deburst(std::vector<std::pair<unsigned long, unsigned TimeType deburstAzimuthStartTime = it->azimuthStartTime; unsigned long deburstEndLine = 0; + + samples = std::make_pair(it->startSample, it->endSample); for(; next!= itend ;++it,++next) { @@ -1466,17 +1478,44 @@ bool ossimSarSensorModel::deburst(std::vector<std::pair<unsigned long, unsigned unsigned long currentStop = it->endLine-halfLineOverlapEnd; - deburstEndLine+=currentStop-currentStart; + deburstEndLine+= currentStop - currentStart + 1; // +1 because currentStart/Stop are both valids + lines.push_back(std::make_pair(currentStart,currentStop)); - currentStart = next->startLine+halfLineOverlapBegin; + currentStart = next->startLine+halfLineOverlapBegin; + + if (onlyValidSample) + { + // Find the first and last valid sampleburst + if (it->startSample > samples.first) + { + samples.first = it->startSample; + } + if (it->endSample < samples.second) + { + samples.second = it->endSample; + } + } } TimeType deburstAzimuthStopTime = it->azimuthStopTime; deburstEndLine+=it->endLine-currentStart; lines.push_back(std::make_pair(currentStart,it->endLine)); + + if (onlyValidSample) + { + if (it->startSample > samples.first) + { + samples.first = it->startSample; + } + if (it->endSample < samples.second) + { + samples.second = it->endSample; + } + } + // Now, update other metadata accordingly @@ -1489,6 +1528,13 @@ bool ossimSarSensorModel::deburst(std::vector<std::pair<unsigned long, unsigned deburstBurst.azimuthStartTime = deburstAzimuthStartTime; deburstBurst.endLine = deburstEndLine; deburstBurst.azimuthStopTime = deburstAzimuthStopTime; + + if (onlyValidSample) + { + deburstBurst.startSample = 0; + deburstBurst.endSample = samples.second - samples.first; + } + theBurstRecords.push_back(deburstBurst); @@ -1501,21 +1547,50 @@ bool ossimSarSensorModel::deburst(std::vector<std::pair<unsigned long, unsigned unsigned long newLine=0; unsigned long gcpLine = std::floor(currentGCP.imPt.y+0.5); + unsigned long gcpSample = std::floor(currentGCP.imPt.x+0.5); + // Be careful about fractional part of GCPs - double fractional = currentGCP.imPt.y - gcpLine; - - bool deburstOk = imageLineToDeburstLine(lines,gcpLine,newLine); + double fractionalLines = currentGCP.imPt.y - gcpLine; + double fractionalSamples = currentGCP.imPt.x - gcpSample; - if(deburstOk) + bool linesOk = imageLineToDeburstLine(lines,gcpLine,newLine); + + // Gcp into valid samples + bool samplesOk = true; + unsigned long newSample = gcpSample; + + if (onlyValidSample) + { + samplesOk = false; + if (gcpSample >= samples.first && gcpSample <= samples.second) + { + samplesOk = true; + newSample -= samples.first; // Offset with first valid sample + } + } + + if(linesOk && samplesOk) { - currentGCP.imPt.y = newLine+fractional; - deburstGCPs.push_back(currentGCP); + currentGCP.imPt.y = newLine + fractionalLines; + currentGCP.imPt.x = newSample + fractionalSamples; + + deburstGCPs.push_back(currentGCP); } } theGCPRecords.swap(deburstGCPs); + + // Adapt general metadata : theNearRangeTime, first_time_line, last_time_line + redaptMedataAfterDeburst = false; + theFirstLineTime = deburstBurst.azimuthStartTime; + theLastLineTime = deburstBurst.azimuthStopTime; + + if (onlyValidSample) + theNearRangeTime += samples.first*(1/theRangeSamplingRate); + + return true; }