libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNBlock.cpp
Go to the documentation of this file.
1 /* Copyright 2006-2016 INSA-Lyon, ENS-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: CRNBlock.cpp
19  * \author Yann LEYDIER
20  */
21 
22 #include <CRNi18n.h>
23 #include <CRNBlock.h>
25 #include <CRNImage/CRNImageRGB.h>
26 #include <CRNImage/CRNImageGray.h>
27 #include <CRNImage/CRNImageBW.h>
28 #include <CRNData/CRNMap.h>
29 #include <CRNException.h>
30 #include <algorithm>
31 #include <functional>
32 #include <CRNIO/CRNFileShield.h>
33 #include <CRNIO/CRNIO.h>
34 #include <CRNXml/CRNXml.h>
35 
36 using namespace crn;
37 
38 /*****************************************************************************/
46 SBlock Block::New(const SImage &src, const String &nam)
47 {
48  auto b = SBlock(new Block(src, nam));
49  b->self = b;
50  return b;
51 }
52 
53 /*****************************************************************************/
61 Block::Block(const SImage &src, const String &nam):
62  Savable(nam),
63  child(std::make_shared<Map>()),
64  srcRGB(nullptr),
65  srcGray(nullptr),
66  srcBW(nullptr),
67  srcGradient(nullptr),
68  buffRGB(nullptr),
69  buffGray(nullptr),
70  buffBW(nullptr),
71  buffGradient(nullptr),
72  grad_sigma(-1),
73  grad_diffusemaxiter(0),
74  grad_diffusemaxdiv(std::numeric_limits<double>::max())
75 {
76  if (!nam)
77  SetName(_("NewBlock"));
78  if (!src)
79  {
80  throw ExceptionInvalidArgument(StringUTF8("Block::Block(SImage*) :") + _("No image."));
81  }
82  srcRGB = std::dynamic_pointer_cast<ImageRGB>(src);
83  if (!srcRGB)
84  {
85  srcGray = std::dynamic_pointer_cast<ImageGray>(src);
86  if (!srcGray)
87  {
88  srcBW = std::dynamic_pointer_cast<ImageBW>(src);
89  if (!srcBW)
90  srcGradient = std::dynamic_pointer_cast<ImageGradient>(src);
91  }
92  }
93  if (!srcRGB && !srcGray && !srcBW && !srcGradient)
94  throw ExceptionInvalidArgument(StringUTF8("Block::Block(SImage src, String nam): ") +
95  _("unsupported image type."));
96  image_is_open = true;
97  bbox = Rect(0, 0, int(src->GetWidth()) - 1, int(src->GetHeight()) - 1);
98 }
99 
100 /*****************************************************************************/
114 SBlock Block::New(const Path &ifname, const Path &xfname, const String &nam)
115 {
116  auto b = SBlock(new Block(ifname, xfname, nam));
117  b->self = b;
118  if (xfname.IsNotEmpty())
119  b->Append(xfname);
120  if ((!b->bbox.IsValid()) || (b->bbox.GetWidth() == 0) || (b->bbox.GetHeight() == 0))
121  {
122  auto irgb = b->GetRGB();
123  b->bbox.SetLeft(0);
124  b->bbox.SetTop(0);
125  b->bbox.SetWidth(int(irgb->GetWidth()));
126  b->bbox.SetHeight(int(irgb->GetHeight()));
127  }
128  return b;
129 }
130 
131 /*****************************************************************************/
145 Block::Block(const Path &ifname, const Path &xfname, const String &nam):
146  Savable(nam),
147  child(std::make_shared<Map>()),
148  srcRGB(nullptr),
149  srcGray(nullptr),
150  srcBW(nullptr),
151  srcGradient(nullptr),
152  buffRGB(nullptr),
153  buffGray(nullptr),
154  buffBW(nullptr),
155  buffGradient(nullptr),
156  grad_sigma(-1),
157  grad_diffusemaxiter(0),
158  grad_diffusemaxdiv(std::numeric_limits<double>::max())
159 {
160  if (!nam)
161  SetName(_("NewBlock"));
162  image_is_open = false;
163  imagefilename = ifname;
164 }
165 
166 /*****************************************************************************/
177 SBlock Block::create(const WBlock &par, const String &tree, const Rect &clip, const String &nam)
178 {
179  auto b = SBlock(new Block(par, tree, clip, nam));
180  b->self = b;
181  return b;
182 }
183 
184 /*****************************************************************************/
195 Block::Block(const WBlock &par, const String &tree, const Rect &clip, const String &nam):
196  Savable(nam),
197  child(std::make_shared<Map>()),
198  parent(par),
199  parenttree(tree),
200  bbox(clip),
201  buffRGB(nullptr),
202  buffGray(nullptr),
203  buffBW(nullptr),
204  buffGradient(nullptr),
205  grad_sigma(-1),
206  grad_diffusemaxiter(0),
207  grad_diffusemaxdiv(std::numeric_limits<double>::max())
208 {
209  if (!nam)
210  SetName(U"NewChildBlock");
211  if (par.expired())
212  throw ExceptionInvalidArgument(StringUTF8("Block::Block(WBlock par, const String &tree, Rect clip, String nam) : ") +
213  _("No parent."));
214  srcRGB = par.lock()->srcRGB;
215  srcGray = par.lock()->srcGray;
216  srcBW = par.lock()->srcBW;
217  srcGradient = par.lock()->srcGradient;
218  image_is_open = par.lock()->image_is_open;
219  // clipping!
220  SImage src = nullptr;
221  if (srcRGB) // search for a valid source
222  src = srcRGB;
223  else if (srcGray)
224  src = srcGray;
225  else if (srcBW)
226  src = srcBW;
227  else if (srcGradient)
228  src = srcGradient;
229  bbox = bbox & par.lock()->GetAbsoluteBBox();
230  if (src)
231  {
232  if (bbox.GetLeft() < 0)
233  bbox.SetLeft(0);
234  if (bbox.GetTop() < 0)
235  bbox.SetTop(0);
236  if (bbox.GetRight() >= int(src->GetWidth()))
237  bbox.SetRight(int(src->GetWidth()) - 1);
238  if (bbox.GetBottom() >= int(src->GetHeight()))
239  bbox.SetBottom(int(src->GetHeight()) - 1);
240  }
241 }
242 
254 void Block::SetAbsoluteBBox(const Rect &newbox)
255 {
256  if (parent.expired())
257  throw ExceptionLogic(StringUTF8("Block::SetAbsoluteBBox(): ") +
258  _("this is a topmost block. Its bounding box cannot be changed."));
259  if (!newbox.IsValid())
260  throw ExceptionInvalidArgument(StringUTF8("Block::SetAbsoluteBBox(): ") +
261  _("Uninitialized bounding box."));
262  Rect nr = parent.lock()->GetAbsoluteBBox();
263  nr &= newbox;
264  if (!nr.IsValid())
265  throw ExceptionDimension(StringUTF8("Block::SetAbsoluteBBox(): ") +
266  _("bounding box out of parent's bounding box."));
267 
268  if ((nr & bbox).GetArea() < bbox.GetArea())
269  for (auto & elem : *child)
270  {
271  SVector v(std::static_pointer_cast<Vector>(elem.second));
272  for (long j = long(v->Size()) - 1 ; j >= 0 ; --j)
273  {
274  SBlock b(std::static_pointer_cast<Block>((*v)[size_t(j)]));
275  Rect newChildBox(nr & b->GetAbsoluteBBox());
276  if (newChildBox.GetArea() > 0)
277  b->SetAbsoluteBBox(newChildBox);
278  else
279  {
280  b->bbox = Rect();
281  v->Remove(j);
282  }
283  }
284  }
285  FlushAll();
286  bbox = nr;
287 }
288 
301 {
302  if (parent.expired())
303  throw ExceptionLogic(StringUTF8("Block::SetRelativeBBox(): ") +
304  _("this is a topmost block. Its bounding box cannot be changed."));
305  if (!newbox.IsValid())
306  throw ExceptionInvalidArgument(StringUTF8("Block::SetRelativeBBox(): ") +
307  _("Uninitialized bounding box."));
308  newbox.Translate(parent.lock()->GetAbsoluteBBox().GetLeft(), parent.lock()->GetAbsoluteBBox().GetTop());
309  SetAbsoluteBBox(newbox);
310 }
317 {
318  if (parent.expired())
319  return bbox;
320  Rect r = bbox;
321  r.Translate(-parent.lock()->GetAbsoluteBBox().GetLeft(), -parent.lock()->GetAbsoluteBBox().GetTop());
322  return r;
323 }
324 
334 Block::block_iterator Block::BlockBegin(const String &tree)
335 {
336  if (child->Find(tree) != child->end())
337  {
338  return block_iterator(getChildList(tree)->begin());
339  }
340  else
341  throw ExceptionInvalidArgument(StringUTF8("Block::block_iterator Block::BlockBegin(const String &tree): ") +
342  _("tree not found."));
343 }
344 
354 Block::block_iterator Block::BlockEnd(const String &tree)
355 {
356  if (child->Find(tree) != child->end())
357  return block_iterator(getChildList(tree)->end());
358  else
359  throw ExceptionInvalidArgument(StringUTF8("Block::block_iterator Block::BlockEnd(const String &tree): ") +
360  _("tree not found."));
361 }
362 
372 Block::const_block_iterator Block::BlockBegin(const String &tree) const
373 {
374  if (child->Find(tree) != child->end())
375  {
376  SCVector v = std::static_pointer_cast<const Vector>(child->Get(tree));
377  return const_block_iterator(v->begin());
378  }
379  else
380  throw ExceptionInvalidArgument(StringUTF8("Block::const_block_iterator Block::BlockBegin(const String &tree) const: ") +
381  _("tree not found."));
382 }
383 
393 Block::const_block_iterator Block::BlockEnd(const String &tree) const
394 {
395  if (child->Find(tree) != child->end())
396  {
397  SCVector v = std::static_pointer_cast<const Vector>(child->Get(tree));
398  return const_block_iterator(v->end());
399  }
400  else
401  throw ExceptionInvalidArgument(StringUTF8("Block::const_block_iterator Block::BlockEnd(const String &tree) const: ") +
402  _("tree not found."));
403 }
404 
411 void Block::openImage(void)
412 {
413  if (image_is_open)
414  {
415  return;
416  }
417  if (!parent.expired())
418  {
419  parent.lock()->openImage();
420  return;
421  }
422 
423  SImage src(nullptr);
424  try
425  {
426  src = NewImageFromFile(imagefilename);
427  }
428  catch (...)
429  {
430  }
431  if (!src)
432  {
433  throw ExceptionIO(StringUTF8("Block::openImage(void): ") +
434  _("Cannot open image."));
435  }
436  srcRGB = std::dynamic_pointer_cast<ImageRGB>(src);
437  srcGray = std::dynamic_pointer_cast<ImageGray>(src);
438  srcBW = std::dynamic_pointer_cast<ImageBW>(src);
439  bbox = Rect(0, 0, int(src->GetWidth()) - 1, int(src->GetHeight()) - 1);
440  image_is_open = true;
441  return;
442 }
443 
452 SImageRGB Block::get_srcRGB(void)
453 {
454  if (!parent.expired())
455  return parent.lock()->get_srcRGB();
456 
457  openImage();
458  return srcRGB;
459 }
460 
469 SImageGray Block::get_srcGray(void)
470 {
471  if (!parent.expired())
472  return parent.lock()->get_srcGray();
473 
474  openImage();
475  return srcGray;
476 }
477 
486 SImageBW Block::get_srcBW(void)
487 {
488  if (!parent.expired())
489  return parent.lock()->get_srcBW();
490 
491  openImage();
492  return srcBW;
493 }
494 
503 SImageGradient Block::get_srcGradient(void)
504 {
505  if (!parent.expired())
506  return parent.lock()->get_srcGradient();
507 
508  openImage();
509  return srcGradient;
510 }
511 
512 /*****************************************************************************/
517 {
518  if (GetFilename().IsNotEmpty())
519  {
520  Save();
521  }
522 }
523 
530 SVector Block::getChildList(const String &name)
531 {
532  if (child->Find(name) == child->end())
533  child->Set(name, std::make_shared<Vector>());
534  return std::static_pointer_cast<Vector>(child->Get(name));
535 }
536 
537 /*****************************************************************************/
548 SBlock Block::AddChildAbsolute(const String &tree, Rect clip)
549 {
550  if (!clip.IsValid())
551  throw ExceptionInvalidArgument(StringUTF8("SBlock Block::AddChildAbsolute(const String &tree, Rect clip): ") +
552  _("Uninitialized clipping rectangle."));
553  SBlock nb = create(self, tree, clip, tree);
554  if (!nb->GetAbsoluteBBox().IsValid())
555  {
556  throw ExceptionDomain(StringUTF8("SBlock Block::AddChildAbsolute(const String &tree, Rect clip): ") +
557  _("Clipping rectangle out of bounds."));
558  }
559  getChildList(tree)->PushBack(nb);
560  return nb;
561 }
562 
563 /*****************************************************************************/
575 SBlock Block::AddChildAbsolute(const String &tree, Rect clip, const String &name)
576 {
577  if (!clip.IsValid())
578  throw ExceptionInvalidArgument(StringUTF8("SBlock Block::AddChildAbsolute(const String &tree, Rect clip, const String &name): ") +
579  _("Uninitialized clipping rectangle."));
580  SBlock nb = create(self, tree, clip, name);
581  if (!nb->GetAbsoluteBBox().IsValid())
582  {
583  throw ExceptionDomain(StringUTF8("SBlock Block::AddChildAbsolute(const String &tree, Rect clip, const String &name): ") +
584  _("Clipping rectangle out of bounds."));
585  }
586  getChildList(tree)->PushBack(nb);
587  return nb;
588 }
589 
590 /*****************************************************************************/
602 SBlock Block::AddChildAbsoluteAt(const String &tree, Rect clip, size_t pos)
603 {
604  if (!clip.IsValid())
605  throw ExceptionInvalidArgument(StringUTF8("SBlock Block::AddChildAbsoluteAt(const String &tree, Rect clip, size_t pos): ") +
606  _("Uninitialized clipping rectangle."));
607  if (child->Find(tree) == child->end())
608  return AddChildAbsolute(tree, clip);
609  else if (pos >= getChildList(tree)->Size())
610  return AddChildAbsolute(tree, clip);
611  else
612  {
613  SBlock nb = create(self, tree, clip, tree.CStr());
614  if (!nb->GetAbsoluteBBox().IsValid())
615  {
616  throw ExceptionDomain(StringUTF8("SBlock Block::AddChildAbsoluteAt(const String &tree, Rect clip, size_t pos): ") +
617  _("Clipping rectangle out of bounds."));
618  }
619  getChildList(tree)->Insert(nb, pos);
620  return nb;
621  }
622 }
623 
624 /*****************************************************************************/
637 SBlock Block::AddChildAbsoluteAt(const String &tree, Rect clip, const String &name, size_t pos)
638 {
639  if (!clip.IsValid())
640  throw ExceptionInvalidArgument(StringUTF8("SBlock Block::AddChildAbsoluteAt(const String &tree, Rect clip, const String &name, size_t pos): ") +
641  _("Uninitialized clipping rectangle."));
642  if (child->Find(tree) == child->end())
643  return AddChildAbsolute(tree, clip, name);
644  else if (pos >= getChildList(tree)->Size())
645  return AddChildAbsolute(tree, clip, name);
646  else
647  {
648  SBlock nb = create(self, tree, clip, name);
649  if (!nb->GetAbsoluteBBox().IsValid())
650  {
651  throw ExceptionDomain(StringUTF8("SBlock Block::AddChildAbsoluteAt(const String &tree, Rect clip, const String &name, size_t pos): ") +
652  _("Clipping rectangle out of bounds."));
653  }
654  getChildList(tree)->Insert(nb, pos);
655  return nb;
656  }
657 }
658 
659 /*****************************************************************************/
670 SBlock Block::AddChildRelative(const String &tree, Rect clip)
671 {
672  if (!clip.IsValid())
673  throw ExceptionInvalidArgument(StringUTF8("SBlock Block::AddChildRelative(const String &tree, Rect clip): ") +
674  _("Uninitialized clipping rectangle."));
675  clip.Translate(bbox.GetLeft(), bbox.GetTop());
676  SBlock nb = create(self, tree, clip, tree);
677  if (!nb->GetAbsoluteBBox().IsValid())
678  {
679  throw ExceptionDomain(StringUTF8("SBlock Block::AddChildRelative(const String &tree, Rect clip): ") +
680  _("Clipping rectangle out of bounds."));
681  }
682  getChildList(tree)->PushBack(nb);
683  return nb;
684 }
685 
686 /*****************************************************************************/
698 SBlock Block::AddChildRelative(const String &tree, Rect clip, const String &name)
699 {
700  if (!clip.IsValid())
701  throw ExceptionInvalidArgument(StringUTF8("SBlock Block::AddChildRelative(const String &tree, Rect clip, const String &name): ") +
702  _("Uninitialized clipping rectangle."));
703  clip.Translate(bbox.GetLeft(), bbox.GetTop());
704  SBlock nb = create(self, tree, clip, name);
705  if (!nb->GetAbsoluteBBox().IsValid())
706  {
707  throw ExceptionDomain(StringUTF8("SBlock Block::AddChildRelative(const String &tree, Rect clip, const String &name): ") +
708  _("Clipping rectangle out of bounds."));
709  }
710  getChildList(tree)->PushBack(nb);
711  return nb;
712 }
713 
714 /*****************************************************************************/
726 SBlock Block::AddChildRelativeAt(const String &tree, Rect clip, size_t pos)
727 {
728  if (!clip.IsValid())
729  throw ExceptionInvalidArgument(StringUTF8("SBlock Block::AddChildRelativeAt(const String &tree, Rect clip, size_t pos): ") +
730  _("Uninitialized clipping rectangle."));
731  if (child->Find(tree) == child->end())
732  return AddChildRelative(tree, clip);
733  else if (pos >= getChildList(tree)->Size())
734  return AddChildRelative(tree, clip);
735  else
736  {
737  clip.Translate(bbox.GetLeft(), bbox.GetTop());
738  SBlock nb = create(self, tree, clip, tree.CStr());
739  if (!nb->GetAbsoluteBBox().IsValid())
740  {
741  throw ExceptionDomain(StringUTF8("SBlock Block::AddChildRelativeAt(const String &tree, Rect clip, size_t pos): ") +
742  _("Clipping rectangle out of bounds."));
743  }
744  getChildList(tree)->Insert(nb, pos);
745  return nb;
746  }
747 }
748 
749 /*****************************************************************************/
762 SBlock Block::AddChildRelativeAt(const String &tree, Rect clip, const String &name, size_t pos)
763 {
764  if (!clip.IsValid())
765  throw ExceptionInvalidArgument(StringUTF8("SBlock Block::AddChildRelativeAt(const String &tree, Rect clip, const String &name, size_t pos): ") +
766  _("Uninitialized clipping rectangle."));
767  if (child->Find(tree) == child->end())
768  return AddChildRelative(tree, clip, name);
769  else if (pos >= getChildList(tree)->Size())
770  return AddChildRelative(tree, clip, name);
771  else
772  {
773  clip.Translate(bbox.GetLeft(), bbox.GetTop());
774  SBlock nb = create(self, tree, clip, name);
775  if (!nb->GetAbsoluteBBox().IsValid())
776  {
777  throw ExceptionDomain(StringUTF8("SBlock Block::AddChildRelativeAt(const String &tree, Rect clip, const String &name, size_t pos): ") +
778  _("Clipping rectangle out of bounds."));
779  }
780  getChildList(tree)->Insert(nb, pos);
781  return nb;
782  }
783 }
784 
785 
786 /*****************************************************************************/
795 SImageRGB Block::GetRGB()
796 {
797  if (!buffRGB)
798  {
799  // 1st option: get from the RGB source
800  if (get_srcRGB())
801  {
802  buffRGB = std::make_shared<ImageRGB>(*get_srcRGB(), bbox);
803  return buffRGB;
804  }
805  // 2nd option: get parent RGB buffer
806  if (!parent.expired())
807  {
808  if (parent.lock()->GetRGB())
809  {
810  Rect localbbox(bbox.GetLeft() - parent.lock()->GetAbsoluteBBox().GetLeft(),
811  bbox.GetTop() - parent.lock()->GetAbsoluteBBox().GetTop(),
812  bbox.GetRight() - parent.lock()->GetAbsoluteBBox().GetLeft(),
813  bbox.GetBottom() - parent.lock()->GetAbsoluteBBox().GetTop());
814  buffRGB = std::make_shared<ImageRGB>(*parent.lock()->GetRGB(), localbbox);
815  return buffRGB;
816  }
817  }
818  // 3rd option: get from the gray local buffer
819  if (buffGray)
820  {
821  buffRGB = std::make_shared<ImageRGB>(*buffGray);
822  return buffRGB;
823  }
824  // 4th option: get from the b&w local buffer
825  if (buffBW)
826  {
827  buffRGB = std::make_shared<ImageRGB>(*buffBW);
828  return buffRGB;
829  }
830  // 5th option: get from the gray source
831  if (get_srcGray())
832  {
833  buffRGB = std::make_shared<ImageRGB>(*get_srcGray());
834  return buffRGB;
835  }
836  // 6th option: get from the b&w source
837  if (get_srcBW())
838  {
839  buffRGB = std::make_shared<ImageRGB>(*get_srcBW());
840  return buffRGB;
841  }
842  //else
843  return nullptr;
844  }
845 
846  return buffRGB;
847 }
848 
849 
850 
851 /*****************************************************************************/
862 SImageGray Block::GetGray(bool create)
863 {
864  if (!buffGray)
865  {
866  // 1st option: get from the gray source
867  if (get_srcGray())
868  {
869  buffGray = std::make_shared<ImageGray>(*get_srcGray(), bbox);
870  return buffGray;
871  }
872  // 2nd option: get parent gray buffer
873  if (!parent.expired())
874  {
875  if (parent.lock()->GetGray(create))
876  {
877  Rect localbbox(bbox.GetLeft() - parent.lock()->GetAbsoluteBBox().GetLeft(),
878  bbox.GetTop() - parent.lock()->GetAbsoluteBBox().GetTop(),
879  bbox.GetRight() - parent.lock()->GetAbsoluteBBox().GetLeft(),
880  bbox.GetBottom() - parent.lock()->GetAbsoluteBBox().GetTop());
881  buffGray = std::make_shared<ImageGray>(*parent.lock()->GetGray(false), localbbox);
882  return buffGray;
883  }
884  }
885  // 3rd option: get from the RGB buffer
886  if (buffRGB && create)
887  {
888  buffGray = MoveShared(MakeImageGray(*buffRGB));
889  return buffGray;
890  }
891  // 4th option: get from the RGB source
892  if (get_srcRGB() && create)
893  {
894  buffGray = MoveShared(MakeImageGray(*GetRGB()));
895  return buffGray;
896  }
897  // 5th option: get from the BW buffer
898  if (buffBW)
899  {
900  buffGray = std::make_shared<ImageGray>(*buffBW);
901  return buffGray;
902  }
903  // 6th option: get from the BW source
904  if (get_srcBW())
905  {
906  buffGray = std::make_shared<ImageGray>(*get_srcBW());
907  return buffGray;
908  }
909  //else
910  if (create)
911  {
912  CRNWarning(String(U"SImageGray* Block::GetGray(): ") +
913  _("Cannot access to any source or buffer."));
914  }
915  return nullptr;
916  }
917  return buffGray;
918 }
919 
920 
921 /*****************************************************************************/
932 SImageBW Block::GetBW(bool create)
933 {
934  if (!buffBW)
935  {
936  // 1st option: get from the b&w source
937  if (get_srcBW())
938  {
939  buffBW = std::make_shared<ImageBW>(*get_srcBW(), bbox);
940  return buffBW;
941  }
942  // 2nd option: get parent BW buffer
943  if (!parent.expired())
944  {
945  if (parent.lock()->GetBW(create))
946  {
947  Rect localbbox(bbox.GetLeft() - parent.lock()->GetAbsoluteBBox().GetLeft(),
948  bbox.GetTop() - parent.lock()->GetAbsoluteBBox().GetTop(),
949  bbox.GetRight() - parent.lock()->GetAbsoluteBBox().GetLeft(),
950  bbox.GetBottom() - parent.lock()->GetAbsoluteBBox().GetTop());
951  buffBW = std::make_shared<ImageBW>(*parent.lock()->GetBW(false), localbbox);
952  return buffBW;
953  }
954  }
955  // 3rd option: create it
956  if (create)
957  {
958  SImageGray tmp = GetGray(true);
959  if (tmp)
960  {
961  buffBW = MoveShared(MakeImageBW(*tmp));
962  return buffBW;
963  }
964  else
965  {
966  return nullptr;
967  }
968  }
969  // else
970  if (create)
971  {
972  CRNWarning(String(U"SImageBW* Block::GetBW(): ") +
973  _("Cannot access to any source or buffer."));
974  }
975  return nullptr;
976  }
977 
978  return buffBW;
979 }
980 
986 std::vector<String> Block::GetTreeNames() const
987 {
988  std::vector<String> list;
989  for (Map::const_iterator it = child->begin(); it != child->end(); ++it)
990  {
991  list.push_back(it->first);
992  }
993  return list;
994 }
995 
1004 void Block::Save(const Path &fname)
1005 {
1006  std::lock_guard<std::mutex> lockf(crn::FileShield::GetMutex(fname)); // lock the file
1007  if (!fname)
1008  {
1009  throw ExceptionInvalidArgument(StringUTF8("void Block::Save(const Path &fname): ") +
1010  _("No filename given."));
1011  }
1012 
1013  xml::Document doc;
1014  doc.PushBackComment("libcrn Block tree file");
1015  addToXml(doc);
1016  doc.Save(fname.CStr()); // may throw
1017 }
1018 
1023 void Block::addToXml(xml::Document &parent)
1024 {
1025  xml::Element eb(parent.PushBackElement("Block"));
1026  eb.SetAttribute("left", bbox.GetLeft());
1027  eb.SetAttribute("top", bbox.GetTop());
1028  eb.SetAttribute("right", bbox.GetRight());
1029  eb.SetAttribute("bottom", bbox.GetBottom());
1030  for (Map::const_iterator it = child->begin(); it != child->end(); ++it)
1031  {
1032  xml::Element el(eb.PushBackElement("BlockTree"));
1033  el.SetAttribute("treename", it->first.CStr());
1034  SVector v = std::static_pointer_cast<Vector>(it->second);
1035  for (size_t tmp = 0; tmp < v->Size(); tmp++)
1036  {
1037  std::static_pointer_cast<Block>(v->At(tmp))->addToXml(el);
1038  }
1039  }
1040  // save userdata
1042 }
1043 
1048 void Block::addToXml(xml::Element &parent)
1049 {
1050  xml::Element eb(parent.PushBackElement("Block"));
1051  eb.SetAttribute("left", bbox.GetLeft());
1052  eb.SetAttribute("top", bbox.GetTop());
1053  eb.SetAttribute("right", bbox.GetRight());
1054  eb.SetAttribute("bottom", bbox.GetBottom());
1055  for (Map::const_iterator it = child->begin(); it != child->end(); ++it)
1056  {
1057  xml::Element el(eb.PushBackElement("BlockTree"));
1058  el.SetAttribute("treename", it->first.CStr());
1059  SVector v = std::static_pointer_cast<Vector>(it->second);
1060  for (size_t tmp = 0; tmp < v->Size(); tmp++)
1061  {
1062  std::static_pointer_cast<Block>(v->At(tmp))->addToXml(el);
1063  }
1064  }
1065  // save userdata
1067 }
1068 
1079 bool Block::Append(const Path &fname)
1080 {
1081  std::lock_guard<std::mutex> lockf(crn::FileShield::GetMutex(fname)); // lock the file
1082 
1083  setFilename(fname);
1084  if (!fname)
1085  {
1086  throw ExceptionInvalidArgument(StringUTF8("bool Block::Append(const Path &fname): ") +
1087  _("No filename given."));
1088  }
1089  Path fn(fname);
1090  fn.ToLocal();
1091  if (!IO::Access(fn, IO::EXISTS))
1092  { // file not found, do not warn
1093  return false;
1094  }
1095  xml::Document doc(fname); // may throw
1096  xml::Element root = doc.GetRoot();
1097  int l = root.GetAttribute<int>("left", false); // may throw
1098  int t = root.GetAttribute<int>("top", false); // may throw
1099  int b = root.GetAttribute<int>("bottom", false); // may throw
1100  int r = root.GetAttribute<int>("right", false); // may throw
1101  if (image_is_open)
1102  {
1103  if ((l != bbox.GetLeft()) || (t != bbox.GetTop()) ||
1104  (r != bbox.GetRight()) || (b != bbox.GetBottom()))
1105  {
1106  throw ExceptionRuntime(StringUTF8("bool Block::Append(const Path &fname): ") +
1107  _("Saved block do not have the same size."));
1108  }
1109  }
1110  else
1111  {
1112  bbox.SetLeft(l);
1113  bbox.SetRight(r);
1114  bbox.SetTop(t);
1115  bbox.SetBottom(b);
1116  }
1117  addTreeFromXml(root);
1118  return true;
1119 }
1120 
1130 void Block::addTreeFromXml(xml::Element &bnode)
1131 {
1132  for (xml::Element tree = bnode.BeginElement(); tree != bnode.EndElement(); ++tree)
1133  {
1134  if (tree.GetName() != "BlockTree")
1135  continue;
1136  const StringUTF8 treename = tree.GetAttribute<StringUTF8>("treename");
1137  for (xml::Element block = tree.BeginElement(); block != tree.EndElement(); ++block)
1138  {
1139  if (block.GetName() != "Block")
1140  continue;
1141  int l = block.GetAttribute<int>("left", false); // may throw
1142  int t = block.GetAttribute<int>("top", false); // may throw
1143  int b = block.GetAttribute<int>("bottom", false); // may throw
1144  int r = block.GetAttribute<int>("right", false); // may throw
1145  StringUTF8 bn = block.GetAttribute<StringUTF8>("name");
1146  if (bn.IsEmpty()) // ancient files have a blockname in stead of a name
1147  bn = block.GetAttribute<StringUTF8>("blockname");
1148  Rect rec(l, t, r, b);
1149  if (!rec.IsValid())
1150  throw ExceptionIO(StringUTF8("void Block::addTreeFromXml(xml::Node &bnode): ") +
1151  _("Wrong content."));
1152  SBlock newblock = AddChildAbsolute(treename, rec, (bn.IsNotEmpty() ? bn : String(U"")));
1153  newblock->addTreeFromXml(block);
1154  }
1155  }
1156  // load userdata
1158  if (!GetName()) // ancient files have a blockname in stead of a name
1159  {
1160  const StringUTF8 bn = bnode.GetAttribute<StringUTF8>("blockname");
1161  if (bn.IsNotEmpty())
1162  SetName(bn);
1163  }
1164 }
1165 
1172 bool Block::HasTree(const String &tname) const
1173 {
1174  if (child->Find(tname) == child->end())
1175  return false;
1176  else
1177  return GetNbChildren(tname) != 0;
1178 }
1179 
1187 void Block::RemoveTree(const String &tname)
1188 {
1189  child->Remove(tname); // may throw
1190 }
1191 
1192 /*****************************************************************************/
1205 SImageGradient Block::GetGradient(bool create, double sigma, size_t diffusemaxiter, double diffusemaxdiv)
1206 {
1207  if (buffGradient)
1208  return buffGradient;
1209  if (!create)
1210  return nullptr;
1211  // need to compute the buffer
1212  grad_sigma = sigma;
1213  grad_diffusemaxiter = diffusemaxiter;
1214  grad_diffusemaxdiv = diffusemaxdiv;
1215 
1216  // is the source a gradient?
1217  if (get_srcGradient())
1218  {
1219  // warning all arguments are ignored!
1220  buffGradient = std::make_shared<ImageGradient>(*get_srcGradient(), bbox);
1221  buffGradient->SetMinModule(get_srcGradient()->GetMinModule());
1222  return buffGradient;
1223  }
1224 
1225  // autocompute sigma if needed
1226  if (sigma == -1)
1227  {
1228  sigma = 0.5;
1229  auto ig = GetGray(true);
1230  if (ig)
1231  {
1232  size_t sw = StrokesWidth(*ig, 50, 3);
1233  sigma = double(sw) / 6.0;
1234  }
1235  }
1236 
1237  // topmost block
1238  if (parent.expired())
1239  {
1241  if (diffusemaxiter)
1242  diff.Diffuse(diffusemaxiter, diffusemaxdiv);
1243 
1244  buffGradient = MoveShared(diff.MakeImageGradient());
1245  return buffGradient;
1246  }
1247 
1248  // not topmost
1249  // look for a parent with a gradient
1250  WBlock gpar = parent;
1251  while (!gpar.expired())
1252  {
1253  if (gpar.lock()->GetGradient(false) && (gpar.lock()->grad_sigma == grad_sigma) && (gpar.lock()->grad_diffusemaxiter == grad_diffusemaxiter) && (gpar.lock()->grad_diffusemaxdiv == grad_diffusemaxdiv))
1254  break;
1255  gpar = gpar.lock()->parent;
1256  }
1257  if (!gpar.expired())
1258  { // some top gradient was already computed
1259  Rect b(bbox);
1260  b.Translate(-gpar.lock()->GetAbsoluteBBox().GetLeft(),
1261  -gpar.lock()->GetAbsoluteBBox().GetTop());
1262  SImageGradient topgrad(gpar.lock()->GetGradient(true, sigma, diffusemaxiter, diffusemaxdiv));
1263  buffGradient = std::make_shared<ImageGradient>(*topgrad, b);
1264  buffGradient->SetMinModule(topgrad->GetMinModule());
1265 
1266  return buffGradient;
1267  }
1268  else
1269  { // top gradient not computed
1270  Rect clip = GetTop().lock()->GetAbsoluteBBox();
1271  // add a margin
1272  clip.SetLeft(Max(0, bbox.GetLeft() - 10));
1273  clip.SetTop(Max(0, bbox.GetTop() - 10));
1274  clip.SetRight(Min(clip.GetRight(), bbox.GetRight() + 10));
1275  clip.SetBottom(Min(clip.GetBottom(), bbox.GetBottom() + 10));
1276  int offsetx = bbox.GetLeft() - clip.GetLeft();
1277  int offsety = bbox.GetTop() - clip.GetTop();
1278  auto tmp = ImageRGB(*GetTop().lock()->GetRGB(), clip);
1280  if (diffusemaxiter)
1281  diff.Diffuse(diffusemaxiter, diffusemaxdiv);
1282 
1283  auto tmpGradient = diff.MakeImageGradient();
1284  Rect r;
1285  r.SetLeft((int)offsetx);
1286  r.SetTop((int)offsety);
1287  r.SetRight((int)(offsetx + bbox.GetWidth() - 1));
1288  r.SetBottom((int)(offsety + bbox.GetHeight() - 1));
1289  buffGradient = std::make_shared<ImageGradient>(tmpGradient, r);
1290  buffGradient->SetMinModule(tmpGradient.GetMinModule());
1291 
1292  return buffGradient;
1293  }
1294 }
1295 
1296 
1297 /*****************************************************************************/
1305 {
1306  if (!parent.expired())
1307  parent.lock()->ReloadImage();
1308  else
1309  {
1310  FlushAll(true);
1311  image_is_open = false;
1312  srcRGB = nullptr;
1313  srcGray = nullptr;
1314  srcBW = nullptr;
1315  srcGradient = nullptr;
1316  openImage();
1317  refreshSources();
1318  }
1319 }
1320 
1321 /*****************************************************************************/
1327 void Block::FlushAll(bool recursive)
1328 {
1329  FlushRGB(recursive);
1330  FlushGray(recursive);
1331  FlushBW(recursive);
1332  FlushGradient(recursive);
1333 }
1334 
1335 /*****************************************************************************/
1341 void Block::FlushRGB(bool recursive)
1342 {
1343  buffRGB = nullptr;
1344  if (recursive)
1345  {
1346  for (crn::Map::pair p : child)
1347  {
1348  SVector l(std::static_pointer_cast<Vector>(p.second));
1349  for (SObject b : l)
1350  {
1351  std::static_pointer_cast<Block>(b)->FlushRGB(true);
1352  }
1353  }
1354  }
1355 }
1356 
1357 /*****************************************************************************/
1363 void Block::FlushGray(bool recursive)
1364 {
1365  buffGray = nullptr;
1366  if (recursive)
1367  {
1368  for (crn::Map::pair p : child)
1369  {
1370  SVector l(std::static_pointer_cast<Vector>(p.second));
1371  for (SObject b : l)
1372  {
1373  std::static_pointer_cast<Block>(b)->FlushGray(true);
1374  }
1375  }
1376  }
1377 }
1378 
1379 /*****************************************************************************/
1385 void Block::FlushBW(bool recursive)
1386 {
1387  buffBW = nullptr;
1388  if (recursive)
1389  {
1390  for (crn::Map::pair p : child)
1391  {
1392  SVector l(std::static_pointer_cast<Vector>(p.second));
1393  for (SObject b : l)
1394  {
1395  std::static_pointer_cast<Block>(b)->FlushBW(true);
1396  }
1397  }
1398  }
1399 }
1400 
1401 /*****************************************************************************/
1407 void Block::FlushGradient(bool recursive)
1408 {
1409  buffGradient = nullptr;
1410  if (recursive)
1411  {
1412  for (crn::Map::pair p : child)
1413  {
1414  SVector l(std::static_pointer_cast<Vector>(p.second));
1415  for (SObject b : l)
1416  {
1417  std::static_pointer_cast<Block>(b)->FlushGradient(true);
1418  }
1419  }
1420  }
1421 }
1422 
1423 /*****************************************************************************/
1435 void Block::SubstituteRGB(const SImageRGB &img)
1436 {
1437  if (!img)
1438  {
1439  FlushRGB();
1440  return;
1441  }
1442  int w = GetAbsoluteBBox().GetWidth();
1443  int h = GetAbsoluteBBox().GetHeight();
1444  if ((int(img->GetWidth()) != w) || (int(img->GetHeight()) != h))
1445  {
1446  throw ExceptionDimension(StringUTF8("bool Block::SubstituteRGB(const SImageRGB &img): ") +
1447  _("Wrong image dimensions."));
1448  }
1449  FlushRGB();
1450  buffRGB = img;
1451 }
1452 
1453 /*****************************************************************************/
1465 void Block::SubstituteGray(const SImageGray &img)
1466 {
1467  if (!img)
1468  {
1469  FlushGray();
1470  return;
1471  }
1472  int w = GetAbsoluteBBox().GetWidth();
1473  int h = GetAbsoluteBBox().GetHeight();
1474  if ((int(img->GetWidth()) != w) || (int(img->GetHeight()) != h))
1475  {
1476  throw ExceptionDimension(StringUTF8("bool Block::SubstituteGray(const SImageGray &img): ") +
1477  _("Wrong image dimensions."));
1478  }
1479  FlushGray();
1480  buffGray = img;
1481 }
1482 
1483 /*****************************************************************************/
1495 void Block::SubstituteBW(const SImageBW &img)
1496 {
1497  if (!img)
1498  {
1499  FlushBW();
1500  return;
1501  }
1502  int w = GetAbsoluteBBox().GetWidth();
1503  int h = GetAbsoluteBBox().GetHeight();
1504  if ((int(img->GetWidth()) != w) || (int(img->GetHeight()) != h))
1505  {
1506  throw ExceptionDimension(StringUTF8("bool Block::SubstituteBW(const SImageBW &img): ") +
1507  _("Wrong image dimensions."));
1508  }
1509  FlushBW();
1510  buffBW = img;
1511 }
1512 
1513 /*****************************************************************************/
1525 void Block::SubstituteGradient(const SImageGradient &img)
1526 {
1527  if (!img)
1528  {
1529  FlushGradient();
1530  return;
1531  }
1532  int w = GetAbsoluteBBox().GetWidth();
1533  int h = GetAbsoluteBBox().GetHeight();
1534  if ((int(img->GetWidth()) != w) || (int(img->GetHeight()) != h))
1535  {
1536  throw ExceptionDimension(StringUTF8("bool Block::SubstituteGradient(const SImageGradient &img): ") +
1537  _("Wrong image dimensions."));
1538  }
1539  FlushGradient();
1540  buffGradient = img;
1541 }
1542 
1544 struct interResolv
1545 {
1547  void Add(int a, int b)
1548  {
1549  int found = -1;
1550  int foundwhat = 0; // -1 = a, +1 = b
1551  for (int tmp = 0; tmp < int(inter.size()); ++tmp)
1552  {
1553  if (inter[tmp].count(a))
1554  {
1555  inter[tmp].insert(b);
1556  found = tmp;
1557  foundwhat = -1;
1558  break;
1559  }
1560  if (inter[tmp].count(b))
1561  {
1562  inter[tmp].insert(a);
1563  found = tmp;
1564  foundwhat = +1;
1565  break;
1566  }
1567  }
1568  if (found == -1)
1569  {
1570  inter.push_back(std::set<int>());
1571  inter.back().insert(a);
1572  inter.back().insert(b);
1573  }
1574  else
1575  {
1576  if (foundwhat == -1) // found a
1577  {
1578  for (int tmp = 0; tmp < int(inter.size()); ++tmp)
1579  {
1580  if (tmp == found)
1581  continue;
1582  if (inter[tmp].count(b))
1583  {
1584  inter[found].insert(inter[tmp].begin(), inter[tmp].end());
1585  inter.erase(inter.begin() + tmp);
1586  break;
1587  }
1588  }
1589  }
1590  else
1591  { // foundwhat == +1, found b
1592  for (int tmp = 0; tmp < int(inter.size()); ++tmp)
1593  {
1594  if (tmp == found)
1595  continue;
1596  if (inter[tmp].count(a))
1597  {
1598  inter[found].insert(inter[tmp].begin(), inter[tmp].end());
1599  inter.erase(inter.begin() + tmp);
1600  break;
1601  }
1602  }
1603  }
1604  }
1605  }
1606  // this ought to be a list since erase() are performed on it, but sweeping a list is slower than sweeping a vector
1607  std::vector<std::set<int> > inter;
1609  void Compile()
1610  {
1611  corresp.clear();
1612  for (std::set<int> &s : inter)
1613  {
1614  auto it = s.begin();
1615  int rep = *it;
1616  ++it;
1617  for (; it != s.end(); ++it)
1618  corresp[*it] = rep;
1619  }
1620  }
1621  std::map<int,int> corresp;
1622 };
1625 /*****************************************************************************/
1640 UImageIntGray Block::ExtractCC(const String &tree)
1641 {
1642  SImageBW bwi = GetBW(true);
1643 
1644  int num = 1;
1645  size_t w = bwi->GetWidth();
1646  size_t h = bwi->GetHeight();
1647  UImageIntGray imap = std::make_unique<ImageIntGray>(w, h, 0);
1648  interResolv ir;
1649  // c2 c3 c4
1650  // c1 C .
1651  // . . .
1652  // 1st pixel
1653  if (bwi->At(0) == pixel::BWBlack)
1654  { // first CC found
1655  imap->At(0) = num;
1656  num += 1;
1657  }
1658  // 1st line
1659  for (size_t x = 1; x < w; ++x)
1660  {
1661  if (bwi->At(x) == pixel::BWBlack)
1662  {
1663  int pv = imap->At(x - 1);
1664  if (pv != 0)
1665  { // continue previous CC
1666  imap->At(x) = pv;
1667  }
1668  else
1669  { // new CC
1670  imap->At(x) = num;
1671  num += 1;
1672  }
1673  }
1674  }
1675  // all other lines
1676  size_t yoffset = 0;
1677  for (size_t y = 1; y < h; ++y)
1678  {
1679  yoffset += w;
1680  // 1st pixel of the line
1681  if (bwi->At(yoffset) == pixel::BWBlack)
1682  {
1683  // c3
1684  size_t c3 = yoffset - w;
1685  int cval = imap->At(c3);
1686  if (cval != 0)
1687  {
1688  imap->At(yoffset) = cval;
1689  }
1690  else
1691  {
1692  // c4
1693  cval = imap->At(c3 + 1);
1694  if (cval != 0)
1695  {
1696  imap->At(yoffset) = cval;
1697  }
1698  else
1699  {
1700  // new
1701  imap->At(yoffset) = num;
1702  num += 1;
1703  }
1704  }
1705  }
1706  // middle pixels
1707  for (size_t x = 1; x < w - 1; ++x)
1708  {
1709  size_t offset = x + yoffset;
1710  if (bwi->At(offset) == pixel::BWBlack)
1711  {
1712  // c3
1713  size_t c3 = offset - w;
1714  int cval = imap->At(c3);
1715  if (cval != 0)
1716  {
1717  imap->At(offset) = cval;
1718  continue;
1719  }
1720  // c1
1721  size_t c1 = offset - 1;
1722  size_t c4 = c3 + 1;
1723  cval = imap->At(c1);
1724  int cval4 = imap->At(c4);
1725  if (cval != 0)
1726  {
1727  imap->At(offset) = cval;
1728  if ((cval4 != 0) && (cval4 != cval))
1729  ir.Add(cval, cval4);
1730  continue;
1731  }
1732  // c2
1733  size_t c2 = c3 - 1;
1734  cval = imap->At(c2);
1735  if (cval != 0)
1736  {
1737  imap->At(offset) = cval;
1738  if ((cval4 != 0) && (cval4 != cval))
1739  ir.Add(cval, cval4);
1740  continue;
1741  }
1742  // c4
1743  if (cval4 != 0)
1744  {
1745  imap->At(offset) = cval4;
1746  continue;
1747  }
1748  // new
1749  imap->At(offset) = num;
1750  num += 1;
1751  }
1752  }
1753  // last pixel
1754  size_t offset = w - 1 + yoffset;
1755  if (bwi->At(offset) == pixel::BWBlack)
1756  {
1757  // c3
1758  size_t c3 = offset - w;
1759  int cval = imap->At(c3);
1760  if (cval != 0)
1761  {
1762  imap->At(offset) = cval;
1763  continue;
1764  }
1765  // c1
1766  size_t c1 = offset - 1;
1767  cval = imap->At(c1);
1768  if (cval != 0)
1769  {
1770  imap->At(offset) = cval;
1771  continue;
1772  }
1773  // c2
1774  size_t c2 = c3 - 1;
1775  cval = imap->At(c2);
1776  if (cval != 0)
1777  {
1778  imap->At(offset) = cval;
1779  continue;
1780  }
1781  // new
1782  imap->At(offset) = num;
1783  num += 1;
1784  }
1785  }
1786  // now merge the CCs and create the boxes
1787  std::map<int, Rect> bboxes;
1788  ir.Compile();
1789  FOREACHPIXEL(x, y, *imap)
1790  {
1791  int v = imap->At(x, y);
1792  if (v != 0)
1793  {
1794  auto it = ir.corresp.find(v);
1795  if (it != ir.corresp.end())
1796  { // has to be changed
1797  v = it->second;
1798  imap->At(x, y) = v;
1799  }
1800  if (bboxes.find(v) == bboxes.end())
1801  { // create bounding box
1802  //bboxes[v] = Rect(x, y, x, y);
1803  bboxes.insert(std::make_pair(v, Rect(int(x), int(y), int(x), int(y))));
1804  }
1805  else
1806  { // grow bounding box
1807  Rect &r = bboxes[v];
1808  if (r.GetLeft() > int(x))
1809  r.SetLeft(int(x));
1810  if (r.GetTop() > int(y))
1811  r.SetTop(int(y));
1812  if (r.GetRight() < int(x))
1813  r.SetRight(int(x));
1814  if (r.GetBottom() < int(y))
1815  r.SetBottom(int(y));
1816  }
1817  }
1818  }
1819  // add sub blocks
1820  for (const auto &bbox : bboxes)
1821  {
1822  if (!bbox.second.IsValid())
1823  {
1824  continue;
1825  }
1826  AddChildRelative(tree, bbox.second, String(bbox.first));
1827  }
1828 
1829  return std::forward<UImageIntGray>(imap);
1830 }
1831 
1832 /*****************************************************************************/
1845 void Block::GetTreeMeans(const String &tree, double *mwidth, double *mheight, double *marea) const
1846 {
1847  if (!HasTree(tree))
1848  throw ExceptionInvalidArgument(StringUTF8("void Block::GetTreeMeans(const String &tree, "
1849  "double *mwidth, double *mheight, double *marea) const: ") + _("tree not found."));
1850  long w = 0, h = 0, a = 0;
1851  for (size_t tmp = 0; tmp < GetNbChildren(tree); tmp++)
1852  {
1853  SCBlock b = GetChild(tree, tmp);
1854  w += b->GetAbsoluteBBox().GetWidth();
1855  h += b->GetAbsoluteBBox().GetHeight();
1856  a += b->GetAbsoluteBBox().GetArea();
1857  }
1858  double tot = (double)GetNbChildren(tree);
1859  if (mwidth)
1860  *mwidth = double(w) / tot;
1861  if (mheight)
1862  *mheight = double(h) / tot;
1863  if (marea)
1864  *marea = double(a) / tot;
1865 }
1866 
1867 /*****************************************************************************/
1877 size_t Block::GetNbChildren(const String &tree) const
1878 {
1879  if (child->Find(tree) == child->end())
1880  throw ExceptionInvalidArgument(StringUTF8("size_t Block::GetNbChildren(const String &tree) const: ") + _("tree not found."));
1881  const SVector v = std::static_pointer_cast<Vector>(child->Get(tree));
1882  return v->Size();
1883 }
1884 
1885 /*****************************************************************************/
1897 SBlock Block::GetChild(const String &tree, size_t num)
1898 {
1899  if (child->Find(tree) == child->end())
1900  throw ExceptionNotFound(StringUTF8("SBlock Block::GetChild(const String &tree, size_t num): ") + _("tree not found."));
1901  else if (num >= getChildList(tree)->Size())
1902  throw ExceptionDomain(StringUTF8("SBlock Block::GetChild(const String &tree, size_t num): ") + _("index out of bounds."));
1903  else
1904  return std::static_pointer_cast<Block>(getChildList(tree)->At(num));
1905 }
1906 
1907 /*****************************************************************************/
1919 SCBlock Block::GetChild(const String &tree, size_t num) const
1920 {
1921  if (child->Find(tree) == child->end())
1922  throw ExceptionNotFound(StringUTF8("SCBlock Block::GetChild(const String &tree, size_t num) const: ") + _("tree not found."));
1923  else
1924  {
1925  SCVector v = std::static_pointer_cast<const Vector>(child->Get(tree));
1926  if (num >= v->Size())
1927  throw ExceptionDomain(StringUTF8("SCBlock Block::GetChild(const String &tree, size_t num) const: ") + _("index out of bounds."));
1928  else
1929  return std::static_pointer_cast<const Block>(v->At(num));
1930  }
1931 }
1932 
1933 /*****************************************************************************/
1944 SBlock Block::GetChild(const String &tree, const String &name)
1945 {
1946  if (child->Find(tree) == child->end())
1947  throw ExceptionNotFound(StringUTF8("SBlock Block::GetChild(const String &tree, const String &name): ") + _("tree not found."));
1948  SVector v = std::static_pointer_cast<Vector>(child->Get(tree));
1949  for (SObject ob : v)
1950  {
1951  SBlock b(std::static_pointer_cast<Block>(ob));
1952  if (b->GetName() == name)
1953  return b;
1954  }
1955  throw ExceptionNotFound(StringUTF8("SBlock Block::GetChild(const String &tree, const String &name): ") + _("block not found."));
1956 }
1957 
1958 /*****************************************************************************/
1969 SCBlock Block::GetChild(const String &tree, const String &name) const
1970 {
1971  if (child->Find(tree) == child->end())
1972  throw ExceptionNotFound(StringUTF8("SCBlock Block::GetChild(const String &tree, const String &name) const: ") + _("tree not found."));
1973  SCVector v = std::static_pointer_cast<const Vector>(child->Get(tree));
1974  for (SCObject ob : v)
1975  {
1976  SCBlock b(std::static_pointer_cast<const Block>(ob));
1977  if (b->GetName() == name)
1978  return b;
1979  }
1980  throw ExceptionNotFound(StringUTF8("SCBlock Block::GetChild(const String &tree, const String &name) const: ") + _("block not found."));
1981 }
1982 
1983 
1984 /*****************************************************************************/
1995 void Block::RemoveChild(const String &tree, size_t num)
1996 {
1997  if (child->Find(tree) == child->end())
1998  throw ExceptionNotFound(StringUTF8("void Block::RemoveChild(const String &tree, size_t num): ") +
1999  _("tree not found."));
2000 
2001  if (num >= getChildList(tree)->Size())
2002  throw ExceptionDomain(StringUTF8("void Block::RemoveChild(const String &tree, size_t num): ") +
2003  _("index out of bounds."));
2004  SBlock b(std::static_pointer_cast<Block>((*getChildList(tree))[num]));
2005  b->parent = WBlock();
2006  getChildList(tree)->Remove(num);
2007 }
2008 
2009 /*****************************************************************************/
2019 void Block::RemoveChild(const String &tree, const String &name)
2020 {
2021  RemoveChild(tree, GetChild(tree, name));
2022 }
2023 
2024 /*****************************************************************************/
2034 void Block::RemoveChild(const String &tree, SBlock b)
2035 {
2036  if (child->Find(tree) == child->end())
2037  throw ExceptionNotFound(StringUTF8("void Block::RemoveChild(const String &tree, SBlock b): ") +
2038  _("tree not found."));
2039 
2040  if (getChildList(tree)->Contains(b))
2041  {
2042  b->parent = WBlock();
2043  getChildList(tree)->Remove(b);
2044  }
2045  else
2046  throw ExceptionNotFound(StringUTF8("void Block::RemoveChild(const String &tree, SBlock b): ") +
2047  _("block not found."));
2048 }
2049 
2058 void Block::RemoveChildren(const String &tree, const std::set<SBlock> &toremove)
2059 {
2060  if (child->Find(tree) == child->end())
2061  throw ExceptionNotFound(StringUTF8("void Block::RemoveChildren(const String &tree, const std::set<SBlock> &toremove): ") +
2062  _("tree not found."));
2063  SVector lst(getChildList(tree));
2064  lst->RemoveIf(
2065  [&toremove](const SObject &b)
2066  { return toremove.find(std::static_pointer_cast<Block>(b)) != toremove.end(); }
2067  );
2068 }
2069 
2070 /*****************************************************************************/
2080 void Block::FilterMinAnd(const String &tree, size_t minw, size_t minh)
2081 {
2082  if (child->Find(tree) == child->end())
2083  {
2084  throw ExceptionNotFound(StringUTF8("void Block::FilterMinAnd(const String &tree, size_t minw, size_t minh): ") +
2085  _("tree not found."));
2086  }
2087 
2088  // Blocks to be removed are copied in a list first...
2089  std::set<SBlock> v;
2090  for (Vector::iterator it = getChildList(tree)->begin(); it != getChildList(tree)->end(); ++it)
2091  {
2092  SBlock b(std::static_pointer_cast<Block>(*it));
2093  if ((b->GetAbsoluteBBox().GetWidth() < int(minw)) && (b->GetAbsoluteBBox().GetHeight() < int(minh)))
2094  {
2095  v.insert(b);
2096  }
2097  }
2098 
2099  // ... then removed from child tree
2100  RemoveChildren(tree, v);
2101 }
2102 
2103 /*****************************************************************************/
2113 void Block::FilterMinOr(const String &tree, size_t minw, size_t minh)
2114 {
2115  if (child->Find(tree) == child->end())
2116  throw ExceptionNotFound(StringUTF8("void Block::FilterMinOr(const String &tree, size_t minw, size_t minh): ") +
2117  _("tree not found."));
2118  std::set<SBlock> v;
2119  for (Vector::iterator it = getChildList(tree)->begin(); it != getChildList(tree)->end(); ++it)
2120  {
2121  SBlock b(std::static_pointer_cast<Block>(*it));
2122  if ((b->GetAbsoluteBBox().GetWidth() < int(minw)) || (b->GetAbsoluteBBox().GetHeight() < int(minh)))
2123  v.insert(b);
2124  }
2125  RemoveChildren(tree, v);
2126 }
2127 
2128 /*****************************************************************************/
2138 void Block::FilterMaxAnd(const String &tree, size_t maxw, size_t maxh)
2139 {
2140  if (child->Find(tree) == child->end())
2141  throw ExceptionNotFound(StringUTF8("void Block::FilterMaxAnd(const String &tree, size_t maxw, size_t maxh): ") +
2142  _("tree not found."));
2143  std::set<SBlock> v;
2144  for (Vector::iterator it = getChildList(tree)->begin(); it != getChildList(tree)->end(); ++it)
2145  {
2146  SBlock b(std::static_pointer_cast<Block>(*it));
2147  if ((b->GetAbsoluteBBox().GetWidth() > int(maxw)) && (b->GetAbsoluteBBox().GetHeight() > int(maxh)))
2148  v.insert(b);
2149  }
2150  RemoveChildren(tree, v);
2151 }
2152 
2153 /*****************************************************************************/
2163 void Block::FilterMaxOr(const String &tree, size_t maxw, size_t maxh)
2164 {
2165  if (child->Find(tree) == child->end())
2166  throw ExceptionNotFound(StringUTF8("void Block::FilterMaxOr(const String &tree, size_t maxw, size_t maxh): ") +
2167  _("tree not found."));
2168  std::set<SBlock> v;
2169  for (Vector::iterator it = getChildList(tree)->begin(); it != getChildList(tree)->end(); ++it)
2170  {
2171  SBlock b(std::static_pointer_cast<Block>(*it));
2172  if ((b->GetAbsoluteBBox().GetWidth() > int(maxw)) || (b->GetAbsoluteBBox().GetHeight() > int(maxh)))
2173  v.insert(b);
2174  }
2175  RemoveChildren(tree, v);
2176 }
2177 
2178 /*****************************************************************************/
2187 void Block::FilterBorders(const String &tree, size_t margin)
2188 {
2189  if (child->Find(tree) == child->end())
2190  throw ExceptionNotFound(StringUTF8("void Block::FilterBorders(const String &tree, size_t margin): ") +
2191  _("tree not found."));
2192  std::set<SBlock> v;
2193  for (Vector::iterator it = getChildList(tree)->begin(); it != getChildList(tree)->end(); ++it)
2194  {
2195  SBlock b(std::static_pointer_cast<Block>(*it));
2196  if ((b->GetAbsoluteBBox().GetLeft() < int(GetAbsoluteBBox().GetLeft() + margin)) ||
2197  (b->GetAbsoluteBBox().GetTop() < int(GetAbsoluteBBox().GetTop() + margin)) ||
2198  (b->GetAbsoluteBBox().GetRight() > int(GetAbsoluteBBox().GetRight() - margin)) ||
2199  (b->GetAbsoluteBBox().GetBottom() > int(GetAbsoluteBBox().GetBottom() - margin)))
2200  v.insert(b);
2201  }
2202  RemoveChildren(tree, v);
2203 }
2204 
2205 /*****************************************************************************/
2215 void Block::FilterWidthRatio(const String &tree, double ratio)
2216 {
2217  if (child->Find(tree) == child->end())
2218  throw ExceptionNotFound(StringUTF8("void Block::FilterWidthRatio(const String &tree, double ratio): ") +
2219  _("tree not found."));
2220  if (ratio <= 0)
2221  throw ExceptionDomain(StringUTF8("bool Block::FilterWidthRatio("
2222  "const String &tree, double ratio): ") +
2223  _("ratio is null or negative."));
2224 
2225  getChildList(tree)->RemoveIf(
2226  [ratio](const SObject &b)
2227  {
2228  const Rect bbox(std::static_pointer_cast<Block>(b)->GetAbsoluteBBox());
2229  return bbox.GetWidth() > ratio * bbox.GetHeight();
2230  }
2231  );
2232 }
2233 
2234 /*****************************************************************************/
2244 void Block::FilterHeightRatio(const String &tree, double ratio)
2245 {
2246  if (child->Find(tree) == child->end())
2247  throw ExceptionNotFound(StringUTF8("void Block::FilterHeightRatio(const String &tree, double ratio): ") +
2248  _("tree not found."));
2249  if (ratio <= 0)
2250  throw ExceptionDomain(StringUTF8("bool Block::FilterHeightRatio("
2251  "const String &tree, double ratio): ") +
2252  _("ratio is null or negative."));
2253 
2254  getChildList(tree)->RemoveIf(
2255  [ratio](const SObject &b)
2256  {
2257  const Rect bbox(std::static_pointer_cast<Block>(b)->GetAbsoluteBBox());
2258  return bbox.GetHeight() > ratio * bbox.GetWidth();
2259  }
2260  );
2261 }
2262 
2263 /*****************************************************************************/
2279 bool Block::MergeChildren(const String &tree, double overlap, ImageIntGray *imap)
2280 {
2281  if (child->Find(tree) == child->end())
2282  throw ExceptionNotFound(StringUTF8("void Block::MergeChildren(const String &tree, double overlap, ImageIntGray *imap): ") +
2283  _("tree not found."));
2284  if (overlap < 0)
2285  throw ExceptionDomain(StringUTF8("void Block::MergeChildren(const String &tree, double overlap, ImageIntGray *imap): ") +
2286  _("overlap is negative."));
2287 
2288  std::map<size_t, std::map<size_t, size_t> > overlaps;
2289  SVector vtree(GetTree(tree));
2290  for (size_t b1 = 0; b1 < vtree->Size(); ++b1)
2291  {
2292  SBlock cb1(std::static_pointer_cast<Block>((*vtree)[b1]));
2293  Rect bb1(cb1->GetAbsoluteBBox());
2294  double ov1 = bb1.GetArea() * overlap;
2295  for (size_t b2 = b1 + 1; b2 < vtree->Size(); ++b2)
2296  {
2297  SBlock cb2(std::static_pointer_cast<Block>((*vtree)[b2]));
2298  Rect bb2(cb2->GetAbsoluteBBox());
2299  Rect rov = bb1 & bb2;
2300  if (rov.IsValid())
2301  {
2302  double rova = rov.GetArea();
2303  double ov2 = bb2.GetArea() * overlap;
2304  size_t dist = Min(Abs(bb1.GetCenterX() - bb2.GetCenterX()), Abs(bb1.GetCenterY() - bb2.GetCenterY()));
2305  if ((rova >= ov1) && (rova >= ov2))
2306  {
2307  if (ov1 >= ov2)
2308  {
2309  overlaps[b2][dist] = b1;
2310  }
2311  else
2312  {
2313  overlaps[b1][dist] = b2;
2314  break;
2315  }
2316  } // all two overlap
2317  else if (rova >= ov1)
2318  {
2319  overlaps[b1][dist] = b2;
2320  break;
2321  } // b1 overlaps
2322  else if (rova >= ov2)
2323  {
2324  overlaps[b2][dist] = b1;
2325  } // b2 overlaps
2326  } // intersection
2327  } // for b2
2328  } // for b1
2329  if (overlaps.empty())
2330  return false;
2331 
2332  // propagate
2333  std::map<size_t, size_t> change;
2334  for (auto it = overlaps.begin(); it != overlaps.end(); ++it)
2335  {
2336  size_t from = it->first;
2337  size_t to = it->second.begin()->second;
2338  auto next = overlaps.find(to);
2339  while (next != overlaps.end())
2340  {
2341  to = next->second.begin()->second;
2342  next = overlaps.find(to);
2343  }
2344  change[from] = to;
2345  }
2346  // merge
2347  std::set<SBlock> toremove;
2348  SVector lst(getChildList(tree));
2349  for (auto & elem : change)
2350  {
2351  SBlock bfrom(std::static_pointer_cast<Block>((*vtree)[elem.first]));
2352  SBlock bto(std::static_pointer_cast<Block>((*vtree)[elem.second]));
2353  int f = bfrom->GetName().ToInt();
2354  int t = bto->GetName().ToInt();
2355  //toremove.insert(it->first);
2356  toremove.insert(std::static_pointer_cast<Block>((*lst)[elem.first]));
2357  bto->SetAbsoluteBBox(bto->GetAbsoluteBBox() | bfrom->GetAbsoluteBBox());
2358  if (imap)
2359  {
2360  for (const Point2DInt &p : bfrom->GetAbsoluteBBox())
2361  {
2362  if (imap->At(p.X, p.Y) == f)
2363  imap->At(p.X, p.Y) = t;
2364  }
2365  }
2366  }
2367  RemoveChildren(tree, toremove);
2368 
2369  return true;
2370 }
2371 
2372 /*****************************************************************************/
2385 void Block::MergeSiblings(const String &tree, size_t index1, size_t index2, ImageIntGray *imap)
2386 {
2387  if (!HasTree(tree))
2388  {
2389  throw ExceptionNotFound(StringUTF8("bool Block::MergeSiblings("
2390  "const String &tree, size_t index1, size_t index1, "
2391  "ImageIntGray *imap): ") + _("Tree not found."));
2392  }
2393  if (index1 == index2)
2394  {
2395  throw ExceptionLogic(StringUTF8("bool Block::MergeSiblings("
2396  "const String &tree, size_t index1, size_t index1, "
2397  "ImageIntGray *imap): ") + _("Identical indexes."));
2398  }
2399  if ((index1 >= GetNbChildren(tree)) || (index2 >= GetNbChildren(tree)))
2400  {
2401  throw ExceptionDomain(StringUTF8("bool Block::MergeSiblings("
2402  "const String &tree, size_t index1, size_t index1, "
2403  "ImageIntGray *imap): ") + _("Index out of bounds."));
2404  }
2405  // update map image
2406  if (imap)
2407  {
2408  int v1, v2;
2409  v1 = (int)GetChild(tree, index1)->GetName().ToInt();
2410  v2 = (int)GetChild(tree, index2)->GetName().ToInt();
2411  for (Rect::iterator it = GetChild(tree, index2)->GetAbsoluteBBox().begin();
2412  it != GetChild(tree, index2)->GetAbsoluteBBox().end(); ++it)
2413  {
2414  if (imap->At(it->X, it->Y) == v2)
2415  imap->At(it->X, it->Y) = v1;
2416  }
2417  }
2418  // update child 1
2419  GetChild(tree, index1)->bbox |= GetChild(tree, index2)->GetAbsoluteBBox();
2420  // copy childrens from child2 into child1
2421  const std::vector<String> child2TreeNames = GetChild(tree, index2)->GetTreeNames();
2422  for (const auto & child2TreeName : child2TreeNames)
2423  for (size_t i = 0 ; i < GetChild(tree, index2)->GetNbChildren(child2TreeName) ; ++i)
2424  {
2425  SBlock child = GetChild(tree, index2)->GetChild(child2TreeName, i);
2426  child->parent = GetChild(tree, index1);
2427  GetChild(tree, index1)->getChildList(child2TreeName)->PushBack(child);
2428  }
2429  // remove child 2
2430  RemoveChild(tree, index2);
2431 }
2432 
2433 /*****************************************************************************/
2446 void Block::MergeSiblings(const String &tree, Block &sb1, Block &sb2, ImageIntGray *imap)
2447 {
2448  if (!HasTree(tree))
2449  {
2450  throw ExceptionNotFound(StringUTF8("bool Block::MergeSiblings("
2451  "const String &tree, Block &sb1, Block &sb2, "
2452  "ImageIntGray *imap): ") + _("Tree not found."));
2453  }
2454  // look for indexes
2455  int index1 = -1, index2 = -1;
2456  for (int tmp = 0; tmp < int(GetNbChildren(tree)); tmp++)
2457  {
2458  if (GetChild(tree, tmp).get() == &sb1)
2459  index1 = tmp;
2460  if (GetChild(tree, tmp).get() == &sb2)
2461  index2 = tmp;
2462  if ((index1 != -1) && (index2 != -1))
2463  break;
2464  }
2465  if ((index1 == -1) || (index2 == -1))
2466  {
2467  throw ExceptionNotFound(StringUTF8("bool Block::MergeSiblings("
2468  "const String &tree, Block &sb1, Block &sb2, "
2469  "ImageIntGray *imap): ") + _("Cannot find subblock."));
2470  }
2471 
2472  MergeSiblings(tree, index1, index2, imap);
2473 }
2474 
2481 {
2482  if (parent.expired())
2483  return self;
2484  else
2485  return parent.lock()->GetTop();
2486 }
2487 
2495 bool Block::IsParent(const Block &b) const
2496 {
2497  if (parent.expired())
2498  return false;
2499  if (parent.lock().get() == &b)
2500  return true;
2501  return parent.lock()->IsParent(b);
2502 }
2503 
2515 Block::pixel_iterator Block::PixelBegin(const String &tree, size_t num) const
2516 {
2517  if (child->Find(tree) == child->end())
2518  throw ExceptionInvalidArgument(StringUTF8("Block::pixel_iterator Block::PixelBegin(const String &tree, size_t num) const: ") +
2519  _("tree not found."));
2520  const SVector v = std::static_pointer_cast<Vector>(child->Get(tree));
2521  if (num >= v->Size())
2522  throw ExceptionDomain(StringUTF8("Block::pixel_iterator Block::PixelBegin(const String &tree, size_t num) const: ") +
2523  _("index out of bounds."));
2524  const SBlock b(std::static_pointer_cast<Block>(v->At(num)));
2525  Rect r = b->GetAbsoluteBBox();
2526  r.Translate(-bbox.GetLeft(), -bbox.GetTop());
2527  return r.begin();
2528 }
2539 {
2540  if (!b || !b->IsParent(*this))
2541  throw ExceptionInvalidArgument(StringUTF8("Block::pixel_iterator Block::PixelBegin(const SBlock &b) const: ") +
2542  _("null block or block is not a child."));
2543  Rect r = b->GetAbsoluteBBox();
2544  r.Translate(-bbox.GetLeft(), -bbox.GetTop());
2545  return r.begin();
2546 }
2558 Block::pixel_iterator Block::PixelEnd(const String &tree, size_t num) const
2559 {
2560  if (child->Find(tree) == child->end())
2561  throw ExceptionInvalidArgument(StringUTF8("Block::pixel_iterator Block::PixelEnd(const String &tree, size_t num) const: ") +
2562  _("tree not found."));
2563  const SVector v = std::static_pointer_cast<Vector>(child->Get(tree));
2564  if (num >= v->Size())
2565  throw ExceptionDomain(StringUTF8("Block::pixel_iterator Block::PixelEnd(const String &tree, size_t num) const: ") +
2566  _("index out of bounds."));
2567  return Block::pixel_iterator();
2568 }
2579 {
2580  if (!b || !b->IsParent(*this))
2581  throw ExceptionInvalidArgument(StringUTF8("Block::pixel_iterator Block::PixelEnd(const SBlock &b) const: ") +
2582  _("null block or block is not a child."));
2583  return Block::pixel_iterator();
2584 }
2585 
2601 {
2602  if (child->Find(tree) == child->end())
2603  throw ExceptionInvalidArgument(StringUTF8("Block::masked_pixel_iterator Block::MaskedPixelBegin(const String &tree, size_t num, pixel::BW mask_value): ") +
2604  _("tree not found."));
2605  SVector v = std::static_pointer_cast<Vector>(child->Get(tree));
2606  if (num >= v->Size())
2607  throw ExceptionDomain(StringUTF8("Block::masked_pixel_iterator Block::MaskedPixelBegin(const String &tree, size_t num, pixel::BW mask_value): ") +
2608  _("index out of bounds."));
2609  SBlock b(std::static_pointer_cast<Block>(v->At(num)));
2610  Rect r = b->GetAbsoluteBBox();
2611  r.Translate(-bbox.GetLeft(), -bbox.GetTop());
2612  return Block::masked_pixel_iterator(r, b->GetBW(true), r.GetLeft(), r.GetTop(), mask_value);
2613 }
2614 
2628 {
2629  if (!b || !b->IsParent(*this))
2630  throw ExceptionInvalidArgument(StringUTF8("Block::masked_pixel_iterator Block::MaskedPixelBegin(const SBlock &b, pixel::BW mask_value): ") +
2631  _("tree not found."));
2632  Rect r = b->GetAbsoluteBBox();
2633  r.Translate(-bbox.GetLeft(), -bbox.GetTop());
2634  return Block::masked_pixel_iterator(r, b->GetBW(true), r.GetLeft(), r.GetTop(), mask_value);
2635 }
2636 
2650 {
2651  if (child->Find(tree) == child->end())
2652  throw ExceptionInvalidArgument(StringUTF8("Block::masked_pixel_iterator Block::MaskedPixelEnd(const String &tree, size_t num, pixel::BW mask_value): ") +
2653  _("tree not found."));
2654  SCVector v = std::static_pointer_cast<Vector>(child->Get(tree));
2655  if (num >= v->Size())
2656  throw ExceptionDomain(StringUTF8("Block::masked_pixel_iterator Block::MaskedPixelEnd(const String &tree, size_t num, pixel::BW mask_value): ") +
2657  _("index out of bounds."));
2659 }
2660 
2672 {
2673  if (!b || !b->IsParent(*this))
2674  throw ExceptionInvalidArgument(StringUTF8("Block::masked_pixel_iterator Block::MaskedPixelEnd(const SBlock &b, pixel::BW mask_value): ") +
2675  _("null block or block is not a child."));
2677 }
2678 
2691 void Block::SortTree(const String &name, Direction direction)
2692 {
2693  if (!HasTree(name))
2694  {
2695  throw ExceptionInvalidArgument(StringUTF8("void Block::SortTree(const String &name, Direction direction): ")
2696  + _("Tree not found."));
2697  }
2698  if (direction == Direction::LEFT)
2699  {
2700  std::sort(BlockBegin(name), BlockEnd(name), [](const SObject &b1, const SObject &b2)
2701  {
2702  return std::static_pointer_cast<Block>(b1)->GetAbsoluteBBox().GetLeft() <
2703  std::static_pointer_cast<Block>(b2)->GetAbsoluteBBox().GetLeft();
2704  }
2705  );
2706  }
2707  else if (direction == Direction::RIGHT)
2708  {
2709  std::sort(BlockBegin(name), BlockEnd(name), [](const SObject &b1, const SObject &b2)
2710  {
2711  return std::static_pointer_cast<Block>(b1)->GetAbsoluteBBox().GetRight() >
2712  std::static_pointer_cast<Block>(b2)->GetAbsoluteBBox().GetRight();
2713  }
2714  );
2715  }
2716  else if (direction == Direction::TOP)
2717  {
2718  std::sort(BlockBegin(name), BlockEnd(name), [](const SObject &b1, const SObject &b2)
2719  {
2720  return std::static_pointer_cast<Block>(b1)->GetAbsoluteBBox().GetTop() <
2721  std::static_pointer_cast<Block>(b2)->GetAbsoluteBBox().GetTop();
2722  }
2723  );
2724  }
2725  else if (direction == Direction::BOTTOM)
2726  {
2727  std::sort(BlockBegin(name), BlockEnd(name), [](const SObject &b1, const SObject &b2)
2728  {
2729  return std::static_pointer_cast<Block>(b1)->GetAbsoluteBBox().GetBottom() >
2730  std::static_pointer_cast<Block>(b2)->GetAbsoluteBBox().GetBottom();
2731  }
2732  );
2733  }
2734  else
2735  {
2736  throw ExceptionDomain(StringUTF8("void Block::SortTree(const String &name, Direction direction): ")
2737  + _("Wrong direction."));
2738  }
2739 }
2740 
2742 void Block::refreshSources()
2743 {
2744  if (!parent.expired())
2745  {
2746  srcRGB = parent.lock()->srcRGB;
2747  srcGray = parent.lock()->srcGray;
2748  srcBW = parent.lock()->srcBW;
2749  srcGradient = parent.lock()->srcGradient;
2750  }
2751  for (crn::Map::pair p : child)
2752  {
2753  SVector l(std::static_pointer_cast<Vector>(p.second));
2754  for (SObject b : l)
2755  {
2756  std::static_pointer_cast<Block>(b)->refreshSources();
2757  }
2758  }
2759 }
2760 
block_iterator BlockBegin(const String &tree)
Returns an iterator on the first block of a tree.
Definition: CRNBlock.cpp:334
bool Append(const Path &fname)
Appends child trees from a file.
Definition: CRNBlock.cpp:1079
void Save()
Saves the child trees into the default file.
Definition: CRNBlock.h:143
Abstract class for images.
Definition: CRNImage.h:141
SBlock AddChildRelativeAt(const String &tree, Rect clip, size_t pos)
Adds a child to current block.
Definition: CRNBlock.cpp:726
SVector GetTree(const String &name)
Returns a list of children. Can be used with CRN_FOREACH.
Definition: CRNBlock.h:212
void FilterMaxOr(const String &tree, size_t maxw, size_t maxh)
Filters a child tree.
Definition: CRNBlock.cpp:2163
void FilterHeightRatio(const String &tree, double ratio)
Filters a child tree.
Definition: CRNBlock.cpp:2244
void FlushGray(bool recursive=false)
Frees the local gray buffer.
Definition: CRNBlock.cpp:1363
Comment PushBackComment(const StringUTF8 &text)
Adds a comment at the end of the children list.
Definition: CRNXml.cpp:1126
ImageBW MakeImageBW(const ImageGray &img)
SBlock AddChildRelative(const String &tree, Rect clip)
Adds a child to current block.
Definition: CRNBlock.cpp:670
Iterator on the blocks of a child tree.
XML element.
Definition: CRNXml.h:135
A generic runtime error.
Definition: CRNException.h:131
void RemoveChildren(const String &tree, const std::set< SBlock > &toremove)
Removes a set of blocks from a child tree.
Definition: CRNBlock.cpp:2058
void FilterBorders(const String &tree, size_t margin)
Filters a child tree.
Definition: CRNBlock.cpp:2187
complex base abstract class
Definition: CRNSavable.h:58
block_iterator BlockEnd(const String &tree)
Returns an iterator after the last block of a tree.
Definition: CRNBlock.cpp:354
void FilterMinOr(const String &tree, size_t minw, size_t minh)
Filters a child tree.
Definition: CRNBlock.cpp:2113
std::vector< pixel_type >::reference At(size_t x, size_t y) noexcept
Returns a reference to a pixel.
Definition: CRNImage.h:224
#define _(String)
Definition: CRNi18n.h:51
void SetAbsoluteBBox(const Rect &newbox)
Sets the absolute bounding box of the block.
Definition: CRNBlock.cpp:254
std::vector< String > GetTreeNames() const
Gets the list of the tree names.
Definition: CRNBlock.cpp:986
const T & Max(const T &a, const T &b)
Returns the max of two values.
Definition: CRNMath.h:47
WBlock GetTop()
Gets a reference to the topmost parent of the block.
Definition: CRNBlock.cpp:2480
Image< pixel::RGB< uint8_t >> ImageRGB
Color image class.
const char * CStr() const
Conversion to UTF8 cstring.
Definition: CRNString.cpp:167
int GetBottom() const
Returns the bottommost coordinate.
Definition: CRNRect.h:111
XML document.
Definition: CRNXml.h:429
Element GetRoot()
Gets the first element.
Definition: CRNXml.cpp:1034
void MergeSiblings(const String &tree, size_t index1, size_t index2, ImageIntGray *imap=nullptr)
Merges two subblocks.
Definition: CRNBlock.cpp:2385
Direction
An enumeration of directions.
Definition: CRNMath.h:122
Element PushBackElement(const StringUTF8 &name)
Adds an element at the end of the children list.
Definition: CRNXml.cpp:1087
virtual ~Block() override
Destructor.
Definition: CRNBlock.cpp:516
void FilterMinAnd(const String &tree, size_t minw, size_t minh)
Filters a child tree.
Definition: CRNBlock.cpp:2080
bool IsNotEmpty() const noexcept
Checks if the string is not empty.
const String & GetName() const
Returns the name of the object.
Definition: CRNSavable.h:78
#define CRNWarning(x)
Definition: CRNIO.h:145
Element BeginElement()
Gets the first child element.
Definition: CRNXml.h:174
#define FOREACHPIXEL(x, y, img)
Convenience macro to sweep an image.
Definition: CRNImage.h:37
int GetTop() const
Returns the topmost coordinate.
Definition: CRNRect.h:107
void serialize_internal_data(xml::Element &el) const
Dumps some internal data to an XML element.
Definition: CRNSavable.cpp:316
ImageGray MakeImageGray(const ImageRGB &img)
int GetLeft() const
Returns the leftmost coordinate.
Definition: CRNRect.h:99
A generic logic error.
Definition: CRNException.h:71
Rect::iterator pixel_iterator
Iterator on the pixels the block.
Definition: CRNBlock.h:228
bool IsParent(const Block &b) const
Checks if a block is a parent of current.
Definition: CRNBlock.cpp:2495
static std::mutex & GetMutex(const Path &fname)
Gets the mutex associated to a file.
A UTF32 character string class.
Definition: CRNString.h:61
int SetBottom(int endY) noexcept
Changes the bottommost coordinate.
Definition: CRNRect.h:194
SImageBW GetBW(bool create=true)
Returns a pointer to the local b&w buffer.
Definition: CRNBlock.cpp:932
A generic domain error.
Definition: CRNException.h:83
pixel_iterator PixelEnd(const String &tree, size_t num) const
Returns an iterator after the last pixel of the block.
Definition: CRNBlock.cpp:2558
bool MergeChildren(const String &tree, double overlap, ImageIntGray *imap=nullptr)
Merges overlapping children in a tree.
Definition: CRNBlock.cpp:2279
void SubstituteGradient(const SImageGradient &img)
Substitutes the Gradient buffer with a new one.
Definition: CRNBlock.cpp:1525
SImageGray GetGray(bool create=true)
Returns a pointer to the local gray buffer.
Definition: CRNBlock.cpp:862
static SBlock New(const SImage &src, const String &nam=U"")
Top block creator.
Definition: CRNBlock.cpp:46
A block.
Definition: CRNBlock.h:52
static Differential NewGaussian(const ImageRGB &src, RGBProjection proj, double sigma)
Convolution with Gaussian derivatives.
const char * CStr() const noexcept
Conversion to UTF8 cstring.
const Rect & GetAbsoluteBBox() const noexcept
Gets the absolute bounding box of the block.
Definition: CRNBlock.h:71
A convenience class for file paths.
Definition: CRNPath.h:39
masked_pixel_iterator MaskedPixelBegin(const String &tree, size_t num, pixel::BW mask_value=pixel::BWBlack)
Returns a masked iterator on the first pixel of the block.
Definition: CRNBlock.cpp:2600
unsigned int GetArea() const noexcept
Returns the area of the rectangle.
Definition: CRNRect.h:142
A dimension error.
Definition: CRNException.h:119
void SetAttribute(const StringUTF8 &name, const StringUTF8 &value)
Sets the value of an attribute.
Definition: CRNXml.cpp:595
std::map< String, SObject >::const_iterator const_iterator
const_iterator on the contents of the container
Definition: CRNMap.h:93
SBlock AddChildAbsoluteAt(const String &tree, Rect clip, size_t pos)
Adds a child to current block.
Definition: CRNBlock.cpp:602
void SortTree(const String &name, Direction direction)
Sorts a child tree.
Definition: CRNBlock.cpp:2691
void Save(const Path &fname)
Saves to file.
Definition: CRNXml.cpp:1009
void FlushBW(bool recursive=false)
Frees the local b&w buffer.
Definition: CRNBlock.cpp:1385
bool IsValid() const noexcept
Returns whether the rect is valid.
Definition: CRNRect.h:94
Data vector class.
Definition: CRNVector.h:42
Rect GetRelativeBBox() const
Gets the relative bounding box of the block.
Definition: CRNBlock.cpp:316
iterator begin() const
Returns an iterator to the first point of the rectangle.
Definition: CRNRect.h:746
SImageGradient GetGradient(bool create=true, double sigma=-1, size_t diffusemaxiter=0, double diffusemaxdiv=std::numeric_limits< double >::max())
Returns a pointer to the local gradient buffer.
Definition: CRNBlock.cpp:1205
T GetAttribute(const StringUTF8 &name, bool silent=true) const
Gets an attribute.
Definition: CRNXml.h:219
bool BW
Definition: CRNPixel.h:40
Gradient image in polar form.
const Path & GetFilename() const noexcept
Returns the file name of the object.
Definition: CRNSavable.h:116
SBlock GetChild(const String &tree, size_t num)
Gets a block of a child tree.
Definition: CRNBlock.cpp:1897
void SubstituteBW(const SImageBW &img)
Substitutes the BW buffer with a new one.
Definition: CRNBlock.cpp:1495
void RemoveChild(const String &tree, size_t num)
Removes a block of a child tree.
Definition: CRNBlock.cpp:1995
void Translate(int x, int y)
Translates the rectangle.
Definition: CRNRect.cpp:598
int GetHeight() const
Returns the height of the rectangle.
Definition: CRNRect.h:119
void setFilename(const Path &fname)
Overwrites the filename.
Definition: CRNSavable.h:119
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
UImage NewImageFromFile(const Path &fname)
Loads an image from a file.
Definition: CRNImage.cpp:609
void FlushGradient(bool recursive=false)
Frees the local gradient buffer.
Definition: CRNBlock.cpp:1407
const T & Min(const T &a, const T &b)
Returns the min of two values.
Definition: CRNMath.h:49
void FilterMaxAnd(const String &tree, size_t maxw, size_t maxh)
Filters a child tree.
Definition: CRNBlock.cpp:2138
void SetName(const String &s)
Sets the name of the object.
Definition: CRNSavable.h:80
void GetTreeMeans(const String &tree, double *mwidth, double *mheight, double *marea) const
Returns the mean width, height and area of the subblocks.
Definition: CRNBlock.cpp:1845
int SetRight(int endX) noexcept
Changes the rightmost coordinate.
Definition: CRNRect.h:166
bool HasTree(const String &tname) const
Checks if a child tree exists.
Definition: CRNBlock.cpp:1172
UImageIntGray ExtractCC(const String &tree)
Creates a child tree with connected components.
Definition: CRNBlock.cpp:1640
int SetTop(int begY) noexcept
Changes the topmost coordinate.
Definition: CRNRect.h:180
std::shared_ptr< T > MoveShared(T &&v)
Definition: CRNType.h:329
int GetWidth() const
Returns the width of the rectangle.
Definition: CRNRect.h:115
Path & ToLocal()
Converts the path to the local format.
Definition: CRNPath.cpp:534
std::pair< const crn::String, SObject > pair
A (key, value) pair.
Definition: CRNMap.h:120
void FlushRGB(bool recursive=false)
Frees the local RGB buffer.
Definition: CRNBlock.cpp:1341
static bool Access(const Path &name, int mode)
Checks rights on a file.
Definition: CRNIO.cpp:161
SImageRGB GetRGB()
Returns a pointer to the local RGB buffer.
Definition: CRNBlock.cpp:795
void RemoveTree(const String &tname)
Deletes a child tree.
Definition: CRNBlock.cpp:1187
Data map class.
Definition: CRNMap.h:42
size_t StrokesWidth(const Image< T > &img, size_t maxval=50, size_t defaultval=0, typename std::enable_if< std::is_arithmetic< T >::value >::type *dummy=nullptr)
Definition: CRNImageGray.h:90
iterator for a Rect
Definition: CRNRect.h:723
std::shared_ptr< ImageBase > SImage
Definition: CRNImage.h:101
SBlock AddChildAbsolute(const String &tree, Rect clip)
Adds a child to current block.
Definition: CRNBlock.cpp:548
A character string class.
Definition: CRNStringUTF8.h:49
void SubstituteRGB(const SImageRGB &img)
Substitutes the RGB buffer with a new one.
Definition: CRNBlock.cpp:1435
size_t GetNbChildren(const String &tree) const
Gets the number of blocks in a child tree.
Definition: CRNBlock.cpp:1877
void ReloadImage()
Reloads the image.
Definition: CRNBlock.cpp:1304
Iterator on the pixels the block with BW buffer as mask.
Definition: CRNBlock.h:239
masked_pixel_iterator MaskedPixelEnd(const String &tree, size_t num, pixel::BW mask_value=pixel::BWBlack)
Returns a masked iterator on the first pixel of the block.
Definition: CRNBlock.cpp:2649
const Iterator on the blocks of a child tree
I/O error.
Definition: CRNException.h:179
A 2D point class.
Definition: CRNPoint2DInt.h:39
void FilterWidthRatio(const String &tree, double ratio)
Filters a child tree.
Definition: CRNBlock.cpp:2215
int GetRight() const
Returns the rightmost coordinate.
Definition: CRNRect.h:103
pixel_iterator PixelBegin(const String &tree, size_t num) const
Returns an iterator on the first pixel of the block.
Definition: CRNBlock.cpp:2515
Element PushBackElement(const StringUTF8 &name)
Adds an element at the end of the children list.
Definition: CRNXml.cpp:355
Block(const Block &)=delete
void SubstituteGray(const SImageGray &img)
Substitutes the Gray buffer with a new one.
Definition: CRNBlock.cpp:1465
Element EndElement()
Gets a null node.
Definition: CRNXml.h:176
size_t Size() const noexcept
Returns the number of data objects in the vector.
Definition: CRNVector.h:56
void SetRelativeBBox(Rect newbox)
Sets the absolute bounding box of the block.
Definition: CRNBlock.cpp:300
void deserialize_internal_data(xml::Element &el)
Initializes some internal data from an XML element.
Definition: CRNSavable.cpp:282
Invalid argument error (e.g.: nullptr pointer)
Definition: CRNException.h:107
int SetLeft(int begX) noexcept
Changes the leftmost coordinate.
Definition: CRNRect.h:152
An item was not found in a container.
Definition: CRNException.h:95
A rectangle class.
Definition: CRNRect.h:46
void FlushAll(bool recursive=false)
Frees the local buffers.
Definition: CRNBlock.cpp:1327