Mercurial > octave
changeset 22142:62208397b99e
Add function "smooth3" (patch #8864).
* scripts/plot/draw/smooth3.m: New function.
* scripts/plot/draw/module.mk: Add to build system.
* __unimplemented__.m: Remove "smooth3" from list.
* plot.txi: Add to documentation.
* isosurface.m: Add "smooth3" to @seealso list.
author | Markus Muetzel <markus.muetzel@gmx.de> |
---|---|
date | Mon, 18 Jul 2016 19:38:27 +0200 |
parents | ed6104a78ebf |
children | 1bd872efdab5 |
files | doc/interpreter/plot.txi scripts/help/__unimplemented__.m scripts/plot/draw/isonormals.m scripts/plot/draw/isosurface.m scripts/plot/draw/module.mk scripts/plot/draw/smooth3.m |
diffstat | 6 files changed, 243 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/interpreter/plot.txi Mon Jul 18 11:42:20 2016 -0700 +++ b/doc/interpreter/plot.txi Mon Jul 18 19:38:27 2016 +0200 @@ -384,6 +384,8 @@ @DOCSTRING(isocolors) +@DOCSTRING(smooth3) + @DOCSTRING(shrinkfaces) @DOCSTRING(diffuse)
--- a/scripts/help/__unimplemented__.m Mon Jul 18 11:42:20 2016 -0700 +++ b/scripts/help/__unimplemented__.m Mon Jul 18 19:38:27 2016 +0200 @@ -801,7 +801,6 @@ "serial", "setpixelposition", "showplottool", - "smooth3", "snapnow", "splitapply", "ss2tf",
--- a/scripts/plot/draw/isonormals.m Mon Jul 18 11:42:20 2016 -0700 +++ b/scripts/plot/draw/isonormals.m Mon Jul 18 19:38:27 2016 +0200 @@ -92,7 +92,7 @@ ## isofinish (p); ## @end smallexample ## -## @seealso{isosurface, isocolors} +## @seealso{isosurface, isocolors, smooth3} ## @end deftypefn ## Author: Martin Helm <martin@mhelm.de>
--- a/scripts/plot/draw/isosurface.m Mon Jul 18 11:42:20 2016 -0700 +++ b/scripts/plot/draw/isosurface.m Mon Jul 18 19:38:27 2016 +0200 @@ -132,12 +132,13 @@ ## light ("Position", [1 1 5]); ## @end smallexample ## -## @seealso{isonormals, isocolors} +## @seealso{isonormals, isocolors, smooth3} ## @end deftypefn ## Author: Martin Helm <martin@mhelm.de> -## FIXME: Add support for string input argument "verbose" (needs changes to __marching_cube__.m) +## FIXME: Add support for string input argument "verbose" +## (needs changes to __marching_cube__.m) function varargout = isosurface (varargin)
--- a/scripts/plot/draw/module.mk Mon Jul 18 11:42:20 2016 -0700 +++ b/scripts/plot/draw/module.mk Mon Jul 18 19:38:27 2016 +0200 @@ -82,6 +82,7 @@ scripts/plot/draw/semilogy.m \ scripts/plot/draw/shrinkfaces.m \ scripts/plot/draw/slice.m \ + scripts/plot/draw/smooth3.m \ scripts/plot/draw/sombrero.m \ scripts/plot/draw/sphere.m \ scripts/plot/draw/stairs.m \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/plot/draw/smooth3.m Mon Jul 18 19:38:27 2016 +0200 @@ -0,0 +1,236 @@ +## Copyright (C) 2016 Markus Muetzel +## +## This file is part of Octave. +## +## Octave 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. +## +## Octave 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 Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {} {@var{smoothed_data} =} smooth3 (@var{data}) +## @deftypefnx {} {@var{smoothed_data} =} smooth3 (@var{data}, @var{method}) +## @deftypefnx {} {@var{smoothed_data} =} smooth3 (@var{data}, @var{method}, @var{sz}) +## @deftypefnx {} {@var{smoothed_data} =} smooth3 (@var{data}, @var{method}, @var{sz}, @var{std_dev}) +## Smooth values of 3-dimensional matrix @var{data}. +## +## This function can be used, for example, to reduce the impact of noise in +## @var{data} before calculating isosurfaces. +## +## @var{data} must be a non-singleton 3-dimensional matrix. The smoothed data +## from this matrix is returned in @var{smoothed_data} which is of the same +## size as @var{data}. +## +## The option input @var{method} determines which convolution kernel is used +## for the smoothing process. Possible choices: +## +## @table @asis +## @item @qcode{"box"}, @qcode{"b"} (default) +## to use a convolution kernel with sharp edges. +## +## @item @qcode{"gaussian"}, @qcode{"g"} +## to use a convolution kernel that is represented by a non-correlated +## trivariate normal distribution function. +## @end table +## +## @var{sz} is either a vector of 3 elements representing the size of the +## convolution kernel in x-, y- and z-direction or a scalar, in which case +## the same size is used in all three dimensions. The default value is 3. +## +## When @var{method} is @qcode{"gaussian"}, @var{std_dev} defines the standard +## deviation of the trivariate normal distribution function. @var{std_dev} is +## either a vector of 3 elements representing the standard deviation of the +## Gaussian convolution kernel in x-, y- and z-directions or a scalar, in which +## case the same value is used in all three dimensions. The default value is +## 0.65. +## +## @seealso{isosurface, isonormals, patch} +## @end deftypefn + +## Author: mmuetzel + +function smoothed_data = smooth3 (data, method = "box", sz = 3, std_dev = 0.65) + + if (nargin < 1 || nargin > 4) + print_usage (); + endif + + [data, conv_kernel, sz, std_dev] = ... + __get_check_smooth3_args__ (data, method, sz, std_dev); + + ## Manually pad data by replicating the values at the edges. + ## (convn would pad with zeros) + idx = cell (3, 1); + for i_dim = 1:3 + sz_dim = size (data, i_dim); + pad_vec = ones (1, (sz(i_dim)-1)/2); + idx{i_dim} = [pad_vec 1:sz_dim sz_dim*pad_vec]; + endfor + data_padded = data(idx{:}); + + smoothed_data = convn (data_padded, conv_kernel, "valid"); + +endfunction + +function [data, conv_kernel, sz, std_dev] = __get_check_smooth3_args__ (data, method, sz, std_dev); + + if (! isnumeric (data) || ndims (data) != 3) + error ("smooth3: DATA must be a 3-D numeric matrix"); + endif + + if (! ischar (method)) + error ("smooth3: METHOD must be a string"); + endif + + if (! isreal (sz)) + error ("smooth3: SZ must be a real scalar or 3-element vector"); + elseif (isscalar (sz)) + sz(1:3) = sz; + elseif (numel (sz) != 3) + error (["smooth3: SZ of the convolution kernel must be " ... + "a scalar or 3-element vector"]); + endif + if (any (sz < 1) || any (rem (sz, 2) != 1)) + error (["smooth3: SZ of the convolution kernel must consist " ... + "of positive odd integers"]); + endif + + switch (tolower (method)) + case {"g", "gaussian"} + ## check std_dev + if (! isreal (std_dev)) + error ("smooth3: STD_DEV must be a real scalar or 3-element vector"); + elseif (isscalar (std_dev)) + std_dev(1:3) = std_dev; + elseif (numel (std_dev) != 3) + error (["smooth3: STD_DEV of the Gaussian convolution kernel " ... + "must be scalar or 3-element vector"]); + endif + + conv_kernel = __smooth3_gaussian3__ (sz, std_dev); + + case {"b", "box"} + conv_kernel = ones (sz) / prod (sz); + + otherwise + error ("smooth3: invalid METHOD '%s'", method); + + endswitch + +endfunction + +function gaussian3 = __smooth3_gaussian3__ (sz, std_dev) + ## trivariate non-correlated Gaussian distribution function + + x = (-(sz(2)-1)/2:(sz(2)-1)/2) / std_dev(2); + y = (-(sz(1)-1)/2:(sz(1)-1)/2) / std_dev(1); + z = (-(sz(3)-1)/2:(sz(3)-1)/2) / std_dev(3); + + [xx, yy, zz] = meshgrid (x, y, z); + + gaussian3 = exp (-(xx.^2 + yy.^2 + zz.^2)/2); + + gaussian3 /= sum (gaussian3(:)); # normalize + +endfunction + + +%!demo +%! data = rand (10, 10, 10); +%! clf; +%! subplot (1, 2, 1); +%! patch (isosurface (data, .5), ... +%! 'FaceColor', 'blue', 'EdgeColor', 'k'); +%! title ('Original data') +%! view (3); +%! smoothed_data = smooth3 (data); +%! subplot (1, 2, 2); +%! patch (isosurface (smoothed_data, .5), ... +%! 'FaceColor', 'blue', 'EdgeColor', 'k'); +%! title ('Smoothed data'); +%! view (3); + +## one input argument (method: "box") +%!test +%! a = rand (10, 8, 7); +%! b = smooth3 (a); +%! assert (size_equal (a, b), true); + +## two input argument (method: "gaussian") +%!test +%! a = rand (5, 8, 7); +%! b = smooth3 (a, "gaussian"); +%! assert (size_equal (a, b), true); + +## three input argument (method: "box") +%!test +%! a = rand (3, 8, 7); +%! b = smooth3 (a, "box", 5); +%! assert (size_equal (a, b), true); + +## three input argument (method: "gaussian") +%!test +%! a = rand (3, 8, 7); +%! b = smooth3 (a, "gaussian", 7); +%! assert (size_equal (a, b), true); + +## size of convolution kernel = 1: no smoothing (method: "box") +%!test +%! a = rand (9, 8, 7); +%! b = smooth3 (a, "box", 1); +%! assert (a, b); + +## size of convolution kernel = 1: no smoothing (method: "gaussian") +%!test +%! a = rand (9, 8, 7); +%! b = smooth3 (a, "gaussian", 1); +%! assert (a, b); + +## four input arguments (method: "gaussian") +%!test +%! a = rand (3, 8, 7); +%! b = smooth3 (a, "gaussian", 7, .5); +%! assert (size_equal (a, b), true); + +## size of convolution kernel is different in x, y and z (method: "box") +%!test +%! a = rand (3, 8, 7); +%! b = smooth3 (a, "box", [5 3 7]); +%! assert (size_equal (a, b), true); + +## size of convolution kernel is different in x, y and z (method: "gaussian") +%!test +%! a = rand (3, 8, 7); +%! b = smooth3 (a, "gaussian", [5 3 7]); +%! assert (size_equal (a, b), true); + +## size and width of gaussian convolution kernel is different in x, y and z +## (method: "gaussian") +%!test +%! a = rand (3, 8, 7); +%! b = smooth3 (a, "gaussian", [7 3 5], [.3 .5 .4]); +%! assert (size_equal (a, b), true); + +## Test input validation +%!error smooth3 () +%!error smooth3 (1,2,3,4,5) +%!error <DATA must be a 3-D numeric matrix> smooth3 (cell (2,2,2)) +%!error <DATA must be a 3-D numeric matrix> smooth3 (1) +%!error <METHOD must be a string> smooth3 (ones (2,2,2), {3}) +%!error <SZ must be a real> smooth3 (ones (2,2,2), "b", {3}) +%!error <SZ .* must be .* 3-element vector> smooth3 (ones (2,2,2), :, [3 5]) +%!error <SZ .* must .* positive .* integers> smooth3 (ones (2,2,2), :, [3 0 5]) +%!error <SZ .* must .* odd integers> smooth3 (ones (2,2,2), :, [3 2 5]) +%!error <STD_DEV must be a real> smooth3 (ones (2,2,2), "g", :, {0.65}) +%!error <STD_DEV .* must be .* 3-element> smooth3 (ones (2,2,2), "g", :, [1 2]) +%!error <invalid METHOD 'foobar'> smooth3 (ones (2,2,2), "foobar") +