You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

240 line
7.3 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2004—2017 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #include <lol/engine-internal.h>
  13. /*
  14. * Median filter functions
  15. */
  16. /* FIXME: this is in desperate want of optimisation. Here is what could
  17. * be done to improve the performance:
  18. * - prefetch the neighbourhood; most neighbours are the same as the
  19. * previous pixels.
  20. * - use a better sort algorithm; bubble sort is ridiculous
  21. * - even better, use state-of-the art median selection, ie. O(3n), for
  22. * most common combinations (9, 25, 49, 81). */
  23. namespace lol
  24. {
  25. static int cmpfloat(void const *i1, void const *i2)
  26. {
  27. float a = *(float const *)i1;
  28. float b = *(float const *)i2;
  29. return (a > b) - (a < b);
  30. }
  31. image image::Median(ivec2 ksize) const
  32. {
  33. ivec2 const isize = size();
  34. image tmp = *this;
  35. image ret(isize);
  36. if (format() == PixelFormat::Y_8 || format() == PixelFormat::Y_F32)
  37. {
  38. ivec2 const lsize = 2 * ksize + ivec2(1);
  39. array2d<float> list(lsize);
  40. float *srcp = tmp.lock<PixelFormat::Y_F32>();
  41. float *dstp = ret.lock<PixelFormat::Y_F32>();
  42. for (int y = 0; y < isize.y; y++)
  43. {
  44. for (int x = 0; x < isize.x; x++)
  45. {
  46. /* Make a list of neighbours */
  47. for (int j = -ksize.y; j <= ksize.y; j++)
  48. {
  49. int y2 = y + j;
  50. if (y2 < 0) y2 = isize.y - 1 - ((-y2 - 1) % isize.y);
  51. else if (y2 > 0) y2 = y2 % isize.y;
  52. for (int i = -ksize.x; i <= ksize.x; i++)
  53. {
  54. int x2 = x + i;
  55. if (x2 < 0) x2 = isize.x - 1 - ((-x2 - 1) % isize.x);
  56. else if (x2 > 0) x2 = x2 % isize.x;
  57. list[i + ksize.x][j + ksize.y] = srcp[y2 * isize.x + x2];
  58. }
  59. }
  60. /* Sort the list */
  61. qsort(&list[0][0], lsize.x * lsize.y, sizeof(float), cmpfloat);
  62. /* Store the median value */
  63. dstp[y * isize.x + x] = *(&list[0][0] + lsize.x * lsize.y / 2);
  64. }
  65. }
  66. tmp.unlock(srcp);
  67. ret.unlock(dstp);
  68. }
  69. else
  70. {
  71. ivec2 const lsize = 2 * ksize + ivec2(1);
  72. array2d<vec3> list(lsize);
  73. vec4 *srcp = tmp.lock<PixelFormat::RGBA_F32>();
  74. vec4 *dstp = ret.lock<PixelFormat::RGBA_F32>();
  75. for (int y = 0; y < isize.y; y++)
  76. {
  77. for (int x = 0; x < isize.x; x++)
  78. {
  79. /* Make a list of neighbours */
  80. for (int j = -ksize.y; j <= ksize.y; j++)
  81. {
  82. int y2 = y + j;
  83. if (y2 < 0) y2 = isize.y - 1 - ((-y2 - 1) % isize.y);
  84. else if (y2 > 0) y2 = y2 % isize.y;
  85. for (int i = -ksize.x; i <= ksize.x; i++)
  86. {
  87. int x2 = x + i;
  88. if (x2 < 0) x2 = isize.x - 1 - ((-x2 - 1) % isize.x);
  89. else if (x2 > 0) x2 = x2 % isize.x;
  90. list[i + ksize.x][j + ksize.y] = srcp[y2 * isize.x + x2].rgb;
  91. }
  92. }
  93. /* Algorithm constants, empirically chosen */
  94. int const N = 5;
  95. float const K = 1.5f;
  96. /* Iterate using Weiszfeld’s algorithm */
  97. vec3 oldmed(0.f), median(0.f);
  98. for (int iter = 0; ; ++iter)
  99. {
  100. oldmed = median;
  101. vec3 s1(0.f);
  102. float s2 = 0.f;
  103. for (int j = 0; j < lsize.y; ++j)
  104. for (int i = 0; i < lsize.x; ++i)
  105. {
  106. float d = 1.0f /
  107. (1e-10f + distance(median, list[i][j]));
  108. s1 += list[i][j] * d;
  109. s2 += d;
  110. }
  111. median = s1 / s2;
  112. if (iter > 1 && iter < N)
  113. {
  114. median += K * (median - oldmed);
  115. }
  116. if (iter > 3 && distance(oldmed, median) < 1.e-5f)
  117. break;
  118. }
  119. /* Store the median value */
  120. dstp[y * isize.x + x] = vec4(median, srcp[y * isize.x + x].a);
  121. }
  122. }
  123. tmp.unlock(srcp);
  124. ret.unlock(dstp);
  125. }
  126. return ret;
  127. }
  128. image image::Median(array2d<float> const &ker) const
  129. {
  130. ivec2 const isize = size();
  131. image tmp = *this;
  132. image ret(isize);
  133. /* FIXME: TODO */
  134. #if 0
  135. if (format() == PixelFormat::Y_8 || format() == PixelFormat::Y_F32)
  136. {
  137. }
  138. else
  139. #endif
  140. {
  141. ivec2 const ksize = ker.size();
  142. array2d<vec3> list(ksize);
  143. vec4 *srcp = tmp.lock<PixelFormat::RGBA_F32>();
  144. vec4 *dstp = ret.lock<PixelFormat::RGBA_F32>();
  145. for (int y = 0; y < isize.y; y++)
  146. {
  147. for (int x = 0; x < isize.x; x++)
  148. {
  149. /* Make a list of neighbours */
  150. for (int j = 0; j < ksize.y; j++)
  151. {
  152. int y2 = y + j - ksize.y / 2;
  153. if (y2 < 0) y2 = isize.y - 1 - ((-y2 - 1) % isize.y);
  154. else if (y2 > 0) y2 = y2 % isize.y;
  155. for (int i = 0; i < ksize.x; i++)
  156. {
  157. int x2 = x + i - ksize.x / 2;
  158. if (x2 < 0) x2 = isize.x - 1 - ((-x2 - 1) % isize.x);
  159. else if (x2 > 0) x2 = x2 % isize.x;
  160. list[i][j] = srcp[y2 * isize.x + x2].rgb;
  161. }
  162. }
  163. /* Algorithm constants, empirically chosen */
  164. int const N = 5;
  165. float const K = 1.5f;
  166. /* Iterate using Weiszfeld’s algorithm */
  167. vec3 oldmed(0.f), median(0.f);
  168. for (int iter = 0; ; ++iter)
  169. {
  170. oldmed = median;
  171. vec3 s1(0.f);
  172. float s2 = 0.f;
  173. for (int j = 0; j < ksize.y; ++j)
  174. for (int i = 0; i < ksize.x; ++i)
  175. {
  176. float d = ker[i][j] /
  177. (1e-10f + distance(median, list[i][j]));
  178. s1 += list[i][j] * d;
  179. s2 += d;
  180. }
  181. median = s1 / s2;
  182. if (iter > 1 && iter < N)
  183. {
  184. median += K * (median - oldmed);
  185. }
  186. if (iter > 3 && distance(oldmed, median) < 1.e-5f)
  187. break;
  188. }
  189. /* Store the median value */
  190. dstp[y * isize.x + x] = vec4(median, srcp[y * isize.x + x].a);
  191. }
  192. }
  193. tmp.unlock(srcp);
  194. ret.unlock(dstp);
  195. }
  196. return ret;
  197. }
  198. } /* namespace lol */