/*!
 *  pixaCreateFromPix()
 *
 *      Input:  pixs  (with individual components on a lattice)
 *              n   (number of components)
 *              cellw   (width of each cell)
 *              cellh   (height of each cell)
 *      Return: pixa, or null on error
 *
 *  Note: for bpp = 1, we truncate each retrieved pix to
 *        the ON pixels, which we assume for now start at (0,0)
 */
PIXA *
pixaCreateFromPix(PIX     *pixs,
                  l_int32  n,
                  l_int32  cellw,
                  l_int32  cellh)
{
l_int32  w, h, d, nw, nh, i, j, index;
PIX     *pix, *pixt;
PIXA    *pixa;

    PROCNAME("pixaCreateFromPix");

    if (!pixs)
        return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL);
    if (n <= 0)
        return (PIXA *)ERROR_PTR("n must be > 0", procName, NULL);

    if ((pixa = pixaCreate(n)) == NULL)
        return (PIXA *)ERROR_PTR("pixa not made", procName, NULL);
    pixGetDimensions(pixs, &w, &h, &d);
    if ((pixt = pixCreate(cellw, cellh, d)) == NULL)
        return (PIXA *)ERROR_PTR("pixt not made", procName, NULL);

    nw = (w + cellw - 1) / cellw;
    nh = (h + cellh - 1) / cellh;
    for (i = 0, index = 0; i < nh; i++) {
        for (j = 0; j < nw && index < n; j++, index++) {
            pixRasterop(pixt, 0, 0, cellw, cellh, PIX_SRC, pixs,
                   j * cellw, i * cellh);
            if (d == 1 && !pixClipToForeground(pixt, &pix, NULL))
                pixaAddPix(pixa, pix, L_INSERT);
            else
                pixaAddPix(pixa, pixt, L_COPY);
        }
    }

    pixDestroy(&pixt);
    return pixa;
}
std::vector<Figure> extractFigures(PIX *original, PageRegions &pageRegions,
                                   DocumentStatistics &docStats, bool verbose,
                                   bool showSteps,
                                   std::vector<Figure> &errors) {
  BOXA *bodytext = pageRegions.bodytext;
  BOXA *graphics = pageRegions.graphics;
  BOXA *captions = pageRegions.getCaptionsBoxa();
  std::vector<Caption> unassigned_captions = pageRegions.captions;
  int total_captions = captions->n;

  PIXA *steps = showSteps ? pixaCreate(4) : NULL;

  // Add bodyText boxes to fill up the margin
  BOX *margin;
  BOX *foreground;
  pixClipToForeground(original, NULL, &foreground);
  BOX *extent;
  boxaGetExtent(graphics, NULL, NULL, &extent);
  margin = boxBoundingRegion(extent, foreground);
  boxDestroy(&extent);
  boxaGetExtent(bodytext, NULL, NULL, &extent);
  margin = boxBoundingRegion(margin, extent);
  boxDestroy(&extent);
  boxaGetExtent(pageRegions.other, NULL, NULL, &extent);
  margin = boxBoundingRegion(margin, extent);
  int x = margin->x - 2, y = margin->y - 2, h = margin->h + 4,
      w = margin->w + 4;
  x = std::max(x, 0);
  y = std::max(y, 0);
  h = std::min((int)original->h, h);
  w = std::min((int)original->w, w);
  boxDestroy(&margin);
  boxaAddBox(bodytext, boxCreate(0, 0, original->w, y), L_CLONE);
  boxaAddBox(bodytext, boxCreate(0, y + h, original->w, original->h - y - h),
             L_CLONE);
  boxaAddBox(bodytext, boxCreate(0, 0, x, original->h), L_CLONE);
  boxaAddBox(bodytext, boxCreate(x + w, 0, original->w - x - w, original->h),
             L_CLONE);

  // Add captions to body text
  boxaJoin(bodytext, captions, 0, captions->n);

  if (showSteps)
    pixaAddPix(steps, original, L_CLONE);

  // Generate proposed regions for each caption box
  double center = original->w / 2.0;
  BOXAA *allProposals = boxaaCreate(captions->n);
  BOXA *claimedImages = boxaCreate(captions->n);
  for (int i = 0; i < captions->n; i++) {
    BOX *captBox = boxaGetBox(captions, i, L_CLONE);
    BOXA *proposals = boxaCreate(4);
    for (int j = 0; j < bodytext->n; j++) {
      BOX *txtBox = boxaGetBox(bodytext, j, L_CLONE);
      BOX *proposal = NULL;
      int tolerance = 2;
      int horizontal = 0;
      int vertical = 0;

      boxAlignment(captBox, txtBox, tolerance, &horizontal, &vertical);
      if (vertical * horizontal != 0 or (vertical == 0 and horizontal == 0)) {
        continue;
      }

      if (vertical == 0) {
        if (horizontal == 1) {
          proposal = boxRelocateOneSide(NULL, captBox,
                                        txtBox->x + txtBox->w + 2, L_FROM_LEFT);
        } else if (horizontal == -1) {
          proposal =
              boxRelocateOneSide(NULL, captBox, txtBox->x - 2, L_FROM_RIGHT);
        }
        boxExpandUD(proposal, bodytext);
        if (horizontal == -1) {
          proposal->w -= captBox->w + 1;
          proposal->x = captBox->x + captBox->w + 1;
        } else if (horizontal == 1) {
          proposal->w -= captBox->w + 1;
        }
      } else {
        if (vertical == 1) {
          proposal = boxRelocateOneSide(NULL, captBox,
                                        txtBox->y + txtBox->h + 3, L_FROM_TOP);
        } else if (vertical == -1) {
          proposal =
              boxRelocateOneSide(NULL, captBox, txtBox->y - 3, L_FROM_BOT);
        }
        boxExpandLR(proposal, bodytext);
        if (vertical == -1) {
          proposal->h -= captBox->h + 1;
          proposal->y = captBox->y + captBox->h + 1;
        } else if (vertical == 1) {
          proposal->h -= captBox->h + 1;
        }
      }

      // For two columns document, captions that do not
      // cross the center should not have regions pass the center
      if (docStats.documentIsTwoColumn()) {
        if (captBox->x + captBox->w <= center and
            proposal->x + proposal->w > center) {
          boxRelocateOneSide(proposal, proposal, center - 1, L_FROM_RIGHT);
        } else if (captBox->x >= center and proposal->x < center) {
          boxRelocateOneSide(proposal, proposal, center + 1, L_FROM_LEFT);
        }
      }

      BOX *clippedProposal;
      pixClipBoxToForeground(original, proposal, NULL, &clippedProposal);
      if (clippedProposal != NULL and
          scoreBox(clippedProposal, pageRegions.captions.at(i).type, bodytext,
                   graphics, claimedImages, original) > 0) {
        boxaAddBox(proposals, clippedProposal, L_CLONE);
      }
    }

    if (proposals->n > 0) {
      boxaaAddBoxa(allProposals, proposals, L_CLONE);
    } else {
      // Give up on this caption
      int on_caption = i - (total_captions - unassigned_captions.size());
      errors.push_back(Figure(unassigned_captions.at(on_caption), NULL));
      unassigned_captions.erase(unassigned_captions.begin() + on_caption);
    }
  }
  std::vector<Figure> figures = std::vector<Figure>();
  if (unassigned_captions.size() == 0) {
    return figures;
  }

  // Now go through every possible assignment of captions
  // to proposals pick the highest scorign one
  int numConfigurations = 1;
  for (int i = 0; i < allProposals->n; ++i) {
    numConfigurations *= allProposals->boxa[i]->n;
  }

  if (verbose)
    printf("Found %d possible configurations\n", numConfigurations);

  BOXA *bestProposals = NULL;
  std::vector<bool> bestKeep;
  int bestFound = -1;
  double bestScore = -1;
  for (int onConfig = 0; onConfig < numConfigurations; ++onConfig) {

    // Gather the proposed regions based on the configuration number
    int configNum = onConfig;
    BOXA *proposals = boxaCreate(allProposals->n);
    std::vector<bool> keep;
    for (int i = 0; i < allProposals->n; ++i) {
      int numProposals = allProposals->boxa[i]->n;
      int selected = configNum % numProposals;
      configNum = configNum / numProposals;
      boxaAddBox(proposals, allProposals->boxa[i]->box[selected], L_COPY);
    }

    // Attempt to split any overlapping regions
    for (int i = 0; i < proposals->n; ++i) {
      for (int j = i; j < proposals->n; ++j) {
        BOX *p1 = proposals->box[i];
        BOX *p2 = proposals->box[j];
        int eq;
        boxEqual(p1, p2, &eq);
        if (not eq)
          continue;
        int vertical, horizontal;
        boxAlignment(unassigned_captions.at(i).boundingBox,
                     unassigned_captions.at(j).boundingBox, 2, &horizontal,
                     &vertical);
        if (vertical == 0 or horizontal != 0)
          continue;

        double split = splitBoxVertical(original, p1);
        if (split > 0) {
          BOX *topClipped;
          BOX *botClipped;
          BOX *top = boxRelocateOneSide(NULL, p1, split - 1, L_FROM_BOT);
          pixClipBoxToForeground(original, top, NULL, &topClipped);
          BOX *bot = boxRelocateOneSide(NULL, p1, split + 1, L_FROM_TOP);
          pixClipBoxToForeground(original, bot, NULL, &botClipped);
          if (vertical == -1) {
            proposals->box[i] = topClipped;
            proposals->box[j] = botClipped;
          } else {
            proposals->box[i] = botClipped;
            proposals->box[j] = topClipped;
          }
          if (verbose)
            printf("Split a region vertically\n");
        }
      }
    }

    if (showSteps) {
      pixaAddPix(steps, pixDrawBoxa(original, proposals, 4, 0xff000000),
                 L_CLONE);
    }

    // Score the proposals
    int numFound = 0;
    double totalScore = 0;
    for (int i = 0; i < proposals->n; ++i) {
      double score =
          scoreBox(proposals->box[i], pageRegions.captions.at(i).type, bodytext,
                   graphics, proposals, original);
      totalScore += score;
      if (score > 0) {
        numFound += 1;
        keep.push_back(true);
      } else {
        keep.push_back(false);
      }
    }

    // Switch in for the current best needed
    if (numFound > bestFound or
        (numFound == bestFound and totalScore > bestScore)) {
      bestFound = numFound;
      bestScore = totalScore;
      bestProposals = proposals;
      bestKeep = keep;
    }
  }

  if (showSteps) {
    BOX *clip;
    PIXA *show = pixaCreate(4);
    pixClipBoxToForeground(original, NULL, NULL, &clip);
    int pad = 10;
    clip->x -= 10;
    clip->y -= 10;
    clip->w += pad * 2;
    clip->h += pad * 2;
    for (int i = 0; i < steps->n; ++i) {
      pixaAddPix(show, pixClipRectangle(steps->pix[i], clip, NULL), L_CLONE);
    }
    pixDisplay(pixaDisplayTiled(pixaConvertTo32(show), 4000, 1, 30), 0, 0);
  }

  for (int i = 0; i < bestProposals->n; ++i) {
    if (bestKeep.at(i)) {
      BOX *imageBox = bestProposals->box[i];
      int pad = 2;
      imageBox->x -= pad;
      imageBox->y -= pad;
      imageBox->w += pad * 2;
      imageBox->h += pad * 2;
      figures.push_back(Figure(unassigned_captions.at(i), imageBox));
    } else {
      errors.push_back(Figure(unassigned_captions.at(i), NULL));
    }
  }
  return figures;
}
main(int    argc,
     char **argv)
{
#if HAVE_FMEMOPEN
char          psname[256];
#endif  /* HAVE_FMEMOPEN */
char         *tempname;
l_uint8      *data;
l_int32       i, d, n, success, failure, same;
l_int32       w, h, bps, spp;
size_t        size, nbytes;
PIX          *pix1, *pix2, *pix4, *pix8, *pix16, *pix32;
PIX          *pix, *pixt, *pixd;
PIXA         *pixa;
L_REGPARAMS  *rp;

#if  !HAVE_LIBJPEG
    fprintf(stderr, "Omitting libjpeg tests in ioformats_reg\n");
#endif  /* !HAVE_LIBJPEG */

#if  !HAVE_LIBTIFF
    fprintf(stderr, "Omitting libtiff tests in ioformats_reg\n");
#endif  /* !HAVE_LIBTIFF */

#if  !HAVE_LIBPNG || !HAVE_LIBZ
    fprintf(stderr, "Omitting libpng tests in ioformats_reg\n");
#endif  /* !HAVE_LIBPNG || !HAVE_LIBZ */

    if (regTestSetup(argc, argv, &rp))
        return 1;

    /* --------- Part 1: Test all lossless formats for r/w to file ---------*/

    failure = FALSE;
    success = TRUE;
    fprintf(stderr, "Test bmp 1 bpp file:\n");
    if (ioFormatTest(BMP_FILE)) success = FALSE;


#if  HAVE_LIBTIFF
    fprintf(stderr, "\nTest other 1 bpp file:\n");
    if (ioFormatTest(FILE_1BPP)) success = FALSE;
#endif  /* HAVE_LIBTIFF */

#if  HAVE_LIBPNG
    fprintf(stderr, "\nTest 2 bpp file:\n");
    if (ioFormatTest(FILE_2BPP)) success = FALSE;
    fprintf(stderr, "\nTest 2 bpp file with cmap:\n");
    if (ioFormatTest(FILE_2BPP_C)) success = FALSE;
    fprintf(stderr, "\nTest 4 bpp file:\n");
    if (ioFormatTest(FILE_4BPP)) success = FALSE;
    fprintf(stderr, "\nTest 4 bpp file with cmap:\n");
    if (ioFormatTest(FILE_4BPP_C)) success = FALSE;
    fprintf(stderr, "\nTest 8 bpp grayscale file with cmap:\n");
    if (ioFormatTest(FILE_8BPP_1)) success = FALSE;
    fprintf(stderr, "\nTest 8 bpp color file with cmap:\n");
    if (ioFormatTest(FILE_8BPP_2)) success = FALSE;
#endif  /* HAVE_LIBPNG */

#if  HAVE_LIBJPEG
    fprintf(stderr, "\nTest 8 bpp file without cmap:\n");
    if (ioFormatTest(FILE_8BPP_3)) success = FALSE;
#endif  /* HAVE_LIBJPEG */

#if  HAVE_LIBTIFF
    fprintf(stderr, "\nTest 16 bpp file:\n");
    if (ioFormatTest(FILE_16BPP)) success = FALSE;
#endif  /* HAVE_LIBTIFF */

#if  HAVE_LIBJPEG
    fprintf(stderr, "\nTest 32 bpp file:\n");
    if (ioFormatTest(FILE_32BPP)) success = FALSE;
#endif  /* HAVE_LIBJPEG */

    if (success)
        fprintf(stderr,
            "\n  ********** Success on all i/o format tests *********\n");
    else
        fprintf(stderr,
            "\n  ******* Failure on at least one i/o format test ******\n");
    if (!success) failure = TRUE;


    /* ------------------ Part 2: Test tiff r/w to file ------------------- */
#if  !HAVE_LIBTIFF
    goto part6;
#endif  /* !HAVE_LIBTIFF */

    fprintf(stderr, "\nTest tiff r/w and format extraction\n");
    pixa = pixaCreate(6);
    pix1 = pixRead(BMP_FILE);
    pix2 = pixConvert1To2(NULL, pix1, 3, 0);
    pix4 = pixConvert1To4(NULL, pix1, 15, 0);
    pix16 = pixRead(FILE_16BPP);
    fprintf(stderr, "Input format: %d\n", pixGetInputFormat(pix16));
    pix8 = pixConvert16To8(pix16, 1);
    pix32 = pixRead(FILE_32BPP);
    pixaAddPix(pixa, pix1, L_INSERT);
    pixaAddPix(pixa, pix2, L_INSERT);
    pixaAddPix(pixa, pix4, L_INSERT);
    pixaAddPix(pixa, pix8, L_INSERT);
    pixaAddPix(pixa, pix16, L_INSERT);
    pixaAddPix(pixa, pix32, L_INSERT);
    n = pixaGetCount(pixa);

    success = TRUE;
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixa, i, L_CLONE);
	d = pixGetDepth(pix);
        fprintf(stderr, "%d bpp\n", d);
	if (i == 0) {   /* 1 bpp */
            pixWrite("/tmp/junkg3.tif", pix, IFF_TIFF_G3);
            pixWrite("/tmp/junkg4.tif", pix, IFF_TIFF_G4);
            pixWrite("/tmp/junkrle.tif", pix, IFF_TIFF_RLE);
            pixWrite("/tmp/junkpb.tif", pix, IFF_TIFF_PACKBITS);
	    if (testcomp("/tmp/junkg3.tif", pix, IFF_TIFF_G3)) success = FALSE;
	    if (testcomp("/tmp/junkg4.tif", pix, IFF_TIFF_G4)) success = FALSE;
	    if (testcomp("/tmp/junkrle.tif", pix, IFF_TIFF_RLE))
                success = FALSE;
	    if (testcomp("/tmp/junkpb.tif", pix, IFF_TIFF_PACKBITS))
                success = FALSE;
	}
        pixWrite("/tmp/junklzw.tif", pix, IFF_TIFF_LZW);
        pixWrite("/tmp/junkzip.tif", pix, IFF_TIFF_ZIP);
        pixWrite("/tmp/junknon.tif", pix, IFF_TIFF);
        if (testcomp("/tmp/junklzw.tif", pix, IFF_TIFF_LZW)) success = FALSE;
        if (testcomp("/tmp/junkzip.tif", pix, IFF_TIFF_ZIP)) success = FALSE;
        if (testcomp("/tmp/junknon.tif", pix, IFF_TIFF)) success = FALSE;
	pixDestroy(&pix);
    }
    if (success)
        fprintf(stderr,
            "\n  ********** Success on tiff r/w to file *********\n\n");
    else
        fprintf(stderr,
            "\n  ******* Failure on at least one tiff r/w to file ******\n\n");
    if (!success) failure = TRUE;

    /* ------------------ Part 3: Test tiff r/w to memory ----------------- */

    success = TRUE;
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixa, i, L_CLONE);
	d = pixGetDepth(pix);
        fprintf(stderr, "%d bpp\n", d);
	if (i == 0) {   /* 1 bpp */
            pixWriteMemTiff(&data, &size, pix, IFF_TIFF_G3);
            nbytes = nbytesInFile("/tmp/junkg3.tif");
            fprintf(stderr, "nbytes = %ld, size = %ld\n", nbytes, size);
            pixt = pixReadMemTiff(data, size, 0);
            if (testcomp_mem(pix, &pixt, i, IFF_TIFF_G3)) success = FALSE;
            lept_free(data);
            pixWriteMemTiff(&data, &size, pix, IFF_TIFF_G4);
            nbytes = nbytesInFile("/tmp/junkg4.tif");
            fprintf(stderr, "nbytes = %ld, size = %ld\n", nbytes, size);
            pixt = pixReadMemTiff(data, size, 0);
            if (testcomp_mem(pix, &pixt, i, IFF_TIFF_G4)) success = FALSE;
            readHeaderMemTiff(data, size, 0, &w, &h, &bps, &spp,
                              NULL, NULL, NULL);
            fprintf(stderr, "(w,h,bps,spp) = (%d,%d,%d,%d)\n", w, h, bps, spp);
            lept_free(data);
            pixWriteMemTiff(&data, &size, pix, IFF_TIFF_RLE);
            nbytes = nbytesInFile("/tmp/junkrle.tif");
            fprintf(stderr, "nbytes = %ld, size = %ld\n", nbytes, size);
            pixt = pixReadMemTiff(data, size, 0);
            if (testcomp_mem(pix, &pixt, i, IFF_TIFF_RLE)) success = FALSE;
            lept_free(data);
            pixWriteMemTiff(&data, &size, pix, IFF_TIFF_PACKBITS);
            nbytes = nbytesInFile("/tmp/junkpb.tif");
            fprintf(stderr, "nbytes = %ld, size = %ld\n", nbytes, size);
            pixt = pixReadMemTiff(data, size, 0);
            if (testcomp_mem(pix, &pixt, i, IFF_TIFF_PACKBITS)) success = FALSE;
            lept_free(data);
	}
        pixWriteMemTiff(&data, &size, pix, IFF_TIFF_LZW);
        pixt = pixReadMemTiff(data, size, 0);
        if (testcomp_mem(pix, &pixt, i, IFF_TIFF_LZW)) success = FALSE;
        lept_free(data);
        pixWriteMemTiff(&data, &size, pix, IFF_TIFF_ZIP);
        pixt = pixReadMemTiff(data, size, 0);
        if (testcomp_mem(pix, &pixt, i, IFF_TIFF_ZIP)) success = FALSE;
        readHeaderMemTiff(data, size, 0, &w, &h, &bps, &spp, NULL, NULL, NULL);
        fprintf(stderr, "(w,h,bps,spp) = (%d,%d,%d,%d)\n", w, h, bps, spp);
        lept_free(data);
        pixWriteMemTiff(&data, &size, pix, IFF_TIFF);
        pixt = pixReadMemTiff(data, size, 0);
        if (testcomp_mem(pix, &pixt, i, IFF_TIFF)) success = FALSE;
        lept_free(data);
        pixDestroy(&pix);
    }
    if (success)
        fprintf(stderr,
            "\n  ********** Success on tiff r/w to memory *********\n\n");
    else
        fprintf(stderr,
            "\n  ******* Failure on at least one tiff r/w to memory ******\n\n");
    if (!success) failure = TRUE;


    /* ---------------- Part 4: Test non-tiff r/w to memory ---------------- */

