void Java_com_example_ocr_Pixa_nativeMergeAndReplacePix(JNIEnv *env, jclass clazz, jint nativePixa, jint indexA, jint indexB) { PIXA *pixa = (PIXA *) nativePixa; l_int32 op; l_int32 x, y, w, h; l_int32 dx, dy, dw, dh; PIX *pixs, *pixd; BOX *boxA, *boxB, *boxd; boxA = pixaGetBox(pixa, indexA, L_CLONE); boxB = pixaGetBox(pixa, indexB, L_CLONE); boxd = boxBoundingRegion(boxA, boxB); boxGetGeometry(boxd, &x, &y, &w, &h); pixd = pixCreate(w, h, 1); op = PIX_SRC | PIX_DST; pixs = pixaGetPix(pixa, indexA, L_CLONE); boxGetGeometry(boxA, &dx, &dy, &dw, &dh); pixRasterop(pixd, dx - x, dy - y, dw, dh, op, pixs, 0, 0); pixDestroy(&pixs); boxDestroy(&boxA); pixs = pixaGetPix(pixa, indexB, L_CLONE); boxGetGeometry(boxB, &dx, &dy, &dw, &dh); pixRasterop(pixd, dx - x, dy - y, dw, dh, op, pixs, 0, 0); pixDestroy(&pixs); boxDestroy(&boxB); pixaReplacePix(pixa, indexA, pixd, boxd); }
// create a union of two arbitrary pix Pix *CubeLineSegmenter::PixUnion(Pix *dest_pix, Box *dest_box, Pix *src_pix, Box *src_box) { // compute dimensions of union rect BOX *union_box = boxBoundingRegion(src_box, dest_box); // create the union pix Pix *union_pix = pixCreate(union_box->w, union_box->h, src_pix->d); if (union_pix == NULL) { return NULL; } // blt the src and dest pix pixRasterop(union_pix, src_box->x - union_box->x, src_box->y - union_box->y, src_box->w, src_box->h, PIX_SRC | PIX_DST, src_pix, 0, 0); pixRasterop(union_pix, dest_box->x - union_box->x, dest_box->y - union_box->y, dest_box->w, dest_box->h, PIX_SRC | PIX_DST, dest_pix, 0, 0); // replace the dest_box *dest_box = *union_box; boxDestroy(&union_box); return union_pix; }
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; }
/*! * boxaCombineOverlaps() * * Input: boxas * Return: boxad (where each set of boxes in boxas that overlap are * combined into a single bounding box in boxad), or * null on error. * * Notes: * (1) If there are no overlapping boxes, it simply returns a copy * of @boxas. * (2) The alternative method of painting each rectanle and finding * the 4-connected components gives the wrong result, because * two non-overlapping rectangles, when rendered, can still * be 4-connected, and hence they will be joined. * (3) A bad case is to have n boxes, none of which overlap. * Then you have one iteration with O(n^2) compares. This * is still faster than painting each rectangle and finding * the connected components, even for thousands of rectangles. */ BOXA * boxaCombineOverlaps(BOXA *boxas) { l_int32 i, j, n1, n2, inter, interfound, niters; BOX *box1, *box2, *box3; BOXA *boxat1, *boxat2; PROCNAME("boxaCombineOverlaps"); if (!boxas) return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); boxat1 = boxaCopy(boxas, L_COPY); n1 = boxaGetCount(boxat1); niters = 0; /* fprintf(stderr, "%d iters: %d boxes\n", niters, n1); */ while (1) { /* loop until no change from previous iteration */ niters++; boxat2 = boxaCreate(n1); for (i = 0; i < n1; i++) { box1 = boxaGetBox(boxat1, i, L_COPY); if (i == 0) { boxaAddBox(boxat2, box1, L_INSERT); continue; } n2 = boxaGetCount(boxat2); /* Now test box1 against all boxes already put in boxat2. * If it is found to intersect with an existing box, * replace that box by the union of the two boxes, * and break to the outer loop. If no overlap is * found, add box1 to boxat2. */ interfound = FALSE; for (j = 0; j < n2; j++) { box2 = boxaGetBox(boxat2, j, L_CLONE); boxIntersects(box1, box2, &inter); if (inter == 1) { box3 = boxBoundingRegion(box1, box2); boxaReplaceBox(boxat2, j, box3); boxDestroy(&box1); boxDestroy(&box2); interfound = TRUE; break; } boxDestroy(&box2); } if (interfound == FALSE) boxaAddBox(boxat2, box1, L_INSERT); } n2 = boxaGetCount(boxat2); /* fprintf(stderr, "%d iters: %d boxes\n", niters, n2); */ if (n2 == n1) /* we're done */ break; else { n1 = n2; boxaDestroy(&boxat1); boxat1 = boxat2; } } boxaDestroy(&boxat1); return boxat2; }