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