#if HAVE_FMEMOPEN
    pixDisplayWrite(NULL, -1);
    success = TRUE;
    for (i = 0; i < n; i++) {
        pix = pixaGetPix(pixa, i, L_CLONE);
	d = pixGetDepth(pix);
        sprintf(psname, "/tmp/junkps.%d", d);
        fprintf(stderr, "%d bpp\n", d);
        if (d != 16) {
            if (test_writemem(pix, IFF_PNG, NULL)) success = FALSE;
            if (test_writemem(pix, IFF_BMP, NULL)) success = FALSE;
        }
        if (test_writemem(pix, IFF_PNM, NULL)) success = FALSE;
        if (test_writemem(pix, IFF_PS, psname)) success = FALSE;
	if (d == 8 || d == 32)
            if (test_writemem(pix, IFF_JFIF_JPEG, NULL)) success = FALSE;
        pixDestroy(&pix);
    }
    if (success)
        fprintf(stderr,
            "\n  ********** Success on non-tiff r/w to memory *********\n\n");
    else
        fprintf(stderr,
            "\n  **** Failure on at least one non-tiff r/w to memory *****\n\n");
    if (!success) failure = TRUE;
#else
        fprintf(stderr,
            "\n  ***** Non-tiff r/w to memory not enabled *****\n\n");
