$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
deskew.hh
1 // Copyright (C) 2010, 2013 EPITA Research and Development Laboratory (LRDE)
2 //
3 // This file is part of Olena.
4 //
5 // Olena is free software: you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation, version 2 of the License.
8 //
9 // Olena is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
16 //
17 // As a special exception, you may use this file as part of a free
18 // software project without restriction. Specifically, if other files
19 // instantiate templates or use macros or inline functions from this
20 // file, or you compile this file and link it with other files to produce
21 // an executable, this file does not by itself cause the resulting
22 // executable to be covered by the GNU General Public License. This
23 // exception does not however invalidate any other reasons why the
24 // executable file might be covered by the GNU General Public License.
25 
26 #ifndef SCRIBO_PREPROCESSING_DESKEW_HH
27 # define SCRIBO_PREPROCESSING_DESKEW_HH
28 
32 
34 
35 
36 # include <queue>
37 
38 # include <mln/core/image/image2d.hh>
39 # include <mln/math/pi.hh>
40 # include <mln/geom/rotate.hh>
41 # include <mln/value/int_u8.hh>
42 
43 namespace scribo
44 {
45 
46  namespace preprocessing
47  {
48 
49  using namespace mln;
50 
51  namespace internal
52  {
53 
54  class Hough
55  {
56  public:
57  Hough(int width, int height);
58 
59  ~Hough();
60 
61  void look_up_table();
62 
63  int width() const;
64 
65  int height() const;
66 
67  double mtheta() const;
68  double mrho() const;
69 
70  int mrhoi() const;
71  int mthetai() const;
72 
73  double get_cos(int index) const;
74  double get_sin(int index) const;
75 
76  image2d<unsigned>& acc();
77 
78  private:
79  int width_;
80  int height_;
81 
82  double max_rho_;
83  double max_theta_;
84 
85  int max_rho_index_;
86  int max_theta_index_;
87 
88  double* cos_;
89  double* sin_;
90 
91  image2d<unsigned> acc_;
92  };
93 
94 
95  struct s_angle
96  {
97  int pos;
98  unsigned max;
99  };
100 
101 
102  struct QCompare
103  {
104  bool operator()(const s_angle& s1, const s_angle& s2);
105  };
106 
107  } // end of namespace scribo::preprocessing::internal
108 
109 
110 
111 # ifndef MLN_INCLUDE_ONLY
112 
113  namespace internal
114  {
115 
116  inline
117  bool
118  QCompare::operator()(const s_angle& s1, const s_angle& s2)
119  {
120  return (s1.max > s2.max);
121  }
122 
123 
124  inline
125  Hough::Hough(int width, int height)
126  : width_(width / 2),
127  height_(height / 2),
128  max_rho_(sqrt((width * width) + (height * height))),
129  max_theta_(math::pi),
130  max_rho_index_(int(this->max_rho_) + 1),
131  max_theta_index_(500),
132  acc_(this->max_rho_index_, this->max_theta_index_)
133  {
134  look_up_table();
135  }
136 
137 
138  inline
139  Hough::~Hough()
140  {
141  delete[] this->cos_;
142  delete[] this->sin_;
143  }
144 
145 
146  inline
147  void Hough::look_up_table()
148  {
149  this->cos_ = new double[this->max_theta_index_];
150  this->sin_ = new double[this->max_theta_index_];
151 
152  for (int i = 0; i < this->max_theta_index_; ++i)
153  {
154  double i_val = (i + 650) * this->max_theta_ / 1800.0f;
155 
156  this->cos_[i] = cos(i_val);
157  this->sin_[i] = sin(i_val);
158  }
159  }
160 
161  inline
162  int Hough::width() const
163  {
164  return this->width_;
165  }
166 
167  inline
168  int Hough::height() const
169  {
170  return this->height_;
171  }
172 
173  inline
174  double Hough::mtheta() const
175  {
176  return this->max_theta_;
177  }
178 
179  inline
180  double Hough::mrho() const
181  {
182  return this->max_rho_;
183  }
184 
185  inline
186  int Hough::mrhoi() const
187  {
188  return this->max_rho_index_;
189  }
190 
191  inline
192  int Hough::mthetai() const
193  {
194  return this->max_theta_index_;
195  }
196 
197  inline
198  double Hough::get_cos(int index) const
199  {
200  return this->cos_[index];
201  }
202 
203  inline
204  double Hough::get_sin(int index) const
205  {
206  return this->sin_[index];
207  }
208 
209  inline
210  image2d<unsigned>& Hough::acc()
211  {
212  return this->acc_;
213  }
214 
215  inline
216  static
217  void
218  vote(int x, int y, Hough& hough, int theta)
219  {
220  int theta_min = std::max(theta - 25, 0);
221  int theta_max = std::min(theta + 25, hough.mthetai());
222 
223  x -= hough.width();
224  y -= hough.height();
225 
226  for (int i = theta_min; i < theta_max; ++i)
227  {
228  double rho = x * hough.get_cos(i) + y * hough.get_sin(i);
229  double rho_index = (0.5 + (rho / hough.mrho() + 0.5)
230  * hough.mrhoi());
231 
232  ++(opt::at(hough.acc(),
233  static_cast<mln::def::coord>(rho_index), i));
234  }
235  }
236 
237 
238  inline
239  static
240  void
241  init_hist(Hough& hough, int hist[500],
242  std::priority_queue<s_angle, std::vector<s_angle>, QCompare>& q,
243  int nb_elm)
244  {
245  int max_rho = hough.mrhoi();
246  int max_theta = hough.mthetai();
247  unsigned max_elm = (nb_elm > max_rho) ? (nb_elm / max_rho) << 5 : 1;
248 
249  for (int j = 0; j < max_theta; ++j)
250  {
251  hist[j] = 0;
252 
253  if (q.size() < max_elm)
254  {
255  if (opt::at(hough.acc(), 0, j) > 0)
256  {
257  s_angle s;
258 
259  s.max = opt::at(hough.acc(), 0, j);
260  s.pos = j;
261 
262  q.push(s);
263  }
264  }
265  else if (opt::at(hough.acc(), 0, j) > q.top().max)
266  {
267  s_angle s;
268 
269  s.max = opt::at(hough.acc(), 0, j);
270  s.pos = j;
271 
272  q.pop();
273  q.push(s);
274  }
275  }
276  }
277 
278 
279  inline
280  static
281  double
282  get_max(Hough& hough, int hist[500],
283  std::priority_queue<s_angle, std::vector<s_angle>, QCompare>& q,
284  int nb_elm)
285  {
286  int max = 0;
287  int h_value = 0;
288  int max_rho = hough.mrhoi();
289  int max_theta = hough.mthetai();
290  double pos = 0.f;
291  unsigned max_elm = (nb_elm > max_rho) ? (nb_elm / max_rho) << 5 : 1;
292 
293 
294  for (int i = 1; i < max_rho; ++i)
295  {
296  for (int j = 0; j < max_theta; ++j)
297  {
298  if (q.size() < max_elm)
299  {
300  if (opt::at(hough.acc(), i, j) > 0)
301  {
302  s_angle s;
303 
304  s.max = opt::at(hough.acc(), i, j);
305  s.pos = j;
306 
307  q.push(s);
308  }
309  }
310  else if (opt::at(hough.acc(), i, j) > q.top().max)
311  {
312  s_angle s;
313 
314  s.max = opt::at(hough.acc(), i, j);
315  s.pos = j;
316 
317  q.pop();
318  q.push(s);
319  }
320  }
321  }
322 
323  while (!q.empty())
324  {
325  hist[q.top().pos] += q.top().max;
326  h_value = hist[q.top().pos];
327 
328  if (h_value > max)
329  {
330  max = h_value;
331  pos = q.top().pos;
332  }
333 
334  q.pop();
335  }
336 
337  return pos;
338  }
339 
340 
341  inline
342  double
343  perform_deskew(const image2d<value::int_u8>& gray)
344  {
345  Hough hough(gray.ncols(), gray.nrows());
346  std::priority_queue<s_angle, std::vector<s_angle>, QCompare> q;
347  int hist[500];
348  int nb_elm = 0;
349 
350  for (unsigned i = 0; i < gray.nrows() - 1; ++i)
351  {
352  for (unsigned j = 1; j < gray.ncols() - 1; ++j)
353  {
354  unsigned up = 1;
355  unsigned down = 1;
356  unsigned mean = ((opt::at(gray, i, j) * opt::at(gray, i + 1, j))) >> 8;
357 
358  for (unsigned k = j - 1; k <= j + 1; ++k)
359  {
360  up *= opt::at(gray, i, k);
361  down *= opt::at(gray, i + 1, k);
362  }
363 
364  up = 255 - (up >> 16);
365  down = down >> 16;
366 
367  if (up > down && down > mean && down > 130)
368  {
369 
370  ++nb_elm;
371  double gy = opt::at(gray, i - 1, j - 1) + 2 * opt::at(gray, i - 1, j) +
372  opt::at(gray, i - 1, j + 1);
373  gy += -opt::at(gray, i + 1, j - 1) - 2 * opt::at(gray, i + 1, j) -
374  opt::at(gray, i + 1, j + 1);
375 
376  double gx = opt::at(gray, i - 1, j - 1) + 2 * opt::at(gray, i, j - 1) +
377  opt::at(gray, i + 1, j - 1);
378  gx += -opt::at(gray, i - 1, j + 1) - 2 * opt::at(gray, i, j + 1) -
379  opt::at(gray, i + 1, j + 1);
380 
381  double tanv = (math::pi / 2.0 - atan(gy / gx)) * 180.0 / math::pi;
382 
383  if (tanv <= 25.0 || tanv >= 155.0)
384  {
385  ++nb_elm;
386  vote(j, i, hough, int((tanv <= 25.0 ? 250.0 - tanv * 10.0 :
387  (180.0 - tanv) * 10.0 + 250.0)));
388  }
389  }
390  }
391  }
392 
393  init_hist(hough, hist, q, nb_elm);
394 
395  return 90 - (get_max(hough, hist, q, nb_elm) + 650) / 10;
396  }
397 
398 
399 
400  } // end of namespace scribo::preprocessing::internal
401 
402 
403 
404  template <typename I>
405  mln_concrete(I)
406  deskew(const Image<I>& input_gl_)
407  {
408  const I& input_gl = exact(input_gl_);
409 
410  mln_trace("scribo::preprocessing::deskew");
411  mln_assertion(input_gl.is_valid());
412  mlc_is(mln_domain(I), box2d)::check();
413  mlc_is_not(mln_value(I), bool)::check();
414  mlc_is_not_a(mln_value(I), value::Vectorial)::check();
415 
416  double angle = internal::perform_deskew(input_gl);
417 
418  mln_concrete(I) output = input_gl;
419 
420  // FIXME: trick to make this routine faster for really small
421  // angles (no impact on the results)
422  if (angle > 0.5 || angle < -0.5)
423  output = geom::rotate(input_gl, - angle,
424  //mln_max(mln_value(I)),
425  extend(input_gl, mln_min(mln_value(I))),
426  mln::make::box2d(input_gl.nrows(),
427  input_gl.ncols()));
428 
429  return output;
430  }
431 
432 
433 # endif // ! MLN_INCLUDE_ONLY
434 
435 
436  } // end of namespace scribo::preprocessing
437 
438 } // end of namespace scribo
439 
440 
441 # endif // SCRIBO_PREPROCESSING_DESKEW_HH