main(int argc, char **argv) { l_int32 i; l_float32 pi, scale, angle; PIX *pixc, *pixm, *pix1, *pix2, *pix3; PIXA *pixa; PTA *pta1, *pta2, *pta3, *pta4; static char mainName[] = "smallpix_reg"; /* Make a small test image, the hard way! */ pi = 3.1415926535; pixc = pixCreate(9, 9, 32); pixm = pixCreate(9, 9, 1); pta1 = generatePtaLineFromPt(4, 4, 3.1, 0.0); pta2 = generatePtaLineFromPt(4, 4, 3.1, 0.5 * pi); pta3 = generatePtaLineFromPt(4, 4, 3.1, pi); pta4 = generatePtaLineFromPt(4, 4, 3.1, 1.5 * pi); ptaJoin(pta1, pta2, 0, 0); ptaJoin(pta1, pta3, 0, 0); ptaJoin(pta1, pta4, 0, 0); pixRenderPta(pixm, pta1, L_SET_PIXELS); pixPaintThroughMask(pixc, pixm, 0, 0, 0x00ff0000); ptaDestroy(&pta1); ptaDestroy(&pta2); ptaDestroy(&pta3); ptaDestroy(&pta4); pixDestroy(&pixm); /* Results differ for scaleSmoothLow() w/ and w/out + 0.5. * Neither is properly symmetric (with symm pattern on odd-sized * pix, because the smoothing is destroying the symmetry. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleSmooth(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 100, NULL); /* Results same for pixScaleAreaMap w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleAreaMap(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 200, NULL); /* Results better for pixScaleBySampling with + 0.5, for small, * odd-dimension pix. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 2); for (i = 0; i < 11; i++) { scale = 0.30 + 0.035 * (l_float32)i; pix2 = pixScaleBySampling(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 6); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 300, NULL); /* Results same for pixRotateAM w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAM(pix1, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 400, NULL); /* If the size is odd, we express the center exactly, and the * results are better for pixRotateBySampling() w/out 0.5 * However, if the size is even, the center value is not * exact, and if we choose it 0.5 smaller than the actual * center, we get symmetrical results with +0.5. * So we choose not to include + 0.5. */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateBySampling(pix1, 4, 4, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 500, NULL); /* Results same for pixRotateAMCorner w/ and w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAMCorner(pix1, angle, L_BRING_IN_BLACK); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 600, NULL); /* Results better for pixRotateAMColorFast without + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { angle = 0.10 + 0.05 * (l_float32)i; pix2 = pixRotateAMColorFast(pix1, angle, 0); pix3 = pixExpandReplicate(pix2, 8); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 700, NULL); /* Results slightly better for pixScaleColorLI() w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { scale = 1.0 + 0.2 * (l_float32)i; pix2 = pixScaleColorLI(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 4); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 800, NULL); /* Results slightly better for pixScaleColorLI() w/out + 0.5 */ pixa = pixaCreate(11); pix1 = pixExpandReplicate(pixc, 1); for (i = 0; i < 11; i++) { scale = 1.0 + 0.2 * (l_float32)i; pix2 = pixScaleLI(pix1, scale, scale); pix3 = pixExpandReplicate(pix2, 4); pixSaveTiled(pix3, pixa, 1, (i == 0), 20, 32); pixDestroy(&pix2); pixDestroy(&pix3); } pixDestroy(&pix1); DisplayPix(&pixa, 100, 940, NULL); pixDestroy(&pixc); return 0; }
int main(int argc, char **argv) { l_int32 w, h, wd, hd; l_float32 deg2rad, angle, conf; PIX *pixs, *pixb1, *pixb2, *pixr, *pixf, *pixd, *pixc; PIXA *pixa; L_REGPARAMS *rp; if (regTestSetup(argc, argv, &rp)) return 1; deg2rad = 3.1415926535 / 180.; pixa = pixaCreate(0); pixs = pixRead("feyn.tif"); pixSetOrClearBorder(pixs, 100, 250, 100, 0, PIX_CLR); pixb1 = pixReduceRankBinaryCascade(pixs, 2, 2, 0, 0); regTestWritePixAndCheck(rp, pixb1, IFF_PNG); /* 0 */ pixDisplayWithTitle(pixb1, 0, 100, NULL, rp->display); /* Add a border and locate and deskew a 40 degree rotation */ pixb2 = pixAddBorder(pixb1, BORDER, 0); pixGetDimensions(pixb2, &w, &h, NULL); pixSaveTiled(pixb2, pixa, 0.5, 1, 20, 8); pixr = pixRotateBySampling(pixb2, w / 2, h / 2, deg2rad * 40., L_BRING_IN_WHITE); regTestWritePixAndCheck(rp, pixr, IFF_PNG); /* 1 */ pixSaveTiled(pixr, pixa, 0.5, 0, 20, 0); pixFindSkewSweepAndSearchScorePivot(pixr, &angle, &conf, NULL, 1, 1, 0.0, 45.0, 2.0, 0.03, L_SHEAR_ABOUT_CENTER); fprintf(stderr, "Should be 40 degrees: angle = %7.3f, conf = %7.3f\n", angle, conf); pixf = pixRotateBySampling(pixr, w / 2, h / 2, deg2rad * angle, L_BRING_IN_WHITE); pixd = pixRemoveBorder(pixf, BORDER); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 2 */ pixSaveTiled(pixd, pixa, 0.5, 0, 20, 0); pixDestroy(&pixr); pixDestroy(&pixf); pixDestroy(&pixd); /* Do a rotation larger than 90 degrees using embedding; * Use 2 sets of measurements at 90 degrees to scan the * full range of possible rotation angles. */ pixGetDimensions(pixb1, &w, &h, NULL); pixr = pixRotate(pixb1, deg2rad * 37., L_ROTATE_SAMPLING, L_BRING_IN_WHITE, w, h); regTestWritePixAndCheck(rp, pixr, IFF_PNG); /* 3 */ pixSaveTiled(pixr, pixa, 0.5, 1, 20, 0); startTimer(); pixFindSkewOrthogonalRange(pixr, &angle, &conf, 2, 1, 47.0, 1.0, 0.03, 0.0); fprintf(stderr, "Orth search time: %7.3f sec\n", stopTimer()); fprintf(stderr, "Should be about -128 degrees: angle = %7.3f\n", angle); pixd = pixRotate(pixr, deg2rad * angle, L_ROTATE_SAMPLING, L_BRING_IN_WHITE, w, h); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 4 */ pixGetDimensions(pixd, &wd, &hd, NULL); pixc = pixCreate(w, h, 1); pixRasterop(pixc, 0, 0, w, h, PIX_SRC, pixd, (wd - w) / 2, (hd - h) / 2); regTestWritePixAndCheck(rp, pixc, IFF_PNG); /* 5 */ pixSaveTiled(pixc, pixa, 0.5, 0, 20, 0); pixDestroy(&pixr); pixDestroy(&pixf); pixDestroy(&pixd); pixDestroy(&pixc); pixd = pixaDisplay(pixa, 0, 0); regTestWritePixAndCheck(rp, pixd, IFF_PNG); /* 6 */ pixDisplayWithTitle(pixd, 100, 100, NULL, rp->display); pixDestroy(&pixd); pixDestroy(&pixs); pixDestroy(&pixb1); pixDestroy(&pixb2); pixaDestroy(&pixa); return regTestCleanup(rp); }
/*! * pixRotate() * * Input: pixs (1, 2, 4, 8, 32 bpp rgb) * angle (radians; clockwise is positive) * type (L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING) * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) * width (original width; use 0 to avoid embedding) * height (original height; use 0 to avoid embedding) * Return: pixd, or null on error * * Notes: * (1) This is a high-level, simple interface for rotating images * about their center. * (2) For very small rotations, just return a clone. * (3) Rotation brings either white or black pixels in * from outside the image. * (4) The rotation type is adjusted if necessary for the image * depth and size of rotation angle. For 1 bpp images, we * rotate either by shear or sampling. * (5) Colormaps are removed for rotation by area mapping. * (6) The dest can be expanded so that no image pixels * are lost. To invoke expansion, input the original * width and height. For repeated rotation, use of the * original width and height allows the expansion to * stop at the maximum required size, which is a square * with side = sqrt(w*w + h*h). * * *** Warning: implicit assumption about RGB component ordering *** */ PIX * pixRotate(PIX *pixs, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height) { l_int32 w, h, d; l_uint32 fillval; PIX *pixt1, *pixt2, *pixt3, *pixd; PIXCMAP *cmap; PROCNAME("pixRotate"); if (!pixs) return (PIX *) ERROR_PTR("pixs not defined", procName, NULL); if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP && type != L_ROTATE_SAMPLING) return (PIX *) ERROR_PTR("invalid type", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *) ERROR_PTR("invalid incolor", procName, NULL); if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE) return pixClone(pixs); /* Adjust rotation type if necessary: * - If d == 1 bpp and the angle is more than about 6 degrees, * rotate by sampling; otherwise rotate by shear. * - If d > 1, only allow shear rotation up to about 20 degrees; * beyond that, default a shear request to sampling. */ if (pixGetDepth(pixs) == 1) { if (L_ABS(angle) > MAX_1BPP_SHEAR_ANGLE) { if (type != L_ROTATE_SAMPLING) L_INFO("1 bpp, large angle; rotate by sampling\n", procName); type = L_ROTATE_SAMPLING; } else if (type != L_ROTATE_SHEAR) { L_INFO("1 bpp; rotate by shear\n", procName); type = L_ROTATE_SHEAR; } } else if (L_ABS(angle) > LIMIT_SHEAR_ANGLE && type == L_ROTATE_SHEAR) { L_INFO("large angle; rotate by sampling\n", procName); type = L_ROTATE_SAMPLING; } /* Remove colormap if we rotate by area mapping. */ cmap = pixGetColormap(pixs); if (cmap && type == L_ROTATE_AREA_MAP) pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); else pixt1 = pixClone(pixs); cmap = pixGetColormap(pixt1); /* Otherwise, if there is a colormap and we're not embedding, * add white color if it doesn't exist. */ if (cmap && width == 0) { /* no embedding; generate @incolor */ if (incolor == L_BRING_IN_BLACK) pixcmapAddBlackOrWhite(cmap, 0, NULL); else /* L_BRING_IN_WHITE */ pixcmapAddBlackOrWhite(cmap, 1, NULL); } /* Request to embed in a larger image; do if necessary */ pixt2 = pixEmbedForRotation(pixt1, angle, incolor, width, height); /* Area mapping requires 8 or 32 bpp. If less than 8 bpp and * area map rotation is requested, convert to 8 bpp. */ d = pixGetDepth(pixt2); if (type == L_ROTATE_AREA_MAP && d < 8) pixt3 = pixConvertTo8(pixt2, FALSE); else pixt3 = pixClone(pixt2); /* Do the rotation: shear, sampling or area mapping */ pixGetDimensions(pixt3, &w, &h, &d); if (type == L_ROTATE_SHEAR) { pixd = pixRotateShearCenter(pixt3, angle, incolor); } else if (type == L_ROTATE_SAMPLING) { pixd = pixRotateBySampling(pixt3, w / 2, h / 2, angle, incolor); } else { /* rotate by area mapping */ fillval = 0; if (incolor == L_BRING_IN_WHITE) { if (d == 8) fillval = 255; else /* d == 32 */ fillval = 0xffffff00; } if (d == 8) pixd = pixRotateAMGray(pixt3, angle, fillval); else /* d == 32 */ pixd = pixRotateAMColor(pixt3, angle, fillval); } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); return pixd; }