tesseract  3.05.02
word_size_model.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * File: word_size_model.cpp
3  * Description: Implementation of the Word Size Model Class
4  * Author: Ahmad Abdulkader
5  * Created: 2008
6  *
7  * (C) Copyright 2008, Google Inc.
8  ** Licensed under the Apache License, Version 2.0 (the "License");
9  ** you may not use this file except in compliance with the License.
10  ** You may obtain a copy of the License at
11  ** http://www.apache.org/licenses/LICENSE-2.0
12  ** Unless required by applicable law or agreed to in writing, software
13  ** distributed under the License is distributed on an "AS IS" BASIS,
14  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  ** See the License for the specific language governing permissions and
16  ** limitations under the License.
17  *
18  **********************************************************************/
19 
20 #include <math.h>
21 #include <string>
22 #include <vector>
23 #include "word_size_model.h"
24 #include "cube_utils.h"
25 
26 namespace tesseract {
27 
28 WordSizeModel::WordSizeModel(CharSet * char_set, bool contextual) {
29  char_set_ = char_set;
30  contextual_ = contextual;
31 }
32 
34  for (int fnt = 0; fnt < font_pair_size_models_.size(); fnt++) {
35  FontPairSizeInfo fnt_info = font_pair_size_models_[fnt];
36  delete []fnt_info.pair_size_info[0];
37  delete []fnt_info.pair_size_info;
38  }
39 }
40 
41 WordSizeModel *WordSizeModel::Create(const string &data_file_path,
42  const string &lang,
43  CharSet *char_set,
44  bool contextual) {
45  WordSizeModel *obj = new WordSizeModel(char_set, contextual);
46 
47  if (!obj->Init(data_file_path, lang)) {
48  delete obj;
49  return NULL;
50  }
51  return obj;
52 }
53 
54 bool WordSizeModel::Init(const string &data_file_path, const string &lang) {
55  string stats_file_name;
56  stats_file_name = data_file_path + lang;
57  stats_file_name += ".cube.size";
58 
59  // read file to memory
60  string str_data;
61 
62  if (!CubeUtils::ReadFileToString(stats_file_name, &str_data)) {
63  return false;
64  }
65 
66  // split to words
67  vector<string> tokens;
68  CubeUtils::SplitStringUsing(str_data, "\t\r\n", &tokens);
69  if (tokens.size() < 1) {
70  fprintf(stderr, "Cube ERROR (WordSizeModel::Init): invalid "
71  "file contents: %s\n", stats_file_name.c_str());
72  return false;
73  }
74 
75  font_pair_size_models_.clear();
76 
77  // token count per line depends on whether the language is contextual or not
78  int token_cnt = contextual_ ?
79  (kExpectedTokenCount + 4) : kExpectedTokenCount;
80  // the count of size classes depends on whether the language is contextual
81  // or not. For non contextual languages (Ex: Eng), it is equal to the class
82  // count. For contextual languages (Ex: Ara), it is equal to the class count
83  // multiplied by the position count (4: start, middle, final, isolated)
84  int size_class_cnt = contextual_ ?
85  (char_set_->ClassCount() * 4) : char_set_->ClassCount();
86  string fnt_name = "";
87 
88  for (int tok = 0; tok < tokens.size(); tok += token_cnt) {
89  // a new font, write the old font data and re-init
90  if (tok == 0 || fnt_name != tokens[tok]) {
91  FontPairSizeInfo fnt_info;
92 
93  fnt_info.pair_size_info = new PairSizeInfo *[size_class_cnt];
94 
95  fnt_info.pair_size_info[0] =
96  new PairSizeInfo[size_class_cnt * size_class_cnt];
97 
98  memset(fnt_info.pair_size_info[0], 0, size_class_cnt * size_class_cnt *
99  sizeof(PairSizeInfo));
100 
101  for (int cls = 1; cls < size_class_cnt; cls++) {
102  fnt_info.pair_size_info[cls] =
103  fnt_info.pair_size_info[cls - 1] + size_class_cnt;
104  }
105 
106  // strip out path and extension
107  string stripped_font_name = tokens[tok].substr(0, tokens[tok].find('.'));
108  string::size_type strt_pos = stripped_font_name.find_last_of("/\\");
109  if (strt_pos != string::npos) {
110  fnt_info.font_name = stripped_font_name.substr(strt_pos);
111  } else {
112  fnt_info.font_name = stripped_font_name;
113  }
114  font_pair_size_models_.push_back(fnt_info);
115  }
116 
117  // parse the data
118  int cls_0;
119  int cls_1;
120  double delta_top;
121  double wid_0;
122  double hgt_0;
123  double wid_1;
124  double hgt_1;
125  int size_code_0;
126  int size_code_1;
127 
128  // read and parse the tokens
129  if (contextual_) {
130  int start_0;
131  int end_0;
132  int start_1;
133  int end_1;
134  // The expected format for a character size bigram is as follows:
135  // ClassId0<delim>Start-flag0<delim>End-flag0<delim>String0(ignored)
136  // Width0<delim>Height0<delim>
137  // ClassId1<delim>Start-flag1<delim>End-flag1<delim>String1(ignored)
138  // HeightDelta<delim>Width1<delim>Height0<delim>
139  // In case of non-contextual languages, the Start and End flags are
140  // omitted
141  if (sscanf(tokens[tok + 1].c_str(), "%d", &cls_0) != 1 ||
142  sscanf(tokens[tok + 2].c_str(), "%d", &start_0) != 1 ||
143  sscanf(tokens[tok + 3].c_str(), "%d", &end_0) != 1 ||
144  sscanf(tokens[tok + 5].c_str(), "%lf", &wid_0) != 1 ||
145  sscanf(tokens[tok + 6].c_str(), "%lf", &hgt_0) != 1 ||
146  sscanf(tokens[tok + 7].c_str(), "%d", &cls_1) != 1 ||
147  sscanf(tokens[tok + 8].c_str(), "%d", &start_1) != 1 ||
148  sscanf(tokens[tok + 9].c_str(), "%d", &end_1) != 1 ||
149  sscanf(tokens[tok + 11].c_str(), "%lf", &delta_top) != 1 ||
150  sscanf(tokens[tok + 12].c_str(), "%lf", &wid_1) != 1 ||
151  sscanf(tokens[tok + 13].c_str(), "%lf", &hgt_1) != 1 ||
152  (start_0 != 0 && start_0 != 1) || (end_0 != 0 && end_0 != 1) ||
153  (start_1 != 0 && start_1 != 1) || (end_1 != 0 && end_1 != 1)) {
154  fprintf(stderr, "Cube ERROR (WordSizeModel::Init): bad format at "
155  "line %d\n", 1 + (tok / token_cnt));
156  return false;
157  }
158  size_code_0 = SizeCode(cls_0, start_0, end_0);
159  size_code_1 = SizeCode(cls_1, start_1, end_1);
160  } else {
161  if (sscanf(tokens[tok + 1].c_str(), "%d", &cls_0) != 1 ||
162  sscanf(tokens[tok + 3].c_str(), "%lf", &wid_0) != 1 ||
163  sscanf(tokens[tok + 4].c_str(), "%lf", &hgt_0) != 1 ||
164  sscanf(tokens[tok + 5].c_str(), "%d", &cls_1) != 1 ||
165  sscanf(tokens[tok + 7].c_str(), "%lf", &delta_top) != 1 ||
166  sscanf(tokens[tok + 8].c_str(), "%lf", &wid_1) != 1 ||
167  sscanf(tokens[tok + 9].c_str(), "%lf", &hgt_1) != 1) {
168  fprintf(stderr, "Cube ERROR (WordSizeModel::Init): bad format at "
169  "line %d\n", 1 + (tok / token_cnt));
170  return false;
171  }
172  size_code_0 = cls_0;
173  size_code_1 = cls_1;
174  }
175 
176  // copy the data to the size tables
177  FontPairSizeInfo fnt_info = font_pair_size_models_.back();
178  fnt_info.pair_size_info[size_code_0][size_code_1].delta_top =
179  static_cast<int>(delta_top * kShapeModelScale);
180  fnt_info.pair_size_info[size_code_0][size_code_1].wid_0 =
181  static_cast<int>(wid_0 * kShapeModelScale);
182  fnt_info.pair_size_info[size_code_0][size_code_1].hgt_0 =
183  static_cast<int>(hgt_0 * kShapeModelScale);
184  fnt_info.pair_size_info[size_code_0][size_code_1].wid_1 =
185  static_cast<int>(wid_1 * kShapeModelScale);
186  fnt_info.pair_size_info[size_code_0][size_code_1].hgt_1 =
187  static_cast<int>(hgt_1 * kShapeModelScale);
188 
189  fnt_name = tokens[tok];
190  }
191 
192  return true;
193 }
194 
195 int WordSizeModel::Cost(CharSamp **samp_array, int samp_cnt) const {
196  if (samp_cnt < 2) {
197  return 0;
198  }
199  double best_dist = static_cast<double>(WORST_COST);
200  int best_fnt = -1;
201  for (int fnt = 0; fnt < font_pair_size_models_.size(); fnt++) {
202  const FontPairSizeInfo *fnt_info = &font_pair_size_models_[fnt];
203  double mean_dist = 0;
204  int pair_cnt = 0;
205 
206  for (int smp_0 = 0; smp_0 < samp_cnt; smp_0++) {
207  int cls_0 = char_set_->ClassID(samp_array[smp_0]->StrLabel());
208  if (cls_0 < 1) {
209  continue;
210  }
211  // compute size code for samp 0 based on class id and position
212  int size_code_0;
213  if (contextual_) {
214  size_code_0 = SizeCode(cls_0,
215  samp_array[smp_0]->FirstChar() == 0 ? 0 : 1,
216  samp_array[smp_0]->LastChar() == 0 ? 0 : 1);
217  } else {
218  size_code_0 = cls_0;
219  }
220 
221  int char0_height = samp_array[smp_0]->Height();
222  int char0_width = samp_array[smp_0]->Width();
223  int char0_top = samp_array[smp_0]->Top();
224 
225  for (int smp_1 = smp_0 + 1; smp_1 < samp_cnt; smp_1++) {
226  int cls_1 = char_set_->ClassID(samp_array[smp_1]->StrLabel());
227  if (cls_1 < 1) {
228  continue;
229  }
230  // compute size code for samp 0 based on class id and position
231  int size_code_1;
232  if (contextual_) {
233  size_code_1 = SizeCode(cls_1,
234  samp_array[smp_1]->FirstChar() == 0 ? 0 : 1,
235  samp_array[smp_1]->LastChar() == 0 ? 0 : 1);
236  } else {
237  size_code_1 = cls_1;
238  }
239  double dist = PairCost(
240  char0_width, char0_height, char0_top, samp_array[smp_1]->Width(),
241  samp_array[smp_1]->Height(), samp_array[smp_1]->Top(),
242  fnt_info->pair_size_info[size_code_0][size_code_1]);
243  if (dist > 0) {
244  mean_dist += dist;
245  pair_cnt++;
246  }
247  } // smp_1
248  } // smp_0
249  if (pair_cnt == 0) {
250  continue;
251  }
252  mean_dist /= pair_cnt;
253  if (best_fnt == -1 || mean_dist < best_dist) {
254  best_dist = mean_dist;
255  best_fnt = fnt;
256  }
257  }
258  if (best_fnt == -1) {
259  return static_cast<int>(WORST_COST);
260  } else {
261  return static_cast<int>(best_dist);
262  }
263 }
264 
265 double WordSizeModel::PairCost(int width_0, int height_0, int top_0,
266  int width_1, int height_1, int top_1,
267  const PairSizeInfo& pair_info) {
268  double scale_factor = static_cast<double>(pair_info.hgt_0) /
269  static_cast<double>(height_0);
270  double dist = 0.0;
271  if (scale_factor > 0) {
272  double norm_width_0 = width_0 * scale_factor;
273  double norm_width_1 = width_1 * scale_factor;
274  double norm_height_1 = height_1 * scale_factor;
275  double norm_delta_top = (top_1 - top_0) * scale_factor;
276 
277  // accumulate the distance between the model character and the
278  // predicted one on all dimensions of the pair
279  dist += fabs(pair_info.wid_0 - norm_width_0);
280  dist += fabs(pair_info.wid_1 - norm_width_1);
281  dist += fabs(pair_info.hgt_1 - norm_height_1);
282  dist += fabs(pair_info.delta_top - norm_delta_top);
283  }
284  return dist;
285 }
286 } // namespace tesseract
unsigned short Height() const
Definition: bmp_8.h:50
static int SizeCode(int cls_id, int start, int end)
static double PairCost(int width_0, int height_0, int top_0, int width_1, int height_1, int top_1, const PairSizeInfo &pair_info)
unsigned short Width() const
Definition: bmp_8.h:48
static bool ReadFileToString(const string &file_name, string *str)
Definition: cube_utils.cpp:189
#define WORST_COST
Definition: cube_const.h:30
PairSizeInfo ** pair_size_info
unsigned short Top() const
Definition: char_samp.h:48
int ClassCount() const
Definition: char_set.h:111
int Cost(CharSamp **samp_array, int samp_cnt) const
WordSizeModel(CharSet *, bool contextual)
static void SplitStringUsing(const string &str, const string &delims, vector< string > *str_vec)
Definition: cube_utils.cpp:220
static WordSizeModel * Create(const string &data_file_path, const string &lang, CharSet *char_set, bool contextual)
int ClassID(const char_32 *str) const
Definition: char_set.h:54