#endif  /*  HAVE_FMEMOPEN  */

    pixaDestroy(&pixa);

    /* ------------ Part 5: Test multipage tiff r/w to memory ------------ */

        /* Make a multipage tiff file, and read it back into memory */
    success = TRUE;
    pix = pixRead("feyn.tif");
    pixa = pixaSplitPix(pix, 3, 3, 0, 0);
    for (i = 0; i < 9; i++) {
        pixt = pixaGetPix(pixa, i, L_CLONE);
        if (i == 0)
            pixWriteTiff("/tmp/junktiffmpage.tif", pixt, IFF_TIFF_G4, "w");
        else
            pixWriteTiff("/tmp/junktiffmpage.tif", pixt, IFF_TIFF_G4, "a");
        pixDestroy(&pixt);
    }
    data = l_binaryRead("/tmp/junktiffmpage.tif", &nbytes);
    pixaDestroy(&pixa);

        /* Read the individual pages from memory to a pix */
    pixa = pixaCreate(9);
    for (i = 0; i < 9; i++) {
        pixt = pixReadMemTiff(data, nbytes, i);
        pixaAddPix(pixa, pixt, L_INSERT);
    }
    lept_free(data);

        /* Un-tile the pix in the pixa back to the original image */
    pixt = pixaDisplayUnsplit(pixa, 3, 3, 0, 0);
    pixaDestroy(&pixa);

        /* Clip to foreground to remove any extra rows or columns */
    pixClipToForeground(pix, &pix1, NULL);
    pixClipToForeground(pixt, &pix2, NULL);
    pixEqual(pix1, pix2, &same); 
    if (same)
        fprintf(stderr,
            "\n  ******* Success on tiff multipage read from memory ******\n\n");
    else
        fprintf(stderr,
            "\n  ******* Failure on tiff multipage read from memory ******\n\n");
    if (!same) failure = TRUE;

    pixDestroy(&pix);
    pixDestroy(&pixt);
    pixDestroy(&pix1);
    pixDestroy(&pix2);

    /* ------------ Part 6: Test 24 bpp writing ------------ */
