diff --git a/CMake/OTBSetStandardCompilerFlags.cmake b/CMake/OTBSetStandardCompilerFlags.cmake index d3b11b8a1f2a047bea12d451773bf2b7d53e5f7f..249b396d4219f09a94acc75d3b72bce1260fdb7b 100644 --- a/CMake/OTBSetStandardCompilerFlags.cmake +++ b/CMake/OTBSetStandardCompilerFlags.cmake @@ -315,3 +315,18 @@ set_debug_flags() #----------------------------------------------------------------------------- #Check the set of platform flags the compiler supports check_compiler_platform_flags() + + +# Usage: set_linker_stack_size_flag(otbApplicationLauncherCommandLine 10000000) +# The above macro call will set LINK_FLAGS executable target named +# otbApplicationLauncherCommandLine to 10Mbytes +macro(set_linker_stack_size_flag exe_target requested_stack_size) + # Since CMake 2.8.11, the size of the stack is not modified by CMake on + # windows platform, it uses the default size: with visual compiler it is 1Mbyte + # which is to lower for us (thanks to 6S code). + if(MSVC) + set_target_properties(${exe_target} PROPERTIES LINK_FLAGS "/STACK:${requested_stack_size}") + elseif(MINGW) + set_target_properties(${exe_target} PROPERTIES LINK_FLAGS "-Wl,--stack,${requested_stack_size}") + endif() +endmacro() \ No newline at end of file diff --git a/Documentation/Cookbook/rst/recipes/pbclassif.rst b/Documentation/Cookbook/rst/recipes/pbclassif.rst index e3d6755b25661fc9c0d8eef93d0bcf8e30ba09f1..a8e79cc7a051c1eccd22525443179ef28cd83a14 100644 --- a/Documentation/Cookbook/rst/recipes/pbclassif.rst +++ b/Documentation/Cookbook/rst/recipes/pbclassif.rst @@ -105,6 +105,12 @@ There are several strategies to compute those sampling rates: * **Smallest class strategy:** The class with the least number of samples will be fully sampled. All other classes will be sampled with the same number of samples. +* **Percent strategy:** Each class will be sampled with a user-defined + percentage (same value for all classes) of samples available in this + class. +* **Total strategy:** A global number of samples to generate is + divided proportionally among each class (classes proportions are + enforced). * **Take all strategy:** Take all the available samples * **By class strategy:** Set a target number of samples for each class. The number of samples for each class is read from a CSV file. @@ -229,9 +235,9 @@ image. class required) - *Mode = proportional:* For each image :math:`i` and each class :math:`c`, - :math:`N_i( c ) = M * T_i( c ) / sum_k( T_k(c) )` + :math:`N_i( c ) = \frac{M * T_i( c )}{sum_k( T_k(c)}` - *Mode = equal:* For each image :math:`i` and each class :math:`c`, - :math:`N_i( c ) = M / L` + :math:`N_i( c ) = \frac{M}{L}` - *Mode = custom:* For each image :math:`i` and each class :math:`c`, :math:`N_i( c ) = M_i` where :math:`M_i` is the custom requested number of samples for image i @@ -240,13 +246,31 @@ image. class c) - *Mode = proportional:* For each image :math:`i` and each class :math:`c`, - :math:`N_i( c ) = M(c) * T_i( c ) / sum_k( T_k(c) )` + :math:`N_i( c ) = M(c) * \frac{T_i( c )}{sum_k( T_k(c))}` - *Mode = equal:* For each image :math:`i` and each class :math:`c`, - :math:`N_i( c ) = M(c) / L` + :math:`N_i( c ) = \frac{M(c)}{L}` - *Mode = custom:* For each image :math:`i` and each class :math:`c`, :math:`Ni( c ) = M_i(c)` where :math:`M_i(c)` is the custom requested number of samples for each image :math:`i` and each class :math:`c` - + +* **Strategy = percent** + + - *Mode = proportional:* For each image :math:`i` and each class :math:`c`, + :math:`N_i( c ) = p * T_i(c)` where :math:`p` is the user-defined percentage + - *Mode = equal:* For each image :math:`i` and each class :math:`c`, + :math:`N_i( c ) = p * \frac{sum_k(Tk(c))}{L}` where :math:`p` is the user-defined percentage + - *Mode = custom:* For each image :math:`i` and each class :math:`c`, + :math:`Ni( c ) = p(i) * T_i(c)` where :math:`p(i)` is the user-defined percentage for image :math:`i` + +* **Strategy = total** + + - *Mode = proportional:* For each image :math:`i` and each class :math:`c`, + :math:`N_i( c ) = total * (\frac{sum_k(Ti(k))}{sum_kl(Tl(k))}) * (\frac{Ti(c)}{sum_k(Ti(k))})` where :math:`total` is the total number of samples specified + - *Mode = equal:* For each image :math:`i` and each class :math:`c`, + :math:`N_i( c ) = (total / L) * (\frac{Ti(c)}{sum_k(Ti(k))})` where :math:`total` is the total number of samples specified + - *Mode = custom:* For each image :math:`i` and each class :math:`c`, + :math:`Ni( c ) = total(i) * (\frac{Ti(c)}{sum_k(Ti(k))})` where :math:`total(i)` is the total number of samples specified for image :math:`i` + * **Strategy = smallest class** - *Mode = proportional:* the smallest class is computed globally, then this smallest size is used for the strategy constant+proportional @@ -698,6 +722,7 @@ used to predict output values. The applications to do that are and . .. figure:: ../Art/MonteverdiImages/classification_chain_inputimage.jpg .. figure:: ../Art/MonteverdiImages/classification_chain_fancyclassif_CMR_input.png .. figure:: ../Art/MonteverdiImages/classification_chain_fancyclassif_CMR_3.png + Figure 6: From left to right: Original image, fancy colored classified image and regularized classification map with radius equal to 3 pixels. The input data set for training must have the following structure : diff --git a/Modules/Applications/AppClassification/app/otbClassificationMapRegularization.cxx b/Modules/Applications/AppClassification/app/otbClassificationMapRegularization.cxx index 4652d5556255407d811c591fb0729add8e462b90..1219414c9821766e8204898df8edba1383b41250 100644 --- a/Modules/Applications/AppClassification/app/otbClassificationMapRegularization.cxx +++ b/Modules/Applications/AppClassification/app/otbClassificationMapRegularization.cxx @@ -99,6 +99,13 @@ private: SetParameterDescription("ip.undecidedlabel", "Label for the Undecided class. By default, 'ip.undecidedlabel = 0'."); SetDefaultParameterInt("ip.undecidedlabel", 0.0); + AddParameter(ParameterType_Empty, "ip.onlyisolatedpixels", "Process isolated pixels only"); + SetParameterDescription("ip.onlyisolatedpixels", "Only pixels whose label is unique in the neighbordhood will be processed. By default, 'ip.onlyisolatedpixels = false'."); + + AddParameter(ParameterType_Int, "ip.isolatedthreshold", "Threshold for isolated pixels"); + SetParameterDescription("ip.isolatedthreshold", "Maximum number of neighbours with the same label as the center pixel to consider that it is an isolated pixel. By default, 'ip.isolatedthreshold = 1'."); + SetDefaultParameterInt("ip.isolatedthreshold", 1); + AddRAMParameter(); @@ -107,6 +114,7 @@ private: SetDocExampleParameterValue("io.out", "clLabeledImageQB123_1_CMR_r2_nodl_10_undl_7.tif"); SetDocExampleParameterValue("ip.radius", "2"); SetDocExampleParameterValue("ip.suvbool", "true"); + SetDocExampleParameterValue("ip.onlyisolatedpixels", "true"); SetDocExampleParameterValue("ip.nodatalabel", "10"); SetDocExampleParameterValue("ip.undecidedlabel", "7"); } @@ -149,6 +157,17 @@ private: m_NeighMajVotingFilter->SetKeepOriginalLabelBool(true); } + // Process isolated pixels only + if (IsParameterEnabled("ip.onlyisolatedpixels")) + { + m_NeighMajVotingFilter->SetOnlyIsolatedPixels(true); + m_NeighMajVotingFilter->SetIsolatedThreshold(GetParameterInt("ip.isolatedthreshold")); + } + else + { + m_NeighMajVotingFilter->SetOnlyIsolatedPixels(false); + } + /** REGULARIZATION OF CLASSIFICATION */ SetParameterOutputImage<IOLabelImageType>("io.out", m_NeighMajVotingFilter->GetOutput()); diff --git a/Modules/Applications/AppClassification/app/otbMultiImageSamplingRate.cxx b/Modules/Applications/AppClassification/app/otbMultiImageSamplingRate.cxx index 72d2af0ddd2759cb1374193e81d7b7311083406f..826af62fd982777cabdc7be8944a1a9fb9c26314 100644 --- a/Modules/Applications/AppClassification/app/otbMultiImageSamplingRate.cxx +++ b/Modules/Applications/AppClassification/app/otbMultiImageSamplingRate.cxx @@ -94,6 +94,16 @@ private: " - if mim = proportional, then Ni( c ) = M(c) * Ti( c ) / sum_k( Tk(c) )\n\n" " - if mim = equal , then Ni( c ) = M(c) / L\n\n" " - if mim = custom , then Ni( c ) = Mi(c) where Mi(c) is the custom requested number of samples for image i and class c\n\n" + " * strategy = percent :" + " For each image i and each class c:\n\n" + " - if mim = proportional, then Ni( c ) = p * Ti( c ) where p is the global percentage of samples\n\n" + " - if mim = equal , then Ni( c ) = p * sum_k(Tk(c)]/L where p is the global percentage of samples\n\n" + " - if mim = custom , then Ni( c ) = p(i) * Ti(c) where p(i) is the percentage of samples for image i. c\n\n" + " * strategy = total :" + " For each image i and each class c:\n\n" + " - if mim = proportional, then Ni( c ) = total * (sum_k(Ti(k))/sum_kl(Tl(k))) * (Ti(c)/sum_k(Ti(k))) where total is the total number of samples specified.\n\n" + " - if mim = equal , then Ni( c ) = (total / L) * (Ti(c)/sum_k(Ti(k))) where total is the total number of samples specified.\n\n" + " - if mim = custom , then Ni( c ) = total(i) * (Ti(c)/sum_k(Ti(k))) where total(i) is the total number of samples specified for image i. \n\n" " * strategy = smallest class\n\n" " - if mim = proportional, then the smallest class size (computed globally) is used for the strategy constant+proportional.\n\n" " - if mim = equal , then the smallest class size (computed globally) is used for the strategy constant+equal.\n\n" @@ -135,6 +145,18 @@ private: AddChoice("strategy.smallest","Set same number of samples for all classes, with the smallest class fully sampled"); SetParameterDescription("strategy.smallest","Set same number of samples for all classes, with the smallest class fully sampled"); + AddChoice("strategy.percent","Use a percentage of the samples available for each class"); + SetParameterDescription("strategy.percent","Use a percentage of the samples available for each class"); + + AddParameter(ParameterType_String,"strategy.percent.p","The percentage(s) to use"); + SetParameterDescription("strategy.percent.p","The percentage(s) to use " "In the case of the custom multi-image mode, several values can be given for each image."); + + AddChoice("strategy.total","Set the total number of samples to generate, and use class proportions."); + SetParameterDescription("strategy.total","Set the total number of samples to generate, and use class proportions."); + + AddParameter(ParameterType_String,"strategy.total.v","The number of samples to generate"); + SetParameterDescription("strategy.total.v","The number of samples to generate" "In the case of the custom multi-image mode, several values can be given for each image."); + AddChoice("strategy.all","Take all samples"); SetParameterDescription("strategy.all","Take all samples"); @@ -231,15 +253,68 @@ private: m_CalculatorList->SetNbOfSamplesAllClasses(countList, partitionMode); } break; - // smallest class + // percent case 2: + { + std::vector<itksys::String> parts = itksys::SystemTools::SplitString(this->GetParameterString("strategy.percent.p"),' '); + std::vector<double> percentList; + for (unsigned int i=0 ; i<parts.size() ; i++) + { + if (!parts[i].empty()) + { + std::string::size_type pos1 = parts[i].find_first_not_of(" \t"); + std::string::size_type pos2 = parts[i].find_last_not_of(" \t"); + std::string value(parts[i].substr(pos1, pos2 - pos1 + 1)); + percentList.push_back(boost::lexical_cast<double>(parts[i])); + + if(percentList.back()<0 || percentList.back()>1) + { + otbAppLogFATAL("Percent parameter should be in range [0,1]"); + } + } + } + if (percentList.size() < minParamSize) + { + otbAppLogFATAL("Missing arguments in strategy.percent.p to process sampling rates"); + } + otbAppLogINFO("Sampling strategy : set a percentage of samples to be used."); + m_CalculatorList->SetPercentageOfSamples(percentList, partitionMode); + } + break; + + // total + case 3: + { + std::vector<itksys::String> parts = itksys::SystemTools::SplitString(this->GetParameterString("strategy.total.v"),' '); + std::vector<unsigned long> totalList; + for (unsigned int i=0 ; i<parts.size() ; i++) + { + if (!parts[i].empty()) + { + std::string::size_type pos1 = parts[i].find_first_not_of(" \t"); + std::string::size_type pos2 = parts[i].find_last_not_of(" \t"); + std::string value(parts[i].substr(pos1, pos2 - pos1 + 1)); + totalList.push_back(boost::lexical_cast<unsigned long>(parts[i])); + } + } + if (totalList.size() < minParamSize) + { + otbAppLogFATAL("Missing arguments in strategy.total.v to process sampling rates"); + } + otbAppLogINFO("Sampling strategy : set a constant number of samples for all classes"); + m_CalculatorList->SetTotalNumberOfSamples(totalList, partitionMode); + } + break; + + // smallest class + case 4: { otbAppLogINFO("Sampling strategy : fit the number of samples based on the smallest class"); m_CalculatorList->SetMinimumNbOfSamplesByClass(partitionMode); } break; // all samples - case 3: + case 5: { otbAppLogINFO("Sampling strategy : take all samples"); m_CalculatorList->SetAllSamples(partitionMode); diff --git a/Modules/Applications/AppClassification/app/otbSampleSelection.cxx b/Modules/Applications/AppClassification/app/otbSampleSelection.cxx index eba28624b32752750a8f748353593b8b5673c60c..c4472b33800360f56578cfcfc40fe842e61d12d1 100644 --- a/Modules/Applications/AppClassification/app/otbSampleSelection.cxx +++ b/Modules/Applications/AppClassification/app/otbSampleSelection.cxx @@ -92,8 +92,10 @@ private: " so that the smallest one is fully sampled.\n" " - constant : select the same number of samples N in each class" " (with N below or equal to the size of the smallest class).\n" - " - byclass : set the required number for each class manually, with an input CSV file" + " - byclass : set the required number for each class manually, with an input CSV file" " (first column is class name, second one is the required samples number).\n\n" + " - percent: set a target global percentage of samples to use. Class proportions will be respected. \n\n" + " - total: set a target total number of samples to use. Class proportions will be respected. \n\n" "There is also a choice on the sampling type to performs : \n\n" " - periodic : select samples uniformly distributed\n" " - random : select samples randomly distributed\n\n" @@ -166,6 +168,23 @@ private: AddParameter(ParameterType_Int, "strategy.constant.nb", "Number of samples for all classes"); SetParameterDescription("strategy.constant.nb", "Number of samples for all classes"); + AddChoice("strategy.percent","Use a percentage of the samples available for each class"); + SetParameterDescription("strategy.percent","Use a percentage of the samples available for each class"); + + AddParameter(ParameterType_Float,"strategy.percent.p","The percentage to use"); + SetParameterDescription("strategy.percent.p","The percentage to use"); + SetMinimumParameterFloatValue("strategy.percent.p",0); + SetMaximumParameterFloatValue("strategy.percent.p",1); + SetDefaultParameterFloat("strategy.percent.p",0.5); + + AddChoice("strategy.total","Set the total number of samples to generate, and use class proportions."); + SetParameterDescription("strategy.total","Set the total number of samples to generate, and use class proportions."); + + AddParameter(ParameterType_Int,"strategy.total.v","The number of samples to generate"); + SetParameterDescription("strategy.total.v","The number of samples to generate"); + SetMinimumParameterIntValue("strategy.total.v",1); + SetDefaultParameterInt("strategy.total.v",1000); + AddChoice("strategy.smallest","Set same number of samples for all classes, with the smallest class fully sampled"); SetParameterDescription("strategy.smallest","Set same number of samples for all classes, with the smallest class fully sampled"); @@ -234,15 +253,30 @@ private: m_RateCalculator->SetNbOfSamplesAllClasses(GetParameterInt("strategy.constant.nb")); } break; - // smallest class + // percent case 2: + { + otbAppLogINFO("Sampling strategy: set a percentage of samples for each class."); + m_RateCalculator->SetPercentageOfSamples(this->GetParameterFloat("strategy.percent.p")); + } + break; + // total + case 3: + { + otbAppLogINFO("Sampling strategy: set the total number of samples to generate, use classes proportions."); + m_RateCalculator->SetTotalNumberOfSamples(this->GetParameterInt("strategy.total.v")); + } + break; + + // smallest class + case 4: { otbAppLogINFO("Sampling strategy : fit the number of samples based on the smallest class"); m_RateCalculator->SetMinimumNbOfSamplesByClass(); } break; // all samples - case 3: + case 5: { otbAppLogINFO("Sampling strategy : take all samples"); m_RateCalculator->SetAllSamples(); diff --git a/Modules/Fusion/MajorityVoting/include/otbNeighborhoodMajorityVotingImageFilter.h b/Modules/Fusion/MajorityVoting/include/otbNeighborhoodMajorityVotingImageFilter.h index f7ce6b84e5e25ebbf74c5c3d1cd517dc8a43fce5..009462af47680c6516d562bc1cd3bb15eb1f2d93 100644 --- a/Modules/Fusion/MajorityVoting/include/otbNeighborhoodMajorityVotingImageFilter.h +++ b/Modules/Fusion/MajorityVoting/include/otbNeighborhoodMajorityVotingImageFilter.h @@ -149,6 +149,10 @@ public: //Creates a SetKeepOriginalLabelBool method itkSetMacro(KeepOriginalLabelBool, bool); + //Process only isolated pixels + itkSetMacro(OnlyIsolatedPixels, bool); + itkSetMacro(IsolatedThreshold, unsigned int); + protected: NeighborhoodMajorityVotingImageFilter(); @@ -166,6 +170,31 @@ protected: void GenerateOutputInformation() ITK_OVERRIDE; + + //Type to store the useful information from the label histogram + struct HistoSummary + { + unsigned int freqCenterLabel; + PixelType majorityLabel; + bool majorityUnique; + }; + + struct CompareHistoFequencies + { + typedef std::pair<PixelType, unsigned int> HistoValueType; + bool operator()(const HistoValueType& a, const HistoValueType& b) + { + return a.second > b.second; + } + }; + + //Get a histogram of frequencies of labels with the 2 highest + // frequencies sorted in decreasing order and return the frequency + // of the label of the center pixel + const HistoSummary ComputeNeighborhoodHistogramSummary(const NeighborhoodIteratorType &nit, + const KernelIteratorType kernelBegin, + const KernelIteratorType kernelEnd) const; + private: NeighborhoodMajorityVotingImageFilter(const Self&); //purposely not implemented void operator=(const Self&); //purposely not implemented @@ -177,6 +206,12 @@ private: PixelType m_LabelForUndecidedPixels; bool m_KeepOriginalLabelBool; + //Choose to filter only isolated pixels + bool m_OnlyIsolatedPixels; + //The center pixel is isolated if there are fewer neighbours than + //this threshold with the same label + unsigned int m_IsolatedThreshold; + }; // end of class } // end namespace otb diff --git a/Modules/Fusion/MajorityVoting/include/otbNeighborhoodMajorityVotingImageFilter.txx b/Modules/Fusion/MajorityVoting/include/otbNeighborhoodMajorityVotingImageFilter.txx index 5fab37c3fe7a31395541cf91684ec5ef78419835..9c4de263463dfb2ace173423bc4bc19dd5c05253 100644 --- a/Modules/Fusion/MajorityVoting/include/otbNeighborhoodMajorityVotingImageFilter.txx +++ b/Modules/Fusion/MajorityVoting/include/otbNeighborhoodMajorityVotingImageFilter.txx @@ -38,87 +38,93 @@ NeighborhoodMajorityVotingImageFilter<TInputImage, TOutputImage, TKernel>::Neigh this->SetLabelForNoDataPixels(itk::NumericTraits<PixelType>::NonpositiveMin()); //m_LabelForNoDataPixels = 0 this->SetLabelForUndecidedPixels(itk::NumericTraits<PixelType>::NonpositiveMin()); //m_LabelForUndecidedPixels = 0 this->SetKeepOriginalLabelBool(true); //m_KeepOriginalLabelBool = true + this->SetOnlyIsolatedPixels(false); //process all pixels + this->SetIsolatedThreshold(1); } template<class TInputImage, class TOutputImage, class TKernel> typename NeighborhoodMajorityVotingImageFilter<TInputImage, TOutputImage, -TKernel>::PixelType NeighborhoodMajorityVotingImageFilter<TInputImage, -TOutputImage, TKernel>::Evaluate(const NeighborhoodIteratorType &nit, -const KernelIteratorType kernelBegin, -const KernelIteratorType kernelEnd) + TKernel>::PixelType +NeighborhoodMajorityVotingImageFilter<TInputImage, + TOutputImage, TKernel>::Evaluate(const NeighborhoodIteratorType &nit, + const KernelIteratorType kernelBegin, + const KernelIteratorType kernelEnd) { - PixelType majorityLabel = 0; //Value of the more representative pixels in the neighborhood - unsigned int majorityFreq = 0; //Number of pixels with the more representative value (majorityLabel) in the neighborhood - - unsigned int i; - KernelIteratorType kernel_it; - - std::map<PixelType, unsigned int> histoNeigh; - - PixelType centerPixel = nit.GetCenterPixel(); - - if (centerPixel != m_LabelForNoDataPixels) - { - for (i = 0, kernel_it = kernelBegin; kernel_it < kernelEnd; ++kernel_it, ++i) + const PixelType centerPixel = nit.GetCenterPixel(); + if (centerPixel == m_LabelForNoDataPixels) { - // if structuring element is positive, use the pixel under that element - // in the image - - PixelType label = nit.GetPixel(i); - if ((*kernel_it > itk::NumericTraits<KernelPixelType>::Zero) && (label != m_LabelForNoDataPixels)) + return m_LabelForNoDataPixels; + } + else + { + //Get a histogram of label frequencies where the 2 highest are at the beginning and sorted + const HistoSummary histoSummary = + this->ComputeNeighborhoodHistogramSummary(nit, kernelBegin, kernelEnd); + if(m_OnlyIsolatedPixels && + histoSummary.freqCenterLabel > m_IsolatedThreshold) { - // note we use GetPixel() on the SmartNeighborhoodIterator to - // respect boundary conditions - - //If the current label has already been added to the histogram histoNeigh - if (histoNeigh.count(label) > 0) + //If we want to filter only isolated pixels, keep the label if + //there are enough pixels with the center label to consider that + //it is not isolated + return centerPixel; + } + else + { + //If the majorityLabel is NOT unique in the neighborhood + if(!histoSummary.majorityUnique) { - histoNeigh[label]++; - } + if (m_KeepOriginalLabelBool == true) + { + return centerPixel; + } else - { - histoNeigh[label] = 1; + { + return m_LabelForUndecidedPixels; + } } + //Extraction of the more representative Label in the neighborhood (majorityLabel) + return histoSummary.majorityLabel; } - } - - //Extraction of the more representative Label in the neighborhood (majorityLabel) and its Frequency (majorityFreq) - typename std::map<PixelType, unsigned int>::iterator histoIt; - for (histoIt = histoNeigh.begin(); histoIt != histoNeigh.end(); ++histoIt) - { - if (histoIt->second > majorityFreq) - { - majorityFreq = histoIt->second; //Frequency - majorityLabel = histoIt->first; //Label - } - } + }//END if (centerPixel != m_LabelForNoDataPixels) +} - //If the majorityLabel is NOT unique in the neighborhood - for (histoIt = histoNeigh.begin(); histoIt != histoNeigh.end(); ++histoIt) +template<class TInputImage, class TOutputImage, class TKernel> +const typename NeighborhoodMajorityVotingImageFilter<TInputImage, TOutputImage, + TKernel>::HistoSummary +NeighborhoodMajorityVotingImageFilter<TInputImage, TOutputImage, + TKernel>::ComputeNeighborhoodHistogramSummary(const NeighborhoodIteratorType &nit, + const KernelIteratorType kernelBegin, + const KernelIteratorType kernelEnd) const +{ + typedef std::map<PixelType, unsigned int> HistogramType; + typedef std::vector<std::pair<PixelType, unsigned int> > HistoAsVectorType; + HistogramType histoNeigh; + unsigned int i = 0; + for (KernelIteratorType kernel_it = kernelBegin; + kernel_it < kernelEnd; ++kernel_it, ++i) { - if ( (histoIt->second == majorityFreq) && (histoIt->first != majorityLabel) ) + // if structuring element is positive, use the pixel under that element + // in the image + // note we use GetPixel() on the SmartNeighborhoodIterator to + // respect boundary conditions + const PixelType label = nit.GetPixel(i); + if ((*kernel_it > itk::NumericTraits<KernelPixelType>::Zero) && + (label != m_LabelForNoDataPixels)) { - if (m_KeepOriginalLabelBool == true) - { - majorityLabel = centerPixel; - } - else - { - majorityLabel = m_LabelForUndecidedPixels; - } - break; + histoNeigh[label] += 1; } } - }//END if (centerPixel != m_LabelForNoDataPixels) - - //If (centerPixel == m_LabelForNoDataPixels) - else - { - majorityLabel = m_LabelForNoDataPixels; - } - - return majorityLabel; + HistoAsVectorType histoNeighVec(histoNeigh.begin(), histoNeigh.end()); + //Sort the 2 max elements to the beginning + std::nth_element(histoNeighVec.begin(), histoNeighVec.begin()+1, + histoNeighVec.end(), CompareHistoFequencies()); + typename NeighborhoodMajorityVotingImageFilter<TInputImage, TOutputImage, + TKernel>::HistoSummary result; + result.freqCenterLabel = histoNeigh[nit.GetCenterPixel()]; + result.majorityLabel = histoNeighVec[0].first; + result.majorityUnique = (histoNeighVec[0].second != histoNeighVec[1].second); + return result; } template<class TInputImage, class TOutputImage, class TKernel> diff --git a/Modules/Fusion/MajorityVoting/test/CMakeLists.txt b/Modules/Fusion/MajorityVoting/test/CMakeLists.txt index a32c7664d64ed6f6a7b5134697852a1f6a5d56b4..5547d89d23e1b9aaaf172d0fb48433b301b284e4 100644 --- a/Modules/Fusion/MajorityVoting/test/CMakeLists.txt +++ b/Modules/Fusion/MajorityVoting/test/CMakeLists.txt @@ -27,6 +27,11 @@ otb_add_test(NAME leTvNeighborhoodMajorityVotingImageFilterTest COMMAND otbMajor 5 #yRadius 10 #LabelForNoDataPixels 7 #LabelForUndecidedPixels + 0 #OnlyIsolatedPixels + ) + +otb_add_test(NAME leTvNeighborhoodMajorityVotingIsolThresPixTest COMMAND otbMajorityVotingTestDriver + otbNeighborhoodMajorityVotingImageFilterIsolatedTest ) otb_add_test(NAME leTvSVMImageClassificationFilterWithNeighborhoodMajorityVoting COMMAND otbMajorityVotingTestDriver diff --git a/Modules/Fusion/MajorityVoting/test/otbMajorityVotingTestDriver.cxx b/Modules/Fusion/MajorityVoting/test/otbMajorityVotingTestDriver.cxx index 3a961ec71e79efcfcd093dd8b4d72c45a08b3552..a3a3809011a04089e7ba5efff515975ae4fd416f 100644 --- a/Modules/Fusion/MajorityVoting/test/otbMajorityVotingTestDriver.cxx +++ b/Modules/Fusion/MajorityVoting/test/otbMajorityVotingTestDriver.cxx @@ -3,4 +3,5 @@ void RegisterTests() { REGISTER_TEST(otbNeighborhoodMajorityVotingImageFilterNew); REGISTER_TEST(otbNeighborhoodMajorityVotingImageFilterTest); + REGISTER_TEST(otbNeighborhoodMajorityVotingImageFilterIsolatedTest); } diff --git a/Modules/Fusion/MajorityVoting/test/otbNeighborhoodMajorityVotingImageFilterTest.cxx b/Modules/Fusion/MajorityVoting/test/otbNeighborhoodMajorityVotingImageFilterTest.cxx index b6a0e1e92376c1889f3acee7693618dcc12d78eb..9a9de6918b9d61e3b1935455c210374e0d1a6d77 100644 --- a/Modules/Fusion/MajorityVoting/test/otbNeighborhoodMajorityVotingImageFilterTest.cxx +++ b/Modules/Fusion/MajorityVoting/test/otbNeighborhoodMajorityVotingImageFilterTest.cxx @@ -100,6 +100,11 @@ int otbNeighborhoodMajorityVotingImageFilterTest(int argc, char* argv[]) seBall.CreateStructuringElement(); NeighMajVotingFilter->SetKernel(seBall); + if(argc==9) + { + NeighMajVotingFilter->SetOnlyIsolatedPixels(static_cast<bool>(atoi(argv[8]))); + } + WriterType::Pointer writer = WriterType::New(); writer->SetFileName(outputFileName); writer->SetInput(NeighMajVotingFilter->GetOutput()); @@ -107,3 +112,96 @@ int otbNeighborhoodMajorityVotingImageFilterTest(int argc, char* argv[]) return EXIT_SUCCESS; } + +int otbNeighborhoodMajorityVotingImageFilterIsolatedTest(int itkNotUsed(argc), char* itkNotUsed(argv)[]) +{ + typedef unsigned char PixelType; // 8 bits + const unsigned int Dimension = 2; + + typedef otb::Image<PixelType, Dimension> ImageType; + + ImageType::Pointer image = ImageType::New(); + ImageType::IndexType start; + + start[0] = 0; + start[1] = 0; + + ImageType::SizeType size; + size[0] = 100; + size[1] = 100; + + ImageType::RegionType region; + + region.SetSize(size); + region.SetIndex(start); + image->SetRegions(region); + image->Allocate(); + image->FillBuffer(itk::NumericTraits<PixelType>::Zero); + + // Neighborhood majority voting filter type + typedef otb::NeighborhoodMajorityVotingImageFilter<ImageType> NeighborhoodMajorityVotingFilterType; + + // Binary ball Structuring Element type + typedef NeighborhoodMajorityVotingFilterType::KernelType StructuringType; + typedef StructuringType::RadiusType RadiusType; + + + // Neighborhood majority voting filter + NeighborhoodMajorityVotingFilterType::Pointer NeighMajVotingFilter; + NeighMajVotingFilter = NeighborhoodMajorityVotingFilterType::New(); + + NeighMajVotingFilter->SetInput(image); + + StructuringType seBall; + RadiusType rad; + + + NeighMajVotingFilter->SetKeepOriginalLabelBool(true); + + rad[0] = 1; + rad[1] = 1; + NeighMajVotingFilter->SetLabelForNoDataPixels(10); + NeighMajVotingFilter->SetLabelForUndecidedPixels(7); + seBall.SetRadius(rad); + seBall.CreateStructuringElement(); + NeighMajVotingFilter->SetKernel(seBall); + NeighMajVotingFilter->SetOnlyIsolatedPixels(true); + PixelType value = 255; + ImageType::IndexType coordinate; + coordinate[0] = 10; + coordinate[1] = 10; + image->SetPixel(coordinate, value); + image->Update(); + NeighMajVotingFilter->SetIsolatedThreshold(1); + NeighMajVotingFilter->Update(); + PixelType result = NeighMajVotingFilter->GetOutput()->GetPixel(coordinate); +//Should be filtered + if(result == value) + { + std::cout << "one pixel\n"; + return EXIT_FAILURE; + } + coordinate[0] = 10; + coordinate[1] = 11; + image->SetPixel(coordinate, value); + image->Update(); + NeighMajVotingFilter->Modified(); //needed for the filter to be updated + NeighMajVotingFilter->Update(); + result = NeighMajVotingFilter->GetOutput()->GetPixel(coordinate); + // Should not be filtered + if( result != value) + { + std::cout << "2 pixels thres = 1 result = " << int(result) << '\n'; + return EXIT_FAILURE; + } + NeighMajVotingFilter->SetIsolatedThreshold(3); + NeighMajVotingFilter->Update(); + result = NeighMajVotingFilter->GetOutput()->GetPixel(coordinate); +//Should be filtered + if(result == value) + { + std::cout << "2 pixels thres = 2" << '\n'; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h b/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h index 8743a95553695f3f3e5bff34a1e5ab0378f30bbf..7150b5259057068fa24d74f7da5f9bca99544445 100644 --- a/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h +++ b/Modules/Learning/Sampling/include/otbSamplingRateCalculator.h @@ -68,6 +68,12 @@ public: /** Method to set the same number of required samples in each class */ void SetNbOfSamplesAllClasses(unsigned long); + /** Method to set a percentage of samples for each class */ + void SetPercentageOfSamples(double percent); + + /** Method to set the total number of samples to generate */ + void SetTotalNumberOfSamples(unsigned long value); + /** Method to choose a sampling strategy based on the smallest class. * The number of samples in each class is set to this minimum size*/ void SetMinimumNbOfSamplesByClass(void); diff --git a/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h b/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h index 2e1b622efa8e9c9db7f4fca98f58eff96c92ca0d..97107f70b4ca3481952acef236760eca44d95b13 100644 --- a/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h +++ b/Modules/Learning/Sampling/include/otbSamplingRateCalculatorList.h @@ -82,6 +82,14 @@ public: /** Method to manually set the number of samples required in each class */ void SetNbOfSamplesByClass(const std::vector<ClassCountMapType> &required, PartitionType t); + /** Method to use a percentage of the samples available in each + * class */ + void SetPercentageOfSamples(std::vector<double> &p, PartitionType t); + + /** Method to set the total number of samples and use classes proportions + */ + void SetTotalNumberOfSamples(std::vector<unsigned long> &tot, PartitionType t); + protected: /** Constructor */ SamplingRateCalculatorList(){} diff --git a/Modules/Learning/Sampling/src/otbSamplingRateCalculator.cxx b/Modules/Learning/Sampling/src/otbSamplingRateCalculator.cxx index 65a85589b9450b7d33d3c801d5a03ae3816369c5..f61ba32dc9bf642b34bfc2842e9da51938416d01 100644 --- a/Modules/Learning/Sampling/src/otbSamplingRateCalculator.cxx +++ b/Modules/Learning/Sampling/src/otbSamplingRateCalculator.cxx @@ -110,6 +110,40 @@ SamplingRateCalculator } } + +void SamplingRateCalculator +::SetPercentageOfSamples(double percent) +{ + MapRateType::iterator it = m_RatesByClass.begin(); + for (; it != m_RatesByClass.end() ; ++it) + { + it->second.Required = static_cast<unsigned long>(vcl_floor(0.5+percent * it->second.Tot)); + it->second.Rate = percent; + } +} + +void SamplingRateCalculator +::SetTotalNumberOfSamples(unsigned long value) +{ + // First, get total number of samples + unsigned long totalNumberOfSamplesAvailable = 0; + + MapRateType::iterator it = m_RatesByClass.begin(); + for (; it != m_RatesByClass.end() ; ++it) + { + totalNumberOfSamplesAvailable+=it->second.Tot; + } + + // Then compute number of samples for each class + for (it = m_RatesByClass.begin(); it != m_RatesByClass.end() ; ++it) + { + double ratio = it->second.Tot / static_cast<double>(totalNumberOfSamplesAvailable); + + it->second.Required = static_cast<unsigned long>(0.5+ratio*value); + this->UpdateRate(it->first); + } +} + void SamplingRateCalculator ::Write(std::string filename) diff --git a/Modules/Learning/Sampling/src/otbSamplingRateCalculatorList.cxx b/Modules/Learning/Sampling/src/otbSamplingRateCalculatorList.cxx index 829e3aefd6b3a2468826d8772c24eb7bdd3aa81a..1ffe03a54cd51a35745ff5ec27b980146a4fb54c 100644 --- a/Modules/Learning/Sampling/src/otbSamplingRateCalculatorList.cxx +++ b/Modules/Learning/Sampling/src/otbSamplingRateCalculatorList.cxx @@ -284,6 +284,133 @@ SamplingRateCalculatorList } } +void +SamplingRateCalculatorList +::SetPercentageOfSamples(std::vector<double> &p, PartitionType t) +{ + if (p.empty()) + { + itkGenericExceptionMacro("No percentage given"); + } + + this->UpdateGlobalCounts(); + + ClassCountMapType::const_iterator it; + ClassCountMapType needed; + switch (t) + { + case PROPORTIONAL: + { + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetPercentageOfSamples(p[0]); + } + break; + } + case EQUAL: + { + needed.clear(); + for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it) + { + needed[it->first]=static_cast<unsigned long>(vcl_floor(0.5 + it->second * p[0] / (double)this->Size())); + } + + + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetNbOfSamplesByClass(needed); + } + break; + } + case CUSTOM: + { + if (p.size() < this->Size()) + { + itkGenericExceptionMacro("Not enough values present to set custom percents in all inputs"); + } + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetPercentageOfSamples(p[i]); + } + break; + } + default: + itkGenericExceptionMacro("Unknown partition mode"); + break; + } +} + +void +SamplingRateCalculatorList +::SetTotalNumberOfSamples(std::vector<unsigned long> &tot, PartitionType t) +{ + if (tot.empty()) + { + itkGenericExceptionMacro("No percentage given"); + } + + this->UpdateGlobalCounts(); + + ClassCountMapType needed; + switch (t) + { + case PROPORTIONAL: + { + unsigned long total_nb_samples = 0UL; + + std::vector<unsigned long> nb_samples(this->Size(),0UL); + + for (unsigned int i=0 ; i<this->Size() ; i++) + { + // Compute the total number of samples for image i + const MapRateType & rates = this->GetNthElement(i)->GetRatesByClass(); + + for(MapRateType::const_iterator it = rates.begin();it!=rates.end();++it) + { + nb_samples[i]+=it->second.Tot; + } + + total_nb_samples+=nb_samples[i]; + } + + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetTotalNumberOfSamples(vcl_floor(0.5+tot[0]*nb_samples[i]/static_cast<double>(total_nb_samples))); + } + break; + } + case EQUAL: + { + + unsigned long total_by_image = static_cast<unsigned long>(vcl_floor(tot[0]/static_cast<double>(this->Size()))); + + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetTotalNumberOfSamples(total_by_image); + } + break; + } + case CUSTOM: + { + if (tot.size() < this->Size()) + { + itkGenericExceptionMacro("Not enough values present to set total samples in all inputs"); + } + for (unsigned int i=0 ; i<this->Size() ; i++) + { + this->GetNthElement(i)->SetTotalNumberOfSamples(tot[i]); + } + break; + } + default: + itkGenericExceptionMacro("Unknown partition mode"); + break; + } + + +} + + void SamplingRateCalculatorList ::UpdateGlobalCounts() diff --git a/Modules/Learning/Sampling/test/otbSamplingRateCalculatorListTest.cxx b/Modules/Learning/Sampling/test/otbSamplingRateCalculatorListTest.cxx index 6210ed101e99f7e87cbdc28f2aba332aada775d1..388f920203b677b8eb6b7d5fbbe577858679b0e3 100644 --- a/Modules/Learning/Sampling/test/otbSamplingRateCalculatorListTest.cxx +++ b/Modules/Learning/Sampling/test/otbSamplingRateCalculatorListTest.cxx @@ -72,6 +72,12 @@ int otbSamplingRateCalculatorList(int itkNotUsed(argc), char* argv[]) std::vector<unsigned long> nbSamplesCst; nbSamplesCst.push_back(151); + std::vector<double> percent; + percent.push_back(0.33); + + std::vector<unsigned long> total; + total.push_back(300); + std::vector<unsigned long> nbSamplesCstCustom; nbSamplesCstCustom.push_back(70); nbSamplesCstCustom.push_back(80); @@ -183,6 +189,38 @@ int otbSamplingRateCalculatorList(int itkNotUsed(argc), char* argv[]) rateCalcList->GetNthElement(i)->Print(file, indent_1); } + file <<"#Test the strategy : percent - proportional"<<std::endl; + rateCalcList->SetPercentageOfSamples(percent,typeProportional); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file <<"#Test the strategy : percent - equal"<<std::endl; + rateCalcList->SetPercentageOfSamples(percent,typeEqual); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file <<"#Test the strategy : total - proportional"<<std::endl; + rateCalcList->SetTotalNumberOfSamples(total,typeProportional); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + + file <<"#Test the strategy : total - equal"<<std::endl; + rateCalcList->SetTotalNumberOfSamples(total,typeEqual); + for (unsigned int i=0 ; i<rateCalcList->Size() ; i++) + { + file << "# Input "<< i << std::endl; + rateCalcList->GetNthElement(i)->Print(file, indent_1); + } + file.close(); return EXIT_SUCCESS; } diff --git a/Modules/Radiometry/OpticalCalibration/test/CMakeLists.txt b/Modules/Radiometry/OpticalCalibration/test/CMakeLists.txt index 366591f3a37f5bbf86af585042919e527350b382..88720f365603eb50d286fffdea28ad0275ced292 100644 --- a/Modules/Radiometry/OpticalCalibration/test/CMakeLists.txt +++ b/Modules/Radiometry/OpticalCalibration/test/CMakeLists.txt @@ -42,18 +42,9 @@ otbImageToLuminanceImageFilter.cxx add_executable(otbOpticalCalibrationTestDriver ${OTBOpticalCalibrationTests}) target_link_libraries(otbOpticalCalibrationTestDriver ${OTBOpticalCalibration-Test_LIBRARIES}) otb_module_target_label(otbOpticalCalibrationTestDriver) - -# Since CMake 2.8.11, the size of the stack is not modified by CMake on -# windows platform, it uses the default size: with visual compiler it is 1Mbyte -# which is to lower for us (thanks to 6S code). -if(MSVC) - set_target_properties(otbOpticalCalibrationTestDriver PROPERTIES LINK_FLAGS "/STACK:10000000") -elseif(MINGW) - set_target_properties(otbOpticalCalibrationTestDriver PROPERTIES LINK_FLAGS "-Wl,--stack,10000000") -endif() +set_linker_stack_size_flag(otbOpticalCalibrationTestDriver 10000000) # Tests Declaration - otb_add_test(NAME raTuSpectralSensitivityReaderNew COMMAND otbOpticalCalibrationTestDriver otbSpectralSensitivityReaderNew ) diff --git a/Modules/Wrappers/CommandLine/src/CMakeLists.txt b/Modules/Wrappers/CommandLine/src/CMakeLists.txt index 3da3ad0d7d8bcea460175d35b084dea386655383..2662ced40ba275743e55830337f5bd4dff9cbaf3 100644 --- a/Modules/Wrappers/CommandLine/src/CMakeLists.txt +++ b/Modules/Wrappers/CommandLine/src/CMakeLists.txt @@ -15,6 +15,8 @@ add_executable(otbApplicationLauncherCommandLine otbApplicationLauncherCommandLi target_link_libraries(otbApplicationLauncherCommandLine OTBCommandLine) otb_module_target(otbApplicationLauncherCommandLine) +set_linker_stack_size_flag(otbApplicationLauncherCommandLine 10000000) + # Where we will install the script in the build tree get_target_property(CLI_OUTPUT_DIR otbApplicationLauncherCommandLine RUNTIME_OUTPUT_DIRECTORY) diff --git a/Modules/Wrappers/QtWidget/src/CMakeLists.txt b/Modules/Wrappers/QtWidget/src/CMakeLists.txt index b73874446ce1a0bf90b74aec8de35b20c2e58833..2482fdd2d7c4ef0d771bf1eceee1c0ba5b331182 100644 --- a/Modules/Wrappers/QtWidget/src/CMakeLists.txt +++ b/Modules/Wrappers/QtWidget/src/CMakeLists.txt @@ -92,6 +92,7 @@ target_link_libraries(otbApplicationLauncherQt ${OTBQt4_LIBRARIES} ) otb_module_target(otbApplicationLauncherQt) +set_linker_stack_size_flag(otbApplicationLauncherQt 10000000) # Where we will install the script in the build tree get_target_property(GUI_OUTPUT_DIR otbApplicationLauncherQt RUNTIME_OUTPUT_DIRECTORY) diff --git a/SuperBuild/CMake/External_shark.cmake b/SuperBuild/CMake/External_shark.cmake index a360f876af4b1d73d629b73fa9f664c4b5311523..f9ff3a0d3e223ed884bf0d9ebf4c43eaeb21e055 100644 --- a/SuperBuild/CMake/External_shark.cmake +++ b/SuperBuild/CMake/External_shark.cmake @@ -2,11 +2,11 @@ if(NOT __EXTERNAL_SHARK__) set(__EXTERNAL_SHARK__ 1) if(USE_SYSTEM_SHARK) - message(STATUS " Using SHARK system version") + message(STATUS " Using system version of SHARK") else() SETUP_SUPERBUILD(PROJECT SHARK) cmake_minimum_required(VERSION 3.1) - message(STATUS " Using SHARK SuperBuild version") + message(STATUS " Using SuperBuild version of SHARK") # declare dependencies ADDTO_DEPENDENCIES_IF_NOT_SYSTEM(SHARK BOOST)