RepositoryExternal.mk | 15 + config_host.mk.in | 2 configure.ac | 6 cui/Library_cui.mk | 4 cui/source/dialogs/cuicharmap.cxx | 386 ++++++++++++++++++++++++++- cui/source/factory/neuralnetwork.cxx | 37 ++ cui/source/factory/neuralnetworkfann.cxx | 133 +++++++++ cui/source/factory/neuralnetworkfann.hxx | 58 ++++ cui/source/factory/neuralnetworkinternal.cxx | 360 +++++++++++++++++++++++++ cui/source/inc/cuicharmap.hxx | 23 + cui/source/inc/neuralnetwork.hxx | 55 +++ download.lst | 2 12 files changed, 1080 insertions(+), 1 deletion(-)
New commits: commit 56bfe8ecdc5f3e41ea8ce84ef4e9b54c86f1e2ec Author: Vincent Le Garrec <legarrec.vinc...@gmail.com> Date: Tue Jan 16 22:15:44 2018 +0100 First try to make a NN implementation It doesn't work but a good work has been done. Change-Id: I09d9ed910ef8a905fa0f3e0a689b741006f6fd18 diff --git a/cui/source/factory/neuralnetwork.cxx b/cui/source/factory/neuralnetwork.cxx index 5651b80b3ab8..cd08d084b7f4 100644 --- a/cui/source/factory/neuralnetwork.cxx +++ b/cui/source/factory/neuralnetwork.cxx @@ -20,15 +20,18 @@ #include <neuralnetwork.hxx> #include "neuralnetworkfann.hxx" +#include "neuralnetworkinternal.hxx" AbstractNeuralNetwork * AbstractNeuralNetwork::CreateFactory(sal_uInt32 nLayers, const sal_uInt32* nLayer) { - return new NeuralNetworkFann(nLayers, nLayer); +// return new NeuralNetworkFann(nLayers, nLayer); + return new NeuralNetworkInternal(nLayers, nLayer); } AbstractNeuralNetwork * AbstractNeuralNetwork::CreateFactory(const OUString& file) { - return new NeuralNetworkFann(file); +// return new NeuralNetworkFann(file); + return new NeuralNetworkInternal(file); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/factory/neuralnetworkfann.cxx b/cui/source/factory/neuralnetworkfann.cxx index e622916c1ccf..2366989e7d8b 100644 --- a/cui/source/factory/neuralnetworkfann.cxx +++ b/cui/source/factory/neuralnetworkfann.cxx @@ -39,6 +39,11 @@ void NeuralNetworkFann::SetActivationFunction(ActivationFunction function) { case ActivationFunction::SIGMOID: { + func = FANN_SIGMOID; + break; + } + case ActivationFunction::SIGMOID_SYMMETRIC: + { func = FANN_SIGMOID_SYMMETRIC; break; } @@ -75,8 +80,6 @@ void NeuralNetworkFann::SetLearningRate(float rate) fann_set_learning_rate(ann.get(), rate); } -#include <iostream> - void NeuralNetworkFann::InitTraining(sal_uInt32 nExamples) { data.reset(fann_create_train(nExamples, fann_get_num_input(ann.get()), fann_get_num_output(ann.get()))); @@ -108,9 +111,18 @@ float* NeuralNetworkFann::GetOutput(sal_uInt32 nIeme) return &data->output[nIeme][0]; } -float* NeuralNetworkFann::Run(float *data_input) +void NeuralNetworkFann::Run(float *data_input, float *result) { - return fann_run(ann.get(), data_input); + float * fann_result = fann_run(ann.get(), data_input); + + if (result == nullptr) + { + return; + } + for (sal_uInt32 i = 0; i < GetNumOutput(); i++) + { + result[i] = fann_result[i]; + } } void NeuralNetworkFann::Train(sal_uInt32 nEpochs, float error) diff --git a/cui/source/factory/neuralnetworkfann.hxx b/cui/source/factory/neuralnetworkfann.hxx index 472f4640dde3..993d78c0f3d2 100644 --- a/cui/source/factory/neuralnetworkfann.hxx +++ b/cui/source/factory/neuralnetworkfann.hxx @@ -41,11 +41,13 @@ public: float* GetOutput(sal_uInt32 nIeme) override; void Train(sal_uInt32 nEpochs, float error) override; - float* Run(float *data_input) override; + void Run(float *data_input, float* result) override; void Save(const OUString& file) override; virtual ~NeuralNetworkFann(){} + virtual void * GetTrain(){return data.get();} + private: std::unique_ptr<struct fann, void(*)(struct fann *)> ann; std::unique_ptr<struct fann_train_data, void(*)(struct fann_train_data *)> data; diff --git a/cui/source/factory/neuralnetworkinternal.cxx b/cui/source/factory/neuralnetworkinternal.cxx index 52597c57ca92..087f9c1c6d55 100644 --- a/cui/source/factory/neuralnetworkinternal.cxx +++ b/cui/source/factory/neuralnetworkinternal.cxx @@ -17,4 +17,344 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include "neuralnetworkinternal.hxx" + +#include <iostream> +#include <fstream> + +NeuralNetworkInternal::NeuralNetworkInternal(sal_uInt32 nLayers, const sal_uInt32* nLayer) : + n(nLayers, std::vector<Neuron>()), + learning_rate(0.2), + learning_rate_alpha(0.4), + learning_input(), + learning_output() +{ + for (sal_uInt32 i = 0; i < nLayers; i++) + { + n[i].resize(nLayer[i]+1); // One neuron is biais. + + if (i != nLayers-1) + { + for (sal_uInt32 j = 0; j < n[i].size() /*with biais*/; j++) + { + n[i][j].w.resize(nLayer[i+1]); /*no biais*/ + n[i][j].dw.resize(nLayer[i+1]); + for (sal_uInt32 k = 0; k < nLayer[i+1]; k++) + { + // Between [0;.1] + n[i][j].w[k] = (float)std::rand() / (float)RAND_MAX *0.2 - 0.1; + } + } + } + } +} + +NeuralNetworkInternal::NeuralNetworkInternal(const OUString& file) : + n(), learning_rate(0.), learning_rate_alpha(0.), learning_input(), + learning_output() +{ + OString o = OUStringToOString(file, RTL_TEXTENCODING_ASCII_US); + const char * o_str = o.pData->buffer; + + std::ifstream infile (o_str, std::ifstream::binary); + sal_uInt32 tmpInt; + + infile.read(reinterpret_cast<char *>(&learning_rate), sizeof(learning_rate)); + infile.read(reinterpret_cast<char *>(&learning_rate_alpha), sizeof(learning_rate_alpha)); + infile.read(reinterpret_cast<char *>(&tmpInt), sizeof(tmpInt)); + n.resize(tmpInt); + + for (sal_uInt32 i = 0; i < n.size(); i++) + { + infile.read(reinterpret_cast<char *>(&tmpInt), sizeof(tmpInt)); + n[i].resize(tmpInt); + } + + for (sal_uInt32 i = 0; i < n.size(); i++) + { + if (i != n.size()-1) + { + for (sal_uInt32 j = 0; j < n[i].size(); j++) + { + n[i][j].w.resize(n[i+1].size()); + n[i][j].dw.resize(n[i+1].size()); + } + } + } + + for (sal_uInt32 i = 0; i < n.size(); i++) + for (sal_uInt32 j = 0; j < n[i].size(); j++) + { + infile.read(reinterpret_cast<char *>(&n[i][j].w[0]), n[i][j].w.size()*sizeof(n[i][j].w[0])); + infile.read(reinterpret_cast<char *>(&n[i][j].dw[0]), n[i][j].dw.size()*sizeof(n[i][j].dw[0])); + } + + for (sal_uInt32 i = 0; i < n.size(); i++) + for (sal_uInt32 j = 0; j < n[i].size(); j++) + { + infile.read(reinterpret_cast<char *>(&n[i][j].f), sizeof(n[i][j].f)); + infile.read(reinterpret_cast<char *>(&n[i][j].stp), sizeof(n[i][j].stp)); + } +} + +void NeuralNetworkInternal::SetActivationFunction(ActivationFunction function) +{ + FunctionTrans func; + + switch (function) + { + case ActivationFunction::SIGMOID: + { + func = FunctionTrans::Sigmoid; + break; + } + case ActivationFunction::SIGMOID_SYMMETRIC: + { + func = FunctionTrans::SigmoidSymmetric; + break; + } + default: + { + return; + } + } + + for (sal_uInt32 i = 1; i < n.size(); i++) + { + for (sal_uInt32 j = 0; j < n[i].size(); j++) + { + n[i][j].f = func; + } + } +} + +void NeuralNetworkInternal::SetTrainingAlgorithm(TrainingAlgorithm algorithm) +{ + return; +} + +void NeuralNetworkInternal::SetLearningRate(float rate) +{ + learning_rate = rate; +} + +void NeuralNetworkInternal::InitTraining(sal_uInt32 nExamples) +{ + learning_input.resize(nExamples, std::vector<float>(n[0].size(), 0)); + learning_output.resize(nExamples, std::vector<float>(n.back().size(), 0)); +} + +sal_uInt32 NeuralNetworkInternal::GetNumInput() +{ + return n[0].size(); +} + +float* NeuralNetworkInternal::GetInput(sal_uInt32 nIeme) +{ + if (nIeme >= learning_input.size()) + return nullptr; + return &learning_input[nIeme][0]; +} + +sal_uInt32 NeuralNetworkInternal::GetNumOutput() +{ + return n.back().size(); +} + +float* NeuralNetworkInternal::GetOutput(sal_uInt32 nIeme) +{ + if (nIeme >= learning_output.size()) + return nullptr; + return &learning_output[nIeme][0]; +} + +void NeuralNetworkInternal::Train(sal_uInt32 nEpochs, float error) +{ + for (sal_uInt32 l = 0; l < nEpochs; l++) + { + for (sal_uInt32 m = 0; m < learning_input.size(); m++) + { + Run(&learning_input[m][0], nullptr); + + if (l == nEpochs-1) + { + for (sal_uInt32 i = 0; i < n.back().size(); i++) + std::cout << n.back()[i].a << " "; + std::cout << std::endl; + } + + // error + float errori = 0.f; + for (sal_uInt32 i = 0; i < n.back().size()-1 /*no biais*/; i++) + { + errori += (learning_input[m][i] - n.back()[i].a)*(learning_input[m][i] - n.back()[i].a); + } + errori = errori / (n.back().size()-1); + errori = sqrt(errori); + std::cout << "error " << l << "," << m << ":" << errori << std::endl; + + // Calcul de sM + // s: ann->train_errors + for (sal_uInt32 i = 0; i < n.back().size()-1 /*no biais*/; i++) + { + float diff = learning_output[m][i] - n.back()[i].a; + switch (n.back()[i].f) + { + case FunctionTrans::Sigmoid: + { + n.back()[i].s = 2.*n.back()[i].stp*n.back()[i].a*(1.-n.back()[i].a)*diff; + break; + } + case FunctionTrans::SigmoidSymmetric: + { + // If Symmetric: diff/2. + diff = diff / 2.; + n.back()[i].s = n.back()[i].stp*(1.-n.back()[i].a*n.back()[i].a)*diff; + break; + } + + /* +ftLineaire : RNA.s[High(RNA.s)][I] := -2*(SortiesTest[I] - RNA.a[High(RNA.a)][I]); + +ftTanHyperb : RNA.s[High(RNA.s)][I] := -2*(1-RNA.a[High(RNA.a)][I]*RNA.a[High(RNA.a)][I])* + +(SortiesTest[I] - RNA.a[High(RNA.a)][I]) +*/ + } + } + + // Calcul des sk + // fann_activation_derived + for (sal_uInt32 i = n.size()-2 /*No Output layer*/; i > 0; i--) + { + for (sal_uInt32 j = 0; j < n[i].size() /*with biais*/; j++) + { + float sum = 0.f; + //connection_rate >= 1 + for (sal_uInt32 k = 0; k < n[i+1].size()-1 /*no biais*/; k++) + { + sum += n[i][j].w[k] * n[i+1][k].s; + } + switch (n[i][j].f) + { + case FunctionTrans::Sigmoid: + { + n[i][j].s = sum * 2. * n[i][j].stp * n[i][j].a * (1. - n[i][j].a); + break; + } + case FunctionTrans::SigmoidSymmetric: + { + n[i][j].s = sum * n[i][j].stp * (1. - n[i][j].a*n[i][j].a); + break; + } + default: + { + return; + } + + // Cette ligne est volontairement commentée car pour une fonction linéaire, + // il aurait fallu mettre RNA.s[I][J] := RNA.s[I][J] * 1, ce qui ne sert à rien + // ftLineaire : RNA.s[I][J] := RNA.s[I][J] * 1; + + // ftTanHyperb : RNA.s[I][J] := RNA.s[I][J] * (1 - RNA.a[I][J]*RNA.a[I][J]); + } + } + } + + // Modifications des poids et des biais + for (sal_uInt32 i = n.size()-1 /*No input layer*/; i > 0; --i) + { + for (sal_uInt32 j = 0; j < n[i].size() - 1 /*no biais*/; j++) + { + for (sal_uInt32 k = 0; k < n[i-1].size() /*with biais*/; k++) + { + float delta = learning_rate * n[i][j].s * n[i-1][k].a + learning_rate_alpha * n[i-1][k].dw[j]; + n[i-1][k].dw[j] = delta; + n[i-1][k].w[j] += delta; + } + } + } + } + } +} + +void NeuralNetworkInternal::Run(float *data_input, float *result) +{ + // Calcul de a0 + for (sal_uInt32 i = 0; i < n[0].size(); i++) + n[0][i].a = data_input[i]; + + // Calcul des ak + + for (sal_uInt32 i = 1; i < n.size(); i++) + { + for (sal_uInt32 j = 0; j < n[i].size()-1 /*no biais*/; j++) + { + n[i][j].sum = 0; + for (sal_uInt32 k = 0; k < n[i-1].size() /*with biais*/; k++) + n[i][j].sum += n[i-1][k].a * n[i-1][k].w[j]; + n[i][j].sum *= n[i][j].stp; + switch (n[i][j].f) + { + case FunctionTrans::Sigmoid: + { + n[i][j].a = 1.f / (1.f + exp(-2.f*n[i][j].sum)); + break; + } + case FunctionTrans::SigmoidSymmetric: + { + n[i][j].a = 2.f/(1.f + exp(-2.f/n[i][j].sum)) - 1.f; + break; + } + default: + { + return; + } + } + } + } + + + if (result == nullptr) + { + return; + } + for (sal_uInt32 i = 0; i < n.back().size(); i++) + result[i] = n.back()[i].a; +} + +void NeuralNetworkInternal::Save(const OUString& file) +{ + OString o = OUStringToOString(file, RTL_TEXTENCODING_ASCII_US); + const char * o_str = o.pData->buffer; + + std::ofstream outfile (o_str, std::ofstream::binary); + + outfile.write(reinterpret_cast<const char *>(&learning_rate), sizeof(learning_rate)); + outfile.write(reinterpret_cast<const char *>(&learning_rate_alpha), sizeof(learning_rate_alpha)); + + sal_uInt32 tmpInt = n.size(); + outfile.write(reinterpret_cast<const char *>(&tmpInt), sizeof(tmpInt)); + + for (sal_uInt32 i = 0; i < n.size(); i++) + { + tmpInt = n[i].size(); + outfile.write(reinterpret_cast<const char *>(&tmpInt), sizeof(tmpInt)); + } + + for (sal_uInt32 i = 0; i < n.size(); i++) + for (sal_uInt32 j = 0; j < n[i].size(); j++) + { + outfile.write(reinterpret_cast<const char *>(&n[i][j].w[0]), n[i][j].w.size()*sizeof(n[i][j].w[0])); + outfile.write(reinterpret_cast<const char *>(&n[i][j].dw[0]), n[i][j].dw.size()*sizeof(n[i][j].dw[0])); + } + + for (sal_uInt32 i = 0; i < n.size(); i++) + for (sal_uInt32 j = 0; j < n[i].size(); j++) + { + outfile.write(reinterpret_cast<const char *>(&n[i][j].f), sizeof(n[i][j].f)); + outfile.write(reinterpret_cast<const char *>(&n[i][j].stp), sizeof(n[i][j].stp)); + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit 6bdd077f569a629965a4f1544d83606bf410f4fe Author: Vincent Le Garrec <legarrec.vinc...@gmail.com> Date: Tue Jan 16 22:13:21 2018 +0100 Use libfann through an abstract factory With this, libfann can easily replaced by internal implementation or other lib. Use memory instead of using a temporary file to train the neural network. Change-Id: I1ec207ec5674ebba112d50425d445e5f38ada666 diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk index 449a44ea22d3..5933cae387f5 100644 --- a/cui/Library_cui.mk +++ b/cui/Library_cui.mk @@ -134,6 +134,9 @@ $(eval $(call gb_Library_add_exception_objects,cui,\ cui/source/factory/cuiresmgr \ cui/source/factory/dlgfact \ cui/source/factory/init \ + cui/source/factory/neuralnetwork \ + cui/source/factory/neuralnetworkfann \ + cui/source/factory/neuralnetworkinternal \ cui/source/options/certpath \ cui/source/options/cfgchart \ cui/source/options/connpoolconfig \ diff --git a/cui/source/dialogs/cuicharmap.cxx b/cui/source/dialogs/cuicharmap.cxx index ecff74b6a6e0..a1fd1a8ee3c9 100644 --- a/cui/source/dialogs/cuicharmap.cxx +++ b/cui/source/dialogs/cuicharmap.cxx @@ -42,6 +42,7 @@ #include <dialmgr.hxx> #include <cuicharmap.hxx> +#include <neuralnetwork.hxx> #include <sfx2/request.hxx> #include <sfx2/sfxsids.hrc> #include <sfx2/app.hxx> @@ -215,46 +216,6 @@ void Ocr::ScaleBitmap(long width, long height) h_ = height; } -void Ocr::ToFile(std::ofstream &file) -{ - BitmapReadAccess * r = m_bitmap.AcquireReadAccess (); - - std::cout << "2r->Width () != w_" << r->Width () << " vs " << w_ << std::endl; - std::cout << "2r->Height() != h_" << r->Height () << " vs " << h_ << std::endl; - for (long j = 0; j < h_; j++) - { - for (long i = 0; i < w_; i++) - { - if (i >= r->Width () || j >= r->Height()) - { - file << "0 "; - std::cout << "-1 "; - } - else - { - Color c = r->GetPixel(j, i); - if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) - { - file << "0 "; - std::cout << "-1 "; - } - else if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) - { - file << " 1 "; - std::cout << " 1 "; - } - else - { - file << " 1 "; - std::cout << " 1 "; - } - } - } - std::cout << std::endl; - } - file << std::endl; -} - void Ocr::ToFann(fann_type *out_data) { BitmapReadAccess * r = m_bitmap.AcquireReadAccess (); @@ -269,7 +230,7 @@ void Ocr::ToFann(fann_type *out_data) { if (i >= r->Width () || j >= r->Height()) { - out_data[idata] = 0; + out_data[idata] = 0.; std::cout << "-1 "; } else @@ -277,17 +238,17 @@ void Ocr::ToFann(fann_type *out_data) Color c = r->GetPixel(j, i); if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) { - out_data[idata] = 0; + out_data[idata] = 0.; std::cout << "-1 "; } else if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) { - out_data[idata] = 1; + out_data[idata] = 1.; std::cout << " 1 "; } else { - out_data[idata] = 1; + out_data[idata] = 1.; std::cout << " 1 "; } } @@ -1224,8 +1185,6 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) // Deep learning. Take about 30-60 minutes if (access( "/tmp/fann.net", F_OK ) == -1) { - - // One input for each pixel. const unsigned int num_input = Ocr::SIZE*Ocr::SIZE; // One ouput for each possible char. @@ -1234,21 +1193,18 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) const unsigned int num_layers = 6; // Number of neurons for each layer is usually between number of input and number on output. const unsigned int num_neurons_hidden = std::max(num_input, num_output); - const float desired_error = (const float) 0.001; + const float desired_error = (const float) 0.0001; const unsigned int max_epochs = 500; // To see progression. It's take time... const unsigned int epochs_between_reports = 1; - std::ofstream file; - // Write in /tmp/xor.data all database of example. - // TODO: MUST be replace by the memory stream. - file.open("/tmp/xor.data"); - - // First line of the file. - file << num_output << " " << num_input << " " << num_output << std::endl; - // Then, the current char : - // - For each pixel : -1 if blank, 1 if not blank. - // - For each possible char : -1 if not the current char, 1 if it is the current char. +// First possibility: fixed internal layer. + sal_uInt32 layers[num_layers] = {num_input, /*num_neurons_hidden, num_neurons_hidden, */num_neurons_hidden, num_neurons_hidden, num_neurons_hidden, num_neurons_hidden, num_output}; + AbstractNeuralNetwork * ann = AbstractNeuralNetwork::CreateFactory(num_layers, layers); + ann->SetActivationFunction(AbstractNeuralNetwork::ActivationFunction::SIGMOID); + ann->SetTrainingAlgorithm(AbstractNeuralNetwork::TrainingAlgorithm::INCREMENTAL); + ann->SetLearningRate(0.07); + ann->InitTraining(num_output); long i = 0; sal_UCS4 sChar = xFontCharMap->GetFirstChar(); @@ -1299,21 +1255,21 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) o2.CropBitmap(); o2.ScaleBitmap(Ocr::SIZE, Ocr::SIZE); // Write the draw of the current char to the file. - o2.ToFile(file); + o2.ToFann(ann->GetInput(i)); // Write that the current char is the only one good. + fann_type *output = ann->GetOutput(i); for (long j = 0; j < i; j++) { - file << "0 "; + output[j] = 0; } - file << "1 "; + output[i] = 1; for (long j = i + 1; j < num_output; j++) { - file << "0 "; + output[j] = 0; } - file << std::endl; sChar = xFontCharMap->GetNextChar(sChar); i++; @@ -1322,19 +1278,10 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) break; } - file.close(); // Creating the database of example done. std::cout << "DONE" << std::endl; - -// First possibility: fixed internal layer. - struct fann *ann = fann_create_standard(num_layers, num_input, num_neurons_hidden, num_neurons_hidden, num_neurons_hidden, num_neurons_hidden, num_output); // TODO - // Default function. Usually works well. - fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC); - fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC); - fann_set_training_algorithm(ann, FANN_TRAIN_INCREMENTAL); - fann_set_learning_rate(ann, 0.2); - fann_train_on_file(ann, "/tmp/xor.data", max_epochs, epochs_between_reports, desired_error); + ann->Train(max_epochs, desired_error); // Second possibility: none predictive internal layer. // Can't find convergence for Total error:900,0000. Start at iteration around 370. @@ -1345,41 +1292,41 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) // fann_set_cascade_max_cand_epochs(ann, std::max(2U*num_input, 150U)); // fann_set_cascade_min_out_epochs(ann, std::max(num_input/5U, 150U/2)); // fann_set_cascade_min_cand_epochs(ann, std::max(num_input/5U, 150U/2)); - // fann_set_cascade_output_change_fraction(ann, 0.15); - // fann_set_cascade_candidate_change_fraction(ann, 0.15); - // fann_set_cascade_output_stagnation_epochs(ann, 5); - // fann_set_cascade_candidate_stagnation_epochs(ann, 5); - // fann_set_cascade_weight_multiplier(ann, 0.2); + fann_set_cascade_output_change_fraction(ann, 0.001); + fann_set_cascade_candidate_change_fraction(ann, 0.001); + fann_set_cascade_output_stagnation_epochs(ann, 3); + fann_set_cascade_candidate_stagnation_epochs(ann, 3); + fann_set_cascade_weight_multiplier(ann, 0.01); + fann_set_cascade_candidate_limit(ann, 1000000000.); fann_cascadetrain_on_file(ann, "/tmp/xor.data", 100000, epochs_between_reports, desired_error); */ // Save the result. Huge !!! - fann_save(ann, "/tmp/fann.net"); - - fann_destroy(ann); + ann->Save("/tmp/fann.net"); + delete ann; } fann_type *calc_out; fann_type input[Ocr::SIZE*Ocr::SIZE]; std::cout << "Starting loading fann" << std::endl; - struct fann *ann = fann_create_from_file("/tmp/fann.net"); + AbstractNeuralNetwork * ann = AbstractNeuralNetwork::CreateFactory("/tmp/fann.net"); o.ToFann(&input[0]); std::cout << "Starting finding best result" << std::endl; - calc_out = fann_run(ann, input); + calc_out = ann->Run(input); std::cout << "End of fann" << std::endl; std::multimap<float, sal_UCS4> sorted_results; - std::cout << fann_get_num_output(ann) << std::endl; - for (long i = 0; i < fann_get_num_output(ann); i++) + std::cout << ann->GetNumOutput() << std::endl; + for (long i = 0; i < ann->GetNumOutput(); i++) { // -1.-x: the minimum will be at end. sorted_results.insert(std::pair<float, sal_UCS4>(-1.-calc_out[i], i)); std::cout << static_cast<char>(xFontCharMap->GetCharFromIndex(i)) << " : " << calc_out[i] << " " << std::endl; } - fann_destroy(ann); + delete ann; // TODO : sort the output and write the better char at first. for (auto it = sorted_results.begin(); it != sorted_results.end(); ++it) diff --git a/cui/source/factory/neuralnetwork.cxx b/cui/source/factory/neuralnetwork.cxx new file mode 100644 index 000000000000..5651b80b3ab8 --- /dev/null +++ b/cui/source/factory/neuralnetwork.cxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you 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 . + */ + +#include <neuralnetwork.hxx> + +#include "neuralnetworkfann.hxx" + +AbstractNeuralNetwork * AbstractNeuralNetwork::CreateFactory(sal_uInt32 nLayers, const sal_uInt32* nLayer) +{ + return new NeuralNetworkFann(nLayers, nLayer); +} + +AbstractNeuralNetwork * AbstractNeuralNetwork::CreateFactory(const OUString& file) +{ + return new NeuralNetworkFann(file); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/factory/neuralnetworkfann.cxx b/cui/source/factory/neuralnetworkfann.cxx new file mode 100644 index 000000000000..e622916c1ccf --- /dev/null +++ b/cui/source/factory/neuralnetworkfann.cxx @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you 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 . + */ + +#include "neuralnetworkfann.hxx" + +NeuralNetworkFann::NeuralNetworkFann(sal_uInt32 nLayers, const sal_uInt32* nLayer) : + ann(fann_create_standard_array(nLayers, nLayer), fann_destroy), + data(nullptr, fann_destroy_train) +{ +} + +NeuralNetworkFann::NeuralNetworkFann(const OUString& file) : + ann(fann_create_from_file(OUStringToOString(file, RTL_TEXTENCODING_ASCII_US).pData->buffer), fann_destroy), + data(nullptr, fann_destroy_train) +{ +} + +void NeuralNetworkFann::SetActivationFunction(ActivationFunction function) +{ + fann_activationfunc_enum func; + + switch (function) + { + case ActivationFunction::SIGMOID: + { + func = FANN_SIGMOID_SYMMETRIC; + break; + } + default: + { + return; + } + } + fann_set_activation_function_hidden(ann.get(), func); + fann_set_activation_function_output(ann.get(), func); +} + +void NeuralNetworkFann::SetTrainingAlgorithm(TrainingAlgorithm algorithm) +{ + fann_train_enum algo; + + switch (algorithm) + { + case TrainingAlgorithm::INCREMENTAL: + { + algo = FANN_TRAIN_INCREMENTAL; + break; + } + default: + { + return; + } + } + fann_set_training_algorithm(ann.get(), algo); +} + +void NeuralNetworkFann::SetLearningRate(float rate) +{ + fann_set_learning_rate(ann.get(), rate); +} + +#include <iostream> + +void NeuralNetworkFann::InitTraining(sal_uInt32 nExamples) +{ + data.reset(fann_create_train(nExamples, fann_get_num_input(ann.get()), fann_get_num_output(ann.get()))); +} + +void NeuralNetworkFann::Save(const OUString& file) +{ + OString o = OUStringToOString(file, RTL_TEXTENCODING_ASCII_US); + fann_save(ann.get(), o.pData->buffer); +} + +sal_uInt32 NeuralNetworkFann::GetNumInput() +{ + return fann_get_num_input(ann.get()); +} + +float* NeuralNetworkFann::GetInput(sal_uInt32 nIeme) +{ + return &data->input[nIeme][0]; +} + +sal_uInt32 NeuralNetworkFann::GetNumOutput() +{ + return fann_get_num_output(ann.get()); +} + +float* NeuralNetworkFann::GetOutput(sal_uInt32 nIeme) +{ + return &data->output[nIeme][0]; +} + +float* NeuralNetworkFann::Run(float *data_input) +{ + return fann_run(ann.get(), data_input); +} + +void NeuralNetworkFann::Train(sal_uInt32 nEpochs, float error) +{ + fann_train_on_data(ann.get(), data.get(), nEpochs, 1, error); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/factory/neuralnetworkfann.hxx b/cui/source/factory/neuralnetworkfann.hxx new file mode 100644 index 000000000000..472f4640dde3 --- /dev/null +++ b/cui/source/factory/neuralnetworkfann.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you 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 . + */ +#ifndef INCLUDED_CUI_SOURCE_FACTORY_NEURALNETWORKFANN_HXX +#define INCLUDED_CUI_SOURCE_FACTORY_NEURALNETWORKFANN_HXX + +#include "neuralnetwork.hxx" + +#include <fann.h> +#include <memory> + +class NeuralNetworkFann : public AbstractNeuralNetwork +{ +public: + NeuralNetworkFann(sal_uInt32 nLayers, const sal_uInt32* nLayer); + NeuralNetworkFann(const OUString& file); + + void SetActivationFunction(ActivationFunction function) override; + void SetTrainingAlgorithm(TrainingAlgorithm algorithm) override; + void SetLearningRate(float rate) override; + + void InitTraining(sal_uInt32 nExamples) override; + virtual sal_uInt32 GetNumInput() override; + float* GetInput(sal_uInt32 nIeme) override; + virtual sal_uInt32 GetNumOutput() override; + float* GetOutput(sal_uInt32 nIeme) override; + + void Train(sal_uInt32 nEpochs, float error) override; + float* Run(float *data_input) override; + void Save(const OUString& file) override; + + virtual ~NeuralNetworkFann(){} + +private: + std::unique_ptr<struct fann, void(*)(struct fann *)> ann; + std::unique_ptr<struct fann_train_data, void(*)(struct fann_train_data *)> data; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/factory/neuralnetworkinternal.cxx b/cui/source/factory/neuralnetworkinternal.cxx new file mode 100644 index 000000000000..52597c57ca92 --- /dev/null +++ b/cui/source/factory/neuralnetworkinternal.cxx @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you 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 . + */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/inc/cuicharmap.hxx b/cui/source/inc/cuicharmap.hxx index d999d2f316a8..1c30cd404ec5 100644 --- a/cui/source/inc/cuicharmap.hxx +++ b/cui/source/inc/cuicharmap.hxx @@ -51,7 +51,6 @@ public: void ReadBitmap(); void CropBitmap(); void ScaleBitmap(long width, long height); - void ToFile(std::ofstream &file); void ToFann(fann_type *out_data); static const int SIZE = 20; diff --git a/cui/source/inc/neuralnetwork.hxx b/cui/source/inc/neuralnetwork.hxx new file mode 100644 index 000000000000..045909214de9 --- /dev/null +++ b/cui/source/inc/neuralnetwork.hxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you 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 . + */ +#ifndef INCLUDED_CUI_SOURCE_FACTORY_NEURALNETWORK_HXX +#define INCLUDED_CUI_SOURCE_FACTORY_NEURALNETWORK_HXX + +#include <sal/types.h> +#include <rtl/ustring.hxx> + +class AbstractNeuralNetwork +{ +public: + enum class ActivationFunction { + SIGMOID + }; + enum class TrainingAlgorithm { + INCREMENTAL + }; + static AbstractNeuralNetwork * CreateFactory(sal_uInt32 nLayers, const sal_uInt32* nLayer); + static AbstractNeuralNetwork * CreateFactory(const OUString& file); + virtual void SetActivationFunction(ActivationFunction function) = 0; + virtual void SetTrainingAlgorithm(TrainingAlgorithm algorithm) = 0; + virtual void SetLearningRate(float rate) = 0; + + virtual void InitTraining(sal_uInt32 nExamples) = 0; + virtual sal_uInt32 GetNumInput() = 0; + virtual float* GetInput(sal_uInt32 nIeme) = 0; + virtual sal_uInt32 GetNumOutput() = 0; + virtual float* GetOutput(sal_uInt32 nIeme) = 0; + + virtual void Train(sal_uInt32 nEpochs, float error) = 0; + virtual float* Run(float *data_input) = 0; + virtual void Save(const OUString& file) = 0; + + virtual ~AbstractNeuralNetwork(){} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit b421a9e3183fd6ee8901f0f42787039499c3e201 Author: Vincent Le Garrec <legarrec.vinc...@gmail.com> Date: Tue Jan 16 22:09:00 2018 +0100 Show the result of the neural network in the UI. And reduce the number of char that can be recognized to start with a small subset. Change-Id: I41d0861f39ff4ba16c1fbebd892ce7f0e6f8043f diff --git a/cui/source/dialogs/cuicharmap.cxx b/cui/source/dialogs/cuicharmap.cxx index b571fdeaf073..ecff74b6a6e0 100644 --- a/cui/source/dialogs/cuicharmap.cxx +++ b/cui/source/dialogs/cuicharmap.cxx @@ -184,8 +184,8 @@ void Ocr::CropBitmap() long right = j; - w_ = right-left+1; - h_ = bottom-top+1; + w_ = right - left + 1; + h_ = bottom - top + 1; tools::Rectangle r_src(left, top, right, bottom); tools::Rectangle r_dst(0, 0, w_, h_); Size s(w_, h_); @@ -223,31 +223,34 @@ void Ocr::ToFile(std::ofstream &file) std::cout << "2r->Height() != h_" << r->Height () << " vs " << h_ << std::endl; for (long j = 0; j < h_; j++) { - std::cout << j << std::endl; for (long i = 0; i < w_; i++) { - std::cout << i << std::endl; if (i >= r->Width () || j >= r->Height()) { - file << "-1 "; + file << "0 "; + std::cout << "-1 "; } else { Color c = r->GetPixel(j, i); if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) { - file << "-1 "; + file << "0 "; + std::cout << "-1 "; } else if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) { - file << "1 "; + file << " 1 "; + std::cout << " 1 "; } else { - file << "1 "; + file << " 1 "; + std::cout << " 1 "; } } } + std::cout << std::endl; } file << std::endl; } @@ -266,7 +269,7 @@ void Ocr::ToFann(fann_type *out_data) { if (i >= r->Width () || j >= r->Height()) { - out_data[idata] = -1; + out_data[idata] = 0; std::cout << "-1 "; } else @@ -274,22 +277,23 @@ void Ocr::ToFann(fann_type *out_data) Color c = r->GetPixel(j, i); if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) { - out_data[idata] = -1; + out_data[idata] = 0; std::cout << "-1 "; } else if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) { out_data[idata] = 1; - std::cout << "1 "; + std::cout << " 1 "; } else { out_data[idata] = 1; - std::cout << "1 "; + std::cout << " 1 "; } } idata++; } + std::cout << std::endl; } std::cout << std::endl; } @@ -1213,33 +1217,28 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) o.ScaleBitmap(Ocr::SIZE, Ocr::SIZE); m_pSearchSet->ClearPreviousData(); + toggleSearchView(true); + FontCharMapRef xFontCharMap(new FontCharMap()); + m_pSearchSet->GetFontCharMap(xFontCharMap); // Deep learning. Take about 30-60 minutes if (access( "/tmp/fann.net", F_OK ) == -1) { - FontCharMapRef xFontCharMap(new FontCharMap()); - m_pSearchSet->GetFontCharMap(xFontCharMap); // One input for each pixel. const unsigned int num_input = Ocr::SIZE*Ocr::SIZE; // One ouput for each possible char. - const unsigned int num_output = xFontCharMap->GetCharCount()-1; - // 4 layers (one input, one output and two is recommended for OCR). - const unsigned int num_layers = 4; + const unsigned int num_output = 2*16/*xFontCharMap->GetCharCount()-1*/; + // 6 layers (one input, one output and the others). + const unsigned int num_layers = 6; // Number of neurons for each layer is usually between number of input and number on output. const unsigned int num_neurons_hidden = std::max(num_input, num_output); const float desired_error = (const float) 0.001; - const unsigned int max_epochs = 500000; + const unsigned int max_epochs = 500; // To see progression. It's take time... const unsigned int epochs_between_reports = 1; - struct fann *ann = fann_create_standard(num_layers, num_input, num_neurons_hidden, num_neurons_hidden, num_output); - - // Default function. Usually works well. - fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC); - fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC); - std::ofstream file; // Write in /tmp/xor.data all database of example. // TODO: MUST be replace by the memory stream. @@ -1305,20 +1304,22 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) // Write that the current char is the only one good. for (long j = 0; j < i; j++) { - file << "-1 "; + file << "0 "; } file << "1 "; for (long j = i + 1; j < num_output; j++) { - file << "-1 "; + file << "0 "; } file << std::endl; sChar = xFontCharMap->GetNextChar(sChar); - m_pSearchSet->AppendCharToList(sChar); i++; + + if (i == num_output) + break; } file.close(); @@ -1326,9 +1327,32 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) std::cout << "DONE" << std::endl; - // Training the network. Take age !!! +// First possibility: fixed internal layer. + struct fann *ann = fann_create_standard(num_layers, num_input, num_neurons_hidden, num_neurons_hidden, num_neurons_hidden, num_neurons_hidden, num_output); // TODO + // Default function. Usually works well. + fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC); + fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC); + fann_set_training_algorithm(ann, FANN_TRAIN_INCREMENTAL); + fann_set_learning_rate(ann, 0.2); fann_train_on_file(ann, "/tmp/xor.data", max_epochs, epochs_between_reports, desired_error); +// Second possibility: none predictive internal layer. +// Can't find convergence for Total error:900,0000. Start at iteration around 370. + /* + struct fann *ann = fann_create_shortcut(2, num_input, num_output); + // Useful??? + // fann_set_cascade_max_out_epochs(ann, std::max(2U*num_input, 150U)); + // fann_set_cascade_max_cand_epochs(ann, std::max(2U*num_input, 150U)); + // fann_set_cascade_min_out_epochs(ann, std::max(num_input/5U, 150U/2)); + // fann_set_cascade_min_cand_epochs(ann, std::max(num_input/5U, 150U/2)); + // fann_set_cascade_output_change_fraction(ann, 0.15); + // fann_set_cascade_candidate_change_fraction(ann, 0.15); + // fann_set_cascade_output_stagnation_epochs(ann, 5); + // fann_set_cascade_candidate_stagnation_epochs(ann, 5); + // fann_set_cascade_weight_multiplier(ann, 0.2); + fann_cascadetrain_on_file(ann, "/tmp/xor.data", 100000, epochs_between_reports, desired_error); + */ + // Save the result. Huge !!! fann_save(ann, "/tmp/fann.net"); @@ -1346,13 +1370,28 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) calc_out = fann_run(ann, input); std::cout << "End of fann" << std::endl; - for (long i = 0; i < 2294; i++) - std::cout << calc_out[i] << " "; + std::multimap<float, sal_UCS4> sorted_results; + std::cout << fann_get_num_output(ann) << std::endl; + for (long i = 0; i < fann_get_num_output(ann); i++) + { + // -1.-x: the minimum will be at end. + sorted_results.insert(std::pair<float, sal_UCS4>(-1.-calc_out[i], i)); + std::cout << static_cast<char>(xFontCharMap->GetCharFromIndex(i)) << " : " << calc_out[i] << " " << std::endl; + } fann_destroy(ann); // TODO : sort the output and write the better char at first. -// m_pSearchSet->AppendCharToList(sChar); + for (auto it = sorted_results.begin(); it != sorted_results.end(); ++it) + { + /* + if ((*it).first == -1) + break; + */ + m_pSearchSet->AppendCharToList(xFontCharMap->GetCharFromIndex((*it).second)); + std::cout << (*it).second << " "; + } + std::cout << std::endl; m_pSearchSet->Resize(); } diff --git a/cui/source/inc/cuicharmap.hxx b/cui/source/inc/cuicharmap.hxx index 0e80d4002c1a..d999d2f316a8 100644 --- a/cui/source/inc/cuicharmap.hxx +++ b/cui/source/inc/cuicharmap.hxx @@ -54,7 +54,7 @@ public: void ToFile(std::ofstream &file); void ToFann(fann_type *out_data); - static const int SIZE = 15; + static const int SIZE = 20; private: Bitmap m_bitmap; commit 85188af850f4e7ea252df6202215c328b63bb115 Author: Vincent Le Garrec <legarrec.vinc...@gmail.com> Date: Tue Jan 16 22:05:50 2018 +0100 Recognize char by using lib fann network Read all char, train the neural network with these char then apply the draw to get recognition. Change-Id: Ie48a173703f599b054f0c6edfa38acfdfb25ec71 diff --git a/cui/source/dialogs/cuicharmap.cxx b/cui/source/dialogs/cuicharmap.cxx index 8bf4d7166801..b571fdeaf073 100644 --- a/cui/source/dialogs/cuicharmap.cxx +++ b/cui/source/dialogs/cuicharmap.cxx @@ -52,6 +52,9 @@ #include <unicode/uchar.h> #include <unicode/utypes.h> +#include <fann.h> +#include <floatfann.h> + using namespace css; Ocr::Ocr(const Bitmap& b, long w, long h) : m_bitmap(b), w_(w), h_(h), data() @@ -67,18 +70,25 @@ void Ocr::ReadBitmap() { for (long i = 0; i < w_; i++) { - Color c = r->GetPixel (j, i); - if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) - { - data[j*w_+i] = 1; - } - else if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) + if (i >= r->Width () || j >= r->Height()) { data[j*w_+i] = 0; } else { - data[j*w_+i] = 2; + Color c = r->GetPixel (j, i); + if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) + { + data[j*w_+i] = 1; + } + else if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) + { + data[j*w_+i] = 0; + } + else + { + data[j*w_+i] = 2; + } } } } @@ -200,6 +210,88 @@ void Ocr::ScaleBitmap(long width, long height) } m_bitmap.Scale(new_size, new_size, BmpScaleFlag::Fast); + + w_ = width; + h_ = height; +} + +void Ocr::ToFile(std::ofstream &file) +{ + BitmapReadAccess * r = m_bitmap.AcquireReadAccess (); + + std::cout << "2r->Width () != w_" << r->Width () << " vs " << w_ << std::endl; + std::cout << "2r->Height() != h_" << r->Height () << " vs " << h_ << std::endl; + for (long j = 0; j < h_; j++) + { + std::cout << j << std::endl; + for (long i = 0; i < w_; i++) + { + std::cout << i << std::endl; + if (i >= r->Width () || j >= r->Height()) + { + file << "-1 "; + } + else + { + Color c = r->GetPixel(j, i); + if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) + { + file << "-1 "; + } + else if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) + { + file << "1 "; + } + else + { + file << "1 "; + } + } + } + } + file << std::endl; +} + +void Ocr::ToFann(fann_type *out_data) +{ + BitmapReadAccess * r = m_bitmap.AcquireReadAccess (); + long idata = 0; + + std::cout << "3r->Width () != w_" << r->Width () << " vs " << w_ << std::endl; + std::cout << "3r->Height() != h_" << r->Height () << " vs " << h_ << std::endl; + + for (long j = 0; j < h_; j++) + { + for (long i = 0; i < w_; i++) + { + if (i >= r->Width () || j >= r->Height()) + { + out_data[idata] = -1; + std::cout << "-1 "; + } + else + { + Color c = r->GetPixel(j, i); + if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) + { + out_data[idata] = -1; + std::cout << "-1 "; + } + else if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) + { + out_data[idata] = 1; + std::cout << "1 "; + } + else + { + out_data[idata] = 1; + std::cout << "1 "; + } + } + idata++; + } + } + std::cout << std::endl; } // class SvxCharacterMap ================================================= @@ -1095,6 +1187,8 @@ IMPL_LINK_NOARG(SvxCharacterMap, InsertClickHdl, Button*, void) EndDialog(RET_OK); } +#include <iostream> + IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) { if (m_pDrawChk->IsChecked()) @@ -1120,57 +1214,145 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) m_pSearchSet->ClearPreviousData(); - sal_UCS4 *range = new sal_UCS4[2]; - range[0] = 0; - range[1] = 1114111; - CmapResult rCR (true, range, 1); - FontCharMapRef xFontCharMap(new FontCharMap(rCR)); - m_pSearchSet->GetFontCharMap(xFontCharMap); - - sal_UCS4 sChar = xFontCharMap->GetFirstChar(); - while(sChar != xFontCharMap->GetLastChar()) + // Deep learning. Take about 30-60 minutes + if (access( "/tmp/fann.net", F_OK ) == -1) { - VirtualDevice od; - OUString aOUStr( &sChar, 1 ); - Size s50(Ocr::SIZE, Ocr::SIZE); - od.SetOutputSize( s50 ); - vcl::Font aFontBis(aFont); - aFontBis.SetFontHeight(Ocr::SIZE); - od.SetFont( aFontBis ); - od.DrawText( p, aOUStr ); - w = od.GetOutputWidthPixel (); - h = od.GetOutputHeightPixel (); - - Size s2(w, h); - b = od.GetBitmap (p, s2); - - BitmapReadAccess* r = b.AcquireReadAccess (); - - for (long j = 0; j < h; j++) + FontCharMapRef xFontCharMap(new FontCharMap()); + m_pSearchSet->GetFontCharMap(xFontCharMap); + + + // One input for each pixel. + const unsigned int num_input = Ocr::SIZE*Ocr::SIZE; + // One ouput for each possible char. + const unsigned int num_output = xFontCharMap->GetCharCount()-1; + // 4 layers (one input, one output and two is recommended for OCR). + const unsigned int num_layers = 4; + // Number of neurons for each layer is usually between number of input and number on output. + const unsigned int num_neurons_hidden = std::max(num_input, num_output); + const float desired_error = (const float) 0.001; + const unsigned int max_epochs = 500000; + // To see progression. It's take time... + const unsigned int epochs_between_reports = 1; + + struct fann *ann = fann_create_standard(num_layers, num_input, num_neurons_hidden, num_neurons_hidden, num_output); + + // Default function. Usually works well. + fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC); + fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC); + + std::ofstream file; + // Write in /tmp/xor.data all database of example. + // TODO: MUST be replace by the memory stream. + file.open("/tmp/xor.data"); + + // First line of the file. + file << num_output << " " << num_input << " " << num_output << std::endl; + // Then, the current char : + // - For each pixel : -1 if blank, 1 if not blank. + // - For each possible char : -1 if not the current char, 1 if it is the current char. + + long i = 0; + sal_UCS4 sChar = xFontCharMap->GetFirstChar(); + while(sChar != xFontCharMap->GetLastChar()) { - for (long i = 0; i < w; i++) + // Draw the current char. + VirtualDevice od; + OUString aOUStr( &sChar, 1 ); + Size s50(Ocr::SIZE, Ocr::SIZE); + od.SetOutputSize( s50 ); + vcl::Font aFontBis(aFont); + aFontBis.SetFontHeight(Ocr::SIZE); + od.SetFont( aFontBis ); + od.DrawText( p, aOUStr ); + + w = od.GetOutputWidthPixel (); + h = od.GetOutputHeightPixel (); + Size s2(w, h); + Bitmap b2 = od.GetBitmap (p, s2); + + BitmapReadAccess* r = b2.AcquireReadAccess (); + + // Show every pixel of the current char. + for (long j = 0; j < h; j++) { - Color c = r->GetPixel(j, i); - if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) - { - std::cout << "1"; - } - else if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) - { - std::cout << "0"; - } - else + for (long k = 0; k < w; k++) { - std::cout << "2"; + Color c = r->GetPixel(j, k); + if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) + { + std::cout << "1"; + } + else if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) + { + std::cout << "0"; + } + else + { + std::cout << "2"; + } } + std::cout << std::endl; } - std::cout << std::endl; + + // Resize the bitmap of the current char. + Ocr o2(b2, w, h); + o2.ReadBitmap(); + o2.CropBitmap(); + o2.ScaleBitmap(Ocr::SIZE, Ocr::SIZE); + // Write the draw of the current char to the file. + o2.ToFile(file); + + // Write that the current char is the only one good. + for (long j = 0; j < i; j++) + { + file << "-1 "; + } + + file << "1 "; + + for (long j = i + 1; j < num_output; j++) + { + file << "-1 "; + } + file << std::endl; + + sChar = xFontCharMap->GetNextChar(sChar); + m_pSearchSet->AppendCharToList(sChar); + i++; } - sChar = xFontCharMap->GetNextChar(sChar); - m_pSearchSet->AppendCharToList(sChar); + file.close(); + // Creating the database of example done. + std::cout << "DONE" << std::endl; + + + // Training the network. Take age !!! + fann_train_on_file(ann, "/tmp/xor.data", max_epochs, epochs_between_reports, desired_error); + + // Save the result. Huge !!! + fann_save(ann, "/tmp/fann.net"); + + fann_destroy(ann); } - m_pSearchSet->AppendCharToList(sChar); + + fann_type *calc_out; + fann_type input[Ocr::SIZE*Ocr::SIZE]; + + std::cout << "Starting loading fann" << std::endl; + struct fann *ann = fann_create_from_file("/tmp/fann.net"); + + o.ToFann(&input[0]); + std::cout << "Starting finding best result" << std::endl; + calc_out = fann_run(ann, input); + std::cout << "End of fann" << std::endl; + + for (long i = 0; i < 2294; i++) + std::cout << calc_out[i] << " "; + + fann_destroy(ann); + + // TODO : sort the output and write the better char at first. +// m_pSearchSet->AppendCharToList(sChar); m_pSearchSet->Resize(); } @@ -1530,7 +1712,6 @@ void DrawingAreaOcr::MouseMove( const MouseEvent &rMEvt ) Invalidate(); Update(); - std::cout << static_cast<int>(state) << std::endl; } void DrawingAreaOcr::MouseButtonUp (const MouseEvent &rMEvt) diff --git a/cui/source/inc/cuicharmap.hxx b/cui/source/inc/cuicharmap.hxx index 10b7dc956c03..0e80d4002c1a 100644 --- a/cui/source/inc/cuicharmap.hxx +++ b/cui/source/inc/cuicharmap.hxx @@ -28,6 +28,10 @@ #include <svx/charmap.hxx> #include <svx/searchcharmap.hxx> #include <sfx2/charwin.hxx> +#include <iostream> +#include <fstream> + +#include <floatfann.h> using namespace ::com::sun::star; class SubsetMap; @@ -47,8 +51,10 @@ public: void ReadBitmap(); void CropBitmap(); void ScaleBitmap(long width, long height); + void ToFile(std::ofstream &file); + void ToFann(fann_type *out_data); - static const int SIZE = 25; + static const int SIZE = 15; private: Bitmap m_bitmap; commit 63e2b57bb5ea4286e196b224fecc157c248d7c04 Author: Vincent Le Garrec <legarrec.vinc...@gmail.com> Date: Tue Jan 16 22:03:04 2018 +0100 Add fann dependency Change-Id: I508ecd58abcd779219c4275d804ec8a268d0ccd8 diff --git a/RepositoryExternal.mk b/RepositoryExternal.mk index 30709792c010..9539ce05f31d 100644 --- a/RepositoryExternal.mk +++ b/RepositoryExternal.mk @@ -2548,6 +2548,21 @@ endef endif # ENABLE_GIO +define gb_LinkTarget__use_fann +$(call gb_LinkTarget_set_include,$(1),\ + $$(INCLUDE) \ + $(FANN_CFLAGS) \ +) + +$(call gb_LinkTarget_add_libs,$(1),$(FANN_LIBS)) + +endef + +define gb_ExternalProject__use_fann +$(call gb_ExternalProject_use_package,$(1),fann) +endef + + ifeq ($(ENABLE_AVAHI),TRUE) define gb_LinkTarget__use_avahi diff --git a/config_host.mk.in b/config_host.mk.in index 0cc343cd351d..a46d6da90e24 100644 --- a/config_host.mk.in +++ b/config_host.mk.in @@ -197,6 +197,8 @@ export GIT_LINK_SRC=@GIT_LINK_SRC@ export GIT_NEEDED_SUBMODULES=@GIT_NEEDED_SUBMODULES@ export EPOXY_CFLAGS=$(gb_SPACE)@EPOXY_CFLAGS@ export EPOXY_LIBS=$(gb_SPACE)@EPOXY_LIBS@ +export FANN_CFLAGS=$(gb_SPACE)@FANN_CFLAGS@ +export FANN_LIBS=$(gb_SPACE)@FANN_LIBS@ export GLM_CFLAGS=$(gb_SPACE)@GLM_CFLAGS@ export GPG_ERROR_CFLAGS=$(gb_SPACE)@GPG_ERROR_CFLAGS@ export GPG_ERROR_LIBS=$(gb_SPACE)@GPG_ERROR_LIBS@ diff --git a/configure.ac b/configure.ac index 7ef4a6c6a334..11ecf94d6658 100644 --- a/configure.ac +++ b/configure.ac @@ -7736,6 +7736,12 @@ libo_CHECK_SYSTEM_MODULE([libstaroffice],[STAROFFICE],[libstaroffice-0.0]) libo_PKG_VERSION([STAROFFICE], [libstaroffice-0.0], [0.0.4]) dnl =================================================================== +dnl Neuronal network fann +dnl =================================================================== +libo_CHECK_SYSTEM_MODULE([fann], [FANN], [fann >= 2.0]) + + +dnl =================================================================== dnl Check for system lcms2 dnl =================================================================== if test "$with_system_lcms2" != "yes"; then diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk index c6dc353f59b4..449a44ea22d3 100644 --- a/cui/Library_cui.mk +++ b/cui/Library_cui.mk @@ -66,6 +66,7 @@ $(eval $(call gb_Library_use_externals,cui,\ boost_headers \ $(call gb_Helper_optional,OPENCL,\ clew) \ + fann \ icuuc \ icu_headers \ )) diff --git a/download.lst b/download.lst index f574617147c9..ca2bf915d38b 100644 --- a/download.lst +++ b/download.lst @@ -44,6 +44,8 @@ export ETONYEK_VERSION_MICRO := 7 export ETONYEK_TARBALL := libetonyek-0.1.$(ETONYEK_VERSION_MICRO).tar.xz export EXPAT_SHA256SUM := d9dc32efba7e74f788fcc4f212a43216fc37cf5f23f4c2339664d473353aedf6 export EXPAT_TARBALL := expat-2.2.5.tar.bz2 +export FANN_SHA256SUM := 434b85fce60701c4e0066c442d60110d8e649f278e4edb814f0c0e7a1e0929fd +export FANN_TARBALL := FANN-2.2.0-Source.zip export FIREBIRD_SHA256SUM := 6994be3555e23226630c587444be19d309b25b0fcf1f87df3b4e3f88943e5860 export FIREBIRD_TARBALL := Firebird-3.0.0.32483-0.tar.bz2 export FONTCONFIG_SHA256SUM := cf0c30807d08f6a28ab46c61b8dbd55c97d2f292cf88f3a07d3384687f31f017 commit 99d95dd1bfbe248086850660f000d5b32c545722 Author: Vincent Le Garrec <legarrec.vinc...@gmail.com> Date: Tue Jan 16 22:00:29 2018 +0100 Read the draw and resize it Convert a char to an image. VirtualDevice od must be convert to VclPtr<VirtualDevice>. Change-Id: I2b53c6f2bde12109c56ce1b4d139af6ddc901718 diff --git a/cui/source/dialogs/cuicharmap.cxx b/cui/source/dialogs/cuicharmap.cxx index 7ca01ffba5b1..8bf4d7166801 100644 --- a/cui/source/dialogs/cuicharmap.cxx +++ b/cui/source/dialogs/cuicharmap.cxx @@ -32,6 +32,8 @@ #include <vcl/settings.hxx> #include <vcl/builderfactory.hxx> #include <vcl/fontcharmap.hxx> +#include <vcl/virdev.hxx> +#include <vcl/bitmapaccess.hxx> #include <svl/stritem.hxx> #include <officecfg/Office/Common.hxx> #include <comphelper/processfactory.hxx> @@ -52,6 +54,154 @@ using namespace css; +Ocr::Ocr(const Bitmap& b, long w, long h) : m_bitmap(b), w_(w), h_(h), data() +{ +} + +void Ocr::ReadBitmap() +{ + BitmapReadAccess * r = m_bitmap.AcquireReadAccess (); + data.resize(h_*w_); + + for (long j = 0; j < h_; j++) + { + for (long i = 0; i < w_; i++) + { + Color c = r->GetPixel (j, i); + if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) + { + data[j*w_+i] = 1; + } + else if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) + { + data[j*w_+i] = 0; + } + else + { + data[j*w_+i] = 2; + } + } + } +} + +void Ocr::CropBitmap() +{ + // Remove useless bottom row. + long j; + for (j = h_ - 1; j >= 0; j--) + { + long i; + for (i = 0; i < w_; i++) + { + if (data[j*w_+i] != 0) + { + break; + } + } + if (i != w_) + { + break; + } + } + + // All image is empty. + if (j == -1) + { + w_ = 1; + h_ = 1; + tools::Rectangle r(0, 0, 1, 1); + m_bitmap.Crop(r); + data.resize(1); + return; + } + + long bottom = j; + + // Remove useless top row. + for (j = 0; j < h_; j++) + { + long i; + for (i = 0; i < w_; i++) + { + if (data[j*w_+i] != 0) + { + break; + } + } + if (i != w_) + { + break; + } + } + + long top = j; + + // Remove useless left col. + for (j = 0; j < w_; j++) + { + long i; + for (i = 0; i < h_; i++) + { + if (data[i*w_+j] != 0) + { + break; + } + } + if (i != h_) + { + break; + } + } + + long left = j; + + // Remove useless right col. + for (j = w_-1; j >= 0; j--) + { + long i; + for (i = 0; i < h_; i++) + { + if (data[i*w_+j] != 0) + { + break; + } + } + if (i != h_) + { + break; + } + } + + long right = j; + + w_ = right-left+1; + h_ = bottom-top+1; + tools::Rectangle r_src(left, top, right, bottom); + tools::Rectangle r_dst(0, 0, w_, h_); + Size s(w_, h_); + Bitmap b_dst(s, 1); + b_dst.CopyPixel(r_dst, r_src, &m_bitmap); + m_bitmap = b_dst; +} + +void Ocr::ScaleBitmap(long width, long height) +{ + double new_ratio = (double)width / height; + double ratio = (double)w_ / h_; + + double new_size; + if (ratio > new_ratio) + { + new_size = (double)width / w_; + } + else + { + new_size = (double)height / h_; + } + + m_bitmap.Scale(new_size, new_size, BmpScaleFlag::Fast); +} + // class SvxCharacterMap ================================================= SvxCharacterMap::SvxCharacterMap( vcl::Window* pParent, const SfxItemSet* pSet, bool bInsert ) @@ -956,6 +1106,73 @@ IMPL_LINK_NOARG(SvxCharacterMap, DrawToggleHdl, Button*, void) { m_pDrawingArea->Hide(); m_pShowChar->Show(); + + Point p(0, 0); + long w = m_pDrawingArea->GetOutputWidthPixel (); + long h = m_pDrawingArea->GetOutputHeightPixel (); + Size s(w, h); + Bitmap b = m_pDrawingArea->GetBitmap (p, s); + + Ocr o(b, w, h); + o.ReadBitmap(); + o.CropBitmap(); + o.ScaleBitmap(Ocr::SIZE, Ocr::SIZE); + + m_pSearchSet->ClearPreviousData(); + + sal_UCS4 *range = new sal_UCS4[2]; + range[0] = 0; + range[1] = 1114111; + CmapResult rCR (true, range, 1); + FontCharMapRef xFontCharMap(new FontCharMap(rCR)); + m_pSearchSet->GetFontCharMap(xFontCharMap); + + sal_UCS4 sChar = xFontCharMap->GetFirstChar(); + while(sChar != xFontCharMap->GetLastChar()) + { + VirtualDevice od; + OUString aOUStr( &sChar, 1 ); + Size s50(Ocr::SIZE, Ocr::SIZE); + od.SetOutputSize( s50 ); + vcl::Font aFontBis(aFont); + aFontBis.SetFontHeight(Ocr::SIZE); + od.SetFont( aFontBis ); + od.DrawText( p, aOUStr ); + w = od.GetOutputWidthPixel (); + h = od.GetOutputHeightPixel (); + + Size s2(w, h); + b = od.GetBitmap (p, s2); + + BitmapReadAccess* r = b.AcquireReadAccess (); + + for (long j = 0; j < h; j++) + { + for (long i = 0; i < w; i++) + { + Color c = r->GetPixel(j, i); + if (c.GetRed () == 0 && c.GetGreen () == 0 && c.GetBlue () == 0) + { + std::cout << "1"; + } + else if (c.GetRed () == 255 && c.GetGreen () == 255 && c.GetBlue () == 255) + { + std::cout << "0"; + } + else + { + std::cout << "2"; + } + } + std::cout << std::endl; + } + + sChar = xFontCharMap->GetNextChar(sChar); + m_pSearchSet->AppendCharToList(sChar); + } + m_pSearchSet->AppendCharToList(sChar); + m_pSearchSet->Resize(); + } } diff --git a/cui/source/inc/cuicharmap.hxx b/cui/source/inc/cuicharmap.hxx index 752678e1ad91..10b7dc956c03 100644 --- a/cui/source/inc/cuicharmap.hxx +++ b/cui/source/inc/cuicharmap.hxx @@ -39,6 +39,24 @@ namespace svx struct SvxShowCharSetItem; } +class Ocr +{ +public: + Ocr(const Bitmap& b, long w, long h); + + void ReadBitmap(); + void CropBitmap(); + void ScaleBitmap(long width, long height); + + static const int SIZE = 25; + +private: + Bitmap m_bitmap; + long w_; + long h_; + std::vector<char> data; +}; + class DrawingAreaOcr : public Control { public: _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits