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 fpix2.c
29 * <pre>
30 *
31 * This file has these FPix utilities:
32 * ~ interconversions with pix, fpix, dpix
33 * ~ min and max values
34 * ~ integer scaling
35 * ~ arithmetic operations
36 * ~ set all
37 * ~ border functions
38 * ~ simple rasterop (source --> dest)
39 * ~ geometric transforms
40 *
41 * Interconversions between Pix, FPix and DPix
42 * FPIX *pixConvertToFPix()
43 * DPIX *pixConvertToDPix()
44 * PIX *fpixConvertToPix()
45 * PIX *fpixDisplayMaxDynamicRange() [useful for debugging]
46 * DPIX *fpixConvertToDPix()
47 * PIX *dpixConvertToPix()
48 * FPIX *dpixConvertToFPix()
49 *
50 * Min/max value
51 * l_int32 fpixGetMin()
52 * l_int32 fpixGetMax()
53 * l_int32 dpixGetMin()
54 * l_int32 dpixGetMax()
55 *
56 * Integer scaling
57 * FPIX *fpixScaleByInteger()
58 * DPIX *dpixScaleByInteger()
59 *
60 * Arithmetic operations
61 * FPIX *fpixLinearCombination()
62 * l_int32 fpixAddMultConstant()
63 * DPIX *dpixLinearCombination()
64 * l_int32 dpixAddMultConstant()
65 *
66 * Set all
67 * l_int32 fpixSetAllArbitrary()
68 * l_int32 dpixSetAllArbitrary()
69 *
70 * FPix border functions
71 * FPIX *fpixAddBorder()
72 * FPIX *fpixRemoveBorder()
73 * FPIX *fpixAddMirroredBorder()
74 * FPIX *fpixAddContinuedBorder()
75 * FPIX *fpixAddSlopeBorder()
76 *
77 * FPix simple rasterop
78 * l_int32 fpixRasterop()
79 *
80 * FPix rotation by multiples of 90 degrees
81 * FPIX *fpixRotateOrth()
82 * FPIX *fpixRotate180()
83 * FPIX *fpixRotate90()
84 * FPIX *fpixFlipLR()
85 * FPIX *fpixFlipTB()
86 *
87 * FPix affine and projective interpolated transforms
88 * FPIX *fpixAffinePta()
89 * FPIX *fpixAffine()
90 * FPIX *fpixProjectivePta()
91 * FPIX *fpixProjective()
92 * l_int32 linearInterpolatePixelFloat()
93 *
94 * Thresholding to 1 bpp Pix
95 * PIX *fpixThresholdToPix()
96 *
97 * Generate function from components
98 * FPIX *pixComponentFunction()
99 * </pre>
100 */
101
102 #include <string.h>
103 #include "allheaders.h"
104
105 /*--------------------------------------------------------------------*
106 * FPix <--> Pix conversions *
107 *--------------------------------------------------------------------*/
108 /*!
109 * \brief pixConvertToFPix()
110 *
111 * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp
112 * \param[in] ncomps number of components: 3 for RGB, 1 otherwise
113 * \return fpix, or NULL on error
114 *
115 * <pre>
116 * Notes:
117 * (1) If colormapped, remove to grayscale.
118 * (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance.
119 * In all other cases the src image is treated as having a single
120 * component of pixel values.
121 * </pre>
122 */
123 FPIX *
pixConvertToFPix(PIX * pixs,l_int32 ncomps)124 pixConvertToFPix(PIX *pixs,
125 l_int32 ncomps)
126 {
127 l_int32 w, h, d, i, j, val, wplt, wpld;
128 l_uint32 uval;
129 l_uint32 *datat, *linet;
130 l_float32 *datad, *lined;
131 PIX *pixt;
132 FPIX *fpixd;
133
134 PROCNAME("pixConvertToFPix");
135
136 if (!pixs)
137 return (FPIX *)ERROR_PTR("pixs not defined", procName, NULL);
138
139 /* Convert to a single component */
140 if (pixGetColormap(pixs))
141 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
142 else if (pixGetDepth(pixs) == 32 && ncomps == 3)
143 pixt = pixConvertRGBToLuminance(pixs);
144 else
145 pixt = pixClone(pixs);
146 pixGetDimensions(pixt, &w, &h, &d);
147 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) {
148 pixDestroy(&pixt);
149 return (FPIX *)ERROR_PTR("invalid depth", procName, NULL);
150 }
151
152 if ((fpixd = fpixCreate(w, h)) == NULL) {
153 pixDestroy(&pixt);
154 return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL);
155 }
156 datat = pixGetData(pixt);
157 wplt = pixGetWpl(pixt);
158 datad = fpixGetData(fpixd);
159 wpld = fpixGetWpl(fpixd);
160 for (i = 0; i < h; i++) {
161 linet = datat + i * wplt;
162 lined = datad + i * wpld;
163 if (d == 1) {
164 for (j = 0; j < w; j++) {
165 val = GET_DATA_BIT(linet, j);
166 lined[j] = (l_float32)val;
167 }
168 } else if (d == 2) {
169 for (j = 0; j < w; j++) {
170 val = GET_DATA_DIBIT(linet, j);
171 lined[j] = (l_float32)val;
172 }
173 } else if (d == 4) {
174 for (j = 0; j < w; j++) {
175 val = GET_DATA_QBIT(linet, j);
176 lined[j] = (l_float32)val;
177 }
178 } else if (d == 8) {
179 for (j = 0; j < w; j++) {
180 val = GET_DATA_BYTE(linet, j);
181 lined[j] = (l_float32)val;
182 }
183 } else if (d == 16) {
184 for (j = 0; j < w; j++) {
185 val = GET_DATA_TWO_BYTES(linet, j);
186 lined[j] = (l_float32)val;
187 }
188 } else { /* d == 32 */
189 for (j = 0; j < w; j++) {
190 uval = GET_DATA_FOUR_BYTES(linet, j);
191 lined[j] = (l_float32)uval;
192 }
193 }
194 }
195
196 pixDestroy(&pixt);
197 return fpixd;
198 }
199
200
201 /*!
202 * \brief pixConvertToDPix()
203 *
204 * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp
205 * \param[in] ncomps number of components: 3 for RGB, 1 otherwise
206 * \return dpix, or NULL on error
207 *
208 * <pre>
209 * Notes:
210 * (1) If colormapped, remove to grayscale.
211 * (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance.
212 * In all other cases the src image is treated as having a single
213 * component of pixel values.
214 * </pre>
215 */
216 DPIX *
pixConvertToDPix(PIX * pixs,l_int32 ncomps)217 pixConvertToDPix(PIX *pixs,
218 l_int32 ncomps)
219 {
220 l_int32 w, h, d, i, j, val, wplt, wpld;
221 l_uint32 uval;
222 l_uint32 *datat, *linet;
223 l_float64 *datad, *lined;
224 PIX *pixt;
225 DPIX *dpixd;
226
227 PROCNAME("pixConvertToDPix");
228
229 if (!pixs)
230 return (DPIX *)ERROR_PTR("pixs not defined", procName, NULL);
231
232 /* Convert to a single component */
233 if (pixGetColormap(pixs))
234 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
235 else if (pixGetDepth(pixs) == 32 && ncomps == 3)
236 pixt = pixConvertRGBToLuminance(pixs);
237 else
238 pixt = pixClone(pixs);
239 pixGetDimensions(pixt, &w, &h, &d);
240 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) {
241 pixDestroy(&pixt);
242 return (DPIX *)ERROR_PTR("invalid depth", procName, NULL);
243 }
244
245 if ((dpixd = dpixCreate(w, h)) == NULL) {
246 pixDestroy(&pixt);
247 return (DPIX *)ERROR_PTR("dpixd not made", procName, NULL);
248 }
249 datat = pixGetData(pixt);
250 wplt = pixGetWpl(pixt);
251 datad = dpixGetData(dpixd);
252 wpld = dpixGetWpl(dpixd);
253 for (i = 0; i < h; i++) {
254 linet = datat + i * wplt;
255 lined = datad + i * wpld;
256 if (d == 1) {
257 for (j = 0; j < w; j++) {
258 val = GET_DATA_BIT(linet, j);
259 lined[j] = (l_float64)val;
260 }
261 } else if (d == 2) {
262 for (j = 0; j < w; j++) {
263 val = GET_DATA_DIBIT(linet, j);
264 lined[j] = (l_float64)val;
265 }
266 } else if (d == 4) {
267 for (j = 0; j < w; j++) {
268 val = GET_DATA_QBIT(linet, j);
269 lined[j] = (l_float64)val;
270 }
271 } else if (d == 8) {
272 for (j = 0; j < w; j++) {
273 val = GET_DATA_BYTE(linet, j);
274 lined[j] = (l_float64)val;
275 }
276 } else if (d == 16) {
277 for (j = 0; j < w; j++) {
278 val = GET_DATA_TWO_BYTES(linet, j);
279 lined[j] = (l_float64)val;
280 }
281 } else { /* d == 32 */
282 for (j = 0; j < w; j++) {
283 uval = GET_DATA_FOUR_BYTES(linet, j);
284 lined[j] = (l_float64)uval;
285 }
286 }
287 }
288
289 pixDestroy(&pixt);
290 return dpixd;
291 }
292
293
294 /*!
295 * \brief fpixConvertToPix()
296 *
297 * \param[in] fpixs
298 * \param[in] outdepth 0, 8, 16 or 32 bpp
299 * \param[in] negvals L_CLIP_TO_ZERO, L_TAKE_ABSVAL
300 * \param[in] errorflag 1 to output error stats; 0 otherwise
301 * \return pixd, or NULL on error
302 *
303 * <pre>
304 * Notes:
305 * (1) Use %outdepth = 0 to programmatically determine the
306 * output depth. If no values are greater than 255,
307 * it will set outdepth = 8; otherwise to 16 or 32.
308 * (2) Because we are converting a float to an unsigned int
309 * with a specified dynamic range (8, 16 or 32 bits), errors
310 * can occur. If errorflag == TRUE, output the number
311 * of values out of range, both negative and positive.
312 * (3) If a pixel value is positive and out of range, clip to
313 * the maximum value represented at the outdepth of 8, 16
314 * or 32 bits.
315 * </pre>
316 */
317 PIX *
fpixConvertToPix(FPIX * fpixs,l_int32 outdepth,l_int32 negvals,l_int32 errorflag)318 fpixConvertToPix(FPIX *fpixs,
319 l_int32 outdepth,
320 l_int32 negvals,
321 l_int32 errorflag)
322 {
323 l_int32 w, h, i, j, wpls, wpld;
324 l_uint32 vald, maxval;
325 l_float32 val;
326 l_float32 *datas, *lines;
327 l_uint32 *datad, *lined;
328 PIX *pixd;
329
330 PROCNAME("fpixConvertToPix");
331
332 if (!fpixs)
333 return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL);
334 if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL)
335 return (PIX *)ERROR_PTR("invalid negvals", procName, NULL);
336 if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32)
337 return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", procName, NULL);
338
339 fpixGetDimensions(fpixs, &w, &h);
340 datas = fpixGetData(fpixs);
341 wpls = fpixGetWpl(fpixs);
342
343 /* Adaptive determination of output depth */
344 if (outdepth == 0) {
345 outdepth = 8;
346 for (i = 0; i < h && outdepth < 32; i++) {
347 lines = datas + i * wpls;
348 for (j = 0; j < w && outdepth < 32; j++) {
349 if (lines[j] > 65535.5)
350 outdepth = 32;
351 else if (lines[j] > 255.5)
352 outdepth = 16;
353 }
354 }
355 }
356 if (outdepth == 8)
357 maxval = 0xff;
358 else if (outdepth == 16)
359 maxval = 0xffff;
360 else /* outdepth == 32 */
361 maxval = 0xffffffff;
362
363 /* Gather statistics if %errorflag = TRUE */
364 if (errorflag) {
365 l_int32 negs = 0;
366 l_int32 overvals = 0;
367 for (i = 0; i < h; i++) {
368 lines = datas + i * wpls;
369 for (j = 0; j < w; j++) {
370 val = lines[j];
371 if (val < 0.0)
372 negs++;
373 else if (val > maxval)
374 overvals++;
375 }
376 }
377 if (negs > 0)
378 L_ERROR("Number of negative values: %d\n", procName, negs);
379 if (overvals > 0)
380 L_ERROR("Number of too-large values: %d\n", procName, overvals);
381 }
382
383 /* Make the pix and convert the data */
384 if ((pixd = pixCreate(w, h, outdepth)) == NULL)
385 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
386 datad = pixGetData(pixd);
387 wpld = pixGetWpl(pixd);
388 for (i = 0; i < h; i++) {
389 lines = datas + i * wpls;
390 lined = datad + i * wpld;
391 for (j = 0; j < w; j++) {
392 val = lines[j];
393 if (val >= 0.0)
394 vald = (l_uint32)(val + 0.5);
395 else if (negvals == L_CLIP_TO_ZERO) /* and val < 0.0 */
396 vald = 0;
397 else
398 vald = (l_uint32)(-val + 0.5);
399 if (vald > maxval)
400 vald = maxval;
401
402 if (outdepth == 8)
403 SET_DATA_BYTE(lined, j, vald);
404 else if (outdepth == 16)
405 SET_DATA_TWO_BYTES(lined, j, vald);
406 else /* outdepth == 32 */
407 SET_DATA_FOUR_BYTES(lined, j, vald);
408 }
409 }
410
411 return pixd;
412 }
413
414
415 /*!
416 * \brief fpixDisplayMaxDynamicRange()
417 *
418 * \param[in] fpixs
419 * \return pixd 8 bpp, or NULL on error
420 */
421 PIX *
fpixDisplayMaxDynamicRange(FPIX * fpixs)422 fpixDisplayMaxDynamicRange(FPIX *fpixs)
423 {
424 l_uint8 dval;
425 l_int32 i, j, w, h, wpls, wpld;
426 l_float32 factor, sval, maxval;
427 l_float32 *lines, *datas;
428 l_uint32 *lined, *datad;
429 PIX *pixd;
430
431 PROCNAME("fpixDisplayMaxDynamicRange");
432
433 if (!fpixs)
434 return (PIX *)ERROR_PTR("fpixs not defined", procName, NULL);
435
436 fpixGetDimensions(fpixs, &w, &h);
437 datas = fpixGetData(fpixs);
438 wpls = fpixGetWpl(fpixs);
439
440 maxval = 0.0;
441 for (i = 0; i < h; i++) {
442 lines = datas + i * wpls;
443 for (j = 0; j < w; j++) {
444 sval = *(lines + j);
445 if (sval > maxval)
446 maxval = sval;
447 }
448 }
449
450 pixd = pixCreate(w, h, 8);
451 if (maxval == 0.0)
452 return pixd; /* all pixels are 0 */
453
454 datad = pixGetData(pixd);
455 wpld = pixGetWpl(pixd);
456 factor = 255. / maxval;
457 for (i = 0; i < h; i++) {
458 lines = datas + i * wpls;
459 lined = datad + i * wpld;
460 for (j = 0; j < w; j++) {
461 sval = *(lines + j);
462 if (sval < 0.0) sval = 0.0;
463 dval = (l_uint8)(factor * sval + 0.5);
464 SET_DATA_BYTE(lined, j, dval);
465 }
466 }
467
468 return pixd;
469 }
470
471
472 /*!
473 * \brief fpixConvertToDPix()
474 *
475 * \param[in] fpix
476 * \return dpix, or NULL on error
477 */
478 DPIX *
fpixConvertToDPix(FPIX * fpix)479 fpixConvertToDPix(FPIX *fpix)
480 {
481 l_int32 w, h, i, j, wpls, wpld;
482 l_float32 val;
483 l_float32 *datas, *lines;
484 l_float64 *datad, *lined;
485 DPIX *dpix;
486
487 PROCNAME("fpixConvertToDPix");
488
489 if (!fpix)
490 return (DPIX *)ERROR_PTR("fpix not defined", procName, NULL);
491
492 fpixGetDimensions(fpix, &w, &h);
493 if ((dpix = dpixCreate(w, h)) == NULL)
494 return (DPIX *)ERROR_PTR("dpix not made", procName, NULL);
495
496 datas = fpixGetData(fpix);
497 datad = dpixGetData(dpix);
498 wpls = fpixGetWpl(fpix);
499 wpld = dpixGetWpl(dpix); /* 8 byte words */
500 for (i = 0; i < h; i++) {
501 lines = datas + i * wpls;
502 lined = datad + i * wpld;
503 for (j = 0; j < w; j++) {
504 val = lines[j];
505 lined[j] = val;
506 }
507 }
508
509 return dpix;
510 }
511
512
513 /*!
514 * \brief dpixConvertToPix()
515 *
516 * \param[in] dpixs
517 * \param[in] outdepth 0, 8, 16 or 32 bpp
518 * \param[in] negvals L_CLIP_TO_ZERO, L_TAKE_ABSVAL
519 * \param[in] errorflag 1 to output error stats; 0 otherwise
520 * \return pixd, or NULL on error
521 *
522 * <pre>
523 * Notes:
524 * (1) Use %outdepth = 0 to programmatically determine the
525 * output depth. If no values are greater than 255,
526 * it will set outdepth = 8; otherwise to 16 or 32.
527 * (2) Because we are converting a float to an unsigned int
528 * with a specified dynamic range (8, 16 or 32 bits), errors
529 * can occur. If errorflag == TRUE, output the number
530 * of values out of range, both negative and positive.
531 * (3) If a pixel value is positive and out of range, clip to
532 * the maximum value represented at the outdepth of 8, 16
533 * or 32 bits.
534 * </pre>
535 */
536 PIX *
dpixConvertToPix(DPIX * dpixs,l_int32 outdepth,l_int32 negvals,l_int32 errorflag)537 dpixConvertToPix(DPIX *dpixs,
538 l_int32 outdepth,
539 l_int32 negvals,
540 l_int32 errorflag)
541 {
542 l_int32 w, h, i, j, wpls, wpld, maxval;
543 l_uint32 vald;
544 l_float64 val;
545 l_float64 *datas, *lines;
546 l_uint32 *datad, *lined;
547 PIX *pixd;
548
549 PROCNAME("dpixConvertToPix");
550
551 if (!dpixs)
552 return (PIX *)ERROR_PTR("dpixs not defined", procName, NULL);
553 if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL)
554 return (PIX *)ERROR_PTR("invalid negvals", procName, NULL);
555 if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32)
556 return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", procName, NULL);
557
558 dpixGetDimensions(dpixs, &w, &h);
559 datas = dpixGetData(dpixs);
560 wpls = dpixGetWpl(dpixs);
561
562 /* Adaptive determination of output depth */
563 if (outdepth == 0) {
564 outdepth = 8;
565 for (i = 0; i < h && outdepth < 32; i++) {
566 lines = datas + i * wpls;
567 for (j = 0; j < w && outdepth < 32; j++) {
568 if (lines[j] > 65535.5)
569 outdepth = 32;
570 else if (lines[j] > 255.5)
571 outdepth = 16;
572 }
573 }
574 }
575 maxval = 0xff;
576 if (outdepth == 16)
577 maxval = 0xffff;
578 else /* outdepth == 32 */
579 maxval = 0xffffffff;
580
581 /* Gather statistics if %errorflag = TRUE */
582 if (errorflag) {
583 l_int32 negs = 0;
584 l_int32 overvals = 0;
585 for (i = 0; i < h; i++) {
586 lines = datas + i * wpls;
587 for (j = 0; j < w; j++) {
588 val = lines[j];
589 if (val < 0.0)
590 negs++;
591 else if (val > maxval)
592 overvals++;
593 }
594 }
595 if (negs > 0)
596 L_ERROR("Number of negative values: %d\n", procName, negs);
597 if (overvals > 0)
598 L_ERROR("Number of too-large values: %d\n", procName, overvals);
599 }
600
601 /* Make the pix and convert the data */
602 if ((pixd = pixCreate(w, h, outdepth)) == NULL)
603 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
604 datad = pixGetData(pixd);
605 wpld = pixGetWpl(pixd);
606 for (i = 0; i < h; i++) {
607 lines = datas + i * wpls;
608 lined = datad + i * wpld;
609 for (j = 0; j < w; j++) {
610 val = lines[j];
611 if (val >= 0.0) {
612 vald = (l_uint32)(val + 0.5);
613 } else { /* val < 0.0 */
614 if (negvals == L_CLIP_TO_ZERO)
615 vald = 0;
616 else
617 vald = (l_uint32)(-val + 0.5);
618 }
619 if (vald > maxval)
620 vald = maxval;
621 if (outdepth == 8)
622 SET_DATA_BYTE(lined, j, vald);
623 else if (outdepth == 16)
624 SET_DATA_TWO_BYTES(lined, j, vald);
625 else /* outdepth == 32 */
626 SET_DATA_FOUR_BYTES(lined, j, vald);
627 }
628 }
629
630 return pixd;
631 }
632
633
634 /*!
635 * \brief dpixConvertToFPix()
636 *
637 * \param[in] dpix
638 * \return fpix, or NULL on error
639 */
640 FPIX *
dpixConvertToFPix(DPIX * dpix)641 dpixConvertToFPix(DPIX *dpix)
642 {
643 l_int32 w, h, i, j, wpls, wpld;
644 l_float64 val;
645 l_float32 *datad, *lined;
646 l_float64 *datas, *lines;
647 FPIX *fpix;
648
649 PROCNAME("dpixConvertToFPix");
650
651 if (!dpix)
652 return (FPIX *)ERROR_PTR("dpix not defined", procName, NULL);
653
654 dpixGetDimensions(dpix, &w, &h);
655 if ((fpix = fpixCreate(w, h)) == NULL)
656 return (FPIX *)ERROR_PTR("fpix not made", procName, NULL);
657
658 datas = dpixGetData(dpix);
659 datad = fpixGetData(fpix);
660 wpls = dpixGetWpl(dpix); /* 8 byte words */
661 wpld = fpixGetWpl(fpix);
662 for (i = 0; i < h; i++) {
663 lines = datas + i * wpls;
664 lined = datad + i * wpld;
665 for (j = 0; j < w; j++) {
666 val = lines[j];
667 lined[j] = (l_float32)val;
668 }
669 }
670
671 return fpix;
672 }
673
674
675
676 /*--------------------------------------------------------------------*
677 * Min/max value *
678 *--------------------------------------------------------------------*/
679 /*!
680 * \brief fpixGetMin()
681 *
682 * \param[in] fpix
683 * \param[out] pminval [optional] min value
684 * \param[out] pxminloc [optional] x location of min
685 * \param[out] pyminloc [optional] y location of min
686 * \return 0 if OK; 1 on error
687 */
688 l_int32
fpixGetMin(FPIX * fpix,l_float32 * pminval,l_int32 * pxminloc,l_int32 * pyminloc)689 fpixGetMin(FPIX *fpix,
690 l_float32 *pminval,
691 l_int32 *pxminloc,
692 l_int32 *pyminloc)
693 {
694 l_int32 i, j, w, h, wpl, xminloc, yminloc;
695 l_float32 *data, *line;
696 l_float32 minval;
697
698 PROCNAME("fpixGetMin");
699
700 if (!pminval && !pxminloc && !pyminloc)
701 return ERROR_INT("no return val requested", procName, 1);
702 if (pminval) *pminval = 0.0;
703 if (pxminloc) *pxminloc = 0;
704 if (pyminloc) *pyminloc = 0;
705 if (!fpix)
706 return ERROR_INT("fpix not defined", procName, 1);
707
708 minval = +1.0e20;
709 xminloc = 0;
710 yminloc = 0;
711 fpixGetDimensions(fpix, &w, &h);
712 data = fpixGetData(fpix);
713 wpl = fpixGetWpl(fpix);
714 for (i = 0; i < h; i++) {
715 line = data + i * wpl;
716 for (j = 0; j < w; j++) {
717 if (line[j] < minval) {
718 minval = line[j];
719 xminloc = j;
720 yminloc = i;
721 }
722 }
723 }
724
725 if (pminval) *pminval = minval;
726 if (pxminloc) *pxminloc = xminloc;
727 if (pyminloc) *pyminloc = yminloc;
728 return 0;
729 }
730
731
732 /*!
733 * \brief fpixGetMax()
734 *
735 * \param[in] fpix
736 * \param[out] pmaxval [optional] max value
737 * \param[out] pxmaxloc [optional] x location of max
738 * \param[out] pymaxloc [optional] y location of max
739 * \return 0 if OK; 1 on error
740 */
741 l_int32
fpixGetMax(FPIX * fpix,l_float32 * pmaxval,l_int32 * pxmaxloc,l_int32 * pymaxloc)742 fpixGetMax(FPIX *fpix,
743 l_float32 *pmaxval,
744 l_int32 *pxmaxloc,
745 l_int32 *pymaxloc)
746 {
747 l_int32 i, j, w, h, wpl, xmaxloc, ymaxloc;
748 l_float32 *data, *line;
749 l_float32 maxval;
750
751 PROCNAME("fpixGetMax");
752
753 if (!pmaxval && !pxmaxloc && !pymaxloc)
754 return ERROR_INT("no return val requested", procName, 1);
755 if (pmaxval) *pmaxval = 0.0;
756 if (pxmaxloc) *pxmaxloc = 0;
757 if (pymaxloc) *pymaxloc = 0;
758 if (!fpix)
759 return ERROR_INT("fpix not defined", procName, 1);
760
761 maxval = -1.0e20;
762 xmaxloc = 0;
763 ymaxloc = 0;
764 fpixGetDimensions(fpix, &w, &h);
765 data = fpixGetData(fpix);
766 wpl = fpixGetWpl(fpix);
767 for (i = 0; i < h; i++) {
768 line = data + i * wpl;
769 for (j = 0; j < w; j++) {
770 if (line[j] > maxval) {
771 maxval = line[j];
772 xmaxloc = j;
773 ymaxloc = i;
774 }
775 }
776 }
777
778 if (pmaxval) *pmaxval = maxval;
779 if (pxmaxloc) *pxmaxloc = xmaxloc;
780 if (pymaxloc) *pymaxloc = ymaxloc;
781 return 0;
782 }
783
784
785 /*!
786 * \brief dpixGetMin()
787 *
788 * \param[in] dpix
789 * \param[out] pminval [optional] min value
790 * \param[out] pxminloc [optional] x location of min
791 * \param[out] pyminloc [optional] y location of min
792 * \return 0 if OK; 1 on error
793 */
794 l_int32
dpixGetMin(DPIX * dpix,l_float64 * pminval,l_int32 * pxminloc,l_int32 * pyminloc)795 dpixGetMin(DPIX *dpix,
796 l_float64 *pminval,
797 l_int32 *pxminloc,
798 l_int32 *pyminloc)
799 {
800 l_int32 i, j, w, h, wpl, xminloc, yminloc;
801 l_float64 *data, *line;
802 l_float64 minval;
803
804 PROCNAME("dpixGetMin");
805
806 if (!pminval && !pxminloc && !pyminloc)
807 return ERROR_INT("no return val requested", procName, 1);
808 if (pminval) *pminval = 0.0;
809 if (pxminloc) *pxminloc = 0;
810 if (pyminloc) *pyminloc = 0;
811 if (!dpix)
812 return ERROR_INT("dpix not defined", procName, 1);
813
814 minval = +1.0e300;
815 xminloc = 0;
816 yminloc = 0;
817 dpixGetDimensions(dpix, &w, &h);
818 data = dpixGetData(dpix);
819 wpl = dpixGetWpl(dpix);
820 for (i = 0; i < h; i++) {
821 line = data + i * wpl;
822 for (j = 0; j < w; j++) {
823 if (line[j] < minval) {
824 minval = line[j];
825 xminloc = j;
826 yminloc = i;
827 }
828 }
829 }
830
831 if (pminval) *pminval = minval;
832 if (pxminloc) *pxminloc = xminloc;
833 if (pyminloc) *pyminloc = yminloc;
834 return 0;
835 }
836
837
838 /*!
839 * \brief dpixGetMax()
840 *
841 * \param[in] dpix
842 * \param[out] pmaxval [optional] max value
843 * \param[out] pxmaxloc [optional] x location of max
844 * \param[out] pymaxloc [optional] y location of max
845 * \return 0 if OK; 1 on error
846 */
847 l_int32
dpixGetMax(DPIX * dpix,l_float64 * pmaxval,l_int32 * pxmaxloc,l_int32 * pymaxloc)848 dpixGetMax(DPIX *dpix,
849 l_float64 *pmaxval,
850 l_int32 *pxmaxloc,
851 l_int32 *pymaxloc)
852 {
853 l_int32 i, j, w, h, wpl, xmaxloc, ymaxloc;
854 l_float64 *data, *line;
855 l_float64 maxval;
856
857 PROCNAME("dpixGetMax");
858
859 if (!pmaxval && !pxmaxloc && !pymaxloc)
860 return ERROR_INT("no return val requested", procName, 1);
861 if (pmaxval) *pmaxval = 0.0;
862 if (pxmaxloc) *pxmaxloc = 0;
863 if (pymaxloc) *pymaxloc = 0;
864 if (!dpix)
865 return ERROR_INT("dpix not defined", procName, 1);
866
867 maxval = -1.0e20;
868 xmaxloc = 0;
869 ymaxloc = 0;
870 dpixGetDimensions(dpix, &w, &h);
871 data = dpixGetData(dpix);
872 wpl = dpixGetWpl(dpix);
873 for (i = 0; i < h; i++) {
874 line = data + i * wpl;
875 for (j = 0; j < w; j++) {
876 if (line[j] > maxval) {
877 maxval = line[j];
878 xmaxloc = j;
879 ymaxloc = i;
880 }
881 }
882 }
883
884 if (pmaxval) *pmaxval = maxval;
885 if (pxmaxloc) *pxmaxloc = xmaxloc;
886 if (pymaxloc) *pymaxloc = ymaxloc;
887 return 0;
888 }
889
890
891 /*--------------------------------------------------------------------*
892 * Special integer scaling *
893 *--------------------------------------------------------------------*/
894 /*!
895 * \brief fpixScaleByInteger()
896 *
897 * \param[in] fpixs low resolution, subsampled
898 * \param[in] factor scaling factor
899 * \return fpixd interpolated result, or NULL on error
900 *
901 * <pre>
902 * Notes:
903 * (1) The width wd of fpixd is related to ws of fpixs by:
904 * wd = factor * (ws - 1) + 1 (and ditto for the height)
905 * We avoid special-casing boundary pixels in the interpolation
906 * by constructing fpixd by inserting (factor - 1) interpolated
907 * pixels between each pixel in fpixs. Then
908 * wd = ws + (ws - 1) * (factor - 1) (same as above)
909 * This also has the advantage that if we subsample by %factor,
910 * throwing out all the interpolated pixels, we regain the
911 * original low resolution fpix.
912 * </pre>
913 */
914 FPIX *
fpixScaleByInteger(FPIX * fpixs,l_int32 factor)915 fpixScaleByInteger(FPIX *fpixs,
916 l_int32 factor)
917 {
918 l_int32 i, j, k, m, ws, hs, wd, hd, wpls, wpld;
919 l_float32 val0, val1, val2, val3;
920 l_float32 *datas, *datad, *lines, *lined, *fract;
921 FPIX *fpixd;
922
923 PROCNAME("fpixScaleByInteger");
924
925 if (!fpixs)
926 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
927
928 fpixGetDimensions(fpixs, &ws, &hs);
929 wd = factor * (ws - 1) + 1;
930 hd = factor * (hs - 1) + 1;
931 fpixd = fpixCreate(wd, hd);
932 datas = fpixGetData(fpixs);
933 datad = fpixGetData(fpixd);
934 wpls = fpixGetWpl(fpixs);
935 wpld = fpixGetWpl(fpixd);
936 fract = (l_float32 *)LEPT_CALLOC(factor, sizeof(l_float32));
937 for (i = 0; i < factor; i++)
938 fract[i] = i / (l_float32)factor;
939 for (i = 0; i < hs - 1; i++) {
940 lines = datas + i * wpls;
941 for (j = 0; j < ws - 1; j++) {
942 val0 = lines[j];
943 val1 = lines[j + 1];
944 val2 = lines[wpls + j];
945 val3 = lines[wpls + j + 1];
946 for (k = 0; k < factor; k++) { /* rows of sub-block */
947 lined = datad + (i * factor + k) * wpld;
948 for (m = 0; m < factor; m++) { /* cols of sub-block */
949 lined[j * factor + m] =
950 val0 * (1.0 - fract[m]) * (1.0 - fract[k]) +
951 val1 * fract[m] * (1.0 - fract[k]) +
952 val2 * (1.0 - fract[m]) * fract[k] +
953 val3 * fract[m] * fract[k];
954 }
955 }
956 }
957 }
958
959 /* Do the right-most column of fpixd, skipping LR corner */
960 for (i = 0; i < hs - 1; i++) {
961 lines = datas + i * wpls;
962 val0 = lines[ws - 1];
963 val1 = lines[wpls + ws - 1];
964 for (k = 0; k < factor; k++) {
965 lined = datad + (i * factor + k) * wpld;
966 lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k];
967 }
968 }
969
970 /* Do the bottom-most row of fpixd */
971 lines = datas + (hs - 1) * wpls;
972 lined = datad + (hd - 1) * wpld;
973 for (j = 0; j < ws - 1; j++) {
974 val0 = lines[j];
975 val1 = lines[j + 1];
976 for (m = 0; m < factor; m++)
977 lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m];
978 lined[wd - 1] = lines[ws - 1]; /* LR corner */
979 }
980
981 LEPT_FREE(fract);
982 return fpixd;
983 }
984
985
986 /*!
987 * \brief dpixScaleByInteger()
988 *
989 * \param[in] dpixs low resolution, subsampled
990 * \param[in] factor scaling factor
991 * \return dpixd interpolated result, or NULL on error
992 *
993 * <pre>
994 * Notes:
995 * (1) The width wd of dpixd is related to ws of dpixs by:
996 * wd = factor * (ws - 1) + 1 (and ditto for the height)
997 * We avoid special-casing boundary pixels in the interpolation
998 * by constructing fpixd by inserting (factor - 1) interpolated
999 * pixels between each pixel in fpixs. Then
1000 * wd = ws + (ws - 1) * (factor - 1) (same as above)
1001 * This also has the advantage that if we subsample by %factor,
1002 * throwing out all the interpolated pixels, we regain the
1003 * original low resolution dpix.
1004 * </pre>
1005 */
1006 DPIX *
dpixScaleByInteger(DPIX * dpixs,l_int32 factor)1007 dpixScaleByInteger(DPIX *dpixs,
1008 l_int32 factor)
1009 {
1010 l_int32 i, j, k, m, ws, hs, wd, hd, wpls, wpld;
1011 l_float64 val0, val1, val2, val3;
1012 l_float64 *datas, *datad, *lines, *lined, *fract;
1013 DPIX *dpixd;
1014
1015 PROCNAME("dpixScaleByInteger");
1016
1017 if (!dpixs)
1018 return (DPIX *)ERROR_PTR("dpixs not defined", procName, NULL);
1019
1020 dpixGetDimensions(dpixs, &ws, &hs);
1021 wd = factor * (ws - 1) + 1;
1022 hd = factor * (hs - 1) + 1;
1023 dpixd = dpixCreate(wd, hd);
1024 datas = dpixGetData(dpixs);
1025 datad = dpixGetData(dpixd);
1026 wpls = dpixGetWpl(dpixs);
1027 wpld = dpixGetWpl(dpixd);
1028 fract = (l_float64 *)LEPT_CALLOC(factor, sizeof(l_float64));
1029 for (i = 0; i < factor; i++)
1030 fract[i] = i / (l_float64)factor;
1031 for (i = 0; i < hs - 1; i++) {
1032 lines = datas + i * wpls;
1033 for (j = 0; j < ws - 1; j++) {
1034 val0 = lines[j];
1035 val1 = lines[j + 1];
1036 val2 = lines[wpls + j];
1037 val3 = lines[wpls + j + 1];
1038 for (k = 0; k < factor; k++) { /* rows of sub-block */
1039 lined = datad + (i * factor + k) * wpld;
1040 for (m = 0; m < factor; m++) { /* cols of sub-block */
1041 lined[j * factor + m] =
1042 val0 * (1.0 - fract[m]) * (1.0 - fract[k]) +
1043 val1 * fract[m] * (1.0 - fract[k]) +
1044 val2 * (1.0 - fract[m]) * fract[k] +
1045 val3 * fract[m] * fract[k];
1046 }
1047 }
1048 }
1049 }
1050
1051 /* Do the right-most column of dpixd, skipping LR corner */
1052 for (i = 0; i < hs - 1; i++) {
1053 lines = datas + i * wpls;
1054 val0 = lines[ws - 1];
1055 val1 = lines[wpls + ws - 1];
1056 for (k = 0; k < factor; k++) {
1057 lined = datad + (i * factor + k) * wpld;
1058 lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k];
1059 }
1060 }
1061
1062 /* Do the bottom-most row of dpixd */
1063 lines = datas + (hs - 1) * wpls;
1064 lined = datad + (hd - 1) * wpld;
1065 for (j = 0; j < ws - 1; j++) {
1066 val0 = lines[j];
1067 val1 = lines[j + 1];
1068 for (m = 0; m < factor; m++)
1069 lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m];
1070 lined[wd - 1] = lines[ws - 1]; /* LR corner */
1071 }
1072
1073 LEPT_FREE(fract);
1074 return dpixd;
1075 }
1076
1077
1078 /*--------------------------------------------------------------------*
1079 * Arithmetic operations *
1080 *--------------------------------------------------------------------*/
1081 /*!
1082 * \brief fpixLinearCombination()
1083 *
1084 * \param[in] fpixd [optional]; this can be null, equal to fpixs1, or
1085 * different from fpixs1
1086 * \param[in] fpixs1 can be == to fpixd
1087 * \param[in] fpixs2
1088 * \param[in] a, b multiplication factors on fpixs1 and fpixs2, rsp.
1089 * \return fpixd always
1090 *
1091 * <pre>
1092 * Notes:
1093 * (1) Computes pixelwise linear combination: a * src1 + b * src2
1094 * (2) Alignment is to UL corner.
1095 * (3) There are 3 cases. The result can go to a new dest,
1096 * in-place to fpixs1, or to an existing input dest:
1097 * * fpixd == null: (src1 + src2) --> new fpixd
1098 * * fpixd == fpixs1: (src1 + src2) --> src1 (in-place)
1099 * * fpixd != fpixs1: (src1 + src2) --> input fpixd
1100 * (4) fpixs2 must be different from both fpixd and fpixs1.
1101 * </pre>
1102 */
1103 FPIX *
fpixLinearCombination(FPIX * fpixd,FPIX * fpixs1,FPIX * fpixs2,l_float32 a,l_float32 b)1104 fpixLinearCombination(FPIX *fpixd,
1105 FPIX *fpixs1,
1106 FPIX *fpixs2,
1107 l_float32 a,
1108 l_float32 b)
1109 {
1110 l_int32 i, j, ws, hs, w, h, wpls, wpld;
1111 l_float32 *datas, *datad, *lines, *lined;
1112
1113 PROCNAME("fpixLinearCombination");
1114
1115 if (!fpixs1)
1116 return (FPIX *)ERROR_PTR("fpixs1 not defined", procName, fpixd);
1117 if (!fpixs2)
1118 return (FPIX *)ERROR_PTR("fpixs2 not defined", procName, fpixd);
1119 if (fpixs1 == fpixs2)
1120 return (FPIX *)ERROR_PTR("fpixs1 == fpixs2", procName, fpixd);
1121 if (fpixs2 == fpixd)
1122 return (FPIX *)ERROR_PTR("fpixs2 == fpixd", procName, fpixd);
1123
1124 if (fpixs1 != fpixd)
1125 fpixd = fpixCopy(fpixd, fpixs1);
1126
1127 datas = fpixGetData(fpixs2);
1128 datad = fpixGetData(fpixd);
1129 wpls = fpixGetWpl(fpixs2);
1130 wpld = fpixGetWpl(fpixd);
1131 fpixGetDimensions(fpixs2, &ws, &hs);
1132 fpixGetDimensions(fpixd, &w, &h);
1133 w = L_MIN(ws, w);
1134 h = L_MIN(hs, h);
1135 for (i = 0; i < h; i++) {
1136 lines = datas + i * wpls;
1137 lined = datad + i * wpld;
1138 for (j = 0; j < w; j++)
1139 lined[j] = a * lined[j] + b * lines[j];
1140 }
1141
1142 return fpixd;
1143 }
1144
1145
1146 /*!
1147 * \brief fpixAddMultConstant()
1148 *
1149 * \param[in] fpix
1150 * \param[in] addc use 0.0 to skip the operation
1151 * \param[in] multc use 1.0 to skip the operation
1152 * \return 0 if OK, 1 on error
1153 *
1154 * <pre>
1155 * Notes:
1156 * (1) This is an in-place operation.
1157 * (2) It can be used to multiply each pixel by a constant,
1158 * and also to add a constant to each pixel. Multiplication
1159 * is done first.
1160 * </pre>
1161 */
1162 l_int32
fpixAddMultConstant(FPIX * fpix,l_float32 addc,l_float32 multc)1163 fpixAddMultConstant(FPIX *fpix,
1164 l_float32 addc,
1165 l_float32 multc)
1166 {
1167 l_int32 i, j, w, h, wpl;
1168 l_float32 *line, *data;
1169
1170 PROCNAME("fpixAddMultConstant");
1171
1172 if (!fpix)
1173 return ERROR_INT("fpix not defined", procName, 1);
1174
1175 if (addc == 0.0 && multc == 1.0)
1176 return 0;
1177
1178 fpixGetDimensions(fpix, &w, &h);
1179 data = fpixGetData(fpix);
1180 wpl = fpixGetWpl(fpix);
1181 for (i = 0; i < h; i++) {
1182 line = data + i * wpl;
1183 if (addc == 0.0) {
1184 for (j = 0; j < w; j++)
1185 line[j] *= multc;
1186 } else if (multc == 1.0) {
1187 for (j = 0; j < w; j++)
1188 line[j] += addc;
1189 } else {
1190 for (j = 0; j < w; j++) {
1191 line[j] = multc * line[j] + addc;
1192 }
1193 }
1194 }
1195
1196 return 0;
1197 }
1198
1199
1200 /*!
1201 * \brief dpixLinearCombination()
1202 *
1203 * \param[in] dpixd [optional]; this can be null, equal to dpixs1, or
1204 * different from dpixs1
1205 * \param[in] dpixs1 can be == to dpixd
1206 * \param[in] dpixs2
1207 * \param[in] a, b multiplication factors on dpixs1 and dpixs2, rsp.
1208 * \return dpixd always
1209 *
1210 * <pre>
1211 * Notes:
1212 * (1) Computes pixelwise linear combination: a * src1 + b * src2
1213 * (2) Alignment is to UL corner.
1214 * (3) There are 3 cases. The result can go to a new dest,
1215 * in-place to dpixs1, or to an existing input dest:
1216 * * dpixd == null: (src1 + src2) --> new dpixd
1217 * * dpixd == dpixs1: (src1 + src2) --> src1 (in-place)
1218 * * dpixd != dpixs1: (src1 + src2) --> input dpixd
1219 * (4) dpixs2 must be different from both dpixd and dpixs1.
1220 * </pre>
1221 */
1222 DPIX *
dpixLinearCombination(DPIX * dpixd,DPIX * dpixs1,DPIX * dpixs2,l_float32 a,l_float32 b)1223 dpixLinearCombination(DPIX *dpixd,
1224 DPIX *dpixs1,
1225 DPIX *dpixs2,
1226 l_float32 a,
1227 l_float32 b)
1228 {
1229 l_int32 i, j, ws, hs, w, h, wpls, wpld;
1230 l_float64 *datas, *datad, *lines, *lined;
1231
1232 PROCNAME("dpixLinearCombination");
1233
1234 if (!dpixs1)
1235 return (DPIX *)ERROR_PTR("dpixs1 not defined", procName, dpixd);
1236 if (!dpixs2)
1237 return (DPIX *)ERROR_PTR("dpixs2 not defined", procName, dpixd);
1238 if (dpixs1 == dpixs2)
1239 return (DPIX *)ERROR_PTR("dpixs1 == dpixs2", procName, dpixd);
1240 if (dpixs2 == dpixd)
1241 return (DPIX *)ERROR_PTR("dpixs2 == dpixd", procName, dpixd);
1242
1243 if (dpixs1 != dpixd)
1244 dpixd = dpixCopy(dpixd, dpixs1);
1245
1246 datas = dpixGetData(dpixs2);
1247 datad = dpixGetData(dpixd);
1248 wpls = dpixGetWpl(dpixs2);
1249 wpld = dpixGetWpl(dpixd);
1250 dpixGetDimensions(dpixs2, &ws, &hs);
1251 dpixGetDimensions(dpixd, &w, &h);
1252 w = L_MIN(ws, w);
1253 h = L_MIN(hs, h);
1254 for (i = 0; i < h; i++) {
1255 lines = datas + i * wpls;
1256 lined = datad + i * wpld;
1257 for (j = 0; j < w; j++)
1258 lined[j] = a * lined[j] + b * lines[j];
1259 }
1260
1261 return dpixd;
1262 }
1263
1264
1265 /*!
1266 * \brief dpixAddMultConstant()
1267 *
1268 * \param[in] dpix
1269 * \param[in] addc use 0.0 to skip the operation
1270 * \param[in] multc use 1.0 to skip the operation
1271 * \return 0 if OK, 1 on error
1272 *
1273 * <pre>
1274 * Notes:
1275 * (1) This is an in-place operation.
1276 * (2) It can be used to multiply each pixel by a constant,
1277 * and also to add a constant to each pixel. Multiplication
1278 * is done first.
1279 * </pre>
1280 */
1281 l_int32
dpixAddMultConstant(DPIX * dpix,l_float64 addc,l_float64 multc)1282 dpixAddMultConstant(DPIX *dpix,
1283 l_float64 addc,
1284 l_float64 multc)
1285 {
1286 l_int32 i, j, w, h, wpl;
1287 l_float64 *line, *data;
1288
1289 PROCNAME("dpixAddMultConstant");
1290
1291 if (!dpix)
1292 return ERROR_INT("dpix not defined", procName, 1);
1293
1294 if (addc == 0.0 && multc == 1.0)
1295 return 0;
1296
1297 dpixGetDimensions(dpix, &w, &h);
1298 data = dpixGetData(dpix);
1299 wpl = dpixGetWpl(dpix);
1300 for (i = 0; i < h; i++) {
1301 line = data + i * wpl;
1302 if (addc == 0.0) {
1303 for (j = 0; j < w; j++)
1304 line[j] *= multc;
1305 } else if (multc == 1.0) {
1306 for (j = 0; j < w; j++)
1307 line[j] += addc;
1308 } else {
1309 for (j = 0; j < w; j++)
1310 line[j] = multc * line[j] + addc;
1311 }
1312 }
1313
1314 return 0;
1315 }
1316
1317
1318 /*--------------------------------------------------------------------*
1319 * Set all *
1320 *--------------------------------------------------------------------*/
1321 /*!
1322 * \brief fpixSetAllArbitrary()
1323 *
1324 * \param[in] fpix
1325 * \param[in] inval to set at each pixel
1326 * \return 0 if OK, 1 on error
1327 */
1328 l_int32
fpixSetAllArbitrary(FPIX * fpix,l_float32 inval)1329 fpixSetAllArbitrary(FPIX *fpix,
1330 l_float32 inval)
1331 {
1332 l_int32 i, j, w, h;
1333 l_float32 *data, *line;
1334
1335 PROCNAME("fpixSetAllArbitrary");
1336
1337 if (!fpix)
1338 return ERROR_INT("fpix not defined", procName, 1);
1339
1340 fpixGetDimensions(fpix, &w, &h);
1341 data = fpixGetData(fpix);
1342 for (i = 0; i < h; i++) {
1343 line = data + i * w;
1344 for (j = 0; j < w; j++)
1345 *(line + j) = inval;
1346 }
1347
1348 return 0;
1349 }
1350
1351
1352 /*!
1353 * \brief dpixSetAllArbitrary()
1354 *
1355 * \param[in] dpix
1356 * \param[in] inval to set at each pixel
1357 * \return 0 if OK, 1 on error
1358 */
1359 l_int32
dpixSetAllArbitrary(DPIX * dpix,l_float64 inval)1360 dpixSetAllArbitrary(DPIX *dpix,
1361 l_float64 inval)
1362 {
1363 l_int32 i, j, w, h;
1364 l_float64 *data, *line;
1365
1366 PROCNAME("dpixSetAllArbitrary");
1367
1368 if (!dpix)
1369 return ERROR_INT("dpix not defined", procName, 1);
1370
1371 dpixGetDimensions(dpix, &w, &h);
1372 data = dpixGetData(dpix);
1373 for (i = 0; i < h; i++) {
1374 line = data + i * w;
1375 for (j = 0; j < w; j++)
1376 *(line + j) = inval;
1377 }
1378
1379 return 0;
1380 }
1381
1382
1383 /*--------------------------------------------------------------------*
1384 * Border functions *
1385 *--------------------------------------------------------------------*/
1386 /*!
1387 * \brief fpixAddBorder()
1388 *
1389 * \param[in] fpixs
1390 * \param[in] left, right, top, bot pixels on each side to be added
1391 * \return fpixd, or NULL on error
1392 *
1393 * <pre>
1394 * Notes:
1395 * (1) Adds border of '0' 32-bit pixels
1396 * </pre>
1397 */
1398 FPIX *
fpixAddBorder(FPIX * fpixs,l_int32 left,l_int32 right,l_int32 top,l_int32 bot)1399 fpixAddBorder(FPIX *fpixs,
1400 l_int32 left,
1401 l_int32 right,
1402 l_int32 top,
1403 l_int32 bot)
1404 {
1405 l_int32 ws, hs, wd, hd;
1406 FPIX *fpixd;
1407
1408 PROCNAME("fpixAddBorder");
1409
1410 if (!fpixs)
1411 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1412
1413 if (left <= 0 && right <= 0 && top <= 0 && bot <= 0)
1414 return fpixCopy(NULL, fpixs);
1415 fpixGetDimensions(fpixs, &ws, &hs);
1416 wd = ws + left + right;
1417 hd = hs + top + bot;
1418 if ((fpixd = fpixCreate(wd, hd)) == NULL)
1419 return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL);
1420
1421 fpixCopyResolution(fpixd, fpixs);
1422 fpixRasterop(fpixd, left, top, ws, hs, fpixs, 0, 0);
1423 return fpixd;
1424 }
1425
1426
1427 /*!
1428 * \brief fpixRemoveBorder()
1429 *
1430 * \param[in] fpixs
1431 * \param[in] left, right, top, bot pixels on each side to be removed
1432 * \return fpixd, or NULL on error
1433 */
1434 FPIX *
fpixRemoveBorder(FPIX * fpixs,l_int32 left,l_int32 right,l_int32 top,l_int32 bot)1435 fpixRemoveBorder(FPIX *fpixs,
1436 l_int32 left,
1437 l_int32 right,
1438 l_int32 top,
1439 l_int32 bot)
1440 {
1441 l_int32 ws, hs, wd, hd;
1442 FPIX *fpixd;
1443
1444 PROCNAME("fpixRemoveBorder");
1445
1446 if (!fpixs)
1447 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1448
1449 if (left <= 0 && right <= 0 && top <= 0 && bot <= 0)
1450 return fpixCopy(NULL, fpixs);
1451 fpixGetDimensions(fpixs, &ws, &hs);
1452 wd = ws - left - right;
1453 hd = hs - top - bot;
1454 if (wd <= 0 || hd <= 0)
1455 return (FPIX *)ERROR_PTR("width & height not both > 0", procName, NULL);
1456 if ((fpixd = fpixCreate(wd, hd)) == NULL)
1457 return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL);
1458
1459 fpixCopyResolution(fpixd, fpixs);
1460 fpixRasterop(fpixd, 0, 0, wd, hd, fpixs, left, top);
1461 return fpixd;
1462 }
1463
1464
1465
1466 /*!
1467 * \brief fpixAddMirroredBorder()
1468 *
1469 * \param[in] fpixs
1470 * \param[in] left, right, top, bot pixels on each side to be added
1471 * \return fpixd, or NULL on error
1472 *
1473 * <pre>
1474 * Notes:
1475 * (1) See pixAddMirroredBorder() for situations of usage.
1476 * </pre>
1477 */
1478 FPIX *
fpixAddMirroredBorder(FPIX * fpixs,l_int32 left,l_int32 right,l_int32 top,l_int32 bot)1479 fpixAddMirroredBorder(FPIX *fpixs,
1480 l_int32 left,
1481 l_int32 right,
1482 l_int32 top,
1483 l_int32 bot)
1484 {
1485 l_int32 i, j, w, h;
1486 FPIX *fpixd;
1487
1488 PROCNAME("fpixAddMirroredBorder");
1489
1490 if (!fpixs)
1491 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1492
1493 fpixd = fpixAddBorder(fpixs, left, right, top, bot);
1494 fpixGetDimensions(fpixs, &w, &h);
1495 for (j = 0; j < left; j++)
1496 fpixRasterop(fpixd, left - 1 - j, top, 1, h,
1497 fpixd, left + j, top);
1498 for (j = 0; j < right; j++)
1499 fpixRasterop(fpixd, left + w + j, top, 1, h,
1500 fpixd, left + w - 1 - j, top);
1501 for (i = 0; i < top; i++)
1502 fpixRasterop(fpixd, 0, top - 1 - i, left + w + right, 1,
1503 fpixd, 0, top + i);
1504 for (i = 0; i < bot; i++)
1505 fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1,
1506 fpixd, 0, top + h - 1 - i);
1507
1508 return fpixd;
1509 }
1510
1511
1512 /*!
1513 * \brief fpixAddContinuedBorder()
1514 *
1515 * \param[in] fpixs
1516 * \param[in] left, right, top, bot pixels on each side to be added
1517 * \return fpixd, or NULL on error
1518 *
1519 * <pre>
1520 * Notes:
1521 * (1) This adds pixels on each side whose values are equal to
1522 * the value on the closest boundary pixel.
1523 * </pre>
1524 */
1525 FPIX *
fpixAddContinuedBorder(FPIX * fpixs,l_int32 left,l_int32 right,l_int32 top,l_int32 bot)1526 fpixAddContinuedBorder(FPIX *fpixs,
1527 l_int32 left,
1528 l_int32 right,
1529 l_int32 top,
1530 l_int32 bot)
1531 {
1532 l_int32 i, j, w, h;
1533 FPIX *fpixd;
1534
1535 PROCNAME("fpixAddContinuedBorder");
1536
1537 if (!fpixs)
1538 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1539
1540 fpixd = fpixAddBorder(fpixs, left, right, top, bot);
1541 fpixGetDimensions(fpixs, &w, &h);
1542 for (j = 0; j < left; j++)
1543 fpixRasterop(fpixd, j, top, 1, h, fpixd, left, top);
1544 for (j = 0; j < right; j++)
1545 fpixRasterop(fpixd, left + w + j, top, 1, h, fpixd, left + w - 1, top);
1546 for (i = 0; i < top; i++)
1547 fpixRasterop(fpixd, 0, i, left + w + right, 1, fpixd, 0, top);
1548 for (i = 0; i < bot; i++)
1549 fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1,
1550 fpixd, 0, top + h - 1);
1551
1552 return fpixd;
1553 }
1554
1555
1556 /*!
1557 * \brief fpixAddSlopeBorder()
1558 *
1559 * \param[in] fpixs
1560 * \param[in] left, right, top, bot pixels on each side to be added
1561 * \return fpixd, or NULL on error
1562 *
1563 * <pre>
1564 * Notes:
1565 * (1) This adds pixels on each side whose values have a normal
1566 * derivative equal to the normal derivative at the boundary
1567 * of fpixs.
1568 * </pre>
1569 */
1570 FPIX *
fpixAddSlopeBorder(FPIX * fpixs,l_int32 left,l_int32 right,l_int32 top,l_int32 bot)1571 fpixAddSlopeBorder(FPIX *fpixs,
1572 l_int32 left,
1573 l_int32 right,
1574 l_int32 top,
1575 l_int32 bot)
1576 {
1577 l_int32 i, j, w, h, fullw, fullh;
1578 l_float32 val1, val2, del;
1579 FPIX *fpixd;
1580
1581 PROCNAME("fpixAddSlopeBorder");
1582
1583 if (!fpixs)
1584 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1585
1586 fpixd = fpixAddBorder(fpixs, left, right, top, bot);
1587 fpixGetDimensions(fpixs, &w, &h);
1588
1589 /* Left */
1590 for (i = top; i < top + h; i++) {
1591 fpixGetPixel(fpixd, left, i, &val1);
1592 fpixGetPixel(fpixd, left + 1, i, &val2);
1593 del = val1 - val2;
1594 for (j = 0; j < left; j++)
1595 fpixSetPixel(fpixd, j, i, val1 + del * (left - j));
1596 }
1597
1598 /* Right */
1599 fullw = left + w + right;
1600 for (i = top; i < top + h; i++) {
1601 fpixGetPixel(fpixd, left + w - 1, i, &val1);
1602 fpixGetPixel(fpixd, left + w - 2, i, &val2);
1603 del = val1 - val2;
1604 for (j = left + w; j < fullw; j++)
1605 fpixSetPixel(fpixd, j, i, val1 + del * (j - left - w + 1));
1606 }
1607
1608 /* Top */
1609 for (j = 0; j < fullw; j++) {
1610 fpixGetPixel(fpixd, j, top, &val1);
1611 fpixGetPixel(fpixd, j, top + 1, &val2);
1612 del = val1 - val2;
1613 for (i = 0; i < top; i++)
1614 fpixSetPixel(fpixd, j, i, val1 + del * (top - i));
1615 }
1616
1617 /* Bottom */
1618 fullh = top + h + bot;
1619 for (j = 0; j < fullw; j++) {
1620 fpixGetPixel(fpixd, j, top + h - 1, &val1);
1621 fpixGetPixel(fpixd, j, top + h - 2, &val2);
1622 del = val1 - val2;
1623 for (i = top + h; i < fullh; i++)
1624 fpixSetPixel(fpixd, j, i, val1 + del * (i - top - h + 1));
1625 }
1626
1627 return fpixd;
1628 }
1629
1630
1631 /*--------------------------------------------------------------------*
1632 * Simple rasterop *
1633 *--------------------------------------------------------------------*/
1634 /*!
1635 * \brief fpixRasterop()
1636 *
1637 * \param[in] fpixd dest fpix
1638 * \param[in] dx x val of UL corner of dest rectangle
1639 * \param[in] dy y val of UL corner of dest rectangle
1640 * \param[in] dw width of dest rectangle
1641 * \param[in] dh height of dest rectangle
1642 * \param[in] fpixs src fpix
1643 * \param[in] sx x val of UL corner of src rectangle
1644 * \param[in] sy y val of UL corner of src rectangle
1645 * \return 0 if OK; 1 on error.
1646 *
1647 * <pre>
1648 * Notes:
1649 * (1) This is similar in structure to pixRasterop(), except
1650 * it only allows copying from the source into the destination.
1651 * For that reason, no op code is necessary. Additionally,
1652 * all pixels are 32 bit words (float values), which makes
1653 * the copy very simple.
1654 * (2) Clipping of both src and dest fpix are done automatically.
1655 * (3) This allows in-place copying, without checking to see if
1656 * the result is valid: use for in-place with caution!
1657 * </pre>
1658 */
1659 l_int32
fpixRasterop(FPIX * fpixd,l_int32 dx,l_int32 dy,l_int32 dw,l_int32 dh,FPIX * fpixs,l_int32 sx,l_int32 sy)1660 fpixRasterop(FPIX *fpixd,
1661 l_int32 dx,
1662 l_int32 dy,
1663 l_int32 dw,
1664 l_int32 dh,
1665 FPIX *fpixs,
1666 l_int32 sx,
1667 l_int32 sy)
1668 {
1669 l_int32 fsw, fsh, fdw, fdh, dhangw, shangw, dhangh, shangh;
1670 l_int32 i, j, wpls, wpld;
1671 l_float32 *datas, *datad, *lines, *lined;
1672
1673 PROCNAME("fpixRasterop");
1674
1675 if (!fpixs)
1676 return ERROR_INT("fpixs not defined", procName, 1);
1677 if (!fpixd)
1678 return ERROR_INT("fpixd not defined", procName, 1);
1679
1680 /* -------------------------------------------------------- *
1681 * Clip to maximum rectangle with both src and dest *
1682 * -------------------------------------------------------- */
1683 fpixGetDimensions(fpixs, &fsw, &fsh);
1684 fpixGetDimensions(fpixd, &fdw, &fdh);
1685
1686 /* First clip horizontally (sx, dx, dw) */
1687 if (dx < 0) {
1688 sx -= dx; /* increase sx */
1689 dw += dx; /* reduce dw */
1690 dx = 0;
1691 }
1692 if (sx < 0) {
1693 dx -= sx; /* increase dx */
1694 dw += sx; /* reduce dw */
1695 sx = 0;
1696 }
1697 dhangw = dx + dw - fdw; /* rect overhang of dest to right */
1698 if (dhangw > 0)
1699 dw -= dhangw; /* reduce dw */
1700 shangw = sx + dw - fsw; /* rect overhang of src to right */
1701 if (shangw > 0)
1702 dw -= shangw; /* reduce dw */
1703
1704 /* Then clip vertically (sy, dy, dh) */
1705 if (dy < 0) {
1706 sy -= dy; /* increase sy */
1707 dh += dy; /* reduce dh */
1708 dy = 0;
1709 }
1710 if (sy < 0) {
1711 dy -= sy; /* increase dy */
1712 dh += sy; /* reduce dh */
1713 sy = 0;
1714 }
1715 dhangh = dy + dh - fdh; /* rect overhang of dest below */
1716 if (dhangh > 0)
1717 dh -= dhangh; /* reduce dh */
1718 shangh = sy + dh - fsh; /* rect overhang of src below */
1719 if (shangh > 0)
1720 dh -= shangh; /* reduce dh */
1721
1722 /* if clipped entirely, quit */
1723 if ((dw <= 0) || (dh <= 0))
1724 return 0;
1725
1726 /* -------------------------------------------------------- *
1727 * Copy block of data *
1728 * -------------------------------------------------------- */
1729 datas = fpixGetData(fpixs);
1730 datad = fpixGetData(fpixd);
1731 wpls = fpixGetWpl(fpixs);
1732 wpld = fpixGetWpl(fpixd);
1733 datas += sy * wpls + sx; /* at UL corner of block */
1734 datad += dy * wpld + dx; /* at UL corner of block */
1735 for (i = 0; i < dh; i++) {
1736 lines = datas + i * wpls;
1737 lined = datad + i * wpld;
1738 for (j = 0; j < dw; j++) {
1739 *lined = *lines;
1740 lines++;
1741 lined++;
1742 }
1743 }
1744
1745 return 0;
1746 }
1747
1748
1749 /*--------------------------------------------------------------------*
1750 * Rotation by multiples of 90 degrees *
1751 *--------------------------------------------------------------------*/
1752 /*!
1753 * \brief fpixRotateOrth()
1754 *
1755 * \param[in] fpixs
1756 * \param[in] quads 0-3; number of 90 degree cw rotations
1757 * \return fpixd, or NULL on error
1758 */
1759 FPIX *
fpixRotateOrth(FPIX * fpixs,l_int32 quads)1760 fpixRotateOrth(FPIX *fpixs,
1761 l_int32 quads)
1762 {
1763 PROCNAME("fpixRotateOrth");
1764
1765 if (!fpixs)
1766 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1767 if (quads < 0 || quads > 3)
1768 return (FPIX *)ERROR_PTR("quads not in {0,1,2,3}", procName, NULL);
1769
1770 if (quads == 0)
1771 return fpixCopy(NULL, fpixs);
1772 else if (quads == 1)
1773 return fpixRotate90(fpixs, 1);
1774 else if (quads == 2)
1775 return fpixRotate180(NULL, fpixs);
1776 else /* quads == 3 */
1777 return fpixRotate90(fpixs, -1);
1778 }
1779
1780
1781 /*!
1782 * \brief fpixRotate180()
1783 *
1784 * \param[in] fpixd [optional]; can be null, equal to fpixs,
1785 * or different from fpixs
1786 * \param[in] fpixs
1787 * \return fpixd, or NULL on error
1788 *
1789 * <pre>
1790 * Notes:
1791 * (1) This does a 180 rotation of the image about the center,
1792 * which is equivalent to a left-right flip about a vertical
1793 * line through the image center, followed by a top-bottom
1794 * flip about a horizontal line through the image center.
1795 * (2) There are 3 cases for input:
1796 * (a) fpixd == null (creates a new fpixd)
1797 * (b) fpixd == fpixs (in-place operation)
1798 * (c) fpixd != fpixs (existing fpixd)
1799 * (3) For clarity, use these three patterns, respectively:
1800 * (a) fpixd = fpixRotate180(NULL, fpixs);
1801 * (b) fpixRotate180(fpixs, fpixs);
1802 * (c) fpixRotate180(fpixd, fpixs);
1803 * </pre>
1804 */
1805 FPIX *
fpixRotate180(FPIX * fpixd,FPIX * fpixs)1806 fpixRotate180(FPIX *fpixd,
1807 FPIX *fpixs)
1808 {
1809 PROCNAME("fpixRotate180");
1810
1811 if (!fpixs)
1812 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1813
1814 /* Prepare pixd for in-place operation */
1815 if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL)
1816 return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL);
1817
1818 fpixFlipLR(fpixd, fpixd);
1819 fpixFlipTB(fpixd, fpixd);
1820 return fpixd;
1821 }
1822
1823
1824 /*!
1825 * \brief fpixRotate90()
1826 *
1827 * \param[in] fpixs
1828 * \param[in] direction 1 = clockwise, -1 = counter-clockwise
1829 * \return fpixd, or NULL on error
1830 *
1831 * <pre>
1832 * Notes:
1833 * (1) This does a 90 degree rotation of the image about the center,
1834 * either cw or ccw, returning a new pix.
1835 * (2) The direction must be either 1 (cw) or -1 (ccw).
1836 * </pre>
1837 */
1838 FPIX *
fpixRotate90(FPIX * fpixs,l_int32 direction)1839 fpixRotate90(FPIX *fpixs,
1840 l_int32 direction)
1841 {
1842 l_int32 i, j, wd, hd, wpls, wpld;
1843 l_float32 *datas, *datad, *lines, *lined;
1844 FPIX *fpixd;
1845
1846 PROCNAME("fpixRotate90");
1847
1848 if (!fpixs)
1849 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1850 if (direction != 1 && direction != -1)
1851 return (FPIX *)ERROR_PTR("invalid direction", procName, NULL);
1852
1853 fpixGetDimensions(fpixs, &hd, &wd);
1854 if ((fpixd = fpixCreate(wd, hd)) == NULL)
1855 return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL);
1856 fpixCopyResolution(fpixd, fpixs);
1857
1858 datas = fpixGetData(fpixs);
1859 wpls = fpixGetWpl(fpixs);
1860 datad = fpixGetData(fpixd);
1861 wpld = fpixGetWpl(fpixd);
1862 if (direction == 1) { /* clockwise */
1863 for (i = 0; i < hd; i++) {
1864 lined = datad + i * wpld;
1865 lines = datas + (wd - 1) * wpls;
1866 for (j = 0; j < wd; j++) {
1867 lined[j] = lines[i];
1868 lines -= wpls;
1869 }
1870 }
1871 } else { /* ccw */
1872 for (i = 0; i < hd; i++) {
1873 lined = datad + i * wpld;
1874 lines = datas;
1875 for (j = 0; j < wd; j++) {
1876 lined[j] = lines[hd - 1 - i];
1877 lines += wpls;
1878 }
1879 }
1880 }
1881
1882 return fpixd;
1883 }
1884
1885
1886 /*!
1887 * \brief pixFlipLR()
1888 *
1889 * \param[in] fpixd [optional]; can be null, equal to fpixs,
1890 * or different from fpixs
1891 * \param[in] fpixs
1892 * \return fpixd, or NULL on error
1893 *
1894 * <pre>
1895 * Notes:
1896 * (1) This does a left-right flip of the image, which is
1897 * equivalent to a rotation out of the plane about a
1898 * vertical line through the image center.
1899 * (2) There are 3 cases for input:
1900 * (a) fpixd == null (creates a new fpixd)
1901 * (b) fpixd == fpixs (in-place operation)
1902 * (c) fpixd != fpixs (existing fpixd)
1903 * (3) For clarity, use these three patterns, respectively:
1904 * (a) fpixd = fpixFlipLR(NULL, fpixs);
1905 * (b) fpixFlipLR(fpixs, fpixs);
1906 * (c) fpixFlipLR(fpixd, fpixs);
1907 * (4) If an existing fpixd is not the same size as fpixs, the
1908 * image data will be reallocated.
1909 * </pre>
1910 */
1911 FPIX *
fpixFlipLR(FPIX * fpixd,FPIX * fpixs)1912 fpixFlipLR(FPIX *fpixd,
1913 FPIX *fpixs)
1914 {
1915 l_int32 i, j, w, h, wpl, bpl;
1916 l_float32 *line, *data, *buffer;
1917
1918 PROCNAME("fpixFlipLR");
1919
1920 if (!fpixs)
1921 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1922
1923 fpixGetDimensions(fpixs, &w, &h);
1924
1925 /* Prepare fpixd for in-place operation */
1926 if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL)
1927 return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL);
1928
1929 data = fpixGetData(fpixd);
1930 wpl = fpixGetWpl(fpixd); /* 4-byte words */
1931 bpl = 4 * wpl;
1932 if ((buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32))) == NULL) {
1933 fpixDestroy(&fpixd);
1934 return (FPIX *)ERROR_PTR("buffer not made", procName, NULL);
1935 }
1936 for (i = 0; i < h; i++) {
1937 line = data + i * wpl;
1938 memcpy(buffer, line, bpl);
1939 for (j = 0; j < w; j++)
1940 line[j] = buffer[w - 1 - j];
1941 }
1942 LEPT_FREE(buffer);
1943 return fpixd;
1944 }
1945
1946
1947 /*!
1948 * \brief fpixFlipTB()
1949 *
1950 * \param[in] fpixd [optional]; can be null, equal to fpixs,
1951 * or different from fpixs
1952 * \param[in] fpixs
1953 * \return fpixd, or NULL on error
1954 *
1955 * <pre>
1956 * Notes:
1957 * (1) This does a top-bottom flip of the image, which is
1958 * equivalent to a rotation out of the plane about a
1959 * horizontal line through the image center.
1960 * (2) There are 3 cases for input:
1961 * (a) fpixd == null (creates a new fpixd)
1962 * (b) fpixd == fpixs (in-place operation)
1963 * (c) fpixd != fpixs (existing fpixd)
1964 * (3) For clarity, use these three patterns, respectively:
1965 * (a) fpixd = fpixFlipTB(NULL, fpixs);
1966 * (b) fpixFlipTB(fpixs, fpixs);
1967 * (c) fpixFlipTB(fpixd, fpixs);
1968 * (4) If an existing fpixd is not the same size as fpixs, the
1969 * image data will be reallocated.
1970 * </pre>
1971 */
1972 FPIX *
fpixFlipTB(FPIX * fpixd,FPIX * fpixs)1973 fpixFlipTB(FPIX *fpixd,
1974 FPIX *fpixs)
1975 {
1976 l_int32 i, k, h, h2, wpl, bpl;
1977 l_float32 *linet, *lineb, *data, *buffer;
1978
1979 PROCNAME("fpixFlipTB");
1980
1981 if (!fpixs)
1982 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
1983
1984 /* Prepare fpixd for in-place operation */
1985 if ((fpixd = fpixCopy(fpixd, fpixs)) == NULL)
1986 return (FPIX *)ERROR_PTR("fpixd not made", procName, NULL);
1987
1988 data = fpixGetData(fpixd);
1989 wpl = fpixGetWpl(fpixd);
1990 fpixGetDimensions(fpixd, NULL, &h);
1991 if ((buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32))) == NULL) {
1992 fpixDestroy(&fpixd);
1993 return (FPIX *)ERROR_PTR("buffer not made", procName, NULL);
1994 }
1995 h2 = h / 2;
1996 bpl = 4 * wpl;
1997 for (i = 0, k = h - 1; i < h2; i++, k--) {
1998 linet = data + i * wpl;
1999 lineb = data + k * wpl;
2000 memcpy(buffer, linet, bpl);
2001 memcpy(linet, lineb, bpl);
2002 memcpy(lineb, buffer, bpl);
2003 }
2004 LEPT_FREE(buffer);
2005 return fpixd;
2006 }
2007
2008
2009 /*--------------------------------------------------------------------*
2010 * Affine and projective interpolated transforms *
2011 *--------------------------------------------------------------------*/
2012 /*!
2013 * \brief fpixAffinePta()
2014 *
2015 * \param[in] fpixs 8 bpp
2016 * \param[in] ptad 4 pts of final coordinate space
2017 * \param[in] ptas 4 pts of initial coordinate space
2018 * \param[in] border size of extension with constant normal derivative
2019 * \param[in] inval value brought in; typ. 0
2020 * \return fpixd, or NULL on error
2021 *
2022 * <pre>
2023 * Notes:
2024 * (1) If %border > 0, all four sides are extended by that distance,
2025 * and removed after the transformation is finished. Pixels
2026 * that would be brought in to the trimmed result from outside
2027 * the extended region are assigned %inval. The purpose of
2028 * extending the image is to avoid such assignments.
2029 * (2) On the other hand, you may want to give all pixels that
2030 * are brought in from outside fpixs a specific value. In that
2031 * case, set %border == 0.
2032 * </pre>
2033 */
2034 FPIX *
fpixAffinePta(FPIX * fpixs,PTA * ptad,PTA * ptas,l_int32 border,l_float32 inval)2035 fpixAffinePta(FPIX *fpixs,
2036 PTA *ptad,
2037 PTA *ptas,
2038 l_int32 border,
2039 l_float32 inval)
2040 {
2041 l_float32 *vc;
2042 PTA *ptas2, *ptad2;
2043 FPIX *fpixs2, *fpixd, *fpixd2;
2044
2045 PROCNAME("fpixAffinePta");
2046
2047 if (!fpixs)
2048 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
2049 if (!ptas)
2050 return (FPIX *)ERROR_PTR("ptas not defined", procName, NULL);
2051 if (!ptad)
2052 return (FPIX *)ERROR_PTR("ptad not defined", procName, NULL);
2053
2054 /* If a border is to be added, also translate the ptas */
2055 if (border > 0) {
2056 ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0);
2057 ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0);
2058 fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border);
2059 } else {
2060 ptas2 = ptaClone(ptas);
2061 ptad2 = ptaClone(ptad);
2062 fpixs2 = fpixClone(fpixs);
2063 }
2064
2065 /* Get backwards transform from dest to src, and apply it */
2066 getAffineXformCoeffs(ptad2, ptas2, &vc);
2067 fpixd2 = fpixAffine(fpixs2, vc, inval);
2068 fpixDestroy(&fpixs2);
2069 ptaDestroy(&ptas2);
2070 ptaDestroy(&ptad2);
2071 LEPT_FREE(vc);
2072
2073 if (border == 0)
2074 return fpixd2;
2075
2076 /* Remove the added border */
2077 fpixd = fpixRemoveBorder(fpixd2, border, border, border, border);
2078 fpixDestroy(&fpixd2);
2079 return fpixd;
2080 }
2081
2082
2083 /*!
2084 * \brief fpixAffine()
2085 *
2086 * \param[in] fpixs 8 bpp
2087 * \param[in] vc vector of 8 coefficients for projective transformation
2088 * \param[in] inval value brought in; typ. 0
2089 * \return fpixd, or NULL on error
2090 */
2091 FPIX *
fpixAffine(FPIX * fpixs,l_float32 * vc,l_float32 inval)2092 fpixAffine(FPIX *fpixs,
2093 l_float32 *vc,
2094 l_float32 inval)
2095 {
2096 l_int32 i, j, w, h, wpld;
2097 l_float32 val;
2098 l_float32 *datas, *datad, *lined;
2099 l_float32 x, y;
2100 FPIX *fpixd;
2101
2102 PROCNAME("fpixAffine");
2103
2104 if (!fpixs)
2105 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
2106 fpixGetDimensions(fpixs, &w, &h);
2107 if (!vc)
2108 return (FPIX *)ERROR_PTR("vc not defined", procName, NULL);
2109
2110 datas = fpixGetData(fpixs);
2111 fpixd = fpixCreateTemplate(fpixs);
2112 fpixSetAllArbitrary(fpixd, inval);
2113 datad = fpixGetData(fpixd);
2114 wpld = fpixGetWpl(fpixd);
2115
2116 /* Iterate over destination pixels */
2117 for (i = 0; i < h; i++) {
2118 lined = datad + i * wpld;
2119 for (j = 0; j < w; j++) {
2120 /* Compute float src pixel location corresponding to (i,j) */
2121 affineXformPt(vc, j, i, &x, &y);
2122 linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val);
2123 *(lined + j) = val;
2124 }
2125 }
2126
2127 return fpixd;
2128 }
2129
2130
2131 /*!
2132 * \brief fpixProjectivePta()
2133 *
2134 * \param[in] fpixs 8 bpp
2135 * \param[in] ptad 4 pts of final coordinate space
2136 * \param[in] ptas 4 pts of initial coordinate space
2137 * \param[in] border size of extension with constant normal derivative
2138 * \param[in] inval value brought in; typ. 0
2139 * \return fpixd, or NULL on error
2140 *
2141 * <pre>
2142 * Notes:
2143 * (1) If %border > 0, all four sides are extended by that distance,
2144 * and removed after the transformation is finished. Pixels
2145 * that would be brought in to the trimmed result from outside
2146 * the extended region are assigned %inval. The purpose of
2147 * extending the image is to avoid such assignments.
2148 * (2) On the other hand, you may want to give all pixels that
2149 * are brought in from outside fpixs a specific value. In that
2150 * case, set %border == 0.
2151 * </pre>
2152 */
2153 FPIX *
fpixProjectivePta(FPIX * fpixs,PTA * ptad,PTA * ptas,l_int32 border,l_float32 inval)2154 fpixProjectivePta(FPIX *fpixs,
2155 PTA *ptad,
2156 PTA *ptas,
2157 l_int32 border,
2158 l_float32 inval)
2159 {
2160 l_float32 *vc;
2161 PTA *ptas2, *ptad2;
2162 FPIX *fpixs2, *fpixd, *fpixd2;
2163
2164 PROCNAME("fpixProjectivePta");
2165
2166 if (!fpixs)
2167 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
2168 if (!ptas)
2169 return (FPIX *)ERROR_PTR("ptas not defined", procName, NULL);
2170 if (!ptad)
2171 return (FPIX *)ERROR_PTR("ptad not defined", procName, NULL);
2172
2173 /* If a border is to be added, also translate the ptas */
2174 if (border > 0) {
2175 ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0);
2176 ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0);
2177 fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border);
2178 } else {
2179 ptas2 = ptaClone(ptas);
2180 ptad2 = ptaClone(ptad);
2181 fpixs2 = fpixClone(fpixs);
2182 }
2183
2184 /* Get backwards transform from dest to src, and apply it */
2185 getProjectiveXformCoeffs(ptad2, ptas2, &vc);
2186 fpixd2 = fpixProjective(fpixs2, vc, inval);
2187 fpixDestroy(&fpixs2);
2188 ptaDestroy(&ptas2);
2189 ptaDestroy(&ptad2);
2190 LEPT_FREE(vc);
2191
2192 if (border == 0)
2193 return fpixd2;
2194
2195 /* Remove the added border */
2196 fpixd = fpixRemoveBorder(fpixd2, border, border, border, border);
2197 fpixDestroy(&fpixd2);
2198 return fpixd;
2199 }
2200
2201
2202 /*!
2203 * \brief fpixProjective()
2204 *
2205 * \param[in] fpixs 8 bpp
2206 * \param[in] vc vector of 8 coefficients for projective transformation
2207 * \param[in] inval value brought in; typ. 0
2208 * \return fpixd, or NULL on error
2209 */
2210 FPIX *
fpixProjective(FPIX * fpixs,l_float32 * vc,l_float32 inval)2211 fpixProjective(FPIX *fpixs,
2212 l_float32 *vc,
2213 l_float32 inval)
2214 {
2215 l_int32 i, j, w, h, wpld;
2216 l_float32 val;
2217 l_float32 *datas, *datad, *lined;
2218 l_float32 x, y;
2219 FPIX *fpixd;
2220
2221 PROCNAME("fpixProjective");
2222
2223 if (!fpixs)
2224 return (FPIX *)ERROR_PTR("fpixs not defined", procName, NULL);
2225 fpixGetDimensions(fpixs, &w, &h);
2226 if (!vc)
2227 return (FPIX *)ERROR_PTR("vc not defined", procName, NULL);
2228
2229 datas = fpixGetData(fpixs);
2230 fpixd = fpixCreateTemplate(fpixs);
2231 fpixSetAllArbitrary(fpixd, inval);
2232 datad = fpixGetData(fpixd);
2233 wpld = fpixGetWpl(fpixd);
2234
2235 /* Iterate over destination pixels */
2236 for (i = 0; i < h; i++) {
2237 lined = datad + i * wpld;
2238 for (j = 0; j < w; j++) {
2239 /* Compute float src pixel location corresponding to (i,j) */
2240 projectiveXformPt(vc, j, i, &x, &y);
2241 linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val);
2242 *(lined + j) = val;
2243 }
2244 }
2245
2246 return fpixd;
2247 }
2248
2249
2250 /*!
2251 * \brief linearInterpolatePixelFloat()
2252 *
2253 * \param[in] datas ptr to beginning of float image data
2254 * \param[in] w, h of image
2255 * \param[in] x, y floating pt location for evaluation
2256 * \param[in] inval float value brought in from the outside when the
2257 * input x,y location is outside the image
2258 * \param[out] pval interpolated float value
2259 * \return 0 if OK, 1 on error
2260 *
2261 * <pre>
2262 * Notes:
2263 * (1) This is a standard linear interpolation function. It is
2264 * equivalent to area weighting on each component, and
2265 * avoids "jaggies" when rendering sharp edges.
2266 * </pre>
2267 */
2268 l_int32
linearInterpolatePixelFloat(l_float32 * datas,l_int32 w,l_int32 h,l_float32 x,l_float32 y,l_float32 inval,l_float32 * pval)2269 linearInterpolatePixelFloat(l_float32 *datas,
2270 l_int32 w,
2271 l_int32 h,
2272 l_float32 x,
2273 l_float32 y,
2274 l_float32 inval,
2275 l_float32 *pval)
2276 {
2277 l_int32 xpm, ypm, xp, yp, xf, yf;
2278 l_float32 v00, v01, v10, v11;
2279 l_float32 *lines;
2280
2281 PROCNAME("linearInterpolatePixelFloat");
2282
2283 if (!pval)
2284 return ERROR_INT("&val not defined", procName, 1);
2285 *pval = inval;
2286 if (!datas)
2287 return ERROR_INT("datas not defined", procName, 1);
2288
2289 /* Skip if off the edge */
2290 if (x < 0.0 || y < 0.0 || x > w - 2.0 || y > h - 2.0)
2291 return 0;
2292
2293 xpm = (l_int32)(16.0 * x + 0.5);
2294 ypm = (l_int32)(16.0 * y + 0.5);
2295 xp = xpm >> 4;
2296 yp = ypm >> 4;
2297 xf = xpm & 0x0f;
2298 yf = ypm & 0x0f;
2299
2300 #if DEBUG
2301 if (xf < 0 || yf < 0)
2302 fprintf(stderr, "xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf);
2303 #endif /* DEBUG */
2304
2305 /* Interpolate by area weighting. */
2306 lines = datas + yp * w;
2307 v00 = (16.0 - xf) * (16.0 - yf) * (*(lines + xp));
2308 v10 = xf * (16.0 - yf) * (*(lines + xp + 1));
2309 v01 = (16.0 - xf) * yf * (*(lines + w + xp));
2310 v11 = (l_float32)(xf) * yf * (*(lines + w + xp + 1));
2311 *pval = (v00 + v01 + v10 + v11) / 256.0;
2312 return 0;
2313 }
2314
2315
2316 /*--------------------------------------------------------------------*
2317 * Thresholding to 1 bpp Pix *
2318 *--------------------------------------------------------------------*/
2319 /*!
2320 * \brief fpixThresholdToPix()
2321 *
2322 * \param[in] fpix
2323 * \param[in] thresh
2324 * \return pixd 1 bpp, or NULL on error
2325 *
2326 * <pre>
2327 * Notes:
2328 * (1) For all values of fpix that are <= thresh, sets the pixel
2329 * in pixd to 1.
2330 * </pre>
2331 */
2332 PIX *
fpixThresholdToPix(FPIX * fpix,l_float32 thresh)2333 fpixThresholdToPix(FPIX *fpix,
2334 l_float32 thresh)
2335 {
2336 l_int32 i, j, w, h, wpls, wpld;
2337 l_float32 *datas, *lines;
2338 l_uint32 *datad, *lined;
2339 PIX *pixd;
2340
2341 PROCNAME("fpixThresholdToPix");
2342
2343 if (!fpix)
2344 return (PIX *)ERROR_PTR("fpix not defined", procName, NULL);
2345
2346 fpixGetDimensions(fpix, &w, &h);
2347 datas = fpixGetData(fpix);
2348 wpls = fpixGetWpl(fpix);
2349 pixd = pixCreate(w, h, 1);
2350 datad = pixGetData(pixd);
2351 wpld = pixGetWpl(pixd);
2352 for (i = 0; i < h; i++) {
2353 lines = datas + i * wpls;
2354 lined = datad + i * wpld;
2355 for (j = 0; j < w; j++) {
2356 if (lines[j] <= thresh)
2357 SET_DATA_BIT(lined, j);
2358 }
2359 }
2360
2361 return pixd;
2362 }
2363
2364
2365 /*--------------------------------------------------------------------*
2366 * Generate function from components *
2367 *--------------------------------------------------------------------*/
2368 /*!
2369 * \brief pixComponentFunction()
2370 *
2371 * \param[in] pix 32 bpp rgb
2372 * \param[in] rnum, gnum, bnum coefficients for numerator
2373 * \param[in] rdenom, gdenom, bdenom coefficients for denominator
2374 * \return fpixd, or NULL on error
2375 *
2376 * <pre>
2377 * Notes:
2378 * (1) This stores a function of the component values of each
2379 * input pixel in %fpixd.
2380 * (2) The function is a ratio of linear combinations of component values.
2381 * There are two special cases for denominator coefficients:
2382 * (a) The denominator is 1.0: input 0 for all denominator coefficients
2383 * (b) Only one component is used in the denominator: input 1.0
2384 * for that denominator component and 0.0 for the other two.
2385 * (3) If the denominator is 0, multiply by an arbitrary number that
2386 * is much larger than 1. Choose 256 "arbitrarily".
2387 *
2388 * </pre>
2389 */
2390 FPIX *
pixComponentFunction(PIX * pix,l_float32 rnum,l_float32 gnum,l_float32 bnum,l_float32 rdenom,l_float32 gdenom,l_float32 bdenom)2391 pixComponentFunction(PIX *pix,
2392 l_float32 rnum,
2393 l_float32 gnum,
2394 l_float32 bnum,
2395 l_float32 rdenom,
2396 l_float32 gdenom,
2397 l_float32 bdenom)
2398 {
2399 l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, zerodenom, onedenom;
2400 l_float32 fnum, fdenom;
2401 l_uint32 *datas, *lines;
2402 l_float32 *datad, *lined, *recip;
2403 FPIX *fpixd;
2404
2405 PROCNAME("pixComponentFunction");
2406
2407 if (!pix || pixGetDepth(pix) != 32)
2408 return (FPIX *)ERROR_PTR("pix undefined or not 32 bpp", procName, NULL);
2409
2410 pixGetDimensions(pix, &w, &h, NULL);
2411 datas = pixGetData(pix);
2412 wpls = pixGetWpl(pix);
2413 fpixd = fpixCreate(w, h);
2414 datad = fpixGetData(fpixd);
2415 wpld = fpixGetWpl(fpixd);
2416 zerodenom = (rdenom == 0.0 && gdenom == 0.0 && bdenom == 0.0) ? 1: 0;
2417 onedenom = ((rdenom == 1.0 && gdenom == 0.0 && bdenom == 0.0) ||
2418 (rdenom == 0.0 && gdenom == 1.0 && bdenom == 0.0) ||
2419 (rdenom == 0.0 && gdenom == 0.0 && bdenom == 1.0)) ? 1 : 0;
2420 recip = NULL;
2421 if (onedenom) {
2422 recip = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32));
2423 recip[0] = 256; /* arbitrary large number */
2424 for (i = 1; i < 256; i++)
2425 recip[i] = 1.0 / (l_float32)i;
2426 }
2427 for (i = 0; i < h; i++) {
2428 lines = datas + i * wpls;
2429 lined = datad + i * wpld;
2430 if (zerodenom) {
2431 for (j = 0; j < w; j++) {
2432 extractRGBValues(lines[j], &rval, &gval, &bval);
2433 lined[j] = rnum * rval + gnum * gval + bnum * bval;
2434 }
2435 } else if (onedenom && rdenom == 1.0) {
2436 for (j = 0; j < w; j++) {
2437 extractRGBValues(lines[j], &rval, &gval, &bval);
2438 lined[j]
2439 = recip[rval] * (rnum * rval + gnum * gval + bnum * bval);
2440 }
2441 } else if (onedenom && gdenom == 1.0) {
2442 for (j = 0; j < w; j++) {
2443 extractRGBValues(lines[j], &rval, &gval, &bval);
2444 lined[j]
2445 = recip[gval] * (rnum * rval + gnum * gval + bnum * bval);
2446 }
2447 } else if (onedenom && bdenom == 1.0) {
2448 for (j = 0; j < w; j++) {
2449 extractRGBValues(lines[j], &rval, &gval, &bval);
2450 lined[j]
2451 = recip[bval] * (rnum * rval + gnum * gval + bnum * bval);
2452 }
2453 } else { /* general case */
2454 for (j = 0; j < w; j++) {
2455 extractRGBValues(lines[j], &rval, &gval, &bval);
2456 fnum = rnum * rval + gnum * gval + bnum * bval;
2457 fdenom = rdenom * rval + gdenom * gval + bdenom * bval;
2458 lined[j] = (fdenom == 0) ? 256.0 * fnum : fnum / fdenom;
2459 }
2460 }
2461 }
2462
2463 LEPT_FREE(recip);
2464 return fpixd;
2465 }
2466