Coverage Report

Created: 2024-06-18 06:05

/src/leptonica/src/affine.c
Line
Count
Source (jump to first uncovered line)
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
/*!
29
 * \file affine.c
30
 * <pre>
31
 *
32
 *      Affine (3 pt) image transformation using a sampled
33
 *      (to nearest integer) transform on each dest point
34
 *           PIX        *pixAffineSampledPta()
35
 *           PIX        *pixAffineSampled()
36
 *
37
 *      Affine (3 pt) image transformation using interpolation
38
 *      (or area mapping) for anti-aliasing images that are
39
 *      2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB
40
 *           PIX        *pixAffinePta()
41
 *           PIX        *pixAffine()
42
 *           PIX        *pixAffinePtaColor()
43
 *           PIX        *pixAffineColor()
44
 *           PIX        *pixAffinePtaGray()
45
 *           PIX        *pixAffineGray()
46
 *
47
 *      Affine transform including alpha (blend) component
48
 *           PIX        *pixAffinePtaWithAlpha()
49
 *
50
 *      Affine coordinate transformation
51
 *           l_int32     getAffineXformCoeffs()
52
 *           l_int32     affineInvertXform()
53
 *           l_int32     affineXformSampledPt()
54
 *           l_int32     affineXformPt()
55
 *
56
 *      Interpolation helper functions
57
 *           l_int32     linearInterpolatePixelGray()
58
 *           l_int32     linearInterpolatePixelColor()
59
 *
60
 *      Gauss-jordan linear equation solver
61
 *           l_int32     gaussjordan()
62
 *
63
 *      Affine image transformation using a sequence of
64
 *      shear/scale/translation operations
65
 *           PIX        *pixAffineSequential()
66
 *
67
 *      One can define a coordinate space by the location of the origin,
68
 *      the orientation of x and y axes, and the unit scaling along
69
 *      each axis.  An affine transform is a general linear
70
 *      transformation from one coordinate space to another.
71
 *
72
 *      For the general case, we can define the affine transform using
73
 *      two sets of three (noncollinear) points in a plane.  One set
74
 *      corresponds to the input (src) coordinate space; the other to the
75
 *      transformed (dest) coordinate space.  Each point in the
76
 *      src corresponds to one of the points in the dest.  With two
77
 *      sets of three points, we get a set of 6 equations in 6 unknowns
78
 *      that specifies the mapping between the coordinate spaces.
79
 *      The interface here allows you to specify either the corresponding
80
 *      sets of 3 points, or the transform itself (as a vector of 6
81
 *      coefficients).
82
 *
83
 *      Given the transform as a vector of 6 coefficients, we can compute
84
 *      both a a pointwise affine coordinate transformation and an
85
 *      affine image transformation.
86
 *
87
 *      To compute the coordinate transform, we need the coordinate
88
 *      value (x',y') in the transformed space for any point (x,y)
89
 *      in the original space.  To derive this transform from the
90
 *      three corresponding points, it is convenient to express the affine
91
 *      coordinate transformation using an LU decomposition of
92
 *      a set of six linear equations that express the six coordinates
93
 *      of the three points in the transformed space as a function of
94
 *      the six coordinates in the original space.  Once we have
95
 *      this transform matrix , we can transform an image by
96
 *      finding, for each destination pixel, the pixel (or pixels)
97
 *      in the source that give rise to it.
98
 *
99
 *      This 'pointwise' transformation can be done either by sampling
100
 *      and picking a single pixel in the src to replicate into the dest,
101
 *      or by interpolating (or averaging) over four src pixels to
102
 *      determine the value of the dest pixel.  The first method is
103
 *      implemented by pixAffineSampled() and the second method by
104
 *      pixAffine().  The interpolated method can only be used for
105
 *      images with more than 1 bpp, but for these, the image quality
106
 *      is significantly better than the sampled method, due to
107
 *      the 'antialiasing' effect of weighting the src pixels.
108
 *
109
 *      Interpolation works well when there is relatively little scaling,
110
 *      or if there is image expansion in general.  However, if there
111
 *      is significant image reduction, one should apply a low-pass
112
 *      filter before subsampling to avoid aliasing the high frequencies.
113
 *
114
 *      A typical application might be to align two images, which
115
 *      may be scaled, rotated and translated versions of each other.
116
 *      Through some pre-processing, three corresponding points are
117
 *      located in each of the two images.  One of the images is
118
 *      then to be (affine) transformed to align with the other.
119
 *      As mentioned, the standard way to do this is to use three
120
 *      sets of points, compute the 6 transformation coefficients
121
 *      from these points that describe the linear transformation,
122
 *
123
 *          x' = ax + by + c
124
 *          y' = dx + ey + f
125
 *
126
 *      and use this in a pointwise manner to transform the image.
127
 *
128
 *      N.B.  Be sure to see the comment in getAffineXformCoeffs(),
129
 *      regarding using the inverse of the affine transform for points
130
 *      to transform images.
131
 *
132
 *      There is another way to do this transformation; namely,
133
 *      by doing a sequence of simple affine transforms, without
134
 *      computing directly the affine coordinate transformation.
135
 *      We have at our disposal (1) translations (using rasterop),
136
 *      (2) horizontal and vertical shear about any horizontal and vertical
137
 *      line, respectively, and (3) non-isotropic scaling by two
138
 *      arbitrary x and y scaling factors.  We also have rotation
139
 *      about an arbitrary point, but this is equivalent to a set
140
 *      of three shears so we do not need to use it.
141
 *
142
 *      Why might we do this?  For binary images, it is usually
143
 *      more efficient to do such transformations by a sequence
144
 *      of word parallel operations.  Shear and translation can be
145
 *      done in-place and word parallel; arbitrary scaling is
146
 *      mostly pixel-wise.
147
 *
148
 *      Suppose that we are transforming image 1 to correspond to image 2.
149
 *      We have a set of three points, describing the coordinate space
150
 *      embedded in image 1, and we need to transform image 1 until
151
 *      those three points exactly correspond to the new coordinate space
152
 *      defined by the second set of three points.  In our image
153
 *      matching application, the latter set of three points was
154
 *      found to be the corresponding points in image 2.
155
 *
156
 *      The most elegant way I can think of to do such a sequential
157
 *      implementation is to imagine that we're going to transform
158
 *      BOTH images until they're aligned.  (We don't really want
159
 *      to transform both, because in fact we may only have one image
160
 *      that is undergoing a general affine transformation.)
161
 *
162
 *      Choose the 3 corresponding points as follows:
163
 *         ~ The 1st point is an origin
164
 *         ~ The 2nd point gives the orientation and scaling of the
165
 *           "x" axis with respect to the origin
166
 *         ~ The 3rd point does likewise for the "y" axis.
167
 *      These "axes" must not be collinear; otherwise they are
168
 *      arbitrary (although some strange things will happen if
169
 *      the handedness sweeping through the minimum angle between
170
 *      the axes is opposite).
171
 *
172
 *      An important constraint is that we have shear operations
173
 *      about an arbitrary horizontal or vertical line, but always
174
 *      parallel to the x or y axis.  If we continue to pretend that
175
 *      we have an unprimed coordinate space embedded in image 1 and
176
 *      a primed coordinate space embedded in image 2, we imagine
177
 *      (a) transforming image 1 by horizontal and vertical shears about
178
 *      point 1 to align points 3 and 2 along the y and x axes,
179
 *      respectively, and (b) transforming image 2 by horizontal and
180
 *      vertical shears about point 1' to align points 3' and 2' along
181
 *      the y and x axes.  Then we scale image 1 so that the distances
182
 *      from 1 to 2 and from 1 to 3 are equal to the distances in
183
 *      image 2 from 1' to 2' and from 1' to 3'.  This scaling operation
184
 *      leaves the true image origin, at (0,0) invariant, and will in
185
 *      general translate point 1.  The original points 1 and 1' will
186
 *      typically not coincide in any event, so we must translate
187
 *      the origin of image 1, at its current point 1, to the origin
188
 *      of image 2 at 1'.  The images should now be aligned.  But
189
 *      because we never really transformed image 2 (and image 2 may
190
 *      not even exist), we now perform  on image 1 the reverse of
191
 *      the shear transforms that we imagined doing on image 2;
192
 *      namely, the negative vertical shear followed by the negative
193
 *      horizontal shear.  Image 1 should now have its transformed
194
 *      unprimed coordinates aligned with the original primed
195
 *      coordinates.  In all this, it is only necessary to keep track
196
 *      of the shear angles and translations of points during the shears.
197
 *      What has been accomplished is a general affine transformation
198
 *      on image 1.
199
 *
200
 *      Having described all this, if you are going to use an
201
 *      affine transformation in an application, this is what you
202
 *      need to know:
203
 *
204
 *          (1) You should NEVER use the sequential method, because
205
 *              the image quality for 1 bpp text is much poorer
206
 *              (even though it is about 2x faster than the pointwise sampled
207
 *              method), and for images with depth greater than 1, it is
208
 *              nearly 20x slower than the pointwise sampled method
209
 *              and over 10x slower than the pointwise interpolated method!
210
 *              The sequential method is given here for purely
211
 *              pedagogical reasons.
212
 *
213
 *          (2) For 1 bpp images, use the pointwise sampled function
214
 *              pixAffineSampled().  For all other images, the best
215
 *              quality results result from using the pointwise
216
 *              interpolated function pixAffinePta() or pixAffine();
217
 *              the cost is less than a doubling of the computation time
218
 *              with respect to the sampled function.  If you use
219
 *              interpolation on colormapped images, the colormap will
220
 *              be removed, resulting in either a grayscale or color
221
 *              image, depending on the values in the colormap.
222
 *              If you want to retain the colormap, use pixAffineSampled().
223
 *
224
 *      Typical relative timing of pointwise transforms (sampled = 1.0):
225
 *      8 bpp:   sampled        1.0
226
 *               interpolated   1.6
227
 *      32 bpp:  sampled        1.0
228
 *               interpolated   1.8
229
 *      Additionally, the computation time/pixel is nearly the same
230
 *      for 8 bpp and 32 bpp, for both sampled and interpolated.
231
 * </pre>
232
 */
