/*!
 *  boxaaWriteStream()
 *
 *      Input: stream
 *             boxaa
 *      Return: 0 if OK, 1 on error
 */
l_int32
boxaaWriteStream(FILE   *fp,
                 BOXAA  *baa)
{
l_int32  n, i, x, y, w, h;
BOX     *box;
BOXA    *boxa;

    PROCNAME("boxaaWriteStream");

    if (!fp)
        return ERROR_INT("stream not defined", procName, 1);
    if (!baa)
        return ERROR_INT("baa not defined", procName, 1);

    n = boxaaGetCount(baa);
    fprintf(fp, "\nBoxaa Version %d\n", BOXAA_VERSION_NUMBER);
    fprintf(fp, "Number of boxa = %d\n", n);

    for (i = 0; i < n; i++) {
        if ((boxa = boxaaGetBoxa(baa, i, L_CLONE)) == NULL)
            return ERROR_INT("boxa not found", procName, 1);
        boxaGetExtent(boxa, NULL, NULL, &box);
        boxGetGeometry(box, &x, &y, &w, &h);
        fprintf(fp, "\nBoxa[%d] extent: x = %d, y = %d, w = %d, h = %d",
                i, x, y, w, h);
        boxaWriteStream(fp, boxa);
        boxDestroy(&box);
        boxaDestroy(&boxa);
    }
    return 0;
}
예제 #2
0
/*!
 *  pixaDisplay()
 *
 *      Input:  pixa
 *              w, h (if set to 0, determines the size from the
 *                    b.b. of the components in pixa)
 *      Return: pix, or null on error
 *
 *  Notes:
 *      (1) This uses the boxes to place each pix in the rendered composite.
 *      (2) Set w = h = 0 to use the b.b. of the components to determine
 *          the size of the returned pix.
 *      (3) Uses the first pix in pixa to determine the depth.
 *      (4) The background is written "white".  On 1 bpp, each successive
 *          pix is "painted" (adding foreground), whereas for grayscale
 *          or color each successive pix is blitted with just the src.
 *      (5) If the pixa is empty, returns an empty 1 bpp pix.
 */
PIX *
pixaDisplay(PIXA    *pixa,
            l_int32  w,
            l_int32  h)
{
l_int32  i, n, d, xb, yb, wb, hb;
BOXA    *boxa;
PIX     *pixt, *pixd;

    PROCNAME("pixaDisplay");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
    
    n = pixaGetCount(pixa);
    if (n == 0 && w == 0 && h == 0)
        return (PIX *)ERROR_PTR("no components; no size", procName, NULL);
    if (n == 0) {
        L_WARNING("no components; returning empty 1 bpp pix", procName);
        return pixCreate(w, h, 1);
    }

        /* If w and h not input, determine the minimum size required
         * to contain the origin and all c.c. */
    if (w == 0 || h == 0) {
        boxa = pixaGetBoxa(pixa, L_CLONE);
        boxaGetExtent(boxa, &w, &h, NULL);
        boxaDestroy(&boxa);
    }

        /* Use the first pix in pixa to determine the depth.  */
    pixt = pixaGetPix(pixa, 0, L_CLONE);
    d = pixGetDepth(pixt);
    pixDestroy(&pixt);

    if ((pixd = pixCreate(w, h, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    if (d > 1)
        pixSetAll(pixd);
    for (i = 0; i < n; i++) {
        if (pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb)) {
            L_WARNING("no box found!", procName);
            continue;
        }
        pixt = pixaGetPix(pixa, i, L_CLONE);
        if (d == 1)
            pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0);
        else
            pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pixt, 0, 0);
        pixDestroy(&pixt);
    }

    return pixd;
}
예제 #3
0
/*!
 *  pixaDisplayRandomCmap()
 *
 *      Input:  pixa (of 1 bpp components, with boxa)
 *              w, h (if set to 0, determines the size from the
 *                    b.b. of the components in pixa)
 *      Return: pix (8 bpp, cmapped, with random colors on the components),
 *              or null on error
 *
 *  Notes:
 *      (1) This uses the boxes to place each pix in the rendered composite.
 *      (2) By default, the background color is: black, cmap index 0.
 *          This can be changed by pixcmapResetColor()
 */
