Leptonica  1.73
Image processing and image analysis suite
flipdetect.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 
173 #include <math.h>
174 #include "allheaders.h"
175 
176  /* Sels for pixOrientDetect() and pixMirrorDetect() */
177 static const char *textsel1 = "x oo "
178  "x oOo "
179  "x o "
180  "x "
181  "xxxxxx";
182 
183 static const char *textsel2 = " oo x"
184  " oOo x"
185  " o x"
186  " x"
187  "xxxxxx";
188 
189 static const char *textsel3 = "xxxxxx"
190  "x "
191  "x o "
192  "x oOo "
193  "x oo ";
194 
195 static const char *textsel4 = "xxxxxx"
196  " x"
197  " o x"
198  " oOo x"
199  " oo x";
200 
201  /* Parameters for determining orientation */
202 static const l_int32 DEFAULT_MIN_UP_DOWN_COUNT = 70;
203 static const l_float32 DEFAULT_MIN_UP_DOWN_CONF = 8.0;
204 static const l_float32 DEFAULT_MIN_UP_DOWN_RATIO = 2.5;
205 
206  /* Parameters for determining mirror flip */
207 static const l_int32 DEFAULT_MIN_MIRROR_FLIP_COUNT = 100;
208 static const l_float32 DEFAULT_MIN_MIRROR_FLIP_CONF = 5.0;
209 
210  /* Static debug function */
211 static void pixDebugFlipDetect(const char *filename, PIX *pixs,
212  PIX *pixhm, l_int32 enable);
213 
214 
215 /*----------------------------------------------------------------*
216  * High-level interface for detection and correction *
217  *----------------------------------------------------------------*/
241 PIX *
243  l_float32 minupconf,
244  l_float32 minratio,
245  l_float32 *pupconf,
246  l_float32 *pleftconf,
247  l_int32 *protation,
248  l_int32 debug)
249 {
250 l_int32 orient;
251 l_float32 upconf, leftconf;
252 PIX *pix1;
253 
254  PROCNAME("pixOrientCorrect");
255 
256  if (!pixs || pixGetDepth(pixs) != 1)
257  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
258 
259  lept_mkdir("lept/orient");
260 
261  /* Get confidences for orientation */
262  pixUpDownDetectDwa(pixs, &upconf, 0, debug);
263  pix1 = pixRotate90(pixs, 1);
264  pixUpDownDetectDwa(pix1, &leftconf, 0, debug);
265  pixDestroy(&pix1);
266  if (pupconf) *pupconf = upconf;
267  if (pleftconf) *pleftconf = leftconf;
268 
269  /* Decide what to do */
270  makeOrientDecision(upconf,leftconf, minupconf, minratio, &orient, debug);
271 
272  /* Do it */
273  switch (orient)
274  {
276  L_INFO("text orientation not determined; no rotation\n", procName);
277  if (protation) *protation = 0;
278  return pixCopy(NULL, pixs);
279  break;
280  case L_TEXT_ORIENT_UP:
281  L_INFO("text is oriented up; no rotation\n", procName);
282  if (protation) *protation = 0;
283  return pixCopy(NULL, pixs);
284  break;
285  case L_TEXT_ORIENT_LEFT:
286  L_INFO("landscape; text oriented left; 90 cw rotation\n", procName);
287  if (protation) *protation = 90;
288  return pixRotateOrth(pixs, 1);
289  break;
290  case L_TEXT_ORIENT_DOWN:
291  L_INFO("text oriented down; 180 cw rotation\n", procName);
292  if (protation) *protation = 180;
293  return pixRotateOrth(pixs, 2);
294  break;
295  case L_TEXT_ORIENT_RIGHT:
296  L_INFO("landscape; text oriented right; 270 cw rotation\n", procName);
297  if (protation) *protation = 270;
298  return pixRotateOrth(pixs, 3);
299  break;
300  default:
301  L_ERROR("invalid orient flag!\n", procName);
302  return pixCopy(NULL, pixs);
303  }
304 }
305 
306 
307 /*----------------------------------------------------------------*
308  * Orientation detection (four 90 degree angles) *
309  * Rasterop implementation *
310  *----------------------------------------------------------------*/
369 l_int32
371  l_float32 *pupconf,
372  l_float32 *pleftconf,
373  l_int32 mincount,
374  l_int32 debug)
375 {
376 PIX *pix1;
377 
378  PROCNAME("pixOrientDetect");
379 
380  if (!pixs || pixGetDepth(pixs) != 1)
381  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
382  if (!pupconf && !pleftconf)
383  return ERROR_INT("nothing to do", procName, 1);
384  if (mincount == 0)
385  mincount = DEFAULT_MIN_UP_DOWN_COUNT;
386 
387  lept_mkdir("lept/orient");
388 
389  if (pupconf)
390  pixUpDownDetect(pixs, pupconf, mincount, debug);
391  if (pleftconf) {
392  pix1 = pixRotate90(pixs, 1);
393  pixUpDownDetect(pix1, pleftconf, mincount, debug);
394  pixDestroy(&pix1);
395  }
396 
397  return 0;
398 }
399 
400 
430 l_int32
431 makeOrientDecision(l_float32 upconf,
432  l_float32 leftconf,
433  l_float32 minupconf,
434  l_float32 minratio,
435  l_int32 *porient,
436  l_int32 debug)
437 {
438 l_float32 absupconf, absleftconf;
439 
440  PROCNAME("makeOrientDecision");
441 
442  if (!porient)
443  return ERROR_INT("&orient not defined", procName, 1);
444  *porient = L_TEXT_ORIENT_UNKNOWN; /* default: no decision */
445  if (upconf == 0.0 || leftconf == 0.0) {
446  L_INFO("not enough confidence to get orientation\n", procName);
447  return 0;
448  }
449 
450  lept_mkdir("lept/orient");
451 
452  if (minupconf == 0.0)
453  minupconf = DEFAULT_MIN_UP_DOWN_CONF;
454  if (minratio == 0.0)
455  minratio = DEFAULT_MIN_UP_DOWN_RATIO;
456  absupconf = L_ABS(upconf);
457  absleftconf = L_ABS(leftconf);
458 
459  /* Here are the four possible orientation decisions, based
460  * on satisfaction of two threshold constraints. */
461  if (upconf > minupconf && absupconf > minratio * absleftconf)
462  *porient = L_TEXT_ORIENT_UP;
463  else if (leftconf > minupconf && absleftconf > minratio * absupconf)
464  *porient = L_TEXT_ORIENT_LEFT;
465  else if (upconf < -minupconf && absupconf > minratio * absleftconf)
466  *porient = L_TEXT_ORIENT_DOWN;
467  else if (leftconf < -minupconf && absleftconf > minratio * absupconf)
468  *porient = L_TEXT_ORIENT_RIGHT;
469 
470  if (debug) {
471  fprintf(stderr, "upconf = %7.3f, leftconf = %7.3f\n", upconf, leftconf);
472  if (*porient == L_TEXT_ORIENT_UNKNOWN)
473  fprintf(stderr, "Confidence is low; no determination is made\n");
474  else if (*porient == L_TEXT_ORIENT_UP)
475  fprintf(stderr, "Text is rightside-up\n");
476  else if (*porient == L_TEXT_ORIENT_LEFT)
477  fprintf(stderr, "Text is rotated 90 deg ccw\n");
478  else if (*porient == L_TEXT_ORIENT_DOWN)
479  fprintf(stderr, "Text is upside-down\n");
480  else /* *porient == L_TEXT_ORIENT_RIGHT */
481  fprintf(stderr, "Text is rotated 90 deg cw\n");
482  }
483 
484  return 0;
485 }
486 
487 
509 l_int32
511  l_float32 *pconf,
512  l_int32 mincount,
513  l_int32 debug)
514 {
515  return pixUpDownDetectGeneral(pixs, pconf, mincount, 0, debug);
516 }
517 
518 
557 l_int32
559  l_float32 *pconf,
560  l_int32 mincount,
561  l_int32 npixels,
562  l_int32 debug)
563 {
564 l_int32 countup, countdown, nmax;
565 l_float32 nup, ndown;
566 PIX *pix0, *pix1, *pix2, *pix3, *pixm;
567 SEL *sel1, *sel2, *sel3, *sel4;
568 
569  PROCNAME("pixUpDownDetectGeneral");
570 
571  if (!pconf)
572  return ERROR_INT("&conf not defined", procName, 1);
573  *pconf = 0.0;
574  if (!pixs || pixGetDepth(pixs) != 1)
575  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
576  if (mincount == 0)
577  mincount = DEFAULT_MIN_UP_DOWN_COUNT;
578  if (npixels < 0)
579  npixels = 0;
580 
581  lept_mkdir("lept/orient");
582 
583  sel1 = selCreateFromString(textsel1, 5, 6, NULL);
584  sel2 = selCreateFromString(textsel2, 5, 6, NULL);
585  sel3 = selCreateFromString(textsel3, 5, 6, NULL);
586  sel4 = selCreateFromString(textsel4, 5, 6, NULL);
587 
588  /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1).
589  * This closes holes in x-height characters and joins them at
590  * the x-height. There is more noise in the descender detection
591  * from this, but it works fairly well. */
592  pix0 = pixMorphCompSequence(pixs, "c1.8 + c30.1", 0);
593 
594  /* Optionally, make a mask of the word bounding boxes, shortening
595  * each of them by a fixed amount at each end. */
596  pixm = NULL;
597  if (npixels > 0) {
598  l_int32 i, nbox, x, y, w, h;
599  BOX *box;
600  BOXA *boxa;
601  pix1 = pixMorphSequence(pix0, "o10.1", 0);
602  boxa = pixConnComp(pix1, NULL, 8);
603  pixm = pixCreateTemplate(pix1);
604  pixDestroy(&pix1);
605  nbox = boxaGetCount(boxa);
606  for (i = 0; i < nbox; i++) {
607  box = boxaGetBox(boxa, i, L_CLONE);
608  boxGetGeometry(box, &x, &y, &w, &h);
609  if (w > 2 * npixels)
610  pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13,
611  PIX_SET, NULL, 0, 0);
612  boxDestroy(&box);
613  }
614  boxaDestroy(&boxa);
615  }
616 
617  /* Find the ascenders and optionally filter with pixm.
618  * For an explanation of the procedure used for counting the result
619  * of the HMT, see comments at the beginning of this function. */
620  pix1 = pixHMT(NULL, pix0, sel1);
621  pix2 = pixHMT(NULL, pix0, sel2);
622  pixOr(pix1, pix1, pix2);
623  if (pixm)
624  pixAnd(pix1, pix1, pixm);
625  pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
626  pixCountPixels(pix3, &countup, NULL);
627  pixDebugFlipDetect("/tmp/lept/orient/up.png", pixs, pix1, debug);
628  pixDestroy(&pix1);
629  pixDestroy(&pix2);
630  pixDestroy(&pix3);
631 
632  /* Find the ascenders and optionally filter with pixm. */
633  pix1 = pixHMT(NULL, pix0, sel3);
634  pix2 = pixHMT(NULL, pix0, sel4);
635  pixOr(pix1, pix1, pix2);
636  if (pixm)
637  pixAnd(pix1, pix1, pixm);
638  pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
639  pixCountPixels(pix3, &countdown, NULL);
640  pixDebugFlipDetect("/tmp/lept/orient/down.png", pixs, pix1, debug);
641  pixDestroy(&pix1);
642  pixDestroy(&pix2);
643  pixDestroy(&pix3);
644 
645  /* Evaluate statistically, generating a confidence that is
646  * related to the probability with a gaussian distribution. */
647  nup = (l_float32)(countup);
648  ndown = (l_float32)(countdown);
649  nmax = L_MAX(countup, countdown);
650  if (nmax > mincount)
651  *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown));
652 
653  if (debug) {
654  if (pixm) pixWrite("/tmp/lept/orient/pixm1.png", pixm, IFF_PNG);
655  fprintf(stderr, "nup = %7.3f, ndown = %7.3f, conf = %7.3f\n",
656  nup, ndown, *pconf);
657  if (*pconf > DEFAULT_MIN_UP_DOWN_CONF)
658  fprintf(stderr, "Text is rightside-up\n");
659  if (*pconf < -DEFAULT_MIN_UP_DOWN_CONF)
660  fprintf(stderr, "Text is upside-down\n");
661  }
662 
663  pixDestroy(&pix0);
664  pixDestroy(&pixm);
665  selDestroy(&sel1);
666  selDestroy(&sel2);
667  selDestroy(&sel3);
668  selDestroy(&sel4);
669  return 0;
670 }
671 
672 
673 /*----------------------------------------------------------------*
674  * Orientation detection (four 90 degree angles) *
675  * DWA implementation *
676  *----------------------------------------------------------------*/
698 l_int32
700  l_float32 *pupconf,
701  l_float32 *pleftconf,
702  l_int32 mincount,
703  l_int32 debug)
704 {
705 PIX *pix1;
706 
707  PROCNAME("pixOrientDetectDwa");
708 
709  if (!pixs || pixGetDepth(pixs) != 1)
710  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
711  if (!pupconf && !pleftconf)
712  return ERROR_INT("nothing to do", procName, 1);
713  if (mincount == 0)
714  mincount = DEFAULT_MIN_UP_DOWN_COUNT;
715 
716  if (pupconf)
717  pixUpDownDetectDwa(pixs, pupconf, mincount, debug);
718  if (pleftconf) {
719  pix1 = pixRotate90(pixs, 1);
720  pixUpDownDetectDwa(pix1, pleftconf, mincount, debug);
721  pixDestroy(&pix1);
722  }
723 
724  return 0;
725 }
726 
727 
751 l_int32
753  l_float32 *pconf,
754  l_int32 mincount,
755  l_int32 debug)
756 {
757  return pixUpDownDetectGeneralDwa(pixs, pconf, mincount, 0, debug);
758 }
759 
760 
776 l_int32
778  l_float32 *pconf,
779  l_int32 mincount,
780  l_int32 npixels,
781  l_int32 debug)
782 {
783 char flipsel1[] = "flipsel1";
784 char flipsel2[] = "flipsel2";
785 char flipsel3[] = "flipsel3";
786 char flipsel4[] = "flipsel4";
787 l_int32 countup, countdown, nmax;
788 l_float32 nup, ndown;
789 PIX *pixt, *pix0, *pix1, *pix2, *pix3, *pixm;
790 
791  PROCNAME("pixUpDownDetectGeneralDwa");
792 
793  if (!pconf)
794  return ERROR_INT("&conf not defined", procName, 1);
795  *pconf = 0.0;
796  if (!pixs || pixGetDepth(pixs) != 1)
797  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
798  if (mincount == 0)
799  mincount = DEFAULT_MIN_UP_DOWN_COUNT;
800  if (npixels < 0)
801  npixels = 0;
802 
803  lept_mkdir("lept/orient");
804 
805  /* One of many reasonable pre-filtering sequences: (1, 8) and (30, 1).
806  * This closes holes in x-height characters and joins them at
807  * the x-height. There is more noise in the descender detection
808  * from this, but it works fairly well. */
809  pixt = pixMorphSequenceDwa(pixs, "c1.8 + c30.1", 0);
810 
811  /* Be sure to add the border before the flip DWA operations! */
814  pixDestroy(&pixt);
815 
816  /* Optionally, make a mask of the word bounding boxes, shortening
817  * each of them by a fixed amount at each end. */
818  pixm = NULL;
819  if (npixels > 0) {
820  l_int32 i, nbox, x, y, w, h;
821  BOX *box;
822  BOXA *boxa;
823  pix1 = pixMorphSequenceDwa(pix0, "o10.1", 0);
824  boxa = pixConnComp(pix1, NULL, 8);
825  pixm = pixCreateTemplate(pix1);
826  pixDestroy(&pix1);
827  nbox = boxaGetCount(boxa);
828  for (i = 0; i < nbox; i++) {
829  box = boxaGetBox(boxa, i, L_CLONE);
830  boxGetGeometry(box, &x, &y, &w, &h);
831  if (w > 2 * npixels)
832  pixRasterop(pixm, x + npixels, y - 6, w - 2 * npixels, h + 13,
833  PIX_SET, NULL, 0, 0);
834  boxDestroy(&box);
835  }
836  boxaDestroy(&boxa);
837  }
838 
839  /* Find the ascenders and optionally filter with pixm.
840  * For an explanation of the procedure used for counting the result
841  * of the HMT, see comments in pixUpDownDetectGeneral(). */
842  pix1 = pixFlipFHMTGen(NULL, pix0, flipsel1);
843  pix2 = pixFlipFHMTGen(NULL, pix0, flipsel2);
844  pixOr(pix1, pix1, pix2);
845  if (pixm)
846  pixAnd(pix1, pix1, pixm);
847  pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
848  pixCountPixels(pix3, &countup, NULL);
849  pixDestroy(&pix1);
850  pixDestroy(&pix2);
851  pixDestroy(&pix3);
852 
853  /* Find the ascenders and optionally filter with pixm. */
854  pix1 = pixFlipFHMTGen(NULL, pix0, flipsel3);
855  pix2 = pixFlipFHMTGen(NULL, pix0, flipsel4);
856  pixOr(pix1, pix1, pix2);
857  if (pixm)
858  pixAnd(pix1, pix1, pixm);
859  pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
860  pixCountPixels(pix3, &countdown, NULL);
861  pixDestroy(&pix1);
862  pixDestroy(&pix2);
863  pixDestroy(&pix3);
864 
865  /* Evaluate statistically, generating a confidence that is
866  * related to the probability with a gaussian distribution. */
867  nup = (l_float32)(countup);
868  ndown = (l_float32)(countdown);
869  nmax = L_MAX(countup, countdown);
870  if (nmax > mincount)
871  *pconf = 2. * ((nup - ndown) / sqrt(nup + ndown));
872 
873  if (debug) {
874  if (pixm) pixWrite("/tmp/lept/orient/pixm2.png", pixm, IFF_PNG);
875  fprintf(stderr, "nup = %7.3f, ndown = %7.3f, conf = %7.3f\n",
876  nup, ndown, *pconf);
877  if (*pconf > DEFAULT_MIN_UP_DOWN_CONF)
878  fprintf(stderr, "Text is rightside-up\n");
879  if (*pconf < -DEFAULT_MIN_UP_DOWN_CONF)
880  fprintf(stderr, "Text is upside-down\n");
881  }
882 
883  pixDestroy(&pix0);
884  pixDestroy(&pixm);
885  return 0;
886 }
887 
888 
889 
890 /*----------------------------------------------------------------*
891  * Left-right mirror detection *
892  * Rasterop implementation *
893  *----------------------------------------------------------------*/
933 l_int32
935  l_float32 *pconf,
936  l_int32 mincount,
937  l_int32 debug)
938 {
939 l_int32 count1, count2, nmax;
940 l_float32 nleft, nright;
941 PIX *pix0, *pix1, *pix2, *pix3;
942 SEL *sel1, *sel2;
943 
944  PROCNAME("pixMirrorDetect");
945 
946  if (!pconf)
947  return ERROR_INT("&conf not defined", procName, 1);
948  *pconf = 0.0;
949  if (!pixs || pixGetDepth(pixs) != 1)
950  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
951  if (mincount == 0)
952  mincount = DEFAULT_MIN_MIRROR_FLIP_COUNT;
953 
954  sel1 = selCreateFromString(textsel1, 5, 6, NULL);
955  sel2 = selCreateFromString(textsel2, 5, 6, NULL);
956 
957  /* Fill x-height characters but not space between them, sort of. */
958  pix3 = pixMorphCompSequence(pixs, "d1.30", 0);
959  pixXor(pix3, pix3, pixs);
960  pix0 = pixMorphCompSequence(pixs, "c15.1", 0);
961  pixXor(pix0, pix0, pixs);
962  pixAnd(pix0, pix0, pix3);
963  pixOr(pix0, pix0, pixs);
964  pixDestroy(&pix3);
965 
966  /* Filter the right-facing characters. */
967  pix1 = pixHMT(NULL, pix0, sel1);
968  pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
969  pixCountPixels(pix3, &count1, NULL);
970  pixDebugFlipDetect("/tmp/lept/orient/right.png", pixs, pix1, debug);
971  pixDestroy(&pix1);
972  pixDestroy(&pix3);
973 
974  /* Filter the left-facing characters. */
975  pix2 = pixHMT(NULL, pix0, sel2);
976  pix3 = pixReduceRankBinaryCascade(pix2, 1, 1, 0, 0);
977  pixCountPixels(pix3, &count2, NULL);
978  pixDebugFlipDetect("/tmp/lept/orient/left.png", pixs, pix2, debug);
979  pixDestroy(&pix2);
980  pixDestroy(&pix3);
981 
982  nright = (l_float32)count1;
983  nleft = (l_float32)count2;
984  nmax = L_MAX(count1, count2);
985  pixDestroy(&pix0);
986  selDestroy(&sel1);
987  selDestroy(&sel2);
988 
989  if (nmax > mincount)
990  *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft));
991 
992  if (debug) {
993  fprintf(stderr, "nright = %f, nleft = %f\n", nright, nleft);
994  if (*pconf > DEFAULT_MIN_MIRROR_FLIP_CONF)
995  fprintf(stderr, "Text is not mirror reversed\n");
996  if (*pconf < -DEFAULT_MIN_MIRROR_FLIP_CONF)
997  fprintf(stderr, "Text is mirror reversed\n");
998  }
999 
1000  return 0;
1001 }
1002 
1003 
1004 /*----------------------------------------------------------------*
1005  * Left-right mirror detection *
1006  * DWA implementation *
1007  *----------------------------------------------------------------*/
1024 l_int32
1026  l_float32 *pconf,
1027  l_int32 mincount,
1028  l_int32 debug)
1029 {
1030 char flipsel1[] = "flipsel1";
1031 char flipsel2[] = "flipsel2";
1032 l_int32 count1, count2, nmax;
1033 l_float32 nleft, nright;
1034 PIX *pix0, *pix1, *pix2, *pix3;
1035 
1036  PROCNAME("pixMirrorDetectDwa");
1037 
1038  if (!pconf)
1039  return ERROR_INT("&conf not defined", procName, 1);
1040  *pconf = 0.0;
1041  if (!pixs || pixGetDepth(pixs) != 1)
1042  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
1043  if (mincount == 0)
1044  mincount = DEFAULT_MIN_MIRROR_FLIP_COUNT;
1045 
1046  /* Fill x-height characters but not space between them, sort of. */
1047  pix3 = pixMorphSequenceDwa(pixs, "d1.30", 0);
1048  pixXor(pix3, pix3, pixs);
1049  pix0 = pixMorphSequenceDwa(pixs, "c15.1", 0);
1050  pixXor(pix0, pix0, pixs);
1051  pixAnd(pix0, pix0, pix3);
1052  pixOr(pix3, pix0, pixs);
1053  pixDestroy(&pix0);
1056  pixDestroy(&pix3);
1057 
1058  /* Filter the right-facing characters. */
1059  pix1 = pixFlipFHMTGen(NULL, pix0, flipsel1);
1060  pix3 = pixReduceRankBinaryCascade(pix1, 1, 1, 0, 0);
1061  pixCountPixels(pix3, &count1, NULL);
1062  pixDestroy(&pix1);
1063  pixDestroy(&pix3);
1064 
1065  /* Filter the left-facing characters. */
1066  pix2 = pixFlipFHMTGen(NULL, pix0, flipsel2);
1067  pix3 = pixReduceRankBinaryCascade(pix2, 1, 1, 0, 0);
1068  pixCountPixels(pix3, &count2, NULL);
1069  pixDestroy(&pix2);
1070  pixDestroy(&pix3);
1071 
1072  pixDestroy(&pix0);
1073  nright = (l_float32)count1;
1074  nleft = (l_float32)count2;
1075  nmax = L_MAX(count1, count2);
1076 
1077  if (nmax > mincount)
1078  *pconf = 2. * ((nright - nleft) / sqrt(nright + nleft));
1079 
1080  if (debug) {
1081  fprintf(stderr, "nright = %f, nleft = %f\n", nright, nleft);
1082  if (*pconf > DEFAULT_MIN_MIRROR_FLIP_CONF)
1083  fprintf(stderr, "Text is not mirror reversed\n");
1084  if (*pconf < -DEFAULT_MIN_MIRROR_FLIP_CONF)
1085  fprintf(stderr, "Text is mirror reversed\n");
1086  }
1087 
1088  return 0;
1089 }
1090 
1091 
1092 /*----------------------------------------------------------------*
1093  * Static debug helper *
1094  *----------------------------------------------------------------*/
1095 /*
1096  * pixDebugFlipDetect()
1097  *
1098  * Input: filename (for output debug file)
1099  * pixs (input to pix*Detect)
1100  * pixhm (hit-miss result from ascenders or descenders)
1101  * enable (1 to enable this function; 0 to disable)
1102  * Return: void
1103  */
1104 static void
1105 pixDebugFlipDetect(const char *filename,
1106  PIX *pixs,
1107  PIX *pixhm,
1108  l_int32 enable)
1109 {
1110 PIX *pixt, *pixthm;
1111 
1112  if (!enable) return;
1113 
1114  /* Display with red dot at counted locations */
1115  pixt = pixConvert1To4Cmap(pixs);
1116  pixthm = pixMorphSequence(pixhm, "d5.5", 0);
1117  pixSetMaskedCmap(pixt, pixthm, 0, 0, 255, 0, 0);
1118 
1119  pixWrite(filename, pixt, IFF_PNG);
1120  pixDestroy(&pixthm);
1121  pixDestroy(&pixt);
1122  return;
1123 }
l_int32 pixOrientDetect(PIX *pixs, l_float32 *pupconf, l_float32 *pleftconf, l_int32 mincount, l_int32 debug)
pixOrientDetect()
Definition: flipdetect.c:370
static const l_int32 ADDED_BORDER
Definition: morph.h:245
l_int32 makeOrientDecision(l_float32 upconf, l_float32 leftconf, l_float32 minupconf, l_float32 minratio, l_int32 *porient, l_int32 debug)
makeOrientDecision()
Definition: flipdetect.c:431
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:1880
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:577
l_int32 pixMirrorDetectDwa(PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug)
pixMirrorDetectDwa()
Definition: flipdetect.c:1025
l_int32 pixMirrorDetect(PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug)
pixMirrorDetect()
Definition: flipdetect.c:934
PIX * pixCreateTemplate(PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:367
Definition: pix.h:492
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
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:144
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1574
void selDestroy(SEL **psel)
selDestroy()
Definition: sel1.c:337
#define PIX_SET
Definition: pix.h:331
PIX * pixAnd(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixAnd()
Definition: pix3.c:1510
PIX * pixMorphSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequence()
Definition: morphseq.c:133
l_int32 boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:309
l_int32 pixUpDownDetect(PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug)
pixUpDownDetect()
Definition: flipdetect.c:510
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:545
PIX * pixRotateOrth(PIX *pixs, l_int32 quads)
pixRotateOrth()
Definition: rotateorth.c:72
BOX * boxaGetBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetBox()
Definition: boxbasic.c:760
PIX * pixOr(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixOr()
Definition: pix3.c:1446
l_int32 pixUpDownDetectGeneralDwa(PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 npixels, l_int32 debug)
pixUpDownDetectGeneralDwa()
Definition: flipdetect.c:777
l_int32 pixUpDownDetectGeneral(PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 npixels, l_int32 debug)
pixUpDownDetectGeneral()
Definition: flipdetect.c:558
PIX * pixHMT(PIX *pixd, PIX *pixs, SEL *sel)
pixHMT()
Definition: morph.c:338
Definition: pix.h:134
PIX * pixConvert1To4Cmap(PIX *pixs)
pixConvert1To4Cmap()
Definition: pixconv.c:2202
Definition: pix.h:706
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:630
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:277
l_int32 boxaGetCount(BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:715
PIX * pixRotate90(PIX *pixs, l_int32 direction)
pixRotate90()
Definition: rotateorth.c:163
PIX * pixMorphSequenceDwa(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphSequenceDwa()
Definition: morphseq.c:450
l_int32 pixUpDownDetectDwa(PIX *pixs, l_float32 *pconf, l_int32 mincount, l_int32 debug)
pixUpDownDetectDwa()
Definition: flipdetect.c:752
l_int32 pixOrientDetectDwa(PIX *pixs, l_float32 *pupconf, l_float32 *pleftconf, l_int32 mincount, l_int32 debug)
pixOrientDetectDwa()
Definition: flipdetect.c:699
l_int32 pixCountPixels(PIX *pixs, l_int32 *pcount, l_int32 *tab8)
pixCountPixels()
Definition: pix3.c:1824
Definition: pix.h:480
PIX * pixReduceRankBinaryCascade(PIX *pixs, l_int32 level1, l_int32 level2, l_int32 level3, l_int32 level4)
pixReduceRankBinaryCascade()
Definition: binreduce.c:148
PIX * pixOrientCorrect(PIX *pixs, l_float32 minupconf, l_float32 minratio, l_float32 *pupconf, l_float32 *pleftconf, l_int32 *protation, l_int32 debug)
pixOrientCorrect()
Definition: flipdetect.c:242
PIX * pixAddBorderGeneral(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot, l_uint32 val)
pixAddBorderGeneral()
Definition: pix2.c:1830
SEL * selCreateFromString(const char *text, l_int32 h, l_int32 w, const char *name)
selCreateFromString()
Definition: sel1.c:1616
PIX * pixMorphCompSequence(PIX *pixs, const char *sequence, l_int32 dispsep)
pixMorphCompSequence()
Definition: morphseq.c:301
l_int32 pixSetMaskedCmap(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 rval, l_int32 gval, l_int32 bval)
pixSetMaskedCmap()
Definition: paintcmap.c:693