233
234
#ifdef HAVE_CONFIG_H
235
#include <config_auto.h>
236
#endif  /* HAVE_CONFIG_H */
237
238
#include <string.h>
239
#include <math.h>
240
#include "allheaders.h"
241
242
extern l_float32  AlphaMaskBorderVals[2];
243
244
#ifndef  NO_CONSOLE_IO
245
#define  DEBUG     0
246
#endif  /* ~NO_CONSOLE_IO */
247
248
/*-------------------------------------------------------------*
249
 *               Sampled affine image transformation           *
250
 *-------------------------------------------------------------*/
251
/*!
252
 * \brief   pixAffineSampledPta()
253
 *
254
 * \param[in]    pixs      all depths
255
 * \param[in]    ptad      3 pts of final coordinate space
256
 * \param[in]    ptas      3 pts of initial coordinate space
257
 * \param[in]    incolor   L_BRING_IN_WHITE, L_BRING_IN_BLACK
258
 * \return  pixd, or NULL on error
259
 *
260
 * <pre>
261
 * Notes:
262
 *      (1) Brings in either black or white pixels from the boundary.
263
 *      (2) Retains colormap, which you can do for a sampled transform..
264
 *      (3) The 3 points must not be collinear.
265
 *      (4) The order of the 3 points is arbitrary; however, to compare
266
 *          with the sequential transform they must be in these locations
267
 *          and in this order: origin, x-axis, y-axis.
268
 *      (5) For 1 bpp images, this has much better quality results
269
 *          than pixAffineSequential(), particularly for text.
270
 *          It is about 3x slower, but does not require additional
271
 *          border pixels.  The poor quality of pixAffineSequential()
272
 *          is due to repeated quantized transforms.  It is strongly
273
 *          recommended that pixAffineSampled() be used for 1 bpp images.
274
 *      (6) For 8 or 32 bpp, much better quality is obtained by the
275
 *          somewhat slower pixAffinePta().  See that function
276
 *          for relative timings between sampled and interpolated.
277
 *      (7) To repeat, use of the sequential transform,
278
 *          pixAffineSequential(), for any images, is discouraged.
279
 * </pre>
280
 */
281
PIX *
282
pixAffineSampledPta(PIX     *pixs,
283
                    PTA     *ptad,
284
                    PTA     *ptas,
285
                    l_int32  incolor)
286
0
{
287
0
l_float32  *vc;
288
0
PIX        *pixd;
289
290
0
    if (!pixs)
291
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
292
0
    if (!ptas)
293
0
        return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
294
0
    if (!ptad)
295
0
        return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
296
0
    if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
297
0
        return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
298
0
    if (ptaGetCount(ptas) != 3)
299
0
        return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL);
300
0
    if (ptaGetCount(ptad) != 3)
301
0
        return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL);
302
303
        /* Get backwards transform from dest to src, and apply it */
304
0
    getAffineXformCoeffs(ptad, ptas, &vc);
305
0
    pixd = pixAffineSampled(pixs, vc, incolor);
306
0
    LEPT_FREE(vc);
307
308
0
    return pixd;
309
0
}
310
311
312
/*!
313
 * \brief   pixAffineSampled()
314
 *
315
 * \param[in]    pixs      all depths
316
 * \param[in]    vc        vector of 6 coefficients for affine transformation
317
 * \param[in]    incolor   L_BRING_IN_WHITE, L_BRING_IN_BLACK
318
 * \return  pixd, or NULL on error
319
 *
320
 * <pre>
321
 * Notes:
322
 *      (1) Brings in either black or white pixels from the boundary.
323
 *      (2) Retains colormap, which you can do for a sampled transform..
324
 *      (3) For 8 or 32 bpp, much better quality is obtained by the
325
 *          somewhat slower pixAffine().  See that function
326
 *          for relative timings between sampled and interpolated.
327
 * </pre>
328
 */
329
PIX *
330
pixAffineSampled(PIX        *pixs,
331
                 l_float32  *vc,
332
                 l_int32     incolor)
333
0
{
334
0
l_int32     i, j, w, h, d, x, y, wpls, wpld, color, cmapindex;
335
0
l_uint32    val;
336
0
l_uint32   *datas, *datad, *lines, *lined;
337
0
PIX        *pixd;
338
0
PIXCMAP    *cmap;
339
340
0
    if (!pixs)
341
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
342
0
    if (!vc)
343
0
        return (PIX *)ERROR_PTR("vc not defined", __func__, NULL);
344
0
    if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
345
0
        return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
346
0
    pixGetDimensions(pixs, &w, &h, &d);
347
0
    if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32)
348
0
        return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", __func__, NULL);
349
350
        /* Init all dest pixels to color to be brought in from outside */
351
0
    pixd = pixCreateTemplate(pixs);
352
0
    if ((cmap = pixGetColormap(pixs)) != NULL) {
353
0
        if (incolor == L_BRING_IN_WHITE)
354
0
            color = 1;
355
0
        else
356
0
            color = 0;
357
0
        pixcmapAddBlackOrWhite(cmap, color, &cmapindex);
358
0
        pixSetAllArbitrary(pixd, cmapindex);
359
0
    } else {
360
0
        if ((d == 1 && incolor == L_BRING_IN_WHITE) ||
361
0
            (d > 1 && incolor == L_BRING_IN_BLACK)) {
362
0
            pixClearAll(pixd);
363
0
        } else {
364
0
            pixSetAll(pixd);
365
0
        }
366
0
    }
367
368
        /* Scan over the dest pixels */
369
0
    datas = pixGetData(pixs);
370
0
    wpls = pixGetWpl(pixs);
371
0
    datad = pixGetData(pixd);
372
0
    wpld = pixGetWpl(pixd);
373
0
    for (i = 0; i < h; i++) {
374
0
        lined = datad + i * wpld;
375
0
        for (j = 0; j < w; j++) {
376
0
            affineXformSampledPt(vc, j, i, &x, &y);
377
0
            if (x < 0 || y < 0 || x >=w || y >= h)
378
0
                continue;
379
0
            lines = datas + y * wpls;
380
0
            if (d == 1) {
381
0
                val = GET_DATA_BIT(lines, x);
382
0
                SET_DATA_BIT_VAL(lined, j, val);
383
0
            } else if (d == 8) {
384
0
                val = GET_DATA_BYTE(lines, x);
385
0
                SET_DATA_BYTE(lined, j, val);
386
0
            } else if (d == 32) {
387
0
                lined[j] = lines[x];
388
0
            } else if (d == 2) {
389
0
                val = GET_DATA_DIBIT(lines, x);
390
0
                SET_DATA_DIBIT(lined, j, val);
391
0
            } else if (d == 4) {
392
0
                val = GET_DATA_QBIT(lines, x);
393
0
                SET_DATA_QBIT(lined, j, val);
394
0
            }
395
0
        }
396
0
    }