PIX *
pixaDisplayRandomCmap(PIXA    *pixa,
                      l_int32  w,
                      l_int32  h)
{
l_int32   i, n, d, index, xb, yb, wb, hb;
BOXA     *boxa;
PIX      *pixs, *pixt, *pixd;
PIXCMAP  *cmap;

    PROCNAME("pixaDisplayRandomCmap");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
    
    n = pixaGetCount(pixa);
    if (n == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* Use the first pix in pixa to verify depth is 1 bpp  */
    pixs = pixaGetPix(pixa, 0, L_CLONE);
    d = pixGetDepth(pixs);
    pixDestroy(&pixs);
    if (d != 1)
        return (PIX *)ERROR_PTR("components not 1 bpp", procName, NULL);

        /* If w and h not input, determine the minimum size required
         * to contain the origin and all c.c. */
    if (w == 0 || h == 0) {
        boxa = pixaGetBoxa(pixa, L_CLONE);
        boxaGetExtent(boxa, &w, &h, NULL);
        boxaDestroy(&boxa);
    }

        /* Set up an 8 bpp dest pix, with a colormap with 254 random colors */
    if ((pixd = pixCreate(w, h, 8)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    cmap = pixcmapCreateRandom(8, 1, 1);
    pixSetColormap(pixd, cmap);

        /* Color each component and blit it in */
    for (i = 0; i < n; i++) {
        index = 1 + (i % 254);
        pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb);
        pixs = pixaGetPix(pixa, i, L_CLONE);
        pixt = pixConvert1To8(NULL, pixs, 0, index);
        pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0);
        pixDestroy(&pixs);
        pixDestroy(&pixt);
    }

    return pixd;
}
예제 #4
0
/*!
 *  pixaCreateFromBoxa()
 *
 *      Input:  pixs
 *              boxa
 *              &cropwarn (<optional return> TRUE if the boxa extent
 *                         is larger than pixs.
 *      Return: pixad, or null on error
 *
 *  Notes:
 *      (1) This simply extracts from pixs the region corresponding to each
 *          box in the boxa.
 *      (2) The 3rd arg is optional.  If the extent of the boxa exceeds the
 *          size of the pixa, so that some boxes are either clipped
 *          or entirely outside the pix, a warning is returned as TRUE.
 *      (3) pixad will have only the properly clipped elements, and
 *          the internal boxa will be correct.
 */
PIXA *
pixaCreateFromBoxa(PIX      *pixs,
                   BOXA     *boxa,
                   l_int32  *pcropwarn)
{
l_int32  i, n, w, h, wbox, hbox, cropwarn;
BOX     *box, *boxc;
PIX     *pixd;
PIXA    *pixad;

    PROCNAME("pixaCreateFromBoxa");

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

    n = boxaGetCount(boxa);
    if ((pixad = pixaCreate(n)) == NULL)
        return (PIXA *)ERROR_PTR("pixad not made", procName, NULL);

    boxaGetExtent(boxa, &wbox, &hbox, NULL);
    pixGetDimensions(pixs, &w, &h, NULL);
    cropwarn = FALSE;
    if (wbox > w || hbox > h)
        cropwarn = TRUE;
    if (pcropwarn)
        *pcropwarn = cropwarn;

    for (i = 0; i < n; i++) {
        box = boxaGetBox(boxa, i, L_COPY);
        if (cropwarn) {  /* if box is outside pixs, pixd is NULL */
            pixd = pixClipRectangle(pixs, box, &boxc);  /* may be NULL */
            if (pixd) {
                pixaAddPix(pixad, pixd, L_INSERT);
                pixaAddBox(pixad, boxc, L_INSERT);
            }
            boxDestroy(&box);
        }
        else {
            pixd = pixClipRectangle(pixs, box, NULL);
            pixaAddPix(pixad, pixd, L_INSERT);
            pixaAddBox(pixad, box, L_INSERT);
        }
    }

    return pixad;
}
예제 #5
0
int main(int    argc,
         char **argv)
{
char        *filein, *fileout, *fontdir;
l_int32      w, h, width, sep;
l_float32    scalefact;
L_BMF       *bmf;
BOXA        *boxa1, *boxa2;
PIX         *pixd;
static char  mainName[] = "displayboxa";

    if (argc != 4 && argc != 5) {
        fprintf(stderr, "Syntax error in displayboxa:\n"
           "   displayboxa filein width fileout [fontdir]\n");
         return 1;
    }

    filein = argv[1];
    fileout = argv[3];
    fontdir = (argc == 4) ? NULL : argv[4];
    width = atoi(argv[2]);
    if (width < 30) {
        L_ERROR("width too small; setting to 100\n", mainName);
        width = 100;
    }

    if ((boxa1 = boxaRead(filein)) == NULL)
        return ERROR_INT("boxa not made", mainName, 1);
    boxaGetExtent(boxa1, &w, &h, NULL);
    scalefact = (l_float32)width / (l_float32)w;
    boxa2 = boxaTransform(boxa1, 0, 0, scalefact, scalefact);
    sep = L_MIN(width / 5, 20);
    pixd = boxaDisplayTiled(boxa2, NULL, 1500, 2, 1.0, 0, sep, 2, fontdir);
    pixWrite(fileout, pixd, IFF_PNG);
    pixDisplay(pixd, 100, 100);

    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    pixDestroy(&pixd);
    return 0;
}
예제 #6
0
static PIX *
DisplayBoxa(BOXA  *boxa)
{
l_int32  w, h;
BOX     *box;
PIX     *pix1, *pix2, *pix3;
PIXA    *pixa;

    pixa = pixaCreate(2);
    boxaGetExtent(boxa, &w, &h, &box);
    pix1 = pixCreate(w, h, 1);
    pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS);
    pixaAddPix(pixa, pix1, L_INSERT);
    pix2 = pixCreate(w, h, 32);
    pixSetAll(pix2);
    pixRenderBoxaArb(pix2, boxa, 2, 0, 255, 0);
    pixRenderBoxArb(pix2, box, 3, 255, 0, 0);
    pixaAddPix(pixa, pix2, L_INSERT);
    pix3 = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2);
    boxDestroy(&box);
    pixaDestroy(&pixa);
    return pix3;
}
예제 #7
0
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;
}
예제 #8
0
/*!
 *  pixaaDisplay()
 *
 *      Input:  pixaa
 *              w, h (if set to 0, determines the size from the
 *                    b.b. of the components in pixaa)
 *      Return: pix, or null on error
 *
 *  Notes:
 *      (1) Each pix of the pixaa is displayed at the location given by
 *          its box, translated by the box of the containing pixa
 *          if it exists.
 */
