Mercurial > hg > octave-image
diff src/__bilateral__.cc @ 313:242b7e14560a
imsmooth: support for bilateral filtering
author | hauberg |
---|---|
date | Mon, 24 Mar 2008 19:15:21 +0000 |
parents | |
children | 23d88658d3ca |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/__bilateral__.cc @@ -0,0 +1,191 @@ +// Copyright (C) 2008 Soren Hauberg +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, see <http://www.gnu.org/licenses/>. + +#include <octave/oct.h> + +inline +int MAX(const int a, const int b) +{ + if (a > b) + return a; + else + return b; +} + +inline +double gauss1(const double x, const double mu, const double sigma) +{ + const double d = x-mu; + return exp( -0.5*(d*d)/(sigma*sigma) ); +} + +inline +double gauss(const double *x, const double *mu, const double sigma, const octave_idx_type ndims) +{ + double s = 0; + for (octave_idx_type i = 0; i < ndims; i++) + { + const double d = x[i] - mu[i]; + s += d*d; + } + return exp( -0.5*s/(sigma*sigma) ); +} + +template <class MatrixType> +octave_value +bilateral(const MatrixType &im, const double sigma_d, const double sigma_r) +{ + // Get sizes + const octave_idx_type ndims = im.ndims(); + const dim_vector size = im.dims(); + const octave_idx_type num_planes = (ndims == 2) ? 1 : size(2); + + // Build spatial kernel + const int s = MAX(round(3*sigma_d), 1); + Matrix kernel(2*s+1, 2*s+1); + for (octave_idx_type r = 0; r < 2*s+1; r++) + { + for (octave_idx_type c = 0; c < 2*s+1; c++) + { + const int dr = r-s; + const int dc = c-s; + kernel(r,c) = exp( -0.5*( dr*dr + dc*dc )/(sigma_d*sigma_d) ); + } + } + + // Allocate output + dim_vector out_size(size); + out_size(0) = MAX(size(0) - 2*s, 0); + out_size(1) = MAX(size(1) - 2*s, 0); + MatrixType out = MatrixType(out_size); + + // Iterate over every element of 'out'. + for (octave_idx_type r = 0; r < out_size(0); r++) + { + for (octave_idx_type c = 0; c < out_size(1); c++) + { + // For each neighbour + double val[num_planes]; + double sum[num_planes]; + double k = 0; + for (octave_idx_type i = 0; i < num_planes; i++) + { + val[i] = im(r,c,i); + sum[i] = 0; + } + for (octave_idx_type kr = 0; kr < 2*s+1; kr++) + { + for (octave_idx_type kc = 0; kc < 2*s+1; kc++) + { + double lval[num_planes]; + for (octave_idx_type i = 0; i < num_planes; i++) lval[i] = im(r+kr,c+kc,i); + const double w = kernel(kr,kc)*gauss(val, lval, sigma_r, ndims); + for (octave_idx_type i = 0; i < num_planes; i++) sum[i] += w*lval[i]; + k += w; + } + } + for (octave_idx_type i = 0; i < num_planes; i++) out(r,c,i) = sum[i]/k; + OCTAVE_QUIT; + } + } + + return octave_value(out); +} + +DEFUN_DLD(__bilateral__, args, , "\ +-*- texinfo -*-\n\ +@deftypefn {Loadable Function} __bilateral__(@var{im}, @var{sigma_d}, @var{sigma_r})\n\ +Performs Gaussian bilateral filtering in the image @var{im}. @var{sigma_d} is the\n\ +spread of the Gaussian used as closenes function, and @var{sigma_r} is the spread\n\ +of Gaussian used as similarity function. This function is internal and should NOT\n\ +be called directly. Instead use @code{imsmooth}.\n\ +@end deftypefn\n\ +") +{ + octave_value_list retval; + if (args.length() != 3) + { + print_usage (); + return retval; + } + + const octave_idx_type ndims = args(0).ndims(); + if (ndims != 2 && ndims != 3) + { + error("__bilateral__: only 2 and 3 dimensional is supported"); + return retval; + } + const double sigma_d = args(1).scalar_value(); + const double sigma_r = args(2).scalar_value(); + if (error_state) + { + error("__bilateral__: invalid input"); + return retval; + } + + // Take action depending on input type + if (args(0).is_real_matrix()) + { + const NDArray im = args(0).array_value(); + retval = bilateral<NDArray>(im, sigma_d, sigma_r); + } + else if (args(0).is_int8_type()) + { + const int8NDArray im = args(0).int8_array_value(); + retval = bilateral<int8NDArray>(im, sigma_d, sigma_r); + } + else if (args(0).is_int16_type()) + { + const int16NDArray im = args(0).int16_array_value(); + retval = bilateral<int16NDArray>(im, sigma_d, sigma_r); + } + else if (args(0).is_int32_type()) + { + const int32NDArray im = args(0).int32_array_value(); + retval = bilateral<int32NDArray>(im, sigma_d, sigma_r); + } + else if (args(0).is_int64_type()) + { + const int64NDArray im = args(0).int64_array_value(); + retval = bilateral<int64NDArray>(im, sigma_d, sigma_r); + } + else if (args(0).is_uint8_type()) + { + const uint8NDArray im = args(0).uint8_array_value(); + retval = bilateral<uint8NDArray>(im, sigma_d, sigma_r); + } + else if (args(0).is_uint16_type()) + { + const uint16NDArray im = args(0).uint16_array_value(); + retval = bilateral<uint16NDArray>(im, sigma_d, sigma_r); + } + else if (args(0).is_uint32_type()) + { + const uint32NDArray im = args(0).uint32_array_value(); + retval = bilateral<uint32NDArray>(im, sigma_d, sigma_r); + } + else if (args(0).is_uint64_type()) + { + const uint64NDArray im = args(0).uint64_array_value(); + retval = bilateral<uint64NDArray>(im, sigma_d, sigma_r); + } + else + { + error("__bilateral__: first input should be a real or integer array"); + return retval; + } + + return retval; +}