/*! * pixRotateAM() * * Input: pixs (2, 4, 8 bpp gray or colormapped, or 32 bpp RGB) * angle (radians; clockwise is positive) * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) * Return: pixd, or null on error * * Notes: * (1) Rotates about image center. * (2) A positive angle gives a clockwise rotation. * (3) Brings in either black or white pixels from the boundary. */ PIX * pixRotateAM(PIX *pixs, l_float32 angle, l_int32 incolor) { l_int32 d; l_uint32 fillval; PIX *pixt1, *pixt2, *pixd; PROCNAME("pixRotateAM"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) == 1) return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL); if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE) return pixClone(pixs); /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); d = pixGetDepth(pixt1); if (d < 8) pixt2 = pixConvertTo8(pixt1, FALSE); else pixt2 = pixClone(pixt1); d = pixGetDepth(pixt2); /* Compute actual incoming color */ fillval = 0; if (incolor == L_BRING_IN_WHITE) { if (d == 8) fillval = 255; else /* d == 32 */ fillval = 0xffffff00; } if (d == 8) pixd = pixRotateAMGray(pixt2, angle, fillval); else /* d == 32 */ pixd = pixRotateAMColor(pixt2, angle, fillval); pixDestroy(&pixt1); pixDestroy(&pixt2); return pixd; }
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; }
/*! * 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; }