/*! * \brief pixRotateShear() * * \param[in] pixs * \param[in] xcen x value for which there is no horizontal shear * \param[in] ycen y value for which there is no vertical shear * \param[in] angle radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd, or NULL on error. * * <pre> * Notes: * (1) This rotates an image about the given point, using * either 2 or 3 shears. * (2) A positive angle gives a clockwise rotation. * (3) This brings in 'incolor' pixels from outside the image. * (4) For rotation angles larger than about 0.35 radians, we issue * a warning because you should probably be using another method * (either sampling or area mapping) * </pre> */ PIX * pixRotateShear(PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor) { PROCNAME("pixRotateShear"); if (!pixs) return (PIX *)(PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL); if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE) return pixClone(pixs); if (L_ABS(angle) <= MAX_2_SHEAR_ANGLE) return pixRotate2Shear(pixs, xcen, ycen, angle, incolor); if (L_ABS(angle) > LIMIT_SHEAR_ANGLE) L_WARNING("%6.2f radians; large angle for shear rotation\n", procName, L_ABS(angle)); return pixRotate3Shear(pixs, xcen, ycen, angle, incolor); }
/*! * \brief pixRotate3Shear() * * \param[in] pixs * \param[in] xcen, ycen center of rotation * \param[in] angle radians * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK; * \return pixd, or NULL on error. * * <pre> * Notes: * (1) This rotates the image about the given point, using the 3-shear * method. It should only be used for angles smaller than * LIMIT_SHEAR_ANGLE. For larger angles, a warning is issued. * (2) A positive angle gives a clockwise rotation. * (3) 3-shear rotation by a specified angle is equivalent * to the sequential transformations * y' = y + tan(angle/2) * (x - xcen) for first y-shear * x' = x + sin(angle) * (y - ycen) for x-shear * y' = y + tan(angle/2) * (x - xcen) for second y-shear * (4) Computation of tan(angle) is performed in the shear operations. * (5) This brings in 'incolor' pixels from outside the image. * (6) If the image has an alpha layer, it is rotated separately by * two shears. * (7) The algorithm was published by Alan Paeth: "A Fast Algorithm * for General Raster Rotation," Graphics Interface '86, * pp. 77-81, May 1986. A description of the method, along with * an implementation, can be found in Graphics Gems, p. 179, * edited by Andrew Glassner, published by Academic Press, 1990. * </pre> */ PIX * pixRotate3Shear(PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor) { l_float32 hangle; PIX *pix1, *pix2, *pixd; PROCNAME("pixRotate3Shear"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL); if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE) return pixClone(pixs); if (L_ABS(angle) > LIMIT_SHEAR_ANGLE) { L_WARNING("%6.2f radians; large angle for 3-shear rotation\n", procName, L_ABS(angle)); } hangle = atan(sin(angle)); if ((pixd = pixVShear(NULL, pixs, xcen, angle / 2., incolor)) == NULL) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); if ((pix1 = pixHShear(NULL, pixd, ycen, hangle, incolor)) == NULL) { pixDestroy(&pixd); return (PIX *)ERROR_PTR("pix1 not made", procName, NULL); } pixVShear(pixd, pix1, xcen, angle / 2., incolor); pixDestroy(&pix1); if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) { pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); /* L_BRING_IN_WHITE brings in opaque for the alpha component */ pix2 = pixRotate3Shear(pix1, xcen, ycen, angle, L_BRING_IN_WHITE); pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); pixDestroy(&pix1); pixDestroy(&pix2); } return pixd; }
/*! * pixRotateShear() * * Input: pixs * xcen (x value for which there is no horizontal shear) * ycen (y value for which there is no vertical shear) * angle (radians) * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK); * Return: pixd, or null on error. * * Notes: * (1) This rotates an image about the given point, using * either 2 or 3 shears. * (2) A positive angle gives a clockwise rotation. * (3) This brings in 'incolor' pixels from outside the image. */ PIX * pixRotateShear(PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor) { PROCNAME("pixRotateShear"); if (!pixs) return (PIX *)(PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *)(PIX *)ERROR_PTR("invalid incolor value", procName, NULL); if (L_ABS(angle) < VERY_SMALL_ANGLE) return pixClone(pixs); if (L_ABS(angle) <= MAX_2_SHEAR_ANGLE) return pixRotate2Shear(pixs, xcen, ycen, angle, incolor); else return pixRotate3Shear(pixs, xcen, ycen, angle, incolor); }
int main(int argc, char **argv) { l_int32 i, w, h, d, rotflag; PIX *pixs, *pixt, *pixd; l_float32 angle, deg2rad, pops, ang; char *filein, *fileout; static char mainName[] = "rotatetest1"; if (argc != 4) return ERROR_INT(" Syntax: rotatetest1 filein angle fileout", mainName, 1); filein = argv[1]; angle = atof(argv[2]); fileout = argv[3]; deg2rad = 3.1415926535 / 180.; if ((pixs = pixRead(filein)) == NULL) return ERROR_INT("pix not made", mainName, 1); if (pixGetDepth(pixs) == 1) { pixt = pixScaleToGray3(pixs); pixDestroy(&pixs); pixs = pixAddBorderGeneral(pixt, 1, 0, 1, 0, 255); pixDestroy(&pixt); } pixGetDimensions(pixs, &w, &h, &d); fprintf(stderr, "w = %d, h = %d\n", w, h); #if 0 /* repertory of rotation operations to choose from */ pixd = pixRotateAM(pixs, deg2rad * angle, L_BRING_IN_WHITE); pixd = pixRotateAMColor(pixs, deg2rad * angle, 0xffffff00); pixd = pixRotateAMColorFast(pixs, deg2rad * angle, 255); pixd = pixRotateAMCorner(pixs, deg2rad * angle, L_BRING_IN_WHITE); pixd = pixRotateShear(pixs, w /2, h / 2, deg2rad * angle, L_BRING_IN_WHITE); pixd = pixRotate3Shear(pixs, w /2, h / 2, deg2rad * angle, L_BRING_IN_WHITE); pixRotateShearIP(pixs, w / 2, h / 2, deg2rad * angle); pixd = pixs; #endif #if 0 /* timing of shear rotation */ for (i = 0; i < NITERS; i++) { pixd = pixRotateShear(pixs, (i * w) / NITERS, (i * h) / NITERS, deg2rad * angle, L_BRING_IN_WHITE); pixDisplay(pixd, 100 + 20 * i, 100 + 20 * i); pixDestroy(&pixd); } #endif #if 0 /* timing of in-place shear rotation */ for (i = 0; i < NITERS; i++) { pixRotateShearIP(pixs, w/2, h/2, deg2rad * angle, L_BRING_IN_WHITE); /* pixRotateShearCenterIP(pixs, deg2rad * angle, L_BRING_IN_WHITE); */ pixDisplay(pixs, 100 + 20 * i, 100 + 20 * i); } pixd = pixs; if (pixGetDepth(pixd) == 1) pixWrite(fileout, pixd, IFF_PNG); else pixWrite(fileout, pixd, IFF_JFIF_JPEG); pixDestroy(&pixs); #endif #if 0 /* timing of various rotation operations (choose) */ startTimer(); w = pixGetWidth(pixs); h = pixGetHeight(pixs); for (i = 0; i < NTIMES; i++) { pixd = pixRotateShearCenter(pixs, deg2rad * angle, L_BRING_IN_WHITE); pixDestroy(&pixd); } pops = (l_float32)(w * h * NTIMES / 1000000.) / stopTimer(); fprintf(stderr, "vers. 1, mpops: %f\n", pops); startTimer(); w = pixGetWidth(pixs); h = pixGetHeight(pixs); for (i = 0; i < NTIMES; i++) { pixRotateShearIP(pixs, w/2, h/2, deg2rad * angle, L_BRING_IN_WHITE); } pops = (l_float32)(w * h * NTIMES / 1000000.) / stopTimer(); fprintf(stderr, "shear, mpops: %f\n", pops); pixWrite(fileout, pixs, IFF_PNG); for (i = 0; i < NTIMES; i++) { pixRotateShearIP(pixs, w/2, h/2, -deg2rad * angle, L_BRING_IN_WHITE); } pixWrite("/usr/tmp/junkout", pixs, IFF_PNG); #endif #if 0 /* area-mapping rotation operations */ pixd = pixRotateAM(pixs, deg2rad * angle, L_BRING_IN_WHITE); /* pixd = pixRotateAMColorFast(pixs, deg2rad * angle, 255); */ if (pixGetDepth(pixd) == 1) pixWrite(fileout, pixd, IFF_PNG); else pixWrite(fileout, pixd, IFF_JFIF_JPEG); #endif #if 0 /* compare the standard area-map color rotation with * the fast area-map color rotation, on a pixel basis */ { PIX *pix1, *pix2; NUMA *nar, *nag, *nab, *naseq; GPLOT *gplot; startTimer(); pix1 = pixRotateAMColor(pixs, 0.12, 0xffffff00); fprintf(stderr, " standard color rotate: %7.2f sec\n", stopTimer()); pixWrite("junkcolor1", pix1, IFF_JFIF_JPEG); startTimer(); pix2 = pixRotateAMColorFast(pixs, 0.12, 0xffffff00); fprintf(stderr, " fast color rotate: %7.2f sec\n", stopTimer()); pixWrite("junkcolor2", pix2, IFF_JFIF_JPEG); pixd = pixAbsDifference(pix1, pix2); pixGetColorHistogram(pixd, 1, &nar, &nag, &nab); naseq = numaMakeSequence(0., 1., 256); gplot = gplotCreate("junk_absdiff", GPLOT_X11, "Number vs diff", "diff", "number"); gplotAddPlot(gplot, naseq, nar, GPLOT_POINTS, "red"); gplotAddPlot(gplot, naseq, nag, GPLOT_POINTS, "green"); gplotAddPlot(gplot, naseq, nab, GPLOT_POINTS, "blue"); gplotMakeOutput(gplot); pixDestroy(&pix1); pixDestroy(&pix2); pixDestroy(&pixd); numaDestroy(&nar); numaDestroy(&nag); numaDestroy(&nab); numaDestroy(&naseq); gplotDestroy(&gplot); } #endif /* Do a succession of 180 7-degree rotations in a cw * direction, and unwind the result with another set in * a ccw direction. Although there is a considerable amount * of distortion after successive rotations, after all * 360 rotations, the resulting image is restored to * its original pristine condition! */ #if 1 rotflag = L_ROTATE_AREA_MAP; /* rotflag = L_ROTATE_SHEAR; */ /* rotflag = L_ROTATE_SAMPLING; */ ang = 7.0 * deg2rad; pixGetDimensions(pixs, &w, &h, NULL); pixd = pixRotate(pixs, ang, rotflag, L_BRING_IN_WHITE, w, h); pixWrite("junkrot7", pixd, IFF_PNG); for (i = 1; i < 180; i++) { pixs = pixd; pixd = pixRotate(pixs, ang, rotflag, L_BRING_IN_WHITE, w, h); if ((i % 30) == 0) pixDisplay(pixd, 600, 0); pixDestroy(&pixs); } pixWrite("junkspin", pixd, IFF_PNG); pixDisplay(pixd, 0, 0); for (i = 0; i < 180; i++) { pixs = pixd; pixd = pixRotate(pixs, -ang, rotflag, L_BRING_IN_WHITE, w, h); if (i && (i % 30) == 0) pixDisplay(pixd, 600, 500); pixDestroy(&pixs); } pixWrite("junkunspin", pixd, IFF_PNG); pixDisplay(pixd, 0, 500); pixDestroy(&pixd); #endif return 0; }