comparison mupdf-source/thirdparty/leptonica/src/warper.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 /*!
28 * \file warper.c
29 * <pre>
30 *
31 * High-level captcha interface
32 * PIX *pixSimpleCaptcha()
33 *
34 * Random sinusoidal warping
35 * PIX *pixRandomHarmonicWarp()
36 *
37 * Helper functions
38 * static l_float64 *generateRandomNumberArray()
39 * static l_int32 applyWarpTransform()
40 *
41 * Version using a LUT for sin
42 * PIX *pixRandomHarmonicWarpLUT()
43 * static l_int32 applyWarpTransformLUT()
44 * static l_int32 makeSinLUT()
45 * static l_float32 getSinFromLUT()
46 *
47 * Stereoscopic warping
48 * PIX *pixWarpStereoscopic()
49 *
50 * Linear and quadratic horizontal stretching
51 * PIX *pixStretchHorizontal()
52 * PIX *pixStretchHorizontalSampled()
53 * PIX *pixStretchHorizontalLI()
54 *
55 * Quadratic vertical shear
56 * PIX *pixQuadraticVShear()
57 * PIX *pixQuadraticVShearSampled()
58 * PIX *pixQuadraticVShearLI()
59 *
60 * Stereo from a pair of images
61 * PIX *pixStereoFromPair()
62 * </pre>
63 */
64
65 #ifdef HAVE_CONFIG_H
66 #include <config_auto.h>
67 #endif /* HAVE_CONFIG_H */
68
69 #include <math.h>
70 #include "allheaders.h"
71
72 static l_float64 *generateRandomNumberArray(l_int32 size);
73 static l_int32 applyWarpTransform(l_float32 xmag, l_float32 ymag,
74 l_float32 xfreq, l_float32 yfreq,
75 l_float64 *randa, l_int32 nx, l_int32 ny,
76 l_int32 xp, l_int32 yp,
77 l_float32 *px, l_float32 *py);
78
79 #define USE_SIN_TABLE 0
80
81 /* Suggested input to pixStereoFromPair(). These are weighting
82 * factors for input to the red channel from the left image. */
83 static const l_float32 DefaultRedWeight = 0.0f;
84 static const l_float32 DefaultGreenWeight = 0.7f;
85 static const l_float32 DefaultBlueWeight = 0.3f;
86
87
88 /*----------------------------------------------------------------------*
89 * High-level example captcha interface *
90 *----------------------------------------------------------------------*/
91 /*!
92 * \brief pixSimpleCaptcha()
93 *
94 * \param[in] pixs 8 bpp; no colormap
95 * \param[in] border added white pixels on each side
96 * \param[in] nterms number of x and y harmonic terms
97 * \param[in] seed of random number generator
98 * \param[in] color for colorizing; in 0xrrggbb00 format; use 0 for black
99 * \param[in] cmapflag 1 for colormap output; 0 for rgb
100 * \return pixd 8 bpp cmap or 32 bpp rgb, or NULL on error
101 *
102 * <pre>
103 * Notes:
104 * (1) This uses typical default values for generating captchas.
105 * The magnitudes of the harmonic warp are typically to be
106 * smaller when more terms are used, even though the phases
107 * are random. See, for example, prog/warptest.c.
108 * </pre>
109 */
110 PIX *
111 pixSimpleCaptcha(PIX *pixs,
112 l_int32 border,
113 l_int32 nterms,
114 l_uint32 seed,
115 l_uint32 color,
116 l_int32 cmapflag)
117 {
118 l_int32 k;
119 l_float32 xmag[] = {7.0f, 5.0f, 4.0f, 3.0f};
120 l_float32 ymag[] = {10.0f, 8.0f, 6.0f, 5.0f};
121 l_float32 xfreq[] = {0.12f, 0.10f, 0.10f, 0.11f};
122 l_float32 yfreq[] = {0.15f, 0.13f, 0.13f, 0.11f};
123 PIX *pixg, *pixgb, *pixw, *pixd;
124
125 if (!pixs)
126 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
127 if (nterms < 1 || nterms > 4)
128 return (PIX *)ERROR_PTR("nterms must be in {1,2,3,4}", __func__, NULL);
129
130 k = nterms - 1;
131 pixg = pixConvertTo8(pixs, 0);
132 pixgb = pixAddBorder(pixg, border, 255);
133 pixw = pixRandomHarmonicWarp(pixgb, xmag[k], ymag[k], xfreq[k], yfreq[k],
134 nterms, nterms, seed, 255);
135 pixd = pixColorizeGray(pixw, color, cmapflag);
136
137 pixDestroy(&pixg);
138 pixDestroy(&pixgb);
139 pixDestroy(&pixw);
140 return pixd;
141 }
142
143
144 /*----------------------------------------------------------------------*
145 * Random sinusoidal warping *
146 *----------------------------------------------------------------------*/
147 /*!
148 * \brief pixRandomHarmonicWarp()
149 *
150 * \param[in] pixs 8 bpp; no colormap
151 * \param[in] xmag, ymag maximum magnitude of x and y distortion
152 * \param[in] xfreq, yfreq maximum magnitude of x and y frequency
153 * \param[in] nx, ny number of x and y harmonic terms
154 * \param[in] seed of random number generator
155 * \param[in] grayval color brought in from the outside;
156 * 0 for black, 255 for white
157 * \return pixd 8 bpp; no colormap, or NULL on error
158 *
159 * <pre>
160 * Notes:
161 * (1) To generate the warped image p(x',y'), set up the transforms
162 * that are in getWarpTransform(). For each (x',y') in the
163 * dest, the warp function computes the originating location
164 * (x, y) in the src. The differences (x - x') and (y - y')
165 * are given as a sum of products of sinusoidal terms. Each
166 * term is multiplied by a maximum amplitude (in pixels), and the
167 * angle is determined by a frequency and phase, and depends
168 * on the (x', y') value of the dest. Random numbers with
169 * a variable input seed are used to allow the warping to be
170 * unpredictable. A linear interpolation is used to find
171 * the value for the source at (x, y); this value is written
172 * into the dest.
173 * (2) This can be used to generate 'captcha's, which are somewhat
174 * randomly distorted images of text. A typical set of parameters
175 * for a captcha are:
176 * xmag = 4.0 ymag = 6.0
177 * xfreq = 0.10 yfreq = 0.13
178 * nx = 3 ny = 3
179 * Other examples can be found in prog/warptest.c.
180 * </pre>
181 */
182 PIX *
183 pixRandomHarmonicWarp(PIX *pixs,
184 l_float32 xmag,
185 l_float32 ymag,
186 l_float32 xfreq,
187 l_float32 yfreq,
188 l_int32 nx,
189 l_int32 ny,
190 l_uint32 seed,
191 l_int32 grayval)
192 {
193 l_int32 w, h, d, i, j, wpls, wpld, val;
194 l_uint32 *datas, *datad, *lined;
195 l_float32 x, y;
196 l_float64 *randa;
197 PIX *pixd;
198
199 if (!pixs)
200 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
201 pixGetDimensions(pixs, &w, &h, &d);
202 if (d != 8)
203 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
204
205 /* Compute filter output at each location. We iterate over
206 * the destination pixels. For each dest pixel, use the
207 * warp function to compute the four source pixels that
208 * contribute, at the location (x, y). Each source pixel
209 * is divided into 16 x 16 subpixels to get an approximate value. */
210 srand(seed);
211 randa = generateRandomNumberArray(5 * (nx + ny));
212 pixd = pixCreateTemplate(pixs);
213 datas = pixGetData(pixs);
214 wpls = pixGetWpl(pixs);
215 datad = pixGetData(pixd);
216 wpld = pixGetWpl(pixd);
217
218 for (i = 0; i < h; i++) {
219 lined = datad + i * wpld;
220 for (j = 0; j < w; j++) {
221 applyWarpTransform(xmag, ymag, xfreq, yfreq, randa, nx, ny,
222 j, i, &x, &y);
223 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val);
224 SET_DATA_BYTE(lined, j, val);
225 }
226 }
227
228 LEPT_FREE(randa);
229 return pixd;
230 }
231
232
233 /*----------------------------------------------------------------------*
234 * Static helper functions *
235 *----------------------------------------------------------------------*/
236 static l_float64 *
237 generateRandomNumberArray(l_int32 size)
238 {
239 l_int32 i;
240 l_float64 *randa;
241
242 if ((randa = (l_float64 *)LEPT_CALLOC(size, sizeof(l_float64))) == NULL)
243 return (l_float64 *)ERROR_PTR("calloc fail for randa", __func__, NULL);
244
245 /* Return random values between 0.5 and 1.0 */
246 for (i = 0; i < size; i++)
247 randa[i] = 0.5 * (1.0 + (l_float64)rand() / (l_float64)RAND_MAX);
248 return randa;
249 }
250
251
252 /*!
253 * \brief applyWarpTransform()
254 *
255 * Notes:
256 * (1) Uses the internal sin function.
257 */
258 static l_int32
259 applyWarpTransform(l_float32 xmag,
260 l_float32 ymag,
261 l_float32 xfreq,
262 l_float32 yfreq,
263 l_float64 *randa,
264 l_int32 nx,
265 l_int32 ny,
266 l_int32 xp,
267 l_int32 yp,
268 l_float32 *px,
269 l_float32 *py)
270 {
271 l_int32 i;
272 l_float64 twopi, x, y, anglex, angley;
273
274 twopi = 6.283185;
275 for (i = 0, x = xp; i < nx; i++) {
276 anglex = xfreq * randa[3 * i + 1] * xp + twopi * randa[3 * i + 2];
277 angley = yfreq * randa[3 * i + 3] * yp + twopi * randa[3 * i + 4];
278 x += xmag * randa[3 * i] * sin(anglex) * sin(angley);
279 }
280 for (i = nx, y = yp; i < nx + ny; i++) {
281 angley = yfreq * randa[3 * i + 1] * yp + twopi * randa[3 * i + 2];
282 anglex = xfreq * randa[3 * i + 3] * xp + twopi * randa[3 * i + 4];
283 y += ymag * randa[3 * i] * sin(angley) * sin(anglex);
284 }
285
286 *px = (l_float32)x;
287 *py = (l_float32)y;
288 return 0;
289 }
290
291
292 #if USE_SIN_TABLE
293 /*----------------------------------------------------------------------*
294 * Version using a LUT for sin *
295 *----------------------------------------------------------------------*/
296 static l_int32 applyWarpTransformLUT(l_float32 xmag, l_float32 ymag,
297 l_float32 xfreq, l_float32 yfreq,
298 l_float64 *randa, l_int32 nx, l_int32 ny,
299 l_int32 xp, l_int32 yp, l_float32 *lut,
300 l_int32 npts, l_float32 *px, l_float32 *py);
301 static l_int32 makeSinLUT(l_int32 npts, NUMA **pna);
302 static l_float32 getSinFromLUT(l_float32 *tab, l_int32 npts,
303 l_float32 radang);
304
305 /*!
306 * \brief pixRandomHarmonicWarpLUT()
307 *
308 * \param[in] pixs 8 bpp; no colormap
309 * \param[in] xmag, ymag maximum magnitude of x and y distortion
310 * \param[in] xfreq, yfreq maximum magnitude of x and y frequency
311 * \param[in] nx, ny number of x and y harmonic terms
312 * \param[in] seed of random number generator
313 * \param[in] grayval color brought in from the outside;
314 * 0 for black, 255 for white
315 * \return pixd 8 bpp; no colormap, or NULL on error
316 *
317 * <pre>
318 * Notes:
319 * (1) See notes and inline comments in pixRandomHarmonicWarp().
320 * This version uses a LUT for the sin function. It is not
321 * appreciably faster than using the built-in sin function,
322 * and is here for comparison only.
323 * </pre>
324 */
325 PIX *
326 pixRandomHarmonicWarpLUT(PIX *pixs,
327 l_float32 xmag,
328 l_float32 ymag,
329 l_float32 xfreq,
330 l_float32 yfreq,
331 l_int32 nx,
332 l_int32 ny,
333 l_uint32 seed,
334 l_int32 grayval)
335 {
336 l_int32 w, h, d, i, j, wpls, wpld, val, npts;
337 l_uint32 *datas, *datad, *lined;
338 l_float32 x, y;
339 l_float32 *lut;
340 l_float64 *randa;
341 NUMA *na;
342 PIX *pixd;
343
344 if (!pixs)
345 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
346 pixGetDimensions(pixs, &w, &h, &d);
347 if (d != 8)
348 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL);
349
350 /* Compute filter output at each location. We iterate over
351 * the destination pixels. For each dest pixel, use the
352 * warp function to compute the four source pixels that
353 * contribute, at the location (x, y). Each source pixel
354 * is divided into 16 x 16 subpixels to get an approximate value. */
355 srand(seed);
356 randa = generateRandomNumberArray(5 * (nx + ny));
357 pixd = pixCreateTemplate(pixs);
358 datas = pixGetData(pixs);
359 wpls = pixGetWpl(pixs);
360 datad = pixGetData(pixd);
361 wpld = pixGetWpl(pixd);
362
363 npts = 100;
364 makeSinLUT(npts, &na);
365 lut = numaGetFArray(na, L_NOCOPY);
366 for (i = 0; i < h; i++) {
367 lined = datad + i * wpld;
368 for (j = 0; j < w; j++) {
369 applyWarpTransformLUT(xmag, ymag, xfreq, yfreq, randa, nx, ny,
370 j, i, lut, npts, &x, &y);
371 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val);
372 SET_DATA_BYTE(lined, j, val);
373 }
374 }
375
376 numaDestroy(&na);
377 LEPT_FREE(randa);
378 return pixd;
379 }
380
381
382 /*!
383 * \brief applyWarpTransformLUT()
384 *
385 * Notes:
386 * (1) Uses an LUT for computing sin(theta). There is little speed
387 * advantage to using the LUT.
388 */
389 static l_int32
390 applyWarpTransformLUT(l_float32 xmag,
391 l_float32 ymag,
392 l_float32 xfreq,
393 l_float32 yfreq,
394 l_float64 *randa,
395 l_int32 nx,
396 l_int32 ny,
397 l_int32 xp,
398 l_int32 yp,
399 l_float32 *lut,
400 l_int32 npts,
401 l_float32 *px,
402 l_float32 *py)
403 {
404 l_int32 i;
405 l_float64 twopi, x, y, anglex, angley, sanglex, sangley;
406
407 twopi = 6.283185;
408 for (i = 0, x = xp; i < nx; i++) {
409 anglex = xfreq * randa[3 * i + 1] * xp + twopi * randa[3 * i + 2];
410 angley = yfreq * randa[3 * i + 3] * yp + twopi * randa[3 * i + 4];
411 sanglex = getSinFromLUT(lut, npts, anglex);
412 sangley = getSinFromLUT(lut, npts, angley);
413 x += xmag * randa[3 * i] * sanglex * sangley;
414 }
415 for (i = nx, y = yp; i < nx + ny; i++) {
416 angley = yfreq * randa[3 * i + 1] * yp + twopi * randa[3 * i + 2];
417 anglex = xfreq * randa[3 * i + 3] * xp + twopi * randa[3 * i + 4];
418 sanglex = getSinFromLUT(lut, npts, anglex);
419 sangley = getSinFromLUT(lut, npts, angley);
420 y += ymag * randa[3 * i] * sangley * sanglex;
421 }
422
423 *px = (l_float32)x;
424 *py = (l_float32)y;
425 return 0;
426 }
427
428
429 static l_int32
430 makeSinLUT(l_int32 npts,
431 NUMA **pna)
432 {
433 l_int32 i, n;
434 l_float32 delx, fval;
435 NUMA *na;
436
437 if (!pna)
438 return ERROR_INT("&na not defined", __func__, 1);
439 *pna = NULL;
440 if (npts < 2)
441 return ERROR_INT("npts < 2", __func__, 1);
442 n = 2 * npts + 1;
443 na = numaCreate(n);
444 *pna = na;
445 delx = 3.14159265 / (l_float32)npts;
446 numaSetParameters(na, 0.0, delx);
447 for (i = 0; i < n / 2; i++)
448 numaAddNumber(na, (l_float32)sin((l_float64)i * delx));
449 for (i = 0; i < n / 2; i++) {
450 numaGetFValue(na, i, &fval);
451 numaAddNumber(na, -fval);
452 }
453 numaAddNumber(na, 0);
454
455 return 0;
456 }
457
458
459 static l_float32
460 getSinFromLUT(l_float32 *tab,
461 l_int32 npts,
462 l_float32 radang)
463 {
464 l_int32 index;
465 l_float32 twopi, invtwopi, findex, diff;
466
467 /* Restrict radang to [0, 2pi] */
468 twopi = 6.283185;
469 invtwopi = 0.1591549;
470 if (radang < 0.0)
471 radang += twopi * (1.0 - (l_int32)(-radang * invtwopi));
472 else if (radang > 0.0)
473 radang -= twopi * (l_int32)(radang * invtwopi);
474
475 /* Interpolate */
476 findex = (2.0 * (l_float32)npts) * (radang * invtwopi);
477 index = (l_int32)findex;
478 if (index == 2 * npts)
479 return tab[index];
480 diff = findex - index;
481 return (1.0 - diff) * tab[index] + diff * tab[index + 1];
482 }
483 #endif /* USE_SIN_TABLE */
484
485
486
487 /*---------------------------------------------------------------------------*
488 * Stereoscopic warping *
489 *---------------------------------------------------------------------------*/
490 /*!
491 * \brief pixWarpStereoscopic()
492 *
493 * \param[in] pixs any depth, colormap ok
494 * \param[in] zbend horizontal separation in pixels of red and cyan
495 * at the left and right sides, that gives rise to
496 * quadratic curvature out of the image plane
497 * \param[in] zshiftt uniform pixel translation difference between
498 * red and cyan, that pushes the top of the image
499 * plane away from the viewer (zshiftt > 0) or
500 * towards the viewer (zshiftt < 0)
501 * \param[in] zshiftb uniform pixel translation difference between
502 * red and cyan, that pushes the bottom of the image
503 * plane away from the viewer (zshiftb > 0) or
504 * towards the viewer (zshiftb < 0)
505 * \param[in] ybendt multiplicative parameter for in-plane vertical
506 * displacement at the left or right edge at the top:
507 * y = ybendt * (2x/w - 1)^2
508 * \param[in] ybendb same as ybendt, except at the left or right edge
509 * at the bottom
510 * \param[in] redleft 1 if the red filter is on the left; 0 otherwise
511 * \return pixd 32 bpp, or NULL on error
512 *
513 * <pre>
514 * Notes:
515 * (1) This function splits out the red channel, mucks around with
516 * it, then recombines with the unmolested cyan channel.
517 * (2) By using a quadratically increasing shift of the red
518 * pixels horizontally and away from the vertical centerline,
519 * the image appears to bend quadratically out of the image
520 * plane, symmetrically with respect to the vertical center
521 * line. A positive value of %zbend causes the plane to be
522 * curved away from the viewer. We use linearly interpolated
523 * stretching to avoid the appearance of kinks in the curve.
524 * (3) The parameters %zshiftt and %zshiftb tilt the image plane
525 * about a horizontal line through the center, and at the
526 * same time move that line either in toward the viewer or away.
527 * This is implemented by a combination of horizontal shear
528 * about the center line (for the tilt) and horizontal
529 * translation (to move the entire plane in or out).
530 * A positive value of %zshiftt moves the top of the plane
531 * away from the viewer, and a positive value of %zshiftb
532 * moves the bottom of the plane away. We use linear interpolated
533 * shear to avoid visible vertical steps in the tilted image.
534 * (4) The image can be bent in the plane and about the vertical
535 * centerline. The centerline does not shift, and the
536 * parameter %ybend gives the relative shift at left and right
537 * edges, with a downward shift for positive values of %ybend.
538 * (6) When writing out a steroscopic (red/cyan) image in jpeg,
539 * first call pixSetChromaSampling(pix, 0) to get sufficient
540 * resolution in the red channel.
541 * (7) Typical values are:
542 * zbend = 20
543 * zshiftt = 15
544 * zshiftb = -15
545 * ybendt = 30
546 * ybendb = 0
547 * If the disparity z-values are too large, it is difficult for
548 * the brain to register the two images.
549 * (8) This function has been cleverly reimplemented by Jeff Breidenbach.
550 * The original implementation used two 32 bpp rgb images,
551 * and merged them at the end. The result is somewhat faded,
552 * and has a parameter "thresh" that controls the amount of
553 * color in the result. (The present implementation avoids these
554 * two problems, skipping both the colorization and the alpha
555 * blending at the end, and is about 3x faster)
556 * The basic operations with 32 bpp are as follows:
557 * // Immediate conversion to 32 bpp
558 * Pix *pixt1 = pixConvertTo32(pixs);
559 * // Do vertical shear
560 * Pix *pixr = pixQuadraticVerticalShear(pixt1, L_WARP_TO_RIGHT,
561 * ybendt, ybendb,
562 * L_BRING_IN_WHITE);
563 * // Colorize two versions, toward red and cyan
564 * Pix *pixc = pixCopy(NULL, pixr);
565 * l_int32 thresh = 150; // if higher, get less original color
566 * pixColorGray(pixr, NULL, L_PAINT_DARK, thresh, 255, 0, 0);
567 * pixColorGray(pixc, NULL, L_PAINT_DARK, thresh, 0, 255, 255);
568 * // Shift the red pixels; e.g., by stretching
569 * Pix *pixrs = pixStretchHorizontal(pixr, L_WARP_TO_RIGHT,
570 * L_QUADRATIC_WARP, zbend,
571 * L_INTERPOLATED,
572 * L_BRING_IN_WHITE);
573 * // Blend the shifted red and unshifted cyan 50:50
574 * Pix *pixg = pixCreate(w, h, 8);
575 * pixSetAllArbitrary(pixg, 128);
576 * pixd = pixBlendWithGrayMask(pixrs, pixc, pixg, 0, 0);
577 * </pre>
578 */
579 PIX *
580 pixWarpStereoscopic(PIX *pixs,
581 l_int32 zbend,
582 l_int32 zshiftt,
583 l_int32 zshiftb,
584 l_int32 ybendt,
585 l_int32 ybendb,
586 l_int32 redleft)
587 {
588 l_int32 w, h, zshift;
589 l_float32 angle;
590 BOX *boxleft, *boxright;
591 PIX *pix1, *pix2, *pix3, *pix4, *pixr, *pixg, *pixb;
592 PIX *pixv1, *pixv2, *pixv3, *pixv4;
593 PIX *pixrs, *pixrss;
594 PIX *pixd;
595
596 if (!pixs)
597 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
598
599 /* Convert to the output depth, 32 bpp. */
600 pix1 = pixConvertTo32(pixs);
601
602 /* If requested, do a quad vertical shearing, pushing pixels up
603 * or down, depending on their distance from the centerline. */
604 pixGetDimensions(pixs, &w, &h, NULL);
605 boxleft = boxCreate(0, 0, w / 2, h);
606 boxright = boxCreate(w / 2, 0, w - w / 2, h);
607 if (ybendt != 0 || ybendb != 0) {
608 pixv1 = pixClipRectangle(pix1, boxleft, NULL);
609 pixv2 = pixClipRectangle(pix1, boxright, NULL);
610 pixv3 = pixQuadraticVShear(pixv1, L_WARP_TO_LEFT, ybendt,
611 ybendb, L_INTERPOLATED,
612 L_BRING_IN_WHITE);
613 pixv4 = pixQuadraticVShear(pixv2, L_WARP_TO_RIGHT, ybendt,
614 ybendb, L_INTERPOLATED,
615 L_BRING_IN_WHITE);
616 pix2 = pixCreate(w, h, 32);
617 pixRasterop(pix2, 0, 0, w / 2, h, PIX_SRC, pixv3, 0, 0);
618 pixRasterop(pix2, w / 2, 0, w - w / 2, h, PIX_SRC, pixv4, 0, 0);
619 pixDestroy(&pixv1);
620 pixDestroy(&pixv2);
621 pixDestroy(&pixv3);
622 pixDestroy(&pixv4);
623 } else {
624 pix2 = pixClone(pix1);
625 }
626 pixDestroy(&pix1);
627
628 /* Split out the 3 components */
629 pixr = pixGetRGBComponent(pix2, COLOR_RED);
630 pixg = pixGetRGBComponent(pix2, COLOR_GREEN);
631 pixb = pixGetRGBComponent(pix2, COLOR_BLUE);
632 pixDestroy(&pix2);
633
634 /* The direction of the stereo disparity below is set
635 * for the red filter to be over the left eye. If the red
636 * filter is over the right eye, invert the horizontal shifts. */
637 if (redleft) {
638 zbend = -zbend;
639 zshiftt = -zshiftt;
640 zshiftb = -zshiftb;
641 }
642
643 /* Shift the red pixels horizontally by an amount that
644 * increases quadratically from the centerline. */
645 if (zbend == 0) {
646 pixrs = pixClone(pixr);
647 } else {
648 pix1 = pixClipRectangle(pixr, boxleft, NULL);
649 pix2 = pixClipRectangle(pixr, boxright, NULL);
650 pix3 = pixStretchHorizontal(pix1, L_WARP_TO_LEFT, L_QUADRATIC_WARP,
651 zbend, L_INTERPOLATED, L_BRING_IN_WHITE);
652 pix4 = pixStretchHorizontal(pix2, L_WARP_TO_RIGHT, L_QUADRATIC_WARP,
653 zbend, L_INTERPOLATED, L_BRING_IN_WHITE);
654 pixrs = pixCreate(w, h, 8);
655 pixRasterop(pixrs, 0, 0, w / 2, h, PIX_SRC, pix3, 0, 0);
656 pixRasterop(pixrs, w / 2, 0, w - w / 2, h, PIX_SRC, pix4, 0, 0);
657 pixDestroy(&pix1);
658 pixDestroy(&pix2);
659 pixDestroy(&pix3);
660 pixDestroy(&pix4);
661 }
662
663 /* Perform a combination of horizontal shift and shear of
664 * red pixels. The causes the plane of the image to tilt and
665 * also move forward or backward. */
666 if (zshiftt == 0 && zshiftb == 0) {
667 pixrss = pixClone(pixrs);
668 } else if (zshiftt == zshiftb) {
669 pixrss = pixTranslate(NULL, pixrs, zshiftt, 0, L_BRING_IN_WHITE);
670 } else {
671 angle = (l_float32)(zshiftb - zshiftt) /
672 L_MAX(1.0f, (l_float32)pixGetHeight(pixrs));
673 zshift = (zshiftt + zshiftb) / 2;
674 pix1 = pixTranslate(NULL, pixrs, zshift, 0, L_BRING_IN_WHITE);
675 pixrss = pixHShearLI(pix1, h / 2, angle, L_BRING_IN_WHITE);
676 pixDestroy(&pix1);
677 }
678
679 /* Combine the unchanged cyan (g,b) image with the shifted red */
680 pixd = pixCreateRGBImage(pixrss, pixg, pixb);
681
682 boxDestroy(&boxleft);
683 boxDestroy(&boxright);
684 pixDestroy(&pixrs);
685 pixDestroy(&pixrss);
686 pixDestroy(&pixr);
687 pixDestroy(&pixg);
688 pixDestroy(&pixb);
689 return pixd;
690 }
691
692
693 /*----------------------------------------------------------------------*
694 * Linear and quadratic horizontal stretching *
695 *----------------------------------------------------------------------*/
696 /*!
697 * \brief pixStretchHorizontal()
698 *
699 * \param[in] pixs 1, 8 or 32 bpp
700 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT
701 * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP
702 * \param[in] hmax horizontal displacement at edge
703 * \param[in] operation L_SAMPLED or L_INTERPOLATED
704 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK
705 * \return pixd stretched/compressed, or NULL on error
706 *
707 * <pre>
708 * Notes:
709 * (1) If %hmax > 0, this is an increase in the coordinate value of
710 * pixels in pixd, relative to the same pixel in pixs.
711 * (2) If %dir == L_WARP_TO_LEFT, the pixels on the right edge of
712 * the image are not moved. So, for example, if %hmax > 0
713 * and %dir == L_WARP_TO_LEFT, the pixels in pixd are
714 * contracted toward the right edge of the image, relative
715 * to those in pixs.
716 * (3) If %type == L_LINEAR_WARP, the pixel positions are moved
717 * to the left or right by an amount that varies linearly with
718 * the horizontal location.
719 * (4) If %operation == L_SAMPLED, the dest pixels are taken from
720 * the nearest src pixel. Otherwise, we use linear interpolation
721 * between pairs of sampled pixels.
722 * </pre>
723 */
724 PIX *
725 pixStretchHorizontal(PIX *pixs,
726 l_int32 dir,
727 l_int32 type,
728 l_int32 hmax,
729 l_int32 operation,
730 l_int32 incolor)
731 {
732 l_int32 d;
733
734 if (!pixs)
735 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
736 d = pixGetDepth(pixs);
737 if (d != 1 && d != 8 && d != 32)
738 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", __func__, NULL);
739 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT)
740 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL);
741 if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP)
742 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
743 if (operation != L_SAMPLED && operation != L_INTERPOLATED)
744 return (PIX *)ERROR_PTR("invalid operation", __func__, NULL);
745 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
746 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
747 if (d == 1 && operation == L_INTERPOLATED) {
748 L_WARNING("Using sampling for 1 bpp\n", __func__);
749 operation = L_INTERPOLATED;
750 }
751
752 if (operation == L_SAMPLED)
753 return pixStretchHorizontalSampled(pixs, dir, type, hmax, incolor);
754 else
755 return pixStretchHorizontalLI(pixs, dir, type, hmax, incolor);
756 }
757
758
759 /*!
760 * \brief pixStretchHorizontalSampled()
761 *
762 * \param[in] pixs 1, 8 or 32 bpp
763 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT
764 * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP
765 * \param[in] hmax horizontal displacement at edge
766 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK
767 * \return pixd stretched/compressed, or NULL on error
768 *
769 * <pre>
770 * Notes:
771 * (1) See pixStretchHorizontal() for details.
772 * </pre>
773 */
774 PIX *
775 pixStretchHorizontalSampled(PIX *pixs,
776 l_int32 dir,
777 l_int32 type,
778 l_int32 hmax,
779 l_int32 incolor)
780 {
781 l_int32 i, j, jd, w, wm, h, d, wpls, wpld, val;
782 l_uint32 *datas, *datad, *lines, *lined;
783 PIX *pixd;
784
785 if (!pixs)
786 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
787 pixGetDimensions(pixs, &w, &h, &d);
788 if (d != 1 && d != 8 && d != 32)
789 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", __func__, NULL);
790 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT)
791 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL);
792 if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP)
793 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
794 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
795 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
796
797 pixd = pixCreateTemplate(pixs);
798 pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE);
799 datas = pixGetData(pixs);
800 datad = pixGetData(pixd);
801 wpls = pixGetWpl(pixs);
802 wpld = pixGetWpl(pixd);
803 wm = w - 1;
804 for (jd = 0; jd < w; jd++) {
805 if (dir == L_WARP_TO_LEFT) {
806 if (type == L_LINEAR_WARP)
807 j = jd - (hmax * (wm - jd)) / wm;
808 else /* L_QUADRATIC_WARP */
809 j = jd - (hmax * (wm - jd) * (wm - jd)) / (wm * wm);
810 } else if (dir == L_WARP_TO_RIGHT) {
811 if (type == L_LINEAR_WARP)
812 j = jd - (hmax * jd) / wm;
813 else /* L_QUADRATIC_WARP */
814 j = jd - (hmax * jd * jd) / (wm * wm);
815 }
816 if (j < 0 || j > w - 1) continue;
817
818 switch (d)
819 {
820 case 1:
821 for (i = 0; i < h; i++) {
822 lines = datas + i * wpls;
823 lined = datad + i * wpld;
824 val = GET_DATA_BIT(lines, j);
825 if (val)
826 SET_DATA_BIT(lined, jd);
827 }
828 break;
829 case 8:
830 for (i = 0; i < h; i++) {
831 lines = datas + i * wpls;
832 lined = datad + i * wpld;
833 val = GET_DATA_BYTE(lines, j);
834 SET_DATA_BYTE(lined, jd, val);
835 }
836 break;
837 case 32:
838 for (i = 0; i < h; i++) {
839 lines = datas + i * wpls;
840 lined = datad + i * wpld;
841 lined[jd] = lines[j];
842 }
843 break;
844 default:
845 L_ERROR("invalid depth: %d\n", __func__, d);
846 pixDestroy(&pixd);
847 return NULL;
848 }
849 }
850
851 return pixd;
852 }
853
854
855 /*!
856 * \brief pixStretchHorizontalLI()
857 *
858 * \param[in] pixs 1, 8 or 32 bpp
859 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT
860 * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP
861 * \param[in] hmax horizontal displacement at edge
862 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK
863 * \return pixd stretched/compressed, or NULL on error
864 *
865 * <pre>
866 * Notes:
867 * (1) See pixStretchHorizontal() for details.
868 * </pre>
869 */
870 PIX *
871 pixStretchHorizontalLI(PIX *pixs,
872 l_int32 dir,
873 l_int32 type,
874 l_int32 hmax,
875 l_int32 incolor)
876 {
877 l_int32 i, j, jd, jp, jf, w, wm, h, d, wpls, wpld, val, rval, gval, bval;
878 l_uint32 word0, word1;
879 l_uint32 *datas, *datad, *lines, *lined;
880 PIX *pixd;
881
882 if (!pixs)
883 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
884 pixGetDimensions(pixs, &w, &h, &d);
885 if (d != 8 && d != 32)
886 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", __func__, NULL);
887 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT)
888 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL);
889 if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP)
890 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
891 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
892 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
893
894 /* Standard linear interpolation, subdividing each pixel into 64 */
895 pixd = pixCreateTemplate(pixs);
896 pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE);
897 datas = pixGetData(pixs);
898 datad = pixGetData(pixd);
899 wpls = pixGetWpl(pixs);
900 wpld = pixGetWpl(pixd);
901 wm = w - 1;
902 for (jd = 0; jd < w; jd++) {
903 if (dir == L_WARP_TO_LEFT) {
904 if (type == L_LINEAR_WARP)
905 j = 64 * jd - 64 * (hmax * (wm - jd)) / wm;
906 else /* L_QUADRATIC_WARP */
907 j = 64 * jd - 64 * (hmax * (wm - jd) * (wm - jd)) / (wm * wm);
908 } else if (dir == L_WARP_TO_RIGHT) {
909 if (type == L_LINEAR_WARP)
910 j = 64 * jd - 64 * (hmax * jd) / wm;
911 else /* L_QUADRATIC_WARP */
912 j = 64 * jd - 64 * (hmax * jd * jd) / (wm * wm);
913 }
914 jp = j / 64;
915 jf = j & 0x3f;
916 if (jp < 0 || jp > wm) continue;
917
918 switch (d)
919 {
920 case 8:
921 if (jp < wm) {
922 for (i = 0; i < h; i++) {
923 lines = datas + i * wpls;
924 lined = datad + i * wpld;
925 val = ((63 - jf) * GET_DATA_BYTE(lines, jp) +
926 jf * GET_DATA_BYTE(lines, jp + 1) + 31) / 63;
927 SET_DATA_BYTE(lined, jd, val);
928 }
929 } else { /* jp == wm */
930 for (i = 0; i < h; i++) {
931 lines = datas + i * wpls;
932 lined = datad + i * wpld;
933 val = GET_DATA_BYTE(lines, jp);
934 SET_DATA_BYTE(lined, jd, val);
935 }
936 }
937 break;
938 case 32:
939 if (jp < wm) {
940 for (i = 0; i < h; i++) {
941 lines = datas + i * wpls;
942 lined = datad + i * wpld;
943 word0 = *(lines + jp);
944 word1 = *(lines + jp + 1);
945 rval = ((63 - jf) * ((word0 >> L_RED_SHIFT) & 0xff) +
946 jf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63;
947 gval = ((63 - jf) * ((word0 >> L_GREEN_SHIFT) & 0xff) +
948 jf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63;
949 bval = ((63 - jf) * ((word0 >> L_BLUE_SHIFT) & 0xff) +
950 jf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63;
951 composeRGBPixel(rval, gval, bval, lined + jd);
952 }
953 } else { /* jp == wm */
954 for (i = 0; i < h; i++) {
955 lines = datas + i * wpls;
956 lined = datad + i * wpld;
957 lined[jd] = lines[jp];
958 }
959 }
960 break;
961 default:
962 L_ERROR("invalid depth: %d\n", __func__, d);
963 pixDestroy(&pixd);
964 return NULL;
965 }
966 }
967
968 return pixd;
969 }
970
971
972 /*----------------------------------------------------------------------*
973 * Quadratic vertical shear *
974 *----------------------------------------------------------------------*/
975 /*!
976 * \brief pixQuadraticVShear()
977 *
978 * \param[in] pixs 1, 8 or 32 bpp
979 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT
980 * \param[in] vmaxt max vertical displacement at edge and at top
981 * \param[in] vmaxb max vertical displacement at edge and at bottom
982 * \param[in] operation L_SAMPLED or L_INTERPOLATED
983 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK
984 * \return pixd stretched, or NULL on error
985 *
986 * <pre>
987 * Notes:
988 * (1) This gives a quadratic bending, upward or downward, as you
989 * move to the left or right.
990 * (2) If %dir == L_WARP_TO_LEFT, the right edge is unchanged, and
991 * the left edge pixels are moved maximally up or down.
992 * (3) Parameters %vmaxt and %vmaxb control the maximum amount of
993 * vertical pixel shear at the top and bottom, respectively.
994 * If %vmaxt > 0, the vertical displacement of pixels at the
995 * top is downward. Likewise, if %vmaxb > 0, the vertical
996 * displacement of pixels at the bottom is downward.
997 * (4) If %operation == L_SAMPLED, the dest pixels are taken from
998 * the nearest src pixel. Otherwise, we use linear interpolation
999 * between pairs of sampled pixels.
1000 * (5) This is for quadratic shear. For uniform (linear) shear,
1001 * use the standard shear operators.
1002 * </pre>
1003 */
1004 PIX *
1005 pixQuadraticVShear(PIX *pixs,
1006 l_int32 dir,
1007 l_int32 vmaxt,
1008 l_int32 vmaxb,
1009 l_int32 operation,
1010 l_int32 incolor)
1011 {
1012 l_int32 w, h, d;
1013
1014 if (!pixs)
1015 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1016 pixGetDimensions(pixs, &w, &h, &d);
1017 if (d != 1 && d != 8 && d != 32)
1018 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", __func__, NULL);
1019 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT)
1020 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL);
1021 if (operation != L_SAMPLED && operation != L_INTERPOLATED)
1022 return (PIX *)ERROR_PTR("invalid operation", __func__, NULL);
1023 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
1024 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
1025
1026 if (vmaxt == 0 && vmaxb == 0)
1027 return pixCopy(NULL, pixs);
1028
1029 if (operation == L_INTERPOLATED && d == 1) {
1030 L_WARNING("no interpolation for 1 bpp; using sampling\n", __func__);
1031 operation = L_SAMPLED;
1032 }
1033
1034 if (operation == L_SAMPLED)
1035 return pixQuadraticVShearSampled(pixs, dir, vmaxt, vmaxb, incolor);
1036 else /* operation == L_INTERPOLATED */
1037 return pixQuadraticVShearLI(pixs, dir, vmaxt, vmaxb, incolor);
1038 }
1039
1040
1041 /*!
1042 * \brief pixQuadraticVShearSampled()
1043 *
1044 * \param[in] pixs 1, 8 or 32 bpp
1045 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT
1046 * \param[in] vmaxt max vertical displacement at edge and at top
1047 * \param[in] vmaxb max vertical displacement at edge and at bottom
1048 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK
1049 * \return pixd stretched, or NULL on error
1050 *
1051 * <pre>
1052 * Notes:
1053 * (1) See pixQuadraticVShear() for details.
1054 * </pre>
1055 */
1056 PIX *
1057 pixQuadraticVShearSampled(PIX *pixs,
1058 l_int32 dir,
1059 l_int32 vmaxt,
1060 l_int32 vmaxb,
1061 l_int32 incolor)
1062 {
1063 l_int32 i, j, id, w, h, d, wm, hm, wpls, wpld, val;
1064 l_uint32 *datas, *datad, *lines, *lined;
1065 l_float32 delrowt, delrowb, denom1, denom2, dely;
1066 PIX *pixd;
1067
1068 if (!pixs)
1069 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1070 pixGetDimensions(pixs, &w, &h, &d);
1071 if (d != 1 && d != 8 && d != 32)
1072 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", __func__, NULL);
1073 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT)
1074 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL);
1075 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
1076 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
1077
1078 if (vmaxt == 0 && vmaxb == 0)
1079 return pixCopy(NULL, pixs);
1080
1081 pixd = pixCreateTemplate(pixs);
1082 pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE);
1083 datas = pixGetData(pixs);
1084 datad = pixGetData(pixd);
1085 wpls = pixGetWpl(pixs);
1086 wpld = pixGetWpl(pixd);
1087 wm = w - 1;
1088 hm = h - 1;
1089 denom1 = 1.f / (l_float32)h;
1090 denom2 = 1.f / (l_float32)(wm * wm);
1091 for (j = 0; j < w; j++) {
1092 if (dir == L_WARP_TO_LEFT) {
1093 delrowt = (l_float32)(vmaxt * (wm - j) * (wm - j)) * denom2;
1094 delrowb = (l_float32)(vmaxb * (wm - j) * (wm - j)) * denom2;
1095 } else if (dir == L_WARP_TO_RIGHT) {
1096 delrowt = (l_float32)(vmaxt * j * j) * denom2;
1097 delrowb = (l_float32)(vmaxb * j * j) * denom2;
1098 }
1099 switch (d)
1100 {
1101 case 1:
1102 for (id = 0; id < h; id++) {
1103 dely = (delrowt * (hm - id) + delrowb * id) * denom1;
1104 i = id - (l_int32)(dely + 0.5);
1105 if (i < 0 || i > hm) continue;
1106 lines = datas + i * wpls;
1107 lined = datad + id * wpld;
1108 val = GET_DATA_BIT(lines, j);
1109 if (val)
1110 SET_DATA_BIT(lined, j);
1111 }
1112 break;
1113 case 8:
1114 for (id = 0; id < h; id++) {
1115 dely = (delrowt * (hm - id) + delrowb * id) * denom1;
1116 i = id - (l_int32)(dely + 0.5);
1117 if (i < 0 || i > hm) continue;
1118 lines = datas + i * wpls;
1119 lined = datad + id * wpld;
1120 val = GET_DATA_BYTE(lines, j);
1121 SET_DATA_BYTE(lined, j, val);
1122 }
1123 break;
1124 case 32:
1125 for (id = 0; id < h; id++) {
1126 dely = (delrowt * (hm - id) + delrowb * id) * denom1;
1127 i = id - (l_int32)(dely + 0.5);
1128 if (i < 0 || i > hm) continue;
1129 lines = datas + i * wpls;
1130 lined = datad + id * wpld;
1131 lined[j] = lines[j];
1132 }
1133 break;
1134 default:
1135 L_ERROR("invalid depth: %d\n", __func__, d);
1136 pixDestroy(&pixd);
1137 return NULL;
1138 }
1139 }
1140
1141 return pixd;
1142 }
1143
1144
1145 /*!
1146 * \brief pixQuadraticVShearLI()
1147 *
1148 * \param[in] pixs 8 or 32 bpp, or colormapped
1149 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT
1150 * \param[in] vmaxt max vertical displacement at edge and at top
1151 * \param[in] vmaxb max vertical displacement at edge and at bottom
1152 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK
1153 * \return pixd stretched, or NULL on error
1154 *
1155 * <pre>
1156 * Notes:
1157 * (1) See pixQuadraticVShear() for details.
1158 * </pre>
1159 */
1160 PIX *
1161 pixQuadraticVShearLI(PIX *pixs,
1162 l_int32 dir,
1163 l_int32 vmaxt,
1164 l_int32 vmaxb,
1165 l_int32 incolor)
1166 {
1167 l_int32 i, j, id, yp, yf, w, h, d, wm, hm, wpls, wpld;
1168 l_int32 val, rval, gval, bval;
1169 l_uint32 word0, word1;
1170 l_uint32 *datas, *datad, *lines, *lined;
1171 l_float32 delrowt, delrowb, denom1, denom2, dely;
1172 PIX *pix, *pixd;
1173 PIXCMAP *cmap;
1174
1175 if (!pixs)
1176 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1177 pixGetDimensions(pixs, &w, &h, &d);
1178 if (d == 1)
1179 return (PIX *)ERROR_PTR("pixs is 1 bpp", __func__, NULL);
1180 cmap = pixGetColormap(pixs);
1181 if (d != 8 && d != 32 && !cmap)
1182 return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", __func__, NULL);
1183 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT)
1184 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL);
1185 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
1186 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
1187
1188 if (vmaxt == 0 && vmaxb == 0)
1189 return pixCopy(NULL, pixs);
1190
1191 /* Remove any existing colormap */
1192 if (cmap)
1193 pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
1194 else
1195 pix = pixClone(pixs);
1196 d = pixGetDepth(pix);
1197 if (d != 8 && d != 32) {
1198 pixDestroy(&pix);
1199 return (PIX *)ERROR_PTR("invalid depth", __func__, NULL);
1200 }
1201
1202 /* Standard linear interp: subdivide each pixel into 64 parts */
1203 pixd = pixCreateTemplate(pix);
1204 pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE);
1205 datas = pixGetData(pix);
1206 datad = pixGetData(pixd);
1207 wpls = pixGetWpl(pix);
1208 wpld = pixGetWpl(pixd);
1209 wm = w - 1;
1210 hm = h - 1;
1211 denom1 = 1.0f / (l_float32)h;
1212 denom2 = 1.0f / (l_float32)(wm * wm);
1213 for (j = 0; j < w; j++) {
1214 if (dir == L_WARP_TO_LEFT) {
1215 delrowt = (l_float32)(vmaxt * (wm - j) * (wm - j)) * denom2;
1216 delrowb = (l_float32)(vmaxb * (wm - j) * (wm - j)) * denom2;
1217 } else if (dir == L_WARP_TO_RIGHT) {
1218 delrowt = (l_float32)(vmaxt * j * j) * denom2;
1219 delrowb = (l_float32)(vmaxb * j * j) * denom2;
1220 }
1221 switch (d)
1222 {
1223 case 8:
1224 for (id = 0; id < h; id++) {
1225 dely = (delrowt * (hm - id) + delrowb * id) * denom1;
1226 i = 64 * id - (l_int32)(64.0 * dely);
1227 yp = i / 64;
1228 yf = i & 63;
1229 if (yp < 0 || yp > hm) continue;
1230 lines = datas + yp * wpls;
1231 lined = datad + id * wpld;
1232 if (yp < hm) {
1233 val = ((63 - yf) * GET_DATA_BYTE(lines, j) +
1234 yf * GET_DATA_BYTE(lines + wpls, j) + 31) / 63;
1235 } else { /* yp == hm */
1236 val = GET_DATA_BYTE(lines, j);
1237 }
1238 SET_DATA_BYTE(lined, j, val);
1239 }
1240 break;
1241 case 32:
1242 for (id = 0; id < h; id++) {
1243 dely = (delrowt * (hm - id) + delrowb * id) * denom1;
1244 i = 64 * id - (l_int32)(64.0 * dely);
1245 yp = i / 64;
1246 yf = i & 63;
1247 if (yp < 0 || yp > hm) continue;
1248 lines = datas + yp * wpls;
1249 lined = datad + id * wpld;
1250 if (yp < hm) {
1251 word0 = *(lines + j);
1252 word1 = *(lines + wpls + j);
1253 rval = ((63 - yf) * ((word0 >> L_RED_SHIFT) & 0xff) +
1254 yf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63;
1255 gval = ((63 - yf) * ((word0 >> L_GREEN_SHIFT) & 0xff) +
1256 yf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63;
1257 bval = ((63 - yf) * ((word0 >> L_BLUE_SHIFT) & 0xff) +
1258 yf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63;
1259 composeRGBPixel(rval, gval, bval, lined + j);
1260 } else { /* yp == hm */
1261 lined[j] = lines[j];
1262 }
1263 }
1264 break;
1265 default:
1266 L_ERROR("invalid depth: %d\n", __func__, d);
1267 pixDestroy(&pix);
1268 pixDestroy(&pixd);
1269 return NULL;
1270 }
1271 }
1272
1273 pixDestroy(&pix);
1274 return pixd;
1275 }
1276
1277
1278 /*----------------------------------------------------------------------*
1279 * Stereo from a pair of images *
1280 *----------------------------------------------------------------------*/
1281 /*!
1282 * \brief pixStereoFromPair()
1283 *
1284 * \param[in] pix1 32 bpp rgb
1285 * \param[in] pix2 32 bpp rgb
1286 * \param[in] rwt, gwt, bwt weighting factors used for each component in
1287 pix1 to determine the output red channel
1288 * \return pixd stereo enhanced, or NULL on error
1289 *
1290 * <pre>
1291 * Notes:
1292 * (1) pix1 and pix2 are a pair of stereo images, ideally taken
1293 * concurrently in the same plane, with some lateral translation.
1294 * (2) The output red channel is determined from %pix1.
1295 * The output green and blue channels are taken from the green
1296 * and blue channels, respectively, of %pix2.
1297 * (3) The weights determine how much of each component in %pix1
1298 * goes into the output red channel. The sum of weights
1299 * must be 1.0. If it's not, we scale the weights to
1300 * satisfy this criterion.
1301 * (4) The most general pixel mapping allowed here is:
1302 * rval = rwt * r1 + gwt * g1 + bwt * b1 (from pix1)
1303 * gval = g2 (from pix2)
1304 * bval = b2 (from pix2)
1305 * (5) The simplest method is to use rwt = 1.0, gwt = 0.0, bwt = 0.0,
1306 * but this causes unpleasant visual artifacts with red in the image.
1307 * Use of green and blue from %pix1 in the red channel,
1308 * instead of red, tends to fix that problem.
1309 * </pre>
1310 */
1311 PIX *
1312 pixStereoFromPair(PIX *pix1,
1313 PIX *pix2,
1314 l_float32 rwt,
1315 l_float32 gwt,
1316 l_float32 bwt)
1317 {
1318 l_int32 i, j, w, h, wpl1, wpl2, rval, gval, bval;
1319 l_uint32 word1, word2;
1320 l_uint32 *data1, *data2, *datad, *line1, *line2, *lined;
1321 l_float32 sum;
1322 PIX *pixd;
1323
1324 if (!pix1 || !pix2)
1325 return (PIX *)ERROR_PTR("pix1, pix2 not both defined", __func__, NULL);
1326 if (pixGetDepth(pix1) != 32 || pixGetDepth(pix2) != 32)
1327 return (PIX *)ERROR_PTR("pix1, pix2 not both 32 bpp", __func__, NULL);
1328
1329 /* Make sure the sum of weights is 1.0; otherwise, you can get
1330 * overflow in the gray value. */
1331 if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) {
1332 rwt = DefaultRedWeight;
1333 gwt = DefaultGreenWeight;
1334 bwt = DefaultBlueWeight;
1335 }
1336 sum = rwt + gwt + bwt;
1337 if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */
1338 L_WARNING("weights don't sum to 1; maintaining ratios\n", __func__);
1339 rwt = rwt / sum;
1340 gwt = gwt / sum;
1341 bwt = bwt / sum;
1342 }
1343
1344 pixGetDimensions(pix1, &w, &h, NULL);
1345 pixd = pixCreateTemplate(pix1);
1346 data1 = pixGetData(pix1);
1347 data2 = pixGetData(pix2);
1348 datad = pixGetData(pixd);
1349 wpl1 = pixGetWpl(pix1);
1350 wpl2 = pixGetWpl(pix2);
1351 for (i = 0; i < h; i++) {
1352 line1 = data1 + i * wpl1;
1353 line2 = data2 + i * wpl2;
1354 lined = datad + i * wpl1; /* wpl1 works for pixd */
1355 for (j = 0; j < w; j++) {
1356 word1 = *(line1 + j);
1357 word2 = *(line2 + j);
1358 rval = (l_int32)(rwt * ((word1 >> L_RED_SHIFT) & 0xff) +
1359 gwt * ((word1 >> L_GREEN_SHIFT) & 0xff) +
1360 bwt * ((word1 >> L_BLUE_SHIFT) & 0xff) + 0.5);
1361 gval = (word2 >> L_GREEN_SHIFT) & 0xff;
1362 bval = (word2 >> L_BLUE_SHIFT) & 0xff;
1363 composeRGBPixel(rval, gval, bval, lined + j);
1364 }
1365 }
1366
1367 return pixd;
1368 }