libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNDifferential.cpp
Go to the documentation of this file.
1 /* Copyright 2006-2014 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: CRNDifferential.cpp
19  * \author Yann LEYDIER
20  */
21 
23 #include <vector>
24 #include <math.h>
25 #include <CRNException.h>
26 #include <CRNImage/CRNImageRGB.h>
28 #include <CRNi18n.h>
29 
30 using namespace crn;
31 
33  srcigray(std::move(src)),
34  lx(std::move(xdiff)),
35  ly(std::move(ydiff)),
36  lxx(std::move(xxdiff)),
37  lyy(std::move(yydiff)),
38  thres(0),
39  lazydata(std::make_unique<std::mutex>())
40 {
41  lx2ly2 = ImageDoubleGray(lx.GetWidth(), lx.GetHeight());
42  updateLx2ly2();
43 }
44 
46  srcigray(std::move(src)),
47  lx(std::move(xdiff)),
48  ly(std::move(ydiff)),
49  thres(0),
50  lazydata(std::make_unique<std::mutex>())
51 {
52  lx2ly2 = ImageDoubleGray(lx.GetWidth(), lx.GetHeight());
53  updateLx2ly2();
54 }
55 
56 static std::tuple<ImageDoubleGray, ImageDoubleGray> derivate1(const ImageDoubleGray &src)
57 {
58  auto diff1 = MatrixDouble({-1, 0, 1}, Orientation::HORIZONTAL);
59  auto lx = src;
60  lx.Convolve(diff1);
61  diff1.Transpose();
62  auto ly = src;
63  ly.Convolve(diff1);
64  return std::make_tuple(std::move(lx), std::move(ly));
65 }
66 
67 static std::tuple<ImageDoubleGray, ImageDoubleGray, ImageDoubleGray, ImageDoubleGray> derivate2(const ImageDoubleGray &src)
68 {
69  auto diff1 = MatrixDouble({-1, 0, 1}, Orientation::HORIZONTAL);
70  auto diff2 = MatrixDouble({1, -2, 1}, Orientation::HORIZONTAL);
71  auto lx = src;
72  lx.Convolve(diff1);
73  diff1.Transpose();
74  auto ly = src;
75  ly.Convolve(diff1);
76  auto lxx = src;
77  lxx.Convolve(diff2);
78  diff2.Transpose();
79  auto lyy = src;
80  lyy.Convolve(diff2);
81  return std::make_tuple(std::move(lx), std::move(ly), std::move(lxx), std::move(lyy));
82 }
83 
84 static std::tuple<ImageDoubleGray, ImageDoubleGray> derivate1Gauss(const ImageDoubleGray &src, double sigma)
85 {
86  if (sigma == 0)
87  return derivate1(src);
88 
89  auto smooth = MatrixDouble::NewGaussianLine(sigma);
90  smooth.NormalizeForConvolution();
91  auto diff1 = MatrixDouble::NewGaussianLineDerivative(sigma);
92  diff1.NormalizeForConvolution();
93 
94  auto ly = src;
95  ly.Convolve(smooth);
96  smooth.Transpose();
97  auto lx = src;
98  lx.Convolve(smooth);
99  lx.Convolve(diff1);
100  diff1.Transpose();
101  ly.Convolve(diff1);
102  return std::make_tuple(std::move(lx), std::move(ly));
103 }
104 
105 static std::tuple<ImageDoubleGray, ImageDoubleGray, ImageDoubleGray, ImageDoubleGray> derivate2Gauss(const ImageDoubleGray &src, double sigma)
106 {
107  if (sigma == 0)
108  return derivate2(src);
109 
110  auto smooth = MatrixDouble::NewGaussianLine(sigma);
111  smooth.NormalizeForConvolution();
112  auto diff1 = MatrixDouble::NewGaussianLineDerivative(sigma);
113  diff1.NormalizeForConvolution();
115  diff2.NormalizeForConvolution();
116 
117  auto ly = src;
118  ly.Convolve(smooth);
119  auto lyy = src;
120  lyy.Convolve(smooth);
121  smooth.Transpose();
122  auto lx = src;
123  lx.Convolve(smooth);
124  lx.Convolve(diff1);
125  diff1.Transpose();
126  ly.Convolve(diff1);
127  auto lxx = src;
128  lxx.Convolve(smooth);
129  lxx.Convolve(diff2);
130  diff2.Transpose();
131  lyy.Convolve(diff2);
132  return std::make_tuple(std::move(lx), std::move(ly), std::move(lxx), std::move(lyy));
133 }
134 
136 {
137  for (auto tmp : Range(r))
138  r.At(tmp) += g.At(tmp) + b.At(tmp);
139  return r;
140 }
141 
143 {
144  for (auto tmp : Range(r))
145  r.At(tmp) = AbsMax(AbsMax(r.At(tmp), g.At(tmp)), b.At(tmp));
146  return r;
147 }
148 
149 static ImageDoubleGray& absmax(ImageDoubleGray &i1, ImageDoubleGray &i2)
150 {
151  for (auto tmp : Range(i1))
152  i1.At(tmp) = AbsMax(i1.At(tmp), i2.At(tmp));
153  return i1;
154 }
155 
156 static ImageDoubleGray& absmin(ImageDoubleGray &i1, ImageDoubleGray &i2)
157 {
158  for (auto tmp : Range(i1))
159  i1.At(tmp) = AbsMin(i1.At(tmp), i2.At(tmp));
160  return i1;
161 }
162 
164 {
165  for (size_t y = 0; y < i.GetHeight(); ++y)
166  {
167  for (size_t x = i.GetWidth() - 1; x >= 1; --x)
168  i.At(x, y) -= i.At(x - 1, y);
169  i.At(0, y) = i.At(1, y);
170  }
171 }
172 
174 {
175  for (size_t y = 0; y < i.GetHeight(); ++y)
176  {
177  for (size_t x = 0; x < i.GetWidth() - 1; ++x)
178  i.At(x, y) -= i.At(x + 1, y);
179  i.At(i.GetWidth() - 1, y) = i.At(i.GetWidth() - 2, y);
180  }
181 }
182 
184 {
185  for (size_t y = i.GetHeight() - 1; y >= 1; --y)
186  {
187  for (size_t x = 0; x < i.GetWidth(); ++x)
188  i.At(x, y) -= i.At(x, y - 1);
189  }
190  for (size_t x = 0; x < i.GetWidth(); ++x)
191  i.At(x, 0) = i.At(x, 1);
192 }
193 
195 {
196  for (size_t y = 0; y < i.GetHeight() - 1; ++y)
197  {
198  for (size_t x = 0; x < i.GetWidth(); ++x)
199  i.At(x, y) -= i.At(x, y + 1);
200  }
201  for (size_t x = 0; x < i.GetWidth(); ++x)
202  i.At(x, i.GetHeight() - 1) -= i.At(x, i.GetHeight() - 2);
203 }
204 
213 {
214  if (proj == RGBProjection::ABSMAX)
215  {
216  auto ig = ImageDoubleGray(src.GetWidth(), src.GetHeight());
217  for (auto tmp : Range(src))
218  ig.At(tmp) = src.At(tmp).r;
219  auto rdiff = derivate1Gauss(ig, sigma);
220  for (auto tmp : Range(src))
221  ig.At(tmp) = src.At(tmp).g;
222  auto gdiff = derivate1Gauss(ig, sigma);
223  for (auto tmp : Range(src))
224  ig.At(tmp) = src.At(tmp).b;
225  auto bdiff = derivate1Gauss(ig, sigma);
226  return Differential(MakeImageGray(src), std::move(absmax(std::get<0>(rdiff), std::get<0>(gdiff), std::get<0>(bdiff))),
227  std::move(absmax(std::get<1>(rdiff), std::get<1>(gdiff), std::get<1>(bdiff))));
228  }
229  else
230  {
231  auto ig = ImageDoubleGray(src.GetWidth(), src.GetHeight());
232  for (auto tmp : Range(src))
233  ig.At(tmp) = src.At(tmp).r;
234  auto rdiff = derivate2Gauss(ig, sigma);
235  for (auto tmp : Range(src))
236  ig.At(tmp) = src.At(tmp).g;
237  auto gdiff = derivate2Gauss(ig, sigma);
238  for (auto tmp : Range(src))
239  ig.At(tmp) = src.At(tmp).b;
240  auto bdiff = derivate2Gauss(ig, sigma);
241  return Differential(MakeImageGray(src), std::move(sumrgb(std::get<0>(rdiff), std::get<0>(gdiff), std::get<0>(bdiff))),
242  std::move(sumrgb(std::get<1>(rdiff), std::get<1>(gdiff), std::get<1>(bdiff))),
243  std::move(sumrgb(std::get<2>(rdiff), std::get<2>(gdiff), std::get<2>(bdiff))),
244  std::move(sumrgb(std::get<3>(rdiff), std::get<3>(gdiff), std::get<3>(bdiff))));
245  }
246 }
247 
254 {
255  auto irx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
256  for (auto tmp : Range(src))
257  irx.At(tmp) = src.At(tmp).r;
258  auto iry = irx;
259  halfdiffleft(irx);
260  halfdifftop(iry);
261  auto igx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
262  for (auto tmp : Range(src))
263  igx.At(tmp) = src.At(tmp).g;
264  auto igy = igx;
265  halfdiffleft(igx);
266  halfdifftop(igy);
267  auto ibx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
268  for (auto tmp : Range(src))
269  ibx.At(tmp) = src.At(tmp).b;
270  auto iby = ibx;
271  halfdiffleft(ibx);
272  halfdifftop(iby);
273  if (proj == RGBProjection::ABSMAX)
274  {
275  auto& lx = absmax(irx, igx, ibx);
276  auto& ly = absmax(iry, igy, iby);
277  auto lxx = lx;
278  halfdiffright(lxx);
279  auto lyy = ly;
280  halfdiffbottom(lyy);
281  return Differential(MakeImageGray(src), std::move(lx), std::move(ly), std::move(lxx), std::move(lyy));
282  }
283  else
284  {
285  auto& lx = sumrgb(irx, igx, ibx);
286  auto& ly = sumrgb(iry, igy, iby);
287  auto lxx = lx;
288  halfdiffright(lxx);
289  auto lyy = ly;
290  halfdiffbottom(lyy);
291  return Differential(MakeImageGray(src), std::move(lx), std::move(ly), std::move(lxx), std::move(lyy));
292  }
293 }
294 
300 {
301  auto irx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
302  for (auto tmp : Range(src))
303  irx.At(tmp) = src.At(tmp).r;
304  auto itmp = irx;
305  auto iry = irx;
306  halfdiffleft(irx);
307  halfdiffright(itmp);
308  absmax(irx, itmp);
309  itmp = iry;
310  halfdifftop(iry);
311  halfdiffbottom(itmp);
312  absmax(iry, itmp);
313 
314  auto igx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
315  for (auto tmp : Range(src))
316  igx.At(tmp) = src.At(tmp).g;
317  itmp = igx;
318  auto igy = igx;
319  halfdiffleft(igx);
320  halfdiffright(itmp);
321  absmax(igx, itmp);
322  itmp = igy;
323  halfdifftop(igy);
324  halfdiffbottom(itmp);
325  absmax(igy, itmp);
326 
327  auto ibx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
328  for (auto tmp : Range(src))
329  ibx.At(tmp) = src.At(tmp).b;
330  itmp = ibx;
331  auto iby = ibx;
332  halfdiffleft(ibx);
333  halfdiffright(itmp);
334  absmax(ibx, itmp);
335  itmp = iby;
336  halfdifftop(iby);
337  halfdiffbottom(itmp);
338  absmax(iby, itmp);
339 
340  if (proj == RGBProjection::ABSMAX)
341  return Differential(MakeImageGray(src), std::move(absmax(irx, igx, ibx)), std::move(absmax(iry, igy, iby)));
342  else
343  return Differential(MakeImageGray(src), std::move(sumrgb(irx, igx, ibx)), std::move(sumrgb(iry, igy, iby)));
344 }
345 
351 {
352  auto irx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
353  for (auto tmp : Range(src))
354  irx.At(tmp) = src.At(tmp).r;
355  auto itmp = irx;
356  auto iry = irx;
357  halfdiffleft(irx);
358  halfdiffright(itmp);
359  absmin(irx, itmp);
360  itmp = iry;
361  halfdifftop(iry);
362  halfdiffbottom(itmp);
363  absmin(iry, itmp);
364 
365  auto igx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
366  for (auto tmp : Range(src))
367  igx.At(tmp) = src.At(tmp).g;
368  itmp = igx;
369  auto igy = igx;
370  halfdiffleft(igx);
371  halfdiffright(itmp);
372  absmin(igx, itmp);
373  itmp = igy;
374  halfdifftop(igy);
375  halfdiffbottom(itmp);
376  absmin(igy, itmp);
377 
378  auto ibx = ImageDoubleGray(src.GetWidth(), src.GetHeight());
379  for (auto tmp : Range(src))
380  ibx.At(tmp) = src.At(tmp).b;
381  itmp = ibx;
382  auto iby = ibx;
383  halfdiffleft(ibx);
384  halfdiffright(itmp);
385  absmin(ibx, itmp);
386  itmp = iby;
387  halfdifftop(iby);
388  halfdiffbottom(itmp);
389  absmin(iby, itmp);
390 
391  if (proj == RGBProjection::ABSMAX)
392  return Differential(MakeImageGray(src), std::move(absmax(irx, igx, ibx)), std::move(absmax(iry, igy, iby)));
393  else
394  return Differential(MakeImageGray(src), std::move(sumrgb(irx, igx, ibx)), std::move(sumrgb(iry, igy, iby)));
395 }
396 
404 {
405  auto diff = derivate2Gauss(src, sigma);
406  return Differential(Downgrade<ImageGray>(src), std::move(std::get<0>(diff)), std::move(std::get<1>(diff)), std::move(std::get<2>(diff)), std::move(std::get<3>(diff)));
407 }
408 
414 {
415  auto lx = src;
416  halfdiffleft(lx);
417  auto lxx = lx;
418  halfdiffright(lxx);
419  auto ly = src;
420  halfdifftop(ly);
421  auto lyy = ly;
422  halfdiffbottom(lyy);
423  return Differential(Downgrade<ImageGray>(src), std::move(lx), std::move(ly), std::move(lxx), std::move(lyy));
424 }
425 
431 {
432  auto lx = src;
433  auto itmp = src;
434  halfdiffleft(lx);
435  halfdiffright(itmp);
436  absmax(lx, itmp);
437  auto ly = src;
438  itmp = src;
439  halfdifftop(ly);
440  halfdiffbottom(itmp);
441  absmax(ly, itmp);
442  return Differential(Downgrade<ImageGray>(src), std::move(lx), std::move(ly));
443 }
444 
450 {
451  auto lx = src;
452  auto itmp = src;
453  halfdiffleft(lx);
454  halfdiffright(itmp);
455  absmin(lx, itmp);
456  auto ly = src;
457  itmp = src;
458  halfdifftop(ly);
459  halfdiffbottom(itmp);
460  absmin(ly, itmp);
461  return Differential(Downgrade<ImageGray>(src), std::move(lx), std::move(ly));
462 }
463 
467 void Differential::updateLx2ly2()
468 {
469  for (auto tmp : Range(lx2ly2))
470  lx2ly2.At(tmp) = Sqr(GetLx().At(tmp)) + Sqr(GetLy().At(tmp));
471  AutoThreshold();
472 }
473 
474 /*****************************************************************************/
481 {
482  std::lock_guard<std::mutex> l(*lazydata);
483  if (lxx.Size() != lx.Size())
484  {
485  lxx = GetLx();
487  lxx.Convolve(sobx);
488  }
489  return lxx;
490 }
491 
492 /*****************************************************************************/
499 {
500  std::lock_guard<std::mutex> l(*lazydata);
501  if (lyy.Size() != ly.Size())
502  {
503  lyy = GetLy();
505  soby.Transpose();
506  lyy.Convolve(soby);
507  }
508  return lyy;
509 }
510 
511 /*****************************************************************************/
518 {
519  std::lock_guard<std::mutex> l(*lazydata);
520  if (lxy.Size() != lx.Size())
521  {
522  lxy = GetLx();
524  soby.Transpose();
525  lxy.Convolve(soby);
526  }
527  return lxy;
528 }
529 
530 /*****************************************************************************/
537 {
538  std::lock_guard<std::mutex> l(*lazydata);
539  if (lyx.Size() != ly.Size())
540  {
541  lyx = GetLy();
543  lyx.Convolve(sobx);
544  }
545  return lyx;
546 }
547 
548 /*****************************************************************************/
555 {
556  auto lw = ImageDoubleGray(lx2ly2.GetWidth(), lx2ly2.GetHeight());
557  for (auto tmp : Range(lx2ly2))
558  lw.At(tmp) = sqrt(lx2ly2.At(tmp));
559  return lw;
560 }
561 
562 /*****************************************************************************/
569 {
570  auto lvv = ImageDoubleGray(lx2ly2.GetWidth(), lx2ly2.GetHeight());
571  for (auto tmp : Range(lx2ly2))
572  {
573  auto n = lx2ly2.At(tmp);
574  if (n != 0.0)
575  lvv.At(tmp) =
576  (Sqr(GetLx().At(tmp)) * GetLyy().At(tmp)
577  + Sqr(GetLy().At(tmp)) * GetLxx().At(tmp)
578  - GetLx().At(tmp) * GetLy().At(tmp) * (GetLxy().At(tmp) + GetLyx().At(tmp)))
579  / n;
580  }
581  return lvv;
582 }
583 
584 /*****************************************************************************/
591 {
592  auto lww = ImageDoubleGray(lx2ly2.GetWidth(), lx2ly2.GetHeight());
593  for (auto tmp : Range(lx2ly2))
594  {
595  double n = lx2ly2.At(tmp);
596  if (n != 0.0)
597  lww.At(tmp) =
598  (Sqr(GetLx().At(tmp)) * GetLyy().At(tmp)
599  + Sqr(GetLy().At(tmp)) * GetLxx().At(tmp)
600  + GetLx().At(tmp) * GetLy().At(tmp) * (GetLxy().At(tmp) + GetLyx().At(tmp)))
601  / n;
602  }
603  return lww;
604 }
605 
606 /*****************************************************************************/
613 {
614  auto lvw = ImageDoubleGray(lx2ly2.GetWidth(), lx2ly2.GetHeight());
615  for (auto tmp : Range(lx2ly2))
616  {
617  auto n = lx2ly2.At(tmp);
618  if (n != 0.0)
619  lvw.At(tmp) =
620  (GetLx().At(tmp) * GetLy().At(tmp) * (GetLyy().At(tmp) - GetLxx().At(tmp))
621  + GetLxy().At(tmp) * Sqr(GetLx().At(tmp))
622  - GetLyx().At(tmp) * Sqr(GetLy().At(tmp))
623  )
624  / n;
625  }
626  return lvw;
627 }
628 
629 /*****************************************************************************/
636 {
637  auto tmpimg = GetLxx();
638  for (auto tmp : Range(lx2ly2))
639  tmpimg.At(tmp) += GetLyy().At(tmp);
640  return tmpimg;
641 }
642 
643 /*****************************************************************************/
650 {
651  auto tmpimg = MakeLvv();
652  for (auto tmp : Range(lx2ly2))
653  {
654  const auto n = lx2ly2.At(tmp);
655  if (n != 0.0)
656  tmpimg.At(tmp) /= sqrt(n);
657  }
658  return tmpimg;
659 }
660 
661 /*****************************************************************************/
668 {
669  auto tmpimg = MakeLvw();
670  for (auto tmp : Range(lx2ly2))
671  {
672  auto n = lx2ly2.At(tmp);
673  if (n != 0.0)
674  tmpimg.At(tmp) /= sqrt(n);
675  }
676  return tmpimg;
677 }
678 
679 /*****************************************************************************/
686 {
687  auto tmpimg = MakeLww();
688  for (auto tmp : Range(lx2ly2))
689  {
690  const auto n = lx2ly2.At(tmp);
691  if (n != 0.0)
692  tmpimg.At(tmp) /= sqrt(n);
693  }
694  return tmpimg;
695 }
696 
697 /*****************************************************************************/
704 {
705  auto tmpimg = MakeLvv();
706  for (auto tmp : Range(lx2ly2))
707  tmpimg.At(tmp) *= lx2ly2.At(tmp);
708  return tmpimg;
709 }
710 
711 /*****************************************************************************/
718 {
719  auto tmpimg = ImageDoubleGray(lx2ly2.GetWidth(), lx2ly2.GetHeight());
720  for (auto tmp : Range(lx2ly2))
721  tmpimg.At(tmp) = GetLxx().At(tmp) * GetLyy().At(tmp) - GetLxy().At(tmp) * GetLyx().At(tmp);
722  return tmpimg;
723 }
724 
725 /*****************************************************************************/
732 {
733  return MakeLw();
734 }
735 
736 /*****************************************************************************/
743 {
744  return MakeImageGradient().MakeCurvature();
745 }
746 
747 /*****************************************************************************/
754 {
755  auto img = ImageGradient(GetLx().GetWidth(), GetLx().GetHeight());
756  for (auto i : Range(img))
757  img.At(i) = pixel::Cart2D<double>(GetLx().At(i), GetLy().At(i));
758  img.SetMinModule((int)sqrt(thres));
759  return img;
760 }
761 
767 {
768  auto tmpimg = ImageDoubleGray(lx2ly2.GetWidth(), lx2ly2.GetHeight());
769  for (auto tmp : Range(lx2ly2))
770  {
771  auto lxxlyy = GetLxx().At(tmp) + GetLyy().At(tmp);
772  tmpimg.At(tmp) =
773  (lxxlyy + sqrt(Abs(
774  Sqr(lxxlyy) - 4 * (
775  GetLxx().At(tmp) * GetLyy().At(tmp)
776  -GetLxy().At(tmp) * GetLyx().At(tmp))
777  ))
778  ) / 2;
779  }
780  return tmpimg;
781 }
782 
788 {
789  auto tmpimg = ImageDoubleGray(lx2ly2.GetWidth(), lx2ly2.GetHeight());
790  for (auto tmp : Range(lx2ly2))
791  {
792  auto lxxlyy = GetLxx().At(tmp) + GetLyy().At(tmp);
793  tmpimg.At(tmp) =
794  (lxxlyy - sqrt(Abs(
795  Sqr(lxxlyy) - 4 * (
796  GetLxx().At(tmp) * GetLyy().At(tmp)
797  -GetLxy().At(tmp) * GetLyx().At(tmp))
798  ))
799  ) / 2;
800  }
801  return tmpimg;
802 }
803 
809 {
810  auto tmpimg = ImageDoubleGray(lx2ly2.GetWidth(), lx2ly2.GetHeight());
811  for (auto tmp : Range(lx2ly2))
812  {
813  tmpimg.At(tmp) = GetLxx().At(tmp) * GetLyy().At(tmp)
814  - GetLxy().At(tmp) * GetLyx().At(tmp)
815  - s * Sqr(GetLxx().At(tmp) + GetLyy().At(tmp));
816  }
817  return tmpimg;
818 }
819 
820 /*****************************************************************************/
828 {
829  int max = 0;
830  for (auto tmp : Range(srcigray))
831  {
832  if (srcigray.At(tmp) > max)
833  {
834  max = srcigray.At(tmp);
835  }
836  }
837  double S1 = 0, S2 = 0, C1 = 0, C2 = 0;
838  for (auto tmp : Range(srcigray))
839  {
840  int w1 = srcigray.At(tmp); // weight on light pixels
841  int w2 = max - w1; // weight on dark pixels
842  S1 += lx2ly2.At(tmp) * w1;
843  S2 += lx2ly2.At(tmp) * w2;
844  C1 += w1;
845  C2 += w2;
846  }
847 
848  double m1 = S1 / C1;
849  double m2 = S2 / C2;
850  thres = (m1 + m2) / 2;
851 
852  return thres;
853 }
854 
855 /*****************************************************************************/
864 void Differential::Diffuse(size_t maxiter, double maxdiv)
865 {
866  // clear second derivatives
867  lxx = lxy = lyy = ImageDoubleGray{};
868 
869  ImageDoubleGray tmplx = lx;
870  ImageDoubleGray tmply = ly;
871  for (size_t iter = 0; iter < maxiter; ++iter)
872  {
873  ImageDoubleGray div;
874  if (maxdiv <= 2.0)
875  div = MakeDivergence();
876  for (size_t y = 1; y < lx.GetHeight() - 1; ++y)
877  for (size_t x = 1; x < lx.GetWidth() - 1; ++x)
878  {
879  auto cond = true;
880  if (maxdiv <= 2.0)
881  cond = Abs(div.At(x, y)) < maxdiv;
882  if (cond)
883  {
884  double gx = (1.0/16.0) * (lx.At(x - 1, y - 1) + lx.At(x - 1, y + 1) +
885  lx.At(x + 1, y - 1) + lx.At(x + 1, y + 1)) +
886  (2.0/16.0) * (lx.At(x - 1, y) + lx.At(x + 1, y) +
887  lx.At(x, y - 1) + lx.At(x, y + 1)) +
888  (4.0/16.0) * lx.At(x, y);
889  double gy = (1.0/16.0) * (ly.At(x - 1, y - 1) + ly.At(x - 1, y + 1) +
890  ly.At(x + 1, y - 1) + ly.At(x + 1, y + 1)) +
891  (2.0/16.0) * (ly.At(x - 1, y) + ly.At(x + 1, y) +
892  ly.At(x, y - 1) + ly.At(x, y + 1)) +
893  (4.0/16.0) * ly.At(x, y);
894  tmplx.At(x, y) = gx;
895  tmply.At(x, y) = gy;
896  lx2ly2.At(x, y) = Sqr(gx) + Sqr(gy);
897  }
898  }
899  std::swap(lx, tmplx);
900  std::swap(ly, tmply);
901  }
902 
903  AutoThreshold();
904 }
905 
912 {
913  const size_t w = GetLx().GetWidth();
914  auto axe = ImageDoubleGray(w, GetLx().GetHeight(), 0.0);
915  // precompute normalized derivatives
916  auto precline = std::vector<Point2DDouble>(w);
917  auto currline = std::vector<Point2DDouble>(w);
918  for (size_t x = 1; x < w - 1; ++x)
919  {
920  auto n = sqrt(GetLx2Ly2().At(x, 0));
921  precline[x].X = GetLx().At(x, 0) / n;
922  precline[x].Y = GetLy().At(x, 0) / n;
923  n = sqrt(GetLx2Ly2().At(x, 1));
924  currline[x].X = GetLx().At(x, 1) / n;
925  currline[x].Y = GetLy().At(x, 1) / n;
926  }
927  auto nextline = std::vector<Point2DDouble>(w);
928  // compute divergence (cannot be parallelized!)
929  for (size_t y = 1; y < GetLx().GetHeight() - 1; ++y)
930  {
931  for (size_t x = 1; x < w - 1; ++x)
932  {
933  // update normalized derivatives
934  auto n = sqrt(GetLx2Ly2().At(x, y + 1));
935  nextline[x].X = GetLx().At(x, y + 1) / n;
936  nextline[x].Y = GetLy().At(x, y + 1) / n;
937 
938  // compute divergence
939  auto dx = AbsMaxSameSign(currline[x + 1].Y - currline[x].Y, currline[x].Y - currline[x - 1].Y);
940  auto dy = AbsMaxSameSign(nextline[x].X - currline[x].X, currline[x].X - precline[x].X);
941  axe.At(x, y) = dx + dy;
942  }
943  // shift normalized derivatives
944  precline.swap(currline);
945  currline.swap(nextline);
946  }
947  return axe;
948 }
949 
950 static inline double norm(double a, double b)
951 {
952  return sqrt(Sqr(a) + Sqr(b));
953 }
960 {
961  auto l1 = ImageDoubleGray(GetLx().GetWidth(), GetLx().GetHeight());
962  for (size_t i = 0; i < GetLx().GetHeight(); i++)
963  {
964  for (size_t j = 0; j < GetLx().GetWidth(); j++)
965  {
966  double xc = GetLx().At(j, i);
967  double yc = GetLy().At(j, i);
968  if ((Abs(xc) < 0.01) && (Abs(yc) < 0.01))
969  continue;
970  double g = norm (xc, yc);
971  double g1, g2, g3, g4, xx, yy;
972 
973  /* Follow the gradient direction, as indicated by the direction of
974  the vector (xc, yc); retain pixels that are a local maximum. */
975  if (Abs(yc) > Abs(xc))
976  {
977  /* The Y component is biggest, so gradient direction is basically UP/DOWN */
978  xx = Abs(xc)/Abs(yc);
979  yy = 1.0;
980 
981  g2 = norm(GetLx().At(j, i-1), GetLy().At(j, i-1));
982  g4 = norm(GetLx().At(j, i+1), GetLy().At(j, i+1));
983  if (xc * yc > 0.0)
984  {
985  g3 = norm(GetLx().At(j+1, i+1), GetLy().At(j+1, i+1));
986  g1 = norm(GetLx().At(j-1, i-1), GetLy().At(j-1, i-1));
987  }
988  else
989  {
990  g3 = norm(GetLx().At(j-1, i+1), GetLy().At(j-1, i+1));
991  g1 = norm(GetLx().At(j+1, i-1), GetLy().At(j+1, i-1));
992  }
993 
994  }
995  else
996  {
997  /* The X component is biggest, so gradient direction is basically LEFT/RIGHT */
998  xx = Abs(yc) / Abs(xc);
999  yy = 1.0;
1000 
1001  g2 = norm(GetLx().At(j+1, i), GetLy().At(j+1, i));
1002  g4 = norm(GetLx().At(j-1, i), GetLy().At(j-1, i));
1003  if (xc * yc > 0.0)
1004  {
1005  g3 = norm(GetLx().At(j-1, i-1), GetLy().At(j-1, i-1));
1006  g1 = norm(GetLx().At(j+1, i+1), GetLy().At(j+1, i+1));
1007  }
1008  else
1009  {
1010  g1 = norm(GetLx().At(j+1, i-1), GetLy().At(j+1, i-1));
1011  g3 = norm(GetLx().At(j-1, i+1), GetLy().At(j-1, i+1));
1012  }
1013  }
1014 
1015  /* Compute the interpolated value of the gradient magnitude */
1016  if ( (g > (xx*g1 + (yy-xx)*g2)) && (g > (xx*g3 + (yy-xx)*g4)) )
1017  {
1018  l1.At(j, i) = g;
1019  }
1020  else
1021  {
1022  l1.At(j, i) = 0;
1023  }
1024 
1025  }
1026  }
1027  return l1;
1028 }
1029 
static MatrixDouble NewGaussianLineDerivative(double sigma)
Creates a line matrix with the derivative of a centered Gaussian.
ImageDoubleGray & GetLy() noexcept
Returns a reference to the internal y derivate.
ScalarRange< T > Range(T b, T e)
Creates a range [[b, e[[.
Definition: CRNType.h:257
void halfdiffbottom(ImageDoubleGray &i)
ImageDoubleGray MakeCorner()
Returns the corner image.
ImageDoubleGray MakeHessianCorner(double s=0.1)
Returns the Hessian corner image.
T AbsMax(const T &a, const T &b) noexcept(noexcept(Abs(a)))
Returns the value that has the maximal absolute value.
Definition: CRNMath.h:67
ImageDoubleGray MakeLvv()
Returns the second derivative of the tangent to the isophote.
ImageDoubleGray & GetLx() noexcept
Returns a reference to the internal x derivate.
virtual Matrix & Transpose()
Inplace transposition.
Definition: CRNMatrix.h:472
std::vector< pixel_type >::reference At(size_t x, size_t y) noexcept
Returns a reference to a pixel.
Definition: CRNImage.h:224
ImageDoubleGray MakeLww()
Returns the second derivative of the normal to the isophote.
const ImageDoubleGray & GetLxy()
Returns a reference to the internal xy derivate.
size_t GetHeight() const noexcept
Definition: CRNImage.h:74
ImageDoubleGray & GetLx2Ly2() noexcept
Returns a reference to the internal first derivatives norm.
static MatrixDouble NewGaussianLineSecondDerivative(double sigma)
Creates a line matrix with the second derivative of a centered Gaussian.
static MatrixDouble NewGaussianLine(double sigma)
Creates a line matrix with a centered Gaussian.
ImageDoubleGray MakeEdge()
Returns the edge image.
void halfdifftop(ImageDoubleGray &i)
ImageDoubleGray MakeIsophoteCurvature()
Returns the isophote curvature image.
ImageDoubleGray MakeKappa2()
Returns the second eigenvalue of the Hessian as an image.
ImageGray MakeCurvature() const
Creates an image representing the curvature of the gradients.
ImageGray MakeImageGray(const ImageRGB &img)
static Differential NewHalfDiffAbsMax(const ImageRGB &src, RGBProjection proj)
AbsMax half derivatives.
ImageDoubleGray MakeDivergence()
Creates the continuous image skeleton.
double AutoThreshold()
Computes the best threshold for lx2ly2.
void NormalizeForConvolution()
Normalizes the matrix to be used for convolution.
Definition: CRNMatrix.h:409
static Differential NewHalfDiff(const ImageRGB &src, RGBProjection proj)
Alternate half derivatives.
static Differential NewGaussian(const ImageRGB &src, RGBProjection proj, double sigma)
Convolution with Gaussian derivatives.
const ImageDoubleGray & GetLxx()
Returns a reference to the internal xx derivate.
void halfdiffleft(ImageDoubleGray &i)
Image< double > ImageDoubleGray
double Grayscale image class
constexpr SumType< T > Sqr(const T &v) noexcept(noexcept(v *v))
Returns the square of a value.
Definition: CRNMath.h:61
const ImageDoubleGray & GetLyx()
Returns a reference to the internal yx derivate.
static Differential NewHalfDiffAbsMin(const ImageRGB &src, RGBProjection proj)
AbsMin half derivatives.
ImageDoubleGray MakeCanny()
Returns Canny's edge detector image.
double matrix class
void Convolve(const MatrixDouble &mat)
Convolution.
Definition: CRNImage.hpp:1023
Gradient image in polar form.
T AbsMaxSameSign(const T &a, const T &b) noexcept(noexcept(Abs(a))&&noexcept(SignOf(a)))
Returns the value that has the maximal absolute value if the values have the same sign...
Definition: CRNMath.h:71
Differential(const Differential &)=delete
ImageDoubleGray MakeKappa1()
Returns the first eigenvalue of the Hessian as an image.
void Abs(Image< T > &img, typename std::enable_if< std::is_arithmetic< T >::value >::type *dummy=nullptr) noexcept
Replaces each pixel by its absolute value.
Definition: CRNImageGray.h:47
void halfdiffright(ImageDoubleGray &i)
const T & At(size_t pos) const noexcept
Definition: CRNMatrix.h:165
T AbsMin(const T &a, const T &b) noexcept(noexcept(Abs(a)))
Returns the value that has the minimal absolute value.
Definition: CRNMath.h:69
ImageDoubleGray MakeFlowlineCurvature()
Returns the flowline curvature image.
size_t GetWidth() const noexcept
Definition: CRNImage.h:72
ImageGradient MakeImageGradient()
Returns the gradient image.
ImageDoubleGray MakeGaussianCurvature()
Returns the Gaussian curvature image.
size_t Size() const noexcept
Definition: CRNImage.h:78
ImageGray MakeGradientCurvature()
Returns the gradient curvature image.
Differential computation on images.
ImageDoubleGray MakeLw()
Returns the derivative of the normal to the isophotes.
ImageDoubleGray MakeLaplacian()
Returns the Laplacian image.
ImageDoubleGray MakeLvw()
Returns the cross derivative of the tangent and normal to the isophote.
void Diffuse(size_t maxiter, double maxdiv=std::numeric_limits< double >::max())
Diffuses the gradient.
ImageDoubleGray MakeGradientModule()
Returns the gradient module image.
const ImageDoubleGray & GetLyy()
Returns a reference to the internal yy derivate.