#if  !HAVE_LIBTIFF
part6:
#endif  /* !HAVE_LIBTIFF */

#if  !HAVE_LIBPNG || !HAVE_LIBJPEG || !HAVE_LIBTIFF
    goto finish;
#endif  /* !HAVE_LIBPNG || !HAVE_LIBJPEG || !HAVE_LIBTIFF */

        /* Generate a 24 bpp (not 32 bpp !!) rgb pix and write it out */
    success = TRUE;
    pix = pixRead("marge.jpg");
    pixt = make_24_bpp_pix(pix);
    pixWrite("/tmp/junk24.png", pixt, IFF_PNG);
    pixWrite("/tmp/junk24.jpg", pixt, IFF_JFIF_JPEG);
    pixWrite("/tmp/junk24.tif", pixt, IFF_TIFF);
    pixd = pixRead("/tmp/junk24.png");
    pixEqual(pix, pixd, &same);
    if (!same) success = FALSE;
    pixDestroy(&pixd);
    pixd = pixRead("/tmp/junk24.jpg");
    regTestCompareSimilarPix(rp, pix, pixd, 10, 0.0002, 0);
    pixDestroy(&pixd);
    pixd = pixRead("/tmp/junk24.tif");
    pixEqual(pix, pixd, &same);
    if (!same) success = FALSE;
    pixDestroy(&pixd);
    if (success)
        fprintf(stderr,
            "\n  ******* Success on 24 bpp rgb writing *******\n\n");
    else
        fprintf(stderr,
            "\n  ******* Failure on 24 bpp rgb writing *******\n\n");
    if (!success) failure = TRUE;
    pixDestroy(&pix);
    pixDestroy(&pixt);

    /* -------------- Part 7: Read header information -------------- */
    success = TRUE;
    if (get_header_data(FILE_1BPP, IFF_TIFF_G4)) success = FALSE;
    if (get_header_data(FILE_2BPP, IFF_PNG)) success = FALSE;
    if (get_header_data(FILE_2BPP_C, IFF_PNG)) success = FALSE;
    if (get_header_data(FILE_4BPP, IFF_PNG)) success = FALSE;
    if (get_header_data(FILE_4BPP_C, IFF_PNG)) success = FALSE;
    if (get_header_data(FILE_8BPP_1, IFF_PNG)) success = FALSE;
    if (get_header_data(FILE_8BPP_2, IFF_PNG)) success = FALSE;
    if (get_header_data(FILE_8BPP_3, IFF_JFIF_JPEG)) success = FALSE;
    if (get_header_data(FILE_16BPP, IFF_TIFF_ZIP)) success = FALSE;
    if (get_header_data(FILE_32BPP, IFF_JFIF_JPEG)) success = FALSE;

#if HAVE_FMEMOPEN
    pix = pixRead(FILE_8BPP_1);
    tempname = genTempFilename((const char *)"/tmp", (const char *)".pnm",
                               1, 1);
    pixWrite(tempname, pix, IFF_PNM);
    if (get_header_data(tempname, IFF_PNM)) success = FALSE;
    pixDestroy(&pix);
    lept_free(tempname);
#endif  /* HAVE_FMEMOPEN */

    pix = pixRead(FILE_1BPP);
    tempname = genTempFilename((const char *)"/tmp", (const char *)".tif",
                               1, 1);
    pixWrite(tempname, pix, IFF_TIFF_G3);
    if (get_header_data(tempname, IFF_TIFF_G3)) success = FALSE;
    pixWrite(tempname, pix, IFF_TIFF_G4);
    if (get_header_data(tempname, IFF_TIFF_G4)) success = FALSE;
    pixWrite(tempname, pix, IFF_TIFF_PACKBITS);
    if (get_header_data(tempname, IFF_TIFF_PACKBITS)) success = FALSE;
    pixWrite(tempname, pix, IFF_TIFF_RLE);
    if (get_header_data(tempname, IFF_TIFF_RLE)) success = FALSE;
    pixWrite(tempname, pix, IFF_TIFF_LZW);
    if (get_header_data(tempname, IFF_TIFF_LZW)) success = FALSE;
    pixWrite(tempname, pix, IFF_TIFF_ZIP);
    if (get_header_data(tempname, IFF_TIFF_ZIP)) success = FALSE;
    pixWrite(tempname, pix, IFF_TIFF);
    if (get_header_data(tempname, IFF_TIFF)) success = FALSE;
    pixDestroy(&pix);
    lept_free(tempname);

    if (success)
        fprintf(stderr,
            "\n  ******* Success on reading headers *******\n\n");
    else
        fprintf(stderr,
            "\n  ******* Failure on reading headers *******\n\n");
    if (!success) failure = TRUE;

