Leptonica  1.77.0
Image processing and image analysis suite
rotate.c
Go to the documentation of this file.
1 /*====================================================================*
2  - Copyright (C) 2001 Leptonica. All rights reserved.
3  -
4  - Redistribution and use in source and binary forms, with or without
5  - modification, are permitted provided that the following conditions
6  - are met:
7  - 1. Redistributions of source code must retain the above copyright
8  - notice, this list of conditions and the following disclaimer.
9  - 2. Redistributions in binary form must reproduce the above
10  - copyright notice, this list of conditions and the following
11  - disclaimer in the documentation and/or other materials
12  - provided with the distribution.
13  -
14  - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18  - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
27 
56 #include <math.h>
57 #include "allheaders.h"
58 
59 extern l_float32 AlphaMaskBorderVals[2];
60 static const l_float32 MIN_ANGLE_TO_ROTATE = 0.001; /* radians; ~0.06 deg */
61 static const l_float32 MAX_1BPP_SHEAR_ANGLE = 0.06; /* radians; ~3 deg */
62 static const l_float32 LIMIT_SHEAR_ANGLE = 0.35; /* radians; ~20 deg */
63 
64 
65 /*------------------------------------------------------------------*
66  * General rotation about the center *
67  *------------------------------------------------------------------*/
98 PIX *
99 pixRotate(PIX *pixs,
100  l_float32 angle,
101  l_int32 type,
102  l_int32 incolor,
103  l_int32 width,
104  l_int32 height)
105 {
106 l_int32 w, h, d;
107 l_uint32 fillval;
108 PIX *pix1, *pix2, *pix3, *pixd;
109 PIXCMAP *cmap;
110 
111  PROCNAME("pixRotate");
112 
113  if (!pixs)
114  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
115  if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP &&
116  type != L_ROTATE_SAMPLING)
117  return (PIX *)ERROR_PTR("invalid type", procName, NULL);
118  if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
119  return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
120 
121  if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE)
122  return pixClone(pixs);
123 
124  /* Adjust rotation type if necessary:
125  * - If d == 1 bpp and the angle is more than about 6 degrees,
126  * rotate by sampling; otherwise rotate by shear.
127  * - If d > 1, only allow shear rotation up to about 20 degrees;
128  * beyond that, default a shear request to sampling. */
129  if (pixGetDepth(pixs) == 1) {
130  if (L_ABS(angle) > MAX_1BPP_SHEAR_ANGLE) {
131  if (type != L_ROTATE_SAMPLING)
132  L_INFO("1 bpp, large angle; rotate by sampling\n", procName);
133  type = L_ROTATE_SAMPLING;
134  } else if (type != L_ROTATE_SHEAR) {
135  L_INFO("1 bpp; rotate by shear\n", procName);
136  type = L_ROTATE_SHEAR;
137  }
138  } else if (L_ABS(angle) > LIMIT_SHEAR_ANGLE && type == L_ROTATE_SHEAR) {
139  L_INFO("large angle; rotate by sampling\n", procName);
140  type = L_ROTATE_SAMPLING;
141  }
142 
143  /* Remove colormap if we rotate by area mapping. */
144  cmap = pixGetColormap(pixs);
145  if (cmap && type == L_ROTATE_AREA_MAP)
147  else
148  pix1 = pixClone(pixs);
149  cmap = pixGetColormap(pix1);
150 
151  /* Otherwise, if there is a colormap and we're not embedding,
152  * add white color if it doesn't exist. */
153  if (cmap && width == 0) { /* no embedding; generate %incolor */
154  if (incolor == L_BRING_IN_BLACK)
155  pixcmapAddBlackOrWhite(cmap, 0, NULL);
156  else /* L_BRING_IN_WHITE */
157  pixcmapAddBlackOrWhite(cmap, 1, NULL);
158  }
159 
160  /* Request to embed in a larger image; do if necessary */
161  pix2 = pixEmbedForRotation(pix1, angle, incolor, width, height);
162 
163  /* Area mapping requires 8 or 32 bpp. If less than 8 bpp and
164  * area map rotation is requested, convert to 8 bpp. */
165  d = pixGetDepth(pix2);
166  if (type == L_ROTATE_AREA_MAP && d < 8)
167  pix3 = pixConvertTo8(pix2, FALSE);
168  else
169  pix3 = pixClone(pix2);
170 
171  /* Do the rotation: shear, sampling or area mapping */
172  pixGetDimensions(pix3, &w, &h, &d);
173  if (type == L_ROTATE_SHEAR) {
174  pixd = pixRotateShearCenter(pix3, angle, incolor);
175  } else if (type == L_ROTATE_SAMPLING) {
176  pixd = pixRotateBySampling(pix3, w / 2, h / 2, angle, incolor);
177  } else { /* rotate by area mapping */
178  fillval = 0;
179  if (incolor == L_BRING_IN_WHITE) {
180  if (d == 8)
181  fillval = 255;
182  else /* d == 32 */
183  fillval = 0xffffff00;
184  }
185  if (d == 8)
186  pixd = pixRotateAMGray(pix3, angle, fillval);
187  else /* d == 32 */
188  pixd = pixRotateAMColor(pix3, angle, fillval);
189  }
190 
191  pixDestroy(&pix1);
192  pixDestroy(&pix2);
193  pixDestroy(&pix3);
194  return pixd;
195 }
196 
197 
240 PIX *
242  l_float32 angle,
243  l_int32 incolor,
244  l_int32 width,
245  l_int32 height)
246 {
247 l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
248 l_float64 sina, cosa, fw, fh;
249 PIX *pixd;
250 
251  PROCNAME("pixEmbedForRotation");
252 
253  if (!pixs)
254  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
255  if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
256  return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
257  if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE)
258  return pixClone(pixs);
259 
260  /* Test if big enough to hold any rotation of the original image */
261  pixGetDimensions(pixs, &w, &h, &d);
262  maxside = (l_int32)(sqrt((l_float64)(width * width) +
263  (l_float64)(height * height)) + 0.5);
264  if (w >= maxside && h >= maxside) /* big enough */
265  return pixClone(pixs);
266 
267  /* Find the new sizes required to hold the image after rotation.
268  * Note that the new dimensions must be at least as large as those
269  * of pixs, because we're rasterop-ing into it before rotation. */
270  cosa = cos(angle);
271  sina = sin(angle);
272  fw = (l_float64)w;
273  fh = (l_float64)h;
274  w1 = (l_int32)(L_ABS(fw * cosa - fh * sina) + 0.5);
275  w2 = (l_int32)(L_ABS(-fw * cosa - fh * sina) + 0.5);
276  h1 = (l_int32)(L_ABS(fw * sina + fh * cosa) + 0.5);
277  h2 = (l_int32)(L_ABS(-fw * sina + fh * cosa) + 0.5);
278  wnew = L_MAX(w, L_MAX(w1, w2));
279  hnew = L_MAX(h, L_MAX(h1, h2));
280 
281  if ((pixd = pixCreate(wnew, hnew, d)) == NULL)
282  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
283  pixCopyResolution(pixd, pixs);
284  pixCopyColormap(pixd, pixs);
285  pixCopySpp(pixd, pixs);
286  pixCopyText(pixd, pixs);
287  xoff = (wnew - w) / 2;
288  yoff = (hnew - h) / 2;
289 
290  /* Set background to color to be rotated in */
291  setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE;
292  pixSetBlackOrWhite(pixd, setcolor);
293 
294  /* Rasterop automatically handles all 4 channels for rgba */
295  pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
296  return pixd;
297 }
298 
299 
300 /*------------------------------------------------------------------*
301  * General rotation by sampling *
302  *------------------------------------------------------------------*/
321 PIX *
323  l_int32 xcen,
324  l_int32 ycen,
325  l_float32 angle,
326  l_int32 incolor)
327 {
328 l_int32 w, h, d, i, j, x, y, xdif, ydif, wm1, hm1, wpld;
329 l_uint32 val;
330 l_float32 sina, cosa;
331 l_uint32 *datad, *lined;
332 void **lines;
333 PIX *pixd;
334 
335  PROCNAME("pixRotateBySampling");
336 
337  if (!pixs)
338  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
339  if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
340  return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
341  pixGetDimensions(pixs, &w, &h, &d);
342  if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
343  return (PIX *)ERROR_PTR("invalid depth", procName, NULL);
344 
345  if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE)
346  return pixClone(pixs);
347 
348  if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL)
349  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
350  pixSetBlackOrWhite(pixd, incolor);
351 
352  sina = sin(angle);
353  cosa = cos(angle);
354  datad = pixGetData(pixd);
355  wpld = pixGetWpl(pixd);
356  wm1 = w - 1;
357  hm1 = h - 1;
358  lines = pixGetLinePtrs(pixs, NULL);
359 
360  /* Treat 1 bpp case specially */
361  if (d == 1) {
362  for (i = 0; i < h; i++) { /* scan over pixd */
363  lined = datad + i * wpld;
364  ydif = ycen - i;
365  for (j = 0; j < w; j++) {
366  xdif = xcen - j;
367  x = xcen + (l_int32)(-xdif * cosa - ydif * sina);
368  if (x < 0 || x > wm1) continue;
369  y = ycen + (l_int32)(-ydif * cosa + xdif * sina);
370  if (y < 0 || y > hm1) continue;
371  if (incolor == L_BRING_IN_WHITE) {
372  if (GET_DATA_BIT(lines[y], x))
373  SET_DATA_BIT(lined, j);
374  } else {
375  if (!GET_DATA_BIT(lines[y], x))
376  CLEAR_DATA_BIT(lined, j);
377  }
378  }
379  }
380  LEPT_FREE(lines);
381  return pixd;
382  }
383 
384  for (i = 0; i < h; i++) { /* scan over pixd */
385  lined = datad + i * wpld;
386  ydif = ycen - i;
387  for (j = 0; j < w; j++) {
388  xdif = xcen - j;
389  x = xcen + (l_int32)(-xdif * cosa - ydif * sina);
390  if (x < 0 || x > wm1) continue;
391  y = ycen + (l_int32)(-ydif * cosa + xdif * sina);
392  if (y < 0 || y > hm1) continue;
393  switch (d)
394  {
395  case 8:
396  val = GET_DATA_BYTE(lines[y], x);
397  SET_DATA_BYTE(lined, j, val);
398  break;
399  case 32:
400  val = GET_DATA_FOUR_BYTES(lines[y], x);
401  SET_DATA_FOUR_BYTES(lined, j, val);
402  break;
403  case 2:
404  val = GET_DATA_DIBIT(lines[y], x);
405  SET_DATA_DIBIT(lined, j, val);
406  break;
407  case 4:
408  val = GET_DATA_QBIT(lines[y], x);
409  SET_DATA_QBIT(lined, j, val);
410  break;
411  case 16:
412  val = GET_DATA_TWO_BYTES(lines[y], x);
413  SET_DATA_TWO_BYTES(lined, j, val);
414  break;
415  default:
416  return (PIX *)ERROR_PTR("invalid depth", procName, NULL);
417  }
418  }
419  }
420 
421  LEPT_FREE(lines);
422  return pixd;
423 }
424 
425 
426 /*------------------------------------------------------------------*
427  * Nice (slow) rotation of 1 bpp image *
428  *------------------------------------------------------------------*/
454 PIX *
456  l_float32 angle,
457  l_int32 incolor)
458 {
459 PIX *pix1, *pix2, *pix3, *pix4, *pixd;
460 
461  PROCNAME("pixRotateBinaryNice");
462 
463  if (!pixs || pixGetDepth(pixs) != 1)
464  return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
465  if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
466  return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
467 
468  pix1 = pixConvertTo8(pixs, 0);
469  pix2 = pixBlockconv(pix1, 1, 1); /* smallest blur allowed */
470  pix3 = pixRotateAM(pix2, angle, incolor);
471  pix4 = pixUnsharpMasking(pix3, 1, 1.0); /* sharpen a bit */
472  pixd = pixThresholdToBinary(pix4, 128);
473  pixDestroy(&pix1);
474  pixDestroy(&pix2);
475  pixDestroy(&pix3);
476  pixDestroy(&pix4);
477  return pixd;
478 }
479 
480 
481 /*------------------------------------------------------------------*
482  * Rotation including alpha (blend) component *
483  *------------------------------------------------------------------*/
532 PIX *
534  l_float32 angle,
535  PIX *pixg,
536  l_float32 fract)
537 {
538 l_int32 ws, hs, d, spp;
539 PIX *pixd, *pix32, *pixg2, *pixgr;
540 
541  PROCNAME("pixRotateWithAlpha");
542 
543  if (!pixs)
544  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
545  pixGetDimensions(pixs, &ws, &hs, &d);
546  if (d != 32 && pixGetColormap(pixs) == NULL)
547  return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
548  if (pixg && pixGetDepth(pixg) != 8) {
549  L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n",
550  procName);
551  pixg = NULL;
552  }
553  if (!pixg && (fract < 0.0 || fract > 1.0)) {
554  L_WARNING("invalid fract; using fully opaque\n", procName);
555  fract = 1.0;
556  }
557  if (!pixg && fract == 0.0)
558  L_WARNING("transparent alpha; image will not be blended\n", procName);
559 
560  /* Make sure input to rotation is 32 bpp rgb, and rotate it */
561  if (d != 32)
562  pix32 = pixConvertTo32(pixs);
563  else
564  pix32 = pixClone(pixs);
565  spp = pixGetSpp(pix32);
566  pixSetSpp(pix32, 3); /* ignore the alpha channel for the rotation */
567  pixd = pixRotate(pix32, angle, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, ws, hs);
568  pixSetSpp(pix32, spp); /* restore initial value in case it's a clone */
569  pixDestroy(&pix32);
570 
571  /* Set up alpha layer with a fading border and rotate it */
572  if (!pixg) {
573  pixg2 = pixCreate(ws, hs, 8);
574  if (fract == 1.0)
575  pixSetAll(pixg2);
576  else if (fract > 0.0)
577  pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract));
578  } else {
579  pixg2 = pixResizeToMatch(pixg, NULL, ws, hs);
580  }
581  if (ws > 10 && hs > 10) { /* see note 8 */
582  pixSetBorderRingVal(pixg2, 1,
583  (l_int32)(255.0 * fract * AlphaMaskBorderVals[0]));
584  pixSetBorderRingVal(pixg2, 2,
585  (l_int32)(255.0 * fract * AlphaMaskBorderVals[1]));
586  }
587  pixgr = pixRotate(pixg2, angle, L_ROTATE_AREA_MAP,
588  L_BRING_IN_BLACK, ws, hs);
589 
590  /* Combine into a 4 spp result */
591  pixSetRGBComponent(pixd, pixgr, L_ALPHA_CHANNEL);
592 
593  pixDestroy(&pixg2);
594  pixDestroy(&pixgr);
595  return pixd;
596 }
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:322
l_ok pixSetRGBComponent(PIX *pixd, PIX *pixs, l_int32 comp)
pixSetRGBComponent()
Definition: pix2.c:2463
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3233
l_ok pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy)
pixRasterop()
Definition: rop.c:193
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3041
void ** pixGetLinePtrs(PIX *pix, l_int32 *psize)
pixGetLinePtrs()
Definition: pix1.c:1810
#define GET_DATA_FOUR_BYTES(pdata, n)
Definition: arrayaccess.h:231
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
PIX * pixRotateWithAlpha(PIX *pixs, l_float32 angle, PIX *pixg, l_float32 fract)
pixRotateWithAlpha()
Definition: rotate.c:533
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:741
#define SET_DATA_QBIT(pdata, n, val)
Definition: arrayaccess.h:168
PIX * pixRotateBinaryNice(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateBinaryNice()
Definition: rotate.c:455
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1624
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:443
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
PIX * pixCreateTemplateNoInit(PIX *pixs)
pixCreateTemplateNoInit()
Definition: pix1.c:397
#define SET_DATA_DIBIT(pdata, n, val)
Definition: arrayaccess.h:149
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:876
#define CLEAR_DATA_BIT(pdata, n)
Definition: arrayaccess.h:131
PIX * pixRotateAMColor(PIX *pixs, l_float32 angle, l_uint32 colorval)
pixRotateAMColor()
Definition: rotateam.c:212
l_ok pixSetBorderRingVal(PIX *pixs, l_int32 dist, l_uint32 val)
pixSetBorderRingVal()
Definition: pix2.c:1592
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
#define GET_DATA_QBIT(pdata, n)
Definition: arrayaccess.h:164
l_ok pixSetBlackOrWhite(PIX *pixs, l_int32 op)
pixSetBlackOrWhite()
Definition: pix2.c:946
PIX * pixRotateShearCenter(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateShearCenter()
Definition: rotateshear.c:439
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:515
PIX * pixEmbedForRotation(PIX *pixs, l_float32 angle, l_int32 incolor, l_int32 width, l_int32 height)
pixEmbedForRotation()
Definition: rotate.c:241
PIX * pixRotateBySampling(PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor)
pixRotateBySampling()
Definition: rotate.c:322
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
l_ok pixCopyColormap(PIX *pixd, PIX *pixs)
pixCopyColormap()
Definition: pix1.c:745
PIX * pixRotateAM(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateAM()
Definition: rotateam.c:149
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
PIX * pixRotateAMGray(PIX *pixs, l_float32 angle, l_uint8 grayval)
pixRotateAMGray()
Definition: rotateam.c:266
l_ok pixcmapAddBlackOrWhite(PIXCMAP *cmap, l_int32 color, l_int32 *pindex)
pixcmapAddBlackOrWhite()
Definition: colormap.c:566
#define SET_DATA_FOUR_BYTES(pdata, n, val)
Definition: arrayaccess.h:235
#define GET_DATA_TWO_BYTES(pdata, n)
Definition: arrayaccess.h:212
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:145
PIX * pixUnsharpMasking(PIX *pixs, l_int32 halfwidth, l_float32 fract)
pixUnsharpMasking()
Definition: enhance.c:903
Definition: pix.h:134
#define PIX_SRC
Definition: pix.h:327
PIX * pixResizeToMatch(PIX *pixs, PIX *pixt, l_int32 w, l_int32 h)
pixResizeToMatch()
Definition: pix5.c:1252
PIX * pixBlockconv(PIX *pix, l_int32 wc, l_int32 hc)
pixBlockconv()
Definition: convolve.c:127
l_ok pixCopySpp(PIX *pixd, const PIX *pixs)
pixCopySpp()
Definition: pix1.c:1188
#define SET_DATA_TWO_BYTES(pdata, n, val)
Definition: arrayaccess.h:222
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
PIX * pixRotate(PIX *pixs, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height)
pixRotate()
Definition: rotate.c:99