static void AddScaledImages(PIXA *pixa, const char *fname, l_int32 width) { l_int32 i, w; l_float32 scalefactor; PIX *pixs, *pixt1, *pixt2, *pix32; pixs = pixRead(fname); w = pixGetWidth(pixs); for (i = 0; i < 5; i++) { scalefactor = (l_float32)width / (FACTOR[i] * (l_float32)w); pixt1 = pixScale(pixs, FACTOR[i], FACTOR[i]); pixt2 = pixScale(pixt1, scalefactor, scalefactor); pix32 = pixConvertTo32(pixt2); if (i == 0) pixSaveTiled(pix32, pixa, 1.0, 1, SPACE, 32); else pixSaveTiled(pix32, pixa, 1.0, 0, SPACE, 32); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pix32); } pixDestroy(&pixs); return; }
/*! * \brief recogShowPath() * * \param[in] recog with LUT's pre-computed * \param[in] select 0 for Viterbi; 1 for rescored * \return pix debug output), or NULL on error */ static PIX * recogShowPath(L_RECOG *recog, l_int32 select) { char textstr[16]; l_int32 i, n, index, xloc, dely; l_float32 score; L_BMF *bmf; NUMA *natempl_s, *nascore_s, *naxloc_s, *nadely_s; PIX *pixs, *pix0, *pix1, *pix2, *pix3, *pix4, *pix5; L_RDID *did; PROCNAME("recogShowPath"); if (!recog) return (PIX *)ERROR_PTR("recog not defined", procName, NULL); if ((did = recogGetDid(recog)) == NULL) return (PIX *)ERROR_PTR("did not defined", procName, NULL); bmf = bmfCreate(NULL, 8); pixs = pixScale(did->pixs, 4.0, 4.0); pix0 = pixAddBorderGeneral(pixs, 0, 0, 0, 40, 0); pix1 = pixConvertTo32(pix0); if (select == 0) { /* Viterbi */ natempl_s = did->natempl; nascore_s = did->nascore; naxloc_s = did->naxloc; nadely_s = did->nadely; } else { /* rescored */ natempl_s = did->natempl_r; nascore_s = did->nascore_r; naxloc_s = did->naxloc_r; nadely_s = did->nadely_r; } n = numaGetCount(natempl_s); for (i = 0; i < n; i++) { numaGetIValue(natempl_s, i, &index); pix2 = pixaGetPix(recog->pixa_u, index, L_CLONE); pix3 = pixScale(pix2, 4.0, 4.0); pix4 = pixErodeBrick(NULL, pix3, 5, 5); pixXor(pix4, pix4, pix3); numaGetFValue(nascore_s, i, &score); snprintf(textstr, sizeof(textstr), "%5.3f", score); pix5 = pixAddTextlines(pix4, bmf, textstr, 1, L_ADD_BELOW); numaGetIValue(naxloc_s, i, &xloc); numaGetIValue(nadely_s, i, &dely); pixPaintThroughMask(pix1, pix5, 4 * xloc, 4 * dely, 0xff000000); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); } pixDestroy(&pixs); pixDestroy(&pix0); bmfDestroy(&bmf); return pix1; }
int main(int argc, char **argv) { BOX *box; PIX *pixt1, *pixt2, *pix1, *pix2, *pix3; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixt1 = pixRead("feyn.tif"); /* 300 ppi */ box = boxCreate(19, 774, 2247, 2025); pix1 = pixClipRectangle(pixt1, box, NULL); pixDestroy(&pixt1); pixt1 = pixRead("lucasta.150.jpg"); pixt2 = pixConvertTo1(pixt1, 128); /* 150 ppi */ pix2 = pixScale(pixt2, 2.2, 2.2); /* 300 ppi */ pixDestroy(&pixt1); pixDestroy(&pixt2); pixt1 = pixRead("zanotti-78.jpg"); pixt2 = pixConvertTo1(pixt1, 128); /* 150 ppi */ pix3 = pixScale(pixt2, 2.0, 2.0); /* 300 ppi */ pixDestroy(&pixt1); pixDestroy(&pixt2); boxDestroy(&box); /* Make word boxes using pixWordMaskByDilation() */ MakeWordBoxes1(pix1, 20, rp); /* 0 */ MakeWordBoxes1(pix2, 20, rp); /* 1 */ MakeWordBoxes1(pix3, 20, rp); /* 2 */ /* Make word boxes using the higher-level functions * pixGetWordsInTextlines() and pixGetWordBoxesInTextlines() */ MakeWordBoxes2(pix1, 1, rp); /* 3, 4 */ MakeWordBoxes2(pix2, 1, rp); /* 5, 6 */ MakeWordBoxes2(pix3, 1, rp); /* 7, 8 */ /* Make word boxes using the higher-level functions * pixGetWordsInTextlines() and pixGetWordBoxesInTextlines() */ MakeWordBoxes2(pix1, 2, rp); /* 9, 10 */ MakeWordBoxes2(pix2, 2, rp); /* 11, 12 */ MakeWordBoxes2(pix3, 2, rp); /* 13, 14 */ pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); return regTestCleanup(rp); }
/* static */ void Input::PreparePixInput(const StaticShape& shape, const Pix* pix, TRand* randomizer, NetworkIO* input) { bool color = shape.depth() == 3; Pix* var_pix = const_cast<Pix*>(pix); int depth = pixGetDepth(var_pix); Pix* normed_pix = nullptr; // On input to BaseAPI, an image is forced to be 1, 8 or 24 bit, without // colormap, so we just have to deal with depth conversion here. if (color) { // Force RGB. if (depth == 32) normed_pix = pixClone(var_pix); else normed_pix = pixConvertTo32(var_pix); } else { // Convert non-8-bit images to 8 bit. if (depth == 8) normed_pix = pixClone(var_pix); else normed_pix = pixConvertTo8(var_pix, false); } int height = pixGetHeight(normed_pix); int target_height = shape.height(); if (target_height == 1) target_height = shape.depth(); if (target_height != 0 && target_height != height) { // Get the scaled image. float im_factor = static_cast<float>(target_height) / height; Pix* scaled_pix = pixScale(normed_pix, im_factor, im_factor); pixDestroy(&normed_pix); normed_pix = scaled_pix; } input->FromPix(shape, normed_pix, randomizer); pixDestroy(&normed_pix); }
/*! * pixDisplayWriteFormat() * * Input: pix (1, 2, 4, 8, 16, 32 bpp) * reduction (-1 to reset/erase; 0 to disable; * otherwise this is a reduction factor) * format (IFF_PNG or IFF_JFIF_JPEG) * Return: 0 if OK; 1 on error * * Notes: * (1) This writes files if reduction > 0. These can be displayed using * pixDisplayMultiple("/tmp/junk_write_display*"); * (2) All previously written files can be erased by calling with * reduction < 0; the value of pixs is ignored. * (3) If reduction > 1 and depth == 1, this does a scale-to-gray * reduction. * (4) This function uses a static internal variable to number * output files written by a single process. Behavior * with a shared library may be unpredictable. * (5) Output file format is as follows: * format == IFF_JFIF_JPEG: * png if d < 8 or d == 16 or if the output pix * has a colormap. Otherwise, output is jpg. * format == IFF_PNG: * png (lossless) on all images. * (6) For 16 bpp, the choice of full dynamic range with log scale * is the best for displaying these images. Alternative outputs are * pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE); * pix8 = pixConvert16To8(pixt, 0); // low order byte * pix8 = pixConvert16To8(pixt, 1); // high order byte */ l_int32 pixDisplayWriteFormat(PIX *pixs, l_int32 reduction, l_int32 format) { char buffer[L_BUF_SIZE]; l_float32 scale; PIX *pixt, *pix8; static l_int32 index = 0; /* caution: not .so or thread safe */ PROCNAME("pixDisplayWriteFormat"); if (reduction == 0) return 0; if (reduction < 0) { index = 0; /* reset; this will cause erasure at next call to write */ return 0; } if (format != IFF_JFIF_JPEG && format != IFF_PNG) return ERROR_INT("invalid format", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (index == 0) { snprintf(buffer, L_BUF_SIZE, "rm -f /tmp/junk_write_display.*.png /tmp/junk_write_display.*.jpg"); system(buffer); } index++; if (reduction == 1) pixt = pixClone(pixs); else { scale = 1. / (l_float32)reduction; if (pixGetDepth(pixs) == 1) pixt = pixScaleToGray(pixs, scale); else pixt = pixScale(pixs, scale, scale); } if (pixGetDepth(pixt) == 16) { pix8 = pixMaxDynamicRange(pixt, L_LOG_SCALE); snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.png", index); pixWrite(buffer, pix8, IFF_PNG); pixDestroy(&pix8); } else if (pixGetDepth(pixt) < 8 || pixGetColormap(pixt) || format == IFF_PNG) { snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.png", index); pixWrite(buffer, pixt, IFF_PNG); } else { snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.jpg", index); pixWrite(buffer, pixt, format); } pixDestroy(&pixt); return 0; }
jint Java_com_googlecode_leptonica_android_Scale_nativeScale(JNIEnv *env, jclass clazz, jint nativePix, jfloat scaleX, jfloat scaleY) { PIX *pixs = (PIX *) nativePix; PIX *pixd = pixScale(pixs, (l_float32) scaleX, (l_float32) scaleY); return (jint) pixd; }
image_object::image_object( const char* fileName, const char* lang ) : image( pixScale( pixRead( fileName ), PIXSCALE, PIXSCALE ) ){ assert( image != NULL ); // TODO: Find a better way to fail. tesseract.Init( TESSDATAPATH, lang ); tesseract.SetImage( image ); tesseract.SetVariable( "tessedit_char_whitelist", "+-#~/:(),<>&* _0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ); } //image_object (constructor)
main(int argc, char **argv) { char *filein, *fileout; l_int32 d; l_float32 scalex, scaley; PIX *pixs, *pixd; static char mainName[] = "scaletest1"; if (argc != 5) return ERROR_INT(" Syntax: scaletest1 filein scalex scaley fileout", mainName, 1); filein = argv[1]; scalex = atof(argv[2]); scaley = atof(argv[3]); fileout = argv[4]; if ((pixs = pixRead(filein)) == NULL) return ERROR_INT("pixs not made", mainName, 1); /* choose type of scaling operation */ #if 1 pixd = pixScale(pixs, scalex, scaley); #elif 0 pixd = pixScaleLI(pixs, scalex, scaley); #elif 0 pixd = pixScaleSmooth(pixs, scalex, scaley); #elif 0 pixd = pixScaleAreaMap(pixs, scalex, scaley); #elif 0 pixd = pixScaleBySampling(pixs, scalex, scaley); #else pixd = pixScaleToGray(pixs, scalex); #endif d = pixGetDepth(pixd); #if 1 if (d <= 8) pixWrite(fileout, pixd, IFF_PNG); else pixWrite(fileout, pixd, IFF_JFIF_JPEG); #else pixWrite(fileout, pixd, IFF_PNG); #endif pixDestroy(&pixs); pixDestroy(&pixd); return 0; }
Pix* upscale(Pix* pix) { l_int32 pixPixelCount = pixGetWidth(pix)* pixGetHeight(pix); const l_int32 MIN_PIXEL_COUNT = 3 * 1024*1024; if (pixPixelCount < MIN_PIXEL_COUNT) { l_float32 scale = ((double) MIN_PIXEL_COUNT) / pixPixelCount; scale = sqrt(scale); Pix* scaled = pixScale(pix, scale,scale); l_int32 xres, yres; pixGetResolution(pix, &xres, &yres); pixSetResolution(scaled, 600, 600); return scaled; } return pixClone(pix); }
unsigned long long getImagePhash_px( PIX *pix_orig, char *tmpFilename ) { int free_8 = 0; // Convert colour images down to grey PIX *pix8; if( pixGetDepth(pix_orig) > 8 ) { pix8 = pixScaleRGBToGrayFast( pix_orig, 1, COLOR_GREEN ); if( pix8 == NULL ) { printf("Covertion to 8bit, did not go well."); return 0; } free_8 = 1; } else { // already gray free_8 = 0; pix8 = pix_orig; } int width = pixGetWidth( pix8 ); int height = pixGetHeight( pix8 ); BOX* box = boxCreate(1, 1, width-2, height-2); PIX* pixc = pixClipRectangle(pix8, box, NULL); if(free_8 == 1) { pixDestroy( &pix8 ); } PIX *pix8s = pixScale(pixc, 0.2, 0.2); pixDestroy( &pixc ); // Convert image down to binary (no gray) /* PIX *pix1 = pixThresholdToBinary( pix8s, 200 ); if( pix1 == NULL ) { printf( "Covertion to 1bit, did not go well."); pixDestroy( &pix8s ); return 0; } pixDestroy( &pix8s ); */ // Save the file for pHash processnig pixWrite( tmpFilename, pix8s, IFF_JFIF_JPEG); pixDestroy( &pix8s ); unsigned long long ret = calculateImagePhash( tmpFilename ); //unlink(tmpFilename); return ret; }
PIXA * GenerateSetOfMargePix(void) { l_float32 factor; BOX *box; PIX *pixs, *pixt1, *pixt2, *pixt3, *pixt4; PIXA *pixa; pixs = pixRead("marge.jpg"); box = boxCreate(130, 93, 263, 253); factor = sqrt(2.0); pixt1 = pixClipRectangle(pixs, box, NULL); /* 266 KB */ pixt2 = pixScale(pixt1, factor, factor); /* 532 KB */ pixt3 = pixScale(pixt2, factor, factor); /* 1064 KB */ pixt4 = pixScale(pixt3, factor, factor); /* 2128 KB */ pixa = pixaCreate(4); pixaAddPix(pixa, pixt1, L_INSERT); pixaAddPix(pixa, pixt2, L_INSERT); pixaAddPix(pixa, pixt3, L_INSERT); pixaAddPix(pixa, pixt4, L_INSERT); boxDestroy(&box); pixDestroy(&pixs); return pixa; }
// Gets anything and everything with a non-NULL pointer, prescaled to a // given target_height (if 0, then the original image height), and aligned. // Also returns (if not NULL) the width and height of the scaled image. // The return value is the scale factor that was applied to the image to // achieve the target_height. float ImageData::PreScale(int target_height, Pix** pix, int* scaled_width, int* scaled_height, GenericVector<TBOX>* boxes) const { int input_width = 0; int input_height = 0; Pix* src_pix = GetPix(); ASSERT_HOST(src_pix != NULL); input_width = pixGetWidth(src_pix); input_height = pixGetHeight(src_pix); if (target_height == 0) target_height = input_height; float im_factor = static_cast<float>(target_height) / input_height; if (scaled_width != NULL) *scaled_width = IntCastRounded(im_factor * input_width); if (scaled_height != NULL) *scaled_height = target_height; if (pix != NULL) { // Get the scaled image. pixDestroy(pix); *pix = pixScale(src_pix, im_factor, im_factor); if (*pix == NULL) { tprintf("Scaling pix of size %d, %d by factor %g made null pix!!\n", input_width, input_height, im_factor); } if (scaled_width != NULL) *scaled_width = pixGetWidth(*pix); if (scaled_height != NULL) *scaled_height = pixGetHeight(*pix); } pixDestroy(&src_pix); if (boxes != NULL) { // Get the boxes. boxes->truncate(0); for (int b = 0; b < boxes_.size(); ++b) { TBOX box = boxes_[b]; box.scale(im_factor); boxes->push_back(box); } if (boxes->empty()) { // Make a single box for the whole image. TBOX box(0, 0, im_factor * input_width, target_height); boxes->push_back(box); } } return im_factor; }
// Gets anything and everything with a non-NULL pointer, prescaled to a // given target_height (if 0, then the original image height), and aligned. // Also returns (if not NULL) the width and height of the scaled image. void ImageData::PreScale(int target_height, Pix** pix, int* scaled_width, int* scaled_height, GenericVector<TBOX>* boxes) const { int input_width = 0; int input_height = 0; Pix* src_pix = GetPix(); ASSERT_HOST(src_pix != NULL); input_width = pixGetWidth(src_pix); input_height = pixGetHeight(src_pix); if (target_height == 0) target_height = input_height; float im_factor = static_cast<float>(target_height) / input_height; if (scaled_width != NULL) *scaled_width = IntCastRounded(im_factor * input_width); if (scaled_height != NULL) *scaled_height = target_height; if (pix != NULL) { // Get the scaled image. pixDestroy(pix); *pix = pixScale(src_pix, im_factor, im_factor); if (scaled_width != NULL) *scaled_width = pixGetWidth(*pix); if (scaled_height != NULL) *scaled_height = pixGetHeight(*pix); } pixDestroy(&src_pix); if (boxes != NULL) { // Get the boxes. boxes->truncate(0); for (int b = 0; b < boxes_.size(); ++b) { TBOX box = boxes_[b]; box.scale(im_factor); boxes->push_back(box); } } }
/*! * pixDisplayWithTitle() * * Input: pix (1, 2, 4, 8, 16, 32 bpp) * x, y (location of display frame) * title (<optional> on frame; can be NULL); * dispflag (1 to write, else disabled) * Return: 0 if OK; 1 on error * * Notes: * (1) See notes for pixDisplay(). * (2) This displays the image if dispflag == 1. */ l_int32 pixDisplayWithTitle(PIX *pixs, l_int32 x, l_int32 y, const char *title, l_int32 dispflag) { char *tempname; char buffer[L_BUF_SIZE]; static l_int32 index = 0; /* caution: not .so or thread safe */ l_int32 w, h, d, spp, maxheight, opaque, threeviews, ignore; l_float32 ratw, rath, ratmin; PIX *pix0, *pix1, *pix2; PIXCMAP *cmap; #ifndef _WIN32 l_int32 wt, ht; #else char *pathname; char fullpath[_MAX_PATH]; #endif /* _WIN32 */ PROCNAME("pixDisplayWithTitle"); if (dispflag != 1) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV && var_DISPLAY_PROG != L_DISPLAY_WITH_XLI && var_DISPLAY_PROG != L_DISPLAY_WITH_XV && var_DISPLAY_PROG != L_DISPLAY_WITH_IV && var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN) { return ERROR_INT("no program chosen for display", procName, 1); } /* Display with three views if either spp = 4 or if colormapped * and the alpha component is not fully opaque */ opaque = TRUE; if ((cmap = pixGetColormap(pixs)) != NULL) pixcmapIsOpaque(cmap, &opaque); spp = pixGetSpp(pixs); threeviews = (spp == 4 || !opaque) ? TRUE : FALSE; /* If colormapped and not opaque, remove the colormap to RGBA */ if (!opaque) pix0 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA); else pix0 = pixClone(pixs); /* Scale if necessary; this will also remove a colormap */ pixGetDimensions(pix0, &w, &h, &d); maxheight = (threeviews) ? MAX_DISPLAY_HEIGHT / 3 : MAX_DISPLAY_HEIGHT; if (w <= MAX_DISPLAY_WIDTH && h <= maxheight) { if (d == 16) /* take MSB */ pix1 = pixConvert16To8(pix0, 1); else pix1 = pixClone(pix0); } else { ratw = (l_float32)MAX_DISPLAY_WIDTH / (l_float32)w; rath = (l_float32)maxheight / (l_float32)h; ratmin = L_MIN(ratw, rath); if (ratmin < 0.125 && d == 1) pix1 = pixScaleToGray8(pix0); else if (ratmin < 0.25 && d == 1) pix1 = pixScaleToGray4(pix0); else if (ratmin < 0.33 && d == 1) pix1 = pixScaleToGray3(pix0); else if (ratmin < 0.5 && d == 1) pix1 = pixScaleToGray2(pix0); else pix1 = pixScale(pix0, ratmin, ratmin); } pixDestroy(&pix0); if (!pix1) return ERROR_INT("pix1 not made", procName, 1); /* Generate the three views if required */ if (threeviews) pix2 = pixDisplayLayersRGBA(pix1, 0xffffff00, 0); else pix2 = pixClone(pix1); if (index == 0) { lept_rmdir("disp"); lept_mkdir("disp"); } index++; if (pixGetDepth(pix2) < 8 || (w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) { snprintf(buffer, L_BUF_SIZE, "/tmp/disp/write.%03d.png", index); pixWrite(buffer, pix2, IFF_PNG); } else { snprintf(buffer, L_BUF_SIZE, "/tmp/disp/write.%03d.jpg", index); pixWrite(buffer, pix2, IFF_JFIF_JPEG); } tempname = stringNew(buffer); #ifndef _WIN32 /* Unix */ if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) { /* no way to display title */ pixGetDimensions(pix2, &wt, &ht, NULL); snprintf(buffer, L_BUF_SIZE, "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10, x, y, tempname); } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) { if (title) { snprintf(buffer, L_BUF_SIZE, "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &", x, y, title, tempname); } else { snprintf(buffer, L_BUF_SIZE, "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &", x, y, tempname); } } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) { if (title) { snprintf(buffer, L_BUF_SIZE, "xv -quit -geometry +%d+%d -name \"%s\" %s &", x, y, title, tempname); } else { snprintf(buffer, L_BUF_SIZE, "xv -quit -geometry +%d+%d %s &", x, y, tempname); } } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_OPEN) { snprintf(buffer, L_BUF_SIZE, "open %s &", tempname); } ignore = system(buffer); #else /* _WIN32 */ /* Windows: L_DISPLAY_WITH_IV */ pathname = genPathname(tempname, NULL); _fullpath(fullpath, pathname, sizeof(fullpath)); if (title) { snprintf(buffer, L_BUF_SIZE, "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"", fullpath, x, y, title); } else { snprintf(buffer, L_BUF_SIZE, "i_view32.exe \"%s\" /pos=(%d,%d)", fullpath, x, y); } ignore = system(buffer); FREE(pathname); #endif /* _WIN32 */ pixDestroy(&pix1); pixDestroy(&pix2); FREE(tempname); return 0; }
/*! * pixSaveTiledOutline() * * Input: pixs (1, 2, 4, 8, 32 bpp) * pixa (the pix are accumulated here) * scalefactor (0.0 to disable; otherwise this is a scale factor) * newrow (0 if placed on the same row as previous; 1 otherwise) * space (horizontal and vertical spacing, in pixels) * linewidth (width of added outline for image; 0 for no outline) * dp (depth of pixa; 8 or 32 bpp; only used on first call) * Return: 0 if OK, 1 on error. * * Notes: * (1) Before calling this function for the first time, use * pixaCreate() to make the @pixa that will accumulate the pix. * This is passed in each time pixSaveTiled() is called. * (2) @scalefactor scales the input image. After scaling and * possible depth conversion, the image is saved in the input * pixa, along with a box that specifies the location to * place it when tiled later. Disable saving the pix by * setting @scalefactor == 0.0. * (3) @newrow and @space specify the location of the new pix * with respect to the last one(s) that were entered. * (4) @dp specifies the depth at which all pix are saved. It can * be only 8 or 32 bpp. Any colormap is removed. This is only * used at the first invocation. * (5) This function uses two variables from call to call. * If they were static, the function would not be .so or thread * safe, and furthermore, there would be interference with two or * more pixa accumulating images at a time. Consequently, * we use the first pix in the pixa to store and obtain both * the depth and the current position of the bottom (one pixel * below the lowest image raster line when laid out using * the boxa). The bottom variable is stored in the input format * field, which is the only field available for storing an int. */ l_int32 pixSaveTiledOutline(PIX *pixs, PIXA *pixa, l_float32 scalefactor, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp) { l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; BOX *box; PIX *pix1, *pix2, *pix3, *pix4; PROCNAME("pixSaveTiledOutline"); if (scalefactor == 0.0) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (n == 0) { bottom = 0; if (dp != 8 && dp != 32) { L_WARNING("dp not 8 or 32 bpp; using 32\n", procName); depth = 32; } else { depth = dp; } } else { /* extract the depth and bottom params from the first pix */ pix1 = pixaGetPix(pixa, 0, L_CLONE); depth = pixGetDepth(pix1); bottom = pixGetInputFormat(pix1); /* not typical usage! */ pixDestroy(&pix1); } /* Remove colormap if it exists; otherwise a copy. This * guarantees that pix4 is not a clone of pixs. */ pix1 = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY); /* Scale and convert to output depth */ if (scalefactor == 1.0) { pix2 = pixClone(pix1); } else if (scalefactor > 1.0) { pix2 = pixScale(pix1, scalefactor, scalefactor); } else if (scalefactor < 1.0) { if (pixGetDepth(pix1) == 1) pix2 = pixScaleToGray(pix1, scalefactor); else pix2 = pixScale(pix1, scalefactor, scalefactor); } pixDestroy(&pix1); if (depth == 8) pix3 = pixConvertTo8(pix2, 0); else pix3 = pixConvertTo32(pix2); pixDestroy(&pix2); /* Add black outline */ if (linewidth > 0) pix4 = pixAddBorder(pix3, linewidth, 0); else pix4 = pixClone(pix3); pixDestroy(&pix3); /* Find position of current pix (UL corner plus size) */ if (n == 0) { top = 0; left = 0; } else if (newrow == 1) { top = bottom + space; left = 0; } else if (n > 0) { pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); top = by; left = bx + bw + space; } pixGetDimensions(pix4, &w, &h, NULL); bottom = L_MAX(bottom, top + h); box = boxCreate(left, top, w, h); pixaAddPix(pixa, pix4, L_INSERT); pixaAddBox(pixa, box, L_INSERT); /* Save the new bottom value */ pix1 = pixaGetPix(pixa, 0, L_CLONE); pixSetInputFormat(pix1, bottom); /* not typical usage! */ pixDestroy(&pix1); return 0; }
int main(int argc, char **argv) { l_int32 i, j, w, h, same; l_float32 t, t1, t2; GPLOT *gplot; NUMA *nax, *nay1, *nay2; PIX *pixs, *pixd, *pixt1, *pixt2, *pixt3, *pixt4; PIXA *pixa; static char mainName[] = "rank_reg"; if (argc != 1) return ERROR_INT(" Syntax: rank_reg", mainName, 1); if ((pixs = pixRead("lucasta.150.jpg")) == NULL) return ERROR_INT("pixs not made", mainName, 1); pixGetDimensions(pixs, &w, &h, NULL); startTimer(); pixd = pixRankFilterGray(pixs, 15, 15, 0.4); t = stopTimer(); fprintf(stderr, "Time = %7.3f sec\n", t); fprintf(stderr, "MPix/sec: %7.3f\n", 0.000001 * w * h / t); pixDisplay(pixs, 0, 200); pixDisplay(pixd, 600, 200); pixWrite("/tmp/filter.png", pixd, IFF_PNG); pixDestroy(&pixd); /* Get results for dilation */ startTimer(); pixt1 = pixDilateGray(pixs, 15, 15); t = stopTimer(); fprintf(stderr, "Dilation time = %7.3f sec\n", t); /* Get results for erosion */ pixt2 = pixErodeGray(pixs, 15, 15); /* Get results using the rank filter for rank = 0.0 and 1.0. * Don't use 0.0 or 1.0, because those are dispatched * automatically to erosion and dilation! */ pixt3 = pixRankFilterGray(pixs, 15, 15, 0.0001); pixt4 = pixRankFilterGray(pixs, 15, 15, 0.9999); /* Compare */ pixEqual(pixt1, pixt4, &same); if (same) fprintf(stderr, "Correct: dilation results same as rank 1.0\n"); else fprintf(stderr, "Error: dilation results differ from rank 1.0\n"); pixEqual(pixt2, pixt3, &same); if (same) fprintf(stderr, "Correct: erosion results same as rank 0.0\n"); else fprintf(stderr, "Error: erosion results differ from rank 0.0\n"); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixDestroy(&pixt4); fprintf(stderr, "\n----------------------------------------\n"); fprintf(stderr, "The next part takes about 30 seconds\n"); fprintf(stderr, "----------------------------------------\n\n"); nax = numaMakeSequence(1, 1, SIZE); nay1 = numaCreate(SIZE); nay2 = numaCreate(SIZE); gplot = gplotCreate("/tmp/rankroot", GPLOT_X11, "sec/MPix vs filter size", "size", "time"); for (i = 1; i <= SIZE; i++) { t1 = t2 = 0.0; for (j = 0; j < 5; j++) { startTimer(); pixt1 = pixRankFilterGray(pixs, i, SIZE + 1, 0.5); t1 += stopTimer(); pixDestroy(&pixt1); startTimer(); pixt1 = pixRankFilterGray(pixs, SIZE + 1, i, 0.5); t2 += stopTimer(); if (j == 0) pixDisplayWrite(pixt1, 1); pixDestroy(&pixt1); } numaAddNumber(nay1, 1000000. * t1 / (5. * w * h)); numaAddNumber(nay2, 1000000. * t2 / (5. * w * h)); } gplotAddPlot(gplot, nax, nay1, GPLOT_LINES, "vertical"); gplotAddPlot(gplot, nax, nay2, GPLOT_LINES, "horizontal"); gplotMakeOutput(gplot); gplotDestroy(&gplot); /* Display tiled */ pixa = pixaReadFiles("/tmp/display", "file"); pixd = pixaDisplayTiledAndScaled(pixa, 8, 250, 5, 0, 25, 2); pixWrite("/tmp/tiles.jpg", pixd, IFF_JFIF_JPEG); pixDestroy(&pixd); pixaDestroy(&pixa); pixDestroy(&pixs); pixDisplayWrite(NULL, -1); /* clear out */ pixs = pixRead("test8.jpg"); for (i = 1; i <= 4; i++) { pixt1 = pixScaleGrayRank2(pixs, i); pixDisplay(pixt1, 300 * (i - 1), 100); pixDestroy(&pixt1); } pixDestroy(&pixs); pixs = pixRead("test24.jpg"); pixt1 = pixConvertRGBToLuminance(pixs); pixt2 = pixScale(pixt1, 1.5, 1.5); for (i = 1; i <= 4; i++) { for (j = 1; j <= 4; j++) { pixt3 = pixScaleGrayRankCascade(pixt2, i, j, 0, 0); pixDisplayWrite(pixt3, 1); pixDestroy(&pixt3); } } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixs); pixDisplayMultiple("/tmp/display/file*"); return 0; }
/*! * pixaDisplayTiledAndScaled() * * Input: pixa * outdepth (output depth: 1, 8 or 32 bpp) * tilewidth (each pix is scaled to this width) * ncols (number of tiles in each row) * background (0 for white, 1 for black; this is the color * of the spacing between the images) * spacing (between images, and on outside) * border (width of additional black border on each image; * use 0 for no border) * Return: pix of tiled images, or null on error * * Notes: * (1) This can be used to tile a number of renderings of * an image that are at different scales and depths. * (2) Each image, after scaling and optionally adding the * black border, has width 'tilewidth'. Thus, the border does * not affect the spacing between the image tiles. The * maximum allowed border width is tilewidth / 5. */ PIX * pixaDisplayTiledAndScaled(PIXA *pixa, l_int32 outdepth, l_int32 tilewidth, l_int32 ncols, l_int32 background, l_int32 spacing, l_int32 border) { l_int32 x, y, w, h, wd, hd, d; l_int32 i, n, nrows, maxht, ninrow, irow, bordval; l_int32 *rowht; l_float32 scalefact; PIX *pix, *pixn, *pixt, *pixb, *pixd; PIXA *pixan; PROCNAME("pixaDisplayTiledAndScaled"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); if (outdepth != 1 && outdepth != 8 && outdepth != 32) return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); if (border < 0 || border > tilewidth / 5) border = 0; if ((n = pixaGetCount(pixa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* Normalize scale and depth for each pix; optionally add border */ pixan = pixaCreate(n); bordval = (outdepth == 1) ? 1 : 0; for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) continue; pixGetDimensions(pix, &w, &h, &d); scalefact = (l_float32)(tilewidth - 2 * border) / (l_float32)w; if (d == 1 && outdepth > 1 && scalefact < 1.0) pixt = pixScaleToGray(pix, scalefact); else pixt = pixScale(pix, scalefact, scalefact); if (outdepth == 1) pixn = pixConvertTo1(pixt, 128); else if (outdepth == 8) pixn = pixConvertTo8(pixt, FALSE); else /* outdepth == 32 */ pixn = pixConvertTo32(pixt); pixDestroy(&pixt); if (border) pixb = pixAddBorder(pixn, border, bordval); else pixb = pixClone(pixn); pixaAddPix(pixan, pixb, L_INSERT); pixDestroy(&pix); pixDestroy(&pixn); } if ((n = pixaGetCount(pixan)) == 0) { /* should not have changed! */ pixaDestroy(&pixan); return (PIX *)ERROR_PTR("no components", procName, NULL); } /* Determine the size of each row and of pixd */ wd = tilewidth * ncols + spacing * (ncols + 1); nrows = (n + ncols - 1) / ncols; if ((rowht = (l_int32 *)CALLOC(nrows, sizeof(l_int32))) == NULL) return (PIX *)ERROR_PTR("rowht array not made", procName, NULL); maxht = 0; ninrow = 0; irow = 0; for (i = 0; i < n; i++) { pix = pixaGetPix(pixan, i, L_CLONE); ninrow++; pixGetDimensions(pix, &w, &h, NULL); maxht = L_MAX(h, maxht); if (ninrow == ncols) { rowht[irow] = maxht; maxht = ninrow = 0; /* reset */ irow++; } pixDestroy(&pix); } if (ninrow > 0) { /* last fencepost */ rowht[irow] = maxht; irow++; /* total number of rows */ } nrows = irow; hd = spacing * (nrows + 1); for (i = 0; i < nrows; i++) hd += rowht[i]; pixd = pixCreate(wd, hd, outdepth); if ((background == 1 && outdepth == 1) || (background == 0 && outdepth != 1)) pixSetAll(pixd); /* Now blit images to pixd */ x = y = spacing; irow = 0; for (i = 0; i < n; i++) { pix = pixaGetPix(pixan, i, L_CLONE); pixGetDimensions(pix, &w, &h, NULL); if (i && ((i % ncols) == 0)) { /* start new row */ x = spacing; y += spacing + rowht[irow]; irow++; } pixRasterop(pixd, x, y, w, h, PIX_SRC, pix, 0, 0); x += tilewidth + spacing; pixDestroy(&pix); } pixaDestroy(&pixan); FREE(rowht); return pixd; }
int main(int argc, char **argv) { char bufname[256]; l_int32 i, w, h; l_float32 *mat1, *mat2, *mat3, *mat1i, *mat2i, *mat3i, *matdinv; l_float32 matd[9], matdi[9]; BOXA *boxa, *boxa2; PIX *pix, *pixs, *pixb, *pixg, *pixc, *pixcs; PIX *pixd, *pix1, *pix2, *pix3; PIXA *pixa; PTA *ptas, *ptad; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pix = pixRead("feyn.tif"); pixs = pixScale(pix, 0.22, 0.22); pixDestroy(&pix); #if ALL /* Test invertability of sequential. */ fprintf(stderr, "Test invertability of sequential\n"); pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pix1 = pixAffineSequential(pixb, ptad, ptas, 0, 0); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 0,3,6 */ pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixAffineSequential(pix1, ptas, ptad, 0, 0); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 1,4,7 */ pixaAddPix(pixa, pix2, L_INSERT); pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixs); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 2,5,8 */ pixaAddPix(pixa, pixd, L_INSERT); pixDestroy(&pixb); ptaDestroy(&ptas); ptaDestroy(&ptad); } pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScaleToGray(pix1, 0.2); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 9 */ pixDisplayWithTitle(pix2, 0, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of sampling */ fprintf(stderr, "Test invertability of sampling\n"); pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pix1 = pixAffineSampledPta(pixb, ptad, ptas, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 10,13,16 */ pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixAffineSampledPta(pix1, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 11,14,17 */ pixaAddPix(pixa, pix2, L_INSERT); pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixs); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 12,15,18 */ pixaAddPix(pixa, pixd, L_INSERT); pixDestroy(&pixb); ptaDestroy(&ptas); ptaDestroy(&ptad); } pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScaleToGray(pix1, 0.2); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 19 */ pixDisplayWithTitle(pix2, 200, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixs); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of interpolation on grayscale */ fprintf(stderr, "Test invertability of grayscale interpolation\n"); pix = pixRead("feyn.tif"); pixg = pixScaleToGray3(pix); pixDestroy(&pix); pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixg, ADDED_BORDER_PIXELS / 3, 255); MakePtas(i, &ptas, &ptad); pix1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG); /* 20,23,26 */ pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixAffinePta(pix1, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 21,24,27 */ pixaAddPix(pixa, pix2, L_INSERT); pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS / 3); pixXor(pixd, pixd, pixg); pixInvert(pixd, pixd); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 22,25,28 */ pixaAddPix(pixa, pixd, L_INSERT); pixDestroy(&pixb); ptaDestroy(&ptas); ptaDestroy(&ptad); } pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScale(pix1, 0.2, 0.2); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 29 */ pixDisplayWithTitle(pix2, 400, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixg); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of interpolation on color */ fprintf(stderr, "Test invertability of color interpolation\n"); pixa = pixaCreate(0); pixc = pixRead("test24.jpg"); pixcs = pixScale(pixc, 0.3, 0.3); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixcs, ADDED_BORDER_PIXELS / 4, 0xffffff00); MakePtas(i, &ptas, &ptad); pix1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG); /* 30,33,36 */ pixaAddPix(pixa, pix1, L_INSERT); pix2 = pixAffinePta(pix1, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 31,34,37 */ pixaAddPix(pixa, pix2, L_INSERT); pixd = pixRemoveBorder(pix2, ADDED_BORDER_PIXELS / 4); pixXor(pixd, pixd, pixcs); pixInvert(pixd, pixd); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 32,35,38 */ pixaAddPix(pixa, pixd, L_INSERT); pixDestroy(&pixb); ptaDestroy(&ptas); ptaDestroy(&ptad); } pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScale(pix1, 0.25, 0.25); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 39 */ pixDisplayWithTitle(pix2, 600, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixc); pixaDestroy(&pixa); #endif #if ALL /* Comparison between sequential and sampling */ fprintf(stderr, "Compare sequential with sampling\n"); pix = pixRead("feyn.tif"); pixs = pixScale(pix, 0.22, 0.22); pixDestroy(&pix); MakePtas(3, &ptas, &ptad); pixa = pixaCreate(0); /* Use sequential transforms */ pix1 = pixAffineSequential(pixs, ptas, ptad, ADDED_BORDER_PIXELS, ADDED_BORDER_PIXELS); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 40 */ pixaAddPix(pixa, pix1, L_INSERT); /* Use sampled transform */ pix2 = pixAffineSampledPta(pixs, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 41 */ pixaAddPix(pixa, pix2, L_COPY); /* Compare the results */ pixXor(pix2, pix2, pix1); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 42 */ pixaAddPix(pixa, pix2, L_INSERT); pix1 = pixaDisplayTiledInColumns(pixa, 3, 1.0, 20, 3); pix2 = pixScale(pix1, 0.5, 0.5); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 43 */ pixDisplayWithTitle(pix2, 800, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixs); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif #if ALL /* Test with large distortion */ fprintf(stderr, "Test with large distortion\n"); MakePtas(4, &ptas, &ptad); pixa = pixaCreate(0); pix = pixRead("feyn.tif"); pixg = pixScaleToGray6(pix); pixDestroy(&pix); pix1 = pixAffineSequential(pixg, ptas, ptad, 0, 0); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 44 */ pixaAddPix(pixa, pix1, L_COPY); pix2 = pixAffineSampledPta(pixg, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 45 */ pixaAddPix(pixa, pix2, L_COPY); pix3 = pixAffinePta(pixg, ptas, ptad, L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 46 */ pixaAddPix(pixa, pix3, L_INSERT); pixXor(pix1, pix1, pix2); pixInvert(pix1, pix1); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 47 */ pixaAddPix(pixa, pix1, L_INSERT); pixXor(pix2, pix2, pix3); pixInvert(pix2, pix2); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 48 */ pixaAddPix(pixa, pix2, L_INSERT); pix1 = pixaDisplayTiledInColumns(pixa, 5, 1.0, 20, 3); pix2 = pixScale(pix1, 0.8, 0.8); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 49 */ pixDisplayWithTitle(pix2, 1000, 100, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixg); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif #if ALL /* Set up pix and boxa */ fprintf(stderr, "Test affine transforms and inverses on pix and boxa\n"); pixa = pixaCreate(0); pix = pixRead("lucasta.1.300.tif"); pixTranslate(pix, pix, 70, 0, L_BRING_IN_WHITE); pix1 = pixCloseBrick(NULL, pix, 14, 5); pixOpenBrick(pix1, pix1, 1, 2); boxa = pixConnComp(pix1, NULL, 8); pixs = pixConvertTo32(pix); pixGetDimensions(pixs, &w, &h, NULL); pixc = pixCopy(NULL, pixs); RenderHashedBoxa(pixc, boxa, 113); regTestWritePixAndCheck(rp, pixc, IFF_PNG); /* 50 */ pixaAddPix(pixa, pixc, L_INSERT); pixDestroy(&pix); pixDestroy(&pix1); /* Set up an affine transform in matd, and apply it to boxa */ mat1 = createMatrix2dTranslate(SHIFTX, SHIFTY); mat2 = createMatrix2dScale(SCALEX, SCALEY); mat3 = createMatrix2dRotate(w / 2, h / 2, ROTATION); l_productMat3(mat3, mat2, mat1, matd, 3); boxa2 = boxaAffineTransform(boxa, matd); /* Set up the inverse transform --> matdi */ mat1i = createMatrix2dTranslate(-SHIFTX, -SHIFTY); mat2i = createMatrix2dScale(1.0/ SCALEX, 1.0 / SCALEY); mat3i = createMatrix2dRotate(w / 2, h / 2, -ROTATION); l_productMat3(mat1i, mat2i, mat3i, matdi, 3); /* Invert the original affine transform --> matdinv */ affineInvertXform(matd, &matdinv); if (rp->display) { fprintf(stderr, " Affine transform, applied to boxa\n"); for (i = 0; i < 9; i++) { if (i && (i % 3 == 0)) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matd[i]); } fprintf(stderr, "\n Inverse transform, by composing inverse parts"); for (i = 0; i < 9; i++) { if (i % 3 == 0) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matdi[i]); } fprintf(stderr, "\n Inverse transform, by inverting affine xform"); for (i = 0; i < 6; i++) { if (i % 3 == 0) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matdinv[i]); } fprintf(stderr, "\n"); } /* Apply the inverted affine transform --> pixs */ pixd = pixAffine(pixs, matdinv, L_BRING_IN_WHITE); RenderHashedBoxa(pixd, boxa2, 513); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 51 */ pixaAddPix(pixa, pixd, L_INSERT); pix1 = pixaDisplayTiledInColumns(pixa, 2, 1.0, 30, 2); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 52 */ pixDisplayWithTitle(pix1, 1200, 100, NULL, rp->display); pixDestroy(&pix1); pixaDestroy(&pixa); pixDestroy(&pixs); boxaDestroy(&boxa); boxaDestroy(&boxa2); lept_free(mat1); lept_free(mat2); lept_free(mat3); lept_free(mat1i); lept_free(mat2i); lept_free(mat3i); lept_free(matdinv); #endif return regTestCleanup(rp); }
main(int argc, char **argv) { l_float32 scalefact; L_BMF *bmf, *bmftop; L_KERNEL *kel, *kelx, *kely; PIX *pixs, *pixg, *pixt, *pixd; PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8; PIXA *pixa; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; /* ----------------- Test on 8 bpp grayscale ---------------------*/ pixa = pixaCreate(5); bmf = bmfCreate("./fonts", 6); bmftop = bmfCreate("./fonts", 10); pixs = pixRead("lucasta-47.jpg"); pixg = pixScale(pixs, 0.4, 0.4); /* 8 bpp grayscale */ pix1 = pixConvertTo32(pixg); /* 32 bpp rgb */ AddTextAndSave(pixa, pix1, 1, bmf, textstr[0], L_ADD_BELOW, 0xff000000); pix2 = pixConvertGrayToSubpixelRGB(pixs, 0.4, 0.4, L_SUBPIXEL_ORDER_RGB); AddTextAndSave(pixa, pix2, 0, bmf, textstr[1], L_ADD_BELOW, 0x00ff0000); pix3 = pixConvertGrayToSubpixelRGB(pixs, 0.4, 0.4, L_SUBPIXEL_ORDER_BGR); AddTextAndSave(pixa, pix3, 0, bmf, textstr[2], L_ADD_BELOW, 0x0000ff00); pix4 = pixConvertGrayToSubpixelRGB(pixs, 0.4, 0.4, L_SUBPIXEL_ORDER_VRGB); AddTextAndSave(pixa, pix4, 0, bmf, textstr[3], L_ADD_BELOW, 0x00ff0000); pix5 = pixConvertGrayToSubpixelRGB(pixs, 0.4, 0.4, L_SUBPIXEL_ORDER_VBGR); AddTextAndSave(pixa, pix5, 0, bmf, textstr[4], L_ADD_BELOW, 0x0000ff00); pixt = pixaDisplay(pixa, 0, 0); pixd = pixAddSingleTextblock(pixt, bmftop, "Regression test for subpixel scaling: gray", 0xff00ff00, L_ADD_ABOVE, NULL); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 0 */ pixDisplayWithTitle(pixd, 50, 50, NULL, rp->display); pixaDestroy(&pixa); pixDestroy(&pixs); pixDestroy(&pixg); pixDestroy(&pixt); pixDestroy(&pixd); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); /* ----------------- Test on 32 bpp rgb ---------------------*/ pixa = pixaCreate(5); pixs = pixRead("fish24.jpg"); pix1 = pixScale(pixs, 0.4, 0.4); AddTextAndSave(pixa, pix1, 1, bmf, textstr[0], L_ADD_BELOW, 0xff000000); pix2 = pixConvertToSubpixelRGB(pixs, 0.4, 0.4, L_SUBPIXEL_ORDER_RGB); AddTextAndSave(pixa, pix2, 0, bmf, textstr[1], L_ADD_BELOW, 0x00ff0000); pix3 = pixConvertToSubpixelRGB(pixs, 0.4, 0.35, L_SUBPIXEL_ORDER_BGR); AddTextAndSave(pixa, pix3, 0, bmf, textstr[2], L_ADD_BELOW, 0x0000ff00); pix4 = pixConvertToSubpixelRGB(pixs, 0.4, 0.45, L_SUBPIXEL_ORDER_VRGB); AddTextAndSave(pixa, pix4, 0, bmf, textstr[3], L_ADD_BELOW, 0x00ff0000); pix5 = pixConvertToSubpixelRGB(pixs, 0.4, 0.4, L_SUBPIXEL_ORDER_VBGR); AddTextAndSave(pixa, pix5, 0, bmf, textstr[4], L_ADD_BELOW, 0x0000ff00); pixt = pixaDisplay(pixa, 0, 0); pixd = pixAddSingleTextblock(pixt, bmftop, "Regression test for subpixel scaling: color", 0xff00ff00, L_ADD_ABOVE, NULL); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 1 */ pixDisplayWithTitle(pixd, 50, 350, NULL, rp->display); pixaDestroy(&pixa); pixDestroy(&pixs); pixDestroy(&pixt); pixDestroy(&pixd); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); bmfDestroy(&bmf); bmfDestroy(&bmftop); /* --------------- Test on images that are initially 1 bpp ------------*/ /* For these, it is better to apply a lowpass filter before scaling */ /* Normal scaling of 8 bpp grayscale */ scalefact = 800. / 2320.; pixs = pixRead("patent.png"); /* sharp, 300 ppi, 1 bpp image */ pix1 = pixConvertTo8(pixs, FALSE); /* use 8 bpp input */ pix2 = pixScale(pix1, scalefact, scalefact); regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 2 */ /* Subpixel scaling; bad because there is very little aliasing. */ pix3 = pixConvertToSubpixelRGB(pix1, scalefact, scalefact, L_SUBPIXEL_ORDER_RGB); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 3 */ /* Get same (bad) result doing subpixel rendering on RGB input */ pix4 = pixConvertTo32(pixs); pix5 = pixConvertToSubpixelRGB(pix4, scalefact, scalefact, L_SUBPIXEL_ORDER_RGB); regTestComparePix(rp, pix3, pix5); /* 4 */ regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 5 */ /* Now apply a small lowpass filter before scaling. */ makeGaussianKernelSep(2, 2, 1.0, 1.0, &kelx, &kely); startTimer(); pix6 = pixConvolveSep(pix1, kelx, kely, 8, 1); /* normalized */ fprintf(stderr, "Time sep: %7.3f\n", stopTimer()); regTestWritePixAndCheck(rp, pix6, IFF_PNG); /* 6 */ /* Get same lowpass result with non-separated convolution */ kel = makeGaussianKernel(2, 2, 1.0, 1.0); startTimer(); pix7 = pixConvolve(pix1, kel, 8, 1); /* normalized */ fprintf(stderr, "Time non-sep: %7.3f\n", stopTimer()); regTestComparePix(rp, pix6, pix7); /* 7 */ /* Now do the subpixel scaling on this slightly blurred image */ pix8 = pixConvertToSubpixelRGB(pix6, scalefact, scalefact, L_SUBPIXEL_ORDER_RGB); regTestWritePixAndCheck(rp, pix8, IFF_PNG); /* 8 */ kernelDestroy(&kelx); kernelDestroy(&kely); kernelDestroy(&kel); pixDestroy(&pixs); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pix6); pixDestroy(&pix7); pixDestroy(&pix8); return regTestCleanup(rp); }
/* * pixWriteSegmentedPageToPS() * * Input: pixs (all depths; colormap ok) * pixm (<optional> 1 bpp segmentation mask over image region) * textscale (scale of text output relative to pixs) * imagescale (scale of image output relative to pixs) * threshold (threshold for binarization; typ. 190) * pageno (page number in set; use 1 for new output file) * fileout (output ps file) * Return: 0 if OK, 1 on error * * Notes: * (1) This generates the PS string for a mixed text/image page, * and adds it to an existing file if @pageno > 1. * The PS output is determined by fitting the result to * a letter-size (8.5 x 11 inch) page. * (2) The two images (pixs and pixm) are at the same resolution * (typically 300 ppi). They are used to generate two compressed * images, pixb and pixc, that are put directly into the output * PS file. * (3) pixb is the text component. In the PostScript world, we think of * it as a mask through which we paint black. It is produced by * scaling pixs by @textscale, and thresholding to 1 bpp. * (4) pixc is the image component, which is that part of pixs under * the mask pixm. It is scaled from pixs by @imagescale. * (5) Typical values are textscale = 2.0 and imagescale = 0.5. * (6) If pixm == NULL, the page has only text. If it is all black, * the page is all image and has no text. * (7) This can be used to write a multi-page PS file, by using * sequential page numbers with the same output file. It can * also be used to write separate PS files for each page, * by using different output files with @pageno = 0 or 1. */ l_int32 pixWriteSegmentedPageToPS(PIX *pixs, PIX *pixm, l_float32 textscale, l_float32 imagescale, l_int32 threshold, l_int32 pageno, const char *fileout) { l_int32 alltext, notext, d, ret; l_uint32 val; l_float32 scaleratio; PIX *pixmi, *pixmis, *pixt, *pixg, *pixsc, *pixb, *pixc; PROCNAME("pixWriteSegmentedPageToPS"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!fileout) return ERROR_INT("fileout not defined", procName, 1); if (imagescale <= 0.0 || textscale <= 0.0) return ERROR_INT("relative scales must be > 0.0", procName, 1); /* Analyze the page. Determine the ratio by which the * binary text mask is scaled relative to the image part. * If there is no image region (alltext == TRUE), the * text mask will be rendered directly to fit the page, * and scaleratio = 1.0. */ alltext = TRUE; notext = FALSE; scaleratio = 1.0; if (pixm) { pixZero(pixm, &alltext); /* pixm empty: all text */ if (alltext) pixm = NULL; /* treat it as not existing here */ else { pixmi = pixInvert(NULL, pixm); pixZero(pixmi, ¬ext); /* pixm full; no text */ pixDestroy(&pixmi); scaleratio = textscale / imagescale; } } if (pixGetDepth(pixs) == 1) { /* render tiff g4 */ pixb = pixClone(pixs); pixc = NULL; } else { pixt = pixConvertTo8Or32(pixs, 0, 0); /* this can be a clone of pixs */ /* Get the binary text mask. Note that pixg cannot be a * clone of pixs, because it may be altered by pixSetMasked(). */ pixb = NULL; if (notext == FALSE) { d = pixGetDepth(pixt); if (d == 8) pixg = pixCopy(NULL, pixt); else /* d == 32 */ pixg = pixConvertRGBToLuminance(pixt); if (pixm) /* clear out the image parts */ pixSetMasked(pixg, pixm, 255); if (textscale == 1.0) pixsc = pixClone(pixg); else if (textscale >= 0.7) pixsc = pixScaleGrayLI(pixg, textscale, textscale); else pixsc = pixScaleAreaMap(pixg, textscale, textscale); pixb = pixThresholdToBinary(pixsc, threshold); pixDestroy(&pixg); pixDestroy(&pixsc); } /* Get the scaled image region */ pixc = NULL; if (pixm) { if (imagescale == 1.0) pixsc = pixClone(pixt); /* can possibly be a clone of pixs */ else pixsc = pixScale(pixt, imagescale, imagescale); /* If pixm is not full, clear the pixels in pixsc * corresponding to bg in pixm, where there can be text * that is written through the mask pixb. Note that * we could skip this and use pixsc directly in * pixWriteMixedToPS(); however, clearing these * non-image regions to a white background will reduce * the size of pixc (relative to pixsc), and hence * reduce the size of the PS file that is generated. * Use a copy so that we don't accidentally alter pixs. */ if (notext == FALSE) { pixmis = pixScale(pixm, imagescale, imagescale); pixmi = pixInvert(NULL, pixmis); val = (d == 8) ? 0xff : 0xffffff00; pixc = pixCopy(NULL, pixsc); pixSetMasked(pixc, pixmi, val); /* clear non-image part */ pixDestroy(&pixmis); pixDestroy(&pixmi); } else pixc = pixClone(pixsc); pixDestroy(&pixsc); } pixDestroy(&pixt); } ret = pixWriteMixedToPS(pixb, pixc, scaleratio, pageno, fileout); pixDestroy(&pixb); pixDestroy(&pixc); return ret; }
int main(int argc, char **argv) { l_int32 i, w, h; PIX *pix0, *pix1, *pix2, *pix3, *pix4, *pix5; PIXA *pixa; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; pixa = pixaCreate(0); /* Blending on a light image */ pix1 = pixRead("fish24.jpg"); pixGetDimensions(pix1, &w, &h, NULL); for (i = 0; i < 3; i++) { pix2 = pixRead(blenders[i]); if (i == 2) { pix3 = pixScale(pix2, 0.5, 0.5); pixDestroy(&pix2); pix2 = pix3; } pix3 = pixAddAlphaToBlend(pix2, 0.3, 0); pix4 = pixMirroredTiling(pix3, w, h); pix5 = pixBlendWithGrayMask(pix1, pix4, NULL, 0, 0); pixaAddPix(pixa, pix5, L_INSERT); regTestWritePixAndCheck(rp, pix5, IFF_JFIF_JPEG); /* 0 - 2 */ pixDisplayWithTitle(pix5, 200 * i, 0, NULL, rp->display); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); } pixDestroy(&pix1); /* Blending on a dark image */ pix0 = pixRead("karen8.jpg"); pix1 = pixScale(pix0, 2.0, 2.0); pixGetDimensions(pix1, &w, &h, NULL); for (i = 0; i < 2; i++) { pix2 = pixRead(blenders[i]); pix3 = pixAddAlphaToBlend(pix2, 0.3, 1); pix4 = pixMirroredTiling(pix3, w, h); pix5 = pixBlendWithGrayMask(pix1, pix4, NULL, 0, 0); pixaAddPix(pixa, pix5, L_INSERT); regTestWritePixAndCheck(rp, pix5, IFF_JFIF_JPEG); /* 3 - 4 */ pixDisplayWithTitle(pix5, 600 + 200 * i, 0, NULL, rp->display); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); } pixaConvertToPdf(pixa, 100, 1.0, L_JPEG_ENCODE, 0, "Blendings: blend4_reg", "/tmp/blend.pdf"); L_INFO("Output pdf: /tmp/blend.pdf\n", rp->testname); pixDestroy(&pix0); pixDestroy(&pix1); pixaDestroy(&pixa); return regTestCleanup(rp); }
l_int32 main(int argc, char **argv) { char buf[256], dirname[256]; char *dirin, *pattern, *subdirout, *fname, *tail, *basename; l_int32 thresh, i, n; l_float32 scalefactor; PIX *pix1, *pix2, *pix3, *pix4; SARRAY *sa; static char mainName[] = "binarizefiles.c"; if (argc != 6) { fprintf(stderr, "Syntax: binarizefiles dirin pattern thresh scalefact dirout\n" " dirin: input directory for image files\n" " pattern: use 'allfiles' to convert all files\n" " in the directory\n" " thresh: 0 for adaptive; > 0 for global thresh (e.g., 128)\n" " scalefactor: in (0.0 ... 4.0]; use 1.0 to prevent scaling\n" " subdirout: subdirectory of /tmp for output files\n"); return 1; } dirin = argv[1]; pattern = argv[2]; thresh = atoi(argv[3]); scalefactor = atof(argv[4]); subdirout = argv[5]; if (!strcmp(pattern, "allfiles")) pattern = NULL; if (scalefactor <= 0.0 || scalefactor > 4.0) { L_WARNING("invalid scalefactor: setting to 1.0\n", mainName); scalefactor = 1.0; } /* Get the input filenames */ sa = getSortedPathnamesInDirectory(dirin, pattern, 0, 0); sarrayWriteStream(stderr, sa); n = sarrayGetCount(sa); /* Write the output files */ makeTempDirname(dirname, 256, subdirout); fprintf(stderr, "dirname: %s\n", dirname); lept_mkdir(subdirout); for (i = 0; i < n; i++) { fname = sarrayGetString(sa, i, L_NOCOPY); if ((pix1 = pixRead(fname)) == NULL) { L_ERROR("file %s not read as image", mainName, fname); continue; } splitPathAtDirectory(fname, NULL, &tail); splitPathAtExtension(tail, &basename, NULL); snprintf(buf, sizeof(buf), "%s/%s.tif", dirname, basename); FREE(tail); FREE(basename); fprintf(stderr, "fileout: %s\n", buf); if (scalefactor != 1.0) pix2 = pixScale(pix1, scalefactor, scalefactor); else pix2 = pixClone(pix1); if (thresh == 0) { pix4 = pixConvertTo8(pix2, 0); pix3 = pixAdaptThresholdToBinary(pix4, NULL, 1.0); pixDestroy(&pix4); } else { pix3 = pixConvertTo1(pix2, thresh); } pixWrite(buf, pix3, IFF_TIFF_G4); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); } sarrayDestroy(&sa); return 0; }
/*! * \brief pixFindBaselines() * * \param[in] pixs 1 bpp, 300 ppi * \param[out] ppta [optional] pairs of pts corresponding to * approx. ends of each text line * \param[in] pixadb for debug output; use NULL to skip * \return na of baseline y values, or NULL on error * * <pre> * Notes: * (1) Input binary image must have text lines already aligned * horizontally. This can be done by either rotating the * image with pixDeskew(), or, if a projective transform * is required, by doing pixDeskewLocal() first. * (2) Input null for &pta if you don't want this returned. * The pta will come in pairs of points (left and right end * of each baseline). * (3) Caution: this will not work properly on text with multiple * columns, where the lines are not aligned between columns. * If there are multiple columns, they should be extracted * separately before finding the baselines. * (4) This function constructs different types of output * for baselines; namely, a set of raster line values and * a set of end points of each baseline. * (5) This function was designed to handle short and long text lines * without using dangerous thresholds on the peak heights. It does * this by combining the differential signal with a morphological * analysis of the locations of the text lines. One can also * combine this data to normalize the peak heights, by weighting * the differential signal in the region of each baseline * by the inverse of the width of the text line found there. * </pre> */ NUMA * pixFindBaselines(PIX *pixs, PTA **ppta, PIXA *pixadb) { l_int32 h, i, j, nbox, val1, val2, ndiff, bx, by, bw, bh; l_int32 imaxloc, peakthresh, zerothresh, inpeak; l_int32 mintosearch, max, maxloc, nloc, locval; l_int32 *array; l_float32 maxval; BOXA *boxa1, *boxa2, *boxa3; GPLOT *gplot; NUMA *nasum, *nadiff, *naloc, *naval; PIX *pix1, *pix2; PTA *pta; PROCNAME("pixFindBaselines"); if (ppta) *ppta = NULL; if (!pixs || pixGetDepth(pixs) != 1) return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); /* Close up the text characters, removing noise */ pix1 = pixMorphSequence(pixs, "c25.1 + e15.1", 0); /* Estimate the resolution */ if (pixadb) pixaAddPix(pixadb, pixScale(pix1, 0.25, 0.25), L_INSERT); /* Save the difference of adjacent row sums. * The high positive-going peaks are the baselines */ if ((nasum = pixCountPixelsByRow(pix1, NULL)) == NULL) { pixDestroy(&pix1); return (NUMA *)ERROR_PTR("nasum not made", procName, NULL); } h = pixGetHeight(pixs); nadiff = numaCreate(h); numaGetIValue(nasum, 0, &val2); for (i = 0; i < h - 1; i++) { val1 = val2; numaGetIValue(nasum, i + 1, &val2); numaAddNumber(nadiff, val1 - val2); } numaDestroy(&nasum); if (pixadb) { /* show the difference signal */ lept_mkdir("lept/baseline"); gplotSimple1(nadiff, GPLOT_PNG, "/tmp/lept/baseline/diff", "Diff Sig"); pix2 = pixRead("/tmp/lept/baseline/diff.png"); pixaAddPix(pixadb, pix2, L_INSERT); } /* Use the zeroes of the profile to locate each baseline. */ array = numaGetIArray(nadiff); ndiff = numaGetCount(nadiff); numaGetMax(nadiff, &maxval, &imaxloc); numaDestroy(&nadiff); /* Use this to begin locating a new peak: */ peakthresh = (l_int32)maxval / PEAK_THRESHOLD_RATIO; /* Use this to begin a region between peaks: */ zerothresh = (l_int32)maxval / ZERO_THRESHOLD_RATIO; naloc = numaCreate(0); naval = numaCreate(0); inpeak = FALSE; for (i = 0; i < ndiff; i++) { if (inpeak == FALSE) { if (array[i] > peakthresh) { /* transition to in-peak */ inpeak = TRUE; mintosearch = i + MIN_DIST_IN_PEAK; /* accept no zeros * between i and mintosearch */ max = array[i]; maxloc = i; } } else { /* inpeak == TRUE; look for max */ if (array[i] > max) { max = array[i]; maxloc = i; mintosearch = i + MIN_DIST_IN_PEAK; } else if (i > mintosearch && array[i] <= zerothresh) { /* leave */ inpeak = FALSE; numaAddNumber(naval, max); numaAddNumber(naloc, maxloc); } } } LEPT_FREE(array); /* If array[ndiff-1] is max, eg. no descenders, baseline at bottom */ if (inpeak) { numaAddNumber(naval, max); numaAddNumber(naloc, maxloc); } if (pixadb) { /* show the raster locations for the peaks */ gplot = gplotCreate("/tmp/lept/baseline/loc", GPLOT_PNG, "Peak locs", "rasterline", "height"); gplotAddPlot(gplot, naloc, naval, GPLOT_POINTS, "locs"); gplotMakeOutput(gplot); gplotDestroy(&gplot); pix2 = pixRead("/tmp/lept/baseline/loc.png"); pixaAddPix(pixadb, pix2, L_INSERT); } numaDestroy(&naval); /* Generate an approximate profile of text line width. * First, filter the boxes of text, where there may be * more than one box for a given textline. */ pix2 = pixMorphSequence(pix1, "r11 + c20.1 + o30.1 +c1.3", 0); if (pixadb) pixaAddPix(pixadb, pix2, L_COPY); boxa1 = pixConnComp(pix2, NULL, 4); pixDestroy(&pix1); pixDestroy(&pix2); if (boxaGetCount(boxa1) == 0) { numaDestroy(&naloc); boxaDestroy(&boxa1); L_INFO("no compnents after filtering\n", procName); return NULL; } boxa2 = boxaTransform(boxa1, 0, 0, 4., 4.); boxa3 = boxaSort(boxa2, L_SORT_BY_Y, L_SORT_INCREASING, NULL); boxaDestroy(&boxa1); boxaDestroy(&boxa2); /* Optionally, find the baseline segments */ pta = NULL; if (ppta) { pta = ptaCreate(0); *ppta = pta; } if (pta) { nloc = numaGetCount(naloc); nbox = boxaGetCount(boxa3); for (i = 0; i < nbox; i++) { boxaGetBoxGeometry(boxa3, i, &bx, &by, &bw, &bh); for (j = 0; j < nloc; j++) { numaGetIValue(naloc, j, &locval); if (L_ABS(locval - (by + bh)) > 25) continue; ptaAddPt(pta, bx, locval); ptaAddPt(pta, bx + bw, locval); break; } } } boxaDestroy(&boxa3); if (pixadb && pta) { /* display baselines */ l_int32 npts, x1, y1, x2, y2; pix1 = pixConvertTo32(pixs); npts = ptaGetCount(pta); for (i = 0; i < npts; i += 2) { ptaGetIPt(pta, i, &x1, &y1); ptaGetIPt(pta, i + 1, &x2, &y2); pixRenderLineArb(pix1, x1, y1, x2, y2, 2, 255, 0, 0); } pixWrite("/tmp/lept/baseline/baselines.png", pix1, IFF_PNG); pixaAddPix(pixadb, pixScale(pix1, 0.25, 0.25), L_INSERT); pixDestroy(&pix1); } return naloc; }
/*! * \brief pixDisplayWriteFormat() * * \param[in] pix 1, 2, 4, 8, 16, 32 bpp * \param[in] reduction -1 to erase and reset; 0 to disable; * otherwise this is a reduction factor * \param[in] format IFF_DEFAULT or IFF_PNG * \return 0 if OK; 1 on error * * <pre> * Notes: * (1) This writes files with pathnames "/tmp/lept/display/file.*" * if reduction \> 0. These can be collected into a pdf using * pixDisplayMultiple(); * (2) Before writing a set of files, call * pixDisplayWrite(NULL, -1); * This erases any previously written files in that directory. * (3) If reduction \> 1 and depth == 1, this does a scale-to-gray * reduction. * (4) This function uses a static internal variable to number * output files written by a single process. Behavior * with a shared library may be unpredictable. * (5) Output file format is as follows: * format == IFF_DEFAULT: * png if d \< 8 or d == 16 or if the output pix * has a colormap. Otherwise, output is jpg. * format == IFF_PNG: * png (lossless) on all images. * (6) For 16 bpp, the choice of full dynamic range with log scale * is the best for displaying these images. Alternative outputs are * pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE); * pix8 = pixConvert16To8(pixt, 0); // low order byte * pix8 = pixConvert16To8(pixt, 1); // high order byte * </pre> */ l_int32 pixDisplayWriteFormat(PIX *pixs, l_int32 reduction, l_int32 format) { char buf[L_BUF_SIZE]; char *fname; l_float32 scale; PIX *pix1, *pix2; static l_int32 index = 0; /* caution: not .so or thread safe */ PROCNAME("pixDisplayWriteFormat"); if (reduction == 0) return 0; if (reduction < 0) { /* initialize */ lept_rmdir("lept/display"); index = 0; return 0; } if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (format != IFF_DEFAULT && format != IFF_PNG) { L_INFO("invalid format; using default\n", procName); format = IFF_DEFAULT; } if (index == 0) lept_mkdir("lept/display"); index++; if (reduction == 1) { pix1 = pixClone(pixs); } else { scale = 1. / (l_float32)reduction; if (pixGetDepth(pixs) == 1) pix1 = pixScaleToGray(pixs, scale); else pix1 = pixScale(pixs, scale, scale); } if (pixGetDepth(pix1) == 16) { pix2 = pixMaxDynamicRange(pix1, L_LOG_SCALE); snprintf(buf, L_BUF_SIZE, "file.%03d.png", index); fname = genPathname("/tmp/lept/display", buf); pixWrite(fname, pix2, IFF_PNG); pixDestroy(&pix2); } else if (pixGetDepth(pix1) < 8 || pixGetColormap(pix1) || format == IFF_PNG) { snprintf(buf, L_BUF_SIZE, "file.%03d.png", index); fname = genPathname("/tmp/lept/display", buf); pixWrite(fname, pix1, IFF_PNG); } else { snprintf(buf, L_BUF_SIZE, "file.%03d.jpg", index); fname = genPathname("/tmp/lept/display", buf); pixWrite(fname, pix1, format); } LEPT_FREE(fname); pixDestroy(&pix1); return 0; }
main(int argc, char **argv) { char bufname[256]; l_int32 i, j, w, h, d, x, y, wpls; l_uint32 *datas, *lines; l_float32 *vc; l_float32 *mat1, *mat2, *mat3, *mat1i, *mat2i, *mat3i, *matdinv; l_float32 matd[9], matdi[9]; BOXA *boxa, *boxa2; PIX *pix, *pixs, *pixb, *pixg, *pixc, *pixcs; PIX *pixd, *pixt1, *pixt2, *pixt3; PIXA *pixa; PTA *ptas, *ptad; static char mainName[] = "affine_reg"; if (argc != 1) exit(ERROR_INT(" Syntax: affine_reg", mainName, 1)); if ((pixs = pixRead("feyn.tif")) == NULL) exit(ERROR_INT("pixs not made", mainName, 1)); #if ALL /* Test invertability of sequential. */ pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pixt1 = pixAffineSequential(pixb, ptad, ptas, 0, 0); pixSaveTiled(pixt1, pixa, 3, 1, 20, 8); pixt2 = pixAffineSequential(pixt1, ptas, ptad, 0, 0); pixSaveTiled(pixt2, pixa, 3, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixs); pixSaveTiled(pixd, pixa, 3, 0, 20, 0); sprintf(bufname, "/tmp/junkseq%d.png", i); pixWrite(bufname, pixd, IFF_PNG); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkaffine1.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 100); pixDestroy(&pixt1); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of sampling */ pixa = pixaCreate(0); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixs, ADDED_BORDER_PIXELS, 0); MakePtas(i, &ptas, &ptad); pixt1 = pixAffineSampledPta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 3, 1, 20, 8); pixt2 = pixAffineSampledPta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 3, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS); pixXor(pixd, pixd, pixs); pixSaveTiled(pixd, pixa, 3, 0, 20, 0); if (i == 0) pixWrite("/tmp/junksamp.png", pixt1, IFF_PNG); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkaffine2.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 300); pixDestroy(&pixt1); pixaDestroy(&pixa); #endif #if ALL /* Test invertability of interpolation on grayscale */ pixa = pixaCreate(0); pixg = pixScaleToGray3(pixs); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixg, ADDED_BORDER_PIXELS / 3, 255); MakePtas(i, &ptas, &ptad); pixt1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixt2 = pixAffinePta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 1, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS / 3); pixXor(pixd, pixd, pixg); pixSaveTiled(pixd, pixa, 1, 0, 20, 0); if (i == 0) pixWrite("/tmp/junkinterp.png", pixt1, IFF_PNG); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkaffine3.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 500); pixDestroy(&pixt1); pixaDestroy(&pixa); pixDestroy(&pixg); #endif #if ALL /* Test invertability of interpolation on color */ pixa = pixaCreate(0); pixc = pixRead("test24.jpg"); pixcs = pixScale(pixc, 0.3, 0.3); for (i = 0; i < 3; i++) { pixb = pixAddBorder(pixcs, ADDED_BORDER_PIXELS / 4, 0xffffff00); MakePtas(i, &ptas, &ptad); pixt1 = pixAffinePta(pixb, ptad, ptas, L_BRING_IN_WHITE); pixSaveTiled(pixt1, pixa, 1, 1, 20, 32); pixt2 = pixAffinePta(pixt1, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 1, 0, 20, 0); pixd = pixRemoveBorder(pixt2, ADDED_BORDER_PIXELS / 4); pixXor(pixd, pixd, pixcs); pixSaveTiled(pixd, pixa, 1, 0, 20, 0); pixDestroy(&pixb); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); ptaDestroy(&ptas); ptaDestroy(&ptad); } pixt1 = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkaffine4.png", pixt1, IFF_PNG); pixDisplay(pixt1, 100, 500); pixDestroy(&pixt1); pixaDestroy(&pixa); pixDestroy(&pixc); pixDestroy(&pixcs); #endif #if ALL /* Comparison between sequential and sampling */ MakePtas(3, &ptas, &ptad); pixa = pixaCreate(0); /* Use sequential transforms */ pixt1 = pixAffineSequential(pixs, ptas, ptad, ADDED_BORDER_PIXELS, ADDED_BORDER_PIXELS); pixSaveTiled(pixt1, pixa, 2, 0, 20, 8); /* Use sampled transform */ pixt2 = pixAffineSampledPta(pixs, ptas, ptad, L_BRING_IN_WHITE); pixSaveTiled(pixt2, pixa, 2, 0, 20, 8); /* Compare the results */ pixXor(pixt2, pixt2, pixt1); pixSaveTiled(pixt2, pixa, 2, 0, 20, 8); pixd = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkaffine5.png", pixd, IFF_PNG); pixDisplay(pixd, 100, 700); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixd); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif #if ALL /* Get timings and test with large distortion */ MakePtas(4, &ptas, &ptad); pixa = pixaCreate(0); pixg = pixScaleToGray3(pixs); startTimer(); pixt1 = pixAffineSequential(pixg, ptas, ptad, 0, 0); fprintf(stderr, " Time for pixAffineSequentialPta(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); startTimer(); pixt2 = pixAffineSampledPta(pixg, ptas, ptad, L_BRING_IN_WHITE); fprintf(stderr, " Time for pixAffineSampledPta(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); startTimer(); pixt3 = pixAffinePta(pixg, ptas, ptad, L_BRING_IN_WHITE); fprintf(stderr, " Time for pixAffinePta(): %6.2f sec\n", stopTimer()); pixSaveTiled(pixt3, pixa, 1, 0, 20, 8); pixXor(pixt1, pixt1, pixt2); pixSaveTiled(pixt1, pixa, 1, 1, 20, 8); pixXor(pixt2, pixt2, pixt3); pixSaveTiled(pixt2, pixa, 1, 0, 20, 8); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); pixd = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkaffine6.png", pixd, IFF_PNG); pixDisplay(pixd, 100, 900); pixDestroy(&pixd); pixDestroy(&pixg); pixaDestroy(&pixa); ptaDestroy(&ptas); ptaDestroy(&ptad); #endif pixDestroy(&pixs); #if 1 /* Set up pix and boxa */ pixa = pixaCreate(0); pix = pixRead("lucasta.1.300.tif"); pixTranslate(pix, pix, 70, 0, L_BRING_IN_WHITE); pixt1 = pixCloseBrick(NULL, pix, 14, 5); pixOpenBrick(pixt1, pixt1, 1, 2); boxa = pixConnComp(pixt1, NULL, 8); pixs = pixConvertTo32(pix); pixGetDimensions(pixs, &w, &h, NULL); pixc = pixCopy(NULL, pixs); RenderHashedBoxa(pixc, boxa, 113); pixSaveTiled(pixc, pixa, 2, 1, 30, 32); pixDestroy(&pix); pixDestroy(&pixc); pixDestroy(&pixt1); /* Set up an affine transform in matd, and apply it to boxa */ mat1 = createMatrix2dTranslate(SHIFTX, SHIFTY); mat2 = createMatrix2dScale(SCALEX, SCALEY); mat3 = createMatrix2dRotate(w / 2, h / 2, ROTATION); l_productMat3(mat3, mat2, mat1, matd, 3); boxa2 = boxaAffineTransform(boxa, matd); /* Set up the inverse transform in matdi */ mat1i = createMatrix2dTranslate(-SHIFTX, -SHIFTY); mat2i = createMatrix2dScale(1.0/ SCALEX, 1.0 / SCALEY); mat3i = createMatrix2dRotate(w / 2, h / 2, -ROTATION); l_productMat3(mat1i, mat2i, mat3i, matdi, 3); /* Invert the original affine transform in matdinv */ affineInvertXform(matd, &matdinv); fprintf(stderr, "Affine transform, applied to boxa\n"); for (i = 0; i < 9; i++) { if (i && (i % 3 == 0)) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matd[i]); } fprintf(stderr, "\nInverse transform, made by composing inverse parts"); for (i = 0; i < 9; i++) { if (i % 3 == 0) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matdi[i]); } fprintf(stderr, "\nInverse transform, made by inverting the affine xform"); for (i = 0; i < 6; i++) { if (i % 3 == 0) fprintf(stderr, "\n"); fprintf(stderr, " %7.3f ", matdinv[i]); } fprintf(stderr, "\n"); /* Apply the inverted affine transform pixs */ pixd = pixAffine(pixs, matdinv, L_BRING_IN_WHITE); RenderHashedBoxa(pixd, boxa2, 513); pixSaveTiled(pixd, pixa, 2, 0, 30, 32); pixDestroy(&pixd); pixd = pixaDisplay(pixa, 0, 0); pixWrite("/tmp/junkaffine7.png", pixd, IFF_PNG); pixDisplay(pixd, 100, 900); pixDestroy(&pixd); pixDestroy(&pixs); pixaDestroy(&pixa); boxaDestroy(&boxa); boxaDestroy(&boxa2); FREE(mat1); FREE(mat2); FREE(mat3); FREE(mat1i); FREE(mat2i); FREE(mat3i); #endif return 0; }
/*! * pixaDisplayTiledInRows() * * Input: pixa * outdepth (output depth: 1, 8 or 32 bpp) * maxwidth (of output image) * scalefactor (applied to every pix; use 1.0 for no scaling) * background (0 for white, 1 for black; this is the color * of the spacing between the images) * spacing (between images, and on outside) * border (width of black border added to each image; * use 0 for no border) * Return: pixd (of tiled images), or null on error * * Notes: * (1) This saves a pixa to a single image file of width not to * exceed maxwidth, with background color either white or black, * and with each row tiled such that the top of each pix is * aligned and separated by 'spacing' from the next one. * A black border can be added to each pix. * (2) All pix are converted to outdepth; existing colormaps are removed. * (3) This does a reasonably spacewise-efficient job of laying * out the individual pix images into a tiled composite. */ PIX * pixaDisplayTiledInRows(PIXA *pixa, l_int32 outdepth, l_int32 maxwidth, l_float32 scalefactor, l_int32 background, l_int32 spacing, l_int32 border) { l_int32 h; /* cumulative height over all the rows */ l_int32 w; /* cumulative height in the current row */ l_int32 bordval, wtry, wt, ht; l_int32 irow; /* index of current pix in current row */ l_int32 wmaxrow; /* width of the largest row */ l_int32 maxh; /* max height in row */ l_int32 i, j, index, n, x, y, nrows, ninrow; NUMA *nainrow; /* number of pix in the row */ NUMA *namaxh; /* height of max pix in the row */ PIX *pix, *pixn, *pixt, *pixd; PIXA *pixan; PROCNAME("pixaDisplayTiledInRows"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); if (outdepth != 1 && outdepth != 8 && outdepth != 32) return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL); if (border < 0) border = 0; if (scalefactor <= 0.0) scalefactor = 1.0; if ((n = pixaGetCount(pixa)) == 0) return (PIX *)ERROR_PTR("no components", procName, NULL); /* Normalize depths, scale, remove colormaps; optionally add border */ pixan = pixaCreate(n); bordval = (outdepth == 1) ? 1 : 0; for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) continue; if (outdepth == 1) pixn = pixConvertTo1(pix, 128); else if (outdepth == 8) pixn = pixConvertTo8(pix, FALSE); else /* outdepth == 32 */ pixn = pixConvertTo32(pix); pixDestroy(&pix); if (scalefactor != 1.0) pixt = pixScale(pixn, scalefactor, scalefactor); else pixt = pixClone(pixn); if (border) pixd = pixAddBorder(pixt, border, bordval); else pixd = pixClone(pixt); pixDestroy(&pixn); pixDestroy(&pixt); pixaAddPix(pixan, pixd, L_INSERT); } if (pixaGetCount(pixan) != n) { n = pixaGetCount(pixan); L_WARNING_INT("only got %d components", procName, n); if (n == 0) { pixaDestroy(&pixan); return (PIX *)ERROR_PTR("no components", procName, NULL); } } /* Compute parameters for layout */ nainrow = numaCreate(0); namaxh = numaCreate(0); wmaxrow = 0; w = h = spacing; maxh = 0; /* max height in row */ for (i = 0, irow = 0; i < n; i++, irow++) { pixaGetPixDimensions(pixan, i, &wt, &ht, NULL); wtry = w + wt + spacing; if (wtry > maxwidth) { /* end the current row and start next one */ numaAddNumber(nainrow, irow); numaAddNumber(namaxh, maxh); wmaxrow = L_MAX(wmaxrow, w); h += maxh + spacing; irow = 0; w = wt + 2 * spacing; maxh = ht; } else { w = wtry; maxh = L_MAX(maxh, ht); } } /* Enter the parameters for the last row */ numaAddNumber(nainrow, irow); numaAddNumber(namaxh, maxh); wmaxrow = L_MAX(wmaxrow, w); h += maxh + spacing; if ((pixd = pixCreate(wmaxrow, h, outdepth)) == NULL) { numaDestroy(&nainrow); numaDestroy(&namaxh); pixaDestroy(&pixan); return (PIX *)ERROR_PTR("pixd not made", procName, NULL); } /* Reset the background color if necessary */ if ((background == 1 && outdepth == 1) || (background == 0 && outdepth != 1)) pixSetAll(pixd); /* Blit the images to the dest */ nrows = numaGetCount(nainrow); y = spacing; for (i = 0, index = 0; i < nrows; i++) { /* over rows */ numaGetIValue(nainrow, i, &ninrow); numaGetIValue(namaxh, i, &maxh); x = spacing; for (j = 0; j < ninrow; j++, index++) { /* over pix in row */ pix = pixaGetPix(pixan, index, L_CLONE); pixGetDimensions(pix, &wt, &ht, NULL); pixRasterop(pixd, x, y, wt, ht, PIX_SRC, pix, 0, 0); pixDestroy(&pix); x += wt + spacing; } y += maxh + spacing; } numaDestroy(&nainrow); numaDestroy(&namaxh); pixaDestroy(&pixan); return pixd; }
/*! * pixDisplayWithTitle() * * Input: pix (1, 2, 4, 8, 16, 32 bpp) * x, y (location of display frame) * title (<optional> on frame; can be NULL); * dispflag (1 to write, else disabled) * Return: 0 if OK; 1 on error * * Notes: * (1) See notes for pixDisplay(). * (2) This displays the image if dispflag == 1. */ l_int32 pixDisplayWithTitle(PIX *pixs, l_int32 x, l_int32 y, const char *title, l_int32 dispflag) { char *tempname; char buffer[L_BUF_SIZE]; static l_int32 index = 0; /* caution: not .so or thread safe */ l_int32 w, h, d, ignore; l_float32 ratw, rath, ratmin; PIX *pixt; #ifndef _WIN32 l_int32 wt, ht; #else char *pathname; char fullpath[_MAX_PATH]; #endif /* _WIN32 */ PROCNAME("pixDisplayWithTitle"); if (dispflag != 1) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (var_DISPLAY_PROG != L_DISPLAY_WITH_XV && var_DISPLAY_PROG != L_DISPLAY_WITH_XLI && var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV && var_DISPLAY_PROG != L_DISPLAY_WITH_IV) return ERROR_INT("no program chosen for display", procName, 1); pixGetDimensions(pixs, &w, &h, &d); if (w <= MAX_DISPLAY_WIDTH && h <= MAX_DISPLAY_HEIGHT) { if (d == 16) /* take MSB */ pixt = pixConvert16To8(pixs, 1); else pixt = pixClone(pixs); } else { ratw = (l_float32)MAX_DISPLAY_WIDTH / (l_float32)w; rath = (l_float32)MAX_DISPLAY_HEIGHT / (l_float32)h; ratmin = L_MIN(ratw, rath); if (ratmin < 0.125 && d == 1) pixt = pixScaleToGray8(pixs); else if (ratmin < 0.25 && d == 1) pixt = pixScaleToGray4(pixs); else if (ratmin < 0.33 && d == 1) pixt = pixScaleToGray3(pixs); else if (ratmin < 0.5 && d == 1) pixt = pixScaleToGray2(pixs); else pixt = pixScale(pixs, ratmin, ratmin); if (!pixt) return ERROR_INT("pixt not made", procName, 1); } if (index == 0) { lept_rmdir("display"); lept_mkdir("display"); } index++; if (pixGetDepth(pixt) < 8 || (w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) { snprintf(buffer, L_BUF_SIZE, "/tmp/display/write.%03d.png", index); pixWrite(buffer, pixt, IFF_PNG); } else { snprintf(buffer, L_BUF_SIZE, "/tmp/display/write.%03d.jpg", index); pixWrite(buffer, pixt, IFF_JFIF_JPEG); } tempname = stringNew(buffer); #ifndef _WIN32 /* Unix */ if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) { if (title) snprintf(buffer, L_BUF_SIZE, "xv -quit -geometry +%d+%d -name \"%s\" %s &", x, y, title, tempname); else snprintf(buffer, L_BUF_SIZE, "xv -quit -geometry +%d+%d %s &", x, y, tempname); } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) { if (title) snprintf(buffer, L_BUF_SIZE, "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &", x, y, title, tempname); else snprintf(buffer, L_BUF_SIZE, "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &", x, y, tempname); } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) { /* no way to display title */ pixGetDimensions(pixt, &wt, &ht, NULL); snprintf(buffer, L_BUF_SIZE, "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10, x, y, tempname); } ignore = system(buffer); #else /* _WIN32 */ /* Windows: L_DISPLAY_WITH_IV */ pathname = genPathname(tempname, NULL); _fullpath(fullpath, pathname, sizeof(fullpath)); if (title) snprintf(buffer, L_BUF_SIZE, "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"", fullpath, x, y, title); else snprintf(buffer, L_BUF_SIZE, "i_view32.exe \"%s\" /pos=(%d,%d)", fullpath, x, y); ignore = system(buffer); FREE(pathname); #endif /* _WIN32 */ pixDestroy(&pixt); FREE(tempname); return 0; }
main(int argc, char **argv) { l_int32 i, j, x, y, rval, gval, bval; l_uint32 pixel; l_float32 frval, fgval, fbval; NUMA *nahue, *nasat, *napk; PIX *pixs, *pixhsv, *pixh, *pixg, *pixf, *pixd; PIX *pixr, *pixt1, *pixt2, *pixt3; PIXA *pixa, *pixapk; PTA *ptapk; L_REGPARAMS *rp; l_chooseDisplayProg(L_DISPLAY_WITH_XV); if (regTestSetup(argc, argv, &rp)) return 1; /* Make a graded frame color */ pixs = pixCreate(650, 900, 32); for (i = 0; i < 900; i++) { rval = 40 + i / 30; for (j = 0; j < 650; j++) { gval = 255 - j / 30; bval = 70 + j / 30; composeRGBPixel(rval, gval, bval, &pixel); pixSetPixel(pixs, j, i, pixel); } } /* Place an image inside the frame and convert to HSV */ pixt1 = pixRead("1555-3.jpg"); pixt2 = pixScale(pixt1, 0.5, 0.5); pixRasterop(pixs, 100, 100, 2000, 2000, PIX_SRC, pixt2, 0, 0); pixDestroy(&pixt1); pixDestroy(&pixt2); pixDisplayWithTitle(pixs, 400, 0, "Input image", rp->display); pixa = pixaCreate(0); pixhsv = pixConvertRGBToHSV(NULL, pixs); /* Work in the HS projection of HSV */ pixh = pixMakeHistoHS(pixhsv, 5, &nahue, &nasat); pixg = pixMaxDynamicRange(pixh, L_LOG_SCALE); pixf = pixConvertGrayToFalseColor(pixg, 1.0); regTestWritePixAndCheck(rp, pixf, IFF_PNG); /* 0 */ pixDisplayWithTitle(pixf, 100, 0, "False color HS histo", rp->display); pixaAddPix(pixa, pixs, L_COPY); pixaAddPix(pixa, pixhsv, L_INSERT); pixaAddPix(pixa, pixg, L_INSERT); pixaAddPix(pixa, pixf, L_INSERT); gplotSimple1(nahue, GPLOT_PNG, "/tmp/junkhue", "Histogram of hue values"); #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ pixt3 = pixRead("/tmp/junkhue.png"); regTestWritePixAndCheck(rp, pixt3, IFF_PNG); /* 1 */ pixDisplayWithTitle(pixt3, 100, 300, "Histo of hue", rp->display); pixaAddPix(pixa, pixt3, L_INSERT); gplotSimple1(nasat, GPLOT_PNG, "/tmp/junksat", "Histogram of saturation values"); #ifndef _WIN32 sleep(1); #else Sleep(1000); #endif /* _WIN32 */ pixt3 = pixRead("/tmp/junksat.png"); regTestWritePixAndCheck(rp, pixt3, IFF_PNG); /* 2 */ pixDisplayWithTitle(pixt3, 100, 800, "Histo of saturation", rp->display); pixaAddPix(pixa, pixt3, L_INSERT); pixd = pixaDisplayTiledAndScaled(pixa, 32, 270, 7, 0, 30, 3); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 3 */ pixDisplayWithTitle(pixd, 0, 400, "Hue and Saturation Mosaic", rp->display); pixDestroy(&pixd); pixaDestroy(&pixa); numaDestroy(&nahue); numaDestroy(&nasat); /* Find all the peaks */ pixFindHistoPeaksHSV(pixh, L_HS_HISTO, 20, 20, 6, 2.0, &ptapk, &napk, &pixapk); numaWriteStream(stderr, napk); ptaWriteStream(stderr, ptapk, 1); pixd = pixaDisplayTiledInRows(pixapk, 32, 1400, 1.0, 0, 30, 2); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 4 */ pixDisplayWithTitle(pixd, 0, 550, "Peaks in HS", rp->display); pixDestroy(&pixh); pixDestroy(&pixd); pixaDestroy(&pixapk); /* Make masks for each of the peaks */ pixa = pixaCreate(0); pixr = pixScaleBySampling(pixs, 0.4, 0.4); for (i = 0; i < 6; i++) { ptaGetIPt(ptapk, i, &x, &y); pixt1 = pixMakeRangeMaskHS(pixr, y, 20, x, 20, L_INCLUDE_REGION); pixaAddPix(pixa, pixt1, L_INSERT); pixGetAverageMaskedRGB(pixr, pixt1, 0, 0, 1, L_MEAN_ABSVAL, &frval, &fgval, &fbval); composeRGBPixel((l_int32)frval, (l_int32)fgval, (l_int32)fbval, &pixel); pixt2 = pixCreateTemplate(pixr); pixSetAll(pixt2); pixPaintThroughMask(pixt2, pixt1, 0, 0, pixel); pixaAddPix(pixa, pixt2, L_INSERT); pixt3 = pixCreateTemplate(pixr); pixSetAllArbitrary(pixt3, pixel); pixaAddPix(pixa, pixt3, L_INSERT); } pixd = pixaDisplayTiledAndScaled(pixa, 32, 225, 3, 0, 30, 3); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 5 */ pixDisplayWithTitle(pixd, 600, 0, "Masks over peaks", rp->display); pixDestroy(&pixs); pixDestroy(&pixr); pixDestroy(&pixd); pixaDestroy(&pixa); ptaDestroy(&ptapk); numaDestroy(&napk); regTestCleanup(rp); return 0; }
int main(int argc, char **argv) { l_uint8 *data; l_int32 w, h, n1, n2, n, i, minval, maxval; l_int32 ncolors, rval, gval, bval, equal; l_int32 *rmap, *gmap, *bmap; l_uint32 color; l_float32 gamma; BOX *box; FILE *fp; PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6; PIX *pixs, *pixb, *pixg, *pixc, *pixd; PIX *pixg2, *pixcs1, *pixcs2, *pixd1, *pixd2; PIXA *pixa, *pixa2, *pixa3; PIXCMAP *cmap, *cmap2; RGBA_QUAD *cta; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; /* ------------------------ (1) ----------------------------*/ /* Blend with a white background */ pix1 = pixRead("books_logo.png"); pixDisplayWithTitle(pix1, 100, 0, NULL, rp->display); pix2 = pixAlphaBlendUniform(pix1, 0xffffff00); pixDisplayWithTitle(pix2, 100, 150, NULL, rp->display); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 0 */ regTestWritePixAndCheck(rp, pix2, IFF_PNG); /* 1 */ /* Generate an alpha layer based on the white background */ pix3 = pixSetAlphaOverWhite(pix2); pixSetSpp(pix3, 3); pixWrite("/tmp/alphaops.2.png", pix3, IFF_PNG); /* without alpha */ regTestCheckFile(rp, "/tmp/alphaops.2.png"); /* 2 */ pixSetSpp(pix3, 4); regTestWritePixAndCheck(rp, pix3, IFF_PNG); /* 3, with alpha */ pixDisplayWithTitle(pix3, 100, 300, NULL, rp->display); /* Render on a light yellow background */ pix4 = pixAlphaBlendUniform(pix3, 0xffffe000); regTestWritePixAndCheck(rp, pix4, IFF_PNG); /* 4 */ pixDisplayWithTitle(pix4, 100, 450, NULL, rp->display); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); /* ------------------------ (2) ----------------------------*/ lept_rmdir("alpha"); lept_mkdir("alpha"); /* Make the transparency (alpha) layer. * pixs is the mask. We turn it into a transparency (alpha) * layer by converting to 8 bpp. A small convolution fuzzes * the mask edges so that you don't see the pixels. */ pixs = pixRead("feyn-fract.tif"); pixGetDimensions(pixs, &w, &h, NULL); pixg = pixConvert1To8(NULL, pixs, 0, 255); pixg2 = pixBlockconvGray(pixg, NULL, 1, 1); regTestWritePixAndCheck(rp, pixg2, IFF_JFIF_JPEG); /* 5 */ pixDisplayWithTitle(pixg2, 0, 0, "alpha", rp->display); /* Make the viewable image. * pixc is the image that we see where the alpha layer is * opaque -- i.e., greater than 0. Scale it to the same * size as the mask. To visualize what this will look like * when displayed over a black background, create the black * background image, pixb, and do the blending with pixcs1 * explicitly using the alpha layer pixg2. */ pixc = pixRead("tetons.jpg"); pixcs1 = pixScaleToSize(pixc, w, h); regTestWritePixAndCheck(rp, pixcs1, IFF_JFIF_JPEG); /* 6 */ pixDisplayWithTitle(pixcs1, 300, 0, "viewable", rp->display); pixb = pixCreateTemplate(pixcs1); /* black */ pixd1 = pixBlendWithGrayMask(pixb, pixcs1, pixg2, 0, 0); regTestWritePixAndCheck(rp, pixd1, IFF_JFIF_JPEG); /* 7 */ pixDisplayWithTitle(pixd1, 600, 0, "alpha-blended 1", rp->display); /* Embed the alpha layer pixg2 into the color image pixc. * Write it out as is. Then clean pixcs1 (to 0) under the fully * transparent part of the alpha layer, and write that result * out as well. */ pixSetRGBComponent(pixcs1, pixg2, L_ALPHA_CHANNEL); pixWrite("/tmp/alpha/pixcs1.png", pixcs1, IFF_PNG); pixcs2 = pixSetUnderTransparency(pixcs1, 0, 0); pixWrite("/tmp/alpha/pixcs2.png", pixcs2, IFF_PNG); /* What will this look like over a black background? * Do the blending explicitly and display. It should * look identical to the blended result pixd1 before cleaning. */ pixd2 = pixBlendWithGrayMask(pixb, pixcs2, pixg2, 0, 0); regTestWritePixAndCheck(rp, pixd2, IFF_JFIF_JPEG); /* 8 */ pixDisplayWithTitle(pixd2, 0, 400, "alpha blended 2", rp->display); /* Read the two images back, ignoring the transparency layer. * The uncleaned image will come back identical to pixcs1. * However, the cleaned image will be black wherever * the alpha layer was fully transparent. It will * look the same when viewed through the alpha layer, * but have much better compression. */ pix1 = pixRead("/tmp/alpha/pixcs1.png"); /* just pixcs1 */ pix2 = pixRead("/tmp/alpha/pixcs2.png"); /* cleaned under transparent */ n1 = nbytesInFile("/tmp/alpha/pixcs1.png"); n2 = nbytesInFile("/tmp/alpha/pixcs2.png"); fprintf(stderr, " Original: %d bytes\n Cleaned: %d bytes\n", n1, n2); regTestWritePixAndCheck(rp, pix1, IFF_JFIF_JPEG); /* 9 */ regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 10 */ pixDisplayWithTitle(pix1, 300, 400, "without alpha", rp->display); pixDisplayWithTitle(pix2, 600, 400, "cleaned under transparent", rp->display); pixa = pixaCreate(0); pixSaveTiled(pixg2, pixa, 1.0, 1, 20, 32); pixSaveTiled(pixcs1, pixa, 1.0, 1, 20, 0); pixSaveTiled(pix1, pixa, 1.0, 0, 20, 0); pixSaveTiled(pixd1, pixa, 1.0, 1, 20, 0); pixSaveTiled(pixd2, pixa, 1.0, 0, 20, 0); pixSaveTiled(pix2, pixa, 1.0, 1, 20, 0); pixd = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_JFIF_JPEG); /* 11 */ pixDisplayWithTitle(pixd, 200, 200, "composite", rp->display); pixWrite("/tmp/alpha/alpha.png", pixd, IFF_JFIF_JPEG); pixDestroy(&pixd); pixaDestroy(&pixa); pixDestroy(&pixs); pixDestroy(&pixb); pixDestroy(&pixg); pixDestroy(&pixg2); pixDestroy(&pixc); pixDestroy(&pixcs1); pixDestroy(&pixcs2); pixDestroy(&pixd); pixDestroy(&pixd1); pixDestroy(&pixd2); pixDestroy(&pix1); pixDestroy(&pix2); /* ------------------------ (3) ----------------------------*/ color = 0xffffa000; gamma = 1.0; minval = 0; maxval = 200; box = boxCreate(0, 85, 600, 100); pixa = pixaCreate(6); pix1 = pixRead("blend-green1.jpg"); pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixRead("blend-green2.png"); pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixRead("blend-green3.png"); pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixRead("blend-orange.jpg"); pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixRead("blend-yellow.jpg"); pixaAddPix(pixa, pix1, L_INSERT); pix1 = pixRead("blend-red.png"); pixaAddPix(pixa, pix1, L_INSERT); n = pixaGetCount(pixa); pixa2 = pixaCreate(n); pixa3 = pixaCreate(n); for (i = 0; i < n; i++) { pix1 = pixaGetPix(pixa, i, L_CLONE); pix2 = DoBlendTest(pix1, box, color, gamma, minval, maxval, 1); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 12, 14, ... 22 */ pixDisplayWithTitle(pix2, 150 * i, 0, NULL, rp->display); pixaAddPix(pixa2, pix2, L_INSERT); pix2 = DoBlendTest(pix1, box, color, gamma, minval, maxval, 2); regTestWritePixAndCheck(rp, pix2, IFF_JFIF_JPEG); /* 13, 15, ... 23 */ pixDisplayWithTitle(pix2, 150 * i, 200, NULL, rp->display); pixaAddPix(pixa3, pix2, L_INSERT); pixDestroy(&pix1); } if (rp->display) { pixaConvertToPdf(pixa2, 0, 0.75, L_FLATE_ENCODE, 0, "blend 1 test", "/tmp/alpha/blending1.pdf"); pixaConvertToPdf(pixa3, 0, 0.75, L_FLATE_ENCODE, 0, "blend 2 test", "/tmp/alpha/blending2.pdf"); } pixaDestroy(&pixa); pixaDestroy(&pixa2); pixaDestroy(&pixa3); boxDestroy(&box); /* ------------------------ (4) ----------------------------*/ /* Use one image as the alpha component for a second image */ pix1 = pixRead("test24.jpg"); pix2 = pixRead("marge.jpg"); pix3 = pixScale(pix2, 1.9, 2.2); pix4 = pixConvertTo8(pix3, 0); pixSetRGBComponent(pix1, pix4, L_ALPHA_CHANNEL); regTestWritePixAndCheck(rp, pix1, IFF_PNG); /* 24 */ pixDisplayWithTitle(pix1, 600, 0, NULL, rp->display); /* Set the alpha value in a colormap to bval */ pix5 = pixOctreeColorQuant(pix1, 128, 0); cmap = pixGetColormap(pix5); pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL); n = pixcmapGetCount(cmap); for (i = 0; i < n; i++) { pixcmapGetColor(cmap, i, &rval, &gval, &bval); cta = (RGBA_QUAD *)cmap->array; cta[i].alpha = bval; } /* Test binary serialization/deserialization of colormap with alpha */ pixcmapSerializeToMemory(cmap, 4, &ncolors, &data); cmap2 = pixcmapDeserializeFromMemory(data, 4, ncolors); CmapEqual(cmap, cmap2, &equal); regTestCompareValues(rp, TRUE, equal, 0.0); /* 25 */ pixcmapDestroy(&cmap2); lept_free(data); /* Test ascii serialization/deserialization of colormap with alpha */ fp = fopenWriteStream("/tmp/alpha/cmap.4", "w"); pixcmapWriteStream(fp, cmap); fclose(fp); fp = fopenReadStream("/tmp/alpha/cmap.4"); cmap2 = pixcmapReadStream(fp); fclose(fp); CmapEqual(cmap, cmap2, &equal); regTestCompareValues(rp, TRUE, equal, 0.0); /* 26 */ pixcmapDestroy(&cmap2); /* Test r/w for cmapped pix with non-opaque alpha */ pixDisplayWithTitle(pix5, 900, 0, NULL, rp->display); regTestWritePixAndCheck(rp, pix5, IFF_PNG); /* 27 */ pixWrite("/tmp/alpha/fourcomp.png", pix5, IFF_PNG); pix6 = pixRead("/tmp/alpha/fourcomp.png"); regTestComparePix(rp, pix5, pix6); /* 28 */ pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pix3); pixDestroy(&pix4); pixDestroy(&pix5); pixDestroy(&pix6); lept_free(rmap); lept_free(gmap); lept_free(bmap); return regTestCleanup(rp); }
/*! * pixSaveTiledOutline() * * Input: pixs (1, 2, 4, 8, 32 bpp) * pixa (the pix are accumulated here) * reduction (0 to disable; otherwise this is a reduction factor) * newrow (0 if placed on the same row as previous; 1 otherwise) * space (horizontal and vertical spacing, in pixels) * linewidth (width of added outline for image; 0 for no outline) * dp (depth of pixa; 8 or 32 bpp; only used on first call) * Return: 0 if OK, 1 on error. * * Notes: * (1) Before calling this function for the first time, use * pixaCreate() to make the @pixa that will accumulate the pix. * This is passed in each time pixSaveTiled() is called. * (2) @reduction is the integer reduction factor for the input * image. After reduction and possible depth conversion, * the image is saved in the input pixa, along with a box * that specifies the location to place it when tiled later. * Disable saving the pix by setting reduction == 0. * (3) @newrow and @space specify the location of the new pix * with respect to the last one(s) that were entered. * (4) @dp specifies the depth at which all pix are saved. It can * be only 8 or 32 bpp. Any colormap is removed. This is only * used at the first invocation. * (5) This function uses two variables from call to call. * If they were static, the function would not be .so or thread * safe, and furthermore, there would be interference with two or * more pixa accumulating images at a time. Consequently, * we use the first pix in the pixa to store and obtain both * the depth and the current position of the bottom (one pixel * below the lowest image raster line when laid out using * the boxa). The bottom variable is stored in the input format * field, which is the only field available for storing an int. */ l_int32 pixSaveTiledOutline(PIX *pixs, PIXA *pixa, l_int32 reduction, l_int32 newrow, l_int32 space, l_int32 linewidth, l_int32 dp) { l_int32 n, top, left, bx, by, bw, w, h, depth, bottom; l_float32 scale; BOX *box; PIX *pix, *pixt1, *pixt2, *pixt3; PROCNAME("pixSaveTiledOutline"); if (reduction == 0) return 0; if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (n == 0) { bottom = 0; if (dp != 8 && dp != 32) { L_WARNING("dp not 8 or 32 bpp; using 32", procName); depth = 32; } else depth = dp; } else { /* extract the depth and bottom params from the first pix */ pix = pixaGetPix(pixa, 0, L_CLONE); depth = pixGetDepth(pix); bottom = pixGetInputFormat(pix); /* not typical usage! */ pixDestroy(&pix); } /* Scale and convert to output depth */ if (reduction == 1) pixt1 = pixClone(pixs); else { scale = 1. / (l_float32)reduction; if (pixGetDepth(pixs) == 1) pixt1 = pixScaleToGray(pixs, scale); else pixt1 = pixScale(pixs, scale, scale); } if (depth == 8) pixt2 = pixConvertTo8(pixt1, 0); else pixt2 = pixConvertTo32(pixt1); pixDestroy(&pixt1); /* Add black outline */ if (linewidth > 0) pixt3 = pixAddBorder(pixt2, linewidth, 0); else pixt3 = pixClone(pixt2); pixDestroy(&pixt2); /* Find position of current pix (UL corner plus size) */ if (n == 0) { top = 0; left = 0; } else if (newrow == 1) { top = bottom + space; left = 0; } else if (n > 0) { pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL); top = by; left = bx + bw + space; } pixGetDimensions(pixt3, &w, &h, NULL); bottom = L_MAX(bottom, top + h); box = boxCreate(left, top, w, h); pixaAddPix(pixa, pixt3, L_INSERT); pixaAddBox(pixa, box, L_INSERT); /* Save the new bottom value */ pix = pixaGetPix(pixa, 0, L_CLONE); pixSetInputFormat(pix, bottom); /* not typical usage! */ pixDestroy(&pix); return 0; }