397
398
0
    return pixd;
399
0
}
400
401
402
/*---------------------------------------------------------------------*
403
 *               Interpolated affine image transformation              *
404
 *---------------------------------------------------------------------*/
405
/*!
406
 * \brief   pixAffinePta()
407
 *
408
 * \param[in]    pixs      all depths; colormap ok
409
 * \param[in]    ptad      3 pts of final coordinate space
410
 * \param[in]    ptas      3 pts of initial coordinate space
411
 * \param[in]    incolor   L_BRING_IN_WHITE, L_BRING_IN_BLACK
412
 * \return  pixd, or NULL on error
413
 *
414
 * <pre>
415
 * Notes:
416
 *      (1) Brings in either black or white pixels from the boundary
417
 *      (2) Removes any existing colormap, if necessary, before transforming
418
 * </pre>
419
 */
420
PIX *
421
pixAffinePta(PIX     *pixs,
422
             PTA     *ptad,
423
             PTA     *ptas,
424
             l_int32  incolor)
425
0
{
426
0
l_int32   d;
427
0
l_uint32  colorval;
428
0
PIX      *pixt1, *pixt2, *pixd;
429
430
0
    if (!pixs)
431
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
432
0
    if (!ptas)
433
0
        return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
434
0
    if (!ptad)
435
0
        return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
436
0
    if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
437
0
        return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
438
0
    if (ptaGetCount(ptas) != 3)
439
0
        return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL);
440
0
    if (ptaGetCount(ptad) != 3)
441
0
        return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL);
442
443
0
    if (pixGetDepth(pixs) == 1)
444
0
        return pixAffineSampledPta(pixs, ptad, ptas, incolor);
445
446
        /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
447
0
    pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
448
0
    d = pixGetDepth(pixt1);
449
0
    if (d < 8)
450
0
        pixt2 = pixConvertTo8(pixt1, FALSE);
451
0
    else
452
0
        pixt2 = pixClone(pixt1);
453
0
    d = pixGetDepth(pixt2);
454
455
        /* Compute actual color to bring in from edges */
456
0
    colorval = 0;
457
0
    if (incolor == L_BRING_IN_WHITE) {
458
0
        if (d == 8)
459
0
            colorval = 255;
460
0
        else  /* d == 32 */
461
0
            colorval = 0xffffff00;
462
0
    }
463
464
0
    if (d == 8)
465
0
        pixd = pixAffinePtaGray(pixt2, ptad, ptas, colorval);
466
0
    else  /* d == 32 */
467
0
        pixd = pixAffinePtaColor(pixt2, ptad, ptas, colorval);
468
0
    pixDestroy(&pixt1);
469
0
    pixDestroy(&pixt2);
470
0
    return pixd;
471
0
}
472
473
474
/*!
475
 * \brief   pixAffine()
476
 *
477
 * \param[in]    pixs      all depths; colormap ok
478
 * \param[in]    vc        vector of 6 coefficients for affine transformation
479
 * \param[in]    incolor   L_BRING_IN_WHITE, L_BRING_IN_BLACK
480
 * \return  pixd, or NULL on error
481
 *
482
 * <pre>
483
 * Notes:
484
 *      (1) Brings in either black or white pixels from the boundary
485
 *      (2) Removes any existing colormap, if necessary, before transforming
486
 * </pre>
487
 */
488
PIX *
489
pixAffine(PIX        *pixs,
490
          l_float32  *vc,
491
          l_int32     incolor)
492
0
{
493
0
l_int32   d;
494
0
l_uint32  colorval;
495
0
PIX      *pixt1, *pixt2, *pixd;
496
497
0
    if (!pixs)
498
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
499
0
    if (!vc)
500
0
        return (PIX *)ERROR_PTR("vc not defined", __func__, NULL);
501
502
0
    if (pixGetDepth(pixs) == 1)
503
0
        return pixAffineSampled(pixs, vc, incolor);
504
505
        /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
506
0
    pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
507
0
    d = pixGetDepth(pixt1);
508
0
    if (d < 8)
509
0
        pixt2 = pixConvertTo8(pixt1, FALSE);
510
0
    else
511
0
        pixt2 = pixClone(pixt1);
512
0
    d = pixGetDepth(pixt2);
513
514
        /* Compute actual color to bring in from edges */
515
0
    colorval = 0;
516
0
    if (incolor == L_BRING_IN_WHITE) {
517
0
        if (d == 8)
518
0
            colorval = 255;
519
0
        else  /* d == 32 */
520
0
            colorval = 0xffffff00;
521
0
    }
522
523
0
    if (d == 8)
524
0
        pixd = pixAffineGray(pixt2, vc, colorval);
525
0
    else  /* d == 32 */
526
0
        pixd = pixAffineColor(pixt2, vc, colorval);
527
0
    pixDestroy(&pixt1);
528
0
    pixDestroy(&pixt2);
529
0
    return pixd;
530
0
}
531
532
533
/*!
534
 * \brief   pixAffinePtaColor()
535
 *
536
 * \param[in]    pixs       32 bpp
537
 * \param[in]    ptad       3 pts of final coordinate space
538
 * \param[in]    ptas       3 pts of initial coordinate space
539
 * \param[in]    colorval   e.g.: 0 to bring in BLACK, 0xffffff00 for WHITE
540
 * \return  pixd, or NULL on error
541
 */
542
PIX *
543
pixAffinePtaColor(PIX      *pixs,
544
                  PTA      *ptad,
545
                  PTA      *ptas,
546
                  l_uint32  colorval)
547
0
{
548
0
l_float32  *vc;
549
0
PIX        *pixd;
550
551
0
    if (!pixs)
552
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
553
0
    if (!ptas)
554
0
        return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
555
0
    if (!ptad)
556
0
        return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
557
0
    if (pixGetDepth(pixs) != 32)
558
0
        return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
559
0
    if (ptaGetCount(ptas) != 3)
560
0
        return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL);
561
0
    if (ptaGetCount(ptad) != 3)
562
0
        return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL);
563
564
        /* Get backwards transform from dest to src, and apply it */
565
0
    getAffineXformCoeffs(ptad, ptas, &vc);
566
0
    pixd = pixAffineColor(pixs, vc, colorval);
567
0
    LEPT_FREE(vc);
568
569
0
    return pixd;
570
0
}
571
572
573
/*!
574
 * \brief   pixAffineColor()
575
 *
576
 * \param[in]    pixs       32 bpp
577
 * \param[in]    vc         vector of 6 coefficients for affine transformation
578
 * \param[in]    colorval   e.g.: 0 to bring in BLACK, 0xffffff00 for WHITE
579
 * \return  pixd, or NULL on error
580
 */
581
PIX *
582
pixAffineColor(PIX        *pixs,
583
               l_float32  *vc,
584
               l_uint32    colorval)
585
0
{
586
0
l_int32    i, j, w, h, d, wpls, wpld;
587
0
l_uint32   val;
588
0
l_uint32  *datas, *datad, *lined;
589
0
l_float32  x, y;
590
0
PIX       *pix1, *pix2, *pixd;
591
592
0
    if (!pixs)
593
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
594
0
    pixGetDimensions(pixs, &w, &h, &d);
595
0
    if (d != 32)
596
0
        return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
597
0
    if (!vc)
598
0
        return (PIX *)ERROR_PTR("vc not defined", __func__, NULL);
599
600
0
    datas = pixGetData(pixs);
601
0
    wpls = pixGetWpl(pixs);
602
0
    pixd = pixCreateTemplate(pixs);
603
0
    pixSetAllArbitrary(pixd, colorval);
604
0
    datad = pixGetData(pixd);
605
0
    wpld = pixGetWpl(pixd);
606
607
        /* Iterate over destination pixels */
608
0
    for (i = 0; i < h; i++) {
609
0
        lined = datad + i * wpld;
610
0
        for (j = 0; j < w; j++) {
611
                /* Compute float src pixel location corresponding to (i,j) */
612
0
            affineXformPt(vc, j, i, &x, &y);
613
0
            linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval,
614
0
                                        &val);
615
0
            *(lined + j) = val;
616
0
        }
617
0
    }
618
619
        /* If rgba, transform the pixs alpha channel and insert in pixd */
620
0
    if (pixGetSpp(pixs) == 4) {
621
0
        pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
622
0
        pix2 = pixAffineGray(pix1, vc, 255);  /* bring in opaque */
623
0
        pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
624
0
        pixDestroy(&pix1);
625
0
        pixDestroy(&pix2);
626
0
    }
