smoothing.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /****************************************************************
  2. *
  3. * MODULE: v.generalize
  4. *
  5. * AUTHOR(S): Daniel Bundala
  6. *
  7. * PURPOSE: Module for line simplification and smoothing
  8. *
  9. * COPYRIGHT: (C) 2002-2005 by the GRASS Development Team
  10. *
  11. * This program is free software under the
  12. * GNU General Public License (>=v2).
  13. * Read the file COPYING that comes with GRASS
  14. * for details.
  15. *
  16. ****************************************************************/
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <math.h>
  20. #include <grass/gis.h>
  21. #include <grass/vector.h>
  22. #include <grass/glocale.h>
  23. #include "point.h"
  24. #include "matrix.h"
  25. /* boyle's forward looking algorithm
  26. * return the number of points in the result = Points->n_points
  27. */
  28. int boyle(struct line_pnts *Points, int look_ahead, int with_z)
  29. {
  30. POINT last, npoint, ppoint;
  31. int next, n, i, p;
  32. double c1, c2;
  33. n = Points->n_points;
  34. /* if look_ahead is too small or line too short, there's nothing
  35. * to smooth */
  36. if (look_ahead < 2 || look_ahead > n) {
  37. return n;
  38. }
  39. point_assign(Points, 0, with_z, &last);
  40. c1 = (double)1 / (double)(look_ahead - 1);
  41. c2 = (double)1 - c1;
  42. next = 1;
  43. for (i = 0; i < n - 2; i++) {
  44. p = i + look_ahead;
  45. if (p >= n)
  46. p = n - 1;
  47. point_assign(Points, p, with_z, &ppoint);
  48. point_scalar(ppoint, c1, &ppoint);
  49. point_scalar(last, c2, &last);
  50. point_add(last, ppoint, &npoint);
  51. Points->x[next] = npoint.x;
  52. Points->y[next] = npoint.y;
  53. Points->z[next] = npoint.z;
  54. next++;
  55. last = npoint;
  56. }
  57. points_copy_last(Points, next);
  58. return Points->n_points;
  59. }
  60. /* mcmaster's sliding averaging algorithm. Return the number of points
  61. * in the output line. This equals to the number of points in the
  62. * input line */
  63. int sliding_averaging(struct line_pnts *Points, double slide, int look_ahead,
  64. int with_z)
  65. {
  66. int n, half, i;
  67. double sc;
  68. POINT p, tmp, s;
  69. POINT *res;
  70. n = Points->n_points;
  71. half = look_ahead / 2;
  72. if (look_ahead % 2 == 0) {
  73. G_fatal_error(_("Look ahead parameter must be odd"));
  74. return n;
  75. }
  76. if (look_ahead >= n || look_ahead == 1)
  77. return n;
  78. res = G_malloc(sizeof(POINT) * n);
  79. if (!res) {
  80. G_fatal_error(_("Out of memory"));
  81. return n;
  82. }
  83. sc = (double)1.0 / (double)look_ahead;
  84. point_assign(Points, 0, with_z, &p);
  85. for (i = 1; i < look_ahead; i++) {
  86. point_assign(Points, i, with_z, &tmp);
  87. point_add(p, tmp, &p);
  88. }
  89. /* and calculate the average of remaining points */
  90. for (i = half; i + half < n; i++) {
  91. point_assign(Points, i, with_z, &s);
  92. point_scalar(s, 1.0 - slide, &s);
  93. point_scalar(p, sc * slide, &tmp);
  94. point_add(tmp, s, &res[i]);
  95. if (i + half + 1 < n) {
  96. point_assign(Points, i - half, with_z, &tmp);
  97. point_subtract(p, tmp, &p);
  98. point_assign(Points, i + half + 1, with_z, &tmp);
  99. point_add(p, tmp, &p);
  100. }
  101. }
  102. for (i = half; i + half < n; i++) {
  103. Points->x[i] = res[i].x;
  104. Points->y[i] = res[i].y;
  105. Points->z[i] = res[i].z;
  106. }
  107. G_free(res);
  108. return Points->n_points;
  109. }
  110. /* mcmaster's distance weighting algorithm. Return the number
  111. * of points in the output line which equals to Points->n_points */
  112. int distance_weighting(struct line_pnts *Points, double slide, int look_ahead,
  113. int with_z)
  114. {
  115. POINT p, c, s, tmp;
  116. int n, i, half, j;
  117. double dists, d;
  118. POINT *res;
  119. n = Points->n_points;
  120. if (look_ahead % 2 == 0) {
  121. G_fatal_error(_("Look ahead parameter must be odd"));
  122. return n;
  123. }
  124. res = (POINT *) G_malloc(sizeof(POINT) * n);
  125. if (!res) {
  126. G_fatal_error(_("Out of memory"));
  127. return n;
  128. }
  129. point_assign(Points, 0, with_z, &res[0]);
  130. half = look_ahead / 2;
  131. for (i = half; i + half < n; i++) {
  132. point_assign(Points, i, with_z, &c);
  133. s.x = s.y = s.z = 0;
  134. dists = 0;
  135. for (j = i - half; j <= i + half; j++) {
  136. if (j == i)
  137. continue;
  138. point_assign(Points, j, with_z, &p);
  139. d = point_dist(p, c);
  140. if (d < GRASS_EPSILON)
  141. continue;
  142. d = (double)1.0 / d;
  143. dists += d;
  144. point_scalar(p, d, &tmp);
  145. s.x += tmp.x;
  146. s.y += tmp.y;
  147. s.z += tmp.z;
  148. }
  149. point_scalar(s, slide / dists, &tmp);
  150. point_scalar(c, (double)1.0 - slide, &s);
  151. point_add(s, tmp, &res[i]);
  152. }
  153. for (i = half; i + half < n; i++) {
  154. Points->x[i] = res[i].x;
  155. Points->y[i] = res[i].y;
  156. Points->z[i] = res[i].z;
  157. }
  158. G_free(res);
  159. return Points->n_points;
  160. }
  161. /* Chaiken's algorithm. Return the number of points in smoothed line
  162. */
  163. int chaiken(struct line_pnts *Points, double thresh, int with_z)
  164. {
  165. int n, i;
  166. POINT_LIST head, *cur;
  167. POINT p0, p1, p2, m1, tmp;
  168. n = Points->n_points;
  169. /* line is too short */
  170. if (n < 3)
  171. return n;
  172. thresh *= thresh;
  173. head.next = NULL;
  174. cur = &head;
  175. point_assign(Points, 0, with_z, &head.p);
  176. point_assign(Points, 0, with_z, &p0);
  177. for (i = 2; i <= n; i++) {
  178. if (i == n)
  179. point_assign(Points, i - 1, with_z, &p2);
  180. else
  181. point_assign(Points, i, with_z, &p2);
  182. point_assign(Points, i - 1, with_z, &p1);
  183. while (1) {
  184. point_add(p1, p2, &tmp);
  185. point_scalar(tmp, 0.5, &m1);
  186. point_list_add(cur, m1);
  187. if (point_dist_square(p0, m1) > thresh) {
  188. point_add(p1, m1, &tmp); /* need to refine the partition */
  189. point_scalar(tmp, 0.5, &p2);
  190. point_add(p1, p0, &tmp);
  191. point_scalar(tmp, 0.5, &p1);
  192. }
  193. else {
  194. break; /* good approximatin */
  195. }
  196. }
  197. while (cur->next != NULL)
  198. cur = cur->next;
  199. p0 = cur->p;
  200. }
  201. point_assign(Points, n - 1, with_z, &p0);
  202. point_list_add(cur, p0);
  203. if (point_list_copy_to_line_pnts(head, Points) == -1) {
  204. G_fatal_error(_("Out of memory"));
  205. }
  206. point_list_free(head);
  207. return Points->n_points;
  208. }
  209. /* use for refining tangent in hermite interpolation */
  210. void refine_tangent(POINT * p)
  211. {
  212. double l = point_dist2(*p);
  213. if (l < GRASS_EPSILON) {
  214. point_scalar(*p, 0.0, p);
  215. }
  216. else {
  217. point_scalar(*p, (double)1.0 / sqrt(sqrt(sqrt(l))), p);
  218. }
  219. return;
  220. }
  221. /* approximates given line using hermite cubic spline
  222. * interpolates by steps of length step
  223. * returns the number of point in result
  224. */
  225. int hermite(struct line_pnts *Points, double step, double angle_thresh,
  226. int with_z)
  227. {
  228. POINT_LIST head, *last, *point;
  229. POINT p0, p1, t0, t1, tmp, res;
  230. double length, next, length_begin, l;
  231. double s;
  232. double h1, h2, h3, h4;
  233. int n, i;
  234. int ni;
  235. n = Points->n_points;
  236. /* line is too short */
  237. if (n <= 2) {
  238. return n;
  239. }
  240. /* convert degrees=>radians */
  241. angle_thresh *= M_PI / 180.0;
  242. head.next = NULL;
  243. point = last = &head;
  244. /* length of p[0]..p[i+1] */
  245. i = 0;
  246. point_assign(Points, 0, with_z, &p0);
  247. point_assign(Points, 1, with_z, &p1);
  248. /* length of line 0..i */
  249. length_begin = 0;
  250. /* length of line from point 0 to i+1 */
  251. length = point_dist(p0, p1);
  252. next = 0;
  253. /* tangent at p0, p1 */
  254. point_subtract(p1, p0, &t0);
  255. refine_tangent(&t0);
  256. point_assign(Points, 2, with_z, &tmp);
  257. point_subtract(tmp, p0, &t1);
  258. refine_tangent(&t1);
  259. /* we always operate on the segment point[i]..point[i+1] */
  260. while (i < n - 1) {
  261. if (next > length || (length - length_begin < GRASS_EPSILON)) { /* segmet i..i+1 is finished or too short */
  262. i++;
  263. if (i >= n - 1)
  264. break; /* we are already out out of line */
  265. point_assign(Points, i, with_z, &p0);
  266. point_assign(Points, i + 1, with_z, &p1);
  267. length_begin = length;
  268. length += point_dist(p0, p1);
  269. ni = i + 2;
  270. if (ni > n - 1)
  271. ni = n - 1; /* ensure that we are in the line */
  272. t0 = t1;
  273. point_assign(Points, ni, with_z, &tmp);
  274. point_subtract(tmp, p0, &t1);
  275. refine_tangent(&t1);
  276. }
  277. else {
  278. l = length - length_begin; /* length of actual segment */
  279. s = (next - length_begin) / l; /* 0<=s<=1 where we want to add new point on the line */
  280. /* then we need to calculate 4 control polynomials */
  281. h1 = 2 * s * s * s - 3 * s * s + 1;
  282. h2 = -2 * s * s * s + 3 * s * s;
  283. h3 = s * s * s - 2 * s * s + s;
  284. h4 = s * s * s - s * s;
  285. point_scalar(p0, h1, &res);
  286. point_scalar(p1, h2, &tmp);
  287. point_add(res, tmp, &res);
  288. point_scalar(t0, h3, &tmp);
  289. point_add(res, tmp, &res);
  290. point_scalar(t1, h4, &tmp);
  291. point_add(res, tmp, &res);
  292. point_list_add(last, res);
  293. last = last->next;
  294. next += step;
  295. }
  296. /* if the angle between 2 vectors is less then eps, remove the
  297. * middle point */
  298. if (point->next && point->next->next && point->next->next->next) {
  299. if (point_angle_between
  300. (point->next->p, point->next->next->p,
  301. point->next->next->next->p) < angle_thresh) {
  302. point_list_delete_next(point->next);
  303. }
  304. else
  305. point = point->next;
  306. }
  307. }
  308. point_assign(Points, n - 1, with_z, &p0);
  309. point_list_add(last, p0);
  310. if (point_list_copy_to_line_pnts(head, Points) == -1)
  311. G_fatal_error(_("Out of memory"));
  312. point_list_free(head);
  313. return Points->n_points;
  314. }
  315. /* snakes algorithm for line simplification/generalization
  316. * returns the number of points in the output line. This is
  317. * always equal to the number of points in the original line
  318. *
  319. * alpha, beta are 2 parameters which change the behaviour of the algorithm
  320. *
  321. * TODO: Add parameter iterations, so the runnining time is O(N^3 * log iterations)
  322. * instead of O(N^3 * itearations). Probably not needed, for many iterations,
  323. * the result is almost straight line
  324. */
  325. int snakes(struct line_pnts *Points, double alpha, double beta, int with_z)
  326. {
  327. MATRIX g, ginv, xcoord, ycoord, zcoord, xout, yout, zout;
  328. int n = Points->n_points;
  329. int i, j;
  330. if (n < 3)
  331. return n;
  332. int plus = 4;
  333. if (!matrix_init(n + 2 * plus, n + 2 * plus, &g)) {
  334. G_fatal_error(_("Out of memory"));
  335. return n;
  336. }
  337. matrix_init(n + 2 * plus, 1, &xcoord);
  338. matrix_init(n + 2 * plus, 1, &ycoord);
  339. matrix_init(n + 2 * plus, 1, &zcoord);
  340. matrix_init(n + 2 * plus, 1, &xout);
  341. matrix_init(n + 2 * plus, 1, &yout);
  342. matrix_init(n + 2 * plus, 1, &zout);
  343. double x0 = Points->x[0];
  344. double y0 = Points->y[0];
  345. double z0 = Points->z[0];
  346. /* store the coordinates in the column vectors */
  347. for (i = 0; i < n; i++) {
  348. xcoord.a[i + plus][0] = Points->x[i] - x0;
  349. ycoord.a[i + plus][0] = Points->y[i] - y0;
  350. zcoord.a[i + plus][0] = Points->z[i] - z0;
  351. }
  352. /* repeat first and last point at the beginning and end
  353. * of each vector respectively */
  354. for (i = 0; i < plus; i++) {
  355. xcoord.a[i][0] = 0;
  356. ycoord.a[i][0] = 0;
  357. zcoord.a[i][0] = 0;
  358. }
  359. for (i = n + plus; i < n + 2 * plus; i++) {
  360. xcoord.a[i][0] = Points->x[n - 1] - x0;
  361. ycoord.a[i][0] = Points->y[n - 1] - y0;
  362. zcoord.a[i][0] = Points->z[n - 1] - z0;
  363. }
  364. /* calculate the matrix */
  365. double a = 2.0 * alpha + 6.0 * beta;
  366. double b = -alpha - 4.0 * beta;
  367. double c = beta;
  368. double val[5] = { c, b, a, b, c };
  369. for (i = 0; i < n + 2 * plus; i++)
  370. for (j = 0; j < n + 2 * plus; j++) {
  371. int index = j - i + 2;
  372. if (index >= 0 && index <= 4)
  373. g.a[i][j] = val[index];
  374. else
  375. g.a[i][j] = 0;
  376. }
  377. matrix_add_identity((double)1.0, &g);
  378. /* find its inverse */
  379. if (!matrix_inverse(g, &ginv, 0)) {
  380. G_fatal_error(_("Unable to find the inverse matrix"));
  381. return n;
  382. }
  383. if (!matrix_mult(ginv, xcoord, &xout)
  384. || !matrix_mult(ginv, ycoord, &yout)
  385. || !matrix_mult(ginv, zcoord, &zout)) {
  386. G_fatal_error(_("Unable to calculate the output vectors"));
  387. return n;
  388. }
  389. /* copy the new values of coordinates, but
  390. * never move the last and first point */
  391. for (i = 1; i < n - 1; i++) {
  392. Points->x[i] = xout.a[i + plus][0] + x0;
  393. Points->y[i] = yout.a[i + plus][0] + y0;
  394. if (with_z)
  395. Points->z[i] = zout.a[i + plus][0] + z0;
  396. }
  397. matrix_free(g);
  398. matrix_free(ginv);
  399. matrix_free(xcoord);
  400. matrix_free(ycoord);
  401. matrix_free(zcoord);
  402. matrix_free(xout);
  403. matrix_free(yout);
  404. matrix_free(zout);
  405. return Points->n_points;
  406. }