/* * Copyright (C) 2005-2019 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. */ #ifndef otbTimeSeriesLSFF_h #define otbTimeSeriesLSFF_h #include "vnl/algo/vnl_matrix_inverse.h" #include "vnl/vnl_transpose.h" #include "vnl/vnl_matrix.h" namespace otb { namespace Functor { /** \class TimeSeriesLeastSquareFittingFunctor * \brief Implements a least squares fitting of a time profile * * This functor * implements a least squares fitting of a time profile. The time * series as well as the date series are supposed to accept the [] * syntax to get the values and the Size() method to get their * length. The fitting is performed using a time function which is * just a weighted sum of basis functions : \f$ f(t) = c_0 * * \phi_0(t) + c_1 * \phi_1(t) + c_1 * \phi_1(t) + \cdots \f$ Using * a matrix notation, this can be written as follows : * \f$ F = \Phi c \f$ * \f$ F = ( f(t_i) ) \f$ * \f$ c = ( c_j ) \f$ * \f$ \Phi_{ij} = ( \phi_j(t_i) ) \f$ * If information about the error * of each measure is available, it can be provided using a weight * series \f$\sigma_i\f$ (the higher the error, the higher the \f$ \sigma_i \f$. The problem then becomes: * \f$ b = A c \f$ * \f$ b = (\frac{ f(t_i) }{\sigma_i}) \f$ * \f$ A_{ij} = \frac{\Phi_{ij}}{\sigma_i} \f$ * * * * \ingroup OTBTimeSeries */ template class TimeSeriesLeastSquareFittingFunctor { public: typedef typename TTimeFunction::CoefficientsType CoefficientsType; /// Constructor TimeSeriesLeastSquareFittingFunctor() { for (unsigned int i = 0; i < m_WeightSeries.Size(); ++i) m_WeightSeries[i] = 1.0; } /// Destructor virtual ~TimeSeriesLeastSquareFittingFunctor() { } inline TSeriesType operator()(const TSeriesType& series) { TTimeFunction estFunction = this->EstimateTimeFunction(series); TSeriesType outSeries; for (unsigned int i = 0; i < m_DoySeries.Size(); ++i) outSeries[i] = estFunction.GetValue(m_DoySeries[i]); return outSeries; } inline void SetDates(const TDateType& doy) { for (unsigned int i = 0; i < doy.Size(); ++i) m_DoySeries[i] = doy[i]; } inline void SetWeights(const TWeightType& weights) { for (unsigned int i = 0; i < weights.Size(); ++i) m_WeightSeries[i] = weights[i]; } inline CoefficientsType GetCoefficients(const TSeriesType& series) const { return (this->EstimateTimeFunction(series)).GetCoefficients(); } inline TTimeFunction EstimateTimeFunction(const TSeriesType& series) const { TTimeFunction estFunction; unsigned int nbDates = m_DoySeries.Size(); unsigned int nbCoefs = estFunction.GetCoefficients().Size(); // b = A * c vnl_matrix A(nbDates, nbCoefs); vnl_matrix b(nbDates, 1); // fill the matrices typename TTimeFunction::CoefficientsType tmpCoefs; for (unsigned int j = 0; j < nbCoefs; ++j) tmpCoefs[j] = 0.0; for (unsigned int i = 0; i < nbDates; ++i) { b.put(i, 0, series[i] / m_WeightSeries[i]); for (unsigned int j = 0; j < nbCoefs; ++j) { tmpCoefs[j] = 1.0; estFunction.SetCoefficients(tmpCoefs); A.put(i, j, estFunction.GetValue(m_DoySeries[i]) / m_WeightSeries[i]); tmpCoefs[j] = 0.0; } } // solve the problem c = (At * A)^-1*At*b vnl_matrix atainv = vnl_matrix_inverse(vnl_transpose(A) * A); vnl_matrix atainvat = atainv * vnl_transpose(A); vnl_matrix c = atainvat * b; for (unsigned int j = 0; j < nbCoefs; ++j) tmpCoefs[j] = c.get(j, 0); estFunction.SetCoefficients(tmpCoefs); return estFunction; } private: /// TDateType m_DoySeries; TWeightType m_WeightSeries; }; } } // namespace otb #endif