Pix* binarizeLegacy(Pix* pix){ Pix* phm = NULL; Pix* pixb = binarizeTiled(pix, phm); pixCopyResolution(pixb, pix); return pixb; }
/*! * pixExpandBinaryPower2() * * Input: pixs (1 bpp) * factor (expansion factor: 1, 2, 4, 8, 16) * Return: pixd (expanded 1 bpp by replication), or null on error */ PIX * pixExpandBinaryPower2(PIX *pixs, l_int32 factor) { l_int32 w, h, d, wd, hd, wpls, wpld; l_uint32 *datas, *datad; PIX *pixd; PROCNAME("pixExpandBinaryPower2"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); if (factor == 1) return pixCopy(NULL, pixs); if (factor != 2 && factor != 4 && factor != 8 && factor != 16) return (PIX *)ERROR_PTR("factor must be in {2,4,8,16}", procName, NULL); wpls = pixGetWpl(pixs); datas = pixGetData(pixs); wd = factor * w; hd = factor * h; if ((pixd = pixCreate(wd, hd, 1)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor); wpld = pixGetWpl(pixd); datad = pixGetData(pixd); expandBinaryPower2Low(datad, wd, hd, wpld, datas, w, h, wpls, factor); return pixd; }
/*! * pixFinalAccumulateThreshold() * * Input: pixs (32 bpp) * offset (same as used for initialization) * threshold (values less than this are set in the destination) * Return: pixd (1 bpp), or null on error * * Notes: * (1) The offset must be >= 0 and should not exceed 0x40000000. * (2) The offset is subtracted from the src 32 bpp image */ PIX * pixFinalAccumulateThreshold(PIX *pixs, l_uint32 offset, l_uint32 threshold) { l_int32 w, h, wpls, wpld; l_uint32 *datas, *datad; PIX *pixd; PROCNAME("pixFinalAccumulateThreshold"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); if (offset > 0x40000000) offset = 0x40000000; pixGetDimensions(pixs, &w, &h, NULL); if ((pixd = pixCreate(w, h, 1)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ datas = pixGetData(pixs); datad = pixGetData(pixd); wpls = pixGetWpl(pixs); wpld = pixGetWpl(pixd); finalAccumulateThreshLow(datad, w, h, wpld, datas, wpls, offset, threshold); return pixd; }
/*! * pixAddRGB() * * Input: pixs1, pixs2 (32 bpp RGB, or colormapped) * Return: pixd, or null on error * * Notes: * (1) Clips computation to the minimum size, aligning the UL corners. * (2) Removes any colormap to RGB, and ignores the LSB of each * pixel word (the alpha channel). * (3) Adds each component value, pixelwise, clipping to 255. * (4) This is useful to combine two images where most of the * pixels are essentially black, such as in pixPerceptualDiff(). */ PIX * pixAddRGB(PIX *pixs1, PIX *pixs2) { l_int32 i, j, w, h, d, w2, h2, d2, wplc1, wplc2, wpld; l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval; l_uint32 *datac1, *datac2, *datad, *linec1, *linec2, *lined; PIX *pixc1, *pixc2, *pixd; PROCNAME("pixAddRGB"); if (!pixs1) return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); if (!pixs2) return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); pixGetDimensions(pixs1, &w, &h, &d); pixGetDimensions(pixs2, &w2, &h2, &d2); if (!pixGetColormap(pixs1) && d != 32) return (PIX *)ERROR_PTR("pixs1 not cmapped or rgb", procName, NULL); if (!pixGetColormap(pixs2) && d2 != 32) return (PIX *)ERROR_PTR("pixs2 not cmapped or rgb", procName, NULL); if (pixGetColormap(pixs1)) pixc1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); else pixc1 = pixClone(pixs1); if (pixGetColormap(pixs2)) pixc2 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_FULL_COLOR); else pixc2 = pixClone(pixs2); w = L_MIN(w, w2); h = L_MIN(h, h2); pixd = pixCreate(w, h, 32); pixCopyResolution(pixd, pixs1); datac1 = pixGetData(pixc1); datac2 = pixGetData(pixc2); datad = pixGetData(pixd); wplc1 = pixGetWpl(pixc1); wplc2 = pixGetWpl(pixc2); wpld = pixGetWpl(pixd); for (i = 0; i < h; i++) { linec1 = datac1 + i * wplc1; linec2 = datac2 + i * wplc2; lined = datad + i * wpld; for (j = 0; j < w; j++) { extractRGBValues(linec1[j], &rval1, &gval1, &bval1); extractRGBValues(linec2[j], &rval2, &gval2, &bval2); rval = L_MIN(255, rval1 + rval2); gval = L_MIN(255, gval1 + gval2); bval = L_MIN(255, bval1 + bval2); composeRGBPixel(rval, gval, bval, lined + j); } } pixDestroy(&pixc1); pixDestroy(&pixc2); return pixd; }
/*! * pixEmbedForRotation() * * Input: pixs (1, 2, 4, 8, 32 bpp rgb) * angle (radians; clockwise is positive) * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) * width (original width; use 0 to avoid embedding) * height (original height; use 0 to avoid embedding) * Return: pixd, or null on error * * Notes: * (1) For very small rotations, just return a clone. * (2) Generate larger image to embed pixs if necessary, and * place the center of the input image in the center. * (3) Rotation brings either white or black pixels in * from outside the image. For colormapped images where * there is no white or black, a new color is added if * possible for these pixels; otherwise, either the * lightest or darkest color is used. In most cases, * the colormap will be removed prior to rotation. * (4) The dest is to be expanded so that no image pixels * are lost after rotation. Input of the original width * and height allows the expansion to stop at the maximum * required size, which is a square with side equal to * sqrt(w*w + h*h). * (5) For an arbitrary angle, the expansion can be found by * considering the UL and UR corners. As the image is * rotated, these move in an arc centered at the center of * the image. Normalize to a unit circle by dividing by half * the image diagonal. After a rotation of T radians, the UL * and UR corners are at points T radians along the unit * circle. Compute the x and y coordinates of both these * points and take the max of absolute values; these represent * the half width and half height of the containing rectangle. * The arithmetic is done using formulas for sin(a+b) and cos(a+b), * where b = T. For the UR corner, sin(a) = h/d and cos(a) = w/d. * For the UL corner, replace a by (pi - a), and you have * sin(pi - a) = h/d, cos(pi - a) = -w/d. The equations * given below follow directly. */ PIX * pixEmbedForRotation(PIX *pixs, l_float32 angle, l_int32 incolor, l_int32 width, l_int32 height) { l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor; l_float64 sina, cosa, fw, fh; PIX *pixd; PROCNAME("pixEmbedForRotation"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE) return pixClone(pixs); /* Test if big enough to hold any rotation of the original image */ pixGetDimensions(pixs, &w, &h, &d); maxside = (l_int32)(sqrt((l_float64)(width * width) + (l_float64)(height * height)) + 0.5); if (w >= maxside && h >= maxside) /* big enough */ return pixClone(pixs); /* Find the new sizes required to hold the image after rotation. * Note that the new dimensions must be at least as large as those * of pixs, because we're rasterop-ing into it before rotation. */ cosa = cos(angle); sina = sin(angle); fw = (l_float64)w; fh = (l_float64)h; w1 = (l_int32)(L_ABS(fw * cosa - fh * sina) + 0.5); w2 = (l_int32)(L_ABS(-fw * cosa - fh * sina) + 0.5); h1 = (l_int32)(L_ABS(fw * sina + fh * cosa) + 0.5); h2 = (l_int32)(L_ABS(-fw * sina + fh * cosa) + 0.5); wnew = L_MAX(w, L_MAX(w1, w2)); hnew = L_MAX(h, L_MAX(h1, h2)); if ((pixd = pixCreate(wnew, hnew, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); pixCopyColormap(pixd, pixs); pixCopySpp(pixd, pixs); pixCopyText(pixd, pixs); xoff = (wnew - w) / 2; yoff = (hnew - h) / 2; /* Set background to color to be rotated in */ setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE; pixSetBlackOrWhite(pixd, setcolor); /* Rasterop automatically handles all 4 channels for rgba */ pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0); return pixd; }
/*! * pixSelectByWidthHeightRatio() * * Input: pixs (1 bpp) * thresh (threshold ratio of width/height) * connectivity (4 or 8) * type (L_SELECT_IF_LT, L_SELECT_IF_GT, * L_SELECT_IF_LTE, L_SELECT_IF_GTE) * &changed (<optional return> 1 if changed; 0 if clone returned) * Return: pixd, or null on error * * Notes: * (1) The args specify constraints on the width-to-height ratio * for components that are kept. * (2) If unchanged, returns a copy of pixs. Otherwise, * returns a new pix with the filtered components. * (3) This filters components based on the width-to-height ratios. * (4) Use L_SELECT_IF_LT or L_SELECT_IF_LTE to save components * with less than the threshold ratio, and * L_SELECT_IF_GT or L_SELECT_IF_GTE to remove them. */ PIX * pixSelectByWidthHeightRatio(PIX *pixs, l_float32 thresh, l_int32 connectivity, l_int32 type, l_int32 *pchanged) { l_int32 w, h, empty, changed, count; BOXA *boxa; PIX *pixd; PIXA *pixas, *pixad; PROCNAME("pixSelectByWidthHeightRatio"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (connectivity != 4 && connectivity != 8) return (PIX *)ERROR_PTR("connectivity not 4 or 8", procName, NULL); if (type != L_SELECT_IF_LT && type != L_SELECT_IF_GT && type != L_SELECT_IF_LTE && type != L_SELECT_IF_GTE) return (PIX *)ERROR_PTR("invalid type", procName, NULL); if (pchanged) *pchanged = FALSE; /* Check if any components exist */ pixZero(pixs, &empty); if (empty) return pixCopy(NULL, pixs); /* Filter components */ boxa = pixConnComp(pixs, &pixas, connectivity); pixad = pixaSelectByWidthHeightRatio(pixas, thresh, type, &changed); boxaDestroy(&boxa); pixaDestroy(&pixas); /* Render the result */ if (!changed) { pixaDestroy(&pixad); return pixCopy(NULL, pixs); } else { if (pchanged) *pchanged = TRUE; pixGetDimensions(pixs, &w, &h, NULL); count = pixaGetCount(pixad); if (count == 0) /* return empty pix */ pixd = pixCreateTemplate(pixs); else { pixd = pixaDisplay(pixad, w, h); pixCopyResolution(pixd, pixs); pixCopyColormap(pixd, pixs); pixCopyText(pixd, pixs); pixCopyInputFormat(pixd, pixs); } pixaDestroy(&pixad); return pixd; } }
Pix* edgeBinarize(Pix* pix){ FUNCNAME("edgeBinarize"); #if HAS_ADAPTIVE_BINARIZER PixAdaptiveBinarizer binarizer(false); return binarizer.bradleyAdaptiveThresholding(pix, 0.15, 8); #endif Pix* result; pixOtsuAdaptiveThreshold(pix, pixGetWidth(pix), pixGetHeight(pix), 0, 0, 0.1, NULL, &result); pixCopyResolution(result, pix); return result; }
/*! * \brief pixColorSegmentCluster() * * \param[in] pixs 32 bpp; 24-bit color * \param[in] maxdist max euclidean dist to existing cluster * \param[in] maxcolors max number of colors allowed in first pass * \param[in] debugflag 1 for debug output; 0 otherwise * \return pixd 8 bit with colormap, or NULL on error * * <pre> * Notes: * (1) This is phase 1. See description in pixColorSegment(). * (2) Greedy unsupervised classification. If the limit 'maxcolors' * is exceeded, the computation is repeated with a larger * allowed cluster size. * (3) On each successive iteration, 'maxdist' is increased by a * constant factor. See comments in pixColorSegment() for * a guideline on parameter selection. * Note that the diagonal of the 8-bit rgb color cube is about * 440, so for 'maxdist' = 440, you are guaranteed to get 1 color! * </pre> */ PIX * pixColorSegmentCluster(PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 debugflag) { l_int32 w, h, newmaxdist, ret, niters, ncolors, success; PIX *pixd; PIXCMAP *cmap; PROCNAME("pixColorSegmentCluster"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); pixGetDimensions(pixs, &w, &h, NULL); if ((pixd = pixCreate(w, h, 8)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); cmap = pixcmapCreate(8); pixSetColormap(pixd, cmap); pixCopyResolution(pixd, pixs); newmaxdist = maxdist; niters = 0; success = TRUE; while (1) { ret = pixColorSegmentTryCluster(pixd, pixs, newmaxdist, maxcolors, debugflag); niters++; if (!ret) { ncolors = pixcmapGetCount(cmap); if (debugflag) L_INFO("Success with %d colors after %d iters\n", procName, ncolors, niters); break; } if (niters == MAX_ALLOWED_ITERATIONS) { L_WARNING("too many iters; newmaxdist = %d\n", procName, newmaxdist); success = FALSE; break; } newmaxdist = (l_int32)(DIST_EXPAND_FACT * (l_float32)newmaxdist); } if (!success) { pixDestroy(&pixd); return (PIX *)ERROR_PTR("failure in phase 1", procName, NULL); } return pixd; }
/*! * pixMaxDynamicRange() * * Input: pixs (4, 8, 16 or 32 bpp source) * type (L_LINEAR_SCALE or L_LOG_SCALE) * Return: pixd (8 bpp), or null on error * * Notes: * (1) Scales pixel values to fit maximally within the dest 8 bpp pixd * (2) Uses a LUT for log scaling */ PIX * pixMaxDynamicRange(PIX *pixs, l_int32 type) { l_uint8 dval; l_int32 i, j, w, h, d, wpls, wpld, max, sval; l_uint32 *datas, *datad; l_uint32 word; l_uint32 *lines, *lined; l_float32 factor; l_float32 *tab; PIX *pixd; PROCNAME("pixMaxDynamicRange"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); d = pixGetDepth(pixs); if (d != 4 && d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("pixs not in {4,8,16,32} bpp", procName, NULL); if (type != L_LINEAR_SCALE && type != L_LOG_SCALE) return (PIX *)ERROR_PTR("invalid type", procName, NULL); pixGetDimensions(pixs, &w, &h, NULL); if ((pixd = pixCreate(w, h, 8)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); datas = pixGetData(pixs); datad = pixGetData(pixd); wpls = pixGetWpl(pixs); wpld = pixGetWpl(pixd); /* Get max */ max = 0; for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < wpls; j++) { word = *(lines + j); if (d == 4) { max = L_MAX(max, word >> 28); max = L_MAX(max, (word >> 24) & 0xf); max = L_MAX(max, (word >> 20) & 0xf); max = L_MAX(max, (word >> 16) & 0xf); max = L_MAX(max, (word >> 12) & 0xf); max = L_MAX(max, (word >> 8) & 0xf); max = L_MAX(max, (word >> 4) & 0xf); max = L_MAX(max, word & 0xf); } else if (d == 8) { max = L_MAX(max, word >> 24); max = L_MAX(max, (word >> 16) & 0xff); max = L_MAX(max, (word >> 8) & 0xff); max = L_MAX(max, word & 0xff); } else if (d == 16) {
/*! * \brief pixExpandBinaryReplicate() * * \param[in] pixs 1 bpp * \param[in] xfact integer scale factor for horiz. replicative expansion * \param[in] yfact integer scale factor for vertical replicative expansion * \return pixd scaled up, or NULL on error */ PIX * pixExpandBinaryReplicate(PIX *pixs, l_int32 xfact, l_int32 yfact) { l_int32 w, h, d, wd, hd, wpls, wpld, i, j, k, start; l_uint32 *datas, *datad, *lines, *lined; PIX *pixd; PROCNAME("pixExpandBinaryReplicate"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); if (xfact <= 0 || yfact <= 0) return (PIX *)ERROR_PTR("invalid scale factor: <= 0", procName, NULL); if (xfact == yfact) { if (xfact == 1) return pixCopy(NULL, pixs); if (xfact == 2 || xfact == 4 || xfact == 8 || xfact == 16) return pixExpandBinaryPower2(pixs, xfact); } wpls = pixGetWpl(pixs); datas = pixGetData(pixs); wd = xfact * w; hd = yfact * h; if ((pixd = pixCreate(wd, hd, 1)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); pixScaleResolution(pixd, (l_float32)xfact, (l_float32)yfact); wpld = pixGetWpl(pixd); datad = pixGetData(pixd); for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + yfact * i * wpld; for (j = 0; j < w; j++) { /* replicate pixels on a single line */ if (GET_DATA_BIT(lines, j)) { start = xfact * j; for (k = 0; k < xfact; k++) SET_DATA_BIT(lined, start + k); } } for (k = 1; k < yfact; k++) /* replicate the line */ memcpy(lined + k * wpld, lined, 4 * wpld); } return pixd; }
Pix* dewarpOrDeskew(Pix* pix) { FUNCNAME("dewarpOrDeskew"); Pix* pixText = NULL; l_int32 dewarpResult = pixDewarp(pix, &pixText); if(dewarpResult){ L_INFO("dewarp failed. Attempting to correct skew instead.", procName); pixText = deskew(pix); } pixCopyResolution(pixText, pix); return pixText; }
Pix* binarize(Pix* pix, ProgressCallback* callback) { FUNCNAME("binarize"); Pix *pixb; #ifdef HAS_ADAPTIVE_BINARIZER PixBinarizer binarizer(false); pixb = binarizer.binarize(pix, callback); #else pixb = binarizeTiled(pix, NULL); #endif pixCopyResolution(pixb, pix); return pixb; }
/*! * pixExpandBinaryReplicate() * * Input: pixs (1 bpp) * factor (integer scale factor for replicative expansion) * Return: pixd (scaled up), or null on error */ PIX * pixExpandBinaryReplicate(PIX *pixs, l_int32 factor) { l_int32 w, h, d, wd, hd, wpls, wpld, i, j, k, start; l_uint32 *datas, *datad, *lines, *lined; PIX *pixd; PROCNAME("pixExpandBinaryReplicate"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); if (factor <= 0) return (PIX *)ERROR_PTR("factor <= 0; invalid", procName, NULL); if (factor == 1) return pixCopy(NULL, pixs); if (factor == 2 || factor == 4 || factor == 8 || factor == 16) return pixExpandBinaryPower2(pixs, factor); wpls = pixGetWpl(pixs); datas = pixGetData(pixs); wd = factor * w; hd = factor * h; if ((pixd = pixCreate(wd, hd, 1)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor); wpld = pixGetWpl(pixd); datad = pixGetData(pixd); for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + factor * i * wpld; for (j = 0; j < w; j++) { if (GET_DATA_BIT(lines, j)) { start = factor * j; for (k = 0; k < factor; k++) SET_DATA_BIT(lined, start + k); } } for (k = 1; k < factor; k++) memcpy(lined + k * wpld, lined, 4 * wpld); } return pixd; }
/*! * pixTransferAllData() * * Input: pixd (must be different from pixs) * &pixs (will be nulled if refcount goes to 0) * copytext (1 to copy the text field; 0 to skip) * copyformat (1 to copy the informat field; 0 to skip) * Return: 0 if OK, 1 on error * * Notes: * (1) This does a complete data transfer from pixs to pixd, * followed by the destruction of pixs (refcount permitting). * (2) If the refcount of pixs is 1, pixs is destroyed. Otherwise, * the data in pixs is copied (rather than transferred) to pixd. * (3) This operation, like all others with a pre-existing pixd, * will side-effect any existing clones of pixd. The pixd * refcount does not change. * (4) When might you use this? Suppose you have an in-place Pix * function (returning void) with the typical signature: * void function-inplace(PIX *pix, ...) * where "..." are non-pointer input parameters, and suppose * further that you sometimes want to return an arbitrary Pix * in place of the input Pix. There are two ways you can do this: * (a) The straightforward way is to change the function * signature to take the address of the Pix ptr: * void function-inplace(PIX **ppix, ...) { * PIX *pixt = function-makenew(*ppix); * pixDestroy(ppix); * *ppix = pixt; * return; * } * Here, the input and returned pix are different, as viewed * by the calling function, and the inplace function is * expected to destroy the input pix to avoid a memory leak. * (b) Keep the signature the same and use pixTransferAllData() * to return the new Pix in the input Pix struct: * void function-inplace(PIX *pix, ...) { * PIX *pixt = function-makenew(pix); * pixTransferAllData(pix, &pixt); // pixt is destroyed * return; * } * Here, the input and returned pix are the same, as viewed * by the calling function, and the inplace function must * never destroy the input pix, because the calling function * maintains an unchanged handle to it. */ l_int32 pixTransferAllData(PIX *pixd, PIX **ppixs, l_int32 copytext, l_int32 copyformat) { l_int32 nbytes; PIX *pixs; PROCNAME("pixTransferAllData"); if (!ppixs) return ERROR_INT("&pixs not defined", procName, 1); if ((pixs = *ppixs) == NULL) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixs == pixd) /* no-op */ return ERROR_INT("pixd == pixs", procName, 1); if (pixGetRefcount(pixs) == 1) { /* transfer the data, cmap, text */ pixFreeData(pixd); /* dealloc any existing data */ pixSetData(pixd, pixGetData(pixs)); /* transfer new data from pixs */ pixs->data = NULL; /* pixs no longer owns data */ pixSetColormap(pixd, pixGetColormap(pixs)); /* frees old; sets new */ pixs->colormap = NULL; /* pixs no longer owns colormap */ if (copytext) { pixSetText(pixd, pixGetText(pixs)); pixSetText(pixs, NULL); } } else { /* preserve pixs by making a copy of the data, cmap, text */ pixResizeImageData(pixd, pixs); nbytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); memcpy((char *)pixGetData(pixd), (char *)pixGetData(pixs), nbytes); pixCopyColormap(pixd, pixs); if (copytext) pixCopyText(pixd, pixs); } pixCopyResolution(pixd, pixs); pixCopyDimensions(pixd, pixs); if (copyformat) pixCopyInputFormat(pixd, pixs); /* This will destroy pixs if data was transferred; * otherwise, it just decrements its refcount. */ pixDestroy(ppixs); return 0; }
Pix* savGol(Pix* pix) { FUNCNAME("savGol"); l_int32 xres, yres; pixGetResolution(pix, &xres, &yres); l_uint8 degree = 4; l_float32 textLineHeight = yres/6.25; l_uint8 window = L_MIN(15, L_MAX(5, textLineHeight/4)); if(window % 2 == 0){ window--; } L_INFO("text line height = %.2f, window = %i, degree = %i\n", procName, textLineHeight, window, degree); Pix* result = pixSavGolFilter(pix, window, degree, degree); pixCopyResolution(result, pix); return result; }
/*! * pixCopy() * * Input: pixd (<optional>; can be null, or equal to pixs, * or different from pixs) * pixs * Return: pixd, or null on error * * Notes: * (1) There are three cases: * (a) pixd == null (makes a new pix; refcount = 1) * (b) pixd == pixs (no-op) * (c) pixd != pixs (data copy; no change in refcount) * If the refcount of pixd > 1, case (c) will side-effect * these handles. * (2) The general pattern of use is: * pixd = pixCopy(pixd, pixs); * This will work for all three cases. * For clarity when the case is known, you can use: * (a) pixd = pixCopy(NULL, pixs); * (c) pixCopy(pixd, pixs); * (3) For case (c), we check if pixs and pixd are the same * size (w,h,d). If so, the data is copied directly. * Otherwise, the data is reallocated to the correct size * and the copy proceeds. The refcount of pixd is unchanged. * (4) This operation, like all others that may involve a pre-existing * pixd, will side-effect any existing clones of pixd. */ PIX * pixCopy(PIX *pixd, /* can be null */ PIX *pixs) { l_int32 bytes; l_uint32 *datas, *datad; PROCNAME("pixCopy"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixs == pixd) return pixd; /* Total bytes in image data */ bytes = 4 * pixGetWpl(pixs) * pixGetHeight(pixs); /* If we're making a new pix ... */ if (!pixd) { if ((pixd = pixCreateTemplate(pixs)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); datas = pixGetData(pixs); datad = pixGetData(pixd); memcpy((char *)datad, (char *)datas, bytes); return pixd; } /* Reallocate image data if sizes are different */ if (pixResizeImageData(pixd, pixs) == 1) return (PIX *)ERROR_PTR("reallocation of data failed", procName, NULL); /* Copy non-image data fields */ pixCopyColormap(pixd, pixs); pixCopyResolution(pixd, pixs); pixCopyInputFormat(pixd, pixs); pixCopyText(pixd, pixs); /* Copy image data */ datas = pixGetData(pixs); datad = pixGetData(pixd); memcpy((char*)datad, (char*)datas, bytes); return pixd; }
/*! * pixCreateTemplateNoInit() * * Input: pixs * Return: pixd, or null on error * * Notes: * (1) Makes a Pix of the same size as the input Pix, with * the data array allocated but not initialized to 0. * (2) Copies the other fields, including colormap if it exists. */ PIX * pixCreateTemplateNoInit(PIX *pixs) { l_int32 w, h, d; PIX *pixd; PROCNAME("pixCreateTemplateNoInit"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if ((pixd = pixCreateNoInit(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); pixCopyColormap(pixd, pixs); pixCopyText(pixd, pixs); pixCopyInputFormat(pixd, pixs); return pixd; }
/*! * pixFinalAccumulateThreshold() * * Input: pixs (32 bpp) * offset (same as used for initialization) * threshold (values less than this are set in the destination) * Return: pixd (1 bpp), or null on error * * Notes: * (1) The offset must be >= 0 and should not exceed 0x40000000. * (2) The offset is subtracted from the src 32 bpp image */ PIX * pixFinalAccumulateThreshold(PIX *pixs, l_uint32 offset, l_uint32 threshold) { l_int32 i, j, w, h, wpls, wpld, val; l_uint32 *datas, *datad, *lines, *lined; PIX *pixd; PROCNAME("pixFinalAccumulateThreshold"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); if (offset > 0x40000000) offset = 0x40000000; pixGetDimensions(pixs, &w, &h, NULL); if ((pixd = pixCreate(w, h, 1)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ datas = pixGetData(pixs); datad = pixGetData(pixd); wpls = pixGetWpl(pixs); wpld = pixGetWpl(pixd); for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; for (j = 0; j < w; j++) { val = lines[j] - offset; if (val >= threshold) { SET_DATA_BIT(lined, j); } } } return pixd; }
/*! * pixAbsDifference() * * Input: pixs1, pixs2 (both either 8 or 16 bpp gray, or 32 bpp RGB) * Return: pixd, or null on error * * Notes: * (1) The depth of pixs1 and pixs2 must be equal. * (2) Clips computation to the min size, aligning the UL corners * (3) For 8 and 16 bpp, assumes one gray component. * (4) For 32 bpp, assumes 3 color components, and ignores the * LSB of each word (the alpha channel) * (5) Computes the absolute value of the difference between * each component value. */ PIX * pixAbsDifference(PIX *pixs1, PIX *pixs2) { l_int32 w, h, w2, h2, d, wpls, wpld; l_uint32 *datas1, *datas2, *datad; PIX *pixd; PROCNAME("pixAbsDifference"); if (!pixs1) return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); if (!pixs2) return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); d = pixGetDepth(pixs1); if (d != pixGetDepth(pixs2)) return (PIX *)ERROR_PTR("src1 and src2 depths unequal", procName, NULL); if (d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", procName, NULL); pixGetDimensions(pixs1, &w, &h, NULL); pixGetDimensions(pixs2, &w2, &h2, NULL); w = L_MIN(w, w2); h = L_MIN(h, h2); if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs1); datas1 = pixGetData(pixs1); datas2 = pixGetData(pixs2); datad = pixGetData(pixd); wpls = pixGetWpl(pixs1); wpld = pixGetWpl(pixd); absDifferenceLow(datad, w, h, wpld, datas1, datas2, d, wpls); return pixd; }
/*! * pixRotate90() * * Input: pixs (all depths) * direction (1 = clockwise, -1 = counter-clockwise) * Return: pixd, or null on error * * Notes: * (1) This does a 90 degree rotation of the image about the center, * either cw or ccw, returning a new pix. * (2) The direction must be either 1 (cw) or -1 (ccw). */ PIX * pixRotate90(PIX *pixs, l_int32 direction) { l_int32 wd, hd, d, wpls, wpld; l_uint32 *datas, *datad; PIX *pixd; PROCNAME("pixRotate90"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); d = pixGetDepth(pixs); if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("pixs not in {1,2,4,8,16,32} bpp", procName, NULL); if (direction != 1 && direction != -1) return (PIX *)ERROR_PTR("invalid direction", procName, NULL); hd = pixGetWidth(pixs); wd = pixGetHeight(pixs); if ((pixd = pixCreate(wd, hd, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyColormap(pixd, pixs); pixCopyResolution(pixd, pixs); pixCopyInputFormat(pixd, pixs); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); datad = pixGetData(pixd); wpld = pixGetWpl(pixd); rotate90Low(datad, wd, hd, d, wpld, datas, wpls, direction); return pixd; }
/*! * \brief pixOtsuAdaptiveThreshold() * * \param[in] pixs 8 bpp * \param[in] sx, sy desired tile dimensions; actual size may vary * \param[in] smoothx, smoothy half-width of convolution kernel applied to * threshold array: use 0 for no smoothing * \param[in] scorefract fraction of the max Otsu score; typ. 0.1; * use 0.0 for standard Otsu * \param[out] ppixth [optional] array of threshold values * found for each tile * \param[out] ppixd [optional] thresholded input pixs, based on * the threshold array * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) The Otsu method finds a single global threshold for an image. * This function allows a locally adapted threshold to be * found for each tile into which the image is broken up. * (2) The array of threshold values, one for each tile, constitutes * a highly downscaled image. This array is optionally * smoothed using a convolution. The full width and height of the * convolution kernel are (2 * %smoothx + 1) and (2 * %smoothy + 1). * (3) The minimum tile dimension allowed is 16. If such small * tiles are used, it is recommended to use smoothing, because * without smoothing, each small tile determines the splitting * threshold independently. A tile that is entirely in the * image bg will then hallucinate fg, resulting in a very noisy * binarization. The smoothing should be large enough that no * tile is only influenced by one type (fg or bg) of pixels, * because it will force a split of its pixels. * (4) To get a single global threshold for the entire image, use * input values of %sx and %sy that are larger than the image. * For this situation, the smoothing parameters are ignored. * (5) The threshold values partition the image pixels into two classes: * one whose values are less than the threshold and another * whose values are greater than or equal to the threshold. * This is the same use of 'threshold' as in pixThresholdToBinary(). * (6) The scorefract is the fraction of the maximum Otsu score, which * is used to determine the range over which the histogram minimum * is searched. See numaSplitDistribution() for details on the * underlying method of choosing a threshold. * (7) This uses enables a modified version of the Otsu criterion for * splitting the distribution of pixels in each tile into a * fg and bg part. The modification consists of searching for * a minimum in the histogram over a range of pixel values where * the Otsu score is within a defined fraction, %scorefract, * of the max score. To get the original Otsu algorithm, set * %scorefract == 0. * (8) N.B. This method is NOT recommended for images with weak text * and significant background noise, such as bleedthrough, because * of the problem noted in (3) above for tiling. Use Sauvola. * </pre> */ l_int32 pixOtsuAdaptiveThreshold(PIX *pixs, l_int32 sx, l_int32 sy, l_int32 smoothx, l_int32 smoothy, l_float32 scorefract, PIX **ppixth, PIX **ppixd) { l_int32 w, h, nx, ny, i, j, thresh; l_uint32 val; PIX *pixt, *pixb, *pixthresh, *pixth, *pixd; PIXTILING *pt; PROCNAME("pixOtsuAdaptiveThreshold"); if (!ppixth && !ppixd) return ERROR_INT("neither &pixth nor &pixd defined", procName, 1); if (ppixth) *ppixth = NULL; if (ppixd) *ppixd = NULL; if (!pixs || pixGetDepth(pixs) != 8) return ERROR_INT("pixs not defined or not 8 bpp", procName, 1); if (sx < 16 || sy < 16) return ERROR_INT("sx and sy must be >= 16", procName, 1); /* Compute the threshold array for the tiles */ pixGetDimensions(pixs, &w, &h, NULL); nx = L_MAX(1, w / sx); ny = L_MAX(1, h / sy); smoothx = L_MIN(smoothx, (nx - 1) / 2); smoothy = L_MIN(smoothy, (ny - 1) / 2); pt = pixTilingCreate(pixs, nx, ny, 0, 0, 0, 0); pixthresh = pixCreate(nx, ny, 8); for (i = 0; i < ny; i++) { for (j = 0; j < nx; j++) { pixt = pixTilingGetTile(pt, i, j); pixSplitDistributionFgBg(pixt, scorefract, 1, &thresh, NULL, NULL, NULL); pixSetPixel(pixthresh, j, i, thresh); /* see note (4) */ pixDestroy(&pixt); } } /* Optionally smooth the threshold array */ if (smoothx > 0 || smoothy > 0) pixth = pixBlockconv(pixthresh, smoothx, smoothy); else pixth = pixClone(pixthresh); pixDestroy(&pixthresh); /* Optionally apply the threshold array to binarize pixs */ if (ppixd) { pixd = pixCreate(w, h, 1); pixCopyResolution(pixd, pixs); for (i = 0; i < ny; i++) { for (j = 0; j < nx; j++) { pixt = pixTilingGetTile(pt, i, j); pixGetPixel(pixth, j, i, &val); pixb = pixThresholdToBinary(pixt, val); pixTilingPaintTile(pixd, i, j, pixb, pt); pixDestroy(&pixt); pixDestroy(&pixb); } } *ppixd = pixd; } if (ppixth) *ppixth = pixth; else pixDestroy(&pixth); pixTilingDestroy(&pt); return 0; }
/*! * \brief pixSauvolaBinarize() * * \param[in] pixs 8 bpp grayscale; not colormapped * \param[in] whsize window half-width for measuring local statistics * \param[in] factor factor for reducing threshold due to variance; >= 0 * \param[in] addborder 1 to add border of width (%whsize + 1) on all sides * \param[out] ppixm [optional] local mean values * \param[out] ppixsd [optional] local standard deviation values * \param[out] ppixth [optional] threshold values * \param[out] ppixd [optional] thresholded image * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) The window width and height are 2 * %whsize + 1. The minimum * value for %whsize is 2; typically it is \>= 7.. * (2) The local statistics, measured over the window, are the * average and standard deviation. * (3) The measurements of the mean and standard deviation are * performed inside a border of (%whsize + 1) pixels. If pixs does * not have these added border pixels, use %addborder = 1 to add * it here; otherwise use %addborder = 0. * (4) The Sauvola threshold is determined from the formula: * t = m * (1 - k * (1 - s / 128)) * where: * t = local threshold * m = local mean * k = %factor (\>= 0) [ typ. 0.35 ] * s = local standard deviation, which is maximized at * 127.5 when half the samples are 0 and half are 255. * (5) The basic idea of Niblack and Sauvola binarization is that * the local threshold should be less than the median value, * and the larger the variance, the closer to the median * it should be chosen. Typical values for k are between * 0.2 and 0.5. * </pre> */ l_int32 pixSauvolaBinarize(PIX *pixs, l_int32 whsize, l_float32 factor, l_int32 addborder, PIX **ppixm, PIX **ppixsd, PIX **ppixth, PIX **ppixd) { l_int32 w, h; PIX *pixg, *pixsc, *pixm, *pixms, *pixth, *pixd; PROCNAME("pixSauvolaBinarize"); if (ppixm) *ppixm = NULL; if (ppixsd) *ppixsd = NULL; if (ppixth) *ppixth = NULL; if (ppixd) *ppixd = NULL; if (!ppixm && !ppixsd && !ppixth && !ppixd) return ERROR_INT("no outputs", procName, 1); if (!pixs || pixGetDepth(pixs) != 8) return ERROR_INT("pixs undefined or not 8 bpp", procName, 1); if (pixGetColormap(pixs)) return ERROR_INT("pixs is cmapped", procName, 1); pixGetDimensions(pixs, &w, &h, NULL); if (whsize < 2) return ERROR_INT("whsize must be >= 2", procName, 1); if (w < 2 * whsize + 3 || h < 2 * whsize + 3) return ERROR_INT("whsize too large for image", procName, 1); if (factor < 0.0) return ERROR_INT("factor must be >= 0", procName, 1); if (addborder) { pixg = pixAddMirroredBorder(pixs, whsize + 1, whsize + 1, whsize + 1, whsize + 1); pixsc = pixClone(pixs); } else { pixg = pixClone(pixs); pixsc = pixRemoveBorder(pixs, whsize + 1); } if (!pixg || !pixsc) return ERROR_INT("pixg and pixsc not made", procName, 1); /* All these functions strip off the border pixels. */ if (ppixm || ppixth || ppixd) pixm = pixWindowedMean(pixg, whsize, whsize, 1, 1); if (ppixsd || ppixth || ppixd) pixms = pixWindowedMeanSquare(pixg, whsize, whsize, 1); if (ppixth || ppixd) pixth = pixSauvolaGetThreshold(pixm, pixms, factor, ppixsd); if (ppixd) { pixd = pixApplyLocalThreshold(pixsc, pixth, 1); pixCopyResolution(pixd, pixs); } if (ppixm) *ppixm = pixm; else pixDestroy(&pixm); pixDestroy(&pixms); if (ppixth) *ppixth = pixth; else pixDestroy(&pixth); if (ppixd) *ppixd = pixd; else pixDestroy(&pixd); pixDestroy(&pixg); pixDestroy(&pixsc); return 0; }
/*! * \brief pixThresholdByConnComp() * * \param[in] pixs depth > 1, colormap OK * \param[in] pixm [optional] 1 bpp mask giving region to ignore by setting * pixels to white; use NULL if no mask * \param[in] start, end, incr binarization threshold levels to test * \param[in] thresh48 threshold on normalized difference between the * numbers of 4 and 8 connected components * \param[in] threshdiff threshold on normalized difference between the * number of 4 cc at successive iterations * \param[out] pglobthresh [optional] best global threshold; 0 * if no threshold is found * \param[out] ppixd [optional] image thresholded to binary, or * null if no threshold is found * \param[in] debugflag 1 for plotted results * \return 0 if OK, 1 on error or if no threshold is found * * <pre> * Notes: * (1) This finds a global threshold based on connected components. * Although slow, it is reasonable to use it in a situation where * (a) the background in the image is relatively uniform, and * (b) the result will be fed to an OCR program that accepts 1 bpp * images and works best with easily segmented characters. * The reason for (b) is that this selects a threshold with a * minimum number of both broken characters and merged characters. * (2) If the pix has color, it is converted to gray using the * max component. * (3) Input 0 to use default values for any of these inputs: * %start, %end, %incr, %thresh48, %threshdiff. * (4) This approach can be understood as follows. When the * binarization threshold is varied, the numbers of c.c. identify * four regimes: * (a) For low thresholds, text is broken into small pieces, and * the number of c.c. is large, with the 4 c.c. significantly * exceeding the 8 c.c. * (b) As the threshold rises toward the optimum value, the text * characters coalesce and there is very little difference * between the numbers of 4 and 8 c.c, which both go * through a minimum. * (c) Above this, the image background gets noisy because some * pixels are(thresholded to foreground, and the numbers * of c.c. quickly increase, with the 4 c.c. significantly * larger than the 8 c.c. * (d) At even higher thresholds, the image background noise * coalesces as it becomes mostly foreground, and the * number of c.c. drops quickly. * (5) If there is no global threshold that distinguishes foreground * text from background (e.g., weak text over a background that * has significant variation and/or bleedthrough), this returns 1, * which the caller should check. * </pre> */ l_int32 pixThresholdByConnComp(PIX *pixs, PIX *pixm, l_int32 start, l_int32 end, l_int32 incr, l_float32 thresh48, l_float32 threshdiff, l_int32 *pglobthresh, PIX **ppixd, l_int32 debugflag) { l_int32 i, thresh, n, n4, n8, mincounts, found, globthresh; l_float32 count4, count8, firstcount4, prevcount4, diff48, diff4; GPLOT *gplot; NUMA *na4, *na8; PIX *pix1, *pix2, *pix3; PROCNAME("pixThresholdByConnComp"); if (pglobthresh) *pglobthresh = 0; if (ppixd) *ppixd = NULL; if (!pixs || pixGetDepth(pixs) == 1) return ERROR_INT("pixs undefined or 1 bpp", procName, 1); if (pixm && pixGetDepth(pixm) != 1) return ERROR_INT("pixm must be 1 bpp", procName, 1); /* Assign default values if requested */ if (start <= 0) start = 80; if (end <= 0) end = 200; if (incr <= 0) incr = 10; if (thresh48 <= 0.0) thresh48 = 0.01; if (threshdiff <= 0.0) threshdiff = 0.01; if (start > end) return ERROR_INT("invalid start,end", procName, 1); /* Make 8 bpp, using the max component if color. */ if (pixGetColormap(pixs)) pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); else pix1 = pixClone(pixs); if (pixGetDepth(pix1) == 32) pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX); else pix2 = pixConvertTo8(pix1, 0); pixDestroy(&pix1); /* Mask out any non-text regions. Do this in-place, because pix2 * can never be the same pix as pixs. */ if (pixm) pixSetMasked(pix2, pixm, 255); /* Make sure there are enough components to get a valid signal */ pix3 = pixConvertTo1(pix2, start); pixCountConnComp(pix3, 4, &n4); pixDestroy(&pix3); mincounts = 500; if (n4 < mincounts) { L_INFO("Insufficient component count: %d\n", procName, n4); pixDestroy(&pix2); return 1; } /* Compute the c.c. data */ na4 = numaCreate(0); na8 = numaCreate(0); numaSetParameters(na4, start, incr); numaSetParameters(na8, start, incr); for (thresh = start, i = 0; thresh <= end; thresh += incr, i++) { pix3 = pixConvertTo1(pix2, thresh); pixCountConnComp(pix3, 4, &n4); pixCountConnComp(pix3, 8, &n8); numaAddNumber(na4, n4); numaAddNumber(na8, n8); pixDestroy(&pix3); } if (debugflag) { gplot = gplotCreate("/tmp/threshroot", GPLOT_PNG, "number of cc vs. threshold", "threshold", "number of cc"); gplotAddPlot(gplot, NULL, na4, GPLOT_LINES, "plot 4cc"); gplotAddPlot(gplot, NULL, na8, GPLOT_LINES, "plot 8cc"); gplotMakeOutput(gplot); gplotDestroy(&gplot); } n = numaGetCount(na4); found = FALSE; for (i = 0; i < n; i++) { if (i == 0) { numaGetFValue(na4, i, &firstcount4); prevcount4 = firstcount4; } else { numaGetFValue(na4, i, &count4); numaGetFValue(na8, i, &count8); diff48 = (count4 - count8) / firstcount4; diff4 = L_ABS(prevcount4 - count4) / firstcount4; if (debugflag) { fprintf(stderr, "diff48 = %7.3f, diff4 = %7.3f\n", diff48, diff4); } if (diff48 < thresh48 && diff4 < threshdiff) { found = TRUE; break; } prevcount4 = count4; } } numaDestroy(&na4); numaDestroy(&na8); if (found) { globthresh = start + i * incr; if (pglobthresh) *pglobthresh = globthresh; if (ppixd) { *ppixd = pixConvertTo1(pix2, globthresh); pixCopyResolution(*ppixd, pixs); } if (debugflag) fprintf(stderr, "global threshold = %d\n", globthresh); pixDestroy(&pix2); return 0; } if (debugflag) fprintf(stderr, "no global threshold found\n"); pixDestroy(&pix2); return 1; }
Pix* binarizeAdaptive(Pix* pix) { PixAdaptiveBinarizer binarizer(false); Pix* pixb = binarizer.bradleyAdaptiveThresholding(pix, 0.11, 30); pixCopyResolution(pixb, pix); return pixb; }
/*! * pixExpandBinaryPower2() * * Input: pixs (1 bpp) * factor (expansion factor: 1, 2, 4, 8, 16) * Return: pixd (expanded 1 bpp by replication), or null on error */ PIX * pixExpandBinaryPower2(PIX *pixs, l_int32 factor) { l_uint8 sval; l_uint16 *tab2; l_int32 i, j, k, w, h, d, wd, hd, wpls, wpld, sdibits, sqbits, sbytes; l_uint32 *datas, *datad, *lines, *lined, *tab4, *tab8; PIX *pixd; PROCNAME("pixExpandBinaryPower2"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (d != 1) return (PIX *)ERROR_PTR("pixs not binary", procName, NULL); if (factor == 1) return pixCopy(NULL, pixs); if (factor != 2 && factor != 4 && factor != 8 && factor != 16) return (PIX *)ERROR_PTR("factor must be in {2,4,8,16}", procName, NULL); wpls = pixGetWpl(pixs); datas = pixGetData(pixs); wd = factor * w; hd = factor * h; if ((pixd = pixCreate(wd, hd, 1)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); pixScaleResolution(pixd, (l_float32)factor, (l_float32)factor); wpld = pixGetWpl(pixd); datad = pixGetData(pixd); if (factor == 2) { if ((tab2 = makeExpandTab2x()) == NULL) return (PIX *)ERROR_PTR("tab2 not made", procName, NULL); sbytes = (w + 7) / 8; for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + 2 * i * wpld; for (j = 0; j < sbytes; j++) { sval = GET_DATA_BYTE(lines, j); SET_DATA_TWO_BYTES(lined, j, tab2[sval]); } memcpy((char *)(lined + wpld), (char *)lined, 4 * wpld); } FREE(tab2); } else if (factor == 4) { if ((tab4 = makeExpandTab4x()) == NULL) return (PIX *)ERROR_PTR("tab4 not made", procName, NULL); sbytes = (w + 7) / 8; for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + 4 * i * wpld; for (j = 0; j < sbytes; j++) { sval = GET_DATA_BYTE(lines, j); lined[j] = tab4[sval]; } for (k = 1; k < 4; k++) memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld); } FREE(tab4); } else if (factor == 8) { if ((tab8 = makeExpandTab8x()) == NULL) return (PIX *)ERROR_PTR("tab8 not made", procName, NULL); sqbits = (w + 3) / 4; for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + 8 * i * wpld; for (j = 0; j < sqbits; j++) { sval = GET_DATA_QBIT(lines, j); if (sval > 15) L_WARNING("sval = %d; should be < 16\n", procName, sval); lined[j] = tab8[sval]; } for (k = 1; k < 8; k++) memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld); } FREE(tab8); } else { /* factor == 16 */ sdibits = (w + 1) / 2; for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + 16 * i * wpld; for (j = 0; j < sdibits; j++) { sval = GET_DATA_DIBIT(lines, j); lined[j] = expandtab16[sval]; } for (k = 1; k < 16; k++) memcpy((char *)(lined + k * wpld), (char *)lined, 4 * wpld); } } return pixd; }
Pix* savGol32(Pix* pix){ FUNCNAME("savGol32"); Pix* result = pixSavGolFilter(pix, 3, 2, 2); pixCopyResolution(result, pix); return result; }
/*! * pixFinalAccumulate() * * Input: pixs (32 bpp) * offset (same as used for initialization) * depth (8, 16 or 32 bpp, of destination) * Return: pixd (8, 16 or 32 bpp), or null on error * * Notes: * (1) The offset must be >= 0 and should not exceed 0x40000000. * (2) The offset is subtracted from the src 32 bpp image * (3) For 8 bpp dest, the result is clipped to [0, 0xff] * (4) For 16 bpp dest, the result is clipped to [0, 0xffff] */ PIX * pixFinalAccumulate(PIX *pixs, l_uint32 offset, l_int32 depth) { l_int32 i, j, w, h, wpls, wpld, val; l_uint32 *datas, *datad, *lines, *lined; PIX *pixd; PROCNAME("pixFinalAccumulate"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL); if (depth != 8 && depth != 16 && depth != 32) return (PIX *)ERROR_PTR("dest depth not 8, 16, 32 bpp", procName, NULL); if (offset > 0x40000000) offset = 0x40000000; pixGetDimensions(pixs, &w, &h, NULL); if ((pixd = pixCreate(w, h, depth)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs); /* but how did pixs get it initially? */ datas = pixGetData(pixs); datad = pixGetData(pixd); wpls = pixGetWpl(pixs); wpld = pixGetWpl(pixd); if (depth == 8) { for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; for (j = 0; j < w; j++) { val = lines[j] - offset; val = L_MAX(0, val); val = L_MIN(255, val); SET_DATA_BYTE(lined, j, (l_uint8)val); } } } else if (depth == 16) { for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; for (j = 0; j < w; j++) { val = lines[j] - offset; val = L_MAX(0, val); val = L_MIN(0xffff, val); SET_DATA_TWO_BYTES(lined, j, (l_uint16)val); } } } else { /* depth == 32 */ for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; for (j = 0; j < w; j++) lined[j] = lines[j] - offset; } } return pixd; }
/*! * pixAbsDifference() * * Input: pixs1, pixs2 (both either 8 or 16 bpp gray, or 32 bpp RGB) * Return: pixd, or null on error * * Notes: * (1) The depth of pixs1 and pixs2 must be equal. * (2) Clips computation to the min size, aligning the UL corners * (3) For 8 and 16 bpp, assumes one gray component. * (4) For 32 bpp, assumes 3 color components, and ignores the * LSB of each word (the alpha channel) * (5) Computes the absolute value of the difference between * each component value. */ PIX * pixAbsDifference(PIX *pixs1, PIX *pixs2) { l_int32 i, j, w, h, w2, h2, d, wpls1, wpls2, wpld, val1, val2, diff; l_int32 rval1, gval1, bval1, rval2, gval2, bval2, rdiff, gdiff, bdiff; l_uint32 *datas1, *datas2, *datad, *lines1, *lines2, *lined; PIX *pixd; PROCNAME("pixAbsDifference"); if (!pixs1) return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); if (!pixs2) return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); d = pixGetDepth(pixs1); if (d != pixGetDepth(pixs2)) return (PIX *)ERROR_PTR("src1 and src2 depths unequal", procName, NULL); if (d != 8 && d != 16 && d != 32) return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", procName, NULL); pixGetDimensions(pixs1, &w, &h, NULL); pixGetDimensions(pixs2, &w2, &h2, NULL); w = L_MIN(w, w2); h = L_MIN(h, h2); if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixCopyResolution(pixd, pixs1); datas1 = pixGetData(pixs1); datas2 = pixGetData(pixs2); datad = pixGetData(pixd); wpls1 = pixGetWpl(pixs1); wpls2 = pixGetWpl(pixs2); wpld = pixGetWpl(pixd); if (d == 8) { for (i = 0; i < h; i++) { lines1 = datas1 + i * wpls1; lines2 = datas2 + i * wpls2; lined = datad + i * wpld; for (j = 0; j < w; j++) { val1 = GET_DATA_BYTE(lines1, j); val2 = GET_DATA_BYTE(lines2, j); diff = L_ABS(val1 - val2); SET_DATA_BYTE(lined, j, diff); } } } else if (d == 16) { for (i = 0; i < h; i++) { lines1 = datas1 + i * wpls1; lines2 = datas2 + i * wpls2; lined = datad + i * wpld; for (j = 0; j < w; j++) { val1 = GET_DATA_TWO_BYTES(lines1, j); val2 = GET_DATA_TWO_BYTES(lines2, j); diff = L_ABS(val1 - val2); SET_DATA_TWO_BYTES(lined, j, diff); } } } else { /* d == 32 */ for (i = 0; i < h; i++) { lines1 = datas1 + i * wpls1; lines2 = datas2 + i * wpls2; lined = datad + i * wpld; for (j = 0; j < w; j++) { extractRGBValues(lines1[j], &rval1, &gval1, &bval1); extractRGBValues(lines2[j], &rval2, &gval2, &bval2); rdiff = L_ABS(rval1 - rval2); gdiff = L_ABS(gval1 - gval2); bdiff = L_ABS(bval1 - bval2); composeRGBPixel(rdiff, gdiff, bdiff, lined + j); } } } return pixd; }