627
628
0
    return pixd;
629
0
}
630
631
632
/*!
633
 * \brief   pixAffinePtaGray()
634
 *
635
 * \param[in]    pixs      8 bpp
636
 * \param[in]    ptad      3 pts of final coordinate space
637
 * \param[in]    ptas      3 pts of initial coordinate space
638
 * \param[in]    grayval   e.g.: 0 to bring in BLACK, 255 for WHITE
639
 * \return  pixd, or NULL on error
640
 */
641
PIX *
642
pixAffinePtaGray(PIX     *pixs,
643
                 PTA     *ptad,
644
                 PTA     *ptas,
645
                 l_uint8  grayval)
646
0
{
647
0
l_float32  *vc;
648
0
PIX        *pixd;
649
650
0
    if (!pixs)
651
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
652
0
    if (!ptas)
653
0
        return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
654
0
    if (!ptad)
655
0
        return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
656
0
    if (pixGetDepth(pixs) != 8)
657
0
        return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
658
0
    if (ptaGetCount(ptas) != 3)
659
0
        return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL);
660
0
    if (ptaGetCount(ptad) != 3)
661
0
        return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL);
662
663
        /* Get backwards transform from dest to src, and apply it */
664
0
    getAffineXformCoeffs(ptad, ptas, &vc);
665
0
    pixd = pixAffineGray(pixs, vc, grayval);
666
0
    LEPT_FREE(vc);
667
668
0
    return pixd;
669
0
}
670
671
672
673
/*!
674
 * \brief   pixAffineGray()
675
 *
676
 * \param[in]    pixs      8 bpp
677
 * \param[in]    vc        vector of 6 coefficients for affine transformation
678
 * \param[in]    grayval   e.g.: 0 to bring in BLACK, 255 for WHITE
679
 * \return  pixd, or NULL on error
680
 */
681
PIX *
682
pixAffineGray(PIX        *pixs,
683
              l_float32  *vc,
684
              l_uint8     grayval)
685
0
{
686
0
l_int32    i, j, w, h, wpls, wpld, val;
687
0
l_uint32  *datas, *datad, *lined;
688
0
l_float32  x, y;
689
0
PIX       *pixd;
690
691
0
    if (!pixs)
692
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
693
0
    pixGetDimensions(pixs, &w, &h, NULL);
694
0
    if (pixGetDepth(pixs) != 8)
695
0
        return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
696
0
    if (!vc)
697
0
        return (PIX *)ERROR_PTR("vc not defined", __func__, NULL);
698
699
0
    datas = pixGetData(pixs);
700
0
    wpls = pixGetWpl(pixs);
701
0
    pixd = pixCreateTemplate(pixs);
702
0
    pixSetAllArbitrary(pixd, grayval);
703
0
    datad = pixGetData(pixd);
704
0
    wpld = pixGetWpl(pixd);
705
706
        /* Iterate over destination pixels */
707
0
    for (i = 0; i < h; i++) {
708
0
        lined = datad + i * wpld;
709
0
        for (j = 0; j < w; j++) {
710
                /* Compute float src pixel location corresponding to (i,j) */
711
0
            affineXformPt(vc, j, i, &x, &y);
712
0
            linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val);
713
0
            SET_DATA_BYTE(lined, j, val);
714
0
        }
715
0
    }
716
717
0
    return pixd;
718
0
}
719
720
721
/*---------------------------------------------------------------------------*
722
 *            Affine transform including alpha (blend) component             *
723
 *---------------------------------------------------------------------------*/
724
/*!
725
 * \brief   pixAffinePtaWithAlpha()
726
 *
727
 * \param[in]    pixs     32 bpp rgb
728
 * \param[in]    ptad     3 pts of final coordinate space
729
 * \param[in]    ptas     3 pts of initial coordinate space
730
 * \param[in]    pixg     [optional] 8 bpp, can be null
731
 * \param[in]    fract    between 0.0 and 1.0, with 0.0 fully transparent
732
 *                        and 1.0 fully opaque
733
 * \param[in]    border   of pixels added to capture transformed source pixels
734
 * \return  pixd, or NULL on error
735
 *
736
 * <pre>
737
 * Notes:
738
 *      (1) The alpha channel is transformed separately from pixs,
739
 *          and aligns with it, being fully transparent outside the
740
 *          boundary of the transformed pixs.  For pixels that are fully
741
 *          transparent, a blending function like pixBlendWithGrayMask()
742
 *          will give zero weight to corresponding pixels in pixs.
743
 *      (2) If pixg is NULL, it is generated as an alpha layer that is
744
 *          partially opaque, using %fract.  Otherwise, it is cropped
745
 *          to pixs if required and %fract is ignored.  The alpha channel
746
 *          in pixs is never used.
747
 *      (3) Colormaps are removed.
748
 *      (4) When pixs is transformed, it doesn't matter what color is brought
749
 *          in because the alpha channel will be transparent (0) there.
750
 *      (5) To avoid losing source pixels in the destination, it may be
751
 *          necessary to add a border to the source pix before doing
752
 *          the affine transformation.  This can be any non-negative number.
753
 *      (6) The input %ptad and %ptas are in a coordinate space before
754
 *          the border is added.  Internally, we compensate for this
755
 *          before doing the affine transform on the image after the border
756
 *          is added.
757
 *      (7) The default setting for the border values in the alpha channel
758
 *          is 0 (transparent) for the outermost ring of pixels and
759
 *          (0.5 * fract * 255) for the second ring.  When blended over
760
 *          a second image, this
761
 *          (a) shrinks the visible image to make a clean overlap edge
762
 *              with an image below, and
763
 *          (b) softens the edges by weakening the aliasing there.
764
 *          Use l_setAlphaMaskBorder() to change these values.
765
 * </pre>
766
 */
767
PIX *
768
pixAffinePtaWithAlpha(PIX       *pixs,
769
                      PTA       *ptad,
770
                      PTA       *ptas,
771
                      PIX       *pixg,
772
                      l_float32  fract,
773
                      l_int32    border)
774
0
{
775
0
l_int32  ws, hs, d;
776
0
PIX     *pixd, *pixb1, *pixb2, *pixg2, *pixga;
777
0
PTA     *ptad2, *ptas2;
778
779
0
    if (!pixs)
780
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
781
0
    pixGetDimensions(pixs, &ws, &hs, &d);
782
0
    if (d != 32 && pixGetColormap(pixs) == NULL)
783
0
        return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL);
784
0
    if (pixg && pixGetDepth(pixg) != 8) {
785
0
        L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n",
786
0
                  __func__);
787
0
        pixg = NULL;
788
0
    }
789
0
    if (!pixg && (fract < 0.0 || fract > 1.0)) {
790
0
        L_WARNING("invalid fract; using 1.0 (fully transparent)\n", __func__);
791
0
        fract = 1.0;
792
0
    }
793
0
    if (!pixg && fract == 0.0)
794
0
        L_WARNING("fully opaque alpha; image will not be blended\n", __func__);
795
0
    if (!ptad)
796
0
        return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
797
0
    if (!ptas)
798
0
        return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
799
800
        /* Add border; the color doesn't matter */
801
0
    pixb1 = pixAddBorder(pixs, border, 0);
802
803
        /* Transform the ptr arrays to work on the bordered image */
804
0
    ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0);
805
0
    ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0);
806
807
        /* Do separate affine transform of rgb channels of pixs and of pixg */
808
0
    pixd = pixAffinePtaColor(pixb1, ptad2, ptas2, 0);
809
0
    if (!pixg) {
810
0
        pixg2 = pixCreate(ws, hs, 8);
811
0
        if (fract == 1.0)
812
0
            pixSetAll(pixg2);
813
0
        else
814
0
            pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract));
815
0
    } else {
816
0
        pixg2 = pixResizeToMatch(pixg, NULL, ws, hs);
817
0
    }
818
0
    if (ws > 10 && hs > 10) {  /* see note 7 */
819
0
        pixSetBorderRingVal(pixg2, 1,
820
0
                            (l_int32)(255.0 * fract * AlphaMaskBorderVals[0]));
821
0
        pixSetBorderRingVal(pixg2, 2,
822
0
                            (l_int32)(255.0 * fract * AlphaMaskBorderVals[1]));
823
824
0
    }
825
0
    pixb2 = pixAddBorder(pixg2, border, 0);  /* must be black border */
