diff --git a/CMake/OTBCheckCpp11Keywords.cmake b/CMake/OTBCheckCpp11Keywords.cmake index a956670218e0ee932f057ea994a534767da9402b..707fe8b05a4aa71343df8daf5de3950c341fad71 100644 --- a/CMake/OTBCheckCpp11Keywords.cmake +++ b/CMake/OTBCheckCpp11Keywords.cmake @@ -22,6 +22,8 @@ include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS}) +unset(OTB_HAS_CXX11 CACHE) + CHECK_CXX_SOURCE_COMPILES(" #ifdef _MSC_VER #if _MSC_VER <= 1700 diff --git a/Documentation/Cookbook/Scripts/otbGenerateWrappersRstDoc.py b/Documentation/Cookbook/Scripts/otbGenerateWrappersRstDoc.py index e31bdcc37cc6508e75ac2ab42f32757a0d7fa6dc..4c6c30fa402b69ad2c3b342a1e3a4b3684650681 100755 --- a/Documentation/Cookbook/Scripts/otbGenerateWrappersRstDoc.py +++ b/Documentation/Cookbook/Scripts/otbGenerateWrappersRstDoc.py @@ -149,10 +149,10 @@ def FindLengthOfLargestColumnText(app,paramlist): else: if colLength[0] < len(param): colLength[0] = len(param) - lenpdescr = len(app.GetParameterName(param)) + lenpdescr = len(GenerateParameterType(app, param)) if colLength[2] < lenpdescr: colLength[2] = lenpdescr - lenptype = len(GenerateParameterType(app,param)) + lenptype = len(app.GetParameterName(param)) if colLength[1] < lenptype: colLength[1] = lenptype return colLength @@ -181,14 +181,14 @@ def MakeText(text, size): def GenerateParametersTable(app,paramlist): colLength = FindLengthOfLargestColumnText(app, paramlist) output = linesep + ".. [#] Table: Parameters table for " + ConvertString(app.GetDocName()) + "." + linesep + linesep - headerlist = ["Parameter Key", "Parameter Type", "Parameter Description"] + headerlist = ["Parameter Key", "Parameter Name", "Parameter Type"] for i in xrange(len(headerlist)): colLength[i] = len(headerlist[i]) if colLength[i] < len(headerlist[i]) else colLength[i] output += RstTableHeading(headerlist, colLength) for param in paramlist: output += MakeText(param, colLength[0]) - output += MakeText(GenerateParameterType(app,param), colLength[1]) - output += MakeText(GenerateParameterType(app,param), colLength[2]) + output += MakeText(app.GetParameterName(param), colLength[1]) + output += MakeText(GenerateParameterType(app, param), colLength[2]) output += '|' + linesep output += RstTableHeaderLine(headerlist, colLength, '-') if app.GetParameterType(param) == otbApplication.ParameterType_Choice: diff --git a/Documentation/Cookbook/rst/recipes/improc.rst b/Documentation/Cookbook/rst/recipes/improc.rst index ca935be146903a9b505fdccf2f32396d238c134d..776fa35ad94722c494e0f508d25ed2a48facf03d 100644 --- a/Documentation/Cookbook/rst/recipes/improc.rst +++ b/Documentation/Cookbook/rst/recipes/improc.rst @@ -145,9 +145,9 @@ the *MeanShiftSmoothing* application: :: - otbcli_MeanShiftSmoothing -in input_image + otbcli_MeanShiftSmoothing -in input_image.tif -fout filtered_range.tif - -foutpos filtered_spat.tif + -foutpos filtered_spatial.tif -ranger 30 -spatialr 5 -maxiter 10 @@ -173,7 +173,7 @@ so, the *LSMSSegmentation* will process them by tiles whose dimensions are defined by the *tilesizex* and *tilesizey* parameters, and by writing intermediate images to disk, thus keeping the memory consumption very low throughout the process. The segmentation will group together -adjacent pixels whose range distance is below the *ranger* parameter and +neighboring pixels whose range distance is below the *ranger* parameter and (optionally) spatial distance is below the *spatialr* parameter. :: @@ -250,7 +250,7 @@ projection, so does the output GIS file). :: - otbcli_LSMSVectorization -in input_image + otbcli_LSMSVectorization -in input_image.tif -inseg segmentation_merged.tif -out segmentation_merged.shp -tilesizex 256 diff --git a/Documentation/Cookbook/rst/recipes/pbclassif.rst b/Documentation/Cookbook/rst/recipes/pbclassif.rst index 8de90bce37a3cee246b18c9664f035268b394947..9eaccd318a2ad4b78c7b622f1f6fc82fdcd54222 100644 --- a/Documentation/Cookbook/rst/recipes/pbclassif.rst +++ b/Documentation/Cookbook/rst/recipes/pbclassif.rst @@ -4,10 +4,10 @@ Classification Pixel based classification -------------------------- -Orfeo ToolBox ships with a set of application to perform supervised -pixel-based image classification. This framework allows to learn from -multiple images, and using several machine learning method such as -SVM, Bayes, KNN, Random Forests, Artificial Neural Network, and +Orfeo ToolBox ships with a set of application to perform supervised or +unsupervised pixel-based image classification. This framework allows +to learn from multiple images, and using several machine learning method +such as SVM, Bayes, KNN, Random Forests, Artificial Neural Network, and others...(see application help of ``TrainImagesClassifier`` and ``TrainVectorClassifier`` for further details about all the available classifiers). Here is an overview of the complete workflow: @@ -347,8 +347,9 @@ using the ``TrainVectorClassifier`` application. -feat band_0 band_1 band_2 band_3 band_4 band_5 band_6 The ``-classifier`` parameter allows to choose which machine learning -model algorithm to train. Please refer to the -``TrainVectorClassifier`` application reference documentation. +model algorithm to train. You have the possibility to do the unsupervised +classification,for it, you must to choose the Shark kmeans classifier. +Please refer to the ``TrainVectorClassifier`` application reference documentation. In case of multiple samples files, you can add them to the ``-io.vd`` parameter (see `Working with several images`_ section). @@ -409,6 +410,11 @@ class too, based on the `ConfusionMatrixCalculator <http://www.orfeo-toolbox.org/doxygen-current/classotb_1_1ConfusionMatrixCalculator.html>`_ class. +If you have made an unsupervised classification, it must be specified +to the ``ConputeConfusionMatrix`` application. In this case, a contingency table +have to be create rather than a confusion matrix. For further details, +see ``format`` parameter in the application help of *ConputeConfusionMatrix*. + :: otbcli_ComputeConfusionMatrix -in labeled_image.tif diff --git a/Documentation/SoftwareGuide/Latex/ReadWrite.tex b/Documentation/SoftwareGuide/Latex/ReadWrite.tex index 5c301d60511d4480b0a7757c7359be03072e1e92..2d82fac6b37302a4c00375c8797cf5eed0c8a7c4 100644 --- a/Documentation/SoftwareGuide/Latex/ReadWrite.tex +++ b/Documentation/SoftwareGuide/Latex/ReadWrite.tex @@ -253,6 +253,26 @@ IMPORTANT: Note that you'll probably need to "quote" the filename. \item Select the JPEG2000 sub-resolution image to read \item 0 by default \end{itemize} +\item \begin{verbatim}&bands=r1,r2,...,rn\end{verbatim} +\begin{itemize} + \item Select a subset of bands from the input image + \item The syntax is inspired by Python indexing syntax with + bands=r1,r2,r3,...,rn where each ri is a band range that can be : + \begin{itemize} + \item a single index (1-based) : + \begin{itemize} + \item $'2'$ means 2nd band + \item $'-1'$ means last band + \end{itemize} + \item or a range of bands : + \begin{itemize} + \item $'3:'$ means 3rd band until the last one + \item $':-2'$ means the first bands until the second to last + \item $'2:4'$ means bands 2,3 and 4 + \end{itemize} + \end{itemize} + \item empty by default (all bands are read from the input image) +\end{itemize} \item \begin{verbatim}&skipcarto=<(bool)true>\end{verbatim} \begin{itemize} \item Skip the cartographic information @@ -339,8 +359,8 @@ IMPORTANT: Note that you'll probably need to "quote" the filename. \item The region must be set with 4 unsigned integers (the separator used is the colon ':'). Values are: \begin{itemize} - \item startx: first index on X - \item starty: first index on Y + \item startx: first index on X (starting with 0) + \item starty: first index on Y (starting with 0) \item sizex: size along X \item sizey: size along Y \end{itemize} @@ -349,6 +369,26 @@ IMPORTANT: Note that you'll probably need to "quote" the filename. itk::Size classes. The origin of the region within the image with which it is associated is defined by Index \end{itemize} +\item \begin{verbatim}&bands=r1,r2,...,rn\end{verbatim} +\begin{itemize} + \item Select a subset of bands from the output image + \item The syntax is inspired by Python indexing syntax with + bands=r1,r2,r3,...,rn where each ri is a band range that can be : + \begin{itemize} + \item a single index (1-based) : + \begin{itemize} + \item $'2'$ means 2nd band + \item $'-1'$ means last band + \end{itemize} + \item or a range of bands : + \begin{itemize} + \item $'3:'$ means 3rd band until the last one + \item $':-2'$ means the first bands until the second to last + \item $'2:4'$ means bands 2,3 and 4 + \end{itemize} + \end{itemize} + \item empty by default (all bands are write from the output image) +\end{itemize} \end{itemize} diff --git a/Modules/Applications/AppSARUtils/CMakeLists.txt b/Modules/Applications/AppSARUtils/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..bd7a065d1828ce742920e76cd6382c222f5f67cf --- /dev/null +++ b/Modules/Applications/AppSARUtils/CMakeLists.txt @@ -0,0 +1,2 @@ +project(OTBAppSARUtils) +otb_module_impl() diff --git a/Modules/Applications/AppSARUtils/app/CMakeLists.txt b/Modules/Applications/AppSARUtils/app/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f0e7132c08c68baa6c95c2bc7dd114974e26278f --- /dev/null +++ b/Modules/Applications/AppSARUtils/app/CMakeLists.txt @@ -0,0 +1,9 @@ +set(OTBAppSARUtils_LINK_LIBS + ${OTBSARUtils_LIBRARIES} + ${OTBApplicationEngine_LIBRARIES} +) + +otb_create_application( + NAME ComputeModulusAndPhase + SOURCES otbComputeModulusAndPhase.cxx + LINK_LIBRARIES ${${otb-module}_LIBRARIES}) diff --git a/Modules/Applications/AppSARUtils/app/otbComputeModulusAndPhase.cxx b/Modules/Applications/AppSARUtils/app/otbComputeModulusAndPhase.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f3ea3c2349c357faf97b6a3d2baa6e36ed99725d --- /dev/null +++ b/Modules/Applications/AppSARUtils/app/otbComputeModulusAndPhase.cxx @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbWrapperApplication.h" +#include "otbWrapperApplicationFactory.h" +#include <otbMultiToMonoChannelExtractROI.h> + +#include "itkComplexToPhaseImageFilter.h" +#include "itkComplexToModulusImageFilter.h" +#include <itkMacro.h> + +namespace otb +{ +// Application class is defined in Wrapper namespace. +namespace Wrapper +{ + +/** \class ComputeModulusAndPhase + * \brief ComputeModulusAndPhase is an application that + * computes modulus and phase from a complex SAR image. + * + * \ingroup AppSARUtils + */ +class ComputeModulusAndPhase: public Application +{ +public: + // Class declaration is followed by ITK public types for the class, the superclass and smart pointers. + typedef ComputeModulusAndPhase Self; + typedef Application Superclass; + typedef itk::SmartPointer<Self> Pointer; + typedef itk::SmartPointer<const Self> ConstPointer; + + /** Standard macro */ + itkNewMacro (Self); + itkTypeMacro(ComputeModulusAndPhase, otb::Wrapper::Application); + + //typedefs for the application + typedef otb::MultiToMonoChannelExtractROI<typename ComplexFloatVectorImageType::InternalPixelType, typename ComplexFloatImageType::PixelType> ExtractFilterType; + typedef itk::ComplexToModulusImageFilter<ComplexFloatImageType, FloatImageType> ModulusFilterType; + typedef itk::ComplexToPhaseImageFilter<ComplexFloatImageType, FloatImageType> PhaseFilterType; + +private: + void DoInit() + { + SetName("ComputeModulusAndPhase"); + SetDescription("This application computes the modulus and the phase of a complex SAR image."); + + SetDocName("Compute Modulus And Phase"); + SetDocLongDescription( + "This application computes the modulus and the phase of a " + "complex SAR image. The input shoud be a single band image with " + "complex pixels." + ); + SetDocLimitations("None"); + SetDocAuthors("Alexia Mondot (alexia.mondot@c-s.fr) and Mickael Savinaud (mickael.savinaud@c-s.fr)"); + SetDocSeeAlso("SARPolarMatrixConvert, SARPolarSynth"); + AddDocTag(Tags::SAR); + + // Input images + AddParameter(ParameterType_ComplexInputImage, "in", "Input Image"); + SetParameterDescription("in", "Input image (complex single band)"); + + // Outputs + AddParameter(ParameterType_OutputImage, "modulus", "Modulus"); + SetParameterDescription("modulus", "Modulus of the input: sqrt(real*real + imag*imag)."); + + AddParameter(ParameterType_OutputImage, "phase", "Phase"); + SetParameterDescription("phase", "Phase of the input: atan2(imag, real)."); + + AddRAMParameter(); + + // Doc example parameter settings + SetDocExampleParameterValue("in", "monobandComplexFloat.tif"); + SetDocExampleParameterValue("modulus", "modulus.tif"); + SetDocExampleParameterValue("phase", "phase.tif"); + } + + // DoUpdateParameters() is called as soon as a parameter value change. + void DoUpdateParameters() + { + } + + // DoExecute() contains the application core. + void DoExecute() + { + m_Modulus = ModulusFilterType::New(); + m_Phase = PhaseFilterType::New(); + + ComplexFloatVectorImageType::Pointer inImage = GetParameterComplexImage("in"); + + if (inImage->GetNumberOfComponentsPerPixel() != 1) + { + otbAppLogFATAL("Input must be a single band complex image."); + } + + // Get first band + m_Extract = ExtractFilterType::New(); + m_Extract->SetInput(inImage); + + // Compute modulus and phase + m_Modulus->SetInput(m_Extract->GetOutput()); + m_Phase->SetInput(m_Extract->GetOutput()); + + SetParameterOutputImage("modulus", m_Modulus->GetOutput() ); + SetParameterOutputImage("phase", m_Phase->GetOutput()); + } + + ExtractFilterType::Pointer m_Extract; + ModulusFilterType::Pointer m_Modulus; + PhaseFilterType::Pointer m_Phase; +}; + +} // namespace Wrapper +} // namespace otb +OTB_APPLICATION_EXPORT(otb::Wrapper::ComputeModulusAndPhase) diff --git a/Modules/Applications/AppSARUtils/otb-module.cmake b/Modules/Applications/AppSARUtils/otb-module.cmake new file mode 100644 index 0000000000000000000000000000000000000000..41a1b43467aa97d35721a46dd6f4e4f4ad6b04be --- /dev/null +++ b/Modules/Applications/AppSARUtils/otb-module.cmake @@ -0,0 +1,12 @@ +set(DOCUMENTATION "SAR Utils application.") + +otb_module(OTBAppSARUtils + DEPENDS + OTBApplicationEngine + TEST_DEPENDS + OTBTestKernel + OTBCommandLine + + DESCRIPTION + "${DOCUMENTATION}" + ) diff --git a/Modules/Applications/AppSARUtils/test/CMakeLists.txt b/Modules/Applications/AppSARUtils/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..65dc6b75a9de038c360f9671a1a83c357a020bed --- /dev/null +++ b/Modules/Applications/AppSARUtils/test/CMakeLists.txt @@ -0,0 +1,13 @@ +otb_module_test() +#----------- ComputeModulusAndPhase TESTS ---------------- +otb_test_application(NAME apTvUtComputeModulusAndPhase_1inputComplex + APP ComputeModulusAndPhase + OPTIONS -in ${INPUTDATA}/monobandComplexFloat.tif + -modulus ${TEMP}/apTvUtMod1inputComplex.tif + -phase ${TEMP}/apTvUtPha1inputComplex.tif + VALID --compare-n-images ${EPSILON_6} 2 + ${BASELINE}/Mod_monobandComplexFloat.tif + ${TEMP}/apTvUtMod1inputComplex.tif + ${BASELINE}/Pha_monobandComplexFloat.tif + ${TEMP}/apTvUtPha1inputComplex.tif + ) diff --git a/Modules/Applications/AppSegmentation/app/otbLSMSVectorization.cxx b/Modules/Applications/AppSegmentation/app/otbLSMSVectorization.cxx index a1e8e9b36f7ee71d6c37c973ce5f2c15752ed3f2..a7dae77d1d92c6413c1a573fee73a4df9adc18ff 100644 --- a/Modules/Applications/AppSegmentation/app/otbLSMSVectorization.cxx +++ b/Modules/Applications/AppSegmentation/app/otbLSMSVectorization.cxx @@ -100,7 +100,7 @@ private: AddRAMParameter(); // Doc example parameter settings - SetDocExampleParameterValue("in","avions.tif"); + SetDocExampleParameterValue("in","maur_rgb.png"); SetDocExampleParameterValue("inseg","merged.tif"); SetDocExampleParameterValue("out","vector.shp"); SetDocExampleParameterValue("tilesizex","256"); diff --git a/Modules/Applications/AppSegmentation/app/otbMeanShiftSmoothing.cxx b/Modules/Applications/AppSegmentation/app/otbMeanShiftSmoothing.cxx index b5ae63bdfb6d6372d23242a0286313a10d8e5578..6184904bfc67a914d198ee74be5013f51d485cd3 100644 --- a/Modules/Applications/AppSegmentation/app/otbMeanShiftSmoothing.cxx +++ b/Modules/Applications/AppSegmentation/app/otbMeanShiftSmoothing.cxx @@ -109,8 +109,8 @@ private: // Doc example parameter settings SetDocExampleParameterValue("in", "maur_rgb.png"); - SetDocExampleParameterValue("fout", "MeanShift_FilterOutput.tif"); - SetDocExampleParameterValue("foutpos", "MeanShift_SpatialOutput.tif"); + SetDocExampleParameterValue("fout", "smooth.tif"); + SetDocExampleParameterValue("foutpos", "position.tif"); SetDocExampleParameterValue("spatialr", "16"); SetDocExampleParameterValue("ranger", "16"); SetDocExampleParameterValue("thres", "0.1"); diff --git a/Modules/Core/ImageBase/include/otbImageIOBase.h b/Modules/Core/ImageBase/include/otbImageIOBase.h index 50e79ffaef85755d348ed5fad177d1603c45152d..b6ae1ec2a1393e4c73c66af09d7253b875b85a47 100644 --- a/Modules/Core/ImageBase/include/otbImageIOBase.h +++ b/Modules/Core/ImageBase/include/otbImageIOBase.h @@ -303,7 +303,6 @@ public: virtual void SetOutputImagePixelType( bool isComplexInternalPixelType, bool isVectorImage) = 0; - /*-------- This part of the interfaces deals with reading data ----- */ /** Determine the file type. Returns true if this ImageIO can read the @@ -421,6 +420,12 @@ public: */ const ArrayOfExtensionsType & GetSupportedWriteExtensions() const; + /** Remap band order in an input buffer using band mapping bandList + * This operation is done in-place. The buffer size should enough to + * contain extracted bands before and after mapping. bandList mapping + * between origin components and output components (before any + * conversion)*/ + void DoMapBuffer(void* buffer, size_t numberOfPixels, std::vector<unsigned int>& bandList); protected: ImageIOBase(); diff --git a/Modules/Core/ImageBase/src/otbImageIOBase.cxx b/Modules/Core/ImageBase/src/otbImageIOBase.cxx index efcf4d0244bc81d150f2686a3c871dd2ac17fca7..d88d601fd66a007b1db2ec7c4f782f1eee852b4b 100644 --- a/Modules/Core/ImageBase/src/otbImageIOBase.cxx +++ b/Modules/Core/ImageBase/src/otbImageIOBase.cxx @@ -1269,6 +1269,54 @@ ImageIOBase return axis; } +void +ImageIOBase +::DoMapBuffer(void* buffer, size_t numberOfPixels, std::vector<unsigned int>& bandList) +{ + size_t componentSize = this->GetComponentSize(); + size_t inPixelSize = componentSize * this->GetNumberOfComponents(); + size_t outPixelSize = componentSize * bandList.size(); + char* inPos = static_cast<char*>(buffer); + char* outPos = static_cast<char*>(buffer); + bool workBackward = (outPixelSize > inPixelSize); + char *pixBuffer = new char[outPixelSize]; + + memset(pixBuffer, 0, outPixelSize); + + if (workBackward) + { + inPos = inPos + numberOfPixels*inPixelSize; + outPos = outPos + numberOfPixels*outPixelSize; + for (size_t n=0 ; n<numberOfPixels ; n++) + { + inPos -= inPixelSize; + outPos -= outPixelSize; + for (unsigned int i=0 ; i < bandList.size() ; i++) + { + memcpy(pixBuffer + i*componentSize, inPos + bandList[i]*componentSize, componentSize); + } + // copy pixBuffer to output + memcpy(outPos, pixBuffer, outPixelSize); + } + } + else + { + for (size_t n=0 ; n<numberOfPixels ; n++) + { + for (unsigned int i=0 ; i < bandList.size() ; i++) + { + memcpy(pixBuffer + i*componentSize, inPos + bandList[i]*componentSize, componentSize); + } + // copy pixBuffer to output + memcpy(outPos, pixBuffer, outPixelSize); + inPos += inPixelSize; + outPos += outPixelSize; + } + } + + delete[] pixBuffer; +} + void ImageIOBase::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); diff --git a/Modules/Filtering/MathParser/src/otbParser.cxx b/Modules/Filtering/MathParser/src/otbParser.cxx index 56bdd287dcdb7f2c12fdb481bbe7d6f71dd03de4..4280d3ac9c85b508453b5c23685bf0dc52f89255 100644 --- a/Modules/Filtering/MathParser/src/otbParser.cxx +++ b/Modules/Filtering/MathParser/src/otbParser.cxx @@ -62,6 +62,7 @@ public: { m_MuParser.DefineFun("ndvi", Self::NDVI); m_MuParser.DefineFun("NDVI", Self::NDVI); + m_MuParser.DefineFun("atan2", Self::ATAN2); #ifdef OTB_MUPARSER_HAS_CXX_LOGICAL_OPERATORS /* Starting with muParser 2.0.0, logical operators have been @@ -208,6 +209,11 @@ private: } return (niri-r)/(niri+r); } + + static ValueType ATAN2(ValueType y, ValueType x) + { + return vcl_atan2(y,x); + } #ifdef OTB_MUPARSER_HAS_CXX_LOGICAL_OPERATORS static ValueType AND(ValueType left, ValueType right) diff --git a/Modules/IO/ExtendedFilename/include/otbExtendedFilenameHelper.h b/Modules/IO/ExtendedFilename/include/otbExtendedFilenameHelper.h index 1f6db89a082f5aa59589929c7ca44c8a8060d110..b6d29171cd0ef81fd67ad3822c06366c417f2544 100644 --- a/Modules/IO/ExtendedFilename/include/otbExtendedFilenameHelper.h +++ b/Modules/IO/ExtendedFilename/include/otbExtendedFilenameHelper.h @@ -55,6 +55,30 @@ public: itkGetStringMacro(ExtendedFileName); itkGetStringMacro(SimpleFileName); + struct GenericBandRange : std::pair<int,int> + { + GenericBandRange() {} + + GenericBandRange(int a); + + GenericBandRange(const std::pair<int,int>& a); + + GenericBandRange(int a,int b); + + bool SetString(const std::string& str, size_t start=0 , size_t size=std::string::npos); + + void Print(std::ostream& os); + }; + + /** Decode the string into a list of GenericBandRange, band indexes are + * 1-based. */ + std::vector<ExtendedFilenameHelper::GenericBandRange> GetGenericBandRange(const std::string &bandRange) const; + + /** Resolve the list of band ranges into real band indexes, according to + * a total number of bands in the image. Note that the output indexes are + * zero-based (0 is the first component) */ + bool ResolveBandRange(const std::string &bandRange, const unsigned int &nbBands, std::vector<unsigned int> &output) const; + protected: ExtendedFilenameHelper() {} ~ExtendedFilenameHelper() ITK_OVERRIDE {} diff --git a/Modules/IO/ExtendedFilename/include/otbExtendedFilenameToReaderOptions.h b/Modules/IO/ExtendedFilename/include/otbExtendedFilenameToReaderOptions.h index 0abb00111d23e4945ed98be97556d81928316654..79c8bb00e0a7058d1a5531174254661deb47da50 100644 --- a/Modules/IO/ExtendedFilename/include/otbExtendedFilenameToReaderOptions.h +++ b/Modules/IO/ExtendedFilename/include/otbExtendedFilenameToReaderOptions.h @@ -35,6 +35,13 @@ namespace otb * - &resol : resolution factor for jpeg200 files * - &skipcarto : switch to skip the cartographic information * - &skipgeom : switch to skip the geometric information + * - &bands : select a band composition different from the input image, + * syntax is bands=r1,r2,r3,...,rn where each ri is a band range + * that can be : + * - a single index (1-based) : '2' means 2nd band, '-1' means last band + * - a range of bands : '3:' means 3rd band until the last one + * ':-2' means the first bands until the second to last + * '2:4' means bands 2,3 and 4 * * \sa ImageFileReader * @@ -67,6 +74,7 @@ public: std::pair< bool, bool > skipCarto; std::pair< bool, bool > skipGeom; std::pair< bool, bool > skipRpcTag; + std::pair< bool, std::string > bandRange; std::vector<std::string> optionList; }; @@ -86,6 +94,10 @@ public: bool GetSkipGeom () const; bool SkipRpcTagIsSet () const; bool GetSkipRpcTag () const; + std::string GetBandRange () const; + + /** Test if band range extended filename is set */ + bool BandRangeIsSet () const; protected: ExtendedFilenameToReaderOptions(); diff --git a/Modules/IO/ExtendedFilename/include/otbExtendedFilenameToWriterOptions.h b/Modules/IO/ExtendedFilename/include/otbExtendedFilenameToWriterOptions.h index b99b755b263aa1b32744e1c0913df706e32f25f2..81909d152832b2a6ef7f6c98333db05835a805b3 100644 --- a/Modules/IO/ExtendedFilename/include/otbExtendedFilenameToWriterOptions.h +++ b/Modules/IO/ExtendedFilename/include/otbExtendedFilenameToWriterOptions.h @@ -72,6 +72,7 @@ public: std::pair<bool, std::string> streamingSizeMode; std::pair<bool, double> streamingSizeValue; std::pair<bool, std::string> box; + std::pair< bool, std::string> bandRange; std::vector<std::string> optionList; }; @@ -91,10 +92,13 @@ public: std::string GetStreamingSizeMode() const; bool StreamingSizeValueIsSet() const; double GetStreamingSizeValue() const; + std::string GetBandRange () const; bool BoxIsSet() const; std::string GetBox() const; + /** Test if band range extended filename is set */ + bool BandRangeIsSet () const; protected: ExtendedFilenameToWriterOptions(); diff --git a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameHelper.cxx b/Modules/IO/ExtendedFilename/src/otbExtendedFilenameHelper.cxx index e6885ffbaf77ba027edbd53f5bbee5ce203cc479..e92e2b05fb6dc23643e42bd7b4b56d7f7a14e95b 100644 --- a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameHelper.cxx +++ b/Modules/IO/ExtendedFilename/src/otbExtendedFilenameHelper.cxx @@ -20,6 +20,8 @@ #include "otbExtendedFilenameHelper.h" #include "otb_boost_string_header.h" +#include "otbStringUtils.h" + namespace otb { @@ -43,32 +45,31 @@ ExtendedFilenameHelper { boost::split(tmp2, tmp1[1], boost::is_any_of("&"), boost::token_compress_on); for (unsigned int i=0; i<tmp2.size(); i++) - if (tmp2[i].length() >0) + if (!tmp2[i].empty()) { - std::vector<std::string> tmp; - boost::split(tmp, tmp2[i], boost::is_any_of("="), boost::token_compress_on); - if (tmp.size()>1) - { - if (tmp[1].length()>0) - { - if (m_OptionMap[tmp[0]].empty()) - { - m_OptionMap[tmp[0]]=tmp[1]; - } - else - { - itkWarningMacro("Duplicated option detected: " << tmp[0] << ". Using value " << tmp[1] << "."); - } - } - else - itkGenericExceptionMacro( << "Value for option '" << tmp[0] << "' is not set."); - } + std::vector<std::string> tmp; + boost::split(tmp, tmp2[i], boost::is_any_of("="), boost::token_compress_on); + if (tmp.size()>1) + { + if (!tmp[1].empty()) + { + if (m_OptionMap[tmp[0]].empty()) + { + m_OptionMap[tmp[0]]=tmp[1]; + } + else + { + itkWarningMacro("Duplicated option detected: " << tmp[0] << ". Using value " << tmp[1] << "."); + } + } + else + itkGenericExceptionMacro( << "Value for option '" << tmp[0] << "' is not set."); + } } } } } - const ExtendedFilenameHelper::OptionMapType & ExtendedFilenameHelper ::GetOptionMap(void) const @@ -76,4 +77,181 @@ ExtendedFilenameHelper return this->m_OptionMap; } +/*-------------------- GenericBandRange ----------------------*/ + +ExtendedFilenameHelper::GenericBandRange +::GenericBandRange(int a) + : std::pair<int,int>(a,a) + { + } + +ExtendedFilenameHelper::GenericBandRange +::GenericBandRange(const std::pair<int,int>& a) + : std::pair<int,int>(a) + { + if (a.second>=0 && a.second < a.first) + { + throw std::range_error("Invalid range"); + } + } + +ExtendedFilenameHelper::GenericBandRange +::GenericBandRange(int a,int b) + : std::pair<int,int>(a,b) + { + if (b>=0 && b < a) + { + throw std::range_error("Invalid range"); + } + } + +bool +ExtendedFilenameHelper::GenericBandRange +::SetString(const std::string& str, size_t start , size_t size) + { + assert(start < str.size()); + bool ret = true; + if (size == 0) + { + first = 0; + second = 0; + return false; + } + size_t end = str.size(); + if (size != std::string::npos && (start+size) <= str.size()) + { + end = start + size; + } + size_t pos = str.find(':',start); + if (pos != std::string::npos && pos<end) + { + // range of values + if (pos > start) + { + try + { + first = boost::lexical_cast<int>(str.c_str()+start, pos-start); + } + catch(boost::bad_lexical_cast &) + { + ret = false; + } + } + else + { + first = 0; + } + if (end > pos + 1) + { + try + { + second = boost::lexical_cast<int>(str.c_str()+ pos + 1, end - pos - 1); + } + catch(boost::bad_lexical_cast &) + { + ret = false; + } + } + else + { + second = 0; + } + } + else + { + // single value + try + { + first = boost::lexical_cast<int>(str.c_str()+start, end-start); + second = first; + } + catch(boost::bad_lexical_cast &) + { + ret = false; + } + } + return ret; + } + +void +ExtendedFilenameHelper::GenericBandRange +::Print(std::ostream& os) + { + if (this->first) + { + os << this->first; + } + if (this->first != this->second) + { + if (this->first || this->second) + { + os << ":"; + } + if (this->second) + { + os << this->second; + } + } + } + +std::vector<ExtendedFilenameHelper::GenericBandRange> +ExtendedFilenameHelper +::GetGenericBandRange(const std::string &bandRange) const +{ + //Parse string to return vector of band range + std::vector<ExtendedFilenameHelper::GenericBandRange> vBands; + size_t start = 0; + size_t pos; + if (!bandRange.empty()) + { + while (start != std::string::npos) + { + pos = bandRange.find(',',start); + if (pos > start) + { + ExtendedFilenameHelper::GenericBandRange range; + size_t size = (pos == std::string::npos ? pos : pos - start); + + bool ret = range.SetString(bandRange, start, size); + if (ret) vBands.push_back(range); + } + if (pos != std::string::npos) pos++; + start = pos; + } + } + return vBands; +} + +bool +ExtendedFilenameHelper +::ResolveBandRange(const std::string &bandRange, const unsigned int &nbBands, std::vector<unsigned int> &output) const +{ + output.clear(); + std::vector<ExtendedFilenameHelper::GenericBandRange> bandRangeList = this->GetGenericBandRange(bandRange); + for (unsigned int i=0 ; i<bandRangeList.size() ; i++) + { + int a = bandRangeList[i].first; + int b = bandRangeList[i].second; + if (a<0) a+= nbBands+1; + if (b<0) b+= nbBands+1; + if (a==0) a=1; + if (b==0) b=nbBands; + + if (1<=a && a<=b && b<=(int)nbBands) + { + for (unsigned int k=a ; k <= (unsigned int)b ; k++) + { + output.push_back((int)k -1); + } + } + else + { + // Invalid range wrt. the given number of bands + itkExceptionMacro("Invalid band number."); + return false; + } + } + return true; +} + } // end namespace otb diff --git a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToReaderOptions.cxx b/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToReaderOptions.cxx index de61847cf8e42dd06856b9d5f34f7182bb1ecbda..308f5239c1e9aeb4aaccd7066e08cfaca1e605bc 100644 --- a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToReaderOptions.cxx +++ b/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToReaderOptions.cxx @@ -20,6 +20,7 @@ #include "otbExtendedFilenameToReaderOptions.h" #include "otb_boost_string_header.h" +#include "itksys/RegularExpression.hxx" namespace otb { @@ -48,12 +49,16 @@ ExtendedFilenameToReaderOptions m_Options.skipRpcTag.first = false; m_Options.skipRpcTag.second = false; + m_Options.bandRange.first = false; + m_Options.bandRange.second = ""; + m_Options.optionList.push_back("geom"); m_Options.optionList.push_back("sdataidx"); m_Options.optionList.push_back("resol"); m_Options.optionList.push_back("skipcarto"); m_Options.optionList.push_back("skipgeom"); m_Options.optionList.push_back("skiprpctag"); + m_Options.optionList.push_back("bands"); } void @@ -124,6 +129,22 @@ ExtendedFilenameToReaderOptions } } + if (!map["bands"].empty()) + { + // Basic check on bandRange (using regex) + itksys::RegularExpression reg; + reg.compile("^((\\-?[0-9]+)?(:(\\-?[0-9]+)?)?)(,(\\-?[0-9]+)?(:(\\-?[0-9]+)?)?)*$"); + if (reg.find(map["bands"])) + { + m_Options.bandRange.first = true; + m_Options.bandRange.second = map["bands"]; + } + else + { + itkExceptionMacro("Unkwown value "<<map["bands"]<<" for band range. Expect a list of tokens separated with comma (each token being a single band index or a range in the form x:y)"); + } + } + //Option Checking MapIteratorType it; for ( it=map.begin(); it != map.end(); it++ ) @@ -224,4 +245,18 @@ ExtendedFilenameToReaderOptions return m_Options.skipRpcTag.second; } +bool +ExtendedFilenameToReaderOptions +::BandRangeIsSet () const +{ + return m_Options.bandRange.first; +} + +std::string +ExtendedFilenameToReaderOptions +::GetBandRange () const +{ + return m_Options.bandRange.second; +} + } // end namespace otb diff --git a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToWriterOptions.cxx b/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToWriterOptions.cxx index 305afc72ba4bdecd7b24fe511aaa02abdc83c0f0..b3413fdc53286ac0cbf322f76783edf10c4cdc6f 100644 --- a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToWriterOptions.cxx +++ b/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToWriterOptions.cxx @@ -43,12 +43,16 @@ ExtendedFilenameToWriterOptions m_Options.streamingSizeMode.first = false; m_Options.streamingSizeValue.first = false; + m_Options.bandRange.first = false; + m_Options.bandRange.second = ""; + m_Options.optionList.push_back("writegeom"); m_Options.optionList.push_back("writerpctags"); m_Options.optionList.push_back("streaming:type"); m_Options.optionList.push_back("streaming:sizemode"); m_Options.optionList.push_back("streaming:sizevalue"); m_Options.optionList.push_back("box"); + m_Options.optionList.push_back("bands"); } void @@ -158,6 +162,22 @@ ExtendedFilenameToWriterOptions } } + if (!map["bands"].empty()) + { + // Basic check on bandRange (using regex) + itksys::RegularExpression reg; + reg.compile("^((\\-?[0-9]+)?(:(\\-?[0-9]+)?)?)(,(\\-?[0-9]+)?(:(\\-?[0-9]+)?)?)*$"); + if (reg.find(map["bands"])) + { + m_Options.bandRange.first = true; + m_Options.bandRange.second = map["bands"]; + } + else + { + itkWarningMacro("Unkwown value "<<map["bands"]<<" for band range. Expect a list of tokens separated with comma (each token being a single band index or a range in the form x:y)"); + } + } + //Option Checking for ( it=map.begin(); it != map.end(); it++ ) { @@ -289,5 +309,18 @@ ExtendedFilenameToWriterOptions return m_Options.box.second; } +bool +ExtendedFilenameToWriterOptions +::BandRangeIsSet () const +{ + return m_Options.bandRange.first; +} + +std::string +ExtendedFilenameToWriterOptions +::GetBandRange () const +{ + return m_Options.bandRange.second; +} } // end namespace otb diff --git a/Modules/IO/ExtendedFilename/test/CMakeLists.txt b/Modules/IO/ExtendedFilename/test/CMakeLists.txt index d5a7cedb3b88e0d39be9bff42f84b6459493f28e..2ab3ae184c45597375b44deb46810c98cea1e835 100644 --- a/Modules/IO/ExtendedFilename/test/CMakeLists.txt +++ b/Modules/IO/ExtendedFilename/test/CMakeLists.txt @@ -58,7 +58,7 @@ otb_add_test(NAME ioTvExtendedFilenameToReaderOptions_FullOptions COMMAND otbExt ${BASELINE}/ioTvExtendedFilenameToReaderOptions_FullOptions.txt ${TEMP}/ioTvExtendedFilenameToReaderOptions_FullOptions.txt otbExtendedFilenameToReaderOptions - /home/data/filename.tif?&geom=/home/dev/custom.geom&sdataidx=2&resol=4&skipcarto=On + /home/data/filename.tif?&geom=/home/dev/custom.geom&sdataidx=2&resol=4&skipcarto=On&bands=-23,:3,45:,-6:-6,234:-5 ${TEMP}/ioTvExtendedFilenameToReaderOptions_FullOptions.txt ) @@ -71,6 +71,16 @@ otb_add_test(NAME ioTvExtendedFilenameToReaderOptions_NoOptions COMMAND otbExten ${TEMP}/ioTvExtendedFilenameToReaderOptions_NoOptions.txt ) +otb_add_test(NAME ioTvExtendedFilenameToReaderOptions_BandList COMMAND otbExtendedFilenameTestDriver + --compare-ascii ${NOTOL} + ${BASELINE}/ioTvExtendedFilenameToReaderOptions_BandList.txt + ${TEMP}/ioTvExtendedFilenameToReaderOptions_BandList.txt + otbExtendedFilenameToReaderOptions + /home/data/filename.tif?bands=3,5:,-3,2:-2 + ${TEMP}/ioTvExtendedFilenameToReaderOptions_BandList.txt + 6 + ) + otb_add_test(NAME ioTvExtendedFilenameToWriterOptions_FullOptions COMMAND otbExtendedFilenameTestDriver --compare-ascii ${NOTOL} ${BASELINE}/ioTvExtendedFilenameToWriterOptions_FullOptions.txt diff --git a/Modules/IO/ExtendedFilename/test/otbExtendedFilenameToReaderOptionsTest.cxx b/Modules/IO/ExtendedFilename/test/otbExtendedFilenameToReaderOptionsTest.cxx index 31aab027ebd40b4c64586fea29623318db311987..46ea10134688591fe3fc93bc77fffdec0223474b 100644 --- a/Modules/IO/ExtendedFilename/test/otbExtendedFilenameToReaderOptionsTest.cxx +++ b/Modules/IO/ExtendedFilename/test/otbExtendedFilenameToReaderOptionsTest.cxx @@ -24,7 +24,7 @@ typedef otb::ExtendedFilenameToReaderOptions FilenameHelperType; -int otbExtendedFilenameToReaderOptions(int itkNotUsed(argc), char* argv[]) +int otbExtendedFilenameToReaderOptions(int argc, char* argv[]) { // Verify the number of parameters in the command line const char * inputExtendedFilename = argv[1]; @@ -52,5 +52,38 @@ int otbExtendedFilenameToReaderOptions(int itkNotUsed(argc), char* argv[]) file << helper->SkipCartoIsSet() << std::endl; file << helper->GetSkipCarto() << std::endl; + file << helper->BandRangeIsSet() << std::endl; + file << "["; + + std::vector<otb::ExtendedFilenameHelper::GenericBandRange> rangeList = helper->GetGenericBandRange(helper->GetBandRange()); + for (unsigned int i=0 ; i<rangeList.size(); i++) + { + if (i) file << ","; + rangeList[i].Print(file); + } + file << "]" << std::endl; + + if (argc >= 4) + { + unsigned int nbBands = atoi(argv[3]); + std::vector<unsigned int> bandList; + bool ret = helper->ResolveBandRange(helper->GetBandRange(), nbBands,bandList); + if (ret) + { + file << "BandList = ["; + for (unsigned int k=0 ; k<bandList.size() ; k++) + { + if (k) file << ","; + file << bandList[k]; + } + file << "]" << std::endl; + } + else + { + std::cout << "Invalid band range for a "<<nbBands<<" bands image"<< std::endl; + } + } + + file.close(); return EXIT_SUCCESS; } diff --git a/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx b/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx index c06cdd83acf3bd09fd2cd55cedf2114258e950a2..968ec2a5fff7a52fb1aa9292a7b85aaf9a4728c6 100644 --- a/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx +++ b/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx @@ -1205,7 +1205,23 @@ void GDALImageIO::InternalReadImageInformation() if ((GDALGetRasterColorInterpretation(hBand) == GCI_PaletteIndex) && (hTable = GDALGetRasterColorTable(hBand)) != ITK_NULLPTR) { - m_IsIndexed = true; + + // Mantis: 1049 : OTB does not handle tif with NBITS=1 properly + // When a palette is available and pixel type is Byte, the image is + // automatically read as a color image (using the palette). Perhaps this + // behaviour should be restricted. Comment color table interpretation in + // gdalimageio + + // FIXME: Better support of color table in OTB + // - disable palette conversion in GDALImageIO (the comments in this part + // of the code are rather careful) + // - GDALImageIO should report the palette to ImageFileReader (as a metadata ? + // a kind of LUT ?). + // - ImageFileReader should use a kind of adapter filter to convert the mono + // image into color. + + // Do not set indexed image attribute to true + //m_IsIndexed = true; unsigned int ColorEntryCount = GDALGetColorEntryCount(hTable); diff --git a/Modules/IO/ImageIO/include/otbImageFileReader.h b/Modules/IO/ImageIO/include/otbImageFileReader.h index 928cef11e9be230153b5ab7b8c51bb0fc4c3c16d..daf6c58e800fc4c14bccb519edc9dcfc47400324 100644 --- a/Modules/IO/ImageIO/include/otbImageFileReader.h +++ b/Modules/IO/ImageIO/include/otbImageFileReader.h @@ -207,6 +207,15 @@ private: unsigned int m_AdditionalNumber; bool m_KeywordListUpToDate; + + /** Mapping between origin components and output components (before any + * conversion) */ + std::vector<unsigned int> m_BandList; + + /** Store the number of components to be exported to the output image + * This variable can be the number of components in m_ImageIO or the + * number of components in the m_BandList (if used) */ + unsigned int m_IOComponents; }; } //namespace otb diff --git a/Modules/IO/ImageIO/include/otbImageFileReader.txx b/Modules/IO/ImageIO/include/otbImageFileReader.txx index f0e30086dbd75a3425831cc5f531f478d4566893..cf647aac63f77a3945b60f8b48eb160d94d74779 100644 --- a/Modules/IO/ImageIO/include/otbImageFileReader.txx +++ b/Modules/IO/ImageIO/include/otbImageFileReader.txx @@ -69,7 +69,8 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> m_ActualIORegion(), m_FilenameHelper(FNameHelperType::New()), m_AdditionalNumber(0), - m_KeywordListUpToDate(false) + m_KeywordListUpToDate(false), + m_IOComponents(0) { } @@ -185,7 +186,8 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> if (this->m_ImageIO->GetComponentTypeInfo() == typeid(typename ConvertOutputPixelTraits::ComponentType) && (this->m_ImageIO->GetNumberOfComponents() - == ConvertIOPixelTraits::GetNumberOfComponents())) + == ConvertIOPixelTraits::GetNumberOfComponents()) + && !m_FilenameHelper->BandRangeIsSet()) { // Have the ImageIO read directly into the allocated buffer this->m_ImageIO->Read(buffer); @@ -197,19 +199,26 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> // regardless of the actual type of the pixels. ImageRegionType region = output->GetBufferedRegion(); - // Adapt the image size with the region - std::streamoff nbBytes = (this->m_ImageIO->GetComponentSize() * this->m_ImageIO->GetNumberOfComponents()) - * static_cast<std::streamoff>(region.GetNumberOfPixels()); + // Adapt the image size with the region and take into account a potential + // remapping of the components. m_BandList is empty if no band range is set + std::streamoff nbBytes = + ( this->m_ImageIO->GetComponentSize() + * std::max(this->m_ImageIO->GetNumberOfComponents(),(unsigned int) m_BandList.size())) + * static_cast<std::streamoff>(region.GetNumberOfPixels()); char * loadBuffer = new char[nbBytes]; - otbMsgDevMacro(<< "size of Buffer to GDALImageIO::read = " << nbBytes << " = \n" + otbMsgDevMacro(<< "buffer size for ImageIO::read = " << nbBytes << " = \n" << "ComponentSize ("<< this->m_ImageIO->GetComponentSize() << ") x " \ - << "Nb of Component (" << this->m_ImageIO->GetNumberOfComponents() << ") x " \ - << "Nb of Pixel to read (" << region.GetNumberOfPixels() << ")" ); + << "Nb of Component ( max(" << this->m_ImageIO->GetNumberOfComponents() \ + << " , "<<m_BandList.size() << ") ) x " \ + << "Nb of Pixel to read (" << region.GetNumberOfPixels() << ")"); this->m_ImageIO->Read(loadBuffer); + if (m_FilenameHelper->BandRangeIsSet()) + this->m_ImageIO->DoMapBuffer(loadBuffer, region.GetNumberOfPixels(), this->m_BandList); + this->DoConvertBuffer(loadBuffer, region.GetNumberOfPixels()); delete[] loadBuffer; @@ -534,13 +543,28 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> region.SetSize(dimSize); region.SetIndex(start); + // detect number of output components + m_IOComponents = this->m_ImageIO->GetNumberOfComponents(); + m_BandList.clear(); + if (m_FilenameHelper->BandRangeIsSet()) + { + bool ret = m_FilenameHelper->ResolveBandRange(m_FilenameHelper->GetBandRange(), m_IOComponents, m_BandList); + if (ret == false || m_BandList.size() == 0) + { + // invalid range + itkGenericExceptionMacro("The given band range is either empty or invalid for a " + <<m_IOComponents <<" bands input image!"); + } + m_IOComponents = m_BandList.size(); + } + // THOMAS : ajout // If a VectorImage, this requires us to set the // VectorLength before allocate if (strcmp(output->GetNameOfClass(), "VectorImage") == 0) { typedef typename TOutputImage::AccessorFunctorType AccessorFunctorType; - AccessorFunctorType::SetVectorLength(output, this->m_ImageIO->GetNumberOfComponents()); + AccessorFunctorType::SetVectorLength(output, m_IOComponents); } output->SetLargestPossibleRegion(region); @@ -808,7 +832,7 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> > \ ::ConvertVectorImage( \ static_cast<type*>(inputData), \ - m_ImageIO->GetNumberOfComponents(), \ + m_IOComponents, \ outputData, \ numberOfPixels); \ } \ @@ -821,7 +845,7 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> > \ ::Convert( \ static_cast<type*>(inputData), \ - m_ImageIO->GetNumberOfComponents(), \ + m_IOComponents, \ outputData, \ numberOfPixels); \ } \ @@ -843,7 +867,7 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> > \ ::ConvertComplexVectorImageToVectorImageComplex( \ static_cast<type*>(inputData), \ - m_ImageIO->GetNumberOfComponents(), \ + m_IOComponents, \ outputData, \ numberOfPixels); \ }\ @@ -856,7 +880,7 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> > \ ::ConvertComplexVectorImageToVectorImage( \ static_cast<type*>(inputData), \ - m_ImageIO->GetNumberOfComponents(), \ + m_IOComponents, \ outputData, \ numberOfPixels); \ }\ @@ -870,7 +894,7 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> > \ ::ConvertComplexToGray( \ static_cast<type*>(inputData), \ - m_ImageIO->GetNumberOfComponents(), \ + m_IOComponents, \ outputData, \ numberOfPixels); \ } \ @@ -921,6 +945,7 @@ ImageFileReader<TOutputImage, ConvertPixelTraits> #undef OTB_CONVERT_CBUFFER_IF_BLOCK } + } //namespace otb #endif diff --git a/Modules/IO/ImageIO/include/otbImageFileWriter.h b/Modules/IO/ImageIO/include/otbImageFileWriter.h index 47f64fb70eb57255d4e54afc00a25bac24dbdc56..b7bc6499b089a47cf7c75822523e56fb9cf819e0 100644 --- a/Modules/IO/ImageIO/include/otbImageFileWriter.h +++ b/Modules/IO/ImageIO/include/otbImageFileWriter.h @@ -261,6 +261,15 @@ private: bool m_IsObserving; unsigned long m_ObserverID; InputIndexType m_ShiftOutputIndex; + + /** Mapping between origin components and output components (before any + * conversion) */ + std::vector<unsigned int> m_BandList; + + /** Store the number of components to be exported to the output image + * This variable can be the number of components in m_ImageIO or the + * number of components in the m_BandList (if used) */ + unsigned int m_IOComponents; }; } // end namespace otb diff --git a/Modules/IO/ImageIO/include/otbImageFileWriter.txx b/Modules/IO/ImageIO/include/otbImageFileWriter.txx index da083b764e2bd349a217efb99df005dcabcb4fdc..3224f5a3860a47e2cb59c7d24d00296bb90747dd 100644 --- a/Modules/IO/ImageIO/include/otbImageFileWriter.txx +++ b/Modules/IO/ImageIO/include/otbImageFileWriter.txx @@ -69,7 +69,8 @@ ImageFileWriter<TInputImage> m_WriteGeomFile(false), m_FilenameHelper(), m_IsObserving(true), - m_ObserverID(0) + m_ObserverID(0), + m_IOComponents(0) { //Init output index shift m_ShiftOutputIndex.Fill(0); @@ -710,6 +711,19 @@ ImageFileWriter<TInputImage> typedef typename InputImageType::AccessorFunctorType AccessorFunctorType; m_ImageIO->SetNumberOfComponents(AccessorFunctorType::GetVectorLength(input)); + + m_IOComponents = m_ImageIO->GetNumberOfComponents(); + m_BandList.clear(); + if (m_FilenameHelper->BandRangeIsSet()) + { + // get band range + bool retBandRange = m_FilenameHelper->ResolveBandRange(m_FilenameHelper->GetBandRange(), m_IOComponents, m_BandList); + if (retBandRange == false || m_BandList.empty()) + { + // invalid range + itkGenericExceptionMacro("The given band range is either empty or invalid for a " << m_IOComponents <<" bands input image!"); + } + } } else { @@ -734,8 +748,10 @@ ImageFileWriter<TInputImage> Convert(m_ImageIO->GetIORegion(), ioRegion, m_ShiftOutputIndex); InputImageRegionType bufferedRegion = input->GetBufferedRegion(); - // before this test, bad stuff would happened when they don't match - if (bufferedRegion != ioRegion) + // before this test, bad stuff would happened when they don't match. + // In case of the buffer has not enough components, adapt the region. + if ((bufferedRegion != ioRegion) || (m_FilenameHelper->BandRangeIsSet() + && (m_IOComponents < m_BandList.size()))) { if ( m_NumberOfDivisions > 1 || m_UserSpecifiedIORegion) { @@ -744,9 +760,22 @@ ImageFileWriter<TInputImage> cacheImage = InputImageType::New(); cacheImage->CopyInformation(input); + + // set number of components at the band range size + if (m_FilenameHelper->BandRangeIsSet() && (m_IOComponents < m_BandList.size())) + { + cacheImage->SetNumberOfComponentsPerPixel(m_BandList.size()); + } + cacheImage->SetBufferedRegion(ioRegion); cacheImage->Allocate(); + // set number of components at the initial size + if (m_FilenameHelper->BandRangeIsSet() && (m_IOComponents < m_BandList.size())) + { + cacheImage->SetNumberOfComponentsPerPixel(m_IOComponents); + } + typedef itk::ImageRegionConstIterator<TInputImage> ConstIteratorType; typedef itk::ImageRegionIterator<TInputImage> IteratorType; @@ -777,6 +806,14 @@ ImageFileWriter<TInputImage> } } + if (m_FilenameHelper->BandRangeIsSet() && (!m_BandList.empty())) + { + // Adapt the image size with the region and take into account a potential + // remapping of the components. m_BandList is empty if no band range is set + m_ImageIO->DoMapBuffer(const_cast< void* >(dataPtr), bufferedRegion.GetNumberOfPixels(), this->m_BandList); + m_ImageIO->SetNumberOfComponents(m_BandList.size()); + } + m_ImageIO->Write(dataPtr); if (m_WriteGeomFile || m_FilenameHelper->GetWriteGEOMFile()) diff --git a/Modules/IO/ImageIO/test/CMakeLists.txt b/Modules/IO/ImageIO/test/CMakeLists.txt index fec74c10db2bd998c7509d4a064e7d63807fa246..d8374f8955993381a7db0634300990f485c57964 100644 --- a/Modules/IO/ImageIO/test/CMakeLists.txt +++ b/Modules/IO/ImageIO/test/CMakeLists.txt @@ -74,6 +74,8 @@ otbComplexImageManipulationTest.cxx otbImageFileWriterTest.cxx otbImageIOFactoryNew.cxx otbCompareWritingComplexImage.cxx +otbImageFileReaderOptBandTest.cxx +otbImageFileWriterOptBandTest.cxx ) add_executable(otbImageIOTestDriver ${OTBImageIOTests}) @@ -788,7 +790,7 @@ set_property(TEST ioTvImageFileReaderFloatHDR2LUM PROPERTY DEPENDS ioTvImageFile otb_add_test(NAME ioTvCheckNbBandsPNGIndexee COMMAND otbImageIOTestDriver otbPNGIndexedNbBandsTest ${INPUTDATA}/sbuv_indexee.png - 4 ) + 1 ) otb_add_test(NAME ioTvImageFileReaderENVI2PNG COMMAND otbImageIOTestDriver --compare-image ${EPSILON_9} ${INPUTDATA}/cthead1.png @@ -1301,3 +1303,39 @@ otb_add_test(NAME ioTvImageFileReaderPNG2ENVI COMMAND otbImageIOTestDriver otbImageFileReaderTest ${INPUTDATA}/cthead1.png ${TEMP}/ioImageFileReaderPNG2ENVI.hdr ) + +otb_add_test(NAME ioTvImageIOToReaderOptions_OptBandTest COMMAND otbImageIOTestDriver + --compare-image ${EPSILON_9} ${BASELINE}/QB_Toulouse_Ortho_XS_OptBand2to4.tif + ${TEMP}/QB_Toulouse_Ortho_XS_OptBand2to4.tif + otbImageFileReaderOptBandTest + ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif?bands=2:4 + ${TEMP}/QB_Toulouse_Ortho_XS_OptBand2to4.tif + 4 + ) + +otb_add_test(NAME ioTvImageIOToReaderOptions_OptBandReorgTest COMMAND otbImageIOTestDriver + --compare-image ${EPSILON_9} ${BASELINE}/QB_Toulouse_Ortho_XS_OptBandReorg.tif + ${TEMP}/QB_Toulouse_Ortho_XS_OptBandReorg.tif + otbImageFileReaderOptBandTest + ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif?bands=2,:,-3,2:-1 + ${TEMP}/QB_Toulouse_Ortho_XS_OptBandReorg.tif + 4 + ) + +otb_add_test(NAME ioTvImageIOToWriterOptions_OptBandTest COMMAND otbImageIOTestDriver + --compare-image ${EPSILON_9} ${BASELINE}/QB_Toulouse_Ortho_XS_OptBand2to4.tif + ${TEMP}/QB_Toulouse_Ortho_XS_WriterOptBand2to4.tif + otbImageFileWriterOptBandTest + ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif + ${TEMP}/QB_Toulouse_Ortho_XS_WriterOptBand2to4.tif?bands=2:4 + 4 + ) + +otb_add_test(NAME ioTvImageIOToWriterOptions_OptBandReorgTest COMMAND otbImageIOTestDriver + --compare-image ${EPSILON_9} ${BASELINE}/QB_Toulouse_Ortho_XS_OptBandReorg.tif + ${TEMP}/QB_Toulouse_Ortho_XS_WriterOptBandReorg.tif + otbImageFileWriterOptBandTest + ${INPUTDATA}/QB_Toulouse_Ortho_XS.tif + ${TEMP}/QB_Toulouse_Ortho_XS_WriterOptBandReorg.tif?bands=2,:,-3,2:-1 + 4 + ) diff --git a/Modules/IO/ImageIO/test/otbImageFileReaderOptBandTest.cxx b/Modules/IO/ImageIO/test/otbImageFileReaderOptBandTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5f308702b95f9df3c2cea8b3650c0ab85bdab715 --- /dev/null +++ b/Modules/IO/ImageIO/test/otbImageFileReaderOptBandTest.cxx @@ -0,0 +1,77 @@ +/*========================================================================= + + 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 "itkMacro.h" +#include <iostream> +#include <fstream> + +#include "otbVectorImage.h" +#include "otbImageFileReader.h" +#include "otbImageFileWriter.h" +#include "otbExtendedFilenameToReaderOptions.h" + +int otbImageFileReaderOptBandTest(int itkNotUsed(argc), char* argv[]) +{ + typedef otb::ExtendedFilenameToReaderOptions FilenameHelperType; + FilenameHelperType::Pointer helper = FilenameHelperType::New(); + + // Verify the number of parameters in the command line + const char * inputFilename = argv[1]; + const char * outputFilename = argv[2]; + + + + helper->SetExtendedFileName(inputFilename); + unsigned int nbBands = atoi(argv[3]); + std::vector<unsigned int> bandList; + bool ret = helper->ResolveBandRange(helper->GetBandRange(),nbBands,bandList); + if (ret) + { + std::cout << "BandList = ["; + for (unsigned int k=0 ; k<bandList.size() ; k++) + { + if (k) std::cout << ","; + std::cout << bandList[k]; + } + std::cout << "]" << std::endl; + } + else + { + std::cout << "Invalid band range for a "<<nbBands<<" bands image"<< std::endl; + } + + + typedef unsigned int PixelType; + const unsigned int Dimension = 2; + + typedef otb::VectorImage<PixelType, Dimension> ImageType; + + typedef otb::ImageFileReader<ImageType> ReaderType; + typedef otb::ImageFileWriter<ImageType> WriterType; + + ReaderType::Pointer reader = ReaderType::New(); + WriterType::Pointer writer = WriterType::New(); + + reader->SetFileName(inputFilename); + writer->SetFileName(outputFilename); + + writer->SetInput(reader->GetOutput()); + writer->Update(); + + return EXIT_SUCCESS; +} diff --git a/Modules/IO/ImageIO/test/otbImageFileWriterOptBandTest.cxx b/Modules/IO/ImageIO/test/otbImageFileWriterOptBandTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..66173cd05ab14aa1dce8135ff2300d56a6bbc2e2 --- /dev/null +++ b/Modules/IO/ImageIO/test/otbImageFileWriterOptBandTest.cxx @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2017 Centre National d'Etudes Spatiales (CNES) + * + * This file is part of Orfeo Toolbox + * + * https://www.orfeo-toolbox.org/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "otbImage.h" +#include "itkMacro.h" +#include <iostream> +#include <fstream> + +#include "otbVectorImage.h" +#include "otbImageFileReader.h" +#include "otbImageFileWriter.h" +#include "otbExtendedFilenameToWriterOptions.h" + +int otbImageFileWriterOptBandTest(int itkNotUsed(argc), char* argv[]) +{ + typedef otb::ExtendedFilenameToWriterOptions FilenameHelperType; + FilenameHelperType::Pointer helper = FilenameHelperType::New(); + + // Verify the number of parameters in the command line + const char * inputFilename = argv[1]; + const char * outputFilename = argv[2]; + + helper->SetExtendedFileName(outputFilename); + unsigned int nbBands = atoi(argv[3]); + std::vector<unsigned int> bandList; + bool ret = helper->ResolveBandRange(helper->GetBandRange(),nbBands,bandList); + if (ret) + { + std::cout << "BandList = ["; + for (unsigned int k=0 ; k<bandList.size() ; k++) + { + if (k) std::cout << ","; + std::cout << bandList[k]; + } + std::cout << "]" << std::endl; + } + else + { + std::cout << "Invalid band range for a "<<nbBands<<" bands image"<< std::endl; + } + + typedef unsigned int PixelType; + const unsigned int Dimension = 2; + + typedef otb::VectorImage<PixelType, Dimension> ImageType; + + typedef otb::ImageFileReader<ImageType> ReaderType; + typedef otb::ImageFileWriter<ImageType> WriterType; + + ReaderType::Pointer reader = ReaderType::New(); + WriterType::Pointer writer = WriterType::New(); + + reader->SetFileName(inputFilename); + writer->SetFileName(outputFilename); + + writer->SetInput(reader->GetOutput()); + writer->Update(); + + return EXIT_SUCCESS; +} diff --git a/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx b/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx index 7621e85c445ee20c7d79d4342535182635dc549d..fb2aa583aa73653ece1ffb9f523ae6caaec71d84 100644 --- a/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx +++ b/Modules/IO/ImageIO/test/otbImageIOTestDriver.cxx @@ -152,4 +152,6 @@ void RegisterTests() REGISTER_TEST(otbImageFileWriterTest); REGISTER_TEST(otbImageIOFactoryNew); REGISTER_TEST(otbCompareWritingComplexImageTest); + REGISTER_TEST(otbImageFileReaderOptBandTest); + REGISTER_TEST(otbImageFileWriterOptBandTest); } diff --git a/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx b/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx index 5d0ccbe17c48695077cefec52e2114ca9e0f659b..3ff4103c3405530dc02e89e81bd220529f2aa822 100644 --- a/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx +++ b/Modules/Learning/Supervised/test/otbSupervisedTestDriver.cxx @@ -19,7 +19,10 @@ */ #include "otbTestMain.h" + +#ifdef OTB_USE_OPENCV #include "otb_opencv_api.h" +#endif void RegisterTests() { diff --git a/Modules/Remote/otb-bv.remote.cmake b/Modules/Remote/otb-bv.remote.cmake index 7249a0443e46dc7e04d0c2fb0aeaa745133f3e5a..4816ae226460fae6d54424daa86edad2e8888ddd 100644 --- a/Modules/Remote/otb-bv.remote.cmake +++ b/Modules/Remote/otb-bv.remote.cmake @@ -25,5 +25,5 @@ A more detailed description can be found on the project website: http://tully.ups-tlse.fr/jordi/otb-bv " GIT_REPOSITORY http://tully.ups-tlse.fr/jordi/otb-bv.git - GIT_TAG d13a3b3febe61c3c67777eac8261e07a6257a519 + GIT_TAG master ) diff --git a/Modules/Remote/phenotb.remote.cmake b/Modules/Remote/phenotb.remote.cmake index 4ffba060cf56583604e568cfbe92664b85d0ecff..d76b4f1cc1b30d88f22aab7c036e413eeaf4c068 100644 --- a/Modules/Remote/phenotb.remote.cmake +++ b/Modules/Remote/phenotb.remote.cmake @@ -27,5 +27,5 @@ A more detailed description can be found on the project website: http://tully.ups-tlse.fr/jordi/phenotb " GIT_REPOSITORY http://tully.ups-tlse.fr/jordi/phenotb.git - GIT_TAG 17d69b1bc1f23041dafe265e320b46ffb20229b6 + GIT_TAG master ) diff --git a/Modules/Remote/temporal-gapfilling.remote.cmake b/Modules/Remote/temporal-gapfilling.remote.cmake index 0ef5cd29ba0af30cda3e5de29d57efe219c8608b..5570ceee08dc704ed1ea8e5897db93d88e03d711 100644 --- a/Modules/Remote/temporal-gapfilling.remote.cmake +++ b/Modules/Remote/temporal-gapfilling.remote.cmake @@ -26,5 +26,5 @@ A more detailed description can be found on the project website: http://tully.ups-tlse.fr/jordi/temporalgapfilling " GIT_REPOSITORY http://tully.ups-tlse.fr/jordi/temporalgapfilling.git - GIT_TAG 14c56cb73250861d8694effeba934cebde09424c + GIT_TAG master ) diff --git a/Modules/ThirdParty/OssimPlugins/src/ossim/ossimSentinel1Model.cpp b/Modules/ThirdParty/OssimPlugins/src/ossim/ossimSentinel1Model.cpp index 71fcec0deef817ca1dac61661422e4b0d98577fb..10ecc6080a69d32de8e12cd55f94b140ab8d50d1 100644 --- a/Modules/ThirdParty/OssimPlugins/src/ossim/ossimSentinel1Model.cpp +++ b/Modules/ThirdParty/OssimPlugins/src/ossim/ossimSentinel1Model.cpp @@ -298,11 +298,13 @@ namespace ossimplugins All these cases should not go slient on error message. It must be FATAL errors. */ - - ossimNotify(ossimNotifyLevel_FATAL) - << MODULE - << " !xmlFileName.exists() || !this->readProduct(xmlFileName) fails \n"; - return false; + if(traceDebug()) + { + ossimNotify(ossimNotifyLevel_DEBUG) + << MODULE + << " !xmlFileName.exists() || !this->readProduct(xmlFileName) fails \n"; + } + return false; } else { diff --git a/Modules/Visualization/Monteverdi/src/main.cxx b/Modules/Visualization/Monteverdi/src/main.cxx index feccb84f5e3451e40784f083693ea57d608cb1cc..e44dbb8459d0b239c8bc5c5283d5c41a6412d7ef 100644 --- a/Modules/Visualization/Monteverdi/src/main.cxx +++ b/Modules/Visualization/Monteverdi/src/main.cxx @@ -111,7 +111,7 @@ main( int argc, char* argv[] ) QCoreApplication::translate( PROJECT_NAME, "Usage: %1 [-h|--help] [-a|--applications] [<filename>...]\n" - " -1, --no-glsl force OpenGL 1.x compatible rendering." + " -n, --no-glsl force OpenGL 1.x compatible rendering." " -a, --applications load OTB-applications from OTB_APPLICATIONS_PATH." " -h, --help display this help message.\n" ) @@ -130,7 +130,7 @@ main( int argc, char* argv[] ) it = args.erase( it ); } - else if(it->compare( "-1" )==0 || + else if(it->compare( "-n" )==0 || it->compare( "--no-glsl" )==0 ) { flags.forceNoGLSL = true; diff --git a/Modules/Visualization/Monteverdi/src/mvdMainWindow.cxx b/Modules/Visualization/Monteverdi/src/mvdMainWindow.cxx index fada47696774b0d1bc6faaef8213a0abf146808a..84f7085b12557fd24f2d79a448ea5799071c3294 100644 --- a/Modules/Visualization/Monteverdi/src/mvdMainWindow.cxx +++ b/Modules/Visualization/Monteverdi/src/mvdMainWindow.cxx @@ -2515,8 +2515,8 @@ MainWindow text = tr( "(%1 %2 ; %3 %4 ; %5)" ) - .arg( wgs84[ 0 ]>=0.0 ? "N" : "S" ).arg( fabs( wgs84[ 1 ] ) ) - .arg( wgs84[ 1 ]>=0.0 ? "E" : "W" ).arg( fabs( wgs84[ 0 ] ) ) + .arg( wgs84[ 1 ]>=0.0 ? "N" : "S" ).arg( fabs( wgs84[ 1 ] ) ) + .arg( wgs84[ 0 ]>=0.0 ? "E" : "W" ).arg( fabs( wgs84[ 0 ] ) ) .arg( alt ); } diff --git a/Modules/Visualization/MonteverdiGui/include/mvdShaderWidget.h b/Modules/Visualization/MonteverdiGui/include/mvdShaderWidget.h index 971b0d941de3662f2a52dad377bcf9ee4ffbd4ad..4d1b55c63ca5c478c313f5f497fd3c67c37f77d9 100644 --- a/Modules/Visualization/MonteverdiGui/include/mvdShaderWidget.h +++ b/Modules/Visualization/MonteverdiGui/include/mvdShaderWidget.h @@ -146,6 +146,14 @@ private: /** */ void virtual_SetSettings( ImageSettings * ) ITK_OVERRIDE; + /** + * Set the ComboBox effects item for the corresponding image settings. + * This will append or remove effects (lut) depending if settings + * correspond to grayscale image or not. + * \param imageSettings Settings of the current image. + */ + void UpdateComboBoxEffectItems(ImageSettings *imageSettings) ; + // // Private attributes. private: diff --git a/Modules/Visualization/MonteverdiGui/include/mvdStatusBarWidget.h b/Modules/Visualization/MonteverdiGui/include/mvdStatusBarWidget.h index 714a100883da50fc0dad696e9b29a8ea4739e8b7..6ff51de45d94a3a60c3d4a89471e3d4d28f6ccd1 100644 --- a/Modules/Visualization/MonteverdiGui/include/mvdStatusBarWidget.h +++ b/Modules/Visualization/MonteverdiGui/include/mvdStatusBarWidget.h @@ -161,6 +161,11 @@ private: */ Ui::StatusBarWidget * m_UI; + /** + * \brief Change the scale when scaleLineEdit is pressed or editing is finished with change. + */ + void ChangeScale(); + /*-[ PRIVATE SLOTS SECTION ]-----------------------------------------------*/ // @@ -175,6 +180,9 @@ private slots: /** */ void on_pixelIndexLineEdit_returnPressed(); + /** + */ + void on_scaleLineEdit_returnPressed(); }; } // end namespace 'mvd' diff --git a/Modules/Visualization/MonteverdiGui/src/mvdShaderWidget.cxx b/Modules/Visualization/MonteverdiGui/src/mvdShaderWidget.cxx index 42dc9facf3e506fedb1d20cc17bb168e275aa476..36680eeaaeb232125c4fca2ff2c8c1c377176758 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdShaderWidget.cxx +++ b/Modules/Visualization/MonteverdiGui/src/mvdShaderWidget.cxx @@ -83,7 +83,7 @@ ShaderWidget m_UI->valueLineEdit->setValidator( new QDoubleValidator( m_UI->valueLineEdit ) ); -} +} /*******************************************************************************/ ShaderWidget @@ -128,18 +128,18 @@ void ShaderWidget { assert( qApp!=NULL ); - int index = - m_UI->effectComboBox->findText( - qApp->translate( "mvd", EFFECT_NAMES[ effect ] ) - ); + QString effectName = qApp->translate( "mvd", EFFECT_NAMES[ effect ] ); + int index = m_UI->effectComboBox->findText(effectName); if( visible ) { if( index<0 ) - m_UI->effectComboBox->addItem( qApp->translate( "mvd", EFFECT_NAMES[ effect ] ) ); + m_UI->effectComboBox->addItem( effectName ); } else if( index>=0 ) + { m_UI->effectComboBox->removeItem( index ); + } } /*******************************************************************************/ @@ -159,67 +159,45 @@ ShaderWidget { assert( qApp!=NULL ); - for( int i=0; i<m_UI->effectComboBox->count(); ++i ) - if( m_UI->effectComboBox->itemText( i ) - .compare( - qApp->translate( "mvd", - EFFECT_NAMES[ settings->GetEffect() ] ) )==0 ) - { - m_UI->effectComboBox->setCurrentIndex( i ); - - break; - } - - m_UI->sizeSpinBox->setValue( settings->GetSize() ); + const char *imageEffect = EFFECT_NAMES[settings->GetEffect()]; + QString tradEffect = qApp->translate( "mvd", imageEffect ); - char const * const text = settings->GetValueName(); + // Add or remove items from the comboBox + UpdateComboBoxEffectItems(settings); - if( text==NULL ) - { - m_UI->valueLabel->setVisible( false ); - m_UI->valueLabel->setText( QString() ); - } - else + for( int i = 0; i < m_UI->effectComboBox->count(); ++i ) { - m_UI->valueLabel->setVisible( true ); - m_UI->valueLabel->setText( QString( text ).append( ":" ) ); + QString comboBoxEffect = m_UI->effectComboBox->itemText( i ); + if( QString::compare( comboBoxEffect, tradEffect ) == 0 ) + { + // This change will emit currentIndexChanged SIGNAL + // and then call on_effectComboBox_currentIndexChanged + QString oldText = m_UI->effectComboBox->currentText(); + if(m_UI->effectComboBox->currentIndex() != i) + { + m_UI->effectComboBox->setCurrentIndex( i ); + } + break; + } } - - m_UI->valueLineEdit->setVisible( text!=NULL ); - m_UI->valueLineEdit->setText( - settings->HasValue() - ? QString::number( settings->GetValue(), 'g', std::numeric_limits< double >::digits10 ) // ToQString( settings->GetValue() ) - : QString() - ); - m_UI->valueLineEdit->setCursorPosition( 0 ); } - - VectorImageSettings * vis = dynamic_cast<VectorImageSettings*>(settings); - - if(vis!=NULL) - { - GrayscaleActivated(vis->IsGrayscaleActivated()); - } - -} + } /*******************************************************************************/ /* SLOTS */ /*******************************************************************************/ -void -ShaderWidget -::on_effectComboBox_currentIndexChanged( const QString & text ) +void ShaderWidget::on_effectComboBox_currentIndexChanged(const QString &text) { if( !HasSettings() ) return; - assert( qApp!=NULL ); + assert( qApp != NULL ); - for( int i=0; i<EFFECT_COUNT; ++i ) - if( QString::compare( text, qApp->translate( "mvd", EFFECT_NAMES[ i ] ) )==0 ) + for( int i = 0; i < EFFECT_COUNT; ++i ) + if( QString::compare( text, qApp->translate( "mvd", EFFECT_NAMES[i] ) ) == 0 ) { - ImageSettings * settings = GetSettings(); - assert( settings!=NULL ); + ImageSettings *settings = GetSettings(); + assert( settings != NULL ); settings->SetEffect( static_cast< Effect >( i ) ); @@ -228,29 +206,25 @@ ShaderWidget m_UI->sizeSpinBox->setValue( settings->GetSize() ); - char const * const textName = settings->GetValueName(); + char const *const textName = settings->GetValueName(); - if( textName==NULL ) - { - m_UI->valueLabel->setVisible( false ); - m_UI->valueLabel->setText( QString() ); - } + if( textName == NULL ) + { + m_UI->valueLabel->setVisible( false ); + m_UI->valueLabel->setText( QString() ); + } else - { - m_UI->valueLabel->setVisible( true ); - m_UI->valueLabel->setText( QString( textName ).append( ":" ) ); - } - - m_UI->valueLineEdit->setVisible( textName!=NULL ); - m_UI->valueLineEdit->setText( - settings->HasValue() - ? ToQString( settings->GetValue() ) - : QString() - ); + { + m_UI->valueLabel->setVisible( true ); + m_UI->valueLabel->setText( QString( textName ).append( ":" ) ); + } + + m_UI->valueLineEdit->setVisible( textName != NULL ); + m_UI->valueLineEdit->setText( settings->HasValue() ? ToQString( settings->GetValue() ) : QString() ); emit SettingsChanged(); - return; + break; } } @@ -294,7 +268,7 @@ ShaderWidget void ShaderWidget ::GrayscaleActivated(bool status) -{ +{ SetEffectVisible(EFFECT_LUT_JET,status); SetEffectVisible(EFFECT_LUT_LOCAL_JET,status); SetEffectVisible(EFFECT_LUT_HOT,status); @@ -308,4 +282,21 @@ void ShaderWidget SetEffectVisible(EFFECT_SPECTRAL_ANGLE,!status); } + +void ShaderWidget::UpdateComboBoxEffectItems(ImageSettings *imageSettings) +{ + if( imageSettings != NULL ) + { + VectorImageSettings *vis = dynamic_cast<VectorImageSettings *>(imageSettings); + if( vis != NULL ) + { + // Add Gray shader effect to the ComboBox if the image is grayscale. + // Update items in the comboBox but do not fire signal each time an item is removed. + bool oldState = m_UI->effectComboBox->blockSignals( true ); + GrayscaleActivated( vis->IsGrayscaleActivated() ); + m_UI->effectComboBox->blockSignals( oldState ); + } + } +} + } // end namespace 'mvd' diff --git a/Modules/Visualization/MonteverdiGui/src/mvdStatusBarWidget.cxx b/Modules/Visualization/MonteverdiGui/src/mvdStatusBarWidget.cxx index 358bc123cf9450e7ceb5fa881fc5317342a6dae1..6091f79e077190b7a0d00e963f3643b6bd0eb11e 100644 --- a/Modules/Visualization/MonteverdiGui/src/mvdStatusBarWidget.cxx +++ b/Modules/Visualization/MonteverdiGui/src/mvdStatusBarWidget.cxx @@ -217,9 +217,26 @@ StatusBarWidget } /*****************************************************************************/ +void +StatusBarWidget +::on_scaleLineEdit_returnPressed() +{ + ChangeScale(); +} + void StatusBarWidget ::on_scaleLineEdit_editingFinished() +{ + if(m_UI->scaleLineEdit->isModified()) + { + ChangeScale(); + } +} + +void +StatusBarWidget +::ChangeScale() { // // Cancel if scale text is empty. diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperChoiceParameter.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperChoiceParameter.h index da4d4537b7a30d7bcc9e227416878d15aa2e3dad..b376323214a31e02f6846b80d2aed4c7b126e6e3 100644 --- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperChoiceParameter.h +++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperChoiceParameter.h @@ -90,13 +90,14 @@ public: bool HasValue() const ITK_OVERRIDE { - // a choice parameter always has a value - return true; + return !m_ChoiceList.empty(); } void ClearValue() ITK_OVERRIDE { - // nothing to do : a choice parameter always has a value + // Same as constructor init value + // Note that this may be invalid if HasValue() == false + m_CurrentChoice = 0; } protected: diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx index c2d772d151c62a42a8ee3ba9a7e09a496518c66d..16f2a28637401ce72a634f4e4074a9f0bdf3bee9 100644 --- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx +++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx @@ -1015,19 +1015,26 @@ std::string Application::GetParameterString(std::string parameter) Parameter* param = GetParameterByKey(parameter); if (dynamic_cast<ChoiceParameter*>(param)) - { + { ChoiceParameter* paramDown = dynamic_cast<ChoiceParameter*>(param); - std::string choiceKey = paramDown->GetChoiceKey( paramDown->GetValue() ); - size_t lastPointPos = choiceKey.find_last_of('.'); - if(lastPointPos != std::string::npos) + if (paramDown->HasValue()) + { + std::string choiceKey = paramDown->GetChoiceKey( paramDown->GetValue() ); + size_t lastPointPos = choiceKey.find_last_of('.'); + if(lastPointPos != std::string::npos) { - ret = choiceKey.substr(lastPointPos); - } - else + ret = choiceKey.substr(lastPointPos); + } + else { - ret = choiceKey; + ret = choiceKey; } } + else + { + ret = ""; + } + } else if (dynamic_cast<ListViewParameter*>(param)) { ListViewParameter* paramDown = dynamic_cast<ListViewParameter*>(param);