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