826
0
    pixga = pixAffinePtaGray(pixb2, ptad2, ptas2, 0);
827
0
    pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL);
828
0
    pixSetSpp(pixd, 4);
829
830
0
    pixDestroy(&pixg2);
831
0
    pixDestroy(&pixb1);
832
0
    pixDestroy(&pixb2);
833
0
    pixDestroy(&pixga);
834
0
    ptaDestroy(&ptad2);
835
0
    ptaDestroy(&ptas2);
836
0
    return pixd;
837
0
}
838
839
840
/*-------------------------------------------------------------*
841
 *                 Affine coordinate transformation            *
842
 *-------------------------------------------------------------*/
843
/*!
844
 * \brief   getAffineXformCoeffs()
845
 *
846
 * \param[in]    ptas    source 3 points; unprimed
847
 * \param[in]    ptad    transformed 3 points; primed
848
 * \param[out]   pvc     vector of coefficients of transform
849
 * \return  0 if OK; 1 on error
850
 *
851
 * <pre>
852
 *  We have a set of six equations, describing the affine
853
 *  transformation that takes 3 points ptas into 3 other
854
 *  points ptad.  These equations are:
855
 *
856
 *          x1' = c[0]*x1 + c[1]*y1 + c[2]
857
 *          y1' = c[3]*x1 + c[4]*y1 + c[5]
858
 *          x2' = c[0]*x2 + c[1]*y2 + c[2]
859
 *          y2' = c[3]*x2 + c[4]*y2 + c[5]
860
 *          x3' = c[0]*x3 + c[1]*y3 + c[2]
861
 *          y3' = c[3]*x3 + c[4]*y3 + c[5]
862
 *
863
 *  This can be represented as
864
 *
865
 *          AC = B
866
 *
867
 *  where B and C are column vectors
868
 *
869
 *          B = [ x1' y1' x2' y2' x3' y3' ]
870
 *          C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] ]
871
 *
872
 *  and A is the 6x6 matrix
873
 *
874
 *          x1   y1   1   0    0    0
875
 *           0    0   0   x1   y1   1
876
 *          x2   y2   1   0    0    0
877
 *           0    0   0   x2   y2   1
878
 *          x3   y3   1   0    0    0
879
 *           0    0   0   x3   y3   1
880
 *
881
 *  These six equations are solved here for the coefficients C.
882
 *
883
 *  These six coefficients can then be used to find the dest
884
 *  point x',y') corresponding to any src point (x,y, according
885
 *  to the equations
886
 *
887
 *           x' = c[0]x + c[1]y + c[2]
888
 *           y' = c[3]x + c[4]y + c[5]
889
 *
890
 *  that are implemented in affineXformPt.
891
 *
892
 *  !!!!!!!!!!!!!!!!!!   Very important   !!!!!!!!!!!!!!!!!!!!!!
893
 *
894
 *  When the affine transform is composed from a set of simple
895
 *  operations such as translation, scaling and rotation,
896
 *  it is built in a form to convert from the un-transformed src
897
 *  point to the transformed dest point.  However, when an
898
 *  affine transform is used on images, it is used in an inverted
899
 *  way: it converts from the transformed dest point to the
900
 *  un-transformed src point.  So, for example, if you transform
901
 *  a boxa using transform A, to transform an image in the same
902
 *  way you must use the inverse of A.
903
 *
904
 *  For example, if you transform a boxa with a 3x3 affine matrix
905
 *  'mat', the analogous image transformation must use 'matinv':
906
 * \code
907
 *     boxad = boxaAffineTransform(boxas, mat);
908
 *     affineInvertXform(mat, &matinv);
909
 *     pixd = pixAffine(pixs, matinv, L_BRING_IN_WHITE);
910
 * \endcode
911
 *  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
912
 * </pre>
913
 */
914
l_ok
915
getAffineXformCoeffs(PTA         *ptas,
916
                     PTA         *ptad,
917
                     l_float32  **pvc)
918
0
{
919
0
l_int32     i;
920
0
l_float32   x1, y1, x2, y2, x3, y3;
921
0
l_float32  *b;   /* rhs vector of primed coords X'; coeffs returned in *pvc */
922
0
l_float32  *a[6];  /* 6x6 matrix A  */
923
924
0
    if (!ptas)
925
0
        return ERROR_INT("ptas not defined", __func__, 1);
926
0
    if (!ptad)
927
0
        return ERROR_INT("ptad not defined", __func__, 1);
928
0
    if (!pvc)
929
0
        return ERROR_INT("&vc not defined", __func__, 1);
930
931
0
    b = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32));
932
0
    *pvc = b;
933
934
0
    ptaGetPt(ptas, 0, &x1, &y1);
935
0
    ptaGetPt(ptas, 1, &x2, &y2);
936
0
    ptaGetPt(ptas, 2, &x3, &y3);
937
0
    ptaGetPt(ptad, 0, &b[0], &b[1]);
938
0
    ptaGetPt(ptad, 1, &b[2], &b[3]);
939
0
    ptaGetPt(ptad, 2, &b[4], &b[5]);
940
941
0
    for (i = 0; i < 6; i++)
942
0
        a[i] = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32));
943
0
    a[0][0] = x1;
944
0
    a[0][1] = y1;
945
0
    a[0][2] = 1.;
946
0
    a[1][3] = x1;
947
0
    a[1][4] = y1;
948
0
    a[1][5] = 1.;
949
0
    a[2][0] = x2;
950
0
    a[2][1] = y2;
951
0
    a[2][2] = 1.;
952
0
    a[3][3] = x2;
953
0
    a[3][4] = y2;
954
0
    a[3][5] = 1.;
955
0
    a[4][0] = x3;
956
0
    a[4][1] = y3;
957
0
    a[4][2] = 1.;
958
0
    a[5][3] = x3;
959
0
    a[5][4] = y3;
960
0
    a[5][5] = 1.;
961
962
0
    gaussjordan(a, b, 6);
963
964
0
    for (i = 0; i < 6; i++)
965
0
        LEPT_FREE(a[i]);
966
967
0
    return 0;
968
0
}
969
970
971
/*!
972
 * \brief   affineInvertXform()
973
 *
974
 * \param[in]    vc     vector of 6 coefficients
975
 * \param[out]   pvci   inverted transform
976
 * \return  0 if OK; 1 on error
977
 *
978
 * <pre>
979
 * Notes:
980
 *      (1) The 6 affine transform coefficients are the first
981
 *          two rows of a 3x3 matrix where the last row has
982
 *          only a 1 in the third column.  We invert this
983
 *          using gaussjordan(), and select the first 2 rows
984
 *          as the coefficients of the inverse affine transform.
985
 *      (2) Alternatively, we can find the inverse transform
986
 *          coefficients by inverting the 2x2 submatrix,
987
 *          and treating the top 2 coefficients in the 3rd column as
988
 *          a RHS vector for that 2x2 submatrix.  Then the
989
 *          6 inverted transform coefficients are composed of
990
 *          the inverted 2x2 submatrix and the negative of the
991
 *          transformed RHS vector.  Why is this so?  We have
992
 *             Y = AX + R  (2 equations in 6 unknowns)
993
 *          Then
994
 *             X = A'Y - A'R
995
 *          Gauss-jordan solves
996
 *             AF = R
997
 *          and puts the solution for F, which is A'R,
998
 *          into the input R vector.
999
 *
1000
 * </pre>
1001
 */
1002
l_ok
1003
affineInvertXform(l_float32   *vc,
1004
                  l_float32  **pvci)
1005
0
{
1006
0
l_int32     i;
1007
0
l_float32  *vci;
1008
0
l_float32  *a[3];
1009
0
l_float32   b[3] = {1.0, 1.0, 1.0};   /* anything; results ignored */
1010
1011
0
    if (!pvci)
1012
0
        return ERROR_INT("&vci not defined", __func__, 1);
1013
0
    *pvci = NULL;
1014
0
    if (!vc)
1015
0
        return ERROR_INT("vc not defined", __func__, 1);
1016
1017
0
#if 1
1018
0
    for (i = 0; i < 3; i++)
1019
0
        a[i] = (l_float32 *)LEPT_CALLOC(3, sizeof(l_float32));
1020
0
    a[0][0] = vc[0];
1021
0
    a[0][1] = vc[1];
1022
0
    a[0][2] = vc[2];
1023
0
    a[1][0] = vc[3];
1024
0
    a[1][1] = vc[4];
1025
0
    a[1][2] = vc[5];
1026
0
    a[2][2] = 1.0;
1027
0
    gaussjordan(a, b, 3);  /* this inverts matrix a */
1028
0
    vci = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32));
