/* * 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 otbNeuralNetworkMachineLearningModel_hxx #define otbNeuralNetworkMachineLearningModel_hxx #include #include "otbNeuralNetworkMachineLearningModel.h" #include "itkMacro.h" // itkExceptionMacro namespace otb { template NeuralNetworkMachineLearningModel::NeuralNetworkMachineLearningModel() : #ifdef OTB_OPENCV_3 m_ANNModel(cv::ml::ANN_MLP::create()), // TODO #else m_ANNModel(new CvANN_MLP), #endif m_TrainMethod(CvANN_MLP_TrainParams::RPROP), m_ActivateFunction(CvANN_MLP::SIGMOID_SYM), m_Alpha(1.), m_Beta(1.), m_BackPropDWScale(0.1), m_BackPropMomentScale(0.1), m_RegPropDW0(0.1), m_RegPropDWMin(FLT_EPSILON), m_TermCriteriaType(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS), m_MaxIter(1000), m_Epsilon(0.01) { this->m_ConfidenceIndex = true; this->m_IsRegressionSupported = true; } template NeuralNetworkMachineLearningModel::~NeuralNetworkMachineLearningModel() { #ifndef OTB_OPENCV_3 delete m_ANNModel; #endif } /** Sets the topology of the NN */ template void NeuralNetworkMachineLearningModel::SetLayerSizes(const std::vector layers) { const unsigned int nbLayers = layers.size(); if (nbLayers < 3) itkExceptionMacro(<< "Number of layers in the Neural Network must be >= 3") m_LayerSizes = layers; } /** Converts a ListSample of VariableLengthVector to a CvMat. The user * is responsible for freeing the output pointer with the * cvReleaseMat function. A null pointer is resturned in case the * conversion failed. */ template void NeuralNetworkMachineLearningModel::LabelsToMat(const TargetListSampleType* labels, cv::Mat& output) { unsigned int nbSamples = 0; if (labels != nullptr) { nbSamples = labels->Size(); } // Check for valid listSample if (nbSamples > 0) { // Build an iterator typename TargetListSampleType::ConstIterator labelSampleIt = labels->Begin(); TargetValueType classLabel; for (; labelSampleIt != labels->End(); ++labelSampleIt) { // Retrieve labelSample typename TargetListSampleType::MeasurementVectorType labelSample = labelSampleIt.GetMeasurementVector(); classLabel = labelSample[0]; if (m_MapOfLabels.count(classLabel) == 0) { m_MapOfLabels[classLabel] = -1; } } unsigned int nbClasses = m_MapOfLabels.size(); m_MatrixOfLabels = cv::Mat(1,nbClasses, CV_32FC1); unsigned int itLabel = 0; for (auto& kv : m_MapOfLabels) { classLabel = kv.first; kv.second = itLabel; m_MatrixOfLabels.at(0,itLabel) = classLabel; ++itLabel; } // Sample index unsigned int sampleIdx = 0; labelSampleIt = labels->Begin(); output.create(nbSamples, nbClasses, CV_32FC1); output.setTo(-m_Beta); // Fill the cv matrix for (; labelSampleIt != labels->End(); ++labelSampleIt, ++sampleIdx) { // Retrieve labelSample typename TargetListSampleType::MeasurementVectorType labelSample = labelSampleIt.GetMeasurementVector(); classLabel = labelSample[0]; unsigned int indexLabel = m_MapOfLabels[classLabel]; output.at(sampleIdx, indexLabel) = m_Beta; } } } template void NeuralNetworkMachineLearningModel::CreateNetwork() { // Create the neural network const unsigned int nbLayers = m_LayerSizes.size(); if (nbLayers == 0) itkExceptionMacro(<< "Number of layers in the Neural Network must be >= 3") cv::Mat layers = cv::Mat(nbLayers, 1, CV_32SC1); for (unsigned int i = 0; i < nbLayers; i++) { layers.row(i) = m_LayerSizes[i]; } #ifdef OTB_OPENCV_3 m_ANNModel->setLayerSizes(layers); m_ANNModel->setActivationFunction(m_ActivateFunction, m_Alpha, m_Beta); #else m_ANNModel->create(layers, m_ActivateFunction, m_Alpha, m_Beta); #endif } #ifndef OTB_OPENCV_3 template CvANN_MLP_TrainParams NeuralNetworkMachineLearningModel::SetNetworkParameters() { CvANN_MLP_TrainParams params; params.train_method = m_TrainMethod; params.bp_dw_scale = m_BackPropDWScale; params.bp_moment_scale = m_BackPropMomentScale; params.rp_dw0 = m_RegPropDW0; params.rp_dw_min = m_RegPropDWMin; CvTermCriteria term_crit = cvTermCriteria(m_TermCriteriaType, m_MaxIter, m_Epsilon); params.term_crit = term_crit; return params; } #endif template void NeuralNetworkMachineLearningModel::SetupNetworkAndTrain(cv::Mat& labels) { // convert listsample to opencv matrix cv::Mat samples; otb::ListSampleToMat(this->GetInputListSample(), samples); this->CreateNetwork(); #ifdef OTB_OPENCV_3 int flags = (this->m_RegressionMode ? 0 : cv::ml::ANN_MLP::NO_OUTPUT_SCALE); m_ANNModel->setTrainMethod(m_TrainMethod); m_ANNModel->setBackpropMomentumScale(m_BackPropMomentScale); m_ANNModel->setBackpropWeightScale(m_BackPropDWScale); m_ANNModel->setRpropDW0(m_RegPropDW0); // m_ANNModel->setRpropDWMax( ); m_ANNModel->setRpropDWMin(m_RegPropDWMin); // m_ANNModel->setRpropDWMinus( ); // m_ANNModel->setRpropDWPlus( ); m_ANNModel->setTermCriteria(cv::TermCriteria(m_TermCriteriaType, m_MaxIter, m_Epsilon)); m_ANNModel->train(cv::ml::TrainData::create(samples, cv::ml::ROW_SAMPLE, labels), flags); #else CvANN_MLP_TrainParams params = this->SetNetworkParameters(); // train the Neural network model m_ANNModel->train(samples, labels, cv::Mat(), cv::Mat(), params); #endif } /** Train the machine learning model for classification*/ template void NeuralNetworkMachineLearningModel::Train() { // Transform the targets into a matrix of labels cv::Mat matOutputANN; if (this->m_RegressionMode) { // MODE REGRESSION otb::ListSampleToMat(this->GetTargetListSample(), matOutputANN); } else { // MODE CLASSIFICATION : store the map between internal labels and output labels LabelsToMat(this->GetTargetListSample(), matOutputANN); } this->SetupNetworkAndTrain(matOutputANN); } template typename NeuralNetworkMachineLearningModel::TargetSampleType NeuralNetworkMachineLearningModel::DoPredict(const InputSampleType& input, ConfidenceValueType* quality, ProbaSampleType* proba) const { TargetSampleType target; // convert listsample to Mat cv::Mat sample; otb::SampleToMat(input, sample); cv::Mat response; //(1, 1, CV_32FC1); m_ANNModel->predict(sample, response); float currentResponse = 0; float maxResponse = response.at(0, 0); if (this->m_RegressionMode) { // MODE REGRESSION : only output first response target[0] = maxResponse; return target; } // MODE CLASSIFICATION : find the highest response float secondResponse = -1e10; target[0] = m_MatrixOfLabels.at(0); unsigned int nbClasses = m_MatrixOfLabels.size[1]; for (unsigned itLabel = 1; itLabel < nbClasses; ++itLabel) { currentResponse = response.at(0, itLabel); if (currentResponse > maxResponse) { secondResponse = maxResponse; maxResponse = currentResponse; target[0] = m_MatrixOfLabels.at(itLabel); } else { if (currentResponse > secondResponse) { secondResponse = currentResponse; } } } if (quality != nullptr) { (*quality) = static_cast(maxResponse) - static_cast(secondResponse); } if (proba != nullptr && !this->m_ProbaIndex) itkExceptionMacro("Probability per class not available for this classifier !"); return target; } template void NeuralNetworkMachineLearningModel::Save(const std::string& filename, const std::string& name) { #ifdef OTB_OPENCV_3 cv::FileStorage fs(filename, cv::FileStorage::WRITE); fs << (name.empty() ? m_ANNModel->getDefaultName() : cv::String(name)) << "{"; m_ANNModel->write(fs); if (!m_MatrixOfLabels.empty()) { fs << "class_labels" << m_MatrixOfLabels; } fs << "}"; fs.release(); #else const char* lname = "my_nn"; if (!name.empty()) lname = name.c_str(); CvFileStorage* fs = nullptr; fs = cvOpenFileStorage(filename.c_str(), nullptr, CV_STORAGE_WRITE); if (!fs) { itkExceptionMacro("Could not open the file " << filename << " for writing"); } m_ANNModel->write(fs, lname); if (!m_MatrixOfLabels.empty()) { // cvWrite can't write cv::Mat auto tmpMat = CvMat(m_MatrixOfLabels); cvWrite(fs, "class_labels", &tmpMat); } cvReleaseFileStorage(&fs); #endif } template void NeuralNetworkMachineLearningModel::Load(const std::string& filename, const std::string& name) { #ifdef OTB_OPENCV_3 cv::FileStorage fs(filename, cv::FileStorage::READ); cv::FileNode model_node(name.empty() ? fs.getFirstTopLevelNode() : fs[name]); m_ANNModel->read(model_node); model_node["class_labels"] >> m_MatrixOfLabels; fs.release(); #else const char* lname = nullptr; if (!name.empty()) lname = name.c_str(); cv::FileNode model_node; cv::FileStorage fs(filename, cv::FileStorage::READ); if (!fs.isOpened()) { itkExceptionMacro("Could not open the file " << filename << " for reading"); } if (lname) model_node = fs[lname]; else { cv::FileNode root = fs.root(); if (root.size() > 0) { model_node = *(root.begin()); } } m_ANNModel->read(*fs,*model_node); model_node["class_labels"] >> m_MatrixOfLabels; fs.release(); #endif } template bool NeuralNetworkMachineLearningModel::CanReadFile(const std::string& file) { std::ifstream ifs; ifs.open(file); if (!ifs) { std::cerr << "Could not read file " << file << std::endl; return false; } while (!ifs.eof()) { std::string line; std::getline(ifs, line); if (line.find(CV_TYPE_NAME_ML_ANN_MLP) != std::string::npos #ifdef OTB_OPENCV_3 || line.find(m_ANNModel->getDefaultName()) != std::string::npos #endif ) { return true; } } ifs.close(); return false; } template bool NeuralNetworkMachineLearningModel::CanWriteFile(const std::string& itkNotUsed(file)) { return false; } template void NeuralNetworkMachineLearningModel::PrintSelf(std::ostream& os, itk::Indent indent) const { // Call superclass implementation Superclass::PrintSelf(os, indent); } } // end namespace otb #endif