blob: 67d2f5d7a2c3adaa0d07aed46ce82e90405bb729 [file] [log] [blame]
Honglin Yu0c4760a2020-04-18 20:53:31 +10001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ml/handwriting.h"
6
7#include <string>
Honglin Yucfb19302020-08-17 17:13:14 +10008#include <utility>
Honglin Yu0c4760a2020-04-18 20:53:31 +10009
Qijiang Fan713061e2021-03-08 15:45:12 +090010#include <base/check.h>
11#include <base/check_op.h>
Honglin Yu0c4760a2020-04-18 20:53:31 +100012#include <base/files/file_path.h>
13#include <base/logging.h>
14#include <base/native_library.h>
alanlxl867da6c2021-04-19 10:36:35 +100015#include <base/optional.h>
Honglin Yu0c4760a2020-04-18 20:53:31 +100016
17namespace ml {
Honglin Yu0c4760a2020-04-18 20:53:31 +100018namespace {
charleszhao17777f92020-04-23 12:53:11 +100019
Charles Zhaoc882eb02020-07-27 10:02:35 +100020using chrome_knowledge::HandwritingRecognizerModelPaths;
21using chrome_knowledge::HandwritingRecognizerOptions;
22using chromeos::machine_learning::mojom::HandwritingRecognizerSpecPtr;
23
24constexpr char kHandwritingLibraryRelativePath[] = "libhandwriting.so";
Charles Zhaoc882eb02020-07-27 10:02:35 +100025
26// A list of supported language code.
27constexpr char kLanguageCodeEn[] = "en";
28constexpr char kLanguageCodeGesture[] = "gesture_in_context";
29
30// Returns HandwritingRecognizerModelPaths based on the `spec`.
31HandwritingRecognizerModelPaths GetModelPaths(
Honglin Yu0f5b21d2021-04-06 23:39:04 +100032 const std::string& language, const base::FilePath& model_path) {
Charles Zhaoc882eb02020-07-27 10:02:35 +100033 HandwritingRecognizerModelPaths paths;
Honglin Yu0f5b21d2021-04-06 23:39:04 +100034 if (language == kLanguageCodeEn) {
Charles Zhao6d467e62020-08-31 10:02:03 +100035 paths.set_reco_model_path(model_path.Append("latin_indy.tflite").value());
36 paths.set_seg_model_path(
37 model_path.Append("latin_indy_seg.tflite").value());
38 paths.set_conf_model_path(
39 model_path.Append("latin_indy_conf.tflite").value());
40 paths.set_fst_lm_path(model_path.Append("latin_indy.compact.fst").value());
41 paths.set_recospec_path(model_path.Append("latin_indy.pb").value());
Charles Zhaoc882eb02020-07-27 10:02:35 +100042 return paths;
43 }
44
Honglin Yu0f5b21d2021-04-06 23:39:04 +100045 DCHECK_EQ(language, kLanguageCodeGesture);
Charles Zhao6d467e62020-08-31 10:02:03 +100046 paths.set_reco_model_path(model_path.Append("gic.reco_model.tflite").value());
47 paths.set_recospec_path(model_path.Append("gic.recospec.pb").value());
Charles Zhaoc882eb02020-07-27 10:02:35 +100048 return paths;
Honglin Yu0c4760a2020-04-18 20:53:31 +100049}
50
alanlxl867da6c2021-04-19 10:36:35 +100051class HandwritingLibraryImpl : public HandwritingLibrary {
52 public:
53 HandwritingLibraryImpl();
54 ~HandwritingLibraryImpl() override = default;
charleszhao17777f92020-04-23 12:53:11 +100055
alanlxl867da6c2021-04-19 10:36:35 +100056 Status GetStatus() const override;
57 HandwritingRecognizer CreateHandwritingRecognizer() const override;
58 bool LoadHandwritingRecognizer(HandwritingRecognizer recognizer,
59 const std::string& language) const override;
60 bool RecognizeHandwriting(
61 HandwritingRecognizer recognizer,
62 const chrome_knowledge::HandwritingRecognizerRequest& request,
63 chrome_knowledge::HandwritingRecognizerResult* result) const override;
Charles Zhao6d467e62020-08-31 10:02:03 +100064
alanlxl867da6c2021-04-19 10:36:35 +100065 void DestroyHandwritingRecognizer(
66 HandwritingRecognizer recognizer) const override;
67
68 private:
69 friend class base::NoDestructor<HandwritingLibraryImpl>;
70 FRIEND_TEST(HandwritingLibraryTest, CanLoadLibrary);
71
72 // Initialize the handwriting library.
73 explicit HandwritingLibraryImpl(const std::string& root_path);
74 HandwritingLibraryImpl(const HandwritingLibraryImpl&) = delete;
75 HandwritingLibraryImpl& operator=(const HandwritingLibraryImpl&) = delete;
76
77 base::Optional<base::ScopedNativeLibrary> library_;
78 Status status_;
79 const base::FilePath model_path_;
80
81 // Store the interface function pointers.
82 // TODO(honglinyu) as pointed out by cjmcdonald@, we should group the pointers
83 // into a single `HandwritingInterface` struct and make it optional, i.e.,
84 // declaring something like |base::Optional<HandwritingInterface> interface_|.
85 CreateHandwritingRecognizerFn create_handwriting_recognizer_;
86 LoadHandwritingRecognizerFn load_handwriting_recognizer_;
87 RecognizeHandwritingFn recognize_handwriting_;
88 DeleteHandwritingResultDataFn delete_handwriting_result_data_;
89 DestroyHandwritingRecognizerFn destroy_handwriting_recognizer_;
90};
91
92HandwritingLibraryImpl::HandwritingLibraryImpl(const std::string& model_path)
Honglin Yu0c4760a2020-04-18 20:53:31 +100093 : status_(Status::kUninitialized),
Charles Zhao6d467e62020-08-31 10:02:03 +100094 model_path_(model_path),
Honglin Yu0c4760a2020-04-18 20:53:31 +100095 create_handwriting_recognizer_(nullptr),
96 load_handwriting_recognizer_(nullptr),
97 recognize_handwriting_(nullptr),
98 delete_handwriting_result_data_(nullptr),
99 destroy_handwriting_recognizer_(nullptr) {
charleszhao17777f92020-04-23 12:53:11 +1000100 if (!IsHandwritingLibrarySupported()) {
101 status_ = Status::kNotSupported;
102 return;
103 }
Honglin Yu0c4760a2020-04-18 20:53:31 +1000104 // Load the library with an option preferring own symbols. Otherwise the
105 // library will try to call, e.g., external tflite, which leads to crash.
106 base::NativeLibraryOptions native_library_options;
107 native_library_options.prefer_own_symbols = true;
Honglin Yu0c4760a2020-04-18 20:53:31 +1000108 library_.emplace(base::LoadNativeLibraryWithOptions(
Charles Zhao6d467e62020-08-31 10:02:03 +1000109 model_path_.Append(kHandwritingLibraryRelativePath),
Charles Zhaoc882eb02020-07-27 10:02:35 +1000110 native_library_options, nullptr));
Honglin Yu0c4760a2020-04-18 20:53:31 +1000111 if (!library_->is_valid()) {
112 status_ = Status::kLoadLibraryFailed;
113 return;
114 }
115
116// Helper macro to look up functions from the library, assuming the function
117// pointer type is named as (name+"Fn"), which is the case in
Honglin Yucfb19302020-08-17 17:13:14 +1000118// "libhandwriting/handwriting_interface.h".
Honglin Yu0c4760a2020-04-18 20:53:31 +1000119#define ML_HANDWRITING_LOOKUP_FUNCTION(function_ptr, name) \
120 function_ptr = \
121 reinterpret_cast<name##Fn>(library_->GetFunctionPointer(#name)); \
122 if (function_ptr == NULL) { \
123 status_ = Status::kFunctionLookupFailed; \
124 return; \
125 }
126 // Look up the function pointers.
127 ML_HANDWRITING_LOOKUP_FUNCTION(create_handwriting_recognizer_,
128 CreateHandwritingRecognizer);
129 ML_HANDWRITING_LOOKUP_FUNCTION(load_handwriting_recognizer_,
130 LoadHandwritingRecognizer);
131 ML_HANDWRITING_LOOKUP_FUNCTION(recognize_handwriting_, RecognizeHandwriting);
132 ML_HANDWRITING_LOOKUP_FUNCTION(delete_handwriting_result_data_,
133 DeleteHandwritingResultData);
134 ML_HANDWRITING_LOOKUP_FUNCTION(destroy_handwriting_recognizer_,
135 DestroyHandwritingRecognizer);
136#undef ML_HANDWRITING_LOOKUP_FUNCTION
137
138 status_ = Status::kOk;
139}
140
alanlxl867da6c2021-04-19 10:36:35 +1000141HandwritingLibrary::Status HandwritingLibraryImpl::GetStatus() const {
Honglin Yu0c4760a2020-04-18 20:53:31 +1000142 return status_;
143}
144
Honglin Yu0c4760a2020-04-18 20:53:31 +1000145// Proxy functions to the library function pointers.
alanlxl867da6c2021-04-19 10:36:35 +1000146HandwritingRecognizer HandwritingLibraryImpl::CreateHandwritingRecognizer()
147 const {
Honglin Yu0c4760a2020-04-18 20:53:31 +1000148 DCHECK(status_ == Status::kOk);
149 return (*create_handwriting_recognizer_)();
150}
151
alanlxl867da6c2021-04-19 10:36:35 +1000152bool HandwritingLibraryImpl::LoadHandwritingRecognizer(
Honglin Yu0f5b21d2021-04-06 23:39:04 +1000153 HandwritingRecognizer const recognizer, const std::string& language) const {
Honglin Yu0c4760a2020-04-18 20:53:31 +1000154 DCHECK(status_ == Status::kOk);
Charles Zhaoc882eb02020-07-27 10:02:35 +1000155
156 // options is not used for now.
157 const std::string options_pb =
158 HandwritingRecognizerOptions().SerializeAsString();
159
160 const std::string paths_pb =
Honglin Yu0f5b21d2021-04-06 23:39:04 +1000161 GetModelPaths(language, model_path_).SerializeAsString();
Honglin Yu0c4760a2020-04-18 20:53:31 +1000162 return (*load_handwriting_recognizer_)(recognizer, options_pb.data(),
163 options_pb.size(), paths_pb.data(),
164 paths_pb.size());
165}
166
alanlxl867da6c2021-04-19 10:36:35 +1000167bool HandwritingLibraryImpl::RecognizeHandwriting(
Honglin Yu0c4760a2020-04-18 20:53:31 +1000168 HandwritingRecognizer const recognizer,
169 const chrome_knowledge::HandwritingRecognizerRequest& request,
170 chrome_knowledge::HandwritingRecognizerResult* const result) const {
171 DCHECK(status_ == Status::kOk);
172 const std::string request_pb = request.SerializeAsString();
173 char* result_data = nullptr;
174 int result_size = 0;
175 const bool recognize_result =
176 (*recognize_handwriting_)(recognizer, request_pb.data(),
177 request_pb.size(), &result_data, &result_size);
178 if (recognize_result) {
179 const bool parse_result_status =
180 result->ParseFromArray(result_data, result_size);
181 DCHECK(parse_result_status);
182 // only need to delete result_data if succeeds.
183 (*delete_handwriting_result_data_)(result_data);
184 }
185
186 return recognize_result;
187}
188
alanlxl867da6c2021-04-19 10:36:35 +1000189void HandwritingLibraryImpl::DestroyHandwritingRecognizer(
Honglin Yu0c4760a2020-04-18 20:53:31 +1000190 HandwritingRecognizer const recognizer) const {
191 DCHECK(status_ == Status::kOk);
192 (*destroy_handwriting_recognizer_)(recognizer);
193}
194
alanlxl867da6c2021-04-19 10:36:35 +1000195static HandwritingLibrary* g_fake_handwriting_library = nullptr;
196
197} // namespace
198
199constexpr char HandwritingLibrary::kHandwritingDefaultModelDir[];
200
201HandwritingLibrary* HandwritingLibrary::GetInstance(
202 const std::string& model_path) {
203 if (g_fake_handwriting_library) {
204 return g_fake_handwriting_library;
205 }
206 static base::NoDestructor<HandwritingLibraryImpl> instance(model_path);
207 return instance.get();
208}
209
210void HandwritingLibrary::UseFakeHandwritingLibraryForTesting(
211 HandwritingLibrary* const fake_handwriting_library) {
212 g_fake_handwriting_library = fake_handwriting_library;
213}
214
Honglin Yu0c4760a2020-04-18 20:53:31 +1000215} // namespace ml