1029
0
    *pvci = vci;
1030
0
    vci[0] = a[0][0];
1031
0
    vci[1] = a[0][1];
1032
0
    vci[2] = a[0][2];
1033
0
    vci[3] = a[1][0];
1034
0
    vci[4] = a[1][1];
1035
0
    vci[5] = a[1][2];
1036
0
    for (i = 0; i < 3; i++)
1037
0
        LEPT_FREE(a[i]);
1038
1039
#else
1040
1041
        /* Alternative version, inverting a 2x2 matrix */
1042
    { l_float32 *a2[2];
1043
    for (i = 0; i < 2; i++)
1044
        a2[i] = (l_float32 *)LEPT_CALLOC(2, sizeof(l_float32));
1045
    a2[0][0] = vc[0];
1046
    a2[0][1] = vc[1];
1047
    a2[1][0] = vc[3];
1048
    a2[1][1] = vc[4];
1049
    b[0] = vc[2];
1050
    b[1] = vc[5];
1051
    gaussjordan(a2, b, 2);  /* this inverts matrix a2 */
1052
    vci = (l_float32 *)LEPT_CALLOC(6, sizeof(l_float32));
1053
    *pvci = vci;
1054
    vci[0] = a2[0][0];
1055
    vci[1] = a2[0][1];
1056
    vci[2] = -b[0];   /* note sign */
1057
    vci[3] = a2[1][0];
1058
    vci[4] = a2[1][1];
1059
    vci[5] = -b[1];   /* note sign */
1060
    for (i = 0; i < 2; i++)
1061
        LEPT_FREE(a2[i]);
1062
    }
1063
#endif
1064
1065
0
    return 0;
1066
0
}
1067
1068
1069
/*!
1070
 * \brief   affineXformSampledPt()
1071
 *
1072
 * \param[in]    vc         vector of 6 coefficients
1073
 * \param[in]    x, y       initial point
1074
 * \param[out]   pxp, pyp   transformed point
1075
 * \return  0 if OK; 1 on error
1076
 *
1077
 * <pre>
1078
 * Notes:
1079
 *      (1) This finds the nearest pixel coordinates of the transformed point.
1080
 *      (2) It does not check ptrs for returned data!
1081
 * </pre>
1082
 */
1083
l_ok
1084
affineXformSampledPt(l_float32  *vc,
1085
                     l_int32     x,
1086
                     l_int32     y,
1087
                     l_int32    *pxp,
1088
                     l_int32    *pyp)
1089
0
{
1090
0
    if (!vc)
1091
0
        return ERROR_INT("vc not defined", __func__, 1);
1092
1093
0
    *pxp = (l_int32)(vc[0] * x + vc[1] * y + vc[2] + 0.5);
1094
0
    *pyp = (l_int32)(vc[3] * x + vc[4] * y + vc[5] + 0.5);
1095
0
    return 0;
1096
0
}
1097
1098
1099
/*!
1100
 * \brief   affineXformPt()
1101
 *
1102
 * \param[in]    vc         vector of 6 coefficients
1103
 * \param[in]    x, y       initial point
1104
 * \param[out]   pxp, pyp   transformed point
1105
 * \return  0 if OK; 1 on error
1106
 *
1107
 * <pre>
1108
 * Notes:
1109
 *      (1) This computes the floating point location of the transformed point.
1110
 *      (2) It does not check ptrs for returned data!
1111
 * </pre>
1112
 */
1113
l_ok
1114
affineXformPt(l_float32  *vc,
1115
              l_int32     x,
1116
              l_int32     y,
1117
              l_float32  *pxp,
1118
              l_float32  *pyp)
1119
0
{
1120
0
    if (!vc)
1121
0
        return ERROR_INT("vc not defined", __func__, 1);
1122
1123
0
    *pxp = vc[0] * x + vc[1] * y + vc[2];
1124
0
    *pyp = vc[3] * x + vc[4] * y + vc[5];
1125
0
    return 0;
1126
0
}
1127
1128
1129
/*-------------------------------------------------------------*
1130
 *                 Interpolation helper functions              *
1131
 *-------------------------------------------------------------*/
1132
/*!
1133
 * \brief   linearInterpolatePixelColor()
1134
 *
1135
 * \param[in]    datas      ptr to beginning of image data
1136
 * \param[in]    wpls       32-bit word/line for this data array
1137
 * \param[in]    w, h       of image
1138
 * \param[in]    x, y       floating pt location for evaluation
1139
 * \param[in]    colorval   color brought in from the outside when the
1140
 *                          input x,y location is outside the image;
1141
 *                          in 0xrrggbb00 format)
1142
 * \param[out]   pval       interpolated color value
1143
 * \return  0 if OK, 1 on error
1144
 *
1145
 * <pre>
1146
 * Notes:
1147
 *      (1) This is a standard linear interpolation function.  It is
1148
 *          equivalent to area weighting on each component, and
1149
 *          avoids "jaggies" when rendering sharp edges.
1150
 * </pre>
1151
 */
1152
l_ok
1153
linearInterpolatePixelColor(l_uint32  *datas,
1154
                            l_int32    wpls,
1155
                            l_int32    w,
1156
                            l_int32    h,
1157
                            l_float32  x,
1158
                            l_float32  y,
1159
                            l_uint32   colorval,
1160
                            l_uint32  *pval)
1161
0
{
1162
0
l_int32    valid, xpm, ypm, xp, xp2, yp, xf, yf;
1163
0
l_int32    rval, gval, bval;
1164
0
l_uint32   word00, word01, word10, word11;
1165
0
l_uint32  *lines;
1166
1167
0
    if (!pval)
1168
0
        return ERROR_INT("&val not defined", __func__, 1);
1169
0
    *pval = colorval;
1170
0
    if (!datas)
1171
0
        return ERROR_INT("datas not defined", __func__, 1);
1172
1173
        /* Skip if x or y are invalid. (x,y) must be in the source image.
1174
         * Failure to detect an invalid point will cause a mem address fault.
1175
         * Occasionally, x or y will be a nan, and relational checks always
1176
         * fail for nans.  Therefore we check if the point is inside the pix */
1177
0
    valid = (x >= 0.0 && y >= 0.0 && x < w && y < h);
1178
0
    if (!valid) return 0;
1179
1180
0
    xpm = (l_int32)(16.0 * x);
1181
0
    ypm = (l_int32)(16.0 * y);
1182
0
    xp = xpm >> 4;
1183
0
    xp2 = xp + 1 < w ? xp + 1 : xp;
1184
0
    yp = ypm >> 4;
1185
0
    if (yp + 1 >= h) wpls = 0;
1186
0
    xf = xpm & 0x0f;
1187
0
    yf = ypm & 0x0f;
1188
1189
#if  DEBUG
1190
    if (xf < 0 || yf < 0)
1191
        lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf);
1192
#endif  /* DEBUG */
1193
1194
        /* Do area weighting (eqiv. to linear interpolation) */
1195
0
    lines = datas + yp * wpls;
1196
0
    word00 = *(lines + xp);
1197
0
    word10 = *(lines + xp2);
1198
0
    word01 = *(lines + wpls + xp);
1199
0
    word11 = *(lines + wpls + xp2);
1200
0
    rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) +
1201
0
        xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) +
1202
0
        (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) +
1203
0
        xf * yf * ((word11 >> L_RED_SHIFT) & 0xff)) / 256;
1204
0
    gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) +
1205
0
        xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) +
1206
0
        (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) +
1207
0
        xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff)) / 256;
1208
0
    bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) +
1209
0
        xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) +
1210
0
        (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) +
1211
0
        xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff)) / 256;
1212
0
    composeRGBPixel(rval, gval, bval, pval);
1213
0
    return 0;
1214
0
}
1215
1216
1217
/*!
1218
 * \brief   linearInterpolatePixelGray()
1219
 *
1220
 * \param[in]    datas     ptr to beginning of image data
1221
 * \param[in]    wpls      32-bit word/line for this data array
1222
 * \param[in]    w, h      of image
1223
 * \param[in]    x, y      floating pt location for evaluation
1224
 * \param[in]    grayval   color brought in from the outside when the
1225
 *                         input x,y location is outside the image
1226
 * \param[out]   pval      interpolated gray value
1227
 * \return  0 if OK, 1 on error
1228
 *
1229
 * <pre>
1230
 * Notes:
1231
 *      (1) This is a standard linear interpolation function.  It is
1232
 *          equivalent to area weighting on each component, and
1233
 *          avoids "jaggies" when rendering sharp edges.
1234
 * </pre>
1235
 */
