Leptonica 1.82.0
Image processing and image analysis suite
edge.c
Go to the documentation of this file.
1/*====================================================================*
2 - Copyright (C) 2001 Leptonica. All rights reserved.
3 -
4 - Redistribution and use in source and binary forms, with or without
5 - modification, are permitted provided that the following conditions
6 - are met:
7 - 1. Redistributions of source code must retain the above copyright
8 - notice, this list of conditions and the following disclaimer.
9 - 2. Redistributions in binary form must reproduce the above
10 - copyright notice, this list of conditions and the following
11 - disclaimer in the documentation and/or other materials
12 - provided with the distribution.
13 -
14 - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18 - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *====================================================================*/
26
61#ifdef HAVE_CONFIG_H
62#include <config_auto.h>
63#endif /* HAVE_CONFIG_H */
64
65#include "allheaders.h"
66
67/*----------------------------------------------------------------------*
68 * Sobel edge detecting filter *
69 *----------------------------------------------------------------------*/
93PIX *
95 l_int32 orientflag)
96{
97l_int32 w, h, d, i, j, wplt, wpld, gx, gy, vald;
98l_int32 val1, val2, val3, val4, val5, val6, val7, val8, val9;
99l_uint32 *datat, *linet, *datad, *lined;
100PIX *pixt, *pixd;
101
102 PROCNAME("pixSobelEdgeFilter");
103
104 if (!pixs)
105 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
106 pixGetDimensions(pixs, &w, &h, &d);
107 if (d != 8)
108 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
109 if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES &&
110 orientflag != L_ALL_EDGES)
111 return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL);
112
113 /* Add 1 pixel (mirrored) to each side of the image. */
114 if ((pixt = pixAddMirroredBorder(pixs, 1, 1, 1, 1)) == NULL)
115 return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
116
117 /* Compute filter output at each location. */
118 pixd = pixCreateTemplate(pixs);
119 datat = pixGetData(pixt);
120 wplt = pixGetWpl(pixt);
121 datad = pixGetData(pixd);
122 wpld = pixGetWpl(pixd);
123 for (i = 0; i < h; i++) {
124 linet = datat + i * wplt;
125 lined = datad + i * wpld;
126 for (j = 0; j < w; j++) {
127 if (j == 0) { /* start a new row */
128 val1 = GET_DATA_BYTE(linet, j);
129 val2 = GET_DATA_BYTE(linet + wplt, j);
130 val3 = GET_DATA_BYTE(linet + 2 * wplt, j);
131 val4 = GET_DATA_BYTE(linet, j + 1);
132 val5 = GET_DATA_BYTE(linet + wplt, j + 1);
133 val6 = GET_DATA_BYTE(linet + 2 * wplt, j + 1);
134 val7 = GET_DATA_BYTE(linet, j + 2);
135 val8 = GET_DATA_BYTE(linet + wplt, j + 2);
136 val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2);
137 } else { /* shift right by 1 pixel; update incrementally */
138 val1 = val4;
139 val2 = val5;
140 val3 = val6;
141 val4 = val7;
142 val5 = val8;
143 val6 = val9;
144 val7 = GET_DATA_BYTE(linet, j + 2);
145 val8 = GET_DATA_BYTE(linet + wplt, j + 2);
146 val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2);
147 }
148 if (orientflag == L_HORIZONTAL_EDGES)
149 vald = L_ABS(val1 + 2 * val4 + val7
150 - val3 - 2 * val6 - val9) >> 3;
151 else if (orientflag == L_VERTICAL_EDGES)
152 vald = L_ABS(val1 + 2 * val2 + val3 - val7
153 - 2 * val8 - val9) >> 3;
154 else { /* L_ALL_EDGES */
155 gx = L_ABS(val1 + 2 * val2 + val3 - val7
156 - 2 * val8 - val9) >> 3;
157 gy = L_ABS(val1 + 2 * val4 + val7
158 - val3 - 2 * val6 - val9) >> 3;
159 vald = L_MIN(255, gx + gy);
160 }
161 SET_DATA_BYTE(lined, j, vald);
162 }
163 }
164
165 pixDestroy(&pixt);
166 return pixd;
167}
168
169
170/*----------------------------------------------------------------------*
171 * Two-sided edge gradient filter *
172 *----------------------------------------------------------------------*/
201PIX *
203 l_int32 orientflag)
204{
205l_int32 w, h, d, i, j, wpls, wpld;
206l_int32 cval, rval, bval, val, lgrad, rgrad, tgrad, bgrad;
207l_uint32 *datas, *lines, *datad, *lined;
208PIX *pixd;
209
210 PROCNAME("pixTwoSidedEdgeFilter");
211
212 if (!pixs)
213 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
214 pixGetDimensions(pixs, &w, &h, &d);
215 if (d != 8)
216 return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
217 if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES)
218 return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL);
219
220 pixd = pixCreateTemplate(pixs);
221 datas = pixGetData(pixs);
222 wpls = pixGetWpl(pixs);
223 datad = pixGetData(pixd);
224 wpld = pixGetWpl(pixd);
225 if (orientflag == L_VERTICAL_EDGES) {
226 for (i = 0; i < h; i++) {
227 lines = datas + i * wpls;
228 lined = datad + i * wpld;
229 cval = GET_DATA_BYTE(lines, 1);
230 lgrad = cval - GET_DATA_BYTE(lines, 0);
231 for (j = 1; j < w - 1; j++) {
232 rval = GET_DATA_BYTE(lines, j + 1);
233 rgrad = rval - cval;
234 if (lgrad * rgrad > 0) {
235 if (lgrad < 0)
236 val = -L_MAX(lgrad, rgrad);
237 else
238 val = L_MIN(lgrad, rgrad);
239 SET_DATA_BYTE(lined, j, val);
240 }
241 lgrad = rgrad;
242 cval = rval;
243 }
244 }
245 }
246 else { /* L_HORIZONTAL_EDGES) */
247 for (j = 0; j < w; j++) {
248 lines = datas + wpls;
249 cval = GET_DATA_BYTE(lines, j); /* for line 1 */
250 tgrad = cval - GET_DATA_BYTE(datas, j);
251 for (i = 1; i < h - 1; i++) {
252 lines += wpls; /* for line i + 1 */
253 lined = datad + i * wpld;
254 bval = GET_DATA_BYTE(lines, j);
255 bgrad = bval - cval;
256 if (tgrad * bgrad > 0) {
257 if (tgrad < 0)
258 val = -L_MAX(tgrad, bgrad);
259 else
260 val = L_MIN(tgrad, bgrad);
261 SET_DATA_BYTE(lined, j, val);
262 }
263 tgrad = bgrad;
264 cval = bval;
265 }
266 }
267 }
268
269 return pixd;
270}
271
272
273/*----------------------------------------------------------------------*
274 * Measurement of edge smoothness *
275 *----------------------------------------------------------------------*/
311l_ok
313 l_int32 side,
314 l_int32 minjump,
315 l_int32 minreversal,
316 l_float32 *pjpl,
317 l_float32 *pjspl,
318 l_float32 *prpl,
319 const char *debugfile)
320{
321l_int32 i, n, val, nval, diff, njumps, jumpsum, nreversal;
322NUMA *na, *nae;
323
324 PROCNAME("pixMeasureEdgeSmoothness");
325
326 if (pjpl) *pjpl = 0.0;
327 if (pjspl) *pjspl = 0.0;
328 if (prpl) *prpl = 0.0;
329 if (!pjpl && !pjspl && !prpl && !debugfile)
330 return ERROR_INT("no output requested", procName, 1);
331 if (!pixs || pixGetDepth(pixs) != 1)
332 return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
333 if (side != L_FROM_LEFT && side != L_FROM_RIGHT &&
334 side != L_FROM_TOP && side != L_FROM_BOT)
335 return ERROR_INT("invalid side", procName, 1);
336 if (minjump < 1)
337 return ERROR_INT("invalid minjump; must be >= 1", procName, 1);
338 if (minreversal < 1)
339 return ERROR_INT("invalid minreversal; must be >= 1", procName, 1);
340
341 if ((na = pixGetEdgeProfile(pixs, side, debugfile)) == NULL)
342 return ERROR_INT("edge profile not made", procName, 1);
343 if ((n = numaGetCount(na)) < 2) {
344 numaDestroy(&na);
345 return 0;
346 }
347
348 if (pjpl || pjspl) {
349 jumpsum = 0;
350 njumps = 0;
351 numaGetIValue(na, 0, &val);
352 for (i = 1; i < n; i++) {
353 numaGetIValue(na, i, &nval);
354 diff = L_ABS(nval - val);
355 if (diff >= minjump) {
356 njumps++;
357 jumpsum += diff;
358 }
359 val = nval;
360 }
361 if (pjpl)
362 *pjpl = (l_float32)njumps / (l_float32)(n - 1);
363 if (pjspl)
364 *pjspl = (l_float32)jumpsum / (l_float32)(n - 1);
365 }
366
367 if (prpl) {
368 nae = numaFindExtrema(na, minreversal, NULL);
369 nreversal = numaGetCount(nae) - 1;
370 *prpl = (l_float32)nreversal / (l_float32)(n - 1);
371 numaDestroy(&nae);
372 }
373
374 numaDestroy(&na);
375 return 0;
376}
377
378
388NUMA *
390 l_int32 side,
391 const char *debugfile)
392{
393l_int32 x, y, w, h, loc, index, ival;
394l_uint32 val;
395NUMA *na;
396PIX *pixt;
397PIXCMAP *cmap;
398
399 PROCNAME("pixGetEdgeProfile");
400
401 if (!pixs || pixGetDepth(pixs) != 1)
402 return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
403 if (side != L_FROM_LEFT && side != L_FROM_RIGHT &&
404 side != L_FROM_TOP && side != L_FROM_BOT)
405 return (NUMA *)ERROR_PTR("invalid side", procName, NULL);
406
407 pixGetDimensions(pixs, &w, &h, NULL);
408 if (side == L_FROM_LEFT || side == L_FROM_RIGHT)
409 na = numaCreate(h);
410 else
411 na = numaCreate(w);
412 if (side == L_FROM_LEFT) {
413 pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_LEFT, &loc);
414 loc = (loc == w - 1) ? 0 : loc + 1; /* back to the left edge */
415 numaAddNumber(na, loc);
416 for (y = 1; y < h; y++) {
417 pixGetPixel(pixs, loc, y, &val);
418 if (val == 1) {
419 pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc);
420 } else {
421 pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc);
422 loc = (loc == w - 1) ? 0 : loc + 1;
423 }
424 numaAddNumber(na, loc);
425 }
426 }
427 else if (side == L_FROM_RIGHT) {
428 pixGetLastOffPixelInRun(pixs, w - 1, 0, L_FROM_RIGHT, &loc);
429 loc = (loc == 0) ? w - 1 : loc - 1; /* back to the right edge */
430 numaAddNumber(na, loc);
431 for (y = 1; y < h; y++) {
432 pixGetPixel(pixs, loc, y, &val);
433 if (val == 1) {
434 pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc);
435 } else {
436 pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc);
437 loc = (loc == 0) ? w - 1 : loc - 1;
438 }
439 numaAddNumber(na, loc);
440 }
441 }
442 else if (side == L_FROM_TOP) {
443 pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_TOP, &loc);
444 loc = (loc == h - 1) ? 0 : loc + 1; /* back to the top edge */
445 numaAddNumber(na, loc);
446 for (x = 1; x < w; x++) {
447 pixGetPixel(pixs, x, loc, &val);
448 if (val == 1) {
449 pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_BOT, &loc);
450 } else {
451 pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_TOP, &loc);
452 loc = (loc == h - 1) ? 0 : loc + 1;
453 }
454 numaAddNumber(na, loc);
455 }
456 }
457 else { /* side == L_FROM_BOT */
458 pixGetLastOffPixelInRun(pixs, 0, h - 1, L_FROM_BOT, &loc);
459 loc = (loc == 0) ? h - 1 : loc - 1; /* back to the bottom edge */
460 numaAddNumber(na, loc);
461 for (x = 1; x < w; x++) {
462 pixGetPixel(pixs, x, loc, &val);
463 if (val == 1) {
464 pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_TOP, &loc);
465 } else {
466 pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_BOT, &loc);
467 loc = (loc == 0) ? h - 1 : loc - 1;
468 }
469 numaAddNumber(na, loc);
470 }
471 }
472
473 if (debugfile) {
474 pixt = pixConvertTo8(pixs, TRUE);
475 cmap = pixGetColormap(pixt);
476 pixcmapAddColor(cmap, 255, 0, 0);
477 index = pixcmapGetCount(cmap) - 1;
478 if (side == L_FROM_LEFT || side == L_FROM_RIGHT) {
479 for (y = 0; y < h; y++) {
480 numaGetIValue(na, y, &ival);
481 pixSetPixel(pixt, ival, y, index);
482 }
483 } else { /* L_FROM_TOP or L_FROM_BOT */
484 for (x = 0; x < w; x++) {
485 numaGetIValue(na, x, &ival);
486 pixSetPixel(pixt, x, ival, index);
487 }
488 }
489 pixWrite(debugfile, pixt, IFF_PNG);
490 pixDestroy(&pixt);
491 }
492
493 return na;
494}
495
496
497/*
498 * \brief pixGetLastOffPixelInRun()
499 *
500 * \param[in] pixs 1 bpp
501 * \param[in] x, y starting location
502 * \param[in] direction L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT
503 * \param[out] ploc location in scan direction coordinate
504 * of last OFF pixel found
505 * \return 0 if OK, 1 on error
506 *
507 * <pre>
508 * Notes:
509 * (1) Search starts from the pixel at (x, y), which is OFF.
510 * (2) It returns the location in the scan direction of the last
511 * pixel in the current run that is OFF.
512 * (3) The interface for these pixel run functions is cleaner when
513 * you ask for the last pixel in the current run, rather than the
514 * first pixel of opposite polarity that is found, because the
515 * current run may go to the edge of the image, in which case
516 * no pixel of opposite polarity is found.
517 * </pre>
518 */
519l_ok
520pixGetLastOffPixelInRun(PIX *pixs,
521 l_int32 x,
522 l_int32 y,
523 l_int32 direction,
524 l_int32 *ploc)
525{
526l_int32 loc, w, h;
527l_uint32 val;
528
529 PROCNAME("pixGetLastOffPixelInRun");
530
531 if (!ploc)
532 return ERROR_INT("&loc not defined", procName, 1);
533 *ploc = 0;
534 if (!pixs || pixGetDepth(pixs) != 1)
535 return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
536 if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT &&
537 direction != L_FROM_TOP && direction != L_FROM_BOT)
538 return ERROR_INT("invalid side", procName, 1);
539
540 pixGetDimensions(pixs, &w, &h, NULL);
541 if (direction == L_FROM_LEFT) {
542 for (loc = x; loc < w; loc++) {
543 pixGetPixel(pixs, loc, y, &val);
544 if (val == 1)
545 break;
546 }
547 *ploc = loc - 1;
548 } else if (direction == L_FROM_RIGHT) {
549 for (loc = x; loc >= 0; loc--) {
550 pixGetPixel(pixs, loc, y, &val);
551 if (val == 1)
552 break;
553 }
554 *ploc = loc + 1;
555 }
556 else if (direction == L_FROM_TOP) {
557 for (loc = y; loc < h; loc++) {
558 pixGetPixel(pixs, x, loc, &val);
559 if (val == 1)
560 break;
561 }
562 *ploc = loc - 1;
563 }
564 else if (direction == L_FROM_BOT) {
565 for (loc = y; loc >= 0; loc--) {
566 pixGetPixel(pixs, x, loc, &val);
567 if (val == 1)
568 break;
569 }
570 *ploc = loc + 1;
571 }
572 return 0;
573}
574
575
576/*
577 * \brief pixGetLastOnPixelInRun()
578 *
579 * \param[in] pixs 1 bpp
580 * \param[in] x, y starting location
581 * \param[in] direction L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT
582 * \param[out] ploc location in scan direction coordinate
583 * of first ON pixel found
584 * \return 0 if OK, 1 on error
585 *
586 * <pre>
587 * Notes:
588 * (1) Search starts from the pixel at (x, y), which is ON.
589 * (2) It returns the location in the scan direction of the last
590 * pixel in the current run that is ON.
591 * </pre>
592 */
593l_int32
594pixGetLastOnPixelInRun(PIX *pixs,
595 l_int32 x,
596 l_int32 y,
597 l_int32 direction,
598 l_int32 *ploc)
599{
600l_int32 loc, w, h;
601l_uint32 val;
602
603 PROCNAME("pixLastOnPixelInRun");
604
605 if (!ploc)
606 return ERROR_INT("&loc not defined", procName, 1);
607 *ploc = 0;
608 if (!pixs || pixGetDepth(pixs) != 1)
609 return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
610 if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT &&
611 direction != L_FROM_TOP && direction != L_FROM_BOT)
612 return ERROR_INT("invalid side", procName, 1);
613
614 pixGetDimensions(pixs, &w, &h, NULL);
615 if (direction == L_FROM_LEFT) {
616 for (loc = x; loc < w; loc++) {
617 pixGetPixel(pixs, loc, y, &val);
618 if (val == 0)
619 break;
620 }
621 *ploc = loc - 1;
622 } else if (direction == L_FROM_RIGHT) {
623 for (loc = x; loc >= 0; loc--) {
624 pixGetPixel(pixs, loc, y, &val);
625 if (val == 0)
626 break;
627 }
628 *ploc = loc + 1;
629 }
630 else if (direction == L_FROM_TOP) {
631 for (loc = y; loc < h; loc++) {
632 pixGetPixel(pixs, x, loc, &val);
633 if (val == 0)
634 break;
635 }
636 *ploc = loc - 1;
637 }
638 else if (direction == L_FROM_BOT) {
639 for (loc = y; loc >= 0; loc--) {
640 pixGetPixel(pixs, x, loc, &val);
641 if (val == 0)
642 break;
643 }
644 *ploc = loc + 1;
645 }
646 return 0;
647}
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
NUMA * pixGetEdgeProfile(PIX *pixs, l_int32 side, const char *debugfile)
pixGetEdgeProfile()
Definition: edge.c:389
PIX * pixSobelEdgeFilter(PIX *pixs, l_int32 orientflag)
pixSobelEdgeFilter()
Definition: edge.c:94
PIX * pixTwoSidedEdgeFilter(PIX *pixs, l_int32 orientflag)
pixTwoSidedEdgeFilter()
Definition: edge.c:202
l_ok pixMeasureEdgeSmoothness(PIX *pixs, l_int32 side, l_int32 minjump, l_int32 minreversal, l_float32 *pjpl, l_float32 *pjspl, l_float32 *prpl, const char *debugfile)
pixMeasureEdgeSmoothness()
Definition: edge.c:312
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:478
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:366
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:658
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:754
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:194
NUMA * numaFindExtrema(NUMA *nas, l_float32 delta, NUMA **pnav)
numaFindExtrema()
Definition: numafunc2.c:2550
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:263
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:190
PIX * pixAddMirroredBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot)
pixAddMirroredBorder()
Definition: pix2.c:2101
@ L_FROM_BOT
Definition: pix.h:1037
@ L_FROM_LEFT
Definition: pix.h:1034
@ L_FROM_RIGHT
Definition: pix.h:1035
@ L_FROM_TOP
Definition: pix.h:1036
@ L_ALL_EDGES
Definition: pix.h:1005
@ L_VERTICAL_EDGES
Definition: pix.h:1004
@ L_HORIZONTAL_EDGES
Definition: pix.h:1003
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
Definition: array.h:71
Definition: pix.h:139