Mercurial > forge
changeset 11629:3c1285988902 octave-forge
src/bwdist.cc:
* bugfix for matlab compatibility, no check on matrix actually
performed, all non-zero values are considered true.
* documentation improvement.
* deprecated use of first letter to specify method.
* a lot of clean up, hopefully makes code simpler.
* more tests
author | carandraug |
---|---|
date | Mon, 15 Apr 2013 00:46:30 +0000 |
parents | a99fb0fb3ba6 |
children | 4790a882c6a3 |
files | main/image/src/bwdist.cc |
diffstat | 1 files changed, 111 insertions(+), 112 deletions(-) [+] |
line wrap: on
line diff
--- a/main/image/src/bwdist.cc Sun Apr 14 19:33:57 2013 +0000 +++ b/main/image/src/bwdist.cc Mon Apr 15 00:46:30 2013 +0000 @@ -1,4 +1,5 @@ // Copyright (C) 2009 Stefan Gustavson <stefan.gustavson@gmail.com> +// Copyright (C) 2013 Carnë Draug <carandraug@octave.org> // // 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 @@ -13,18 +14,16 @@ // You should have received a copy of the GNU General Public License along with // this program; if not, see <http://www.gnu.org/licenses/>. -// __bwdist__.cc - OCT file, implements the BWDIST function // Depends on "edtfunc.c" for the actual computations #include <octave/oct.h> - #ifdef __cplusplus extern "C" { #endif -#define DIST_EUCLIDEAN(x,y) ((int)(x)*(x) + (y)*(y)) +#define DIST_EUCLIDEAN(x,y) sqrt((int)(x)*(x) + (y)*(y)) #define MAX(x,y) ((x)>(y) ? (x) : (y)) #define DIST_CHESSBOARD(x,y) (MAX(abs(x), abs(y))) #define DIST_CITYBLOCK(x,y) (abs(x) + abs(y)) @@ -59,131 +58,128 @@ } /* end extern "C" */ #endif -DEFUN_DLD ( __bwdist__, args, nargout, -"-*- texinfo -*-\n\ -@deftypefn {Loadable Function} {@var{D} =} __bwdist__(@var{bw})\n\ -Computes the distance transform of the image @var{bw}.\n\ -@var{bw} should be a binary 2D array, either a Boolean array or a\n\ -numeric array containing only the values 0 and 1.\n\ -The return value @var{D} is a double matrix of the same size as @var{bw}.\n\ -Elements with value 0 are considered background pixels, elements\n\ -with value 1 are considered object pixels. The return value\n\ -for each background pixel is the distance (according to the chosen\n\ -metric) to the closest object pixel. For each object pixel the\n\ -return value is 0.\n\ +DEFUN_DLD (bwdist, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {Loadable Function} {@var{dist} =} bwdist (@var{bw})\n\ +@deftypefnx {Loadable Function} {@var{dist} =} bwdist (@var{bw}, @var{method})\n\ +@deftypefnx {Loadable Function} {[@var{dist}, @var{idx}] =} bwdist (@dots{})\n\ +Compute distance transform in binary image.\n\ +\n\ +The image @var{bw} must be a binary matrix For @sc{matlab} compatibility, no\n\ +check is performed, all non-zero values are considered true, or object pixels.\n\ +The return value @var{dist}, is the distance of each background pixel to the\n\ +closest object pixel.\n\ +\n\ +@var{idx} is the linear index for the closest object, used to calculate the\n\ +distance for each of the pixels.\n\ \n\ -@deftypefnx{Loadable Function} {@var{D} =} __bwdist__(@var{bw}, @var{method})\n\ +The distance can be measured through different @var{method}s:\n\ +\n\ +@table @asis\n\ +@item euclidean (default)\n\ +\n\ +@item chessboard\n\ \n\ -@var{method} is a string to choose the distance metric. Currently\n\ -available metrics are 'euclidean', 'chessboard', 'cityblock' and\n\ -'quasi-euclidean', which may each be abbreviated\n\ -to any string starting with 'e', 'ch', 'ci' and 'q', respectively.\n\ -If @var{method} is not specified, 'euclidean' is the default.\n\ +@item cityblock\n\ +\n\ +@item quasi-euclidean\n\ \n\ -@deftypefnx {Loadable Function} {[@var{D},@var{C}] =} __bwdist__(@var{bw}, @var{method})\n\ +@end table\n\ \n\ -If a second output argument is given, the linear index for the\n\ -closest object pixel is returned for each pixel. (For object\n\ -pixels, the index points to the pixel itself.) The return value\n\ -@var{C} is a matrix the same size as @var{bw}.\n\n\ +Currently, only 2D images are supported.\n\ +\n\ @end deftypefn") { - const int nargin = args.length(); octave_value_list retval; - /* Check for proper number of input and output arguments */ - if ((nargin < 1) || (nargin>2)) { - error ("bwdist accepts only one or two input parameters."); - } - else if (nargout > 2) { - error ("bwdist returns at most 2 output parameters."); - } - else { - /* Make sure input is a matrix */ - const Matrix bw = args(0).matrix_value(); - if (error_state) { - error ("bwdist input argument must be a matrix"); + const int nargin = args.length (); + if (nargin < 1 || nargin > 2) + { + print_usage (); return retval; } - /* Warn if input is not a binary image */ - if(bw.any_element_not_one_or_zero()) { - warning ("bwdist input contains values other than 1 and 0."); + + // for matlab compatibility, we do not actually check if the values are all + // 0 and 1, any non-zero value is considered true + + // FIXME const boolMatrix bw = args (0).bool_matrix_value(); + + const Matrix bw = args (0).matrix_value (); + if (error_state) + { + error ("bwdist: BW must be a matrix"); + return retval; } - /* Everything seems to be OK to proceed */ - dim_vector dims = bw.dims(); - int rows = dims(0); - int cols = dims(1); - int caseMethod = 0; // Default 0 means Euclidean - if(nargin > 1) { - charMatrix method = args(1).char_matrix_value(); - if(method(0) == 'e') caseMethod = 0; // Euclidean; - else if (method(0) == 'c') { - if(method(1) == 'h') caseMethod = 1; // chessboard - else if(method(1) == 'i') caseMethod = 2; // cityblock + std::string method = (nargin > 1) ? args (1).string_value () : "euclidean"; + if (error_state) + { + error ("bwdist: METHOD must be a string"); + return retval; + } + for (int q = 0; q < method.length (); q++) + method[q] = tolower (method[q]); + + if (method.length () <= 2) { + static bool warned = false; + if (! warned ) + { + warning ("bwdist: specifying METHOD with abbreviation is deprecated"); + warned = true; } - else if(method(0) == 'q') caseMethod = 3; // quasi-Euclidean - else { - warning ("unknown metric, using 'euclidean'"); - caseMethod = 0; - } + } + + const int cols = bw.cols (); + const int rows = bw.rows (); + const int numel = bw.numel (); + + // Allocate two arrays for temporary output values + OCTAVE_LOCAL_BUFFER (short, xdist, numel); + OCTAVE_LOCAL_BUFFER (short, ydist, numel); + + Matrix dist (rows, cols); // the output distance matrix + + if (! method.compare ("euclidean") || ! method.compare ("e")) + { + euclidean (bw, rows, cols, xdist, ydist); + for (int i = 0; i < numel; i++) + dist(i) = DIST_EUCLIDEAN (xdist[i], ydist[i]); + } + else if (! method.compare ("chessboard") || ! method.compare ("ch")) + { + chessboard (bw, rows, cols, xdist, ydist); + for (int i = 0; i < numel; i++) + dist(i) = DIST_CHESSBOARD (xdist[i], ydist[i]); + } + else if (! method.compare ("cityblock") || ! method.compare ("ci")) + { + cityblock (bw, rows, cols, xdist, ydist); + for (int i = 0; i < numel; i++) + dist(i) = DIST_CITYBLOCK (xdist[i], ydist[i]); + } + else if (! method.compare ("quasi-euclidean") || ! method.compare ("q")) + { + quasi_euclidean (bw, rows, cols, xdist, ydist); + for (int i = 0; i < numel; i++) + dist(i) = DIST_QUASI_EUCLIDEAN (xdist[i], ydist[i]); + } + else + { + error ("bwdist: unknown METHOD '%s'", method.c_str ()); } - if (!error_state) { - /* Allocate two arrays for temporary output values */ - OCTAVE_LOCAL_BUFFER (short, xdist, dims.numel()); - OCTAVE_LOCAL_BUFFER (short, ydist, dims.numel()); - - /* Create final output array */ - Matrix D (rows, cols); - - /* Call the appropriate C subroutine and compute output */ - switch(caseMethod) { - - case 1: - chessboard(bw, rows, cols, xdist, ydist); - for(int i=0; i<rows*cols; i++) { - D(i) = DIST_CHESSBOARD(xdist[i], ydist[i]); - } - break; - - case 2: - cityblock(bw, rows, cols, xdist, ydist); - for(int i=0; i<rows*cols; i++) { - D(i) = DIST_CITYBLOCK(xdist[i], ydist[i]); - } - break; + retval(0) = dist; - case 3: - quasi_euclidean(bw, rows, cols, xdist, ydist); - for(int i=0; i<rows*cols; i++) { - D(i) = DIST_QUASI_EUCLIDEAN(xdist[i], ydist[i]); - } - break; + if (nargout > 1) // only compute IDX, if requested + { + Matrix idx (rows, cols); + // Compute optional 'index to closest object pixel' + for(int i = 0; i < numel; i++) + idx (i) = i+1 - xdist[i] - ydist[i]*rows; - case 0: - default: - euclidean(bw, rows, cols, xdist, ydist); - /* Remember sqrt() for the final output */ - for(int i=0; i<rows*cols; i++) { - D(i) = sqrt((double)DIST_EUCLIDEAN(xdist[i], ydist[i])); - } - break; - } - - retval(0) = D; + retval(1) = idx; + } - if(nargout > 1) { - /* Create a second output array */ - Matrix C (rows, cols); - /* Compute optional 'index to closest object pixel' */ - for(int i=0; i<rows*cols; i++) { - C (i) = i+1 - xdist[i] - ydist[i]*rows; - } - retval(1) = C; - } - } - } return retval; } @@ -208,6 +204,7 @@ %! 0.00000 0.00000 0.00000 1.00000 1.41421 1.00000 0.00000 1.00000 %! 1.00000 1.00000 0.00000 1.00000 2.00000 1.00000 0.00000 0.00000]; %! +%!assert (bwdist (bw), out, 0.0001); # default is euclidean %!assert (bwdist (bw, "euclidean"), out, 0.0001); %!assert (bwdist (logical (bw), "euclidean"), out, 0.0001); %! @@ -244,6 +241,8 @@ %! %!assert (bwdist (bw, "quasi-euclidean"), out, 0.0001); %! -%!error bwdist (magic (5)); -%!error bwdist (magic (5), "euclidean"); +%! bw(logical (bw)) = 3; # there is no actual check if matrix is binary or 0 and 1 +%!assert (bwdist (bw, "quasi-euclidean"), out, 0.0001); +%! +%!error bwdist (bw, "not a valid method"); */