/*! * 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; }
/*! * 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; }
/*! * 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; }
/*! * 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; }
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; }
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; }
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; }
/*! * 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; }
/*! * 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; }
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); }
/*! * \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; }
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); }
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; }