/*! * boxRotateOrth() * * Input: box * w, h (of image in which the box is embedded) * rotation (0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; * all rotations are clockwise) * Return: boxd, or null on error * * Notes: * (1) Rotate the image with the embedded box by the specified amount. * (2) After rotation, the rotated box is always measured with * respect to the UL corner of the image. */ BOX * boxRotateOrth(BOX *box, l_int32 w, l_int32 h, l_int32 rotation) { l_int32 bx, by, bw, bh, xdist, ydist; PROCNAME("boxRotateOrth"); if (!box) return (BOX *)ERROR_PTR("box not defined", procName, NULL); if (rotation == 0) return boxCopy(box); if (rotation < 1 || rotation > 3) return (BOX *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); boxGetGeometry(box, &bx, &by, &bw, &bh); if (bw <= 0 || bh <= 0) /* invalid */ return boxCreate(0, 0, 0, 0); ydist = h - by - bh; /* below box */ xdist = w - bx - bw; /* to right of box */ if (rotation == 1) /* 90 deg cw */ return boxCreate(ydist, bx, bh, bw); else if (rotation == 2) /* 180 deg cw */ return boxCreate(xdist, ydist, bw, bh); else /* rotation == 3, 270 deg cw */ return boxCreate(by, xdist, bh, bw); }
main(int argc, char **argv) { char *filein, *fileout; l_int32 d; BOX *box1, *box2, *box3, *box4; BOXA *boxa; PIX *pixs, *pixt1, *pixt2, *pixt3; PTA *pta; static char mainName[] = "graphicstest"; if (argc != 3) exit(ERROR_INT(" Syntax: graphicstest filein fileout", mainName, 1)); filein = argv[1]; fileout = argv[2]; if ((pixs = pixRead(filein)) == NULL) exit(ERROR_INT(" Syntax: pixs not made", mainName, 1)); d = pixGetDepth(pixs); if (d <= 8) pixt1 = pixConvertTo32(pixs); else pixt1 = pixClone(pixs); /* Paint on RGB */ pixRenderLineArb(pixt1, 450, 20, 850, 320, 5, 200, 50, 125); pixRenderLineArb(pixt1, 30, 40, 440, 40, 5, 100, 200, 25); pixRenderLineBlend(pixt1, 30, 60, 440, 70, 5, 115, 200, 120, 0.3); pixRenderLineBlend(pixt1, 30, 600, 440, 670, 9, 215, 115, 30, 0.5); pixRenderLineBlend(pixt1, 130, 700, 540, 770, 9, 255, 255, 250, 0.4); pixRenderLineBlend(pixt1, 130, 800, 540, 870, 9, 0, 0, 0, 0.4); box1 = boxCreate(70, 80, 300, 245); box2 = boxCreate(470, 180, 150, 205); box3 = boxCreate(520, 220, 160, 220); box4 = boxCreate(570, 260, 160, 220); boxa = boxaCreate(3); boxaAddBox(boxa, box2, L_INSERT); boxaAddBox(boxa, box3, L_INSERT); boxaAddBox(boxa, box4, L_INSERT); pixRenderBoxArb(pixt1, box1, 3, 200, 200, 25); pixRenderBoxaBlend(pixt1, boxa, 17, 200, 200, 25, 0.4, 1); pta = ptaCreate(5); ptaAddPt(pta, 250, 300); ptaAddPt(pta, 350, 450); ptaAddPt(pta, 400, 600); ptaAddPt(pta, 212, 512); ptaAddPt(pta, 180, 375); pixRenderPolylineBlend(pixt1, pta, 17, 25, 200, 200, 0.5, 1, 1); pixWrite(fileout, pixt1, IFF_JFIF_JPEG); pixDisplay(pixt1, 200, 200); pixDestroy(&pixs); pixDestroy(&pixt1); boxDestroy(&box1); boxaDestroy(&boxa); ptaDestroy(&pta); pixDestroy(&pixs); return 0; }
/*! * boxOverlapRegion() * * Input: box1, box2 (two boxes) * Return: box (of overlap region between input boxes), * or null if no overlap or on error */ BOX * boxOverlapRegion(BOX *box1, BOX *box2) { l_int32 x, y, w, h, left1, left2, top1, top2, right1, right2, bot1, bot2; PROCNAME("boxOverlapRegion"); if (!box1) return (BOX *)ERROR_PTR("box1 not defined", procName, NULL); if (!box2) return (BOX *)ERROR_PTR("box2 not defined", procName, NULL); left1 = box1->x; left2 = box2->x; top1 = box1->y; top2 = box2->y; right1 = box1->x + box1->w - 1; bot1 = box1->y + box1->h - 1; right2 = box2->x + box2->w - 1; bot2 = box2->y + box2->h - 1; if ((bot2 < top1) || (bot1 < top2) || (right1 < left2) || (right2 < left1)) return NULL; x = (left1 > left2) ? left1 : left2; y = (top1 > top2) ? top1 : top2; w = L_MIN(right1 - x + 1, right2 - x + 1); h = L_MIN(bot1 - y + 1, bot2 - y + 1); return boxCreate(x, y, w, h); }
void EquationDetectBase::RenderSpecialText(Pix* pix, BLOBNBOX* blob) { ASSERT_HOST(pix != nullptr && pixGetDepth(pix) == 32 && blob != nullptr); const TBOX& tbox = blob->bounding_box(); int height = pixGetHeight(pix); const int box_width = 5; // Coordinate translation: tesseract use left bottom as the original, while // leptonica uses left top as the original. Box *box = boxCreate(tbox.left(), height - tbox.top(), tbox.width(), tbox.height()); switch (blob->special_text_type()) { case BSTT_MATH: // Red box. pixRenderBoxArb(pix, box, box_width, 255, 0, 0); break; case BSTT_DIGIT: // cyan box. pixRenderBoxArb(pix, box, box_width, 0, 255, 255); break; case BSTT_ITALIC: // Green box. pixRenderBoxArb(pix, box, box_width, 0, 255, 0); break; case BSTT_UNCLEAR: // blue box. pixRenderBoxArb(pix, box, box_width, 0, 255, 0); break; case BSTT_NONE: default: // yellow box. pixRenderBoxArb(pix, box, box_width, 255, 255, 0); break; } boxDestroy(&box); }
int ignore_alpha_at_edge(png_byte *alpha, unsigned char* indata, int w, int h, PIX *in, PIX **out) { int i, j, index, start_y, end_y; int find_end_x = CCX_FALSE; BOX* cropWindow; for (j = 1; j < w-1; j++) { for (i = 0; i < h; i++) { index = indata[i * w + (j)]; if(alpha[index] != 0) { if(find_end_x == CCX_FALSE) { start_y = j; find_end_x = CCX_TRUE; } else { end_y = j; } } } } cropWindow = boxCreate(start_y, 0, (w - (start_y + ( w - end_y) )), h - 1); *out = pixClipRectangle(in, cropWindow, NULL); boxDestroy(&cropWindow); return 0; }
/*! * boxaSplitEvenOdd() * * Input: boxa * &boxae, &boxao (<return> save even and odd boxes in their * separate boxa, setting the other type to invalid boxes.) * Return: 0 if OK, 1 on error * * Notes: * (1) For example, boxae copies of the even boxes, in their original * location, that are in boxa. Invalid boxes are placed * in the odd array locations. * */ l_int32 boxaSplitEvenOdd(BOXA *boxa, BOXA **pboxae, BOXA **pboxao) { l_int32 i, n; BOX *box, *boxt; PROCNAME("boxaSplitEvenOdd"); if (!pboxae || !pboxao) return ERROR_INT("&boxae and &boxao not defined", procName, 1); *pboxae = *pboxao = NULL; if (!boxa) return ERROR_INT("boxa not defined", procName, 1); n = boxaGetCount(boxa); *pboxae = boxaCreate(n); *pboxao = boxaCreate(n); for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_COPY); boxt = boxCreate(0, 0, 0, 0); /* empty placeholder */ if ((i & 1) == 0) { boxaAddBox(*pboxae, box, L_INSERT); boxaAddBox(*pboxao, boxt, L_INSERT); } else { boxaAddBox(*pboxae, boxt, L_INSERT); boxaAddBox(*pboxao, box, L_INSERT); } } return 0; }
void DoJp2kTest1(L_REGPARAMS *rp, const char *fname) { char buf[256]; l_int32 w, h; BOX *box; PIX *pix1, *pix2, *pix3; pix1 = pixRead(fname); pixGetDimensions(pix1, &w, &h, NULL); box = boxCreate(w / 4, h / 4, w / 2, h / 2); snprintf(buf, sizeof(buf), "/tmp/lept/jp2kio.%03d.jp2", rp->index + 1); pixWrite(buf, pix1, IFF_JP2); regTestCheckFile(rp, buf); pix2 = pixRead(buf); pixDisplayWithTitle(pix2, 0, 100, "1", rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pix1 = pixReadJp2k(buf, 1, box, 0, 0); /* just read the box region */ snprintf(buf, sizeof(buf), "/tmp/lept/jp2kio.%03d.jp2", rp->index + 1); pixWriteJp2k(buf, pix1, 38, 0, 0, 0); regTestCheckFile(rp, buf); pix2 = pixRead(buf); regTestWritePixAndCheck(rp, pix2, IFF_JP2); pixDisplayWithTitle(pix2, 500, 100, "2", rp->display); pix3 = pixReadJp2k(buf, 2, NULL, 0, 0); /* read image at 2x reduction */ regTestWritePixAndCheck(rp, pix3, IFF_JP2); pixDisplayWithTitle(pix3, 1000, 100, "3", rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); return; }
/*! * \brief boxaaQuadtreeRegions() * * \param[in] w, h size of pix that is being quadtree-ized * \param[in] nlevels number of levels in quadtree * \return baa for quadtree regions at each level, or NULL on error * * <pre> * Notes: * (1) The returned boxaa has %nlevels of boxa, each containing * the set of rectangles at that level. The rectangle at * level 0 is the entire region; at level 1 the region is * divided into 4 rectangles, and at level n there are n^4 * rectangles. * (2) At each level, the rectangles in the boxa are in "raster" * order, with LR (fast scan) and TB (slow scan). * </pre> */ BOXAA * boxaaQuadtreeRegions(l_int32 w, l_int32 h, l_int32 nlevels) { l_int32 i, j, k, maxpts, nside, nbox, bw, bh; l_int32 *xstart, *xend, *ystart, *yend; BOX *box; BOXA *boxa; BOXAA *baa; PROCNAME("boxaaQuadtreeRegions"); if (nlevels < 1) return (BOXAA *)ERROR_PTR("nlevels must be >= 1", procName, NULL); if (w < (1 << (nlevels - 1))) return (BOXAA *)ERROR_PTR("w doesn't support nlevels", procName, NULL); if (h < (1 << (nlevels - 1))) return (BOXAA *)ERROR_PTR("h doesn't support nlevels", procName, NULL); baa = boxaaCreate(nlevels); maxpts = 1 << (nlevels - 1); xstart = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); xend = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); ystart = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); yend = (l_int32 *)LEPT_CALLOC(maxpts, sizeof(l_int32)); for (k = 0; k < nlevels; k++) { nside = 1 << k; /* number of boxes in each direction */ for (i = 0; i < nside; i++) { xstart[i] = (w - 1) * i / nside; if (i > 0) xstart[i]++; xend[i] = (w - 1) * (i + 1) / nside; ystart[i] = (h - 1) * i / nside; if (i > 0) ystart[i]++; yend[i] = (h - 1) * (i + 1) / nside; #if DEBUG_BOXES fprintf(stderr, "k = %d, xs[%d] = %d, xe[%d] = %d, ys[%d] = %d, ye[%d] = %d\n", k, i, xstart[i], i, xend[i], i, ystart[i], i, yend[i]); #endif /* DEBUG_BOXES */ } nbox = 1 << (2 * k); boxa = boxaCreate(nbox); for (i = 0; i < nside; i++) { bh = yend[i] - ystart[i] + 1; for (j = 0; j < nside; j++) { bw = xend[j] - xstart[j] + 1; box = boxCreate(xstart[j], ystart[i], bw, bh); boxaAddBox(boxa, box, L_INSERT); } } boxaaAddBoxa(baa, boxa, L_INSERT); } LEPT_FREE(xstart); LEPT_FREE(xend); LEPT_FREE(ystart); LEPT_FREE(yend); return baa; }
/** * Returns an image of the current object at the given level in greyscale * if available in the input. To guarantee a binary image use BinaryImage. * NOTE that in order to give the best possible image, the bounds are * expanded slightly over the binary connected component, by the supplied * padding, so the top-left position of the returned image is returned * in (left,top). These will most likely not match the coordinates * returned by BoundingBox. * Use pixDestroy to delete the image after use. */ Pix* PageIterator::GetImage(PageIteratorLevel level, int padding, int* left, int* top) const { int right, bottom; if (!BoundingBox(level, left, top, &right, &bottom)) return NULL; Pix* pix = tesseract_->pix_grey(); if (pix == NULL) return GetBinaryImage(level); // Expand the box. *left = MAX(*left - padding, 0); *top = MAX(*top - padding, 0); right = MIN(right + padding, rect_width_); bottom = MIN(bottom + padding, rect_height_); Box* box = boxCreate(*left, *top, right - *left, bottom - *top); Pix* grey_pix = pixClipRectangle(pix, box, NULL); boxDestroy(&box); if (level == RIL_BLOCK) { Pix* mask = it_->block()->block->render_mask(); Pix* expanded_mask = pixCreate(right - *left, bottom - *top, 1); pixRasterop(expanded_mask, padding, padding, pixGetWidth(mask), pixGetHeight(mask), PIX_SRC, mask, 0, 0); pixDestroy(&mask); pixDilateBrick(expanded_mask, expanded_mask, 2*padding + 1, 2*padding + 1); pixInvert(expanded_mask, expanded_mask); pixSetMasked(grey_pix, expanded_mask, 255); pixDestroy(&expanded_mask); } return grey_pix; }
/*! * boxAdjustSides() * * Input: boxd (<optional>; this can be null, equal to boxs, * or different from boxs) * boxs (starting box; to have sides adjusted) * delleft, delright, deltop, delbot (changes in location of * each side) * Return: boxd, or null on error or if the computed boxd has * width or height <= 0. * * Notes: * (1) Set boxd == NULL to get new box; boxd == boxs for in-place; * or otherwise to resize existing boxd. * (2) For usage, suggest one of these: * boxd = boxAdjustSides(NULL, boxs, ...); // new * boxAdjustSides(boxs, boxs, ...); // in-place * boxAdjustSides(boxd, boxs, ...); // other * (1) New box dimensions are cropped at left and top to x >= 0 and y >= 0. * (2) For example, to expand in-place by 20 pixels on each side, use * boxAdjustSides(box, box, -20, 20, -20, 20); */ BOX * boxAdjustSides(BOX *boxd, BOX *boxs, l_int32 delleft, l_int32 delright, l_int32 deltop, l_int32 delbot) { l_int32 x, y, w, h, xl, xr, yt, yb, wnew, hnew; PROCNAME("boxAdjustSides"); if (!boxs) return (BOX *)ERROR_PTR("boxs not defined", procName, NULL); boxGetGeometry(boxs, &x, &y, &w, &h); xl = L_MAX(0, x + delleft); yt = L_MAX(0, y + deltop); xr = x + w + delright; /* one pixel beyond right edge */ yb = y + h + delbot; /* one pixel below bottom edge */ wnew = xr - xl; hnew = yb - yt; if (wnew < 1 || hnew < 1) return (BOX *)ERROR_PTR("boxd has 0 area", procName, NULL); if (!boxd) return boxCreate(xl, yt, wnew, hnew); else { boxSetGeometry(boxd, xl, yt, wnew, hnew); return boxd; } }
/*! * boxaReadStream() * * Input: stream * Return: boxa, or null on error */ BOXA * boxaReadStream(FILE *fp) { l_int32 n, i, x, y, w, h, version; l_int32 ignore; BOX *box; BOXA *boxa; PROCNAME("boxaReadStream"); if (!fp) return (BOXA *)ERROR_PTR("stream not defined", procName, NULL); if (fscanf(fp, "\nBoxa Version %d\n", &version) != 1) return (BOXA *)ERROR_PTR("not a boxa file", procName, NULL); if (version != BOXA_VERSION_NUMBER) return (BOXA *)ERROR_PTR("invalid boxa version", procName, NULL); if (fscanf(fp, "Number of boxes = %d\n", &n) != 1) return (BOXA *)ERROR_PTR("not a boxa file", procName, NULL); if ((boxa = boxaCreate(n)) == NULL) return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); for (i = 0; i < n; i++) { if (fscanf(fp, " Box[%d]: x = %d, y = %d, w = %d, h = %d\n", &ignore, &x, &y, &w, &h) != 5) return (BOXA *)ERROR_PTR("box descr not valid", procName, NULL); if ((box = boxCreate(x, y, w, h)) == NULL) return (BOXA *)ERROR_PTR("box not made", procName, NULL); boxaAddBox(boxa, box, L_INSERT); } return boxa; }
Box *CubeSearchObject::CharBox(int start_pt, int end_pt) { if (!init_ && !Init()) return NULL; if (!IsValidSegmentRange(start_pt, end_pt)) { fprintf(stderr, "Cube ERROR (CubeSearchObject::CharBox): invalid " "segment range (%d, %d)\n", start_pt, end_pt); return NULL; } // create a char samp object from the specified range of segments, // extract its dimensions into a leptonica box, and delete it bool left_most; bool right_most; CharSamp *samp = CharSamp::FromConComps(segments_, start_pt + 1, end_pt - start_pt, NULL, &left_most, &right_most, hgt_); if (!samp) return NULL; if (kUseCroppedChars) { CharSamp *cropped_samp = samp->Crop(); delete samp; if (!cropped_samp) { return NULL; } samp = cropped_samp; } Box *box = boxCreate(samp->Left(), samp->Top(), samp->Width(), samp->Height()); delete samp; return box; }
/*! * boxaGetRankSize() * * Input: boxa * fract (use 0.0 for smallest, 1.0 for largest) * Return: box (with rank values for x, y, w, h), or null on error * or if the boxa is empty (has no valid boxes) * * Notes: * (1) This function does not assume that all boxes in the boxa are valid * (2) The four box parameters are sorted independently. * For rank order, the width and height are sorted in increasing * order. But what does it mean to sort x and y in "rank order"? * If the boxes are of comparable size and somewhat * aligned (e.g., from multiple images), it makes some sense * to give a "rank order" for x and y by sorting them in * decreasing order. But in general, the interpretation of a rank * order on x and y is highly application dependent. In summary: * - x and y are sorted in decreasing order * - w and h are sorted in increasing order */ BOX * boxaGetRankSize(BOXA *boxa, l_float32 fract) { l_float32 xval, yval, wval, hval; NUMA *nax, *nay, *naw, *nah; BOX *box; PROCNAME("boxaGetRankSize"); if (!boxa) return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); if (fract < 0.0 || fract > 1.0) return (BOX *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL); if (boxaGetValidCount(boxa) == 0) return (BOX *)ERROR_PTR("no valid boxes in boxa", procName, NULL); boxaExtractAsNuma(boxa, &nax, &nay, &naw, &nah, 0); /* valid boxes only */ numaGetRankValue(nax, 1.0 - fract, NULL, 1, &xval); numaGetRankValue(nay, 1.0 - fract, NULL, 1, &yval); numaGetRankValue(naw, fract, NULL, 1, &wval); numaGetRankValue(nah, fract, NULL, 1, &hval); box = boxCreate((l_int32)xval, (l_int32)yval, (l_int32)wval, (l_int32)hval); numaDestroy(&nax); numaDestroy(&nay); numaDestroy(&naw); numaDestroy(&nah); return box; }
BOX* M_Utils::getColPartImCoords(ColPartition* cp, PIX* im) { BLOBNBOX_CLIST* blobnboxes = cp->boxes(); CLIST_ITERATOR bbox_it(blobnboxes); l_int32 height = (l_int32)im->h; l_int32 left = INT_MAX; l_int32 right = INT_MIN; l_int32 top = INT_MAX; l_int32 bottom = INT_MIN; l_int32 l, r, t, b; for (bbox_it.mark_cycle_pt (); !bbox_it.cycled_list(); bbox_it.forward()) { BLOBNBOX* blobnbox = (BLOBNBOX*)bbox_it.data(); l = (l_int32)blobnbox->cblob()->bounding_box().left(); r = (l_int32)blobnbox->cblob()->bounding_box().right(); t = height - (l_int32)blobnbox->cblob()->bounding_box().top(); b = height - (l_int32)blobnbox->cblob()->bounding_box().bottom(); if(l < left) left = l; if(r > right) right = r; if(t < top) top = t; if(b > bottom) bottom = b; } BOX* boxret = boxCreate(left, top, right-left, bottom-top); return boxret; }
jint Java_com_googlecode_leptonica_android_Box_nativeCreate(JNIEnv *env, jclass clazz, jint x, jint y, jint w, jint h) { LOGV(__FUNCTION__); BOX *box = boxCreate((l_int32) x, (l_int32) y, (l_int32) w, (l_int32) h); return (jint) box; }
BOX* M_Utils::getBlobBoxImCoords(BLOBNBOX* blob, PIX* im) { l_int32 height = (l_int32)im->h; l_int32 left = (l_int32)blob->cblob()->bounding_box().left(); l_int32 top = height - (l_int32)blob->cblob()->bounding_box().top(); l_int32 right = (l_int32)blob->cblob()->bounding_box().right(); l_int32 bottom = height - (l_int32)blob->cblob()->bounding_box().bottom(); return boxCreate(left, top, right-left, bottom-top); }
BOX* M_Utils::tessTBoxToImBox(TBOX* box, PIX* im) { l_int32 height = (l_int32)im->h; l_int32 left = (l_int32)box->left(); l_int32 top = height - (l_int32)box->top(); l_int32 right = (l_int32)box->right(); l_int32 bottom = height - (l_int32)box->bottom(); return boxCreate(left, top, right-left, bottom-top); }
/*! * boxTransform() * * Input: box * shiftx, shifty * scalex, scaley * Return: boxd, or null on error * * Notes: * (1) This is a very simple function that first shifts, then scales. * (2) If the box is invalid, a new invalid box is returned. */ BOX * boxTransform(BOX *box, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley) { PROCNAME("boxTransform"); if (!box) return (BOX *)ERROR_PTR("box not defined", procName, NULL); if (box->w <= 0 || box->h <= 0) return boxCreate(0, 0, 0, 0); else return boxCreate((l_int32)(scalex * (box->x + shiftx) + 0.5), (l_int32)(scaley * (box->y + shifty) + 0.5), (l_int32)(L_MAX(1.0, scalex * box->w + 0.5)), (l_int32)(L_MAX(1.0, scaley * box->h + 0.5))); }
native_data_t() { currentTextBox = boxCreate(0, 0, 0, 0); lastProgress = 0; pix = NULL; data = NULL; debug = false; cachedEnv = NULL; cachedObject = NULL; cancel_ocr = false; }
// Finds horizontal line objects in the given pix. // Uses the given resolution to determine size thresholds instead of any // that may be present in the pix. // The output vectors are owned by the list and Frozen (cannot refit) by // having no boxes, as there is no need to refit or merge separator lines. void LineFinder::FindHorizontalLines(int resolution, Pix* pix, TabVector_LIST* vectors) { #ifdef HAVE_LIBLEPT Pix* line_pix; Boxa* boxes = GetHLineBoxes(resolution, pix, &line_pix); C_BLOB_LIST line_cblobs; int width = pixGetWidth(pix); int height = pixGetHeight(pix); ConvertBoxaToBlobs(height, width, &boxes, &line_cblobs); // Make the BLOBNBOXes from the C_BLOBs. BLOBNBOX_LIST line_bblobs; C_BLOB_IT blob_it(&line_cblobs); BLOBNBOX_IT bbox_it(&line_bblobs); for (blob_it.mark_cycle_pt(); !blob_it.cycled_list(); blob_it.forward()) { C_BLOB* cblob = blob_it.data(); BLOBNBOX* bblob = new BLOBNBOX(cblob); bbox_it.add_to_end(bblob); } ICOORD bleft(0, 0); ICOORD tright(height, width); int vertical_x, vertical_y; FindLineVectors(bleft, tright, &line_bblobs, &vertical_x, &vertical_y, vectors); if (!vectors->empty()) { // Some lines were found, so erase the unused blobs from the line image // and then subtract the line image from the source. bbox_it.move_to_first(); for (bbox_it.mark_cycle_pt(); !bbox_it.cycled_list(); bbox_it.forward()) { BLOBNBOX* blob = bbox_it.data(); if (blob->left_tab_type() == TT_UNCONFIRMED) { const TBOX& box = blob->bounding_box(); // Coords are in tess format so filp x and y and then covert // to leptonica by height -y. Box* pixbox = boxCreate(box.bottom(), height - box.right(), box.height(), box.width()); pixClearInRect(line_pix, pixbox); boxDestroy(&pixbox); } } pixDilateBrick(line_pix, line_pix, 3, 1); pixSubtract(pix, pix, line_pix); if (textord_tabfind_show_vlines) pixWrite("hlinesclean.png", line_pix, IFF_PNG); ICOORD vertical; vertical.set_with_shrink(vertical_x, vertical_y); TabVector::MergeSimilarTabVectors(vertical, vectors, NULL); // Iterate the vectors to flip them. TabVector_IT h_it(vectors); for (h_it.mark_cycle_pt(); !h_it.cycled_list(); h_it.forward()) { h_it.data()->XYFlip(); } } pixDestroy(&line_pix); #endif }
// Helper gets the image of a rectangle, using the block.re_rotation() if // needed to get to the image, and rotating the result back to horizontal // layout. (CJK characters will be on their left sides) The vertical text flag // is set in the returned ImageData if the text was originally vertical, which // can be used to invoke a different CJK recognition engine. The revised_box // is also returned to enable calculation of output bounding boxes. ImageData* Tesseract::GetRectImage(const TBOX& box, const BLOCK& block, int padding, TBOX* revised_box) const { TBOX wbox = box; wbox.pad(padding, padding); *revised_box = wbox; // Number of clockwise 90 degree rotations needed to get back to tesseract // coords from the clipped image. int num_rotations = 0; if (block.re_rotation().y() > 0.0f) num_rotations = 1; else if (block.re_rotation().x() < 0.0f) num_rotations = 2; else if (block.re_rotation().y() < 0.0f) num_rotations = 3; // Handle two cases automatically: 1 the box came from the block, 2 the box // came from a box file, and refers to the image, which the block may not. if (block.bounding_box().major_overlap(*revised_box)) revised_box->rotate(block.re_rotation()); // Now revised_box always refers to the image. // BestPix is never colormapped, but may be of any depth. Pix* pix = BestPix(); int width = pixGetWidth(pix); int height = pixGetHeight(pix); TBOX image_box(0, 0, width, height); // Clip to image bounds; *revised_box &= image_box; if (revised_box->null_box()) return NULL; Box* clip_box = boxCreate(revised_box->left(), height - revised_box->top(), revised_box->width(), revised_box->height()); Pix* box_pix = pixClipRectangle(pix, clip_box, NULL); if (box_pix == NULL) return NULL; boxDestroy(&clip_box); if (num_rotations > 0) { Pix* rot_pix = pixRotateOrth(box_pix, num_rotations); pixDestroy(&box_pix); box_pix = rot_pix; } // Convert sub-8-bit images to 8 bit. int depth = pixGetDepth(box_pix); if (depth < 8) { Pix* grey; grey = pixConvertTo8(box_pix, false); pixDestroy(&box_pix); box_pix = grey; } bool vertical_text = false; if (num_rotations > 0) { // Rotated the clipped revised box back to internal coordinates. FCOORD rotation(block.re_rotation().x(), -block.re_rotation().y()); revised_box->rotate(rotation); if (num_rotations != 2) vertical_text = true; } return new ImageData(vertical_text, box_pix); }
/*! * \brief recogRescoreDidResult() * * \param[in] recog with LUT's pre-computed * \param[out] ppixdb [optional] debug result; can be null * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This does correlation matching with all templates using the * viterbi path segmentation. * </pre> */ static l_int32 recogRescoreDidResult(L_RECOG *recog, PIX **ppixdb) { l_int32 i, n, w2, h1, templ, x, xloc, dely, index; char *text; l_float32 score; BOX *box1; PIX *pixs, *pix1; L_RDID *did; PROCNAME("recogRescoreDidResult"); if (ppixdb) *ppixdb = NULL; if (!recog) return ERROR_INT("recog not defined", procName, 1); if ((did = recogGetDid(recog)) == NULL) return ERROR_INT("did not defined", procName, 1); if (did->fullarrays == 0) return ERROR_INT("did full arrays not made", procName, 1); if ((n = numaGetCount(did->naxloc)) == 0) return ERROR_INT("no elements in path", procName, 1); pixs = did->pixs; h1 = pixGetHeight(pixs); for (i = 0; i < n; i++) { numaGetIValue(did->natempl, i, &templ); numaGetIValue(did->naxloc, i, &xloc); numaGetIValue(did->nadely, i, &dely); pixaGetPixDimensions(recog->pixa_u, templ, &w2, NULL, NULL); /* TODO: try to fix xloc - 4, etc. */ x = L_MAX(xloc, 0); box1 = boxCreate(x, dely, w2, h1); pix1 = pixClipRectangle(pixs, box1, NULL); recogIdentifyPix(recog, pix1, NULL); recogTransferRchToDid(recog, x, dely); if (ppixdb) { rchExtract(recog->rch, &index, &score, &text, NULL, NULL, NULL, NULL); fprintf(stderr, "text = %s, index = %d, score = %5.3f\n", text, index, score); } pixDestroy(&pix1); boxDestroy(&box1); LEPT_FREE(text); } /* numaWriteStream(stderr, recog->did->nadely_r); */ if (ppixdb) *ppixdb = recogShowPath(recog, 1); return 0; }
/*! * boxCreateValid() * * Input: x, y, w, h * Return: box, or null on error * * Notes: * (1) This returns NULL if either w = 0 or h = 0. */ BOX * boxCreateValid(l_int32 x, l_int32 y, l_int32 w, l_int32 h) { PROCNAME("boxCreateValid"); if (w <= 0 || h <= 0) return (BOX *)ERROR_PTR("w and h not both > 0", procName, NULL); return boxCreate(x, y, w, h); }
/*! * \brief ptaConvertToBoxa() * * \param[in] pta * \param[in] ncorners 2 or 4 for the representation of each box * \return boxa with one box for each 2 or 4 points in the pta, * or NULL on error * * <pre> * Notes: * (1) For 2 corners, the order of the 2 points is UL, LR. * For 4 corners, the order of points is UL, UR, LL, LR. * (2) Each derived box is the minimum size containing all corners. * </pre> */ BOXA * ptaConvertToBoxa(PTA *pta, l_int32 ncorners) { l_int32 i, n, nbox, x1, y1, x2, y2, x3, y3, x4, y4, x, y, xmax, ymax; BOX *box; BOXA *boxa; PROCNAME("ptaConvertToBoxa"); if (!pta) return (BOXA *)ERROR_PTR("pta not defined", procName, NULL); if (ncorners != 2 && ncorners != 4) return (BOXA *)ERROR_PTR("ncorners not 2 or 4", procName, NULL); n = ptaGetCount(pta); if (n % ncorners != 0) return (BOXA *)ERROR_PTR("size % ncorners != 0", procName, NULL); nbox = n / ncorners; if ((boxa = boxaCreate(nbox)) == NULL) return (BOXA *)ERROR_PTR("boxa not made", procName, NULL); for (i = 0; i < n; i += ncorners) { ptaGetIPt(pta, i, &x1, &y1); ptaGetIPt(pta, i + 1, &x2, &y2); if (ncorners == 2) { box = boxCreate(x1, y1, x2 - x1 + 1, y2 - y1 + 1); boxaAddBox(boxa, box, L_INSERT); continue; } ptaGetIPt(pta, i + 2, &x3, &y3); ptaGetIPt(pta, i + 3, &x4, &y4); x = L_MIN(x1, x3); y = L_MIN(y1, y2); xmax = L_MAX(x2, x4); ymax = L_MAX(y3, y4); box = boxCreate(x, y, xmax - x + 1, ymax - y + 1); boxaAddBox(boxa, box, L_INSERT); } return boxa; }
/** * Returns a binary image of the current object at the given level. * The position and size match the return from BoundingBoxInternal, and so this * could be upscaled with respect to the original input image. * Use pixDestroy to delete the image after use. * The following methods are used to generate the images: * RIL_BLOCK: mask the page image with the block polygon. * RIL_TEXTLINE: Clip the rectangle of the line box from the page image. * TODO(rays) fix this to generate and use a line polygon. * RIL_WORD: Clip the rectangle of the word box from the page image. * RIL_SYMBOL: Render the symbol outline to an image for cblobs (prior * to recognition) or the bounding box otherwise. * A reconstruction of the original image (using xor to check for double * representation) should be reasonably accurate, * apart from removed noise, at the block level. Below the block level, the * reconstruction will be missing images and line separators. * At the symbol level, kerned characters will be invade the bounding box * if rendered after recognition, making an xor reconstruction inaccurate, but * an or construction better. Before recognition, symbol-level reconstruction * should be good, even with xor, since the images come from the connected * components. */ Pix* PageIterator::GetBinaryImage(PageIteratorLevel level) const { int left, top, right, bottom; if (!BoundingBoxInternal(level, &left, &top, &right, &bottom)) return NULL; Pix* pix = NULL; switch (level) { case RIL_BLOCK: case RIL_PARA: int bleft, btop, bright, bbottom; BoundingBoxInternal(RIL_BLOCK, &bleft, &btop, &bright, &bbottom); pix = it_->block()->block->render_mask(); // AND the mask and the image. pixRasterop(pix, 0, 0, pixGetWidth(pix), pixGetHeight(pix), PIX_SRC & PIX_DST, tesseract_->pix_binary(), bleft, btop); if (level == RIL_PARA) { // RIL_PARA needs further attention: // clip the paragraph from the block mask. Box* box = boxCreate(left - bleft, top - btop, right - left, bottom - top); Pix* pix2 = pixClipRectangle(pix, box, NULL); boxDestroy(&box); pixDestroy(&pix); pix = pix2; } break; case RIL_TEXTLINE: case RIL_WORD: case RIL_SYMBOL: if (level == RIL_SYMBOL && cblob_it_ != NULL && cblob_it_->data()->area() != 0) return cblob_it_->data()->render(); // Just clip from the bounding box. Box* box = boxCreate(left, top, right - left, bottom - top); pix = pixClipRectangle(tesseract_->pix_binary(), box, NULL); boxDestroy(&box); break; } return pix; }
int main(int argc, char **argv) { char *dirin, *dirout, *infile, *outfile, *tail; l_int32 i, nfiles, border, x, y, w, h, xb, yb, wb, hb; BOX *box1, *box2; BOXA *boxa1, *boxa2; PIX *pixs, *pixt1, *pixd; SARRAY *safiles; static char mainName[] = "croptext"; if (argc != 4) return ERROR_INT("Syntax: croptext dirin border dirout", mainName, 1); dirin = argv[1]; border = atoi(argv[2]); dirout = argv[3]; setLeptDebugOK(1); safiles = getSortedPathnamesInDirectory(dirin, NULL, 0, 0); nfiles = sarrayGetCount(safiles); for (i = 0; i < nfiles; i++) { infile = sarrayGetString(safiles, i, L_NOCOPY); splitPathAtDirectory(infile, NULL, &tail); outfile = genPathname(dirout, tail); pixs = pixRead(infile); pixt1 = pixMorphSequence(pixs, "r11 + c10.40 + o5.5 + x4", 0); boxa1 = pixConnComp(pixt1, NULL, 8); if (boxaGetCount(boxa1) == 0) { fprintf(stderr, "Warning: no components on page %s\n", tail); continue; } boxa2 = boxaSort(boxa1, L_SORT_BY_AREA, L_SORT_DECREASING, NULL); box1 = boxaGetBox(boxa2, 0, L_CLONE); boxGetGeometry(box1, &x, &y, &w, &h); xb = L_MAX(0, x - border); yb = L_MAX(0, y - border); wb = w + 2 * border; hb = h + 2 * border; box2 = boxCreate(xb, yb, wb, hb); pixd = pixClipRectangle(pixs, box2, NULL); pixWrite(outfile, pixd, IFF_TIFF_G4); pixDestroy(&pixs); pixDestroy(&pixt1); pixDestroy(&pixd); boxaDestroy(&boxa1); boxaDestroy(&boxa2); } return 0; }
unsigned long long getImagePhash_px( PIX *pix_orig, char *tmpFilename ) { int free_8 = 0; // Convert colour images down to grey PIX *pix8; if( pixGetDepth(pix_orig) > 8 ) { pix8 = pixScaleRGBToGrayFast( pix_orig, 1, COLOR_GREEN ); if( pix8 == NULL ) { printf("Covertion to 8bit, did not go well."); return 0; } free_8 = 1; } else { // already gray free_8 = 0; pix8 = pix_orig; } int width = pixGetWidth( pix8 ); int height = pixGetHeight( pix8 ); BOX* box = boxCreate(1, 1, width-2, height-2); PIX* pixc = pixClipRectangle(pix8, box, NULL); if(free_8 == 1) { pixDestroy( &pix8 ); } PIX *pix8s = pixScale(pixc, 0.2, 0.2); pixDestroy( &pixc ); // Convert image down to binary (no gray) /* PIX *pix1 = pixThresholdToBinary( pix8s, 200 ); if( pix1 == NULL ) { printf( "Covertion to 1bit, did not go well."); pixDestroy( &pix8s ); return 0; } pixDestroy( &pix8s ); */ // Save the file for pHash processnig pixWrite( tmpFilename, pix8s, IFF_JFIF_JPEG); pixDestroy( &pix8s ); unsigned long long ret = calculateImagePhash( tmpFilename ); //unlink(tmpFilename); return ret; }
main(int argc, char **argv) { l_int32 i, k, x, y, w, h; BOX *box; BOXA *boxa1, *boxa2; PIX *pix1, *pix2, *pixd; PIXA *pixa; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; for (k = 0; k < 7; k++) { srand(45617); pixa = pixaCreate(2); boxa1 = boxaCreate(0); for (i = 0; i < 500; i++) { x = (l_int32)(600.0 * (l_float64)rand() / (l_float64)RAND_MAX); y = (l_int32)(600.0 * (l_float64)rand() / (l_float64)RAND_MAX); w = (l_int32) (1.0 + maxsize[k] * (l_float64)rand() / (l_float64)RAND_MAX); h = (l_int32) (1.0 + maxsize[k] * (l_float64)rand() / (l_float64)RAND_MAX); box = boxCreate(x, y, w, h); boxaAddBox(boxa1, box, L_INSERT); } pix1 = pixCreate(660, 660, 1); pixRenderBoxa(pix1, boxa1, 1, L_SET_PIXELS); pixaAddPix(pixa, pix1, L_INSERT); boxa2 = boxaCombineOverlaps(boxa1); pix2 = pixCreate(660, 660, 1); pixRenderBoxa(pix2, boxa2, 1, L_SET_PIXELS); pixaAddPix(pixa, pix2, L_INSERT); pixd = pixaDisplayTiledInRows(pixa, 1, 1500, 1.0, 0, 50, 2); pixDisplayWithTitle(pixd, 100, 100 + 100 * k, NULL, rp->display); regTestWritePixAndCheck(rp, pixd, IFF_PNG); fprintf(stderr, "%d: n_init = %d, n_final = %d\n", k, boxaGetCount(boxa1), boxaGetCount(boxa2)); pixDestroy(&pixd); boxaDestroy(&boxa1); boxaDestroy(&boxa2); pixaDestroy(&pixa); } regTestCleanup(rp); return 0; }
/*! * boxCopy() * * Input: box * Return: copy of box, or null on error */ BOX * boxCopy(BOX *box) { BOX *boxc; PROCNAME("boxCopy"); if (!box) return (BOX *)ERROR_PTR("box not defined", procName, NULL); boxc = boxCreate(box->x, box->y, box->w, box->h); return boxc; }
/*! * boxaaGetExtent() * * Input: boxaa * &w (<optional return> width) * &h (<optional return> height) * &box (<optional return>, minimum box containing all boxa * in boxaa) * Return: 0 if OK, 1 on error * * Notes: * (1) The returned w and h are the minimum size image * that would contain all boxes untranslated. */ l_int32 boxaaGetExtent(BOXAA *boxaa, l_int32 *pw, l_int32 *ph, BOX **pbox) { l_int32 i, j, n, m, x, y, w, h, xmax, ymax, xmin, ymin, found; BOXA *boxa; PROCNAME("boxaaGetExtent"); if (!pw && !ph && !pbox) return ERROR_INT("no ptrs defined", procName, 1); if (pbox) *pbox = NULL; if (pw) *pw = 0; if (ph) *ph = 0; if (!boxaa) return ERROR_INT("boxaa not defined", procName, 1); n = boxaaGetCount(boxaa); if (n == 0) return ERROR_INT("no boxa in boxaa", procName, 1); xmax = ymax = 0; xmin = ymin = 100000000; found = FALSE; for (i = 0; i < n; i++) { boxa = boxaaGetBoxa(boxaa, i, L_CLONE); m = boxaGetCount(boxa); for (j = 0; j < m; j++) { boxaGetBoxGeometry(boxa, j, &x, &y, &w, &h); if (w <= 0 || h <= 0) continue; found = TRUE; xmin = L_MIN(xmin, x); ymin = L_MIN(ymin, y); xmax = L_MAX(xmax, x + w); ymax = L_MAX(ymax, y + h); } } if (!found) return ERROR_INT("no valid boxes in boxaa", procName, 1); if (pw) *pw = xmax; if (ph) *ph = ymax; if (pbox) *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin); return 0; }