PIX *
pixaaDisplay(PIXAA   *pixaa,
             l_int32  w,
             l_int32  h)
{
l_int32  i, j, n, nbox, na, d, wmax, hmax, x, y, xb, yb, wb, hb;
BOXA    *boxa1;  /* top-level boxa */
BOXA    *boxa;
PIX     *pixt, *pixd;
PIXA    *pixa;

    PROCNAME("pixaaDisplay");

    if (!pixaa)
        return (PIX *)ERROR_PTR("pixaa not defined", procName, NULL);
    
    n = pixaaGetCount(pixaa);
    if (n == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* If w and h not input, determine the minimum size required
         * to contain the origin and all c.c. */
    boxa1 = pixaaGetBoxa(pixaa, L_CLONE);
    nbox = boxaGetCount(boxa1);
    if (w == 0 || h == 0) {
        if (nbox == n)
            boxaGetExtent(boxa1, &w, &h, NULL);
        else {  /* have to use the lower-level boxa for each pixa */
            wmax = hmax = 0;
            for (i = 0; i < n; i++) {
                pixa = pixaaGetPixa(pixaa, i, L_CLONE);
                boxa = pixaGetBoxa(pixa, L_CLONE);
                boxaGetExtent(boxa, &w, &h, NULL);
                wmax = L_MAX(wmax, w);
                hmax = L_MAX(hmax, h);
                pixaDestroy(&pixa);
                boxaDestroy(&boxa);
            }
            w = wmax;
            h = hmax;
        }
    }

        /* Get depth from first pix */
    pixa = pixaaGetPixa(pixaa, 0, L_CLONE);
    pixt = pixaGetPix(pixa, 0, L_CLONE);
    d = pixGetDepth(pixt);
    pixaDestroy(&pixa);
    pixDestroy(&pixt);

    if ((pixd = pixCreate(w, h, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    
    x = y = 0;
    for (i = 0; i < n; i++) {
        pixa = pixaaGetPixa(pixaa, i, L_CLONE);
        if (nbox == n)
            boxaGetBoxGeometry(boxa1, i, &x, &y, NULL, NULL);
        na = pixaGetCount(pixa);
        for (j = 0; j < na; j++) {
            pixaGetBoxGeometry(pixa, j, &xb, &yb, &wb, &hb);
            pixt = pixaGetPix(pixa, j, L_CLONE);
            pixRasterop(pixd, x + xb, y + yb, wb, hb, PIX_PAINT, pixt, 0, 0);
            pixDestroy(&pixt);
        }
        pixaDestroy(&pixa);
    }
    boxaDestroy(&boxa1);

    return pixd;
}
예제 #9
0
/*!
 *  pixaDisplayOnColor()
 *
 *      Input:  pixa
 *              w, h (if set to 0, determines the size from the
 *                    b.b. of the components in pixa)
 *              color (background color to use)
 *      Return: pix, or null on error
 *
 *  Notes:
 *      (1) This uses the boxes to place each pix in the rendered composite.
 *      (2) Set w = h = 0 to use the b.b. of the components to determine
 *          the size of the returned pix.
 *      (3) If any pix in @pixa are colormapped, or if the pix have
 *          different depths, it returns a 32 bpp pix.  Otherwise,
 *          the depth of the returned pixa equals that of the pix in @pixa.
 *      (4) If the pixa is empty, return null.
 */
PIX *
pixaDisplayOnColor(PIXA     *pixa,
                   l_int32   w,
                   l_int32   h,
                   l_uint32  bgcolor)
{
l_int32  i, n, xb, yb, wb, hb, hascmap, maxdepth, same;
BOXA    *boxa;
PIX     *pixt1, *pixt2, *pixd;
PIXA    *pixat;

    PROCNAME("pixaDisplayOnColor");

    if (!pixa)
        return (PIX *)ERROR_PTR("pixa not defined", procName, NULL);
    if ((n = pixaGetCount(pixa)) == 0)
        return (PIX *)ERROR_PTR("no components", procName, NULL);

        /* If w and h are not input, determine the minimum size
         * required to contain the origin and all c.c. */
    if (w == 0 || h == 0) {
        boxa = pixaGetBoxa(pixa, L_CLONE);
        boxaGetExtent(boxa, &w, &h, NULL);
        boxaDestroy(&boxa);
    }

        /* If any pix have colormaps, or if they have different depths,
         * generate rgb */
    pixaAnyColormaps(pixa, &hascmap);
    pixaGetDepthInfo(pixa, &maxdepth, &same);
    if (hascmap || !same) {
        maxdepth = 32;
        pixat = pixaCreate(n);
        for (i = 0; i < n; i++) {
            pixt1 = pixaGetPix(pixa, i, L_CLONE);
            pixt2 = pixConvertTo32(pixt1);
            pixaAddPix(pixat, pixt2, L_INSERT);
            pixDestroy(&pixt1);
        }
    }
    else 
        pixat = pixaCopy(pixa, L_CLONE);

        /* Make the output pix and set the background color */
    if ((pixd = pixCreate(w, h, maxdepth)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    if ((maxdepth == 1 && bgcolor > 0) ||
        (maxdepth == 2 && bgcolor >= 0x3) ||
        (maxdepth == 4 && bgcolor >= 0xf) ||
        (maxdepth == 8 && bgcolor >= 0xff) ||
        (maxdepth == 16 && bgcolor >= 0xffff) ||
        (maxdepth == 32 && bgcolor >= 0xffffff00)) {
        pixSetAll(pixd);
    }
    else if (bgcolor > 0)
        pixSetAllArbitrary(pixd, bgcolor);

        /* Blit each pix into its place */
    for (i = 0; i < n; i++) {
        if (pixaGetBoxGeometry(pixat, i, &xb, &yb, &wb, &hb)) {
            L_WARNING("no box found!", procName);
            continue;
        }
        pixt1 = pixaGetPix(pixat, i, L_CLONE);
        pixRasterop(pixd, xb, yb, wb, hb, PIX_SRC, pixt1, 0, 0);
        pixDestroy(&pixt1);
    }

    pixaDestroy(&pixat);
    return pixd;
}
예제 #10
0
int main(int    argc,
         char **argv)
{
l_uint8     *data1, *data2;
l_int32      i, same, w, h, width, success, nba;
size_t       size1, size2;
l_float32    diffarea, diffxor, scalefact;
BOX         *box;
BOXA        *boxa1, *boxa2, *boxa3;
BOXAA       *baa1, *baa2, *baa3;
PIX         *pix1, *pixdb;
PIXA        *pixa1, *pixa2;
L_REGPARAMS  *rp;

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

    lept_mkdir("lept/boxa");

        /* Make a boxa and display its contents */
    boxa1 = boxaCreate(6);
    box = boxCreate(60, 60, 40, 20);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(120, 50, 20, 50);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(50, 140, 46, 60);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(166, 130, 64, 28);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(64, 224, 44, 34);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(117, 206, 26, 74);
    boxaAddBox(boxa1, box, L_INSERT);
    pix1 = DisplayBoxa(boxa1);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 0 */
    pixDisplayWithTitle(pix1, 0, 0, NULL, rp->display);
    pixDestroy(&pix1);

    boxaCompareRegions(boxa1, boxa1, 100, &same, &diffarea, &diffxor, NULL);
    regTestCompareValues(rp, 1, same, 0.0);  /* 1 */
    regTestCompareValues(rp, 0.0, diffarea, 0.0);  /* 2 */
    regTestCompareValues(rp, 0.0, diffxor, 0.0);  /* 3 */

    boxa2 = boxaTransform(boxa1, -13, -13, 1.0, 1.0);
    boxaCompareRegions(boxa1, boxa2, 10, &same, &diffarea, &diffxor, NULL);
    regTestCompareValues(rp, 1, same, 0.0);  /* 4 */
    regTestCompareValues(rp, 0.0, diffarea, 0.0);  /* 5 */
    regTestCompareValues(rp, 0.0, diffxor, 0.0);  /* 6 */
    boxaDestroy(&boxa2);

    boxa2 = boxaReconcileEvenOddHeight(boxa1, L_ADJUST_TOP_AND_BOT, 6,
                                       L_ADJUST_CHOOSE_MIN, 1.0, 0);
    pix1 = DisplayBoxa(boxa2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 7 */
    pixDisplayWithTitle(pix1, 200, 0, NULL, rp->display);
    pixDestroy(&pix1);

    boxaCompareRegions(boxa1, boxa2, 10, &same, &diffarea, &diffxor, &pixdb);
    regTestCompareValues(rp, 1, same, 0.0);  /* 8 */
    regTestCompareValues(rp, 0.053, diffarea, 0.002);  /* 9 */
    regTestCompareValues(rp, 0.240, diffxor, 0.002);  /* 10 */
    regTestWritePixAndCheck(rp, pixdb, IFF_PNG);  /* 11 */
    pixDisplayWithTitle(pixdb, 400, 0, NULL, rp->display);
    pixDestroy(&pixdb);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);

        /* Input is a fairly clean boxa */
    boxa1 = boxaRead("boxa1.ba");
    boxa2 = boxaReconcileEvenOddHeight(boxa1, L_ADJUST_TOP, 80,
                                       L_ADJUST_CHOOSE_MIN, 1.05, 1);
    width = 100;
    boxaGetExtent(boxa2, &w, &h, NULL);
    scalefact = (l_float32)width / (l_float32)w;
    boxa3 = boxaTransform(boxa2, 0, 0, scalefact, scalefact);
    pix1 = boxaDisplayTiled(boxa3, NULL, 1500, 2, 1.0, 0, 3, 2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 12 */
    pixDisplayWithTitle(pix1, 600, 0, NULL, rp->display);
    pixDestroy(&pix1);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    boxaDestroy(&boxa3);

        /* Input is an unsmoothed and noisy boxa */
    boxa1 = boxaRead("boxa2.ba");
    boxa2 = boxaReconcileEvenOddHeight(boxa1, L_ADJUST_TOP, 80,
                                       L_ADJUST_CHOOSE_MIN, 1.05, 1);
    width = 100;
    boxaGetExtent(boxa2, &w, &h, NULL);
    scalefact = (l_float32)width / (l_float32)w;
    boxa3 = boxaTransform(boxa2, 0, 0, scalefact, scalefact);
    pix1 = boxaDisplayTiled(boxa3, NULL, 1500, 2, 1.0, 0, 3, 2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 13 */
    pixDisplayWithTitle(pix1, 800, 0, NULL, rp->display);
    pixDestroy(&pix1);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    boxaDestroy(&boxa3);

        /* Input is a boxa smoothed with a median window filter */
    boxa1 = boxaRead("boxa3.ba");
    boxa2 = boxaReconcileEvenOddHeight(boxa1, L_ADJUST_TOP, 80,
                                       L_ADJUST_CHOOSE_MIN, 1.05, 1);
    width = 100;
    boxaGetExtent(boxa2, &w, &h, NULL);
    scalefact = (l_float32)width / (l_float32)w;
    boxa3 = boxaTransform(boxa2, 0, 0, scalefact, scalefact);
    pix1 = boxaDisplayTiled(boxa3, NULL, 1500, 2, 1.0, 0, 3, 2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 14 */
    pixDisplayWithTitle(pix1, 1000, 0, NULL, rp->display);
    pixDestroy(&pix1);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    boxaDestroy(&boxa3);

        /* Test serialized boxa I/O to and from memory */
    data1 = l_binaryRead("boxa2.ba", &size1);
    boxa1 = boxaReadMem(data1, size1);
    boxaWriteMem(&data2, &size2, boxa1);
    boxa2 = boxaReadMem(data2, size2);
    boxaWrite("/tmp/lept/boxa/boxa1.ba", boxa1);
    boxaWrite("/tmp/lept/boxa/boxa2.ba", boxa2);
    filesAreIdentical("/tmp/lept/boxa/boxa1.ba", "/tmp/lept/boxa/boxa2.ba",
                      &same); 
    regTestCompareValues(rp, 1, same, 0.0);  /* 15 */
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    lept_free(data1);
    lept_free(data2);

        /* ----------- Test pixaDisplayBoxaa() ------------ */
    pixa1 = pixaReadBoth("showboxes.pac");
    baa1 = boxaaRead("showboxes1.baa");
    baa2 = boxaaTranspose(baa1);
    baa3 = boxaaTranspose(baa2);
    nba = boxaaGetCount(baa1);
    success = TRUE;
    for (i = 0; i < nba; i++) {
        boxa1 = boxaaGetBoxa(baa1, i, L_CLONE);
        boxa2 = boxaaGetBoxa(baa3, i, L_CLONE);
        boxaEqual(boxa1, boxa2, 0, NULL, &same);
        boxaDestroy(&boxa1);
        boxaDestroy(&boxa2);
        if (!same) success = FALSE;
    }
        /* Check that the transpose is reversible */
    regTestCompareValues(rp, 1, success, 0.0);  /* 16 */
    pixa2 = pixaDisplayBoxaa(pixa1, baa2, L_DRAW_RGB, 2);
    pix1 = pixaDisplayTiledInRows(pixa2, 32, 1400, 1.0, 0, 10, 0);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);  /* 17 */
    pixDisplayWithTitle(pix1, 0, 600, NULL, rp->display);
    fprintf(stderr, "Writing to: /tmp/lept/boxa/show.pdf\n");
    l_pdfSetDateAndVersion(FALSE);
    pixaConvertToPdf(pixa2, 75, 1.0, 0, 0, NULL, "/tmp/lept/boxa/show.pdf");
    regTestCheckFile(rp, "/tmp/lept/boxa/show.pdf");  /* 18 */
    pixDestroy(&pix1);
    pixaDestroy(&pixa1);
    pixaDestroy(&pixa2);
    boxaaDestroy(&baa1);
    boxaaDestroy(&baa2);
    boxaaDestroy(&baa3);

    return regTestCleanup(rp);
}
예제 #11
0
/*!
 * \brief   boxaDisplayTiled()
 *
 * \param[in]    boxas
 * \param[in]    pixa          [optional] background for each box
 * \param[in]    first         index of first box
 * \param[in]    last          index of last box; use -1 to go to end
 * \param[in]    maxwidth      of output image
 * \param[in]    linewidth     width of box outlines, before scaling
 * \param[in]    scalefactor   applied to every box; use 1.0 for no scaling
 * \param[in]    background    0 for white, 1 for black; this is the color
 *                             of the spacing between the images
 * \param[in]    spacing       between images, and on outside
 * \param[in]    border        width of black border added to each image;
 *                             use 0 for no border
 * \return  pixd of tiled images of boxes, or NULL on error
 *
 * <pre>
 * Notes:
 *      (1) Displays each box separately in a tiled 32 bpp image.
 *      (2) If pixa is defined, it must have the same count as the boxa,
 *          and it will be a background over with each box is rendered.
 *          If pixa is not defined, the boxes will be rendered over
 *          blank images of identical size.
 *      (3) See pixaDisplayTiledInRows() for other parameters.
 * </pre>
 */
PIX *
boxaDisplayTiled(BOXA        *boxas,
                 PIXA        *pixa,
                 l_int32      first,
                 l_int32      last,
                 l_int32      maxwidth,
                 l_int32      linewidth,
                 l_float32    scalefactor,
                 l_int32      background,
                 l_int32      spacing,
                 l_int32      border)
{
char     buf[32];
l_int32  i, n, npix, w, h, fontsize;
L_BMF   *bmf;
BOX     *box;
BOXA    *boxa;
PIX     *pix1, *pix2, *pixd;
PIXA    *pixat;

    PROCNAME("boxaDisplayTiled");

    if (!boxas)
        return (PIX *)ERROR_PTR("boxas not defined", procName, NULL);

    boxa = boxaSaveValid(boxas, L_COPY);
    n = boxaGetCount(boxa);
    if (pixa) {
        npix = pixaGetCount(pixa);
        if (n != npix) {
            boxaDestroy(&boxa);
            return (PIX *)ERROR_PTR("boxa and pixa counts differ",
                                    procName, NULL);
        }
    }
    first = L_MAX(0, first);
    if (last < 0) last = n - 1;
    if (first >= n) {
        boxaDestroy(&boxa);
        return (PIX *)ERROR_PTR("invalid first", procName, NULL);
    }
    if (last >= n) {
        L_WARNING("last = %d is beyond max index = %d; adjusting\n",
                  procName, last, n - 1);
        last = n - 1;
    }
    if (first > last) {
        boxaDestroy(&boxa);
        return (PIX *)ERROR_PTR("first > last", procName, NULL);
    }

        /* Because the bitmap font will be reduced when tiled, choose the
         * font size inversely with the scale factor. */
    if (scalefactor > 0.8)
        fontsize = 6;
    else if (scalefactor > 0.6)
        fontsize = 10;
    else if (scalefactor > 0.4)
        fontsize = 14;
    else if (scalefactor > 0.3)
        fontsize = 18;
    else fontsize = 20;
    bmf = bmfCreate(NULL, fontsize);

    pixat = pixaCreate(n);
    boxaGetExtent(boxa, &w, &h, NULL);
    for (i = first; i <= last; i++) {
        box = boxaGetBox(boxa, i, L_CLONE);
        if (!pixa) {
            pix1 = pixCreate(w, h, 32);
            pixSetAll(pix1);
        } else {
            pix1 = pixaGetPix(pixa, i, L_COPY);
        }
        pixSetBorderVal(pix1, 0, 0, 0, 2, 0x0000ff00);
        snprintf(buf, sizeof(buf), "%d", i);
        pix2 = pixAddSingleTextblock(pix1, bmf, buf, 0x00ff0000,
                                     L_ADD_BELOW, NULL);
        pixDestroy(&pix1);
        pixRenderBoxArb(pix2, box, linewidth, 255, 0, 0);
        pixaAddPix(pixat, pix2, L_INSERT);
        boxDestroy(&box);
    }
    bmfDestroy(&bmf);
    boxaDestroy(&boxa);

    pixd = pixaDisplayTiledInRows(pixat, 32, maxwidth, scalefactor, background,
                                  spacing, border);
    pixaDestroy(&pixat);
    return pixd;
}
예제 #12
0
파일: boxa3_reg.c 프로젝트: chewi/leptonica
void static
TestBoxa(L_REGPARAMS  *rp,
         l_int32       index)
{
l_uint8   *data;
l_int32    w, h, medw, medh, isame;
size_t     size;
l_float32  scalefact, devw, devh, ratiowh, fvarp, fvarm;
BOXA      *boxa1, *boxa2, *boxa3;
PIX       *pix1;

    boxa1 = boxaRead(boxafiles[index]);

        /* Read and display initial boxa */
    boxaGetExtent(boxa1, &w, &h, NULL);
    scalefact = 100.0 / (l_float32)w;
    boxa2 = boxaTransform(boxa1, 0, 0, scalefact, scalefact);
    boxaWriteMem(&data, &size, boxa2);
    regTestWriteDataAndCheck(rp, data, size, "ba");  /* 0, 13, 26 */
    lept_free(data);
    pix1 = boxaDisplayTiled(boxa2, NULL, 0, -1, 2200, 2, 1.0, 0, 3, 2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);   /* 1, 14, 27 */
    pixDisplayWithTitle(pix1, 0, 0, NULL, rp->display);
    pixDestroy(&pix1);

        /* Find the median sizes */
    boxaMedianDimensions(boxa2, &medw, &medh, NULL, NULL, NULL, NULL,
                         NULL, NULL);
    if (rp->display)
        fprintf(stderr, "median width = %d, median height = %d\n", medw, medh);

        /* Check for deviations from median by pairs: method 1 */
    boxaSizeConsistency1(boxa2, L_CHECK_HEIGHT, 0.0, 0.0,
                         &fvarp, &fvarm, &isame);
    regTestCompareValues(rp, varp[index], fvarp, 0.003);  /* 2, 15, 28 */
    regTestCompareValues(rp, varm[index], fvarm, 0.003);  /* 3, 16, 29 */
    regTestCompareValues(rp, same[index], isame, 0);  /* 4, 17, 30 */
    if (rp->display)
        fprintf(stderr, "fvarp = %7.4f, fvarm = %7.4f, same = %d\n",
                fvarp, fvarm, isame);

        /* Check for deviations from median by pairs: method 2 */
    boxaSizeConsistency2(boxa2, &devw, &devh, 0);
    regTestCompareValues(rp, devwidth[index], devw, 0.001);  /* 5, 18, 31 */
    regTestCompareValues(rp, devheight[index], devh, 0.001);  /* 6, 19, 32 */
    if (rp->display)
        fprintf(stderr, "dev width = %7.4f, dev height = %7.4f\n", devw, devh);

        /* Reconcile widths */
    boxa3 = boxaReconcileSizeByMedian(boxa2, L_CHECK_WIDTH, 0.05, 0.04, 1.03,
                                      NULL, NULL, &ratiowh);
    boxaWriteMem(&data, &size, boxa3);
    regTestWriteDataAndCheck(rp, data, size, "ba");  /* 7, 20, 33 */
    lept_free(data);
    pix1 = boxaDisplayTiled(boxa3, NULL, 0, -1, 2200, 2, 1.0, 0, 3, 2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);   /* 8, 21, 34 */
    pixDisplayWithTitle(pix1, 500, 0, NULL, rp->display);
    if (rp->display)
        fprintf(stderr, "ratio median width/height = %6.3f\n", ratiowh);
    boxaDestroy(&boxa3);
    pixDestroy(&pix1);

        /* Reconcile heights */
    boxa3 = boxaReconcileSizeByMedian(boxa2, L_CHECK_HEIGHT, 0.05, 0.04, 1.03,
                                      NULL, NULL, NULL);
    boxaWriteMem(&data, &size, boxa3);
    regTestWriteDataAndCheck(rp, data, size, "ba");  /* 9, 22, 35 */
    lept_free(data);
    pix1 = boxaDisplayTiled(boxa3, NULL, 0, -1, 2200, 2, 1.0, 0, 3, 2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);   /* 10, 23, 36 */
    pixDisplayWithTitle(pix1, 1000, 0, NULL, rp->display);
    boxaDestroy(&boxa3);
    pixDestroy(&pix1);

        /* Reconcile both widths and heights */
    boxa3 = boxaReconcileSizeByMedian(boxa2, L_CHECK_BOTH, 0.05, 0.04, 1.03,
                                      NULL, NULL, NULL);
    boxaWriteMem(&data, &size, boxa3);
    regTestWriteDataAndCheck(rp, data, size, "ba");  /* 11, 24, 37 */
    lept_free(data);
    pix1 = boxaDisplayTiled(boxa3, NULL, 0, -1, 2200, 2, 1.0, 0, 3, 2);
    regTestWritePixAndCheck(rp, pix1, IFF_PNG);   /* 12, 25, 38 */
    pixDisplayWithTitle(pix1, 1500, 0, NULL, rp->display);
    boxaDestroy(&boxa3);
    pixDestroy(&pix1);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    boxaDestroy(&boxa3);
}
예제 #13
0
int main(int    argc,
         char **argv)
{
    l_uint8     *data1, *data2;
    l_int32      i, same, w, h, width, success, nba;
    size_t       size1, size2;
    l_float32    diffarea, diffxor, scalefact;
    BOX         *box;
    BOXA        *boxa1, *boxa2, *boxa3;
    BOXAA       *baa1, *baa2, *baa3;
    PIX         *pix1, *pixdb;
    PIXA        *pixa1, *pixa2;
    static char  mainName[] = "boxa1_reg";

    if (argc != 1)
        return ERROR_INT(" Syntax: boxa1_reg", mainName, 1);

    lept_mkdir("lept/boxa");

    /* Make a boxa and display its contents */
    boxa1 = boxaCreate(6);
    box = boxCreate(60, 60, 40, 20);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(120, 50, 20, 50);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(50, 140, 46, 60);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(166, 130, 64, 28);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(64, 224, 44, 34);
    boxaAddBox(boxa1, box, L_INSERT);
    box = boxCreate(117, 206, 26, 74);
    boxaAddBox(boxa1, box, L_INSERT);
    pix1 = DisplayBoxa(boxa1);
    pixDisplay(pix1, 100, 100);
    pixDestroy(&pix1);

    boxaCompareRegions(boxa1, boxa1, 100, &same, &diffarea, &diffxor, NULL);
    fprintf(stderr, "same = %d, diffarea = %5.3f, diffxor = %5.3f\n",
            same, diffarea, diffxor);

    boxa2 = boxaTransform(boxa1, -13, -13, 1.0, 1.0);
    boxaCompareRegions(boxa1, boxa2, 10, &same, &diffarea, &diffxor, NULL);
    fprintf(stderr, "same = %d, diffarea = %5.3f, diffxor = %5.3f\n",
            same, diffarea, diffxor);
    boxaDestroy(&boxa2);

    boxa2 = boxaReconcileEvenOddHeight(boxa1, L_ADJUST_TOP_AND_BOT, 6,
                                       L_ADJUST_CHOOSE_MIN, 1.0, 0);
    pix1 = DisplayBoxa(boxa2);
    pixDisplay(pix1, 100, 500);
    pixDestroy(&pix1);

    boxaCompareRegions(boxa1, boxa2, 10, &same, &diffarea, &diffxor, &pixdb);
    fprintf(stderr, "same = %d, diffarea = %5.3f, diffxor = %5.3f\n",
            same, diffarea, diffxor);
    pixDisplay(pixdb, 700, 100);

    pixDestroy(&pixdb);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);

    /* Input is a fairly clean boxa */
    boxa1 = boxaRead("boxa1.ba");
    boxa2 = boxaReconcileEvenOddHeight(boxa1, L_ADJUST_TOP, 80,
                                       L_ADJUST_CHOOSE_MIN, 1.05, 1);
    width = 100;
    boxaGetExtent(boxa2, &w, &h, NULL);
    scalefact = (l_float32)width / (l_float32)w;
    boxa3 = boxaTransform(boxa2, 0, 0, scalefact, scalefact);
    pix1 = boxaDisplayTiled(boxa3, NULL, 1500, 2, 1.0, 0, 3, 2);
    pixDisplay(pix1, 0, 100);
    pixWrite("/tmp/lept/boxa/pix1.png", pix1, IFF_PNG);
    pixDestroy(&pix1);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    boxaDestroy(&boxa3);

    /* Input is an unsmoothed and noisy boxa */
    boxa1 = boxaRead("boxa2.ba");
    boxa2 = boxaReconcileEvenOddHeight(boxa1, L_ADJUST_TOP, 80,
                                       L_ADJUST_CHOOSE_MIN, 1.05, 1);
    width = 100;
    boxaGetExtent(boxa2, &w, &h, NULL);
    scalefact = (l_float32)width / (l_float32)w;
    boxa3 = boxaTransform(boxa2, 0, 0, scalefact, scalefact);
    pix1 = boxaDisplayTiled(boxa3, NULL, 1500, 2, 1.0, 0, 3, 2);
    pixDisplay(pix1, 500, 100);
    pixWrite("/tmp/lept/boxa/pix2.png", pix1, IFF_PNG);
    pixDestroy(&pix1);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    boxaDestroy(&boxa3);

    /* Input is a boxa smoothed with a median window filter */
    boxa1 = boxaRead("boxa3.ba");
    boxa2 = boxaReconcileEvenOddHeight(boxa1, L_ADJUST_TOP, 80,
                                       L_ADJUST_CHOOSE_MIN, 1.05, 1);
    width = 100;
    boxaGetExtent(boxa2, &w, &h, NULL);
    scalefact = (l_float32)width / (l_float32)w;
    boxa3 = boxaTransform(boxa2, 0, 0, scalefact, scalefact);
    pix1 = boxaDisplayTiled(boxa3, NULL, 1500, 2, 1.0, 0, 3, 2);
    pixDisplay(pix1, 1000, 100);
    pixWrite("/tmp/lept/boxa/pix3.png", pix1, IFF_PNG);
    pixDestroy(&pix1);
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    boxaDestroy(&boxa3);

    /* Test serialized boxa I/O to and from memory */
    data1 = l_binaryRead("boxa2.ba", &size1);
    boxa1 = boxaReadMem(data1, size1);
    boxaWriteMem(&data2, &size2, boxa1);
    boxa2 = boxaReadMem(data2, size2);
    boxaWrite("/tmp/lept/boxa/boxa1.ba", boxa1);
    boxaWrite("/tmp/lept/boxa/boxa2.ba", boxa2);
    filesAreIdentical("/tmp/lept/boxa/boxa1.ba", "/tmp/lept/boxa/boxa2.ba",
                      &same);
    if (same)
        fprintf(stderr, "Good: boxes files are identical\n");
    else
        fprintf(stderr, "Bad: boxes files differ\n");
    boxaDestroy(&boxa1);
    boxaDestroy(&boxa2);
    lept_free(data1);
    lept_free(data2);

    /* Test pixaDisplayBoxaa() */
    pixa1 = pixaReadBoth("showboxes.pac");
    baa1 = boxaaRead("showboxes1.baa");
    baa2 = boxaaTranspose(baa1);
    baa3 = boxaaTranspose(baa2);
    nba = boxaaGetCount(baa1);
    success = TRUE;
    for (i = 0; i < nba; i++) {
        boxa1 = boxaaGetBoxa(baa1, i, L_CLONE);
        boxa2 = boxaaGetBoxa(baa3, i, L_CLONE);
        boxaEqual(boxa1, boxa2, 0, NULL, &same);
        boxaDestroy(&boxa1);
        boxaDestroy(&boxa2);
        if (!same) success = FALSE;
    }
    if (success)
        fprintf(stderr, "Good: transpose is reversible\n");
    else
        fprintf(stderr, "Bad: transpose failed\n");
    pixa2 = pixaDisplayBoxaa(pixa1, baa2, L_DRAW_RGB, 2);
    pix1 = pixaDisplayTiledInRows(pixa2, 32, 1400, 1.0, 0, 10, 0);
    pixDisplay(pix1, 0, 600);
    fprintf(stderr, "Writing to: /tmp/lept/boxa/show.pdf\n");
    pixaConvertToPdf(pixa2, 75, 1.0, 0, 0, NULL, "/tmp/lept/boxa/show.pdf");
    pixDestroy(&pix1);
    pixaDestroy(&pixa1);
    pixaDestroy(&pixa2);
    boxaaDestroy(&baa1);
    boxaaDestroy(&baa2);
    boxaaDestroy(&baa3);

    return 0;
}