1236
l_ok
1237
linearInterpolatePixelGray(l_uint32  *datas,
1238
                           l_int32    wpls,
1239
                           l_int32    w,
1240
                           l_int32    h,
1241
                           l_float32  x,
1242
                           l_float32  y,
1243
                           l_int32    grayval,
1244
                           l_int32   *pval)
1245
0
{
1246
0
l_int32    valid, xpm, ypm, xp, xp2, yp, xf, yf, v00, v10, v01, v11;
1247
0
l_uint32  *lines;
1248
1249
0
    if (!pval)
1250
0
        return ERROR_INT("&val not defined", __func__, 1);
1251
0
    *pval = grayval;
1252
0
    if (!datas)
1253
0
        return ERROR_INT("datas not defined", __func__, 1);
1254
1255
        /* Skip if x or y is invalid. (x,y) must be in the source image.
1256
         * Failure to detect an invalid point will cause a mem address fault.
1257
         * Occasionally, x or y will be a nan, and relational checks always
1258
         * fail for nans.  Therefore we check if the point is inside the pix */
1259
0
    valid = (x >= 0.0 && y >= 0.0 && x < w && y < h);
1260
0
    if (!valid) return 0;
1261
1262
0
    xpm = (l_int32)(16.0 * x);
1263
0
    ypm = (l_int32)(16.0 * y);
1264
0
    xp = xpm >> 4;
1265
0
    xp2 = xp + 1 < w ? xp + 1 : xp;
1266
0
    yp = ypm >> 4;
1267
0
    if (yp + 1 >= h) wpls = 0;
1268
0
    xf = xpm & 0x0f;
1269
0
    yf = ypm & 0x0f;
1270
1271
#if  DEBUG
1272
    if (xf < 0 || yf < 0)
1273
        lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf);
1274
#endif  /* DEBUG */
1275
1276
        /* Interpolate by area weighting. */
1277
0
    lines = datas + yp * wpls;
1278
0
    v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp);
1279
0
    v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp2);
1280
0
    v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp);
1281
0
    v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp2);
1282
0
    *pval = (v00 + v01 + v10 + v11) / 256;
1283
0
    return 0;
1284
0
}
1285
1286
1287
1288
/*-------------------------------------------------------------*
1289
 *               Gauss-jordan linear equation solver           *
1290
 *-------------------------------------------------------------*/
1291
0
#define  SWAP(a,b)   {temp = (a); (a) = (b); (b) = temp;}
1292
1293
/*!
1294
 * \brief   gaussjordan()
1295
 *
1296
 * \param[in]    a     n x n matrix
1297
 * \param[in]    b     n x 1 right-hand side column vector
1298
 * \param[in]    n     dimension
1299
 * \return  0 if ok, 1 on error
1300
 *
1301
 * <pre>
1302
 * Notes:
1303
 *      (1) There are two side-effects:
1304
 *          * The matrix a is transformed to its inverse A
1305
 *          * The rhs vector b is transformed to the solution x
1306
 *            of the linear equation ax = b
1307
 *      (2) The inverse A can then be used to solve the same equation with
1308
 *          different rhs vectors c by multiplication: x = Ac
1309
 *      (3) Adapted from "Numerical Recipes in C, Second Edition", 1992,
1310
 *          pp. 36-41 (gauss-jordan elimination)
1311
 * </pre>
1312
 */
1313
l_int32
1314
gaussjordan(l_float32  **a,
1315
            l_float32   *b,
1316
            l_int32      n)
1317
0
{
1318
0
l_int32    i, icol, irow, j, k, col, row, success;
1319
0
l_int32   *indexc, *indexr, *ipiv;
1320
0
l_float32  maxval, val, pivinv, temp;
1321
1322
0
    if (!a)
1323
0
        return ERROR_INT("a not defined", __func__, 1);
1324
0
    if (!b)
1325
0
        return ERROR_INT("b not defined", __func__, 1);
1326
1327
0
    success = TRUE;
1328
0
    indexc = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32));
1329
0
    indexr = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32));
1330
0
    ipiv = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32));
1331
0
    if (!indexc || !indexr || !ipiv) {
1332
0
        L_ERROR("array not made\n", __func__);
1333
0
        success = FALSE;
1334
0
        goto cleanup_arrays;
1335
0
    }
1336
1337
0
    icol = irow = 0;  /* silence static checker */
1338
0
    for (i = 0; i < n; i++) {
1339
0
        maxval = 0.0;
1340
0
        for (j = 0; j < n; j++) {
1341
0
            if (ipiv[j] != 1) {
1342
0
                for (k = 0; k < n; k++) {
1343
0
                    if (ipiv[k] == 0) {
1344
0
                        if (fabs(a[j][k]) >= maxval) {
1345
0
                            maxval = fabs(a[j][k]);
1346
0
                            irow = j;
1347
0
                            icol = k;
1348
0
                        }
1349
0
                    } else if (ipiv[k] > 1) {
1350
0
                        L_ERROR("singular matrix\n", __func__);
1351
0
                        success = FALSE;
1352
0
                        goto cleanup_arrays;
1353
0
                    }
1354
0
                }
1355
0
            }
1356
0
        }
1357
0
        ++(ipiv[icol]);
1358
1359
0
        if (irow != icol) {
1360
0
            for (col = 0; col < n; col++)
1361
0
                SWAP(a[irow][col], a[icol][col]);
1362
0
            SWAP(b[irow], b[icol]);
1363
0
        }
1364
1365
0
        indexr[i] = irow;
1366
0
        indexc[i] = icol;
1367
0
        if (a[icol][icol] == 0.0) {
1368
0
            L_ERROR("singular matrix\n", __func__);
1369
0
            success = FALSE;
1370
0
            goto cleanup_arrays;
1371
0
        }
1372
0
        pivinv = 1.0 / a[icol][icol];
1373
0
        a[icol][icol] = 1.0;
1374
0
        for (col = 0; col < n; col++)
1375
0
            a[icol][col] *= pivinv;
1376
0
        b[icol] *= pivinv;
1377
1378
0
        for (row = 0; row < n; row++) {
1379
0
            if (row != icol) {
1380
0
                val = a[row][icol];
1381
0
                a[row][icol] = 0.0;
1382
0
                for (col = 0; col < n; col++)
1383
0
                    a[row][col] -= a[icol][col] * val;
1384
0
                b[row] -= b[icol] * val;
1385
0
            }
1386
0
        }
1387
0
    }
1388
1389
0
    for (col = n - 1; col >= 0; col--) {
1390
0
        if (indexr[col] != indexc[col]) {
1391
0
            for (k = 0; k < n; k++)
1392
0
                SWAP(a[k][indexr[col]], a[k][indexc[col]]);
1393
0
        }
1394
0
    }
1395
1396
0
cleanup_arrays:
1397
0
    LEPT_FREE(indexr);
1398
0
    LEPT_FREE(indexc);
1399
0
    LEPT_FREE(ipiv);
1400
0
    return (success) ? 0 : 1;
1401
0
}
1402
1403
1404
/*-------------------------------------------------------------*
1405
 *              Sequential affine image transformation         *
1406
 *-------------------------------------------------------------*/
1407
/*!
1408
 * \brief   pixAffineSequential()
1409
 *
1410
 * \param[in]    pixs
1411
 * \param[in]    ptad   3 pts of final coordinate space
1412
 * \param[in]    ptas   3 pts of initial coordinate space
1413
 * \param[in]    bw     pixels of additional border width during computation
1414
 * \param[in]    bh     pixels of additional border height during computation
1415
 * \return  pixd, or NULL on error
1416
 *
1417
 * <pre>
1418
 * Notes:
1419
 *      (1) The 3 pts must not be collinear.
1420
 *      (2) The 3 pts must be given in this order:
1421
 *           ~ origin
1422
 *           ~ a location along the x-axis
1423
 *           ~ a location along the y-axis.
1424
 *      (3) You must guess how much border must be added so that no
1425
 *          pixels are lost in the transformations from src to
1426
 *          dest coordinate space.  (This can be calculated but it
1427
 *          is a lot of work!)  For coordinate spaces that are nearly
1428
 *          at right angles, on a 300 ppi scanned page, the addition
1429
 *          of 1000 pixels on each side is usually sufficient.
1430
 *      (4) This is here for pedagogical reasons.  It is about 3x faster
1431
 *          on 1 bpp images than pixAffineSampled(), but the results
1432
 *          on text are much inferior.
1433
 * </pre>
1434
 */
