libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNImageGradient.cpp
Go to the documentation of this file.
1 /* Copyright 2006-2016 Yann LEYDIER, INSA-Lyon
2  *
3  * This file is part of libcrn.
4  *
5  * libcrn is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libcrn is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libcrn. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: CRNImageGradient.cpp
19  * \author Yann LEYDIER
20  */
21 
23 #include <CRNStringUTF8.h>
24 #include <CRNi18n.h>
25 
26 using namespace crn;
27 
34 unsigned int ImageGradient::AutoMinModule(const ImageGray &img)
35 {
36  if ((img.GetWidth() != GetWidth()) || (img.GetHeight() != GetHeight()))
37  {
38  throw ExceptionDimension(
39  StringUTF8("unsigned int ImageGradient::AutoMinModule(const ImageGray &img)") +
40  _("Wrong image dimensions."));
41  }
42 
43  auto S1 = 0.0, S2 = 0.0, C1 = 0.0, C2 = 0.0;
44  auto max = MinMax(img).second;
45 
46  for (auto tmp : Range(img))
47  {
48  S1 += double(At(tmp).rho) * double(img.At(tmp));
49  S2 += double(At(tmp).rho) * double(max - img.At(tmp));
50  C1 += double(img.At(tmp));
51  C2 += double(max - img.At(tmp));
52  }
53 
54  auto moy1 = S1 / C1;
55  auto moy2 = S2 / C2;
56  thresh = (int)ceil((moy1 + moy2) / 2);
57 
58  return thresh;
59 }
60 
61 /*****************************************************************************/
68 {
70  for (auto tmp : Range(img))
71  if (At(tmp).rho < thresh)
72  img.At(tmp) = 127;
73  else
74  img.At(tmp) = At(tmp).theta.value;
75  return img;
76 }
77 
78 /*****************************************************************************/
86 {
87  auto min = std::numeric_limits<unsigned int>::max();
88  auto max = std::numeric_limits<unsigned int>::min();
89  for (auto tmp : Range(*this))
90  {
91  if (At(tmp).rho > max) max = At(tmp).rho;
92  if (At(tmp).rho < min) min = At(tmp).rho;
93  }
94  max -= min;
95  if (max == 0) max = 1;
96  ImageRGB img = ImageRGB(GetWidth(), GetHeight());
97  for (auto tmp : Range(img))
98  {
99  pixel::HSV p;
100  p.h = At(tmp).theta.value;
101  p.s = 255;
102  if (thres && (At(tmp).rho < thresh))
103  p.v = 0;
104  else
105  p.v = uint8_t(((At(tmp).rho - min) * 255) / max);
106  img.At(tmp) = p;
107  }
108  return img;
109 }
110 
118 {
119  auto ans = ImageGray(GetWidth(), GetHeight(), uint8_t(0));
120  for (size_t y = 2; y < GetHeight() - 2; y++)
121  for (size_t x = 2; x < GetWidth() - 2; x++)
122  {
123  if (!IsSignificant(x, y))
124  {
125  ans.At(x, y) = 0;
126  continue;
127  }
128  auto a = At(x, y).theta;
129  const auto gx = a.Cos();
130  const auto gy = a.Sin();
131  // orthogonal, on the right
132  auto px = double(x) - gy;
133  auto py = double(y) + gx;
134  auto cosine = 0.0, sine = 0.0;
135  if (px == floor(px))
136  {
137  if (py == floor(py))
138  {
139  a = At(size_t(px), size_t(py)).theta;
140  cosine = a.Cos();
141  sine = a.Sin();
142  }
143  else
144  {
145  auto frac = py - floor(py);
146  a = At(size_t(px), size_t(floor(py))).theta;
147  cosine = a.Cos() * (1 - frac);
148  sine = a.Sin() * (1 - frac);
149  a = At(size_t(px), size_t(floor(py)) + 1).theta;
150  cosine += a.Cos() * frac;
151  sine += a.Sin() * frac;
152  }
153  }
154  else
155  {
156  if (py == floor(py))
157  {
158  auto frac = px - floor(px);
159  a = At(size_t(floor(px)), size_t(py)).theta;
160  cosine = a.Cos() * (1 - frac);
161  sine = a.Sin() * (1 - frac);
162  a = At(size_t(floor(px)) + 1, size_t(py)).theta;
163  cosine += a.Cos() * frac;
164  sine += a.Sin() * frac;
165  }
166  else
167  {
168  auto fracx = px - floor(px);
169  auto fracy = py - floor(py);
170  a = At(size_t(floor(px)), size_t(floor(py))).theta;
171  cosine = a.Cos() * (1 - fracx) * (1 - fracy);
172  sine = a.Sin() * (1 - fracx) * (1 - fracy);
173  a = At(size_t(floor(px)), size_t(floor(py)) + 1).theta;
174  cosine += a.Cos() * (1 - fracx) * fracy;
175  sine += a.Sin() * (1 - fracx) * fracy;
176  a = At(size_t(floor(px)) + 1, size_t(floor(py)) + 1).theta;
177  cosine += a.Cos() * fracx * fracy;
178  sine += a.Sin() * fracx * fracy;
179  a = At(size_t(floor(px)) + 1, size_t(floor(py))).theta;
180  cosine += a.Cos() * fracx * (1 - fracy);
181  sine += a.Sin() * fracx * (1 - fracy);
182  }
183  }
184  const auto angle1 = Angle<ByteAngle>::Atan(sine, cosine);
185  //uint8_t dist1 = AngularDistance<ByteAngle>(angle1, At(size_t(px), size_t(py)));
186  // orthogonal, on the left
187  px = double(x) + gy;
188  py = double(y) - gx;
189  cosine = 0.0;
190  sine = 0.0;
191  if (px == floor(px))
192  {
193  if (py == floor(py))
194  {
195  a = At(size_t(px), size_t(py)).theta;
196  cosine = a.Cos();
197  sine = a.Sin();
198  }
199  else
200  {
201  double frac = py - floor(py);
202  a = At(size_t(px), size_t(floor(py))).theta;
203  cosine = a.Cos() * (1 - frac);
204  sine = a.Sin() * (1 - frac);
205  a = At(size_t(px), size_t(floor(py)) + 1).theta;
206  cosine += a.Cos() * frac;
207  sine += a.Sin() * frac;
208  }
209  }
210  else
211  {
212  if (py == floor(py))
213  {
214  double frac = px - floor(px);
215  a = At(size_t(floor(px)), size_t(py)).theta;
216  cosine = a.Cos() * (1 - frac);
217  sine = a.Sin() * (1 - frac);
218  a = At(size_t(floor(px)) + 1, size_t(py)).theta;
219  cosine += a.Cos() * frac;
220  sine += a.Sin() * frac;
221  }
222  else
223  {
224  double fracx = px - floor(px);
225  double fracy = py - floor(py);
226  a = At(size_t(floor(px)), size_t(floor(py))).theta;
227  cosine = a.Cos() * (1 - fracx) * (1 - fracy);
228  sine = a.Sin() * (1 - fracx) * (1 - fracy);
229  a = At(size_t(floor(px)), size_t(floor(py)) + 1).theta;
230  cosine += a.Cos() * (1 - fracx) * fracy;
231  sine += a.Sin() * (1 - fracx) * fracy;
232  a = At(size_t(floor(px)) + 1, size_t(floor(py)) + 1).theta;
233  cosine += a.Cos() * fracx * fracy;
234  sine += a.Sin() * fracx * fracy;
235  a = At(size_t(floor(px)) + 1, size_t(floor(py))).theta;
236  cosine += a.Cos() * fracx * (1 - fracy);
237  sine += a.Sin() * fracx * (1 - fracy);
238  }
239  }
240  const auto angle2 = Angle<ByteAngle>::Atan(sine, cosine);
241  //uint8_t dist2 = AngularDistance<ByteAngle>(angle2, thetapixels[size_t(px) + size_t(py) * GetWidth()]);
242  auto dist = AngularDistance<ByteAngle>(angle1, angle2);
243  ans.At(x, y) = (uint8_t)Twice((int)dist);
244  }
245  return ans;
246 }
247 
250 {
251  auto res = ImageBW{GetWidth(), GetHeight(), pixel::BWBlack};
252  for (auto tmp : Range(res))
253  if (IsSignificant(tmp))
254  res.At(tmp) = pixel::BWWhite;
255  return res;
256 }
257 
258 /*****************************************************************************/
264 double ImageGradient::GetHRun() const noexcept
265 {
266  const auto GR_WAIT = 1;
267  const auto GR_WAITLEFT = 2;
268  const auto GR_WAITRIGHT = 3;
269  const auto GR_LEFT = 4;
270  const auto GR_RIGHT = 5;
271  auto mode = 0;
272  std::vector<size_t> rightlist, leftlist;
273  size_t acc;
274  for (size_t y = 0; y < GetHeight(); ++y)
275  {
276  auto angle = At(0 ,y).theta;
277  acc = 0;
278  if (At(0, y) < thresh)
279  {
280  mode = GR_WAIT;
281  }
282  else if (AngularDistance<ByteAngle>(angle, Angle<ByteAngle>::RIGHT()) < 16)
283  {
284  mode = GR_WAITRIGHT;
285  }
286  else if (AngularDistance<ByteAngle>(angle, Angle<ByteAngle>::LEFT()) < 16)
287  {
288  mode = GR_WAITLEFT;
289  }
290  else
291  {
292  mode = GR_WAIT;
293  }
294  for (size_t x = 1; x < GetWidth(); x++)
295  {
296  auto angle = At(x, y).theta;
297  auto pix = GR_WAIT;
298  if (At(x, y).rho < thresh)
299  {
300  pix = GR_WAIT;
301  }
302  else if (AngularDistance<ByteAngle>(angle, Angle<ByteAngle>::RIGHT()) < 16)
303  {
304  pix = GR_RIGHT;
305  }
306  else if (AngularDistance<ByteAngle>(angle, Angle<ByteAngle>::LEFT()) < 16)
307  {
308  pix = GR_LEFT;
309  }
310  switch (mode)
311  {
312  case GR_WAITLEFT:
313  switch (pix)
314  {
315  case GR_WAIT:
316  mode = GR_WAIT;
317  acc = 0;
318  break;
319  case GR_RIGHT:
320  mode = GR_RIGHT;
321  break;
322  }
323  break;
324  case GR_WAITRIGHT:
325  switch (pix)
326  {
327  case GR_WAIT:
328  mode = GR_WAIT;
329  acc = 0;
330  break;
331  case GR_LEFT:
332  mode = GR_LEFT;
333  break;
334  }
335  break;
336  case GR_WAIT:
337  switch (pix)
338  {
339  case GR_RIGHT:
340  mode = GR_RIGHT;
341  acc += 1;
342  break;
343  case GR_LEFT:
344  mode = GR_LEFT;
345  acc += 1;
346  break;
347  }
348  break;
349  case GR_LEFT:
350  switch (pix)
351  {
352  case GR_WAIT:
353  leftlist.push_back(acc);
354  mode = GR_WAIT;
355  acc = 0;
356  break;
357  case GR_RIGHT:
358  leftlist.push_back(acc);
359  mode = GR_RIGHT;
360  acc = 1;
361  break;
362  case GR_LEFT:
363  acc += 1;
364  break;
365  }
366  break;
367  case GR_RIGHT:
368  switch (pix)
369  {
370  case GR_WAIT:
371  rightlist.push_back(acc);
372  mode = GR_WAIT;
373  acc = 0;
374  break;
375  case GR_RIGHT:
376  acc += 1;
377  break;
378  case GR_LEFT:
379  rightlist.push_back(acc);
380  mode = GR_LEFT;
381  acc = 1;
382  break;
383  }
384  break;
385  }
386  }
387  }
388  auto suml = 0.0, sumr = 0.0;
389  for (auto & elem : leftlist)
390  suml += double(elem);
391  if (!leftlist.empty())
392  suml /= double(leftlist.size());
393  for (auto & elem : rightlist)
394  sumr += double(elem);
395  if (!rightlist.empty())
396  sumr /= double(rightlist.size());
397  return (sumr + suml);
398 }
399 
400 /*****************************************************************************/
406 double ImageGradient::GetVRun() const noexcept
407 {
408 const auto GR_WAIT = 1;
409 const auto GR_WAITTOP = 2;
410 const auto GR_WAITBOTTOM = 3;
411 const auto GR_TOP = 4;
412 const auto GR_BOTTOM = 5;
413  auto mode = 0;
414  std::vector<size_t> bottomlist, toplist;
415  size_t acc;
416  for (size_t x = 1; x < GetWidth(); x++)
417  {
418  auto angle = At(x).theta;
419  acc = 0;
420  if (At(x).rho < thresh)
421  {
422  mode = GR_WAIT;
423  }
424  else if (AngularDistance<ByteAngle>(angle, Angle<ByteAngle>::BOTTOM()) < 16)
425  {
426  mode = GR_WAITBOTTOM;
427  }
428  else if (AngularDistance<ByteAngle>(angle, Angle<ByteAngle>::TOP()) < 16)
429  {
430  mode = GR_WAITTOP;
431  }
432  else
433  {
434  mode = GR_WAIT;
435  }
436  for (size_t y = 0; y < GetHeight(); y++)
437  {
438  auto angle = At(x, y).theta;
439  auto pix = GR_WAIT;
440  if (At(x, y) < thresh)
441  {
442  pix = GR_WAIT;
443  }
444  else if (AngularDistance<ByteAngle>(angle, Angle<ByteAngle>::BOTTOM()) < 16)
445  {
446  pix = GR_BOTTOM;
447  }
448  else if (AngularDistance<ByteAngle>(angle, Angle<ByteAngle>::TOP()) < 16)
449  {
450  pix = GR_TOP;
451  }
452  switch (mode)
453  {
454  case GR_WAITTOP:
455  switch (pix)
456  {
457  case GR_WAIT:
458  mode = GR_WAIT;
459  acc = 0;
460  break;
461  case GR_BOTTOM:
462  mode = GR_BOTTOM;
463  break;
464  }
465  break;
466  case GR_WAITBOTTOM:
467  switch (pix)
468  {
469  case GR_WAIT:
470  mode = GR_WAIT;
471  acc = 0;
472  break;
473  case GR_TOP:
474  mode = GR_TOP;
475  break;
476  }
477  break;
478  case GR_WAIT:
479  switch (pix)
480  {
481  case GR_BOTTOM:
482  mode = GR_BOTTOM;
483  acc += 1;
484  break;
485  case GR_TOP:
486  mode = GR_TOP;
487  acc += 1;
488  break;
489  }
490  break;
491  case GR_TOP:
492  switch (pix)
493  {
494  case GR_WAIT:
495  toplist.push_back(acc);
496  mode = GR_WAIT;
497  acc = 0;
498  break;
499  case GR_BOTTOM:
500  toplist.push_back(acc);
501  mode = GR_BOTTOM;
502  acc = 1;
503  break;
504  case GR_TOP:
505  acc += 1;
506  break;
507  }
508  break;
509  case GR_BOTTOM:
510  switch (pix)
511  {
512  case GR_WAIT:
513  bottomlist.push_back(acc);
514  mode = GR_WAIT;
515  acc = 0;
516  break;
517  case GR_BOTTOM:
518  acc += 1;
519  break;
520  case GR_TOP:
521  bottomlist.push_back(acc);
522  mode = GR_TOP;
523  acc = 1;
524  break;
525  }
526  break;
527  }
528  }
529  }
530  auto sumt = 0.0, sumb = 0.0;
531  for (auto & elem : toplist)
532  sumt += double(elem);
533  if (!toplist.empty())
534  sumt /= double(toplist.size());
535  for (auto & elem : bottomlist)
536  sumb += double(elem);
537  if (!bottomlist.empty())
538  sumb /= double(bottomlist.size());
539  return (sumt + sumb);
540 }
541 
ScalarRange< T > Range(T b, T e)
Creates a range [[b, e[[.
Definition: CRNType.h:257
std::vector< pixel_type >::reference At(size_t x, size_t y) noexcept
Returns a reference to a pixel.
Definition: CRNImage.h:224
ImageBW MakeMask() const
Create a mask of the gradients' module.
size_t GetHeight() const noexcept
Definition: CRNImage.h:74
#define _(String)
Definition: CRNi18n.h:51
Image< uint8_t > ImageGray
Grayscale image class.
Image< pixel::RGB< uint8_t >> ImageRGB
Color image class.
double GetHRun() const noexcept
Estimates the mean character width.
ImageGray MakeCurvature() const
Creates an image representing the curvature of the gradients.
ImageGray MakeImageGray() const
Converts to a gray image.
A convenience class for angles units.
ImageRGB MakeImageRGB(bool thres=false) const
Converts to a rgb image.
constexpr TypeInfo< T >::SumType Twice(const T &v) noexcept(noexcept(v+v))
Returns the double of a value.
Definition: CRNMath.h:55
A dimension error.
Definition: CRNException.h:119
bool IsSignificant(size_t i) const
Tests if a pixel has a significant gradient.
std::pair< T, T > MinMax(const Image< T > &img, CMP cmp=CMP{})
Returns min and max pixel values.
Definition: CRNImage.hpp:1447
size_t GetWidth() const noexcept
Definition: CRNImage.h:72
unsigned int AutoMinModule(const ImageGray &img)
Computes the module significance threshold.
A character string class.
Definition: CRNStringUTF8.h:49
double GetVRun() const noexcept
Estimates the mean character height.
static Angle Atan(double tangent) noexcept(std::is_nothrow_constructible< typename Unit::type >::value)
Computes arc tangent.