Leptonica  1.77.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 #include "allheaders.h"
62 
63 
64 /*----------------------------------------------------------------------*
65  * Sobel edge detecting filter *
66  *----------------------------------------------------------------------*/
90 PIX *
92  l_int32 orientflag)
93 {
94 l_int32 w, h, d, i, j, wplt, wpld, gx, gy, vald;
95 l_int32 val1, val2, val3, val4, val5, val6, val7, val8, val9;
96 l_uint32 *datat, *linet, *datad, *lined;
97 PIX *pixt, *pixd;
98 
99  PROCNAME("pixSobelEdgeFilter");
100 
101  if (!pixs)
102  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
103  pixGetDimensions(pixs, &w, &h, &d);
104  if (d != 8)
105  return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
106  if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES &&
107  orientflag != L_ALL_EDGES)
108  return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL);
109 
110  /* Add 1 pixel (mirrored) to each side of the image. */
111  if ((pixt = pixAddMirroredBorder(pixs, 1, 1, 1, 1)) == NULL)
112  return (PIX *)ERROR_PTR("pixt not made", procName, NULL);
113 
114  /* Compute filter output at each location. */
115  pixd = pixCreateTemplate(pixs);
116  datat = pixGetData(pixt);
117  wplt = pixGetWpl(pixt);
118  datad = pixGetData(pixd);
119  wpld = pixGetWpl(pixd);
120  for (i = 0; i < h; i++) {
121  linet = datat + i * wplt;
122  lined = datad + i * wpld;
123  for (j = 0; j < w; j++) {
124  if (j == 0) { /* start a new row */
125  val1 = GET_DATA_BYTE(linet, j);
126  val2 = GET_DATA_BYTE(linet + wplt, j);
127  val3 = GET_DATA_BYTE(linet + 2 * wplt, j);
128  val4 = GET_DATA_BYTE(linet, j + 1);
129  val5 = GET_DATA_BYTE(linet + wplt, j + 1);
130  val6 = GET_DATA_BYTE(linet + 2 * wplt, j + 1);
131  val7 = GET_DATA_BYTE(linet, j + 2);
132  val8 = GET_DATA_BYTE(linet + wplt, j + 2);
133  val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2);
134  } else { /* shift right by 1 pixel; update incrementally */
135  val1 = val4;
136  val2 = val5;
137  val3 = val6;
138  val4 = val7;
139  val5 = val8;
140  val6 = val9;
141  val7 = GET_DATA_BYTE(linet, j + 2);
142  val8 = GET_DATA_BYTE(linet + wplt, j + 2);
143  val9 = GET_DATA_BYTE(linet + 2 * wplt, j + 2);
144  }
145  if (orientflag == L_HORIZONTAL_EDGES)
146  vald = L_ABS(val1 + 2 * val4 + val7
147  - val3 - 2 * val6 - val9) >> 3;
148  else if (orientflag == L_VERTICAL_EDGES)
149  vald = L_ABS(val1 + 2 * val2 + val3 - val7
150  - 2 * val8 - val9) >> 3;
151  else { /* L_ALL_EDGES */
152  gx = L_ABS(val1 + 2 * val2 + val3 - val7
153  - 2 * val8 - val9) >> 3;
154  gy = L_ABS(val1 + 2 * val4 + val7
155  - val3 - 2 * val6 - val9) >> 3;
156  vald = L_MIN(255, gx + gy);
157  }
158  SET_DATA_BYTE(lined, j, vald);
159  }
160  }
161 
162  pixDestroy(&pixt);
163  return pixd;
164 }
165 
166 
167 /*----------------------------------------------------------------------*
168  * Two-sided edge gradient filter *
169  *----------------------------------------------------------------------*/
198 PIX *
200  l_int32 orientflag)
201 {
202 l_int32 w, h, d, i, j, wpls, wpld;
203 l_int32 cval, rval, bval, val, lgrad, rgrad, tgrad, bgrad;
204 l_uint32 *datas, *lines, *datad, *lined;
205 PIX *pixd;
206 
207  PROCNAME("pixTwoSidedEdgeFilter");
208 
209  if (!pixs)
210  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
211  pixGetDimensions(pixs, &w, &h, &d);
212  if (d != 8)
213  return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
214  if (orientflag != L_HORIZONTAL_EDGES && orientflag != L_VERTICAL_EDGES)
215  return (PIX *)ERROR_PTR("invalid orientflag", procName, NULL);
216 
217  pixd = pixCreateTemplate(pixs);
218  datas = pixGetData(pixs);
219  wpls = pixGetWpl(pixs);
220  datad = pixGetData(pixd);
221  wpld = pixGetWpl(pixd);
222  if (orientflag == L_VERTICAL_EDGES) {
223  for (i = 0; i < h; i++) {
224  lines = datas + i * wpls;
225  lined = datad + i * wpld;
226  cval = GET_DATA_BYTE(lines, 1);
227  lgrad = cval - GET_DATA_BYTE(lines, 0);
228  for (j = 1; j < w - 1; j++) {
229  rval = GET_DATA_BYTE(lines, j + 1);
230  rgrad = rval - cval;
231  if (lgrad * rgrad > 0) {
232  if (lgrad < 0)
233  val = -L_MAX(lgrad, rgrad);
234  else
235  val = L_MIN(lgrad, rgrad);
236  SET_DATA_BYTE(lined, j, val);
237  }
238  lgrad = rgrad;
239  cval = rval;
240  }
241  }
242  }
243  else { /* L_HORIZONTAL_EDGES) */
244  for (j = 0; j < w; j++) {
245  lines = datas + wpls;
246  cval = GET_DATA_BYTE(lines, j); /* for line 1 */
247  tgrad = cval - GET_DATA_BYTE(datas, j);
248  for (i = 1; i < h - 1; i++) {
249  lines += wpls; /* for line i + 1 */
250  lined = datad + i * wpld;
251  bval = GET_DATA_BYTE(lines, j);
252  bgrad = bval - cval;
253  if (tgrad * bgrad > 0) {
254  if (tgrad < 0)
255  val = -L_MAX(tgrad, bgrad);
256  else
257  val = L_MIN(tgrad, bgrad);
258  SET_DATA_BYTE(lined, j, val);
259  }
260  tgrad = bgrad;
261  cval = bval;
262  }
263  }
264  }
265 
266  return pixd;
267 }
268 
269 
270 /*----------------------------------------------------------------------*
271  * Measurement of edge smoothness *
272  *----------------------------------------------------------------------*/
308 l_ok
310  l_int32 side,
311  l_int32 minjump,
312  l_int32 minreversal,
313  l_float32 *pjpl,
314  l_float32 *pjspl,
315  l_float32 *prpl,
316  const char *debugfile)
317 {
318 l_int32 i, n, val, nval, diff, njumps, jumpsum, nreversal;
319 NUMA *na, *nae;
320 
321  PROCNAME("pixMeasureEdgeSmoothness");
322 
323  if (pjpl) *pjpl = 0.0;
324  if (pjspl) *pjspl = 0.0;
325  if (prpl) *prpl = 0.0;
326  if (!pjpl && !pjspl && !prpl && !debugfile)
327  return ERROR_INT("no output requested", procName, 1);
328  if (!pixs || pixGetDepth(pixs) != 1)
329  return ERROR_INT("pixs not defined or not 1 bpp", procName, 1);
330  if (side != L_FROM_LEFT && side != L_FROM_RIGHT &&
331  side != L_FROM_TOP && side != L_FROM_BOT)
332  return ERROR_INT("invalid side", procName, 1);
333  if (minjump < 1)
334  return ERROR_INT("invalid minjump; must be >= 1", procName, 1);
335  if (minreversal < 1)
336  return ERROR_INT("invalid minreversal; must be >= 1", procName, 1);
337 
338  if ((na = pixGetEdgeProfile(pixs, side, debugfile)) == NULL)
339  return ERROR_INT("edge profile not made", procName, 1);
340  if ((n = numaGetCount(na)) < 2) {
341  numaDestroy(&na);
342  return 0;
343  }
344 
345  if (pjpl || pjspl) {
346  jumpsum = 0;
347  njumps = 0;
348  numaGetIValue(na, 0, &val);
349  for (i = 1; i < n; i++) {
350  numaGetIValue(na, i, &nval);
351  diff = L_ABS(nval - val);
352  if (diff >= minjump) {
353  njumps++;
354  jumpsum += diff;
355  }
356  val = nval;
357  }
358  if (pjpl)
359  *pjpl = (l_float32)njumps / (l_float32)(n - 1);
360  if (pjspl)
361  *pjspl = (l_float32)jumpsum / (l_float32)(n - 1);
362  }
363 
364  if (prpl) {
365  nae = numaFindExtrema(na, minreversal, NULL);
366  nreversal = numaGetCount(nae) - 1;
367  *prpl = (l_float32)nreversal / (l_float32)(n - 1);
368  numaDestroy(&nae);
369  }
370 
371  numaDestroy(&na);
372  return 0;
373 }
374 
375 
385 NUMA *
387  l_int32 side,
388  const char *debugfile)
389 {
390 l_int32 x, y, w, h, loc, index, ival;
391 l_uint32 val;
392 NUMA *na;
393 PIX *pixt;
394 PIXCMAP *cmap;
395 
396  PROCNAME("pixGetEdgeProfile");
397 
398  if (!pixs || pixGetDepth(pixs) != 1)
399  return (NUMA *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
400  if (side != L_FROM_LEFT && side != L_FROM_RIGHT &&
401  side != L_FROM_TOP && side != L_FROM_BOT)
402  return (NUMA *)ERROR_PTR("invalid side", procName, NULL);
403 
404  pixGetDimensions(pixs, &w, &h, NULL);
405  if (side == L_FROM_LEFT || side == L_FROM_RIGHT)
406  na = numaCreate(h);
407  else
408  na = numaCreate(w);
409  if (side == L_FROM_LEFT) {
410  pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_LEFT, &loc);
411  loc = (loc == w - 1) ? 0 : loc + 1; /* back to the left edge */
412  numaAddNumber(na, loc);
413  for (y = 1; y < h; y++) {
414  pixGetPixel(pixs, loc, y, &val);
415  if (val == 1) {
416  pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc);
417  } else {
418  pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc);
419  loc = (loc == w - 1) ? 0 : loc + 1;
420  }
421  numaAddNumber(na, loc);
422  }
423  }
424  else if (side == L_FROM_RIGHT) {
425  pixGetLastOffPixelInRun(pixs, w - 1, 0, L_FROM_RIGHT, &loc);
426  loc = (loc == 0) ? w - 1 : loc - 1; /* back to the right edge */
427  numaAddNumber(na, loc);
428  for (y = 1; y < h; y++) {
429  pixGetPixel(pixs, loc, y, &val);
430  if (val == 1) {
431  pixGetLastOnPixelInRun(pixs, loc, y, L_FROM_LEFT, &loc);
432  } else {
433  pixGetLastOffPixelInRun(pixs, loc, y, L_FROM_RIGHT, &loc);
434  loc = (loc == 0) ? w - 1 : loc - 1;
435  }
436  numaAddNumber(na, loc);
437  }
438  }
439  else if (side == L_FROM_TOP) {
440  pixGetLastOffPixelInRun(pixs, 0, 0, L_FROM_TOP, &loc);
441  loc = (loc == h - 1) ? 0 : loc + 1; /* back to the top edge */
442  numaAddNumber(na, loc);
443  for (x = 1; x < w; x++) {
444  pixGetPixel(pixs, x, loc, &val);
445  if (val == 1) {
446  pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_BOT, &loc);
447  } else {
448  pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_TOP, &loc);
449  loc = (loc == h - 1) ? 0 : loc + 1;
450  }
451  numaAddNumber(na, loc);
452  }
453  }
454  else { /* side == L_FROM_BOT */
455  pixGetLastOffPixelInRun(pixs, 0, h - 1, L_FROM_BOT, &loc);
456  loc = (loc == 0) ? h - 1 : loc - 1; /* back to the bottom edge */
457  numaAddNumber(na, loc);
458  for (x = 1; x < w; x++) {
459  pixGetPixel(pixs, x, loc, &val);
460  if (val == 1) {
461  pixGetLastOnPixelInRun(pixs, x, loc, L_FROM_TOP, &loc);
462  } else {
463  pixGetLastOffPixelInRun(pixs, x, loc, L_FROM_BOT, &loc);
464  loc = (loc == 0) ? h - 1 : loc - 1;
465  }
466  numaAddNumber(na, loc);
467  }
468  }
469 
470  if (debugfile) {
471  pixt = pixConvertTo8(pixs, TRUE);
472  cmap = pixGetColormap(pixt);
473  pixcmapAddColor(cmap, 255, 0, 0);
474  index = pixcmapGetCount(cmap) - 1;
475  if (side == L_FROM_LEFT || side == L_FROM_RIGHT) {
476  for (y = 0; y < h; y++) {
477  numaGetIValue(na, y, &ival);
478  pixSetPixel(pixt, ival, y, index);
479  }
480  } else { /* L_FROM_TOP or L_FROM_BOT */
481  for (x = 0; x < w; x++) {
482  numaGetIValue(na, x, &ival);
483  pixSetPixel(pixt, x, ival, index);
484  }
485  }
486  pixWrite(debugfile, pixt, IFF_PNG);
487  pixDestroy(&pixt);
488  }
489 
490  return na;
491 }
492 
493 
494 /*
495  * pixGetLastOffPixelInRun()
496  *
497  * Input: pixs (1 bpp)
498  * x, y (starting location)
499  * direction (L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT)
500  * &loc (<return> location in scan direction coordinate
501  * of last OFF pixel found)
502  * Return: na (of fg edge pixel locations), or NULL on error
503  *
504  * Notes:
505  * (1) Search starts from the pixel at (x, y), which is OFF.
506  * (2) It returns the location in the scan direction of the last
507  * pixel in the current run that is OFF.
508  * (3) The interface for these pixel run functions is cleaner when
509  * you ask for the last pixel in the current run, rather than the
510  * first pixel of opposite polarity that is found, because the
511  * current run may go to the edge of the image, in which case
512  * no pixel of opposite polarity is found.
513  */
514 l_int32
515 pixGetLastOffPixelInRun(PIX *pixs,
516  l_int32 x,
517  l_int32 y,
518  l_int32 direction,
519  l_int32 *ploc)
520 {
521 l_int32 loc, w, h;
522 l_uint32 val;
523 
524  PROCNAME("pixGetLastOffPixelInRun");
525 
526  if (!ploc)
527  return ERROR_INT("&loc not defined", procName, 1);
528  *ploc = 0;
529  if (!pixs || pixGetDepth(pixs) != 1)
530  return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
531  if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT &&
532  direction != L_FROM_TOP && direction != L_FROM_BOT)
533  return ERROR_INT("invalid side", procName, 1);
534 
535  pixGetDimensions(pixs, &w, &h, NULL);
536  if (direction == L_FROM_LEFT) {
537  for (loc = x; loc < w; loc++) {
538  pixGetPixel(pixs, loc, y, &val);
539  if (val == 1)
540  break;
541  }
542  *ploc = loc - 1;
543  } else if (direction == L_FROM_RIGHT) {
544  for (loc = x; loc >= 0; loc--) {
545  pixGetPixel(pixs, loc, y, &val);
546  if (val == 1)
547  break;
548  }
549  *ploc = loc + 1;
550  }
551  else if (direction == L_FROM_TOP) {
552  for (loc = y; loc < h; loc++) {
553  pixGetPixel(pixs, x, loc, &val);
554  if (val == 1)
555  break;
556  }
557  *ploc = loc - 1;
558  }
559  else if (direction == L_FROM_BOT) {
560  for (loc = y; loc >= 0; loc--) {
561  pixGetPixel(pixs, x, loc, &val);
562  if (val == 1)
563  break;
564  }
565  *ploc = loc + 1;
566  }
567  return 0;
568 }
569 
570 
571 /*
572  * pixGetLastOnPixelInRun()
573  *
574  * Input: pixs (1 bpp)
575  * x, y (starting location)
576  * direction (L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT)
577  * &loc (<return> location in scan direction coordinate
578  * of first ON pixel found)
579  * Return: na (of fg edge pixel locations), or NULL on error
580  *
581  * Notes:
582  * (1) Search starts from the pixel at (x, y), which is ON.
583  * (2) It returns the location in the scan direction of the last
584  * pixel in the current run that is ON.
585  */
586 l_int32
587 pixGetLastOnPixelInRun(PIX *pixs,
588  l_int32 x,
589  l_int32 y,
590  l_int32 direction,
591  l_int32 *ploc)
592 {
593 l_int32 loc, w, h;
594 l_uint32 val;
595 
596  PROCNAME("pixLastOnPixelInRun");
597 
598  if (!ploc)
599  return ERROR_INT("&loc not defined", procName, 1);
600  *ploc = 0;
601  if (!pixs || pixGetDepth(pixs) != 1)
602  return ERROR_INT("pixs undefined or not 1 bpp", procName, 1);
603  if (direction != L_FROM_LEFT && direction != L_FROM_RIGHT &&
604  direction != L_FROM_TOP && direction != L_FROM_BOT)
605  return ERROR_INT("invalid side", procName, 1);
606 
607  pixGetDimensions(pixs, &w, &h, NULL);
608  if (direction == L_FROM_LEFT) {
609  for (loc = x; loc < w; loc++) {
610  pixGetPixel(pixs, loc, y, &val);
611  if (val == 0)
612  break;
613  }
614  *ploc = loc - 1;
615  } else if (direction == L_FROM_RIGHT) {
616  for (loc = x; loc >= 0; loc--) {
617  pixGetPixel(pixs, loc, y, &val);
618  if (val == 0)
619  break;
620  }
621  *ploc = loc + 1;
622  }
623  else if (direction == L_FROM_TOP) {
624  for (loc = y; loc < h; loc++) {
625  pixGetPixel(pixs, x, loc, &val);
626  if (val == 0)
627  break;
628  }
629  *ploc = loc - 1;
630  }
631  else if (direction == L_FROM_BOT) {
632  for (loc = y; loc >= 0; loc--) {
633  pixGetPixel(pixs, x, loc, &val);
634  if (val == 0)
635  break;
636  }
637  *ploc = loc + 1;
638  }
639  return 0;
640 }
NUMA * pixGetEdgeProfile(PIX *pixs, l_int32 side, const char *debugfile)
pixGetEdgeProfile()
Definition: edge.c:386
l_int32 h
Definition: dewarp.h:164
NUMA * numaFindExtrema(NUMA *nas, l_float32 delta, NUMA **pnav)
numaFindExtrema()
Definition: numafunc2.c:2474
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:473
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3041
PIX * pixAddMirroredBorder(PIX *pixs, l_int32 left, l_int32 right, l_int32 top, l_int32 bot)
pixAddMirroredBorder()
Definition: pix2.c:2026
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:187
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1624
PIX * pixCreateTemplate(PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:367
PIX * pixTwoSidedEdgeFilter(PIX *pixs, l_int32 orientflag)
pixTwoSidedEdgeFilter()
Definition: edge.c:199
struct Pix * pixs
Definition: dewarp.h:154
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:727
Definition: array.h:59
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:631
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:253
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:360
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:180
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
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:309
l_int32 pixcmapGetCount(PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:635
Definition: pix.h:134
PIX * pixSobelEdgeFilter(PIX *pixs, l_int32 orientflag)
pixSobelEdgeFilter()
Definition: edge.c:91
l_int32 w
Definition: dewarp.h:163
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:341