jboolean Java_com_googlecode_leptonica_android_Pixa_nativeWriteToFileRandomCmap(JNIEnv *env, jclass clazz, jint nativePixa, jstring fileName, jint width, jint height) { LOGV(__FUNCTION__); PIX *pixtemp; PIXA *pixa = (PIXA *) nativePixa; const char *c_fileName = env->GetStringUTFChars(fileName, NULL); if (c_fileName == NULL) { LOGE("could not extract fileName string!"); return JNI_FALSE; } if (pixaGetCount(pixa) > 0) { pixtemp = pixaDisplayRandomCmap(pixa, (l_int32) width, (l_int32) height); } else { pixtemp = pixCreate((l_int32) width, (l_int32) height, 1); } pixWrite(c_fileName, pixtemp, IFF_BMP); pixDestroy(&pixtemp); env->ReleaseStringUTFChars(fileName, c_fileName); return JNI_TRUE; }
PIX * MakeColorWash(l_int32 w, l_int32 h, l_int32 color) { l_int32 i, j, wpl; l_uint32 val; l_uint32 *data, *line; PIX *pixd; pixd = pixCreate(w, h, 32); data = pixGetData(pixd); wpl = pixGetWpl(pixd); for (i = 0; i < h; i++) { line = data + i * wpl; for (j = 0; j < w; j++) { if (color == COLOR_RED) val = ((j * 255) / w) << L_GREEN_SHIFT | ((j * 255) / w) << L_BLUE_SHIFT | 255 << L_RED_SHIFT; else if (color == COLOR_GREEN) val = ((j * 255) / w) << L_RED_SHIFT | ((j * 255) / w) << L_BLUE_SHIFT | 255 << L_GREEN_SHIFT; else val = ((j * 255) / w) << L_RED_SHIFT | ((j * 255) / w) << L_GREEN_SHIFT | 255 << L_BLUE_SHIFT; line[j] = val; } } 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; }
main(int argc, char **argv) { l_int32 i, j, w, h, same, width, height, cx, cy; l_uint32 val; PIX *pixs, *pixse, *pixd1, *pixd2; SEL *sel; static char mainName[] = "rasterop_reg"; if (argc != 1) return ERROR_INT(" Syntax: rasterop_reg", mainName, 1); pixs = pixRead("feyn.tif"); for (width = 1; width <= 25; width += 3) { for (height = 1; height <= 25; height += 4) { cx = width / 2; cy = height / 2; /* Dilate using an actual sel */ sel = selCreateBrick(height, width, cy, cx, SEL_HIT); pixd1 = pixDilate(NULL, pixs, sel); /* Dilate using a pix as a sel */ pixse = pixCreate(width, height, 1); pixSetAll(pixse); pixd2 = pixCopy(NULL, pixs); w = pixGetWidth(pixs); h = pixGetHeight(pixs); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { pixGetPixel(pixs, j, i, &val); if (val) pixRasterop(pixd2, j - cx, i - cy, width, height, PIX_SRC | PIX_DST, pixse, 0, 0); } } pixEqual(pixd1, pixd2, &same); if (same == 1) fprintf(stderr, "Correct for (%d,%d)\n", width, height); else { fprintf(stderr, "Error: results are different!\n"); fprintf(stderr, "SE: width = %d, height = %d\n", width, height); pixWrite("/tmp/junkout1", pixd1, IFF_PNG); pixWrite("/tmp/junkout2", pixd2, IFF_PNG); return 1; } pixDestroy(&pixse); pixDestroy(&pixd1); pixDestroy(&pixd2); selDestroy(&sel); } } pixDestroy(&pixs); return 0; }
/*! * 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; }
jint Java_com_googlecode_leptonica_android_ReadFile_nativeReadBitmap(JNIEnv *env, jclass clazz, jobject bitmap) { //LOGV(__FUNCTION__); l_int32 w, h, d; AndroidBitmapInfo info; void* pixels; int ret; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return JNI_FALSE; } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { LOGE("Bitmap format is not RGBA_8888 !"); return JNI_FALSE; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); return JNI_FALSE; } PIX *pixd = pixCreate(info.width, info.height, 8); l_uint32 *src = (l_uint32 *) pixels; l_int32 srcWpl = (info.stride / 4); l_uint32 *dst = pixGetData(pixd); l_int32 dstWpl = pixGetWpl(pixd); l_uint8 a, r, g, b, pixel8; for (int y = 0; y < info.height; y++) { l_uint32 *dst_line = dst + (y * dstWpl); l_uint32 *src_line = src + (y * srcWpl); for (int x = 0; x < info.width; x++) { // Get pixel from RGBA_8888 r = *src_line >> SK_R32_SHIFT; g = *src_line >> SK_G32_SHIFT; b = *src_line >> SK_B32_SHIFT; a = *src_line >> SK_A32_SHIFT; pixel8 = (l_uint8) ((r + g + b) / 3); // Set pixel to LUMA_8 SET_DATA_BYTE(dst_line, x, pixel8); // Move to the next pixel src_line++; } } AndroidBitmap_unlockPixels(env, bitmap); return (jint) pixd; }
/*! * pixRasteropHip() * * Input: pixd (in-place operation) * by (top of horizontal band) * bh (height of horizontal band) * hshift (horizontal shift of band; hshift > 0 is to right) * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) * Return: 0 if OK; 1 on error * * Notes: * (1) This rasterop translates a horizontal band of the * image either left or right, bringing in either white * or black pixels from outside the image. * (2) The horizontal band extends the full width of pixd. * (3) If a colormap exists, the nearest color to white or black * is brought in. */ l_int32 pixRasteropHip(PIX *pixd, l_int32 by, l_int32 bh, l_int32 hshift, l_int32 incolor) { l_int32 w, h, d, index, op; PIX *pixt; PIXCMAP *cmap; PROCNAME("pixRasteropHip"); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return ERROR_INT("invalid value for incolor", procName, 1); if (bh <= 0) return ERROR_INT("bh must be > 0", procName, 1); if (hshift == 0) return 0; pixGetDimensions(pixd, &w, &h, &d); rasteropHipLow(pixGetData(pixd), h, d, pixGetWpl(pixd), by, bh, hshift); cmap = pixGetColormap(pixd); if (!cmap) { if ((d == 1 && incolor == L_BRING_IN_BLACK) || (d > 1 && incolor == L_BRING_IN_WHITE)) op = PIX_SET; else op = PIX_CLR; /* Set the pixels brought in at left or right */ if (hshift > 0) pixRasterop(pixd, 0, by, hshift, bh, op, NULL, 0, 0); else /* hshift < 0 */ pixRasterop(pixd, w + hshift, by, -hshift, bh, op, NULL, 0, 0); return 0; } /* Get the nearest index and fill with that */ if (incolor == L_BRING_IN_BLACK) pixcmapGetRankIntensity(cmap, 0.0, &index); else /* white */ pixcmapGetRankIntensity(cmap, 1.0, &index); pixt = pixCreate(L_ABS(hshift), bh, d); pixSetAllArbitrary(pixt, index); if (hshift > 0) pixRasterop(pixd, 0, by, hshift, bh, PIX_SRC, pixt, 0, 0); else /* hshift < 0 */ pixRasterop(pixd, w + hshift, by, -hshift, bh, PIX_SRC, pixt, 0, 0); pixDestroy(&pixt); return 0; }
jint Java_com_googlecode_leptonica_android_ReadFile_nativeReadBitmap(JNIEnv *env, jclass clazz, jobject bitmap) { LOGV(__FUNCTION__); l_int32 w, h, d; AndroidBitmapInfo info; void* pixels; int ret; if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return JNI_FALSE; } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { LOGE("Bitmap format is not RGBA_8888 !"); return JNI_FALSE; } if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); return JNI_FALSE; } PIX *pix = pixCreate(info.width, info.height, 8); l_uint8 *src = (l_uint8 *) pixels; l_uint8 *dst = (l_uint8 *) pixGetData(pixs); l_int32 srcBpl = info.stride; l_int32 dstBpl = 4 * pixGetWpl(pixs); l_uint8 a, r, g, b; SET_PIXEL = SET_PIXEL_8; for (int y = 0; y < info.height; y++) { l_uint8 *dst_line = dst + (y * dstBpl); l_uint8 *src_line = src + (y * srcBpl); for (int x = 0; x < info.width; x++) { // Get pixel from ARGB_8888 r = *src_line++; g = *src_line++; b = *src_line++; a = *src_line++; // Set pixel to LUMA_8 *dst_line = (l_uint8) ((r + g + b) / 3); } } AndroidBitmap_unlockPixels(env, bitmap); return (jint) pix; }
/*! * \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; }
/*! * pixaDisplayRandomCmap() * * Input: pixa (of 1 bpp components, with boxa) * w, h (if set to 0, determines the size from the * b.b. of the components in pixa) * Return: pix (8 bpp, cmapped, with random colors on the components), * or null on error * * Notes: * (1) This uses the boxes to place each pix in the rendered composite. * (2) By default, the background color is: black, cmap index 0. * This can be changed by pixcmapResetColor() */ PIX * pixaDisplayRandomCmap(PIXA *pixa, l_int32 w, l_int32 h) { l_int32 i, n, d, index, xb, yb, wb, hb; BOXA *boxa; PIX *pixs, *pixt, *pixd; PIXCMAP *cmap; PROCNAME("pixaDisplayRandomCmap"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); n = pixaGetCount(pixa); if (n == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* Use the first pix in pixa to verify depth is 1 bpp */ pixs = pixaGetPix(pixa, 0, L_CLONE); d = pixGetDepth(pixs); pixDestroy(&pixs); if (d != 1) return (PIX *)ERROR_PTR("components not 1 bpp", procName, NULL); /* If w and h not input, determine the minimum size required * to contain the origin and all c.c. */ if (w == 0 || h == 0) { boxa = pixaGetBoxa(pixa, L_CLONE); boxaGetExtent(boxa, &w, &h, NULL); boxaDestroy(&boxa); } /* Set up an 8 bpp dest pix, with a colormap with 254 random colors */ if ((pixd = pixCreate(w, h, 8)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); cmap = pixcmapCreateRandom(8, 1, 1); pixSetColormap(pixd, cmap); /* Color each component and blit it in */ for (i = 0; i < n; i++) { index = 1 + (i % 254); pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); pixs = pixaGetPix(pixa, i, L_CLONE); pixt = pixConvert1To8(NULL, pixs, 0, index); pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pixt, 0, 0); pixDestroy(&pixs); pixDestroy(&pixt); } return pixd; }
/*! * 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; }
char* ocr_bitmap(void* arg, png_color *palette,png_byte *alpha, unsigned char* indata,int w, int h) { PIX *pix = NULL; PIX *cpix = NULL; char*text_out= NULL; int i,j,index; unsigned int wpl; unsigned int *data,*ppixel; BOOL tess_ret = FALSE; struct ocrCtx* ctx = arg; pix = pixCreate(w, h, 32); if(pix == NULL) { return NULL; } wpl = pixGetWpl(pix); data = pixGetData(pix); #if LEPTONICA_VERSION > 69 pixSetSpp(pix, 4); #endif for (i = 0; i < h; i++) { ppixel = data + i * wpl; for (j = 0; j < w; j++) { index = indata[i * w + (j)]; composeRGBPixel(palette[index].red, palette[index].green,palette[index].blue, ppixel); SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,alpha[index]); ppixel++; } } ignore_alpha_at_edge(alpha, indata, w, h, pix, &cpix); #ifdef OCR_DEBUG { char str[128] = ""; static int i = 0; sprintf(str,"temp/file_c_%d.png",i); pixWrite(str, cpix, IFF_PNG); i++; } #endif TessBaseAPISetImage2(ctx->api, cpix); tess_ret = TessBaseAPIRecognize(ctx->api, NULL); if( tess_ret != 0) printf("\nsomething messy\n"); text_out = TessBaseAPIGetUTF8Text(ctx->api); pixDestroy(&pix); pixDestroy(&cpix); return text_out; }
// Make a Pix of the correct scaled size for the TraceOutline functions. Pix* GridReducedPix(const TBOX& box, int gridsize, ICOORD bleft, int* left, int* bottom) { // Compute grid bounds of the outline and pad all round by 1. int grid_left = (box.left() - bleft.x()) / gridsize - 1; int grid_bottom = (box.bottom() - bleft.y()) / gridsize - 1; int grid_right = (box.right() - bleft.x()) / gridsize + 1; int grid_top = (box.top() - bleft.y()) / gridsize + 1; *left = grid_left; *bottom = grid_bottom; return pixCreate(grid_right - grid_left + 1, grid_top - grid_bottom + 1, 1); }
/*! * wshedCreate() * * Input: pixs (8 bpp source) * pixm (1 bpp 'marker' seed) * mindepth (minimum depth; anything less is not saved) * debugflag (1 for debug output) * Return: WShed, or null on error * * Notes: * (1) It is not necessary for the fg pixels in the seed image * be at minima, or that they be isolated. We extract a * single pixel from each connected component, and a seed * anywhere in a watershed will eventually label the watershed * when the filling level reaches it. * (2) Set mindepth to some value to ignore noise in pixs that * can create small local minima. Any watershed shallower * than mindepth, even if it has a seed, will not be saved; * It will either be incorporated in another watershed or * eliminated. */ L_WSHED * wshedCreate(PIX *pixs, PIX *pixm, l_int32 mindepth, l_int32 debugflag) { l_int32 w, h; L_WSHED *wshed; PROCNAME("wshedCreate"); if (!pixs) return (L_WSHED *)ERROR_PTR("pixs is not defined", procName, NULL); if (pixGetDepth(pixs) != 8) return (L_WSHED *)ERROR_PTR("pixs is not 8 bpp", procName, NULL); if (!pixm) return (L_WSHED *)ERROR_PTR("pixm is not defined", procName, NULL); if (pixGetDepth(pixm) != 1) return (L_WSHED *)ERROR_PTR("pixm is not 1 bpp", procName, NULL); pixGetDimensions(pixs, &w, &h, NULL); if (pixGetWidth(pixm) != w || pixGetHeight(pixm) != h) return (L_WSHED *)ERROR_PTR("pixs/m sizes are unequal", procName, NULL); if ((wshed = (L_WSHED *)CALLOC(1, sizeof(L_WSHED))) == NULL) return (L_WSHED *)ERROR_PTR("wshed not made", procName, NULL); wshed->pixs = pixClone(pixs); wshed->pixm = pixClone(pixm); wshed->mindepth = L_MAX(1, mindepth); wshed->pixlab = pixCreate(w, h, 32); pixSetAllArbitrary(wshed->pixlab, MAX_LABEL_VALUE); wshed->pixt = pixCreate(w, h, 1); wshed->lines8 = pixGetLinePtrs(pixs, NULL); wshed->linem1 = pixGetLinePtrs(pixm, NULL); wshed->linelab32 = pixGetLinePtrs(wshed->pixlab, NULL); wshed->linet1 = pixGetLinePtrs(wshed->pixt, NULL); wshed->debug = debugflag; return wshed; }
/*! * pixConnCompTransform() * * Input: pixs (1 bpp) * connect (connectivity: 4 or 8) * depth (of pixd: 8 or 16 bpp; use 0 for auto determination) * Return: pixd (8 or 16 bpp), or null on error * * Notes: * (1) pixd is 8 or 16 bpp, and the pixel values label the fg component, * starting with 1. Pixels in the bg are labelled 0. * (2) If @depth = 0, the depth of pixd is 8 if the number of c.c. * is less than 254, and 16 otherwise. * (3) If @depth = 8, the assigned label for the n-th component is * 1 + n % 254. We use mod 254 because 0 is uniquely assigned * to black: e.g., see pixcmapCreateRandom(). Likewise, * if @depth = 16, the assigned label uses mod(2^16 - 2). */ PIX * pixConnCompTransform(PIX *pixs, l_int32 connect, l_int32 depth) { l_int32 i, n, index, w, h, xb, yb, wb, hb; BOXA *boxa; PIX *pix1, *pix2, *pixd; PIXA *pixa; PROCNAME("pixConnCompTransform"); if (!pixs || pixGetDepth(pixs) != 1) return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); if (connect != 4 && connect != 8) return (PIX *)ERROR_PTR("connectivity must be 4 or 8", procName, NULL); if (depth != 0 && depth != 8 && depth != 16) return (PIX *)ERROR_PTR("depth must be 0, 8 or 16", procName, NULL); boxa = pixConnComp(pixs, &pixa, connect); n = pixaGetCount(pixa); boxaDestroy(&boxa); pixGetDimensions(pixs, &w, &h, NULL); if (depth == 0) { depth = (n < 254) ? 8 : 16; } pixd = pixCreate(w, h, depth); if (n == 0) { /* no fg */ pixaDestroy(&pixa); return pixd; } /* Label each component and blit it in */ for (i = 0; i < n; i++) { pixaGetBoxGeometry(pixa, i, &xb, &yb, &wb, &hb); pix1 = pixaGetPix(pixa, i, L_CLONE); if (depth == 8) { index = 1 + (i % 254); pix2 = pixConvert1To8(NULL, pix1, 0, index); } else { /* depth == 16 */ index = 1 + (i % 0xfffe); pix2 = pixConvert1To16(NULL, pix1, 0, index); } pixRasterop(pixd, xb, yb, wb, hb, PIX_PAINT, pix2, 0, 0); pixDestroy(&pix1); pixDestroy(&pix2); } pixaDestroy(&pixa); return pixd; }
static PIX * DisplayBoxa(BOXA *boxa) { l_int32 w, h; BOX *box; PIX *pix1, *pix2, *pix3; PIXA *pixa; pixa = pixaCreate(2); boxaGetExtent(boxa, &w, &h, &box); pix1 = pixCreate(w, h, 1); pixMaskBoxa(pix1, pix1, boxa, L_SET_PIXELS); pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixCreate(w, h, 32); pixSetAll(pix2); pixRenderBoxaArb(pix2, boxa, 2, 0, 255, 0); pixRenderBoxArb(pix2, box, 3, 255, 0, 0); pixaAddPix(pixa, pix2, L_INSERT); pix3 = pixaDisplayTiledInRows(pixa, 32, 1000, 1.0, 0, 30, 2); boxDestroy(&box); pixaDestroy(&pixa); return pix3; }
/*! * boxaGetCoverage() * * Input: boxa * wc, hc (dimensions of overall clipping rectangle with UL * corner at (0, 0) that is covered by the boxes. * exactflag (1 for guaranteeing an exact result; 0 for getting * an exact result only if the boxes do not overlap) * &fract (<return> sum of box area as fraction of w * h) * Return: 0 if OK, 1 on error * * Notes: * (1) The boxes in boxa are clipped to the input rectangle. * (2) * When @exactflag == 1, we generate a 1 bpp pix of size * wc x hc, paint all the boxes black, and count the fg pixels. * This can take 1 msec on a large page with many boxes. * * When @exactflag == 0, we clip each box to the wc x hc region * and sum the resulting areas. This is faster. * * The results are the same when none of the boxes overlap * within the wc x hc region. */ l_int32 boxaGetCoverage(BOXA *boxa, l_int32 wc, l_int32 hc, l_int32 exactflag, l_float32 *pfract) { l_int32 i, n, x, y, w, h, sum; BOX *box, *boxc; PIX *pixt; PROCNAME("boxaGetCoverage"); if (!pfract) return ERROR_INT("&fract not defined", procName, 1); *pfract = 0.0; if (!boxa) return ERROR_INT("boxa not defined", procName, 1); n = boxaGetCount(boxa); if (n == 0) return ERROR_INT("no boxes in boxa", procName, 1); if (exactflag == 0) { /* quick and dirty */ sum = 0; for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_CLONE); if ((boxc = boxClipToRectangle(box, wc, hc)) != NULL) { boxGetGeometry(boxc, NULL, NULL, &w, &h); sum += w * h; boxDestroy(&boxc); } boxDestroy(&box); } } else { /* slower and exact */ pixt = pixCreate(wc, hc, 1); for (i = 0; i < n; i++) { box = boxaGetBox(boxa, i, L_CLONE); boxGetGeometry(box, &x, &y, &w, &h); pixRasterop(pixt, x, y, w, h, PIX_SET, NULL, 0, 0); boxDestroy(&box); } pixCountPixels(pixt, &sum, NULL); pixDestroy(&pixt); } *pfract = (l_float32)sum / (l_float32)(wc * hc); return 0; }
/*! * 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; }
/*! * fpixDisplayMaxDynamicRange() * * Input: fpixs * Return: pixd (8 bpp), or null on error */ PIX * fpixDisplayMaxDynamicRange(FPIX *fpixs) { l_uint8 dval; l_int32 i, j, w, h, wpls, wpld; l_float32 factor, sval, maxval; l_float32 *lines, *datas; l_uint32 *lined, *datad; PIX *pixd; PROCNAME("fpixDisplayMaxDynamicRange"); if (!fpixs) return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL); fpixGetDimensions(fpixs, &w, &h); datas = fpixGetData(fpixs); wpls = fpixGetWpl(fpixs); maxval = 0.0; for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { sval = *(lines + j); if (sval > maxval) maxval = sval; } } pixd = pixCreate(w, h, 8); if (maxval == 0.0) return pixd; /* all pixels are 0 */ datad = pixGetData(pixd); wpld = pixGetWpl(pixd); factor = 255. / maxval; for (i = 0; i < h; i++) { lines = datas + i * wpls; lined = datad + i * wpld; for (j = 0; j < w; j++) { sval = *(lines + j); if (sval < 0.0) sval = 0.0; dval = (l_uint8)(factor * sval + 0.5); SET_DATA_BYTE(lined, j, dval); } } return pixd; }
/*! * pixaDisplayUnsplit() * * Input: pixa * nx (number of mosaic cells horizontally) * ny (number of mosaic cells vertically) * borderwidth (of added border on all sides) * bordercolor (in our RGBA format: 0xrrggbbaa) * Return: pix of tiled images, or null on error * * Notes: * (1) This is a logical inverse of pixaSplitPix(). It * constructs a pix from a mosaic of tiles, all of equal size. * (2) For added generality, a border of arbitrary color can * be added to each of the tiles. * (3) In use, pixa will typically have either been generated * from pixaSplitPix() or will derived from a pixa that * was so generated. * (4) All pix in the pixa must be of equal depth, and, if * colormapped, have the same colormap. */ PIX * pixaDisplayUnsplit(PIXA *pixa, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor) { l_int32 w, h, d, wt, ht; l_int32 i, j, k, x, y, n; PIX *pixt, *pixd; PROCNAME("pixaDisplayUnsplit"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); if (nx <= 0 || ny <= 0) return (PIX *)ERROR_PTR("nx and ny must be > 0", procName, NULL); if ((n = pixaGetCount(pixa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); if (n != nx * ny) return (PIX *)ERROR_PTR("n != nx * ny", procName, NULL); borderwidth = L_MAX(0, borderwidth); pixaGetPixDimensions(pixa, 0, &wt, &ht, &d); w = nx * (wt + 2 * borderwidth); h = ny * (ht + 2 * borderwidth); if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); pixt = pixaGetPix(pixa, 0, L_CLONE); pixCopyColormap(pixd, pixt); pixDestroy(&pixt); if (borderwidth > 0) pixSetAllArbitrary(pixd, bordercolor); y = borderwidth; for (i = 0, k = 0; i < ny; i++) { x = borderwidth; for (j = 0; j < nx; j++, k++) { pixt = pixaGetPix(pixa, k, L_CLONE); pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pixt, 0, 0); pixDestroy(&pixt); x += wt + 2 * borderwidth; } y += ht + 2 * borderwidth; } return pixd; }
/*! * pixDeserializeFromMemory() * * Input: data (serialized data in memory) * nbytes (number of bytes in data string) * Return: pix, or NULL on error * * Notes: * (1) See pixSerializeToMemory() for the binary format. */ PIX * pixDeserializeFromMemory(const l_uint32 *data, size_t nbytes) { char *id; l_int32 w, h, d, imdatasize, ncolors; l_uint32 *imdata; /* data in pix raster */ PIX *pixd; PIXCMAP *cmap; PROCNAME("pixDeserializeFromMemory"); if (!data) return (PIX *)ERROR_PTR("data not defined", procName, NULL); if (nbytes < 28) return (PIX *)ERROR_PTR("invalid data", procName, NULL); id = (char *)data; if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x') return (PIX *)ERROR_PTR("invalid id string", procName, NULL); w = data[1]; h = data[2]; d = data[3]; if ((pixd = pixCreate(w, h, d)) == NULL) return (PIX *)ERROR_PTR("pix not made", procName, NULL); ncolors = data[5]; if (ncolors > 0) { cmap = pixcmapDeserializeFromMemory((l_uint8 *)(&data[6]), 4, ncolors); if (!cmap) return (PIX *)ERROR_PTR("cmap not made", procName, NULL); pixSetColormap(pixd, cmap); } imdata = pixGetData(pixd); imdatasize = nbytes - 24 - 4 * ncolors - 4; if (imdatasize != data[6 + ncolors]) L_ERROR("imdatasize is inconsistent with nbytes\n", procName); memcpy((char *)imdata, (char *)(data + 7 + ncolors), imdatasize); #if DEBUG_SERIALIZE fprintf(stderr, "Deserialize: " "raster size = %d, ncolors in cmap = %d, total bytes = %lu\n", imdatasize, ncolors, nbytes); #endif /* DEBUG_SERIALIZE */ return pixd; }
int main(int argc, char** argv) { if(argc != 5 && argc != 4) syntax(); int startarg = 1; int x=INT_MAX,y=INT_MAX,xmax=INT_MIN,ymax=INT_MIN,w=-1,h=-1; if(strncmp(argv[1], "-dim=", 5) == 0) { sscanf(argv[1]+5, "%d,%d,%d,%d", &x, &y, &w, &h); startarg++; } const char* pic1 = argv[startarg++]; const char* pic2 = argv[startarg++]; const char* out = argv[startarg++]; struct Pix* p1 = pixRead(pic1); struct Pix* p2 = pixRead(pic2); if(p1->h != p2->h || p1->w != p2->w) { dprintf(2, "error: both pics need same dimensions!"); exit(1); } struct Pix* p1b = pixConvertTo32(p1); struct Pix* p2b = pixConvertTo32(p2); int X, Y, W = p1->w, H = p1->h; if(x == INT_MAX) { for(Y = 0; Y < H; Y++) { for(X = 0; X < W; X++) { if(((uint32_t*)p1b->data)[Y * W + X] != ((uint32_t*)p2b->data)[Y * W + X]) { if(X < x) x = X; if(X > xmax) xmax = X; if(Y < y) y = Y; if(Y > ymax) ymax = Y; } } } ++xmax; ++ymax; w = xmax - x; h = ymax - y; } struct Pix* po = pixCreate(w,h,32); int ox, oy; for(oy = 0, Y = y; Y < y + h; oy++, Y++) for(ox = 0, X = x; X < x + w; ox++, X++) { if(((uint32_t*)p1b->data)[Y * W + X] != ((uint32_t*)p2b->data)[Y * W + X]) ((uint32_t*)po->data)[oy * w + ox] = ((uint32_t*)p2b->data)[Y * W + X]; else ((uint32_t*)po->data)[oy * w + ox] = 0; } pixWritePng(out, po, 0); dprintf(1, "wrote pixels x,y,w,h: %d,%d,%d,%d to %s\n", x,y,w,h,out); return 0; }
// Returns a full-resolution binary pix in which each cell over the given // threshold is filled as a black square. pixDestroy after use. // Edge cells, which have a zero 4-neighbour, are not marked. Pix* IntGrid::ThresholdToPix(int threshold) const { Pix* pix = pixCreate(tright().x() - bleft().x(), tright().y() - bleft().y(), 1); int cellsize = gridsize(); for (int y = 0; y < gridheight(); ++y) { for (int x = 0; x < gridwidth(); ++x) { if (GridCellValue(x, y) > threshold && GridCellValue(x - 1, y) > 0 && GridCellValue(x + 1, y) > 0 && GridCellValue(x, y - 1) > 0 && GridCellValue(x, y + 1) > 0) { pixRasterop(pix, x * cellsize, tright().y() - ((y + 1) * cellsize), cellsize, cellsize, PIX_SET, NULL, 0, 0); } } } return pix; }
/*! * pixInitAccumulate() * * Input: w, h (of accumulate array) * offset (initialize the 32 bpp to have this * value; not more than 0x40000000) * Return: pixd (32 bpp), or null on error * * Notes: * (1) The offset must be >= 0. * (2) The offset is used so that we can do arithmetic * with negative number results on l_uint32 data; it * prevents the l_uint32 data from going negative. * (3) Because we use l_int32 intermediate data results, * these should never exceed the max of l_int32 (0x7fffffff). * We do not permit the offset to be above 0x40000000, * which is half way between 0 and the max of l_int32. * (4) The same offset should be used for initialization, * multiplication by a constant, and final extraction! * (5) If you're only adding positive values, offset can be 0. */ PIX * pixInitAccumulate(l_int32 w, l_int32 h, l_uint32 offset) { PIX *pixd; PROCNAME("pixInitAccumulate"); if ((pixd = pixCreate(w, h, 32)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); if (offset > 0x40000000) offset = 0x40000000; pixSetAllArbitrary(pixd, offset); return pixd; }
// Helper writes a grey image to a file for use by scrollviewer. // Normally for speed we don't display the image in the layout debug windows. // If textord_debug_images is true, we draw the image as a background to some // of the debug windows. printable determines whether these // images are optimized for printing instead of screen display. static void WriteDebugBackgroundImage(bool printable, Pix* pix_binary) { Pix* grey_pix = pixCreate(pixGetWidth(pix_binary), pixGetHeight(pix_binary), 8); // Printable images are light grey on white, but for screen display // they are black on dark grey so the other colors show up well. if (printable) { pixSetAll(grey_pix); pixSetMasked(grey_pix, pix_binary, 192); } else { pixSetAllArbitrary(grey_pix, 64); pixSetMasked(grey_pix, pix_binary, 0); } AlignedBlob::IncrementDebugPix(); pixWrite(AlignedBlob::textord_debug_pix().string(), grey_pix, IFF_PNG); pixDestroy(&grey_pix); }
/*! * pixaSplitPix() * * Input: pixs (with individual components on a lattice) * nx (number of mosaic cells horizontally) * ny (number of mosaic cells vertically) * borderwidth (of added border on all sides) * bordercolor (in our RGBA format: 0xrrggbbaa) * Return: pixa, or null on error * * Notes: * (1) This is a variant on pixaCreateFromPix(), where we * simply divide the image up into (approximately) equal * subunits. If you want the subimages to have essentially * the same aspect ratio as the input pix, use nx = ny. * (2) If borderwidth is 0, we ignore the input bordercolor and * redefine it to white. * (3) The bordercolor is always used to initialize each tiled pix, * so that if the src is clipped, the unblitted part will * be this color. This avoids 1 pixel wide black stripes at the * left and lower edges. */ PIXA * pixaSplitPix(PIX *pixs, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor) { l_int32 w, h, d, cellw, cellh, i, j; PIX *pixt; PIXA *pixa; PROCNAME("pixaSplitPix"); if (!pixs) return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); if (nx <= 0 || ny <= 0) return (PIXA *)ERROR_PTR("nx and ny must be > 0", procName, NULL); borderwidth = L_MAX(0, borderwidth); if ((pixa = pixaCreate(nx * ny)) == NULL) return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); cellw = (w + nx - 1) / nx; /* round up */ cellh = (h + ny - 1) / ny; for (i = 0; i < ny; i++) { for (j = 0; j < nx; j++) { if ((pixt = pixCreate(cellw + 2 * borderwidth, cellh + 2 * borderwidth, d)) == NULL) return (PIXA *)ERROR_PTR("pixt not made", procName, NULL); pixCopyColormap(pixt, pixs); if (borderwidth == 0) { /* initialize full image to white */ if (d == 1) pixClearAll(pixt); else pixSetAll(pixt); } else pixSetAllArbitrary(pixt, bordercolor); pixRasterop(pixt, borderwidth, borderwidth, cellw, cellh, PIX_SRC, pixs, j * cellw, i * cellh); pixaAddPix(pixa, pixt, L_INSERT); } } return pixa; }
/*! * \brief pixConnCompIncrInit() * * \param[in] pixs 1 bpp * \param[in] conn connectivity: 4 or 8 * \param[out] ppixd 32 bpp, with c.c. labelled * \param[out] pptaa with pixel locations indexed by c.c. * \param[out] pncc initial number of c.c. * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This labels the connected components in a 1 bpp pix, and * additionally sets up a ptaa that lists the locations of pixels * in each of the components. * (2) It can be used to initialize the output image and arrays for * an application that maintains information about connected * components incrementally as pixels are added. * (3) pixs can be empty or have some foreground pixels. * (4) The connectivity is stored in pixd->special. * (5) Always initialize with the first pta in ptaa being empty * and representing the background value (index 0) in the pix. * </pre> */ l_int32 pixConnCompIncrInit(PIX *pixs, l_int32 conn, PIX **ppixd, PTAA **pptaa, l_int32 *pncc) { l_int32 empty, w, h, ncc; PIX *pixd; PTA *pta; PTAA *ptaa; PROCNAME("pixConnCompIncrInit"); if (ppixd) *ppixd = NULL; if (pptaa) *pptaa = NULL; if (pncc) *pncc = 0; if (!ppixd || !pptaa || !pncc) return ERROR_INT("&pixd, &ptaa, &ncc not all defined", procName, 1); if (!pixs || pixGetDepth(pixs) != 1) return ERROR_INT("pixs undefined or not 1 bpp", procName, 1); if (conn != 4 && conn != 8) return ERROR_INT("connectivity must be 4 or 8", procName, 1); pixGetDimensions(pixs, &w, &h, NULL); pixZero(pixs, &empty); if (empty) { *ppixd = pixCreate(w, h, 32); pixSetSpp(*ppixd, 1); pixSetSpecial(*ppixd, conn); *pptaa = ptaaCreate(0); pta = ptaCreate(1); ptaaAddPta(*pptaa, pta, L_INSERT); /* reserve index 0 for background */ return 0; } /* Set up the initial labeled image and indexed pixel arrays */ if ((pixd = pixConnCompTransform(pixs, conn, 32)) == NULL) return ERROR_INT("pixd not made", procName, 1); pixSetSpecial(pixd, conn); *ppixd = pixd; if ((ptaa = ptaaIndexLabeledPixels(pixd, &ncc)) == NULL) return ERROR_INT("ptaa not made", procName, 1); *pptaa = ptaa; *pncc = ncc; return 0; }
/*! * \brief recogGetWindowedArea() * * \param[in] recog * \param[in] index of template * \param[in] x pixel position of left hand edge of template * \param[out] pdely y shift of template relative to pix1 * \param[out] pwsum number of fg pixels in window of pixs * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) This is called after the best path has been found through * the trellis, in order to produce a correlation that can be used * to evaluate the confidence we have in the identification. * The correlation is |1 \& 2|^2 / (|1| * |2|). * |1 \& 2| is given by the count array, |2| is found from * nasum_u[], and |1| is wsum returned from this function. * </pre> */ static l_int32 recogGetWindowedArea(L_RECOG *recog, l_int32 index, l_int32 x, l_int32 *pdely, l_int32 *pwsum) { l_int32 w1, h1, w2, h2; PIX *pix1, *pix2, *pixt; L_RDID *did; PROCNAME("recogGetWindowedArea"); if (pdely) *pdely = 0; if (pwsum) *pwsum = 0; if (!pdely || !pwsum) return ERROR_INT("&dely and &wsum not both defined", procName, 1); if (!recog) return ERROR_INT("recog not defined", procName, 1); if ((did = recogGetDid(recog)) == NULL) return ERROR_INT("did not defined", procName, 1); if (index < 0 || index >= did->narray) return ERROR_INT("invalid index", procName, 1); pix1 = did->pixs; pixGetDimensions(pix1, &w1, &h1, NULL); if (x >= w1) return ERROR_INT("invalid x position", procName, 1); pix2 = pixaGetPix(recog->pixa_u, index, L_CLONE); pixGetDimensions(pix2, &w2, &h2, NULL); if (w1 < w2) { L_INFO("template %d too small\n", procName, index); pixDestroy(&pix2); return 0; } *pdely = did->delya[index][x]; pixt = pixCreate(w2, h1, 1); pixRasterop(pixt, 0, *pdely, w2, h2, PIX_SRC, pix2, 0, 0); pixRasterop(pixt, 0, 0, w2, h1, PIX_SRC & PIX_DST, pix1, x, 0); pixCountPixels(pixt, pwsum, recog->sumtab); pixDestroy(&pix2); pixDestroy(&pixt); return 0; }
/*! * \brief pixReadMemWebP() * * \param[in] filedata webp compressed data in memory * \param[in] filesize number of bytes in data * \return pix 32 bpp, or NULL on error * * <pre> * Notes: * (1) When the encoded data only has 3 channels (no alpha), * WebPDecodeRGBAInto() generates a raster of 32-bit pixels, with * the alpha channel set to opaque (255). * (2) We don't need to use the gnu runtime functions like fmemopen() * for redirecting data from a stream to memory, because * the webp library has been written with memory-to-memory * functions at the lowest level (which is good!). And, in * any event, fmemopen() doesn't work with l_binaryReadStream(). * </pre> */ PIX * pixReadMemWebP(const l_uint8 *filedata, size_t filesize) { l_uint8 *out = NULL; l_int32 w, h, has_alpha, wpl, stride; l_uint32 *data; size_t size; PIX *pix; WebPBitstreamFeatures features; PROCNAME("pixReadMemWebP"); if (!filedata) return (PIX *)ERROR_PTR("filedata not defined", procName, NULL); if (WebPGetFeatures(filedata, filesize, &features)) return (PIX *)ERROR_PTR("Invalid WebP file", procName, NULL); w = features.width; h = features.height; has_alpha = features.has_alpha; /* Write from compressed Y,U,V arrays to pix raster data */ pix = pixCreate(w, h, 32); pixSetInputFormat(pix, IFF_WEBP); if (has_alpha) pixSetSpp(pix, 4); data = pixGetData(pix); wpl = pixGetWpl(pix); stride = wpl * 4; size = stride * h; out = WebPDecodeRGBAInto(filedata, filesize, (uint8_t *)data, size, stride); if (out == NULL) { /* error: out should also point to data */ pixDestroy(&pix); return (PIX *)ERROR_PTR("WebP decode failed", procName, NULL); } /* The WebP API expects data in RGBA order. The pix stores * in host-dependent order with R as the MSB and A as the LSB. * On little-endian machines, the bytes in the word must * be swapped; e.g., R goes from byte 0 (LSB) to byte 3 (MSB). * No swapping is necessary for big-endians. */ pixEndianByteSwap(pix); return pix; }