#if  !HAVE_LIBPNG || !HAVE_LIBJPEG || !HAVE_LIBTIFF
finish:
#endif  /* !HAVE_LIBPNG || !HAVE_LIBJPEG || !HAVE_LIBTIFF */

    if (!failure)
        fprintf(stderr,
            "  ******* Success on all tests *******\n\n");
    else
        fprintf(stderr,
            "  ******* Failure on at least one test *******\n\n");

    return regTestCleanup(rp);
}
Exemple #4
0
/*!
 *  pixGenerateSelBoundary()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              hitdist (min distance from fg boundary pixel)
 *              missdist (min distance from bg boundary pixel)
 *              hitskip (number of boundary pixels skipped between hits)
 *              missskip (number of boundary pixels skipped between misses)
 *              topflag (flag for extra pixels of bg added above)
 *              botflag (flag for extra pixels of bg added below)
 *              leftflag (flag for extra pixels of bg added to left)
 *              rightflag (flag for extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) All fg elements selected are exactly hitdist pixels away from
 *        the nearest fg boundary pixel, and ditto for bg elements.
 *        Valid inputs of hitdist and missdist are 0, 1, 2, 3 and 4.
 *        For example, a hitdist of 0 puts the hits at the fg boundary.
 *        Usually, the distances should be > 0 avoid the effect of
 *        noise at the boundary.
 *    (2) Set hitskip < 0 if no hits are to be used.  Ditto for missskip.
 *        If both hitskip and missskip are < 0, the sel would be empty,
 *        and NULL is returned.
 *    (3) The 4 flags determine whether the sel is increased on that side
 *        to allow bg misses to be placed all along that boundary.
 *        The increase in sel size on that side is the minimum necessary
 *        to allow the misses to be placed at mindist.  For text characters,
 *        the topflag and botflag are typically set to 1, and the leftflag
 *        and rightflag to 0.
 *    (4) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 *    (5) This is probably the best of the three sel generators, in the
 *        sense that you have the most flexibility with the smallest number
 *        of hits and misses.
 */
