Leptonica  1.73
Image processing and image analysis suite
boxfunc4.c
Go to the documentation of this file.
1 /*====================================================================*
2  - Copyright (C) 2001 Leptonica. All rights reserved.
3  -
4  - Redistribution and use in source and binary forms, with or without
5  - modification, are permitted provided that the following conditions
6  - are met:
7  - 1. Redistributions of source code must retain the above copyright
8  - notice, this list of conditions and the following disclaimer.
9  - 2. Redistributions in binary form must reproduce the above
10  - copyright notice, this list of conditions and the following
11  - disclaimer in the documentation and/or other materials
12  - provided with the distribution.
13  -
14  - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18  - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
82 #include <math.h>
83 #include "allheaders.h"
84 
85 static l_int32 boxaTestEvenOddHeight(BOXA *boxa1, BOXA *boxa2, l_int32 start,
86  l_float32 *pdel1, l_float32 *pdel2);
87 static l_int32 boxaFillAll(BOXA *boxa);
88 
89 
90 /*---------------------------------------------------------------------*
91  * Boxa and boxaa range selection *
92  *---------------------------------------------------------------------*/
109 BOXA *
111  l_int32 first,
112  l_int32 last,
113  l_int32 copyflag)
114 {
115 l_int32 n, nbox, i;
116 BOX *box;
117 BOXA *boxad;
118 
119  PROCNAME("boxaSelectRange");
120 
121  if (!boxas)
122  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
123  if (copyflag != L_COPY && copyflag != L_CLONE)
124  return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL);
125  if ((n = boxaGetCount(boxas)) == 0) {
126  L_WARNING("boxas is empty\n", procName);
127  return boxaCopy(boxas, copyflag);
128  }
129  first = L_MAX(0, first);
130  if (last <= 0) last = n - 1;
131  if (first >= n)
132  return (BOXA *)ERROR_PTR("invalid first", procName, NULL);
133  if (first > last)
134  return (BOXA *)ERROR_PTR("first > last", procName, NULL);
135 
136  nbox = last - first + 1;
137  boxad = boxaCreate(nbox);
138  for (i = first; i <= last; i++) {
139  box = boxaGetBox(boxas, i, copyflag);
140  boxaAddBox(boxad, box, L_INSERT);
141  }
142  return boxad;
143 }
144 
145 
162 BOXAA *
164  l_int32 first,
165  l_int32 last,
166  l_int32 copyflag)
167 {
168 l_int32 n, nboxa, i;
169 BOXA *boxa;
170 BOXAA *baad;
171 
172  PROCNAME("boxaaSelectRange");
173 
174  if (!baas)
175  return (BOXAA *)ERROR_PTR("baas not defined", procName, NULL);
176  if (copyflag != L_COPY && copyflag != L_CLONE)
177  return (BOXAA *)ERROR_PTR("invalid copyflag", procName, NULL);
178  if ((n = boxaaGetCount(baas)) == 0)
179  return (BOXAA *)ERROR_PTR("empty baas", procName, NULL);
180  first = L_MAX(0, first);
181  if (last <= 0) last = n - 1;
182  if (first >= n)
183  return (BOXAA *)ERROR_PTR("invalid first", procName, NULL);
184  if (first > last)
185  return (BOXAA *)ERROR_PTR("first > last", procName, NULL);
186 
187  nboxa = last - first + 1;
188  baad = boxaaCreate(nboxa);
189  for (i = first; i <= last; i++) {
190  boxa = boxaaGetBoxa(baas, i, copyflag);
191  boxaaAddBoxa(baad, boxa, L_INSERT);
192  }
193  return baad;
194 }
195 
196 
197 /*---------------------------------------------------------------------*
198  * Boxa size selection *
199  *---------------------------------------------------------------------*/
225 BOXA *
227  l_int32 width,
228  l_int32 height,
229  l_int32 type,
230  l_int32 relation,
231  l_int32 *pchanged)
232 {
233 BOXA *boxad;
234 NUMA *na;
235 
236  PROCNAME("boxaSelectBySize");
237 
238  if (pchanged) *pchanged = FALSE;
239  if (!boxas)
240  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
241  if (boxaGetCount(boxas) == 0) {
242  L_WARNING("boxas is empty\n", procName);
243  return boxaCopy(boxas, L_COPY);
244  }
245  if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
246  type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
247  return (BOXA *)ERROR_PTR("invalid type", procName, NULL);
248  if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
249  relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
250  return (BOXA *)ERROR_PTR("invalid relation", procName, NULL);
251 
252  /* Compute the indicator array for saving components */
253  if ((na =
254  boxaMakeSizeIndicator(boxas, width, height, type, relation)) == NULL)
255  return (BOXA *)ERROR_PTR("na not made", procName, NULL);
256 
257  /* Filter to get output */
258  boxad = boxaSelectWithIndicator(boxas, na, pchanged);
259 
260  numaDestroy(&na);
261  return boxad;
262 }
263 
264 
288 NUMA *
290  l_int32 width,
291  l_int32 height,
292  l_int32 type,
293  l_int32 relation)
294 {
295 l_int32 i, n, w, h, ival;
296 NUMA *na;
297 
298  PROCNAME("boxaMakeSizeIndicator");
299 
300  if (!boxa)
301  return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL);
302  if ((n = boxaGetCount(boxa)) == 0)
303  return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL);
304  if (type != L_SELECT_WIDTH && type != L_SELECT_HEIGHT &&
305  type != L_SELECT_IF_EITHER && type != L_SELECT_IF_BOTH)
306  return (NUMA *)ERROR_PTR("invalid type", procName, NULL);
307  if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
308  relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
309  return (NUMA *)ERROR_PTR("invalid relation", procName, NULL);
310 
311  na = numaCreate(n);
312  for (i = 0; i < n; i++) {
313  ival = 0;
314  boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
315  switch (type)
316  {
317  case L_SELECT_WIDTH:
318  if ((relation == L_SELECT_IF_LT && w < width) ||
319  (relation == L_SELECT_IF_GT && w > width) ||
320  (relation == L_SELECT_IF_LTE && w <= width) ||
321  (relation == L_SELECT_IF_GTE && w >= width))
322  ival = 1;
323  break;
324  case L_SELECT_HEIGHT:
325  if ((relation == L_SELECT_IF_LT && h < height) ||
326  (relation == L_SELECT_IF_GT && h > height) ||
327  (relation == L_SELECT_IF_LTE && h <= height) ||
328  (relation == L_SELECT_IF_GTE && h >= height))
329  ival = 1;
330  break;
331  case L_SELECT_IF_EITHER:
332  if (((relation == L_SELECT_IF_LT) && (w < width || h < height)) ||
333  ((relation == L_SELECT_IF_GT) && (w > width || h > height)) ||
334  ((relation == L_SELECT_IF_LTE) && (w <= width || h <= height)) ||
335  ((relation == L_SELECT_IF_GTE) && (w >= width || h >= height)))
336  ival = 1;
337  break;
338  case L_SELECT_IF_BOTH:
339  if (((relation == L_SELECT_IF_LT) && (w < width && h < height)) ||
340  ((relation == L_SELECT_IF_GT) && (w > width && h > height)) ||
341  ((relation == L_SELECT_IF_LTE) && (w <= width && h <= height)) ||
342  ((relation == L_SELECT_IF_GTE) && (w >= width && h >= height)))
343  ival = 1;
344  break;
345  default:
346  L_WARNING("can't get here!\n", procName);
347  break;
348  }
349  numaAddNumber(na, ival);
350  }
351 
352  return na;
353 }
354 
355 
375 BOXA *
377  l_int32 area,
378  l_int32 relation,
379  l_int32 *pchanged)
380 {
381 BOXA *boxad;
382 NUMA *na;
383 
384  PROCNAME("boxaSelectByArea");
385 
386  if (pchanged) *pchanged = FALSE;
387  if (!boxas)
388  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
389  if (boxaGetCount(boxas) == 0) {
390  L_WARNING("boxas is empty\n", procName);
391  return boxaCopy(boxas, L_COPY);
392  }
393  if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
394  relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
395  return (BOXA *)ERROR_PTR("invalid relation", procName, NULL);
396 
397  /* Compute the indicator array for saving components */
398  na = boxaMakeAreaIndicator(boxas, area, relation);
399 
400  /* Filter to get output */
401  boxad = boxaSelectWithIndicator(boxas, na, pchanged);
402 
403  numaDestroy(&na);
404  return boxad;
405 }
406 
407 
425 NUMA *
427  l_int32 area,
428  l_int32 relation)
429 {
430 l_int32 i, n, w, h, ival;
431 NUMA *na;
432 
433  PROCNAME("boxaMakeAreaIndicator");
434 
435  if (!boxa)
436  return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL);
437  if ((n = boxaGetCount(boxa)) == 0)
438  return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL);
439  if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
440  relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
441  return (NUMA *)ERROR_PTR("invalid relation", procName, NULL);
442 
443  na = numaCreate(n);
444  for (i = 0; i < n; i++) {
445  ival = 0;
446  boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
447 
448  if ((relation == L_SELECT_IF_LT && w * h < area) ||
449  (relation == L_SELECT_IF_GT && w * h > area) ||
450  (relation == L_SELECT_IF_LTE && w * h <= area) ||
451  (relation == L_SELECT_IF_GTE && w * h >= area))
452  ival = 1;
453  numaAddNumber(na, ival);
454  }
455 
456  return na;
457 }
458 
459 
479 BOXA *
481  l_float32 ratio,
482  l_int32 relation,
483  l_int32 *pchanged)
484 {
485 BOXA *boxad;
486 NUMA *na;
487 
488  PROCNAME("boxaSelectByWHRatio");
489 
490  if (pchanged) *pchanged = FALSE;
491  if (!boxas)
492  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
493  if (boxaGetCount(boxas) == 0) {
494  L_WARNING("boxas is empty\n", procName);
495  return boxaCopy(boxas, L_COPY);
496  }
497  if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
498  relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
499  return (BOXA *)ERROR_PTR("invalid relation", procName, NULL);
500 
501  /* Compute the indicator array for saving components */
502  na = boxaMakeWHRatioIndicator(boxas, ratio, relation);
503 
504  /* Filter to get output */
505  boxad = boxaSelectWithIndicator(boxas, na, pchanged);
506 
507  numaDestroy(&na);
508  return boxad;
509 }
510 
511 
529 NUMA *
531  l_float32 ratio,
532  l_int32 relation)
533 {
534 l_int32 i, n, w, h, ival;
535 l_float32 whratio;
536 NUMA *na;
537 
538  PROCNAME("boxaMakeWHRatioIndicator");
539 
540  if (!boxa)
541  return (NUMA *)ERROR_PTR("boxa not defined", procName, NULL);
542  if ((n = boxaGetCount(boxa)) == 0)
543  return (NUMA *)ERROR_PTR("boxa is empty", procName, NULL);
544  if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
545  relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
546  return (NUMA *)ERROR_PTR("invalid relation", procName, NULL);
547 
548  na = numaCreate(n);
549  for (i = 0; i < n; i++) {
550  ival = 0;
551  boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
552  whratio = (l_float32)w / (l_float32)h;
553 
554  if ((relation == L_SELECT_IF_LT && whratio < ratio) ||
555  (relation == L_SELECT_IF_GT && whratio > ratio) ||
556  (relation == L_SELECT_IF_LTE && whratio <= ratio) ||
557  (relation == L_SELECT_IF_GTE && whratio >= ratio))
558  ival = 1;
559  numaAddNumber(na, ival);
560  }
561 
562  return na;
563 }
564 
565 
581 BOXA *
583  NUMA *na,
584  l_int32 *pchanged)
585 {
586 l_int32 i, n, ival, nsave;
587 BOX *box;
588 BOXA *boxad;
589 
590  PROCNAME("boxaSelectWithIndicator");
591 
592  if (pchanged) *pchanged = FALSE;
593  if (!boxas)
594  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
595  if (!na)
596  return (BOXA *)ERROR_PTR("na not defined", procName, NULL);
597 
598  nsave = 0;
599  n = numaGetCount(na);
600  for (i = 0; i < n; i++) {
601  numaGetIValue(na, i, &ival);
602  if (ival == 1) nsave++;
603  }
604 
605  if (nsave == n) {
606  if (pchanged) *pchanged = FALSE;
607  return boxaCopy(boxas, L_COPY);
608  }
609  if (pchanged) *pchanged = TRUE;
610  boxad = boxaCreate(nsave);
611  for (i = 0; i < n; i++) {
612  numaGetIValue(na, i, &ival);
613  if (ival == 0) continue;
614  box = boxaGetBox(boxas, i, L_COPY);
615  boxaAddBox(boxad, box, L_INSERT);
616  }
617 
618  return boxad;
619 }
620 
621 
622 /*---------------------------------------------------------------------*
623  * Boxa Permutation *
624  *---------------------------------------------------------------------*/
639 BOXA *
641 {
642 l_int32 n;
643 NUMA *na;
644 BOXA *boxad;
645 
646  PROCNAME("boxaPermutePseudorandom");
647 
648  if (!boxas)
649  return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL);
650 
651  n = boxaGetCount(boxas);
652  na = numaPseudorandomSequence(n, 0);
653  boxad = boxaSortByIndex(boxas, na);
654  numaDestroy(&na);
655  return boxad;
656 }
657 
658 
680 BOXA *
682  BOXA *boxas)
683 {
684 l_int32 i, n, index;
685 
686  PROCNAME("boxaPermuteRandom");
687 
688  if (!boxas)
689  return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL);
690  if (boxad && (boxad != boxas))
691  return (BOXA *)ERROR_PTR("boxad defined but in-place", procName, NULL);
692 
693  if (!boxad)
694  boxad = boxaCopy(boxas, L_COPY);
695  if ((n = boxaGetCount(boxad)) == 0)
696  return boxad;
697  index = (l_uint32)rand() % n;
698  index = L_MAX(1, index);
699  boxaSwapBoxes(boxad, 0, index);
700  for (i = 1; i < n; i++) {
701  index = (l_uint32)rand() % n;
702  if (index == i) index--;
703  boxaSwapBoxes(boxad, i, index);
704  }
705 
706  return boxad;
707 }
708 
709 
717 l_int32
719  l_int32 i,
720  l_int32 j)
721 {
722 l_int32 n;
723 BOX *box;
724 
725  PROCNAME("boxaSwapBoxes");
726 
727  if (!boxa)
728  return ERROR_INT("boxa not defined", procName, 1);
729  n = boxaGetCount(boxa);
730  if (i < 0 || i >= n)
731  return ERROR_INT("i invalid", procName, 1);
732  if (j < 0 || j >= n)
733  return ERROR_INT("j invalid", procName, 1);
734  if (i == j)
735  return ERROR_INT("i == j", procName, 1);
736 
737  box = boxa->box[i];
738  boxa->box[i] = boxa->box[j];
739  boxa->box[j] = box;
740  return 0;
741 }
742 
743 
744 /*---------------------------------------------------------------------*
745  * Boxa and Box Conversions *
746  *---------------------------------------------------------------------*/
761 PTA *
763  l_int32 ncorners)
764 {
765 l_int32 i, n;
766 BOX *box;
767 PTA *pta, *pta1;
768 
769  PROCNAME("boxaConvertToPta");
770 
771  if (!boxa)
772  return (PTA *)ERROR_PTR("boxa not defined", procName, NULL);
773  if (ncorners != 2 && ncorners != 4)
774  return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL);
775 
776  n = boxaGetCount(boxa);
777  if ((pta = ptaCreate(n)) == NULL)
778  return (PTA *)ERROR_PTR("pta not made", procName, NULL);
779  for (i = 0; i < n; i++) {
780  box = boxaGetBox(boxa, i, L_COPY);
781  pta1 = boxConvertToPta(box, ncorners);
782  ptaJoin(pta, pta1, 0, -1);
783  boxDestroy(&box);
784  ptaDestroy(&pta1);
785  }
786 
787  return pta;
788 }
789 
790 
806 BOXA *
808  l_int32 ncorners)
809 {
810 l_int32 i, n, nbox, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax;
811 BOX *box;
812 BOXA *boxa;
813 
814  PROCNAME("ptaConvertToBoxa");
815 
816  if (!pta)
817  return (BOXA *)ERROR_PTR("pta not defined", procName, NULL);
818  if (ncorners != 2 && ncorners != 4)
819  return (BOXA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL);
820  n = ptaGetCount(pta);
821  if (n % ncorners != 0)
822  return (BOXA *)ERROR_PTR("size % ncorners != 0", procName, NULL);
823  nbox = n / ncorners;
824  if ((boxa = boxaCreate(nbox)) == NULL)
825  return (BOXA *)ERROR_PTR("boxa not made", procName, NULL);
826  for (i = 0; i < n; i += ncorners) {
827  ptaGetIPt(pta, i, &x1, &y1);
828  ptaGetIPt(pta, i + 1, &x2, &y2);
829  if (ncorners == 2) {
830  box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
831  boxaAddBox(boxa, box, L_INSERT);
832  continue;
833  }
834  ptaGetIPt(pta, i + 2, &x3, &y3);
835  ptaGetIPt(pta, i + 3, &x4, &y4);
836  x = L_MIN(x1, x3);
837  y = L_MIN(y1, y2);
838  xmax = L_MAX(x2, x4);
839  ymax = L_MAX(y3, y4);
840  box = boxCreate(x, y, xmax - x + 1, ymax - y + 1);
841  boxaAddBox(boxa, box, L_INSERT);
842  }
843 
844  return boxa;
845 }
846 
847 
861 PTA *
863  l_int32 ncorners)
864 {
865 l_int32 x, y, w, h;
866 PTA *pta;
867 
868  PROCNAME("boxConvertToPta");
869 
870  if (!box)
871  return (PTA *)ERROR_PTR("box not defined", procName, NULL);
872  if (ncorners != 2 && ncorners != 4)
873  return (PTA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL);
874 
875  if ((pta = ptaCreate(ncorners)) == NULL)
876  return (PTA *)ERROR_PTR("pta not made", procName, NULL);
877  boxGetGeometry(box, &x, &y, &w, &h);
878  ptaAddPt(pta, x, y);
879  if (ncorners == 2) {
880  ptaAddPt(pta, x + w - 1, y + h - 1);
881  } else {
882  ptaAddPt(pta, x + w - 1, y);
883  ptaAddPt(pta, x, y + h - 1);
884  ptaAddPt(pta, x + w - 1, y + h - 1);
885  }
886 
887  return pta;
888 }
889 
890 
903 BOX *
905 {
906 l_int32 n, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax;
907 
908  PROCNAME("ptaConvertToBox");
909 
910  if (!pta)
911  return (BOX *)ERROR_PTR("pta not defined", procName, NULL);
912  n = ptaGetCount(pta);
913  ptaGetIPt(pta, 0, &x1, &y1);
914  ptaGetIPt(pta, 1, &x2, &y2);
915  if (n == 2)
916  return boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
917 
918  /* 4 corners */
919  ptaGetIPt(pta, 2, &x3, &y3);
920  ptaGetIPt(pta, 3, &x4, &y4);
921  x = L_MIN(x1, x3);
922  y = L_MIN(y1, y2);
923  xmax = L_MAX(x2, x4);
924  ymax = L_MAX(y3, y4);
925  return boxCreate(x, y, xmax - x + 1, ymax - y + 1);
926 }
927 
928 
929 /*---------------------------------------------------------------------*
930  * Boxa sequence fitting *
931  *---------------------------------------------------------------------*/
965 BOXA *
967  l_float32 factor,
968  l_int32 subflag,
969  l_int32 maxdiff,
970  l_int32 extrapixels,
971  l_int32 debug)
972 {
973 l_int32 n;
974 BOXA *boxae, *boxao, *boxalfe, *boxalfo, *boxame, *boxamo, *boxad;
975 
976  PROCNAME("boxaSmoothSequenceLS");
977 
978  if (!boxas)
979  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
980  if (factor <= 0.0) {
981  L_WARNING("factor must be > 0.0; returning copy\n", procName);
982  return boxaCopy(boxas, L_COPY);
983  }
984  if (maxdiff < 0) {
985  L_WARNING("maxdiff must be >= 0; returning copy\n", procName);
986  return boxaCopy(boxas, L_COPY);
987  }
988  if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE &&
989  subflag != L_SUB_ON_LOC_DIFF && subflag != L_SUB_ON_SIZE_DIFF &&
990  subflag != L_USE_CAPPED_MIN && subflag != L_USE_CAPPED_MAX) {
991  L_WARNING("invalid subflag; returning copy\n", procName);
992  return boxaCopy(boxas, L_COPY);
993  }
994  if ((n = boxaGetCount(boxas)) < 4) {
995  L_WARNING("need at least 4 boxes; returning copy\n", procName);
996  return boxaCopy(boxas, L_COPY);
997  }
998 
999  boxaSplitEvenOdd(boxas, 1, &boxae, &boxao);
1000  if (debug) {
1001  lept_mkdir("lept/smooth");
1002  boxaWrite("/tmp/lept/smooth/boxae.ba", boxae);
1003  boxaWrite("/tmp/lept/smooth/boxao.ba", boxao);
1004  }
1005 
1006  boxalfe = boxaLinearFit(boxae, factor, debug);
1007  boxalfo = boxaLinearFit(boxao, factor, debug);
1008  if (debug) {
1009  boxaWrite("/tmp/lept/smooth/boxalfe.ba", boxalfe);
1010  boxaWrite("/tmp/lept/smooth/boxalfo.ba", boxalfo);
1011  }
1012 
1013  boxame = boxaModifyWithBoxa(boxae, boxalfe, subflag, maxdiff, extrapixels);
1014  boxamo = boxaModifyWithBoxa(boxao, boxalfo, subflag, maxdiff, extrapixels);
1015  if (debug) {
1016  boxaWrite("/tmp/lept/smooth/boxame.ba", boxame);
1017  boxaWrite("/tmp/lept/smooth/boxamo.ba", boxamo);
1018  }
1019 
1020  boxad = boxaMergeEvenOdd(boxame, boxamo, 1);
1021  boxaDestroy(&boxae);
1022  boxaDestroy(&boxao);
1023  boxaDestroy(&boxalfe);
1024  boxaDestroy(&boxalfo);
1025  boxaDestroy(&boxame);
1026  boxaDestroy(&boxamo);
1027  return boxad;
1028 }
1029 
1030 
1071 BOXA *
1073  l_int32 halfwin,
1074  l_int32 subflag,
1075  l_int32 maxdiff,
1076  l_int32 extrapixels,
1077  l_int32 debug)
1078 {
1079 l_int32 n;
1080 BOXA *boxae, *boxao, *boxamede, *boxamedo, *boxame, *boxamo, *boxad;
1081 
1082  PROCNAME("boxaSmoothSequenceMedian");
1083 
1084  if (!boxas)
1085  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
1086  if (halfwin <= 0) {
1087  L_WARNING("halfwin must be > 0; returning copy\n", procName);
1088  return boxaCopy(boxas, L_COPY);
1089  }
1090  if (maxdiff < 0) {
1091  L_WARNING("maxdiff must be >= 0; returning copy\n", procName);
1092  return boxaCopy(boxas, L_COPY);
1093  }
1094  if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE &&
1095  subflag != L_SUB_ON_LOC_DIFF && subflag != L_SUB_ON_SIZE_DIFF &&
1096  subflag != L_USE_CAPPED_MIN && subflag != L_USE_CAPPED_MAX) {
1097  L_WARNING("invalid subflag; returning copy\n", procName);
1098  return boxaCopy(boxas, L_COPY);
1099  }
1100  if ((n = boxaGetCount(boxas)) < 6) {
1101  L_WARNING("need at least 6 boxes; returning copy\n", procName);
1102  return boxaCopy(boxas, L_COPY);
1103  }
1104 
1105  boxaSplitEvenOdd(boxas, 0, &boxae, &boxao);
1106  if (debug) {
1107  lept_mkdir("lept/smooth");
1108  boxaWrite("/tmp/lept/smooth/boxae.ba", boxae);
1109  boxaWrite("/tmp/lept/smooth/boxao.ba", boxao);
1110  }
1111 
1112  boxamede = boxaWindowedMedian(boxae, halfwin, debug);
1113  boxamedo = boxaWindowedMedian(boxao, halfwin, debug);
1114  if (debug) {
1115  boxaWrite("/tmp/lept/smooth/boxamede.ba", boxamede);
1116  boxaWrite("/tmp/lept/smooth/boxamedo.ba", boxamedo);
1117  }
1118 
1119  boxame = boxaModifyWithBoxa(boxae, boxamede, subflag, maxdiff, extrapixels);
1120  boxamo = boxaModifyWithBoxa(boxao, boxamedo, subflag, maxdiff, extrapixels);
1121  if (debug) {
1122  boxaWrite("/tmp/lept/smooth/boxame.ba", boxame);
1123  boxaWrite("/tmp/lept/smooth/boxamo.ba", boxamo);
1124  }
1125 
1126  boxad = boxaMergeEvenOdd(boxame, boxamo, 0);
1127  if (debug) {
1128  boxaPlotSides(boxas, NULL, NULL, NULL, NULL, NULL, NULL);
1129  boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, NULL);
1130  boxaPlotSizes(boxas, NULL, NULL, NULL, NULL);
1131  boxaPlotSizes(boxad, NULL, NULL, NULL, NULL);
1132  }
1133 
1134  boxaDestroy(&boxae);
1135  boxaDestroy(&boxao);
1136  boxaDestroy(&boxamede);
1137  boxaDestroy(&boxamedo);
1138  boxaDestroy(&boxame);
1139  boxaDestroy(&boxamo);
1140  return boxad;
1141 }
1142 
1143 
1177 BOXA *
1179  l_float32 factor,
1180  l_int32 debug)
1181 {
1182 l_int32 n, i, w, h, lval, tval, rval, bval, rejectlr, rejecttb;
1183 l_float32 al, bl, at, bt, ar, br, ab, bb; /* LSF coefficients */
1184 l_float32 medw, medh, medvarw, medvarh;
1185 BOX *box, *boxempty;
1186 BOXA *boxalr, *boxatb, *boxad;
1187 NUMA *naw, *nah;
1188 PTA *ptal, *ptat, *ptar, *ptab;
1189 
1190  PROCNAME("boxaLinearFit");
1191 
1192  if (!boxas)
1193  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
1194  if ((n = boxaGetCount(boxas)) < 2)
1195  return (BOXA *)ERROR_PTR("need at least 2 boxes", procName, NULL);
1196 
1197  /* Remove outliers based on width and height.
1198  * First find the median width and the median deviation from
1199  * the median width. Ditto for the height. */
1200  boxaExtractAsNuma(boxas, NULL, NULL, NULL, NULL, &naw, &nah, 0);
1201  numaGetMedianVariation(naw, &medw, &medvarw);
1202  numaGetMedianVariation(nah, &medh, &medvarh);
1203  numaDestroy(&naw);
1204  numaDestroy(&nah);
1205 
1206  if (debug) {
1207  fprintf(stderr, "medw = %7.3f, medvarw = %7.3f\n", medw, medvarw);
1208  fprintf(stderr, "medh = %7.3f, medvarh = %7.3f\n", medh, medvarh);
1209  }
1210 
1211  /* To fit the left and right sides, only use boxes whose
1212  * width is within (factor * medvarw) of the median width.
1213  * Ditto for the top and bottom sides. Add empty boxes
1214  * in as placeholders so that the index remains the same
1215  * as in boxas. */
1216  boxalr = boxaCreate(n);
1217  boxatb = boxaCreate(n);
1218  boxempty = boxCreate(0, 0, 0, 0); /* placeholders */
1219  rejectlr = rejecttb = 0;
1220  for (i = 0; i < n; i++) {
1221  if ((box = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) {
1222  boxaAddBox(boxalr, boxempty, L_COPY);
1223  boxaAddBox(boxatb, boxempty, L_COPY);
1224  continue;
1225  }
1226  boxGetGeometry(box, NULL, NULL, &w, &h);
1227  if (L_ABS(w - medw) <= factor * medvarw) {
1228  boxaAddBox(boxalr, box, L_COPY);
1229  } else {
1230  rejectlr++;
1231  boxaAddBox(boxalr, boxempty, L_COPY);
1232  }
1233  if (L_ABS(h - medh) <= factor * medvarh) {
1234  boxaAddBox(boxatb, box, L_COPY);
1235  } else {
1236  rejecttb++;
1237  boxaAddBox(boxatb, boxempty, L_COPY);
1238  }
1239  boxDestroy(&box);
1240  }
1241  boxDestroy(&boxempty);
1242  if (boxaGetCount(boxalr) < 2 || boxaGetCount(boxatb) < 2) {
1243  boxaDestroy(&boxalr);
1244  boxaDestroy(&boxatb);
1245  return (BOXA *)ERROR_PTR("need at least 2 valid boxes", procName, NULL);
1246  }
1247 
1248  if (debug) {
1249  L_INFO("# lr reject = %d, # tb reject = %d\n", procName,
1250  rejectlr, rejecttb);
1251  lept_mkdir("linfit");
1252  boxaWrite("/tmp/linfit/boxalr.ba", boxalr);
1253  boxaWrite("/tmp/linfit/boxatb.ba", boxatb);
1254  }
1255 
1256  /* Extract the valid left and right box sides, along with the box
1257  * index, from boxalr. This only extracts pts corresponding to
1258  * valid boxes. Ditto: top and bottom sides from boxatb. */
1259  boxaExtractAsPta(boxalr, &ptal, NULL, &ptar, NULL, NULL, NULL, 0);
1260  boxaExtractAsPta(boxatb, NULL, &ptat, NULL, &ptab, NULL, NULL, 0);
1261  boxaDestroy(&boxalr);
1262  boxaDestroy(&boxatb);
1263 
1264  if (debug) {
1265  ptaWrite("/tmp/linfit/ptal.pta", ptal, 1);
1266  ptaWrite("/tmp/linfit/ptar.pta", ptar, 1);
1267  ptaWrite("/tmp/linfit/ptat.pta", ptat, 1);
1268  ptaWrite("/tmp/linfit/ptab.pta", ptab, 1);
1269  }
1270 
1271  /* Do a linear LSF fit to the points that are width and height
1272  * validated. Because we've eliminated the outliers, there is no
1273  * need to use ptaNoisyLinearLSF(ptal, factor, NULL, &al, &bl, ...) */
1274  ptaGetLinearLSF(ptal, &al, &bl, NULL);
1275  ptaGetLinearLSF(ptat, &at, &bt, NULL);
1276  ptaGetLinearLSF(ptar, &ar, &br, NULL);
1277  ptaGetLinearLSF(ptab, &ab, &bb, NULL);
1278 
1279  /* Return the LSF smoothed values, interleaved with invalid
1280  * boxes when the corresponding box in boxas is invalid. */
1281  boxad = boxaCreate(n);
1282  boxempty = boxCreate(0, 0, 0, 0); /* use for placeholders */
1283  for (i = 0; i < n; i++) {
1284  lval = (l_int32)(al * i + bl + 0.5);
1285  tval = (l_int32)(at * i + bt + 0.5);
1286  rval = (l_int32)(ar * i + br + 0.5);
1287  bval = (l_int32)(ab * i + bb + 0.5);
1288  if ((box = boxaGetValidBox(boxas, i, L_CLONE)) == NULL) {
1289  boxaAddBox(boxad, boxempty, L_COPY);
1290  } else {
1291  boxDestroy(&box);
1292  box = boxCreate(lval, tval, rval - lval + 1, bval - tval + 1);
1293  boxaAddBox(boxad, box, L_INSERT);
1294  }
1295  }
1296  boxDestroy(&boxempty);
1297 
1298  if (debug) {
1299  boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, NULL);
1300  boxaPlotSizes(boxad, NULL, NULL, NULL, NULL);
1301  }
1302 
1303  ptaDestroy(&ptal);
1304  ptaDestroy(&ptat);
1305  ptaDestroy(&ptar);
1306  ptaDestroy(&ptab);
1307  return boxad;
1308 }
1309 
1310 
1329 BOXA *
1331  l_int32 halfwin,
1332  l_int32 debug)
1333 {
1334 l_int32 n, i, left, top, right, bot;
1335 BOX *box;
1336 BOXA *boxaf, *boxad;
1337 NUMA *nal, *nat, *nar, *nab, *naml, *namt, *namr, *namb;
1338 
1339  PROCNAME("boxaWindowedMedian");
1340 
1341  if (!boxas)
1342  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
1343  if ((n = boxaGetCount(boxas)) < 3) {
1344  L_WARNING("less than 3 boxes; returning a copy\n", procName);
1345  return boxaCopy(boxas, L_COPY);
1346  }
1347  if (halfwin <= 0) {
1348  L_WARNING("halfwin must be > 0; returning copy\n", procName);
1349  return boxaCopy(boxas, L_COPY);
1350  }
1351 
1352  /* Fill invalid boxes in the input sequence */
1353  if ((boxaf = boxaFillSequence(boxas, L_USE_ALL_BOXES, debug)) == NULL)
1354  return (BOXA *)ERROR_PTR("filled boxa not made", procName, NULL);
1355 
1356  /* Get the windowed median output from each of the sides */
1357  boxaExtractAsNuma(boxaf, &nal, &nat, &nar, &nab, NULL, NULL, 0);
1358  naml = numaWindowedMedian(nal, halfwin);
1359  namt = numaWindowedMedian(nat, halfwin);
1360  namr = numaWindowedMedian(nar, halfwin);
1361  namb = numaWindowedMedian(nab, halfwin);
1362 
1363  n = boxaGetCount(boxaf);
1364  boxad = boxaCreate(n);
1365  for (i = 0; i < n; i++) {
1366  numaGetIValue(naml, i, &left);
1367  numaGetIValue(namt, i, &top);
1368  numaGetIValue(namr, i, &right);
1369  numaGetIValue(namb, i, &bot);
1370  box = boxCreate(left, top, right - left + 1, bot - top + 1);
1371  boxaAddBox(boxad, box, L_INSERT);
1372  }
1373 
1374  if (debug) {
1375  boxaPlotSides(boxaf, NULL, NULL, NULL, NULL, NULL, NULL);
1376  boxaPlotSides(boxad, NULL, NULL, NULL, NULL, NULL, NULL);
1377  boxaPlotSizes(boxaf, NULL, NULL, NULL, NULL);
1378  boxaPlotSizes(boxad, NULL, NULL, NULL, NULL);
1379  }
1380 
1381  boxaDestroy(&boxaf);
1382  numaDestroy(&nal);
1383  numaDestroy(&nat);
1384  numaDestroy(&nar);
1385  numaDestroy(&nab);
1386  numaDestroy(&naml);
1387  numaDestroy(&namt);
1388  numaDestroy(&namr);
1389  numaDestroy(&namb);
1390  return boxad;
1391 }
1392 
1393 
1467 BOXA *
1469  BOXA *boxam,
1470  l_int32 subflag,
1471  l_int32 maxdiff,
1472  l_int32 extrapixels)
1473 {
1474 l_int32 n, i, ls, ts, rs, bs, ws, hs, lm, tm, rm, bm, wm, hm, ld, td, rd, bd;
1475 BOX *boxs, *boxm, *boxd, *boxempty;
1476 BOXA *boxad;
1477 
1478  PROCNAME("boxaModifyWithBoxa");
1479 
1480  if (!boxas)
1481  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
1482  if (!boxam) {
1483  L_WARNING("boxam not defined; returning copy", procName);
1484  return boxaCopy(boxas, L_COPY);
1485  }
1486  if (subflag != L_USE_MINSIZE && subflag != L_USE_MAXSIZE &&
1487  subflag != L_SUB_ON_LOC_DIFF && subflag != L_SUB_ON_SIZE_DIFF &&
1488  subflag != L_USE_CAPPED_MIN && subflag != L_USE_CAPPED_MAX) {
1489  L_WARNING("invalid subflag; returning copy", procName);
1490  return boxaCopy(boxas, L_COPY);
1491  }
1492  n = boxaGetCount(boxas);
1493  if (n != boxaGetCount(boxam)) {
1494  L_WARNING("boxas and boxam sizes differ; returning copy", procName);
1495  return boxaCopy(boxas, L_COPY);
1496  }
1497 
1498  boxad = boxaCreate(n);
1499  boxempty = boxCreate(0, 0, 0, 0); /* placeholders */
1500  for (i = 0; i < n; i++) {
1501  boxs = boxaGetValidBox(boxas, i, L_CLONE);
1502  boxm = boxaGetValidBox(boxam, i, L_CLONE);
1503  if (!boxs || !boxm) {
1504  boxaAddBox(boxad, boxempty, L_COPY);
1505  } else {
1506  boxGetGeometry(boxs, &ls, &ts, &ws, &hs);
1507  boxGetGeometry(boxm, &lm, &tm, &wm, &hm);
1508  rs = ls + ws - 1;
1509  bs = ts + hs - 1;
1510  rm = lm + wm - 1;
1511  bm = tm + hm - 1;
1512  if (subflag == L_USE_MINSIZE) {
1513  ld = L_MAX(ls, lm);
1514  rd = L_MIN(rs, rm);
1515  td = L_MAX(ts, tm);
1516  bd = L_MIN(bs, bm);
1517  } else if (subflag == L_USE_MAXSIZE) {
1518  ld = L_MIN(ls, lm);
1519  rd = L_MAX(rs, rm);
1520  td = L_MIN(ts, tm);
1521  bd = L_MAX(bs, bm);
1522  } else if (subflag == L_SUB_ON_LOC_DIFF) {
1523  ld = (L_ABS(lm - ls) <= maxdiff) ? ls : lm - extrapixels;
1524  td = (L_ABS(tm - ts) <= maxdiff) ? ts : tm - extrapixels;
1525  rd = (L_ABS(rm - rs) <= maxdiff) ? rs : rm + extrapixels;
1526  bd = (L_ABS(bm - bs) <= maxdiff) ? bs : bm + extrapixels;
1527  } else if (subflag == L_SUB_ON_SIZE_DIFF) {
1528  ld = (L_ABS(wm - ws) <= maxdiff) ? ls : lm - extrapixels;
1529  td = (L_ABS(hm - hs) <= maxdiff) ? ts : tm - extrapixels;
1530  rd = (L_ABS(wm - ws) <= maxdiff) ? rs : rm + extrapixels;
1531  bd = (L_ABS(hm - hs) <= maxdiff) ? bs : bm + extrapixels;
1532  } else if (subflag == L_USE_CAPPED_MIN) {
1533  ld = L_MAX(lm, L_MIN(ls, lm + maxdiff));
1534  td = L_MAX(tm, L_MIN(ts, tm + maxdiff));
1535  rd = L_MIN(rm, L_MAX(rs, rm - maxdiff));
1536  bd = L_MIN(bm, L_MAX(bs, bm - maxdiff));
1537  } else { /* subflag == L_USE_CAPPED_MAX */
1538  ld = L_MIN(lm, L_MAX(ls, lm - maxdiff));
1539  td = L_MIN(tm, L_MAX(ts, tm - maxdiff));
1540  rd = L_MAX(rm, L_MIN(rs, rm + maxdiff));
1541  bd = L_MAX(bm, L_MIN(bs, bm + maxdiff));
1542  }
1543  boxd = boxCreate(ld, td, rd - ld + 1, bd - td + 1);
1544  boxaAddBox(boxad, boxd, L_INSERT);
1545  }
1546  boxDestroy(&boxs);
1547  boxDestroy(&boxm);
1548  }
1549  boxDestroy(&boxempty);
1550 
1551  return boxad;
1552 }
1553 
1554 
1583 BOXA *
1585  l_int32 width,
1586  l_int32 widthflag,
1587  l_int32 height,
1588  l_int32 heightflag)
1589 {
1590 l_int32 n, i, x, y, w, h, invalid;
1591 l_int32 delw, delh, del_left, del_right, del_top, del_bot;
1592 BOX *medbox, *boxs, *boxd;
1593 BOXA *boxad;
1594 
1595  PROCNAME("boxaConstrainSize");
1596 
1597  if (!boxas)
1598  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
1599 
1600  /* Need median values if requested or if there are invalid boxes */
1601  invalid = boxaGetCount(boxas) - boxaGetValidCount(boxas);
1602  medbox = NULL;
1603  if (width == 0 || height == 0 || invalid > 0) {
1604  if (boxaGetMedianVals(boxas, &x, &y, &w, &h)) {
1605  L_ERROR("median vals not returned", procName);
1606  return boxaCopy(boxas, L_COPY);
1607  }
1608  medbox = boxCreate(x, y, w, h);
1609  if (width == 0) width = w;
1610  if (height == 0) height = h;
1611  }
1612 
1613  n = boxaGetCount(boxas);
1614  boxad = boxaCreate(n);
1615  for (i = 0; i < n; i++) {
1616  if ((boxs = boxaGetValidBox(boxas, i, L_COPY)) == NULL)
1617  boxs = boxCopy(medbox);
1618  boxGetGeometry(boxs, NULL, NULL, &w, &h);
1619  delw = width - w;
1620  delh = height - h;
1621  del_left = del_right = del_top = del_bot = 0;
1622  if (widthflag == L_ADJUST_LEFT) {
1623  del_left = -delw;
1624  } else if (widthflag == L_ADJUST_RIGHT) {
1625  del_right = delw;
1626  } else {
1627  del_left = -delw / 2;
1628  del_right = delw / 2 + L_SIGN(delw) * (delw & 1);
1629  }
1630  if (heightflag == L_ADJUST_TOP) {
1631  del_top = -delh;
1632  } else if (heightflag == L_ADJUST_BOT) {
1633  del_bot = delh;
1634  } else {
1635  del_top = -delh / 2;
1636  del_bot = delh / 2 + L_SIGN(delh) * (delh & 1);
1637  }
1638  boxd = boxAdjustSides(NULL, boxs, del_left, del_right,
1639  del_top, del_bot);
1640  boxaAddBox(boxad, boxd, L_INSERT);
1641  boxDestroy(&boxs);
1642  }
1643 
1644  boxDestroy(&medbox);
1645  return boxad;
1646 }
1647 
1648 
1692 BOXA *
1694  l_int32 sides,
1695  l_int32 delh,
1696  l_int32 op,
1697  l_float32 factor,
1698  l_int32 start)
1699 {
1700 l_int32 n, he, ho, hmed, doeven;
1701 l_float32 del1, del2;
1702 BOXA *boxae, *boxao, *boxa1e, *boxa1o, *boxad;
1703 
1704  PROCNAME("boxaReconcileEvenOddHeight");
1705 
1706  if (!boxas)
1707  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
1708  if (sides != L_ADJUST_TOP && sides != L_ADJUST_BOT &&
1709  sides != L_ADJUST_TOP_AND_BOT) {
1710  L_WARNING("no action requested; returning copy\n", procName);
1711  return boxaCopy(boxas, L_COPY);
1712  }
1713  if ((n = boxaGetValidCount(boxas)) < 6) {
1714  L_WARNING("need at least 6 valid boxes; returning copy\n", procName);
1715  return boxaCopy(boxas, L_COPY);
1716  }
1717  if (factor <= 0.0) {
1718  L_WARNING("invalid factor; setting to 1.0\n", procName);
1719  factor = 1.0;
1720  }
1721 
1722  /* Require at least 3 valid boxes of both types */
1723  boxaSplitEvenOdd(boxas, 0, &boxae, &boxao);
1724  if (boxaGetValidCount(boxae) < 3 || boxaGetValidCount(boxao) < 3) {
1725  boxaDestroy(&boxae);
1726  boxaDestroy(&boxao);
1727  return boxaCopy(boxas, L_COPY);
1728  }
1729 
1730  /* Get the median heights for each set */
1731  boxaGetMedianVals(boxae, NULL, NULL, NULL, &he);
1732  boxaGetMedianVals(boxao, NULL, NULL, NULL, &ho);
1733  L_INFO("median he = %d, median ho = %d\n", procName, he, ho);
1734 
1735  /* If the difference in median height reaches the threshold %delh,
1736  * only adjust the side(s) of one of the sets. If we choose
1737  * the minimum median height as the target, allow the target
1738  * to be scaled by a factor, typically near 1.0, of the
1739  * minimum median height. And similarly if the target is
1740  * the maximum median height. */
1741  if (L_ABS(he - ho) > delh) {
1742  if (op == L_ADJUST_CHOOSE_MIN) {
1743  doeven = (ho < he) ? TRUE : FALSE;
1744  hmed = (l_int32)(factor * L_MIN(he, ho));
1745  hmed = L_MIN(hmed, L_MAX(he, ho)); /* don't make it bigger! */
1746  } else { /* max height */
1747  doeven = (ho > he) ? TRUE : FALSE;
1748  hmed = (l_int32)(factor * L_MAX(he, ho));
1749  hmed = L_MAX(hmed, L_MIN(he, ho)); /* don't make it smaller! */
1750  }
1751  if (doeven) {
1752  boxa1e = boxaAdjustHeightToTarget(NULL, boxae, sides, hmed, delh);
1753  boxa1o = boxaCopy(boxao, L_COPY);
1754  } else { /* !doeven */
1755  boxa1e = boxaCopy(boxae, L_COPY);
1756  boxa1o = boxaAdjustHeightToTarget(NULL, boxao, sides, hmed, delh);
1757  }
1758  } else {
1759  boxa1e = boxaCopy(boxae, L_CLONE);
1760  boxa1o = boxaCopy(boxao, L_CLONE);
1761  }
1762  boxaDestroy(&boxae);
1763  boxaDestroy(&boxao);
1764 
1765  /* It can happen that the median is not a good measure for an
1766  * entire book. In that case, the reconciliation above can do
1767  * more harm than good. Sanity check by comparing height and y
1768  * differences of adjacent even/odd boxes, before and after
1769  * reconciliation. */
1770  boxad = boxaMergeEvenOdd(boxa1e, boxa1o, 0);
1771  boxaTestEvenOddHeight(boxas, boxad, start, &del1, &del2);
1772  boxaDestroy(&boxa1e);
1773  boxaDestroy(&boxa1o);
1774  if (del2 < del1 + 10.)
1775  return boxad;
1776 
1777  /* Using the median made it worse. Skip reconciliation:
1778  * forcing all pairs of top and bottom values to have
1779  * maximum extent does not improve the situation either. */
1780  L_INFO("Got worse: del2 = %f > del1 = %f\n", procName, del2, del1);
1781  boxaDestroy(&boxad);
1782  return boxaCopy(boxas, L_COPY);
1783 }
1784 
1785 
1801 static l_int32
1803  BOXA *boxa2,
1804  l_int32 start,
1805  l_float32 *pdel1,
1806  l_float32 *pdel2)
1807 {
1808 l_int32 i, n, npairs, y1a, y1b, y2a, y2b, h1a, h1b, h2a, h2b;
1809 l_float32 del1, del2;
1810 
1811  PROCNAME("boxaTestEvenOddHeight");
1812 
1813  if (pdel1) *pdel1 = 0.0;
1814  if (pdel2) *pdel2 = 0.0;
1815  if (!pdel1 || !pdel2)
1816  return ERROR_INT("&del1 and &del2 not both defined", procName, 1);
1817  if (!boxa1 || !boxa2)
1818  return ERROR_INT("boxa1 and boxa2 not both defined", procName, 1);
1819  n = L_MIN(boxaGetCount(boxa1), boxaGetCount(boxa2));
1820 
1821  /* For boxa1 and boxa2 separately, we expect the y and h values
1822  * to be similar for adjacent boxes. Get a measure of similarity
1823  * by finding the sum of squares of differences between
1824  * y values and between h values, and adding them. */
1825  del1 = del2 = 0.0;
1826  npairs = (n - start) / 2;
1827  for (i = start; i < 2 * npairs; i += 2) {
1828  boxaGetBoxGeometry(boxa1, i, NULL, &y1a, NULL, &h1a);
1829  boxaGetBoxGeometry(boxa1, i + 1, NULL, &y1b, NULL, &h1b);
1830  del1 += (l_float32)(y1a - y1b) * (y1a - y1b)
1831  + (h1a - h1b) * (h1a - h1b);
1832  boxaGetBoxGeometry(boxa2, i, NULL, &y2a, NULL, &h2a);
1833  boxaGetBoxGeometry(boxa2, i + 1, NULL, &y2b, NULL, &h2b);
1834  del2 += (l_float32)(y2a - y2b) * (y2a - y2b)
1835  + (h2a - h2b) * (h2a - h2b);
1836  }
1837 
1838  /* Get the root of the average of the sum of square differences */
1839  *pdel1 = (l_float32)sqrt((l_float64)del1 / (0.5 * n));
1840  *pdel2 = (l_float32)sqrt((l_float64)del2 / (0.5 * n));
1841  return 0;
1842 }
1843 
1844 
1873 BOXA *
1875  l_int32 delw,
1876  l_int32 op,
1877  l_float32 factor,
1878  NUMA *na)
1879 {
1880 l_int32 i, ne, no, nmin, xe, we, xo, wo, inde, indo, x, w;
1881 BOX *boxe, *boxo;
1882 BOXA *boxae, *boxao, *boxad;
1883 
1884  PROCNAME("boxaReconcilePairWidth");
1885 
1886  if (!boxas)
1887  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
1888  if (factor <= 0.0) {
1889  L_WARNING("invalid factor; setting to 1.0\n", procName);
1890  factor = 1.0;
1891  }
1892 
1893  /* Taking the boxes in pairs, if the difference in width reaches
1894  * the threshold %delw, adjust the left or right side of one
1895  * of the pair. */
1896  boxaSplitEvenOdd(boxas, 0, &boxae, &boxao);
1897  ne = boxaGetCount(boxae);
1898  no = boxaGetCount(boxao);
1899  nmin = L_MIN(ne, no);
1900  for (i = 0; i < nmin; i++) {
1901  /* Set indicator values */
1902  if (na) {
1903  numaGetIValue(na, 2 * i, &inde);
1904  numaGetIValue(na, 2 * i + 1, &indo);
1905  } else {
1906  inde = indo = 1;
1907  }
1908  if (inde == 0 && indo == 0) continue;
1909 
1910  boxe = boxaGetBox(boxae, i, L_CLONE);
1911  boxo = boxaGetBox(boxao, i, L_CLONE);
1912  boxGetGeometry(boxe, &xe, NULL, &we, NULL);
1913  boxGetGeometry(boxo, &xo, NULL, &wo, NULL);
1914  if (we == 0 || wo == 0) { /* if either is invalid; skip */
1915  boxDestroy(&boxe);
1916  boxDestroy(&boxo);
1917  continue;
1918  } else if (L_ABS(we - wo) > delw) {
1919  if (op == L_ADJUST_CHOOSE_MIN) {
1920  if (we > wo && inde == 1) {
1921  /* move left side of even to the right */
1922  w = factor * wo;
1923  x = xe + (we - w);
1924  boxSetGeometry(boxe, x, -1, w, -1);
1925  } else if (we < wo && indo == 1) {
1926  /* move right side of odd to the left */
1927  w = factor * we;
1928  boxSetGeometry(boxo, -1, -1, w, -1);
1929  }
1930  } else { /* maximize width */
1931  if (we < wo && inde == 1) {
1932  /* move left side of even to the left */
1933  w = factor * wo;
1934  x = L_MAX(0, xe + (we - w));
1935  w = we + (xe - x); /* covers both cases for the max */
1936  boxSetGeometry(boxe, x, -1, w, -1);
1937  } else if (we > wo && indo == 1) {
1938  /* move right side of odd to the right */
1939  w = factor * we;
1940  boxSetGeometry(boxo, -1, -1, w, -1);
1941  }
1942  }
1943  }
1944  boxDestroy(&boxe);
1945  boxDestroy(&boxo);
1946  }
1947 
1948  boxad = boxaMergeEvenOdd(boxae, boxao, 0);
1949  boxaDestroy(&boxae);
1950  boxaDestroy(&boxao);
1951  return boxad;
1952 }
1953 
1954 
1978 l_int32
1980  const char *plotname,
1981  NUMA **pnal,
1982  NUMA **pnat,
1983  NUMA **pnar,
1984  NUMA **pnab,
1985  PIX **ppixd)
1986 {
1987 char buf[128], titlebuf[128];
1988 static l_int32 plotid = 0;
1989 l_int32 n, i, w, h, left, top, right, bot;
1990 BOXA *boxat;
1991 GPLOT *gplot;
1992 NUMA *nal, *nat, *nar, *nab;
1993 
1994  PROCNAME("boxaPlotSides");
1995 
1996  if (pnal) *pnal = NULL;
1997  if (pnat) *pnat = NULL;
1998  if (pnar) *pnar = NULL;
1999  if (pnab) *pnab = NULL;
2000  if (ppixd) *ppixd = NULL;
2001  if (!boxa)
2002  return ERROR_INT("boxa not defined", procName, 1);
2003  if ((n = boxaGetCount(boxa)) < 2)
2004  return ERROR_INT("less than 2 boxes", procName, 1);
2005 
2006  boxat = boxaFillSequence(boxa, L_USE_ALL_BOXES, 0);
2007 
2008  /* Build the numas for each side */
2009  nal = numaCreate(n);
2010  nat = numaCreate(n);
2011  nar = numaCreate(n);
2012  nab = numaCreate(n);
2013 
2014  for (i = 0; i < n; i++) {
2015  boxaGetBoxGeometry(boxat, i, &left, &top, &w, &h);
2016  right = left + w - 1;
2017  bot = top + h - 1;
2018  numaAddNumber(nal, left);
2019  numaAddNumber(nat, top);
2020  numaAddNumber(nar, right);
2021  numaAddNumber(nab, bot);
2022  }
2023  boxaDestroy(&boxat);
2024 
2025  lept_mkdir("lept/plots");
2026  if (plotname) {
2027  snprintf(buf, sizeof(buf), "/tmp/lept/plots/sides.%s", plotname);
2028  snprintf(titlebuf, sizeof(titlebuf), "%s: Box sides vs. box index",
2029  plotname);
2030  } else {
2031  snprintf(buf, sizeof(buf), "/tmp/lept/plots/sides.%d", plotid++);
2032  snprintf(titlebuf, sizeof(titlebuf), "Box sides vs. box index");
2033  }
2034  gplot = gplotCreate(buf, GPLOT_PNG, titlebuf,
2035  "box index", "side location");
2036  gplotAddPlot(gplot, NULL, nal, GPLOT_LINES, "left side");
2037  gplotAddPlot(gplot, NULL, nat, GPLOT_LINES, "top side");
2038  gplotAddPlot(gplot, NULL, nar, GPLOT_LINES, "right side");
2039  gplotAddPlot(gplot, NULL, nab, GPLOT_LINES, "bottom side");
2040  gplotMakeOutput(gplot);
2041  gplotDestroy(&gplot);
2042 
2043  if (ppixd) {
2044  stringCat(buf, sizeof(buf), ".png");
2045  *ppixd = pixRead(buf);
2046  }
2047 
2048  if (pnal)
2049  *pnal = nal;
2050  else
2051  numaDestroy(&nal);
2052  if (pnat)
2053  *pnat = nat;
2054  else
2055  numaDestroy(&nat);
2056  if (pnar)
2057  *pnar = nar;
2058  else
2059  numaDestroy(&nar);
2060  if (pnab)
2061  *pnab = nab;
2062  else
2063  numaDestroy(&nab);
2064  return 0;
2065 }
2066 
2067 
2090 l_int32
2092  const char *plotname,
2093  NUMA **pnaw,
2094  NUMA **pnah,
2095  PIX **ppixd)
2096 {
2097 char buf[128], titlebuf[128];
2098 static l_int32 plotid = 0;
2099 l_int32 n, i, w, h;
2100 BOXA *boxat;
2101 GPLOT *gplot;
2102 NUMA *naw, *nah;
2103 
2104  PROCNAME("boxaPlotSizes");
2105 
2106  if (pnaw) *pnaw = NULL;
2107  if (pnah) *pnah = NULL;
2108  if (ppixd) *ppixd = NULL;
2109  if (!boxa)
2110  return ERROR_INT("boxa not defined", procName, 1);
2111  if ((n = boxaGetCount(boxa)) < 2)
2112  return ERROR_INT("less than 2 boxes", procName, 1);
2113 
2114  boxat = boxaFillSequence(boxa, L_USE_ALL_BOXES, 0);
2115 
2116  /* Build the numas for the width and height */
2117  naw = numaCreate(n);
2118  nah = numaCreate(n);
2119 
2120  for (i = 0; i < n; i++) {
2121  boxaGetBoxGeometry(boxat, i, NULL, NULL, &w, &h);
2122  numaAddNumber(naw, w);
2123  numaAddNumber(nah, h);
2124  }
2125  boxaDestroy(&boxat);
2126 
2127  lept_mkdir("lept/plots");
2128  if (plotname) {
2129  snprintf(buf, sizeof(buf), "/tmp/lept/plots/size.%s", plotname);
2130  snprintf(titlebuf, sizeof(titlebuf), "%s: Box size vs. box index",
2131  plotname);
2132  } else {
2133  snprintf(buf, sizeof(buf), "/tmp/lept/plots/size.%d", plotid++);
2134  snprintf(titlebuf, sizeof(titlebuf), "Box size vs. box index");
2135  }
2136  gplot = gplotCreate(buf, GPLOT_PNG, titlebuf,
2137  "box index", "box dimension");
2138  gplotAddPlot(gplot, NULL, naw, GPLOT_LINES, "width");
2139  gplotAddPlot(gplot, NULL, nah, GPLOT_LINES, "height");
2140  gplotMakeOutput(gplot);
2141  gplotDestroy(&gplot);
2142 
2143  if (ppixd) {
2144  stringCat(buf, sizeof(buf), ".png");
2145  *ppixd = pixRead(buf);
2146  }
2147 
2148  if (pnaw)
2149  *pnaw = naw;
2150  else
2151  numaDestroy(&naw);
2152  if (pnah)
2153  *pnah = nah;
2154  else
2155  numaDestroy(&nah);
2156  return 0;
2157 }
2158 
2159 
2178 BOXA *
2180  l_int32 useflag,
2181  l_int32 debug)
2182 {
2183 l_int32 n, nv;
2184 BOXA *boxae, *boxao, *boxad;
2185 
2186  PROCNAME("boxaFillSequence");
2187 
2188  if (!boxas)
2189  return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
2190  if (useflag != L_USE_ALL_BOXES && useflag != L_USE_SAME_PARITY_BOXES)
2191  return (BOXA *)ERROR_PTR("invalid useflag", procName, NULL);
2192 
2193  n = boxaGetCount(boxas);
2194  nv = boxaGetValidCount(boxas);
2195  if (n == nv)
2196  return boxaCopy(boxas, L_COPY); /* all valid */
2197  if (debug)
2198  L_INFO("%d valid boxes, %d invalid boxes\n", procName, nv, n - nv);
2199  if (useflag == L_USE_SAME_PARITY_BOXES && n < 3) {
2200  L_WARNING("n < 3; some invalid\n", procName);
2201  return boxaCopy(boxas, L_COPY);
2202  }
2203 
2204  if (useflag == L_USE_ALL_BOXES) {
2205  boxad = boxaCopy(boxas, L_COPY);
2206  boxaFillAll(boxad);
2207  } else {
2208  boxaSplitEvenOdd(boxas, 0, &boxae, &boxao);
2209  boxaFillAll(boxae);
2210  boxaFillAll(boxao);
2211  boxad = boxaMergeEvenOdd(boxae, boxao, 0);
2212  boxaDestroy(&boxae);
2213  boxaDestroy(&boxao);
2214  }
2215 
2216  nv = boxaGetValidCount(boxad);
2217  if (n != nv)
2218  L_WARNING("there are still %d invalid boxes\n", procName, n - nv);
2219 
2220  return boxad;
2221 }
2222 
2223 
2237 static l_int32
2239 {
2240 l_int32 n, nv, i, j, spandown, spanup;
2241 l_int32 *indic;
2242 BOX *box, *boxt;
2243 
2244  PROCNAME("boxaFillAll");
2245 
2246  if (!boxa)
2247  return ERROR_INT("boxa not defined", procName, 1);
2248  n = boxaGetCount(boxa);
2249  nv = boxaGetValidCount(boxa);
2250  if (n == nv) return 0;
2251  if (nv == 0) {
2252  L_WARNING("no valid boxes out of %d boxes\n", procName, n);
2253  return 0;
2254  }
2255 
2256  /* Make indicator array for valid boxes */
2257  if ((indic = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL)
2258  return ERROR_INT("indic not made", procName, 1);
2259  for (i = 0; i < n; i++) {
2260  box = boxaGetValidBox(boxa, i, L_CLONE);
2261  if (box)
2262  indic[i] = 1;
2263  boxDestroy(&box);
2264  }
2265 
2266  /* Replace invalid boxes with the nearest valid one */
2267  for (i = 0; i < n; i++) {
2268  box = boxaGetValidBox(boxa, i, L_CLONE);
2269  if (!box) {
2270  spandown = spanup = 10000000;
2271  for (j = i - 1; j >= 0; j--) {
2272  if (indic[j] == 1) {
2273  spandown = i - j;
2274  break;
2275  }
2276  }
2277  for (j = i + 1; j < n; j++) {
2278  if (indic[j] == 1) {
2279  spanup = j - i;
2280  break;
2281  }
2282  }
2283  if (spandown < spanup)
2284  boxt = boxaGetBox(boxa, i - spandown, L_COPY);
2285  else
2286  boxt = boxaGetBox(boxa, i + spanup, L_COPY);
2287  boxaReplaceBox(boxa, i, boxt);
2288  }
2289  boxDestroy(&box);
2290  }
2291 
2292  LEPT_FREE(indic);
2293  return 0;
2294 }
2295 
2296 
2297 /*---------------------------------------------------------------------*
2298  * Miscellaneous Boxa functions *
2299  *---------------------------------------------------------------------*/
2320 l_int32
2322  l_int32 *pw,
2323  l_int32 *ph,
2324  BOX **pbox)
2325 {
2326 l_int32 i, n, x, y, w, h, xmax, ymax, xmin, ymin, found;
2327 
2328  PROCNAME("boxaGetExtent");
2329 
2330  if (!pw && !ph && !pbox)
2331  return ERROR_INT("no ptrs defined", procName, 1);
2332  if (pw) *pw = 0;
2333  if (ph) *ph = 0;
2334  if (pbox) *pbox = NULL;
2335  if (!boxa)
2336  return ERROR_INT("boxa not defined", procName, 1);
2337 
2338  n = boxaGetCount(boxa);
2339  xmax = ymax = 0;
2340  xmin = ymin = 100000000;
2341  found = FALSE;
2342  for (i = 0; i < n; i++) {
2343  boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h);
2344  if (w <= 0 || h <= 0)
2345  continue;
2346  found = TRUE;
2347  xmin = L_MIN(xmin, x);
2348  ymin = L_MIN(ymin, y);
2349  xmax = L_MAX(xmax, x + w);
2350  ymax = L_MAX(ymax, y + h);
2351  }
2352  if (found == FALSE) /* no valid boxes in boxa */
2353  xmin = ymin = 0;
2354  if (pw) *pw = xmax;
2355  if (ph) *ph = ymax;
2356  if (pbox)
2357  *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin);
2358 
2359  return 0;
2360 }
2361 
2362 
2386 l_int32
2388  l_int32 wc,
2389  l_int32 hc,
2390  l_int32 exactflag,
2391  l_float32 *pfract)
2392 {
2393 l_int32 i, n, x, y, w, h, sum;
2394 BOX *box, *boxc;
2395 PIX *pixt;
2396 
2397  PROCNAME("boxaGetCoverage");
2398 
2399  if (!pfract)
2400  return ERROR_INT("&fract not defined", procName, 1);
2401  *pfract = 0.0;
2402  if (!boxa)
2403  return ERROR_INT("boxa not defined", procName, 1);
2404 
2405  n = boxaGetCount(boxa);
2406  if (n == 0)
2407  return ERROR_INT("no boxes in boxa", procName, 1);
2408 
2409  if (exactflag == 0) { /* quick and dirty */
2410  sum = 0;
2411  for (i = 0; i < n; i++) {
2412  box = boxaGetBox(boxa, i, L_CLONE);
2413  if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) {
2414  boxGetGeometry(boxc, NULL, NULL, &w, &h);
2415  sum += w * h;
2416  boxDestroy(&boxc);
2417  }
2418  boxDestroy(&box);
2419  }
2420  } else { /* slower and exact */
2421  pixt = pixCreate(wc, hc, 1);
2422  for (i = 0; i < n; i++) {
2423  box = boxaGetBox(boxa, i, L_CLONE);
2424  boxGetGeometry(box, &x, &y, &w, &h);
2425  pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0);
2426  boxDestroy(&box);
2427  }
2428  pixCountPixels(pixt, &sum, NULL);
2429  pixDestroy(&pixt);
2430  }
2431 
2432  *pfract = (l_float32)sum / (l_float32)(wc * hc);
2433  return 0;
2434 }
2435 
2436 
2445 l_int32
2447  l_int32 *pminw,
2448  l_int32 *pminh,
2449  l_int32 *pmaxw,
2450  l_int32 *pmaxh)
2451 {
2452 l_int32 minw, minh, maxw, maxh, minbw, minbh, maxbw, maxbh, i, n;
2453 BOXA *boxa;
2454 
2455  PROCNAME("boxaaSizeRange");
2456 
2457  if (!pminw && !pmaxw && !pminh && !pmaxh)
2458  return ERROR_INT("no data can be returned", procName, 1);
2459  if (pminw) *pminw = 0;
2460  if (pminh) *pminh = 0;
2461  if (pmaxw) *pmaxw = 0;
2462  if (pmaxh) *pmaxh = 0;
2463  if (!baa)
2464  return ERROR_INT("baa not defined", procName, 1);
2465 
2466  minw = minh = 100000000;
2467  maxw = maxh = 0;
2468  n = boxaaGetCount(baa);
2469  for (i = 0; i < n; i++) {
2470  boxa = boxaaGetBoxa(baa, i, L_CLONE);
2471  boxaSizeRange(boxa, &minbw, &minbh, &maxbw, &maxbh);
2472  if (minbw < minw)
2473  minw = minbw;
2474  if (minbh < minh)
2475  minh = minbh;
2476  if (maxbw > maxw)
2477  maxw = maxbw;
2478  if (maxbh > maxh)
2479  maxh = maxbh;
2480  boxaDestroy(&boxa);
2481  }
2482 
2483  if (pminw) *pminw = minw;
2484  if (pminh) *pminh = minh;
2485  if (pmaxw) *pmaxw = maxw;
2486  if (pmaxh) *pmaxh = maxh;
2487  return 0;
2488 }
2489 
2490 
2499 l_int32
2501  l_int32 *pminw,
2502  l_int32 *pminh,
2503  l_int32 *pmaxw,
2504  l_int32 *pmaxh)
2505 {
2506 l_int32 minw, minh, maxw, maxh, i, n, w, h;
2507 
2508  PROCNAME("boxaSizeRange");
2509 
2510  if (!pminw && !pmaxw && !pminh && !pmaxh)
2511  return ERROR_INT("no data can be returned", procName, 1);
2512  if (pminw) *pminw = 0;
2513  if (pminh) *pminh = 0;
2514  if (pmaxw) *pmaxw = 0;
2515  if (pmaxh) *pmaxh = 0;
2516  if (!boxa)
2517  return ERROR_INT("boxa not defined", procName, 1);
2518 
2519  minw = minh = 100000000;
2520  maxw = maxh = 0;
2521  n = boxaGetCount(boxa);
2522  for (i = 0; i < n; i++) {
2523  boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
2524  if (w < minw)
2525  minw = w;
2526  if (h < minh)
2527  minh = h;
2528  if (w > maxw)
2529  maxw = w;
2530  if (h > maxh)
2531  maxh = h;
2532  }
2533 
2534  if (pminw) *pminw = minw;
2535  if (pminh) *pminh = minh;
2536  if (pmaxw) *pmaxw = maxw;
2537  if (pmaxh) *pmaxh = maxh;
2538  return 0;
2539 }
2540 
2541 
2550 l_int32
2552  l_int32 *pminx,
2553  l_int32 *pminy,
2554  l_int32 *pmaxx,
2555  l_int32 *pmaxy)
2556 {
2557 l_int32 minx, miny, maxx, maxy, i, n, x, y;
2558 
2559  PROCNAME("boxaLocationRange");
2560 
2561  if (!pminx && !pminy && !pmaxx && !pmaxy)
2562  return ERROR_INT("no data can be returned", procName, 1);
2563  if (pminx) *pminx = 0;
2564  if (pminy) *pminy = 0;
2565  if (pmaxx) *pmaxx = 0;
2566  if (pmaxy) *pmaxy = 0;
2567  if (!boxa)
2568  return ERROR_INT("boxa not defined", procName, 1);
2569 
2570  minx = miny = 100000000;
2571  maxx = maxy = 0;
2572  n = boxaGetCount(boxa);
2573  for (i = 0; i < n; i++) {
2574  boxaGetBoxGeometry(boxa, i, &x, &y, NULL, NULL);
2575  if (x < minx)
2576  minx = x;
2577  if (y < miny)
2578  miny = y;
2579  if (x > maxx)
2580  maxx = x;
2581  if (y > maxy)
2582  maxy = y;
2583  }
2584 
2585  if (pminx) *pminx = minx;
2586  if (pminy) *pminy = miny;
2587  if (pmaxx) *pmaxx = maxx;
2588  if (pmaxy) *pmaxy = maxy;
2589 
2590  return 0;
2591 }
2592 
2593 
2601 l_int32
2603  NUMA **pnaw,
2604  NUMA **pnah)
2605 {
2606 l_int32 i, n, w, h;
2607 BOX *box;
2608 
2609  PROCNAME("boxaGetSizes");
2610 
2611  if (pnaw) *pnaw = NULL;
2612  if (pnah) *pnah = NULL;
2613  if (!pnaw && !pnah)
2614  return ERROR_INT("no output requested", procName, 1);
2615  if (!boxa)
2616  return ERROR_INT("boxa not defined", procName, 1);
2617 
2618  n = boxaGetValidCount(boxa);
2619  if (pnaw) *pnaw = numaCreate(n);
2620  if (pnah) *pnah = numaCreate(n);
2621  for (i = 0; i < n; i++) {
2622  box = boxaGetValidBox(boxa, i, L_COPY);
2623  if (box) {
2624  boxGetGeometry(box, NULL, NULL, &w, &h);
2625  if (pnaw) numaAddNumber(*pnaw, w);
2626  if (pnah) numaAddNumber(*pnah, h);
2627  boxDestroy(&box);
2628  }
2629  }
2630 
2631  return 0;
2632 }
2633 
2634 
2647 l_int32
2649  l_int32 *parea)
2650 {
2651 l_int32 i, n, w, h;
2652 
2653  PROCNAME("boxaGetArea");
2654 
2655  if (!parea)
2656  return ERROR_INT("&area not defined", procName, 1);
2657  *parea = 0;
2658  if (!boxa)
2659  return ERROR_INT("boxa not defined", procName, 1);
2660 
2661  n = boxaGetCount(boxa);
2662  for (i = 0; i < n; i++) {
2663  boxaGetBoxGeometry(boxa, i, NULL, NULL, &w, &h);
2664  *parea += w * h;
2665  }
2666  return 0;
2667 }
2668 
2669 
2695 PIX *
2697  PIXA *pixa,
2698  l_int32 maxwidth,
2699  l_int32 linewidth,
2700  l_float32 scalefactor,
2701  l_int32 background,
2702  l_int32 spacing,
2703  l_int32 border)
2704 {
2705 char buf[32];
2706 l_int32 i, n, npix, w, h, fontsize;
2707 L_BMF *bmf;
2708 BOX *box;
2709 BOXA *boxa;
2710 PIX *pix1, *pix2, *pixd;
2711 PIXA *pixat;
2712 
2713  PROCNAME("boxaDisplayTiled");
2714 
2715  if (!boxas)
2716  return (PIX *)ERROR_PTR("boxas not defined", procName, NULL);
2717 
2718  boxa = boxaSaveValid(boxas, L_COPY);
2719  n = boxaGetCount(boxa);
2720  if (pixa) {
2721  npix = pixaGetCount(pixa);
2722  if (n != npix) {
2723  boxaDestroy(&boxa);
2724  return (PIX *)ERROR_PTR("boxa and pixa counts differ",
2725  procName, NULL);
2726  }
2727  }
2728 
2729  /* Because the bitmap font will be reduced when tiled, choose the
2730  * font size inversely with the scale factor. */
2731  if (scalefactor > 0.8)
2732  fontsize = 6;
2733  else if (scalefactor > 0.6)
2734  fontsize = 10;
2735  else if (scalefactor > 0.4)
2736  fontsize = 14;
2737  else if (scalefactor > 0.3)
2738  fontsize = 18;
2739  else fontsize = 20;
2740  bmf = bmfCreate(NULL, fontsize);
2741 
2742  pixat = pixaCreate(n);
2743  boxaGetExtent(boxa, &w, &h, NULL);
2744  for (i = 0; i < n; i++) {
2745  box = boxaGetBox(boxa, i, L_CLONE);
2746  if (!pixa) {
2747  pix1 = pixCreate(w, h, 32);
2748  pixSetAll(pix1);
2749  } else {
2750  pix1 = pixaGetPix(pixa, i, L_COPY);
2751  }
2752  pixSetBorderVal(pix1, 0, 0, 0, 2, 0x0000ff00);
2753  snprintf(buf, sizeof(buf), "%d", i);
2754  pix2 = pixAddSingleTextblock(pix1, bmf, buf, 0x00ff0000,
2755  L_ADD_BELOW, NULL);
2756  pixDestroy(&pix1);
2757  pixRenderBoxArb(pix2, box, linewidth, 255, 0, 0);
2758  pixaAddPix(pixat, pix2, L_INSERT);
2759  boxDestroy(&box);
2760  }
2761  bmfDestroy(&bmf);
2762  boxaDestroy(&boxa);
2763 
2764  pixd = pixaDisplayTiledInRows(pixat, 32, maxwidth, scalefactor, background,
2765  spacing, border);
2766  pixaDestroy(&pixat);
2767  return pixd;
2768 }
void gplotDestroy(GPLOT **pgplot)
gplotDestroy()
Definition: gplot.c:197
void bmfDestroy(L_BMF **pbmf)
bmfDestroy()
Definition: bmf.c:166
l_int32 gplotAddPlot(GPLOT *gplot, NUMA *nax, NUMA *nay, l_int32 plotstyle, const char *plottitle)
gplotAddPlot()
Definition: gplot.c:263
l_int32 boxaExtractAsPta(BOXA *boxa, PTA **pptal, PTA **pptat, PTA **pptar, PTA **pptab, PTA **pptaw, PTA **pptah, l_int32 keepinvalid)
boxaExtractAsPta()
Definition: boxfunc2.c:1170
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:1880
BOX * boxaGetValidBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetValidBox()
Definition: boxbasic.c:799
BOXA * boxaConstrainSize(BOXA *boxas, l_int32 width, l_int32 widthflag, l_int32 height, l_int32 heightflag)
boxaConstrainSize()
Definition: boxfunc4.c:1584
PIX * boxaDisplayTiled(BOXA *boxas, PIXA *pixa, l_int32 maxwidth, l_int32 linewidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border)
boxaDisplayTiled()
Definition: boxfunc4.c:2696
l_int32 numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:472
l_int32 boxaExtractAsNuma(BOXA *boxa, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, NUMA **pnaw, NUMA **pnah, l_int32 keepinvalid)
boxaExtractAsNuma()
Definition: boxfunc2.c:1093
PTA * boxaConvertToPta(BOXA *boxa, l_int32 ncorners)
boxaConvertToPta()
Definition: boxfunc4.c:762
l_int32 boxaPlotSides(BOXA *boxa, const char *plotname, NUMA **pnal, NUMA **pnat, NUMA **pnar, NUMA **pnab, PIX **ppixd)
boxaPlotSides()
Definition: boxfunc4.c:1979
BOXA * boxaSelectRange(BOXA *boxas, l_int32 first, l_int32 last, l_int32 copyflag)
boxaSelectRange()
Definition: boxfunc4.c:110
BOXA * boxaMergeEvenOdd(BOXA *boxae, BOXA *boxao, l_int32 fillflag)
boxaMergeEvenOdd()
Definition: boxfunc1.c:2475
l_int32 boxaWrite(const char *filename, BOXA *boxa)
boxaWrite()
Definition: boxbasic.c:2193
l_int32 ptaAddPt(PTA *pta, l_float32 x, l_float32 y)
ptaAddPt()
Definition: ptabasic.c:341
PIXA * pixaCreate(l_int32 n)
pixaCreate()
Definition: pixabasic.c:161
l_int32 boxaaGetCount(BOXAA *baa)
boxaaGetCount()
Definition: boxbasic.c:1438
l_int32 boxaGetSizes(BOXA *boxa, NUMA **pnaw, NUMA **pnah)
boxaGetSizes()
Definition: boxfunc4.c:2602
GPLOT * gplotCreate(const char *rootname, l_int32 outformat, const char *title, const char *xlabel, const char *ylabel)
gplotCreate()
Definition: gplot.c:138
l_int32 boxaLocationRange(BOXA *boxa, l_int32 *pminx, l_int32 *pminy, l_int32 *pmaxx, l_int32 *pmaxy)
boxaLocationRange()
Definition: boxfunc4.c:2551
l_int32 ptaGetLinearLSF(PTA *pta, l_float32 *pa, l_float32 *pb, NUMA **pnafit)
ptaGetLinearLSF()
Definition: ptafunc1.c:1029
BOXA * boxaSelectBySize(BOXA *boxas, l_int32 width, l_int32 height, l_int32 type, l_int32 relation, l_int32 *pchanged)
boxaSelectBySize()
Definition: boxfunc4.c:226
PTA * ptaCreate(l_int32 n)
ptaCreate()
Definition: ptabasic.c:115
l_int32 boxSetGeometry(BOX *box, l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxSetGeometry()
Definition: boxbasic.c:339
l_int32 boxaAddBox(BOXA *boxa, BOX *box, l_int32 copyflag)
boxaAddBox()
Definition: boxbasic.c:615
NUMA * boxaMakeSizeIndicator(BOXA *boxa, l_int32 width, l_int32 height, l_int32 type, l_int32 relation)
boxaMakeSizeIndicator()
Definition: boxfunc4.c:289
l_int32 boxaaSizeRange(BOXAA *baa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh)
boxaaSizeRange()
Definition: boxfunc4.c:2446
BOX * boxClipToRectangle(BOX *box, l_int32 wi, l_int32 hi)
boxClipToRectangle()
Definition: boxfunc1.c:1587
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
static l_int32 boxaFillAll(BOXA *boxa)
boxaFillAll()
Definition: boxfunc4.c:2238
BOXA * ptaConvertToBoxa(PTA *pta, l_int32 ncorners)
ptaConvertToBoxa()
Definition: boxfunc4.c:807
BOXA * boxaCopy(BOXA *boxa, l_int32 copyflag)
boxaCopy()
Definition: boxbasic.c:531
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:186
l_int32 ptaGetCount(PTA *pta)
ptaGetCount()
Definition: ptabasic.c:503
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:577
BOXA * boxaAdjustHeightToTarget(BOXA *boxad, BOXA *boxas, l_int32 sides, l_int32 target, l_int32 thresh)
boxaAdjustHeightToTarget()
Definition: boxfunc1.c:1999
NUMA * numaWindowedMedian(NUMA *nas, l_int32 halfwin)
numaWindowedMedian()
Definition: numafunc2.c:758
static l_int32 boxaTestEvenOddHeight(BOXA *boxa1, BOXA *boxa2, l_int32 start, l_float32 *pdel1, l_float32 *pdel2)
boxaTestEvenOddHeight()
Definition: boxfunc4.c:1802
l_int32 boxaGetBoxGeometry(BOXA *boxa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxaGetBoxGeometry()
Definition: boxbasic.c:860
Definition: pix.h:492
l_int32 gplotMakeOutput(GPLOT *gplot)
gplotMakeOutput()
Definition: gplot.c:379
Definition: bmf.h:45
l_int32 pixRenderBoxArb(PIX *pix, BOX *box, l_int32 width, l_uint8 rval, l_uint8 gval, l_uint8 bval)
pixRenderBoxArb()
Definition: graphics.c:1641
l_int32 pixSetBorderVal(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val)
pixSetBorderVal()
Definition: pix2.c:1476
Definition: pix.h:502
l_int32 boxaSizeRange(BOXA *boxa, l_int32 *pminw, l_int32 *pminh, l_int32 *pmaxw, l_int32 *pmaxh)
boxaSizeRange()
Definition: boxfunc4.c:2500
BOXAA * boxaaSelectRange(BOXAA *baas, l_int32 first, l_int32 last, l_int32 copyflag)
boxaaSelectRange()
Definition: boxfunc4.c:163
l_int32 stringCat(char *dest, size_t size, const char *src)
stringCat()
Definition: utils2.c:356
l_int32 pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy)
pixRasterop()
Definition: rop.c:193
NUMA * numaPseudorandomSequence(l_int32 size, l_int32 seed)
numaPseudorandomSequence()
Definition: numafunc1.c:2969
Definition: array.h:59
static const l_int32 L_INSERT
Definition: pix.h:710
l_int32 ptaJoin(PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend)
ptaJoin()
Definition: ptafunc1.c:154
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:630
l_int32 boxaGetArea(BOXA *boxa, l_int32 *parea)
boxaGetArea()
Definition: boxfunc4.c:2648
l_int32 pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag)
pixaAddPix()
Definition: pixabasic.c:493
BOXA * boxaReconcilePairWidth(BOXA *boxas, l_int32 delw, l_int32 op, l_float32 factor, NUMA *na)
boxaReconcilePairWidth()
Definition: boxfunc4.c:1874
#define PIX_SET
Definition: pix.h:331
BOXA * boxaSaveValid(BOXA *boxas, l_int32 copyflag)
boxaSaveValid()
Definition: boxbasic.c:1109
PIX * pixaDisplayTiledInRows(PIXA *pixa, l_int32 outdepth, l_int32 maxwidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border)
pixaDisplayTiledInRows()
Definition: pixafunc2.c:821
BOXA * boxaPermuteRandom(BOXA *boxad, BOXA *boxas)
boxaPermuteRandom()
Definition: boxfunc4.c:681
BOXA * boxaSelectByWHRatio(BOXA *boxas, l_float32 ratio, l_int32 relation, l_int32 *pchanged)
boxaSelectByWHRatio()
Definition: boxfunc4.c:480
l_int32 boxaGetCoverage(BOXA *boxa, l_int32 wc, l_int32 hc, l_int32 exactflag, l_float32 *pfract)
boxaGetCoverage()
Definition: boxfunc4.c:2387
l_int32 boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:309
BOX * boxAdjustSides(BOX *boxd, BOX *boxs, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot)
boxAdjustSides()
Definition: boxfunc1.c:1807
Definition: gplot.h:75
l_int32 numaGetMedianVariation(NUMA *na, l_float32 *pmedval, l_float32 *pmedvar)
numaGetMedianVariation()
Definition: numafunc1.c:3266
BOXA * boxaWindowedMedian(BOXA *boxas, l_int32 halfwin, l_int32 debug)
boxaWindowedMedian()
Definition: boxfunc4.c:1330
l_int32 boxaSwapBoxes(BOXA *boxa, l_int32 i, l_int32 j)
boxaSwapBoxes()
Definition: boxfunc4.c:718
BOXA * boxaSelectByArea(BOXA *boxas, l_int32 area, l_int32 relation, l_int32 *pchanged)
boxaSelectByArea()
Definition: boxfunc4.c:376
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:545
BOX * boxaGetBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetBox()
Definition: boxbasic.c:760
BOXA * boxaModifyWithBoxa(BOXA *boxas, BOXA *boxam, l_int32 subflag, l_int32 maxdiff, l_int32 extrapixels)
boxaModifyWithBoxa()
Definition: boxfunc4.c:1468
Definition: pix.h:454
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:359
BOXA * boxaReconcileEvenOddHeight(BOXA *boxas, l_int32 sides, l_int32 delh, l_int32 op, l_float32 factor, l_int32 start)
boxaReconcileEvenOddHeight()
Definition: boxfunc4.c:1693
NUMA * boxaMakeAreaIndicator(BOXA *boxa, l_int32 area, l_int32 relation)
boxaMakeAreaIndicator()
Definition: boxfunc4.c:426
BOXAA * boxaaCreate(l_int32 n)
boxaaCreate()
Definition: boxbasic.c:1237
l_int32 ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py)
ptaGetIPt()
Definition: ptabasic.c:554
l_int32 boxaPlotSizes(BOXA *boxa, const char *plotname, NUMA **pnaw, NUMA **pnah, PIX **ppixd)
boxaPlotSizes()
Definition: boxfunc4.c:2091
BOXA * boxaSelectWithIndicator(BOXA *boxas, NUMA *na, l_int32 *pchanged)
boxaSelectWithIndicator()
Definition: boxfunc4.c:582
PIX * pixRead(const char *filename)
pixRead()
Definition: readfile.c:189
l_int32 boxaGetValidCount(BOXA *boxa)
boxaGetValidCount()
Definition: boxbasic.c:732
PTA * boxConvertToPta(BOX *box, l_int32 ncorners)
boxConvertToPta()
Definition: boxfunc4.c:862
l_int32 boxaGetMedianVals(BOXA *boxa, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxaGetMedianVals()
Definition: boxfunc2.c:1317
l_int32 boxaaAddBoxa(BOXAA *baa, BOXA *ba, l_int32 copyflag)
boxaaAddBoxa()
Definition: boxbasic.c:1341
BOXA * boxaPermutePseudorandom(BOXA *boxas)
boxaPermutePseudorandom()
Definition: boxfunc4.c:640
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:660
Definition: pix.h:705
BOX * boxCopy(BOX *box)
boxCopy()
Definition: boxbasic.c:229
Definition: pix.h:134
Definition: pix.h:706
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:191
l_int32 ptaWrite(const char *filename, PTA *pta, l_int32 type)
ptaWrite()
Definition: ptabasic.c:772
BOXA * boxaCreate(l_int32 n)
boxaCreate()
Definition: boxbasic.c:496
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:277
l_int32 boxaGetExtent(BOXA *boxa, l_int32 *pw, l_int32 *ph, BOX **pbox)
boxaGetExtent()
Definition: boxfunc4.c:2321
BOXA * boxaLinearFit(BOXA *boxas, l_float32 factor, l_int32 debug)
boxaLinearFit()
Definition: boxfunc4.c:1178
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:715
l_int32 numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:726
l_int32 pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:729
struct Box ** box
Definition: pix.h:497
l_int32 pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1824
Definition: pix.h:480
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:398
BOXA * boxaSmoothSequenceLS(BOXA *boxas, l_float32 factor, l_int32 subflag, l_int32 maxdiff, l_int32 extrapixels, l_int32 debug)
boxaSmoothSequenceLS()
Definition: boxfunc4.c:966
PIX * pixAddSingleTextblock(PIX *pixs, L_BMF *bmf, const char *textstr, l_uint32 val, l_int32 location, l_int32 *poverflow)
pixAddSingleTextblock()
Definition: textops.c:115
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:164
NUMA * boxaMakeWHRatioIndicator(BOXA *boxa, l_float32 ratio, l_int32 relation)
boxaMakeWHRatioIndicator()
Definition: boxfunc4.c:530
l_int32 pixaGetCount(PIXA *pixa)
pixaGetCount()
Definition: pixabasic.c:620
BOXA * boxaFillSequence(BOXA *boxas, l_int32 useflag, l_int32 debug)
boxaFillSequence()
Definition: boxfunc4.c:2179
BOXA * boxaSmoothSequenceMedian(BOXA *boxas, l_int32 halfwin, l_int32 subflag, l_int32 maxdiff, l_int32 extrapixels, l_int32 debug)
boxaSmoothSequenceMedian()
Definition: boxfunc4.c:1072
BOX * ptaConvertToBox(PTA *pta)
ptaConvertToBox()
Definition: boxfunc4.c:904
Definition: pix.h:517
l_int32 boxaReplaceBox(BOXA *boxa, l_int32 index, BOX *box)
boxaReplaceBox()
Definition: boxbasic.c:942
BOXA * boxaaGetBoxa(BOXAA *baa, l_int32 index, l_int32 accessflag)
boxaaGetBoxa()
Definition: boxbasic.c:1485
l_int32 boxaSplitEvenOdd(BOXA *boxa, l_int32 fillflag, BOXA **pboxae, BOXA **pboxao)
boxaSplitEvenOdd()
Definition: boxfunc1.c:2411
BOXA * boxaSortByIndex(BOXA *boxas, NUMA *naindex)
boxaSortByIndex()
Definition: boxfunc2.c:761
L_BMF * bmfCreate(const char *dir, l_int32 fontsize)
bmfCreate()
Definition: bmf.c:114