PageRenderTime 26ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/tags/R2009-05-08/main/image/src/__spatial_filtering__.cc

#
C++ | 578 lines | 445 code | 71 blank | 62 comment | 173 complexity | 3aaf9a2f2894048a5b3e16f9aa77da60 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, GPL-3.0, LGPL-3.0
  1. // Copyright (C) 2008 Soren Hauberg
  2. //
  3. // This program is free software; you can redistribute it and/or
  4. // modify it under the terms of the GNU General Public License
  5. // as published by the Free Software Foundation; either version 3
  6. // of the License, or (at your option) any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful, but
  9. // WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. // General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, see <http://www.gnu.org/licenses/>.
  15. // The 'compare' and 'selnth' functions are copied from the 'cordflt2' function
  16. // developed by Teemu Ikonen. This work was released under GPLv2 or later.
  17. // -- Soren Hauberg, March 21st, 2008
  18. #include <octave/oct.h>
  19. /**
  20. * Filter functions for ordered filtering.
  21. */
  22. template <class ET>
  23. inline bool compare (const ET a, const ET b)
  24. {
  25. if (a > b)
  26. return 1;
  27. else
  28. return 0;
  29. }
  30. template <> inline bool compare<Complex> (const Complex a, const Complex b)
  31. {
  32. const double anorm2 = a.real () * a.real () + a.imag () * a.imag ();
  33. const double bnorm2 = b.real () * b.real () + b.imag () * b.imag ();
  34. if (anorm2 > bnorm2)
  35. return 1;
  36. else
  37. return 0;
  38. }
  39. // select nth largest member from the array values
  40. // Partitioning algorithm, see Numerical recipes chap. 8.5
  41. template <class ET, class MT, class ET_OUT>
  42. ET_OUT selnth (MT &vals, octave_idx_type len, int nth)
  43. {
  44. ET hinge;
  45. int l, r, mid, i, j;
  46. l = 0;
  47. r = len - 1;
  48. for (;;)
  49. {
  50. // if partition size is 1 or two, then sort and return
  51. if (r <= l+1)
  52. {
  53. if (r == l+1 && compare<ET> (vals (l), vals (r)))
  54. std::swap (vals (l), vals (r));
  55. return vals (nth);
  56. }
  57. else
  58. {
  59. mid = (l+r) >> 1;
  60. std::swap (vals (mid), vals (l+1));
  61. // choose median of l, mid, r to be the hinge element
  62. // and set up sentinels in the borders (order l, l+1 and r)
  63. if (compare<ET> (vals (l), vals (r)))
  64. std::swap (vals (l), vals (r));
  65. if (compare<ET> (vals (l+1), vals (r)))
  66. std::swap (vals (l+1), vals (r));
  67. if (compare<ET> (vals (l), vals (l+1)))
  68. std::swap (vals (l), vals (l+1));
  69. i = l + 1;
  70. j = r;
  71. hinge = vals (l+1);
  72. for (;;)
  73. {
  74. do i++; while (compare<ET> (hinge, vals (i)));
  75. do j--; while (compare<ET> (vals (j), hinge));
  76. if (i > j)
  77. break;
  78. std::swap (vals (i), vals (j));
  79. }
  80. vals (l+1) = vals (j);
  81. vals (j) = hinge;
  82. if (j >= nth)
  83. r = j - 1;
  84. if (j <= nth)
  85. l = i;
  86. }
  87. }
  88. }
  89. template <class ET, class MT, class ET_OUT>
  90. ET_OUT min_filt (MT &vals, octave_idx_type len, int not_used)
  91. {
  92. ET_OUT min_val = vals (0);
  93. for (octave_idx_type i = 1; i < len; i++)
  94. min_val = compare (min_val, vals (i)) ? vals (i) : min_val;
  95. return min_val;
  96. }
  97. template <class ET, class MT, class ET_OUT>
  98. ET_OUT max_filt (MT &vals, octave_idx_type len, int not_used)
  99. {
  100. ET_OUT max_val = vals (0);
  101. for (octave_idx_type i = 1; i < len; i++)
  102. max_val = compare (max_val, vals (i)) ? max_val : vals (i);
  103. return max_val;
  104. }
  105. /**
  106. * Filter functions for standard deviation filters
  107. */
  108. template <class ET> inline
  109. ET square (const ET a)
  110. {
  111. return a * a;
  112. }
  113. template <class ET, class MT, class ET_OUT>
  114. ET_OUT std_filt (MT &vals, octave_idx_type len, int norm)
  115. {
  116. // Compute mean
  117. ET_OUT mean = 0;
  118. for (octave_idx_type i = 0; i < len; i++)
  119. mean += (ET_OUT)vals (i);
  120. mean /= (ET_OUT)len;
  121. // Compute sum of square differences from the mean
  122. ET_OUT var = 0;
  123. for (octave_idx_type i = 0; i < len; i++)
  124. var += square ((ET_OUT)vals (i) - mean);
  125. // Normalise to produce variance
  126. var /= (ET_OUT)norm;
  127. // Compute std. deviation
  128. return sqrt (var);
  129. }
  130. /**
  131. * Functions for the entropy filter.
  132. */
  133. /* We only need the explicit typed versions */
  134. template <class ET>
  135. void get_entropy_info (ET &add, int &nbins)
  136. {
  137. }
  138. #define ENTROPY_INFO(TYPE, ADD, NBINS) \
  139. template <> \
  140. void get_entropy_info<TYPE> (TYPE &add, int &nbins) \
  141. { \
  142. add = ADD; \
  143. if (nbins <= 0) \
  144. nbins = NBINS; \
  145. }
  146. ENTROPY_INFO (bool, 0, 2)
  147. ENTROPY_INFO (octave_int8, 128, 256)
  148. //ENTROPY_INFO (octave_int16, 32768, 65536)
  149. ENTROPY_INFO (octave_uint8, 0, 256)
  150. //ENTROPY_INFO (octave_uint16, 0, 65536)
  151. #undef ENTROPY_INFO
  152. template <class ET, class MT, class ET_OUT>
  153. ET_OUT entropy_filt (MT &vals, octave_idx_type len, int nbins)
  154. {
  155. ET add;
  156. get_entropy_info<ET> (add, nbins);
  157. // Compute histogram from values
  158. ColumnVector hist (nbins, 0);
  159. for (octave_idx_type i = 0; i < len; i++)
  160. hist (vals (i) + add) ++;
  161. for (octave_idx_type i = 0; i < len; i++)
  162. hist (vals (i) + add) /= (double)len;
  163. // Compute entropy
  164. double entropy = 0;
  165. for (octave_idx_type i = 0; i < nbins; i++)
  166. {
  167. const double p = hist (i);
  168. if (p > 0)
  169. entropy -= p * xlog2 (p);
  170. }
  171. return entropy;
  172. }
  173. /**
  174. * The function for the range filter
  175. */
  176. template <class ET, class MT, class ET_OUT>
  177. ET_OUT range_filt (MT &vals, octave_idx_type len, int not_used)
  178. {
  179. const ET_OUT min_val = min_filt<ET, MT, ET_OUT> (vals, len, not_used);
  180. const ET_OUT max_val = max_filt<ET, MT, ET_OUT> (vals, len, not_used);
  181. return max_val - min_val;
  182. }
  183. /**
  184. * The general function for doing the filtering.
  185. */
  186. template <class MT, class ET, class MTout, class ETout>
  187. octave_value_list do_filtering (const MT &A, const boolNDArray &dom,
  188. ETout (*filter_function) (MT&, octave_idx_type, int), const MT &S, int arg4)
  189. {
  190. octave_value_list retval;
  191. const int ndims = dom.ndims ();
  192. const octave_idx_type dom_numel = dom.numel ();
  193. const dim_vector dom_size = dom.dims ();
  194. const dim_vector A_size = A.dims ();
  195. octave_idx_type len = 0;
  196. for (octave_idx_type i = 0; i < dom_numel; i++)
  197. len += dom (i);
  198. dim_vector dim_offset (dom_size);
  199. for (int i = 0; i < ndims; i++)
  200. dim_offset (i) = (dom_size (i)+1) / 2 - 1;
  201. // Allocate output
  202. dim_vector out_size (dom_size);
  203. for (int i = 0; i < ndims; i++)
  204. out_size (i) = A_size (i) - dom_size (i) + 1;
  205. MTout out = MTout (out_size);
  206. const octave_idx_type out_numel = out.numel ();
  207. // Iterate over every element of 'out'.
  208. dim_vector idx_dim (ndims);
  209. Array<octave_idx_type> dom_idx (idx_dim);
  210. Array<octave_idx_type> A_idx (idx_dim);
  211. Array<octave_idx_type> out_idx (idx_dim, 0);
  212. dim_vector values_size (2);
  213. values_size (0) = 1;
  214. values_size (1) = len;
  215. MT values (values_size);
  216. for (octave_idx_type i = 0; i < out_numel; i++)
  217. {
  218. // For each neighbour
  219. int l = 0;
  220. for (int n = 0; n < ndims; n++)
  221. dom_idx (n) = 0;
  222. for (octave_idx_type j = 0; j < dom_numel; j++)
  223. {
  224. for (int n = 0; n < ndims; n++)
  225. A_idx (n) = out_idx (n) + dom_idx (n);
  226. if (dom (dom_idx))
  227. values (l++) = A (A_idx) + S (dom_idx);
  228. dom.increment_index (dom_idx, dom_size);
  229. }
  230. // Compute filter result
  231. out (out_idx) = filter_function (values, len, arg4);
  232. // Prepare for next iteration
  233. out.increment_index (out_idx, out_size);
  234. OCTAVE_QUIT;
  235. }
  236. retval (0) = octave_value (out);
  237. return retval;
  238. }
  239. /**
  240. * The Octave function
  241. */
  242. DEFUN_DLD(__spatial_filtering__, args, , "\
  243. -*- texinfo -*-\n\
  244. @deftypefn {Loadable Function} __spatial_filtering__(@var{A}, @var{domain},\
  245. @var{method}, @var{S}, @var{arg})\n\
  246. Implementation of two-dimensional spatial filtering. In general this function\n\
  247. should NOT be used -- user interfaces are available in other functions.\n\
  248. The function computes local characteristics of the image @var{A} in the domain\n\
  249. @var{domain}. The following values of @var{method} are supported.\n\
  250. \n\
  251. @table @asis\n\
  252. @item \"ordered\"\n\
  253. Perform ordered filtering. The output in a pixel is the @math{n}th value of a\n\
  254. sorted list containing the elements of the neighbourhood. The value of @math{n}\n\
  255. is given in the @var{arg} argument. The corresponding user interface is available\n\
  256. in @code{ordfilt2} and @code{ordfiltn}.\n\
  257. @item \"std\"\n\
  258. Compute the local standard deviation. The correponding user interface is available\n\
  259. in @code{stdfilt}.\n\
  260. @item \"entropy\"\n\
  261. Compute the local entropy. The correponding user interface is available\n\
  262. in @code{entropyfilt}.\n\
  263. @item \"range\"\n\
  264. Compute the local range of the data. The corresponding user interface is\n\
  265. available in @code{rangefilt}.\n\
  266. @item \"min\"\n\
  267. Computes the smallest value in a local neighbourheed.\n\
  268. @item \"max\"\n\
  269. Computes the largest value in a local neighbourheed.\n\
  270. @item \"encoded sign of difference\"\n\
  271. NOT IMPLEMENTED (local binary patterns style)\n\
  272. @end table\n\
  273. @seealso{cordflt2, ordfilt2}\n\
  274. @end deftypefn\n\
  275. ")
  276. {
  277. octave_value_list retval;
  278. const int nargin = args.length ();
  279. if (nargin < 4)
  280. {
  281. print_usage ();
  282. return retval;
  283. }
  284. const boolNDArray dom = args (1).bool_array_value ();
  285. if (error_state)
  286. {
  287. error ("__spatial_filtering__: invalid input");
  288. return retval;
  289. }
  290. octave_idx_type len = 0;
  291. for (octave_idx_type i = 0; i < dom.numel (); i++)
  292. len += dom (i);
  293. const int ndims = dom.ndims ();
  294. const int args0_ndims = args (0).ndims ();
  295. if (args0_ndims != ndims || args (3).ndims () != ndims)
  296. {
  297. error ("__spatial_filtering__: input must be of the same dimension");
  298. return retval;
  299. }
  300. int arg4 = (nargin == 4) ? 0 : args (4).int_value ();
  301. const std::string method = args (2).string_value ();
  302. if (error_state)
  303. {
  304. error ("__spatial_filtering__: method must be a string");
  305. return retval;
  306. }
  307. #define GENERAL_ACTION(MT, FUN, ET, MT_OUT, ET_OUT, FILTER_FUN) \
  308. { \
  309. const MT A = args (0).FUN (); \
  310. const MT S = args (3).FUN (); \
  311. if (error_state) \
  312. error ("__spatial_filtering__: invalid input"); \
  313. else \
  314. retval = do_filtering<MT, ET, MT_OUT, ET_OUT> (A, dom, FILTER_FUN<ET, MT, ET_OUT>, S, arg4); \
  315. }
  316. if (method == "ordered")
  317. {
  318. // Handle input
  319. arg4 -= 1; // convert arg to zero-based index
  320. if (arg4 > len - 1)
  321. {
  322. warning ("__spatial_filtering__: nth should be less than number of non-zero "
  323. "values in domain setting nth to largest possible value");
  324. arg4 = len - 1;
  325. }
  326. if (arg4 < 0)
  327. {
  328. warning ("__spatial_filtering__: nth should be non-negative, setting to 1");
  329. arg4 = 0;
  330. }
  331. // Do the real work
  332. #define ACTION(MT, FUN, ET) \
  333. GENERAL_ACTION(MT, FUN, ET, MT, ET, selnth)
  334. if (args (0).is_real_matrix ())
  335. ACTION (NDArray, array_value, double)
  336. else if (args (0).is_complex_matrix ())
  337. ACTION (ComplexNDArray, complex_array_value, Complex)
  338. else if (args (0).is_bool_matrix ())
  339. ACTION (boolNDArray, bool_array_value, bool)
  340. else if (args (0).is_int8_type ())
  341. ACTION (int8NDArray, int8_array_value, octave_int8)
  342. else if (args (0).is_int16_type ())
  343. ACTION (int16NDArray, int16_array_value, octave_int16)
  344. else if (args (0).is_int32_type ())
  345. ACTION (int32NDArray, int32_array_value, octave_int32)
  346. else if (args (0).is_int64_type ())
  347. ACTION (int64NDArray, int64_array_value, octave_int64)
  348. else if (args (0).is_uint8_type ())
  349. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  350. else if (args (0).is_uint16_type ())
  351. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  352. else if (args (0).is_uint32_type ())
  353. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  354. else if (args (0).is_uint64_type ())
  355. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  356. else
  357. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  358. #undef ACTION
  359. }
  360. else if (method == "min")
  361. {
  362. // Do the real work
  363. #define ACTION(MT, FUN, ET) \
  364. GENERAL_ACTION(MT, FUN, ET, MT, ET, min_filt)
  365. if (args (0).is_real_matrix ())
  366. ACTION (NDArray, array_value, double)
  367. else if (args (0).is_complex_matrix ())
  368. ACTION (ComplexNDArray, complex_array_value, Complex)
  369. else if (args (0).is_bool_matrix ())
  370. ACTION (boolNDArray, bool_array_value, bool)
  371. else if (args (0).is_int8_type ())
  372. ACTION (int8NDArray, int8_array_value, octave_int8)
  373. else if (args (0).is_int16_type ())
  374. ACTION (int16NDArray, int16_array_value, octave_int16)
  375. else if (args (0).is_int32_type ())
  376. ACTION (int32NDArray, int32_array_value, octave_int32)
  377. else if (args (0).is_int64_type ())
  378. ACTION (int64NDArray, int64_array_value, octave_int64)
  379. else if (args (0).is_uint8_type ())
  380. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  381. else if (args (0).is_uint16_type ())
  382. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  383. else if (args (0).is_uint32_type ())
  384. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  385. else if (args (0).is_uint64_type ())
  386. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  387. else
  388. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  389. #undef ACTION
  390. }
  391. else if (method == "max")
  392. {
  393. // Do the real work
  394. #define ACTION(MT, FUN, ET) \
  395. GENERAL_ACTION(MT, FUN, ET, MT, ET, max_filt)
  396. if (args (0).is_real_matrix ())
  397. ACTION (NDArray, array_value, double)
  398. else if (args (0).is_complex_matrix ())
  399. ACTION (ComplexNDArray, complex_array_value, Complex)
  400. else if (args (0).is_bool_matrix ())
  401. ACTION (boolNDArray, bool_array_value, bool)
  402. else if (args (0).is_int8_type ())
  403. ACTION (int8NDArray, int8_array_value, octave_int8)
  404. else if (args (0).is_int16_type ())
  405. ACTION (int16NDArray, int16_array_value, octave_int16)
  406. else if (args (0).is_int32_type ())
  407. ACTION (int32NDArray, int32_array_value, octave_int32)
  408. else if (args (0).is_int64_type ())
  409. ACTION (int64NDArray, int64_array_value, octave_int64)
  410. else if (args (0).is_uint8_type ())
  411. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  412. else if (args (0).is_uint16_type ())
  413. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  414. else if (args (0).is_uint32_type ())
  415. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  416. else if (args (0).is_uint64_type ())
  417. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  418. else
  419. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  420. #undef ACTION
  421. }
  422. else if (method == "range")
  423. {
  424. // Do the real work
  425. #define ACTION(MT, FUN, ET) \
  426. GENERAL_ACTION(MT, FUN, ET, MT, ET, range_filt)
  427. if (args (0).is_real_matrix ())
  428. ACTION (NDArray, array_value, double)
  429. else if (args (0).is_complex_matrix ())
  430. ACTION (ComplexNDArray, complex_array_value, Complex)
  431. else if (args (0).is_bool_matrix ())
  432. ACTION (boolNDArray, bool_array_value, bool)
  433. else if (args (0).is_int8_type ())
  434. ACTION (int8NDArray, int8_array_value, octave_int8)
  435. else if (args (0).is_int16_type ())
  436. ACTION (int16NDArray, int16_array_value, octave_int16)
  437. else if (args (0).is_int32_type ())
  438. ACTION (int32NDArray, int32_array_value, octave_int32)
  439. else if (args (0).is_int64_type ())
  440. ACTION (int64NDArray, int64_array_value, octave_int64)
  441. else if (args (0).is_uint8_type ())
  442. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  443. else if (args (0).is_uint16_type ())
  444. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  445. else if (args (0).is_uint32_type ())
  446. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  447. else if (args (0).is_uint64_type ())
  448. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  449. else
  450. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  451. #undef ACTION
  452. }
  453. else if (method == "std")
  454. {
  455. // Compute normalisation factor
  456. if (arg4 == 0)
  457. arg4 = len - 1; // unbiased
  458. else
  459. arg4 = len; // max. likelihood
  460. // Do the real work
  461. #define ACTION(MT, FUN, ET) \
  462. GENERAL_ACTION(MT, FUN, ET, NDArray, double, std_filt)
  463. if (args (0).is_real_matrix ())
  464. ACTION (NDArray, array_value, double)
  465. else if (args (0).is_bool_matrix ())
  466. ACTION (boolNDArray, bool_array_value, bool)
  467. else if (args (0).is_int8_type ())
  468. ACTION (int8NDArray, int8_array_value, octave_int8)
  469. else if (args (0).is_int16_type ())
  470. ACTION (int16NDArray, int16_array_value, octave_int16)
  471. else if (args (0).is_int32_type ())
  472. ACTION (int32NDArray, int32_array_value, octave_int32)
  473. else if (args (0).is_int64_type ())
  474. ACTION (int64NDArray, int64_array_value, octave_int64)
  475. else if (args (0).is_uint8_type ())
  476. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  477. else if (args (0).is_uint16_type ())
  478. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  479. else if (args (0).is_uint32_type ())
  480. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  481. else if (args (0).is_uint64_type ())
  482. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  483. else
  484. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  485. #undef ACTION
  486. }
  487. else if (method == "entropy")
  488. {
  489. // Do the real work
  490. #define ACTION(MT, FUN, ET) \
  491. GENERAL_ACTION(MT, FUN, ET, NDArray, double, entropy_filt)
  492. if (args (0).is_bool_matrix ())
  493. ACTION (boolNDArray, bool_array_value, bool)
  494. else if (args (0).is_int8_type ())
  495. ACTION (int8NDArray, int8_array_value, octave_int8)
  496. else if (args (0).is_uint8_type ())
  497. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  498. else
  499. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  500. #undef ACTION
  501. }
  502. else
  503. {
  504. error ("__spatial_filtering__: unknown method '%s'.", method.c_str ());
  505. }
  506. return retval;
  507. }