SEL *
pixGenerateSelBoundary(PIX     *pixs,
                       l_int32  hitdist,
                       l_int32  missdist,
                       l_int32  hitskip,
                       l_int32  missskip,
                       l_int32  topflag,
                       l_int32  botflag,
                       l_int32  leftflag,
                       l_int32  rightflag,
                       PIX      **ppixe)
{
l_int32  ws, hs, w, h, x, y, ix, iy, i, npt;
PIX     *pixt1, *pixt2, *pixt3, *pixfg, *pixbg;
SEL     *selh, *selm, *sel_3, *sel;
PTA     *ptah, *ptam;

    PROCNAME("pixGenerateSelBoundary");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (hitdist < 0 || hitdist > 4 || missdist < 0 || missdist > 4)
        return (SEL *)ERROR_PTR("dist not in {0 .. 4}", procName, NULL);
    if (hitskip < 0 && missskip < 0)
        return (SEL *)ERROR_PTR("no hits or misses", procName, NULL);

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (topflag || botflag || leftflag || rightflag) {
        x = y = 0;
        if (topflag) {
            h += missdist + 1;
            y = missdist + 1;
        }
        if (botflag)
            h += missdist + 1;
        if (leftflag) {
            w += missdist + 1;
            x = missdist + 1;
        }
        if (rightflag)
            w += missdist + 1;
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else {
        pixt2 = pixClone(pixt1);
    }
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are exactly hitdist and
         * missdist (rsp) away from the boundary pixels in their set.
         * Then get a subsampled set of these points. */
    sel_3 = selCreateBrick(3, 3, 1, 1, SEL_HIT);
    if (hitskip >= 0) {
        selh = selCreateBrick(2 * hitdist + 1, 2 * hitdist + 1,
                              hitdist, hitdist, SEL_HIT);
        pixt3 = pixErode(NULL, pixt2, selh);
        pixfg = pixErode(NULL, pixt3, sel_3);
        pixXor(pixfg, pixfg, pixt3);
        ptah = pixSubsampleBoundaryPixels(pixfg, hitskip);
        pixDestroy(&pixt3);
        pixDestroy(&pixfg);
        selDestroy(&selh);
    }
    if (missskip >= 0) {
        selm = selCreateBrick(2 * missdist + 1, 2 * missdist + 1,
                              missdist, missdist, SEL_HIT);
        pixt3 = pixDilate(NULL, pixt2, selm);
        pixbg = pixDilate(NULL, pixt3, sel_3);
        pixXor(pixbg, pixbg, pixt3);
        ptam = pixSubsampleBoundaryPixels(pixbg, missskip);
        pixDestroy(&pixt3);
        pixDestroy(&pixbg);
        selDestroy(&selm);
    }
    selDestroy(&sel_3);
    pixDestroy(&pixt2);

        /* Generate the hit-miss sel from these point */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    if (hitskip >= 0) {
        npt = ptaGetCount(ptah);
        for (i = 0; i < npt; i++) {
            ptaGetIPt(ptah, i, &ix, &iy);
            selSetElement(sel, iy, ix, SEL_HIT);
        }
    }
    if (missskip >= 0) {
        npt = ptaGetCount(ptam);
        for (i = 0; i < npt; i++) {
            ptaGetIPt(ptam, i, &ix, &iy);
            selSetElement(sel, iy, ix, SEL_MISS);
        }
    }

    ptaDestroy(&ptah);
    ptaDestroy(&ptam);
    return sel;
}
Exemple #5
0
/*!
 *  pixGenerateSelRandom()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              hitfract (fraction of allowable fg pixels that are hits)
 *              missfract (fraction of allowable bg pixels that are misses)
 *              distance (min distance from boundary pixel; use 0 for default)
 *              toppix (number of extra pixels of bg added above)
 *              botpix (number of extra pixels of bg added below)
 *              leftpix (number of extra pixels of bg added to left)
 *              rightpix (number of extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) Either of hitfract and missfract can be zero.  If both are zero,
 *        the sel would be empty, and NULL is returned.
 *    (2) No elements are selected that are less than 'distance' pixels away
 *        from a boundary pixel of the same color.  This makes the
 *        match much more robust to edge noise.  Valid inputs of
 *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
 *        greater than 4, we reset it to the default value.
 *    (3) The 4 numbers for adding rectangles of pixels outside the fg
 *        can be use if the pattern is expected to be surrounded by bg
 *        (white) pixels.  On the other hand, if the pattern may be near
 *        other fg (black) components on some sides, use 0 for those sides.
 *    (4) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 */
SEL *
pixGenerateSelRandom(PIX       *pixs,
                     l_float32  hitfract,
                     l_float32  missfract,
                     l_int32    distance,
                     l_int32    toppix,
                     l_int32    botpix,
                     l_int32    leftpix,
                     l_int32    rightpix,
                     PIX      **ppixe)
{
l_int32    ws, hs, w, h, x, y, i, j, thresh;
l_uint32   val;
PIX       *pixt1, *pixt2, *pixfg, *pixbg;
SEL       *seld, *sel;

    PROCNAME("pixGenerateSelRandom");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (hitfract <= 0.0 && missfract <= 0.0)
        return (SEL *)ERROR_PTR("no hits or misses", procName, NULL);
    if (hitfract > 1.0 || missfract > 1.0)
        return (SEL *)ERROR_PTR("fraction can't be > 1.0", procName, NULL);

    if (distance <= 0)
        distance = DEFAULT_DISTANCE_TO_BOUNDARY;
    if (distance > MAX_DISTANCE_TO_BOUNDARY) {
        L_WARNING("distance too large; setting to max value", procName);
        distance = MAX_DISTANCE_TO_BOUNDARY;
    }

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (toppix || botpix || leftpix || rightpix) {
        x = y = 0;
        if (toppix) {
            h += toppix;
            y = toppix;
        }
        if (botpix)
            h += botpix;
        if (leftpix) {
            w += leftpix;
            x = leftpix;
        }
        if (rightpix)
            w += rightpix;
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else
        pixt2 = pixClone(pixt1);
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are at least 'distance' pixels
         * away from the boundary pixels in their set */
    seld = selCreateBrick(2 * distance + 1, 2 * distance + 1,
                          distance, distance, SEL_HIT);
    pixfg = pixErode(NULL, pixt2, seld);
    pixbg = pixDilate(NULL, pixt2, seld);
    pixInvert(pixbg, pixbg);
    selDestroy(&seld);
    pixDestroy(&pixt2);

        /* Generate the sel from a random selection of these points */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    if (hitfract > 0.0) {
        thresh = (l_int32)(hitfract * (l_float64)RAND_MAX);
        for (i = 0; i < h; i++) {
            for (j = 0; j < w; j++) {
                pixGetPixel(pixfg, j, i, &val);
                if (val) {
                    if (rand() < thresh)
                        selSetElement(sel, i, j, SEL_HIT);
                }
            }
        }
    }
    if (missfract > 0.0) {
        thresh = (l_int32)(missfract * (l_float64)RAND_MAX);
        for (i = 0; i < h; i++) {
            for (j = 0; j < w; j++) {
                pixGetPixel(pixbg, j, i, &val);
                if (val) {
                    if (rand() < thresh)
                        selSetElement(sel, i, j, SEL_MISS);
                }
            }
        }
    }

    pixDestroy(&pixfg);
    pixDestroy(&pixbg);
    return sel;
}
Exemple #6
0
/*!
 *  pixGenerateSelWithRuns()
 *
 *      Input:  pix (1 bpp, typically small, to be used as a pattern)
 *              nhlines (number of hor lines along which elements are found)
 *              nvlines (number of vert lines along which elements are found)
 *              distance (min distance from boundary pixel; use 0 for default)
 *              minlength (min runlength to set hit or miss; use 0 for default)
 *              toppix (number of extra pixels of bg added above)
 *              botpix (number of extra pixels of bg added below)
 *              leftpix (number of extra pixels of bg added to left)
 *              rightpix (number of extra pixels of bg added to right)
 *              &pixe (<optional return> input pix expanded by extra pixels)
 *      Return: sel (hit-miss for input pattern), or null on error
 *
 *  Notes:
 *    (1) The horizontal and vertical lines along which elements are
 *        selected are roughly equally spaced.  The actual locations of
 *        the hits and misses are the centers of respective run-lengths.
 *    (2) No elements are selected that are less than 'distance' pixels away
 *        from a boundary pixel of the same color.  This makes the
 *        match much more robust to edge noise.  Valid inputs of
 *        'distance' are 0, 1, 2, 3 and 4.  If distance is either 0 or
 *        greater than 4, we reset it to the default value.
 *    (3) The 4 numbers for adding rectangles of pixels outside the fg
 *        can be use if the pattern is expected to be surrounded by bg
 *        (white) pixels.  On the other hand, if the pattern may be near
 *        other fg (black) components on some sides, use 0 for those sides.
 *    (4) The pixels added to a side allow you to have miss elements there.
 *        There is a constraint between distance, minlength, and
 *        the added pixels for this to work.  We illustrate using the
 *        default values.  If you add 5 pixels to the top, and use a
 *        distance of 1, then you end up with a vertical run of at least
 *        4 bg pixels along the top edge of the image.  If you use a
 *        minimum runlength of 3, each vertical line will always find
 *        a miss near the center of its run.  However, if you use a
 *        minimum runlength of 5, you will not get a miss on every vertical
 *        line.  As another example, if you have 7 added pixels and a
 *        distance of 2, you can use a runlength up to 5 to guarantee
 *        that the miss element is recorded.  We give a warning if the
 *        contraint does not guarantee a miss element outside the
 *        image proper.
 *    (5) The input pix, as extended by the extra pixels on selected sides,
 *        can optionally be returned.  For debugging, call
 *        pixDisplayHitMissSel() to visualize the hit-miss sel superimposed
 *        on the generating bitmap.
 */
SEL *
pixGenerateSelWithRuns(PIX     *pixs,
                       l_int32  nhlines,
                       l_int32  nvlines,
                       l_int32  distance,
                       l_int32  minlength,
                       l_int32  toppix,
                       l_int32  botpix,
                       l_int32  leftpix,
                       l_int32  rightpix,
                       PIX    **ppixe)
{
l_int32    ws, hs, w, h, x, y, xval, yval, i, j, nh, nm;
l_float32  delh, delw;
NUMA      *nah, *nam;
PIX       *pixt1, *pixt2, *pixfg, *pixbg;
PTA       *ptah, *ptam;
SEL       *seld, *sel;

    PROCNAME("pixGenerateSelWithRuns");

    if (ppixe) *ppixe = NULL;
    if (!pixs)
        return (SEL *)ERROR_PTR("pixs not defined", procName, NULL);
    if (pixGetDepth(pixs) != 1)
        return (SEL *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
    if (nhlines < 1 && nvlines < 1)
        return (SEL *)ERROR_PTR("nvlines and nhlines both < 1", procName, NULL);

    if (distance <= 0)
        distance = DEFAULT_DISTANCE_TO_BOUNDARY;
    if (minlength <= 0)
        minlength = DEFAULT_MIN_RUNLENGTH;
    if (distance > MAX_DISTANCE_TO_BOUNDARY) {
        L_WARNING("distance too large; setting to max value", procName);
        distance = MAX_DISTANCE_TO_BOUNDARY;
    }

        /* Locate the foreground */
    pixClipToForeground(pixs, &pixt1, NULL);
    if (!pixt1)
        return (SEL *)ERROR_PTR("pixt1 not made", procName, NULL);
    ws = pixGetWidth(pixt1);
    hs = pixGetHeight(pixt1);
    w = ws;
    h = hs;

        /* Crop out a region including the foreground, and add pixels
         * on sides depending on the side flags */
    if (toppix || botpix || leftpix || rightpix) {
        x = y = 0;
        if (toppix) {
            h += toppix;
            y = toppix;
            if (toppix < distance + minlength)
                L_WARNING("no miss elements in added top pixels", procName);
        }
        if (botpix) {
            h += botpix;
            if (botpix < distance + minlength)
                L_WARNING("no miss elements in added bot pixels", procName);
        }
        if (leftpix) {
            w += leftpix;
            x = leftpix;
            if (leftpix < distance + minlength)
                L_WARNING("no miss elements in added left pixels", procName);
        }
        if (rightpix) {
            w += rightpix;
            if (rightpix < distance + minlength)
                L_WARNING("no miss elements in added right pixels", procName);
        }
        pixt2 = pixCreate(w, h, 1);
        pixRasterop(pixt2, x, y, ws, hs, PIX_SRC, pixt1, 0, 0);
    }
    else
        pixt2 = pixClone(pixt1);
    if (ppixe)
        *ppixe = pixClone(pixt2);
    pixDestroy(&pixt1);

        /* Identify fg and bg pixels that are at least 'distance' pixels
         * away from the boundary pixels in their set */
    seld = selCreateBrick(2 * distance + 1, 2 * distance + 1,
                          distance, distance, SEL_HIT);
    pixfg = pixErode(NULL, pixt2, seld);
    pixbg = pixDilate(NULL, pixt2, seld);
    pixInvert(pixbg, pixbg);
    selDestroy(&seld);
    pixDestroy(&pixt2);

        /* Accumulate hit and miss points */
    ptah = ptaCreate(0);
    ptam = ptaCreate(0);
    if (nhlines >= 1) {
        delh = (l_float32)h / (l_float32)(nhlines + 1);
        for (i = 0, y = 0; i < nhlines; i++) {
            y += (l_int32)(delh + 0.5);
            nah = pixGetRunCentersOnLine(pixfg, -1, y, minlength);
            nam = pixGetRunCentersOnLine(pixbg, -1, y, minlength);
            nh = numaGetCount(nah);
            nm = numaGetCount(nam);
            for (j = 0; j < nh; j++) {
                numaGetIValue(nah, j, &xval);
                ptaAddPt(ptah, xval, y);
            }
            for (j = 0; j < nm; j++) {
                numaGetIValue(nam, j, &xval);
                ptaAddPt(ptam, xval, y);
            }
            numaDestroy(&nah);
            numaDestroy(&nam);
        }
    }
    if (nvlines >= 1) {
        delw = (l_float32)w / (l_float32)(nvlines + 1);
        for (i = 0, x = 0; i < nvlines; i++) {
            x += (l_int32)(delw + 0.5);
            nah = pixGetRunCentersOnLine(pixfg, x, -1, minlength);
            nam = pixGetRunCentersOnLine(pixbg, x, -1, minlength);
            nh = numaGetCount(nah);
            nm = numaGetCount(nam);
            for (j = 0; j < nh; j++) {
                numaGetIValue(nah, j, &yval);
                ptaAddPt(ptah, x, yval);
            }
            for (j = 0; j < nm; j++) {
                numaGetIValue(nam, j, &yval);
                ptaAddPt(ptam, x, yval);
            }
            numaDestroy(&nah);
            numaDestroy(&nam);
        }
    }

        /* Make the Sel with those points */
    sel = selCreateBrick(h, w, h / 2, w / 2, SEL_DONT_CARE);
    nh = ptaGetCount(ptah);
    for (i = 0; i < nh; i++) {
        ptaGetIPt(ptah, i, &x, &y);
        selSetElement(sel, y, x, SEL_HIT);
    }
    nm = ptaGetCount(ptam);
    for (i = 0; i < nm; i++) {
        ptaGetIPt(ptam, i, &x, &y);
        selSetElement(sel, y, x, SEL_MISS);
    }

    pixDestroy(&pixfg);
    pixDestroy(&pixbg);
    ptaDestroy(&ptah);
    ptaDestroy(&ptam);
    return sel;
}
Exemple #7
0
/*!
 *  pixFindPageForeground()
 *
 *      Input:  pixs (full resolution (any type or depth)
 *              threshold (for binarization; typically about 128)
 *              mindist (min distance of text from border to allow
 *                       cleaning near border; at 2x reduction, this
 *                       should be larger than 50; typically about 70)
 *              erasedist (when conditions are satisfied, erase anything
 *                         within this distance of the edge;
 *                         typically 30 at 2x reduction)
 *              pagenum (use for debugging when called repeatedly; labels
 *                       debug images that are assembled into pdfdir)
 *              showmorph (set to a negative integer to show steps in
 *                         generating masks; this is typically used
 *                         for debugging region extraction)
 *              display (set to 1  to display mask and selected region
 *                       for debugging a single page)
 *              pdfdir (subdirectory of /tmp where images showing the
 *                      result are placed when called repeatedly; use
 *                      null if no output requested)
 *      Return: box (region including foreground, with some pixel noise
 *                   removed), or null if not found
 *
 *  Notes:
 *      (1) This doesn't simply crop to the fg.  It attempts to remove
 *          pixel noise and junk at the edge of the image before cropping.
 *          The input @threshold is used if pixs is not 1 bpp.
 *      (2) There are several debugging options, determined by the
 *          last 4 arguments.
 *      (3) If you want pdf output of results when called repeatedly,
 *          the pagenum arg labels the images written, which go into
 *          /tmp/<pdfdir>/<pagenum>.png.  In that case,
 *          you would clean out the /tmp directory before calling this
 *          function on each page:
 *              lept_rmdir(pdfdir);
 *              lept_mkdir(pdfdir);
 */
BOX *
pixFindPageForeground(PIX         *pixs,
                      l_int32      threshold,
                      l_int32      mindist,
                      l_int32      erasedist,
                      l_int32      pagenum,
                      l_int32      showmorph,
                      l_int32      display,
                      const char  *pdfdir)
{
char     buf[64];
l_int32  flag, nbox, intersects;
l_int32  w, h, bx, by, bw, bh, left, right, top, bottom;
PIX     *pixb, *pixb2, *pixseed, *pixsf, *pixm, *pix1, *pixg2;
BOX     *box, *boxfg, *boxin, *boxd;
BOXA    *ba1, *ba2;

    PROCNAME("pixFindPageForeground");

    if (!pixs)
        return (BOX *)ERROR_PTR("pixs not defined", procName, NULL);

        /* Binarize, downscale by 0.5, remove the noise to generate a seed,
         * and do a seedfill back from the seed into those 8-connected
         * components of the binarized image for which there was at least
         * one seed pixel.  Also clear out any components that are within
         * 10 pixels of the edge at 2x reduction. */
    flag = (showmorph) ? -1 : 0;  /* if showmorph == -1, write intermediate
                                   * images to /tmp/seq_output_1.pdf */
    pixb = pixConvertTo1(pixs, threshold);
    pixb2 = pixScale(pixb, 0.5, 0.5);
    pixseed = pixMorphSequence(pixb2, "o1.2 + c9.9 + o3.5", flag);
    pixsf = pixSeedfillBinary(NULL, pixseed, pixb2, 8);
    pixSetOrClearBorder(pixsf, 10, 10, 10, 10, PIX_SET);
    pixm = pixRemoveBorderConnComps(pixsf, 8);
    if (display) pixDisplay(pixm, 100, 100);

        /* Now, where is the main block of text?  We want to remove noise near
         * the edge of the image, but to do that, we have to be convinced that
         * (1) there is noise and (2) it is far enough from the text block
         * and close enough to the edge.  For each edge, if the block
         * is more than mindist from that edge, then clean 'erasedist'
         * pixels from the edge. */
    pix1 = pixMorphSequence(pixm, "c50.50", flag - 1);
    ba1 = pixConnComp(pix1, NULL, 8);
    ba2 = boxaSort(ba1, L_SORT_BY_AREA, L_SORT_DECREASING, NULL);
    pixGetDimensions(pix1, &w, &h, NULL);
    nbox = boxaGetCount(ba2);
    if (nbox > 1) {
        box = boxaGetBox(ba2, 0, L_CLONE);
        boxGetGeometry(box, &bx, &by, &bw, &bh);
        left = (bx > mindist) ? erasedist : 0;
        right = (w - bx - bw > mindist) ? erasedist : 0;
        top = (by > mindist) ? erasedist : 0;
        bottom = (h - by - bh > mindist) ? erasedist : 0;
        pixSetOrClearBorder(pixm, left, right, top, bottom, PIX_CLR);
        boxDestroy(&box);
    }
    pixDestroy(&pix1);
    boxaDestroy(&ba1);
    boxaDestroy(&ba2);

        /* Locate the foreground region; don't bother cropping */
    pixClipToForeground(pixm, NULL, &boxfg);

        /* Sanity check the fg region.  Make sure it's not confined
         * to a thin boundary on the left and right sides of the image,
         * in which case it is likely to be noise. */
    if (boxfg) {
        boxin = boxCreate(0.1 * w, 0, 0.8 * w, h);
        boxIntersects(boxfg, boxin, &intersects);
        if (!intersects) {
            L_INFO("found only noise on page %d\n", procName, pagenum);
            boxDestroy(&boxfg);
        }
        boxDestroy(&boxin);
    }

    boxd = NULL;
    if (!boxfg) {
        L_INFO("no fg region found for page %d\n", procName, pagenum);
    } else {
        boxAdjustSides(boxfg, boxfg, -2, 2, -2, 2);  /* tiny expansion */
        boxd = boxTransform(boxfg, 0, 0, 2.0, 2.0);

            /* Write image showing box for this page.  This is to be
             * bundled up into a pdf of all the pages, which can be
             * generated by convertFilesToPdf()  */
        if (pdfdir) {
            pixg2 = pixConvert1To4Cmap(pixb);
            pixRenderBoxArb(pixg2, boxd, 3, 255, 0, 0);
            snprintf(buf, sizeof(buf), "/tmp/%s/%05d.png", pdfdir, pagenum);
            if (display) pixDisplay(pixg2, 700, 100);
            pixWrite(buf, pixg2, IFF_PNG);
            pixDestroy(&pixg2);
        }
    }

    pixDestroy(&pixb);
    pixDestroy(&pixb2);
    pixDestroy(&pixseed);
    pixDestroy(&pixsf);
    pixDestroy(&pixm);
    boxDestroy(&boxfg);
    return boxd;
}
Exemple #8
0
/* ----------------------------------------------------- */
void ProcessDigits(l_int32  index)
{
char       rootname[8] = "digit5";
char       buf[64];
l_int32    i, nc, ns, same;
NUMA      *na1;
PIX       *pix1, *pix2, *pix3, *pix4, *pix5, *pix6;
PIXA      *pixa1, *pixa2, *pixa3;

        /* Read the unfiltered, unscaled pixa of twenty-five 5s */
    snprintf(buf, sizeof(buf), "digits/%s.orig-25.pa", rootname);
    pixa1 = pixaRead(buf);

        /* Number and show the input images */
    snprintf(buf, sizeof(buf), "/tmp/lept/digit/%s.orig-num", rootname);
    PixaDisplayNumbered(pixa1, buf);

        /* Remove some of them */
    na1 = numaCreateFromString(removeset);
    pixaRemoveSelected(pixa1, na1);
    numaDestroy(&na1);
    snprintf(buf, sizeof(buf), "/tmp/lept/digit/%s.filt.pa", rootname);
    pixaWrite(buf, pixa1);

        /* Number and show the filtered images */
    snprintf(buf, sizeof(buf), "/tmp/lept/digit/%s.filt-num", rootname);
    PixaDisplayNumbered(pixa1, buf);

        /* Extract the largest c.c., clip to the foreground,
         * and scale the result to a fixed size. */
    nc = pixaGetCount(pixa1);
    pixa2 = pixaCreate(nc);
    for (i = 0; i < nc; i++) {
        pix1 = pixaGetPix(pixa1, i, L_CLONE);
            /* A threshold of 140 gives reasonable results */
        pix2 = pixThresholdToBinary(pix1, 140);
            /* Join nearly touching pieces */
        pix3 = pixCloseSafeBrick(NULL, pix2, 5, 5);
            /* Take the largest (by area) connected component */
        pix4 = pixFilterComponentBySize(pix3, 0, L_SELECT_BY_AREA, 8, NULL);
            /* Extract the original 1 bpp pixels that have been
             * covered by the closing operation */
        pixAnd(pix4, pix4, pix2);
            /* Grab the result as an image with no surrounding whitespace */
        pixClipToForeground(pix4, &pix5, NULL);
            /* Rescale the result to the canonical size */
        pix6 = pixScaleToSize(pix5, 20, 30);
        pixaAddPix(pixa2, pix6, L_INSERT);
        pixDestroy(&pix1);
        pixDestroy(&pix2);
        pixDestroy(&pix3);
        pixDestroy(&pix4);
        pixDestroy(&pix5);
    }

        /* Add the index (a "5") in the text field of each pix; save pixa2 */
    snprintf(buf, sizeof(buf), "%d", index);
    for (i = 0; i < nc; i++) {
        pix1 = pixaGetPix(pixa2, i, L_CLONE);
        pixSetText(pix1, buf);
        pixDestroy(&pix1);
    }
    snprintf(buf, sizeof(buf), "/tmp/lept/digit/%s.comp.pa", rootname);
    pixaWrite(buf, pixa2);

        /* Number and show the resulting binary templates */
    snprintf(buf, sizeof(buf), "/tmp/lept/digit/%s.comp-num", rootname);
    PixaDisplayNumbered(pixa2, buf);

        /* Save the binary templates as a packed tiling (tiff g4).
         * This is the most efficient way to represent the templates. */
    pix1 = pixaDisplayOnLattice(pixa2, 20, 30, NULL, NULL);
    pixDisplay(pix1, 1000, 500);
    snprintf(buf, sizeof(buf), "/tmp/lept/digit/%s.comp.tif", rootname);
    pixWrite(buf, pix1, IFF_TIFF_G4);

        /* The number of templates is in the pix text string; check it. */
    pix2 = pixRead(buf);
    if (sscanf(pixGetText(pix2), "n = %d", &ns) != 1)
        fprintf(stderr, "Failed to read the number of templates!\n");
    if (ns != nc)
        fprintf(stderr, "(stored = %d) != (actual number = %d)\n", ns, nc);

        /* Reconstruct the pixa of templates from the tiled compressed
         * image, and verify that the resulting pixa is the same.  */
    pixa3 = pixaMakeFromTiledPix(pix1, 20, 30, 0, 0, NULL);
    pixaEqual(pixa2, pixa3, 0, NULL, &same);
    if (!same)
        fprintf(stderr, "Pixa are not the same!\n");

    pixDestroy(&pix1);
    pixDestroy(&pix2);
    pixaDestroy(&pixa1);
    pixaDestroy(&pixa2);
    pixaDestroy(&pixa3);
}