thin_lines.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /* The following code is the implementation of the thinning
  2. algorithm described in the "Analysis of Thinning Algorithms Using
  3. Mathematical Morphology" by Ben-Kwei Jang and Ronald T. Chin
  4. (Transactions on pattern analysis and machine intellegence, vol. 12,
  5. NO 6, JUNE 1990)
  6. Olga Waupotitsch, USA CERL, jan, 1993
  7. The code for finding the bounding box as well as input/output code
  8. is written by Mike Baba (1990) (DBA Systems) and Jean Ezell (1988,
  9. USA CERL)
  10. */
  11. /* algorithm B */
  12. #include <stdlib.h>
  13. #include <stdio.h>
  14. #include <unistd.h>
  15. #include <grass/gis.h>
  16. #include <grass/glocale.h>
  17. #include "local_proto.h"
  18. #define LEFT 1
  19. #define RIGHT 2
  20. #define true 1
  21. #define false 0
  22. #define DELETED_PIX 9999
  23. extern char *error_prefix;
  24. static char *work_file_name;
  25. static int n_rows, n_cols, pad_size;
  26. static int box_right, box_left, box_top, box_bottom;
  27. extern CELL *get_a_row(int row);
  28. extern int put_a_row(int row, CELL * buf);
  29. int thin_lines(int iterations)
  30. {
  31. int j, i, col, deleted, row;
  32. CELL *row_buf, *new_med, *med, *bottom, *top, *get_a_row();
  33. char W, N_W, Templ[8], N_Templ[8];
  34. map_size(&n_rows, &n_cols, &pad_size);
  35. box_right = box_bottom = 0;
  36. box_left = n_cols;
  37. box_top = n_rows;
  38. bottom = get_a_row(pad_size - 1);
  39. for (row = pad_size; row < n_rows - pad_size; row++) {
  40. top = bottom; /* line above the one we're changing */
  41. bottom = get_a_row(row); /* line we're working on now */
  42. for (col = pad_size; col < n_cols - pad_size; col++) {
  43. /* skip background cells */
  44. if (!Rast_is_c_null_value(&(bottom[col]))) {
  45. if (col < box_left) /* find bounding box which will */
  46. box_left = col; /* cover part of raster map which */
  47. if (col > box_right) /* has lines in it */
  48. box_right = col;
  49. if (row < box_top)
  50. box_top = row;
  51. if (row > box_bottom)
  52. box_bottom = row;
  53. }
  54. } /* col-loop */
  55. put_a_row(row, bottom);
  56. } /* row-loop */
  57. if (box_right < box_left || box_bottom < box_top) {
  58. unlink(work_file_name);
  59. G_fatal_error(_("%s: Unable to find bounding box for lines"),
  60. error_prefix);
  61. }
  62. G_message(_("Bounding box: l = %d, r = %d, t = %d, b = %d"),
  63. box_left, box_right, box_top, box_bottom);
  64. /*
  65. * thin - thin lines to a single pixel width
  66. *
  67. * (destructive)
  68. *
  69. * +---+---+---+---+---+---+---+
  70. * | | | | | | | |
  71. * +---+---+---+---+---+---+---+
  72. * | | | t | t | t | | | row - 1
  73. * +---+---+---+---+---+---+---+
  74. * | | | t | T | t | | | row
  75. * +---+---+---+---+---+---+---+
  76. * | | | t | t | t | | | row + 1
  77. * +---+---+---+---+---+---+---+
  78. * | | | | | | | |
  79. * +---+---+---+---+---+---+---+
  80. * col
  81. */
  82. Templ[0] = /* 00101000 */ 40;
  83. Templ[1] = /* 00001010 */ 10;
  84. Templ[2] = /* 10000010 */ 130;
  85. Templ[3] = /* 10100000 */ 160;
  86. Templ[4] = /* 00101010 */ 42;
  87. Templ[5] = /* 10001010 */ 138;
  88. Templ[6] = /* 10100010 */ 162;
  89. Templ[7] = /* 10101000 */ 168;
  90. N_Templ[0] = /* 10000011 */ 131;
  91. N_Templ[1] = /* 11100000 */ 224;
  92. N_Templ[2] = /* 00111000 */ 56;
  93. N_Templ[3] = /* 00001110 */ 14;
  94. N_Templ[4] = /* 10000000 */ 128;
  95. N_Templ[5] = /* 00100000 */ 32;
  96. N_Templ[6] = /* 00001000 */ 8;
  97. N_Templ[7] = /* 00000010 */ 2;
  98. new_med = (CELL *) G_malloc(sizeof(CELL) * (n_cols));
  99. Rast_set_c_null_value(new_med, n_cols);
  100. row_buf = (CELL *) G_malloc(sizeof(CELL) * (n_cols));
  101. Rast_set_c_null_value(row_buf, n_cols);
  102. deleted = 1;
  103. i = 1;
  104. while ((deleted > 0) && (i <= iterations)) { /* it must be done in <= iterations passes */
  105. G_message(_("Pass number %d"), i);
  106. i++;
  107. deleted = 0;
  108. for (j = 1; j <= 4; j++) {
  109. int ind1, ind2, ind3;
  110. ind1 = j - 1;
  111. if (j <= 3)
  112. ind2 = j;
  113. else
  114. ind2 = 0;
  115. ind3 = (j - 1) + 4;
  116. top = get_a_row(box_top - 1);
  117. med = get_a_row(box_top);
  118. for (row = box_top; row <= box_bottom; row++) {
  119. for (col = box_left; col <= box_right; col++)
  120. new_med[col] = med[col];
  121. bottom = get_a_row(row + 1);
  122. for (col = box_left; col <= box_right; col++) {
  123. if (!Rast_is_c_null_value(&(med[col]))) { /* if cell is not NULL */
  124. W = encode_neighbours(top, med, bottom, col, 1);
  125. /* current window */
  126. N_W = encode_neighbours(top, med, bottom, col, -1);
  127. /* negated window */
  128. if ((((Templ[ind1] & W) == Templ[ind1]) &&
  129. ((N_Templ[ind1] & N_W) == N_Templ[ind1])) ||
  130. (((Templ[ind2] & W) == Templ[ind2]) &&
  131. ((N_Templ[ind2] & N_W) == N_Templ[ind2])) ||
  132. (((Templ[ind3] & W) == Templ[ind3]) &&
  133. ((N_Templ[ind3] & N_W) == N_Templ[ind3]))) {
  134. /* fprintf(stdout, "col: %d, row: %d\n", col, row); */
  135. deleted++;
  136. Rast_set_c_null_value(&(new_med[col]), 1);
  137. }
  138. } /* end blank pixel */
  139. } /* end col loop */
  140. for (col = box_left; col <= box_right; col++)
  141. row_buf[col] = med[col];
  142. top = row_buf;
  143. put_a_row(row, new_med);
  144. /* this is because I want to keep the old copy op top */
  145. /* if I say top=med, top will point to already changed */
  146. /* row, since put_a_row(row, med_row) was called and med */
  147. med = bottom;
  148. } /* end row loop */
  149. } /* j-loop */
  150. G_message(_("Deleted %d pixels "), deleted);
  151. } /* while delete >0 */
  152. if ((deleted == 0) && (i <= iterations))
  153. G_message(_("Thinning completed successfully."));
  154. else
  155. G_message(_("Thinning not completed, consider to increase 'iterations' parameter."));
  156. return 0;
  157. }
  158. /* encode_neighbours- return neighborhood information for pixel at (middle,col) */
  159. char
  160. encode_neighbours(CELL * top, CELL * middle, CELL * bottom, int col, int neg)
  161. {
  162. char T;
  163. T = 0;
  164. if (neg > 0)
  165. T = (((!Rast_is_c_null_value(&(middle[col + 1]))) << 5) |
  166. ((!Rast_is_c_null_value(&(top[col + 1]))) << 6) |
  167. ((!Rast_is_c_null_value(&(top[col]))) << 7) |
  168. ((!Rast_is_c_null_value(&(top[col - 1])))) |
  169. ((!Rast_is_c_null_value(&(middle[col - 1]))) << 1) |
  170. ((!Rast_is_c_null_value(&(bottom[col - 1]))) << 2) |
  171. ((!Rast_is_c_null_value(&(bottom[col]))) << 3) |
  172. ((!Rast_is_c_null_value(&(bottom[col + 1]))) << 4));
  173. else
  174. T = (((Rast_is_c_null_value(&(middle[col + 1]))) << 5) |
  175. ((Rast_is_c_null_value(&(top[col + 1]))) << 6) |
  176. ((Rast_is_c_null_value(&(top[col]))) << 7) |
  177. ((Rast_is_c_null_value(&(top[col - 1])))) |
  178. ((Rast_is_c_null_value(&(middle[col - 1]))) << 1) |
  179. ((Rast_is_c_null_value(&(bottom[col - 1]))) << 2) |
  180. ((Rast_is_c_null_value(&(bottom[col]))) << 3) |
  181. ((Rast_is_c_null_value(&(bottom[col + 1]))) << 4));
  182. return (T);
  183. }