PageRenderTime 84ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/trunk/octave-forge/main/image/src/__spatial_filtering__.cc

#
C++ | 572 lines | 440 code | 70 blank | 62 comment | 172 complexity | ef260308e821f074549bf3f9ee4c433d 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. // Allocate output
  199. dim_vector out_size (dom_size);
  200. for (int i = 0; i < ndims; i++)
  201. out_size (i) = A_size (i) - dom_size (i) + 1;
  202. MTout out = MTout (out_size);
  203. const octave_idx_type out_numel = out.numel ();
  204. // Iterate over every element of 'out'.
  205. dim_vector idx_dim (ndims, 1);
  206. Array<octave_idx_type> dom_idx (idx_dim);
  207. Array<octave_idx_type> A_idx (idx_dim);
  208. Array<octave_idx_type> out_idx (idx_dim, 0);
  209. dim_vector values_size (1, len);
  210. MT values (values_size);
  211. for (octave_idx_type i = 0; i < out_numel; i++)
  212. {
  213. // For each neighbour
  214. int l = 0;
  215. for (int n = 0; n < ndims; n++)
  216. dom_idx (n) = 0;
  217. for (octave_idx_type j = 0; j < dom_numel; j++)
  218. {
  219. for (int n = 0; n < ndims; n++)
  220. A_idx (n) = out_idx (n) + dom_idx (n);
  221. if (dom (dom_idx))
  222. values (l++) = A (A_idx) + S (dom_idx);
  223. dom.increment_index (dom_idx, dom_size);
  224. }
  225. // Compute filter result
  226. out (out_idx) = filter_function (values, len, arg4);
  227. // Prepare for next iteration
  228. out.increment_index (out_idx, out_size);
  229. OCTAVE_QUIT;
  230. }
  231. retval (0) = octave_value (out);
  232. return retval;
  233. }
  234. /**
  235. * The Octave function
  236. */
  237. DEFUN_DLD(__spatial_filtering__, args, , "\
  238. -*- texinfo -*-\n\
  239. @deftypefn {Loadable Function} __spatial_filtering__(@var{A}, @var{domain},\
  240. @var{method}, @var{S}, @var{arg})\n\
  241. Implementation of two-dimensional spatial filtering. In general this function\n\
  242. should NOT be used -- user interfaces are available in other functions.\n\
  243. The function computes local characteristics of the image @var{A} in the domain\n\
  244. @var{domain}. The following values of @var{method} are supported.\n\
  245. \n\
  246. @table @asis\n\
  247. @item \"ordered\"\n\
  248. Perform ordered filtering. The output in a pixel is the @math{n}th value of a\n\
  249. sorted list containing the elements of the neighbourhood. The value of @math{n}\n\
  250. is given in the @var{arg} argument. The corresponding user interface is available\n\
  251. in @code{ordfilt2} and @code{ordfiltn}.\n\
  252. @item \"std\"\n\
  253. Compute the local standard deviation. The corresponding user interface is available\n\
  254. in @code{stdfilt}.\n\
  255. @item \"entropy\"\n\
  256. Compute the local entropy. The corresponding user interface is available\n\
  257. in @code{entropyfilt}.\n\
  258. @item \"range\"\n\
  259. Compute the local range of the data. The corresponding user interface is\n\
  260. available in @code{rangefilt}.\n\
  261. @item \"min\"\n\
  262. Computes the smallest value in a local neighbourheed.\n\
  263. @item \"max\"\n\
  264. Computes the largest value in a local neighbourheed.\n\
  265. @item \"encoded sign of difference\"\n\
  266. NOT IMPLEMENTED (local binary patterns style)\n\
  267. @end table\n\
  268. @seealso{cordflt2, ordfilt2}\n\
  269. @end deftypefn\n\
  270. ")
  271. {
  272. octave_value_list retval;
  273. const int nargin = args.length ();
  274. if (nargin < 4)
  275. {
  276. print_usage ();
  277. return retval;
  278. }
  279. const boolNDArray dom = args (1).bool_array_value ();
  280. if (error_state)
  281. {
  282. error ("__spatial_filtering__: invalid input");
  283. return retval;
  284. }
  285. octave_idx_type len = 0;
  286. for (octave_idx_type i = 0; i < dom.numel (); i++)
  287. len += dom (i);
  288. const int ndims = dom.ndims ();
  289. const int args0_ndims = args (0).ndims ();
  290. if (args0_ndims != ndims || args (3).ndims () != ndims)
  291. {
  292. error ("__spatial_filtering__: input must be of the same dimension");
  293. return retval;
  294. }
  295. int arg4 = (nargin == 4) ? 0 : args (4).int_value ();
  296. const std::string method = args (2).string_value ();
  297. if (error_state)
  298. {
  299. error ("__spatial_filtering__: method must be a string");
  300. return retval;
  301. }
  302. #define GENERAL_ACTION(MT, FUN, ET, MT_OUT, ET_OUT, FILTER_FUN) \
  303. { \
  304. const MT A = args (0).FUN (); \
  305. const MT S = args (3).FUN (); \
  306. if (error_state) \
  307. error ("__spatial_filtering__: invalid input"); \
  308. else \
  309. retval = do_filtering<MT, ET, MT_OUT, ET_OUT> (A, dom, FILTER_FUN<ET, MT, ET_OUT>, S, arg4); \
  310. }
  311. if (method == "ordered")
  312. {
  313. // Handle input
  314. arg4 -= 1; // convert arg to zero-based index
  315. if (arg4 > len - 1)
  316. {
  317. warning ("__spatial_filtering__: nth should be less than number of non-zero "
  318. "values in domain setting nth to largest possible value");
  319. arg4 = len - 1;
  320. }
  321. if (arg4 < 0)
  322. {
  323. warning ("__spatial_filtering__: nth should be non-negative, setting to 1");
  324. arg4 = 0;
  325. }
  326. // Do the real work
  327. #define ACTION(MT, FUN, ET) \
  328. GENERAL_ACTION(MT, FUN, ET, MT, ET, selnth)
  329. if (args (0).is_real_matrix ())
  330. ACTION (NDArray, array_value, double)
  331. else if (args (0).is_complex_matrix ())
  332. ACTION (ComplexNDArray, complex_array_value, Complex)
  333. else if (args (0).is_bool_matrix ())
  334. ACTION (boolNDArray, bool_array_value, bool)
  335. else if (args (0).is_int8_type ())
  336. ACTION (int8NDArray, int8_array_value, octave_int8)
  337. else if (args (0).is_int16_type ())
  338. ACTION (int16NDArray, int16_array_value, octave_int16)
  339. else if (args (0).is_int32_type ())
  340. ACTION (int32NDArray, int32_array_value, octave_int32)
  341. else if (args (0).is_int64_type ())
  342. ACTION (int64NDArray, int64_array_value, octave_int64)
  343. else if (args (0).is_uint8_type ())
  344. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  345. else if (args (0).is_uint16_type ())
  346. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  347. else if (args (0).is_uint32_type ())
  348. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  349. else if (args (0).is_uint64_type ())
  350. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  351. else
  352. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  353. #undef ACTION
  354. }
  355. else if (method == "min")
  356. {
  357. // Do the real work
  358. #define ACTION(MT, FUN, ET) \
  359. GENERAL_ACTION(MT, FUN, ET, MT, ET, min_filt)
  360. if (args (0).is_real_matrix ())
  361. ACTION (NDArray, array_value, double)
  362. else if (args (0).is_complex_matrix ())
  363. ACTION (ComplexNDArray, complex_array_value, Complex)
  364. else if (args (0).is_bool_matrix ())
  365. ACTION (boolNDArray, bool_array_value, bool)
  366. else if (args (0).is_int8_type ())
  367. ACTION (int8NDArray, int8_array_value, octave_int8)
  368. else if (args (0).is_int16_type ())
  369. ACTION (int16NDArray, int16_array_value, octave_int16)
  370. else if (args (0).is_int32_type ())
  371. ACTION (int32NDArray, int32_array_value, octave_int32)
  372. else if (args (0).is_int64_type ())
  373. ACTION (int64NDArray, int64_array_value, octave_int64)
  374. else if (args (0).is_uint8_type ())
  375. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  376. else if (args (0).is_uint16_type ())
  377. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  378. else if (args (0).is_uint32_type ())
  379. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  380. else if (args (0).is_uint64_type ())
  381. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  382. else
  383. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  384. #undef ACTION
  385. }
  386. else if (method == "max")
  387. {
  388. // Do the real work
  389. #define ACTION(MT, FUN, ET) \
  390. GENERAL_ACTION(MT, FUN, ET, MT, ET, max_filt)
  391. if (args (0).is_real_matrix ())
  392. ACTION (NDArray, array_value, double)
  393. else if (args (0).is_complex_matrix ())
  394. ACTION (ComplexNDArray, complex_array_value, Complex)
  395. else if (args (0).is_bool_matrix ())
  396. ACTION (boolNDArray, bool_array_value, bool)
  397. else if (args (0).is_int8_type ())
  398. ACTION (int8NDArray, int8_array_value, octave_int8)
  399. else if (args (0).is_int16_type ())
  400. ACTION (int16NDArray, int16_array_value, octave_int16)
  401. else if (args (0).is_int32_type ())
  402. ACTION (int32NDArray, int32_array_value, octave_int32)
  403. else if (args (0).is_int64_type ())
  404. ACTION (int64NDArray, int64_array_value, octave_int64)
  405. else if (args (0).is_uint8_type ())
  406. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  407. else if (args (0).is_uint16_type ())
  408. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  409. else if (args (0).is_uint32_type ())
  410. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  411. else if (args (0).is_uint64_type ())
  412. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  413. else
  414. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  415. #undef ACTION
  416. }
  417. else if (method == "range")
  418. {
  419. // Do the real work
  420. #define ACTION(MT, FUN, ET) \
  421. GENERAL_ACTION(MT, FUN, ET, MT, ET, range_filt)
  422. if (args (0).is_real_matrix ())
  423. ACTION (NDArray, array_value, double)
  424. else if (args (0).is_complex_matrix ())
  425. ACTION (ComplexNDArray, complex_array_value, Complex)
  426. else if (args (0).is_bool_matrix ())
  427. ACTION (boolNDArray, bool_array_value, bool)
  428. else if (args (0).is_int8_type ())
  429. ACTION (int8NDArray, int8_array_value, octave_int8)
  430. else if (args (0).is_int16_type ())
  431. ACTION (int16NDArray, int16_array_value, octave_int16)
  432. else if (args (0).is_int32_type ())
  433. ACTION (int32NDArray, int32_array_value, octave_int32)
  434. else if (args (0).is_int64_type ())
  435. ACTION (int64NDArray, int64_array_value, octave_int64)
  436. else if (args (0).is_uint8_type ())
  437. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  438. else if (args (0).is_uint16_type ())
  439. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  440. else if (args (0).is_uint32_type ())
  441. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  442. else if (args (0).is_uint64_type ())
  443. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  444. else
  445. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  446. #undef ACTION
  447. }
  448. else if (method == "std")
  449. {
  450. // Compute normalisation factor
  451. if (arg4 == 0)
  452. arg4 = len - 1; // unbiased
  453. else
  454. arg4 = len; // max. likelihood
  455. // Do the real work
  456. #define ACTION(MT, FUN, ET) \
  457. GENERAL_ACTION(MT, FUN, ET, NDArray, double, std_filt)
  458. if (args (0).is_real_matrix ())
  459. ACTION (NDArray, array_value, double)
  460. else if (args (0).is_bool_matrix ())
  461. ACTION (boolNDArray, bool_array_value, bool)
  462. else if (args (0).is_int8_type ())
  463. ACTION (int8NDArray, int8_array_value, octave_int8)
  464. else if (args (0).is_int16_type ())
  465. ACTION (int16NDArray, int16_array_value, octave_int16)
  466. else if (args (0).is_int32_type ())
  467. ACTION (int32NDArray, int32_array_value, octave_int32)
  468. else if (args (0).is_int64_type ())
  469. ACTION (int64NDArray, int64_array_value, octave_int64)
  470. else if (args (0).is_uint8_type ())
  471. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  472. else if (args (0).is_uint16_type ())
  473. ACTION (uint16NDArray, uint16_array_value, octave_uint16)
  474. else if (args (0).is_uint32_type ())
  475. ACTION (uint32NDArray, uint32_array_value, octave_uint32)
  476. else if (args (0).is_uint64_type ())
  477. ACTION (uint64NDArray, uint64_array_value, octave_uint64)
  478. else
  479. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  480. #undef ACTION
  481. }
  482. else if (method == "entropy")
  483. {
  484. // Do the real work
  485. #define ACTION(MT, FUN, ET) \
  486. GENERAL_ACTION(MT, FUN, ET, NDArray, double, entropy_filt)
  487. if (args (0).is_bool_matrix ())
  488. ACTION (boolNDArray, bool_array_value, bool)
  489. else if (args (0).is_int8_type ())
  490. ACTION (int8NDArray, int8_array_value, octave_int8)
  491. else if (args (0).is_uint8_type ())
  492. ACTION (uint8NDArray, uint8_array_value, octave_uint8)
  493. else
  494. error ("__spatial_filtering__: first input should be a real, complex, or integer array");
  495. #undef ACTION
  496. }
  497. else
  498. {
  499. error ("__spatial_filtering__: unknown method '%s'.", method.c_str ());
  500. }
  501. return retval;
  502. }