1435
PIX *
1436
pixAffineSequential(PIX     *pixs,
1437
                    PTA     *ptad,
1438
                    PTA     *ptas,
1439
                    l_int32  bw,
1440
                    l_int32  bh)
1441
0
{
1442
0
l_int32    x1, y1, x2, y2, x3, y3;    /* ptas */
1443
0
l_int32    x1p, y1p, x2p, y2p, x3p, y3p;   /* ptad */
1444
0
l_int32    x1sc, y1sc;  /* scaled origin */
1445
0
l_float32  x2s, x2sp, scalex, scaley;
1446
0
l_float32  th3, th3p, ph2, ph2p;
1447
#if  DEBUG
1448
l_float32  rad2deg;
1449
#endif  /* DEBUG */
1450
0
PIX       *pix1, *pix2, *pixd;
1451
1452
0
    if (!pixs)
1453
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1454
0
    if (!ptas)
1455
0
        return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
1456
0
    if (!ptad)
1457
0
        return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
1458
1459
0
    if (ptaGetCount(ptas) != 3)
1460
0
        return (PIX *)ERROR_PTR("ptas count not 3", __func__, NULL);
1461
0
    if (ptaGetCount(ptad) != 3)
1462
0
        return (PIX *)ERROR_PTR("ptad count not 3", __func__, NULL);
1463
0
    ptaGetIPt(ptas, 0, &x1, &y1);
1464
0
    ptaGetIPt(ptas, 1, &x2, &y2);
1465
0
    ptaGetIPt(ptas, 2, &x3, &y3);
1466
0
    ptaGetIPt(ptad, 0, &x1p, &y1p);
1467
0
    ptaGetIPt(ptad, 1, &x2p, &y2p);
1468
0
    ptaGetIPt(ptad, 2, &x3p, &y3p);
1469
1470
0
    pix1 = pix2 = pixd = NULL;
1471
1472
0
    if (y1 == y3)
1473
0
        return (PIX *)ERROR_PTR("y1 == y3!", __func__, NULL);
1474
0
    if (y1p == y3p)
1475
0
        return (PIX *)ERROR_PTR("y1p == y3p!", __func__, NULL);
1476
1477
0
    if (bw != 0 || bh != 0) {
1478
            /* resize all points and add border to pixs */
1479
0
        x1 = x1 + bw;
1480
0
        y1 = y1 + bh;
1481
0
        x2 = x2 + bw;
1482
0
        y2 = y2 + bh;
1483
0
        x3 = x3 + bw;
1484
0
        y3 = y3 + bh;
1485
0
        x1p = x1p + bw;
1486
0
        y1p = y1p + bh;
1487
0
        x2p = x2p + bw;
1488
0
        y2p = y2p + bh;
1489
0
        x3p = x3p + bw;
1490
0
        y3p = y3p + bh;
1491
1492
0
        if ((pix1 = pixAddBorderGeneral(pixs, bw, bw, bh, bh, 0)) == NULL)
1493
0
            return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL);
1494
0
    } else {
1495
0
        pix1 = pixCopy(NULL, pixs);
1496
0
    }
1497
1498
    /*-------------------------------------------------------------*
1499
        The horizontal shear is done to move the 3rd point to the
1500
        y axis.  This moves the 2nd point either towards or away
1501
        from the y axis, depending on whether it is above or below
1502
        the x axis.  That motion must be computed so that we know
1503
        the angle of vertical shear to use to get the 2nd point
1504
        on the x axis.  We must also know the x coordinate of the
1505
        2nd point in order to compute how much scaling is required
1506
        to match points on the axis.
1507
     *-------------------------------------------------------------*/
1508
1509
        /* Shear angles required to put src points on x and y axes */
1510
0
    th3 = atan2((l_float64)(x1 - x3), (l_float64)(y1 - y3));
1511
0
    x2s = (l_float32)(x2 - ((l_float32)(y1 - y2) * (x3 - x1)) / (y1 - y3));
1512
0
    if (x2s == (l_float32)x1) {
1513
0
        L_ERROR("x2s == x1!\n", __func__);
1514
0
        goto cleanup_pix;
1515
0
    }
1516
0
    ph2 = atan2((l_float64)(y1 - y2), (l_float64)(x2s - x1));
1517
1518
        /* Shear angles required to put dest points on x and y axes.
1519
         * Use the negative of these values to instead move the
1520
         * src points from the axes to the actual dest position.
1521
         * These values are also needed to scale the image. */
1522
0
    th3p = atan2((l_float64)(x1p - x3p), (l_float64)(y1p - y3p));
1523
0
    x2sp = (l_float32)(x2p -
1524
0
                       ((l_float32)(y1p - y2p) * (x3p - x1p)) / (y1p - y3p));
1525
0
    if (x2sp == (l_float32)x1p) {
1526
0
        L_ERROR("x2sp == x1p!\n", __func__);
1527
0
        goto cleanup_pix;
1528
0
    }
1529
0
    ph2p = atan2((l_float64)(y1p - y2p), (l_float64)(x2sp - x1p));
1530
1531
        /* Shear image to first put src point 3 on the y axis,
1532
         * and then to put src point 2 on the x axis */
1533
0
    pixHShearIP(pix1, y1, th3, L_BRING_IN_WHITE);
1534
0
    pixVShearIP(pix1, x1, ph2, L_BRING_IN_WHITE);
1535
1536
        /* Scale image to match dest scale.  The dest scale
1537
         * is calculated above from the angles th3p and ph2p
1538
         * that would be required to move the dest points to
1539
         * the x and y axes. */
1540
0
    scalex = (l_float32)(x2sp - x1p) / (x2s - x1);
1541
0
    scaley = (l_float32)(y3p - y1p) / (y3 - y1);
1542
0
    if ((pix2 = pixScale(pix1, scalex, scaley)) == NULL) {
1543
0
        L_ERROR("pix2 not made\n", __func__);
1544
0
        goto cleanup_pix;
1545
0
    }
1546
1547
#if  DEBUG
1548
    rad2deg = 180. / 3.1415926535;
1549
    lept_stderr("th3 = %5.1f deg, ph2 = %5.1f deg\n",
1550
                rad2deg * th3, rad2deg * ph2);
1551
    lept_stderr("th3' = %5.1f deg, ph2' = %5.1f deg\n",
1552
                rad2deg * th3p, rad2deg * ph2p);
1553
    lept_stderr("scalex = %6.3f, scaley = %6.3f\n", scalex, scaley);
1554
#endif  /* DEBUG */
1555
1556
    /*-------------------------------------------------------------*
1557
        Scaling moves the 1st src point, which is the origin.
1558
        It must now be moved again to coincide with the origin
1559
        (1st point) of the dest.  After this is done, the 2nd
1560
        and 3rd points must be sheared back to the original
1561
        positions of the 2nd and 3rd dest points.  We use the
1562
        negative of the angles that were previously computed
1563
        for shearing those points in the dest image to x and y
1564
        axes, and take the shears in reverse order as well.
1565
     *-------------------------------------------------------------*/
1566
        /* Shift image to match dest origin. */
1567
0
    x1sc = (l_int32)(scalex * x1 + 0.5);   /* x comp of origin after scaling */
1568
0
    y1sc = (l_int32)(scaley * y1 + 0.5);   /* y comp of origin after scaling */
1569
0
    pixRasteropIP(pix2, x1p - x1sc, y1p - y1sc, L_BRING_IN_WHITE);
1570
1571
        /* Shear image to take points 2 and 3 off the axis and
1572
         * put them in the original dest position */
1573
0
    pixVShearIP(pix2, x1p, -ph2p, L_BRING_IN_WHITE);
1574
0
    pixHShearIP(pix2, y1p, -th3p, L_BRING_IN_WHITE);
1575
1576
0
    if (bw != 0 || bh != 0) {
1577
0
        if ((pixd = pixRemoveBorderGeneral(pix2, bw, bw, bh, bh)) == NULL)
1578
0
            L_ERROR("pixd not made\n", __func__);
1579
0
    } else {
1580
0
        pixd = pixClone(pix2);
1581
0
    }
1582
1583
0
cleanup_pix:
1584
0
    pixDestroy(&pix1);
1585
0
    pixDestroy(&pix2);
1586
0
    return pixd;
1587
0
}