# HG changeset patch
# User carandraug
# Date 1366168147 0
# Node ID 5d87258abc1a6c30a6b388121c7d0b763560a421
# Parent 2d1db11b14705567a686135db3aa3f0597fbf9b6
bwdist: matlab compatibility fixes
* distance matrix must be class single
* index class is uint32 or uint64 dependent on matrix size
* object pixels are all non-zero (not larger than zero)
* added tests
* OCTAVE_QUIT on the main loops so we can abort
* sqrt when using euclidean distance is done after getting the distances
* dropped some templates from last time (not really a performance increase)
diff -r 2d1db11b1470 -r 5d87258abc1a main/image/NEWS
--- a/main/image/NEWS Tue Apr 16 15:32:11 2013 +0000
+++ b/main/image/NEWS Wed Apr 17 03:09:07 2013 +0000
@@ -71,6 +71,10 @@
** Bug fixes on the concavity, intermodes, maxlikelihood, and minimum methods
of graythresh.
+ ** The `bwdist' function will now consider any non zero value as object pixels,
+ the class of the distance matrix has changed to single, and indexes an
+ uint dependent on the matrix size.
+
Summary of important user-visible changes for image 2.0.0:
-------------------------------------------------------------------
diff -r 2d1db11b1470 -r 5d87258abc1a main/image/src/bwdist.cc
--- a/main/image/src/bwdist.cc Tue Apr 16 15:32:11 2013 +0000
+++ b/main/image/src/bwdist.cc Wed Apr 17 03:09:07 2013 +0000
@@ -14,11 +14,6 @@
// You should have received a copy of the GNU General Public License along with
// this program; if not, see .
-// TODO output Matrix must be single class
-// class of IDX is uint32 for numel () <= 2^32 - 1
-// uint64 for numel () >= 2^32
-// check for any improvement in using boolean matrix
-
#include
/*
@@ -41,17 +36,12 @@
Edited in 2009 to form the foundation for Octave BWDIST:
added #define-configurable distance measure and function name
- Edited in 2013 for C++ and removed the #define stuff
+ Edited in 2013 for C++, removed the #define stuff, and other
+ fixes for matlab compatibility.
*/
-/*
- Using template for optimization.
- Because func is called a lot of times in edtfunc, we instantiate edtfunc
- multiple times for each of the different distance measuring functions.
-*/
-
-template
-void edtfunc (const Matrix &img,
+void edtfunc (float (*func)(short int, short int),
+ const Matrix &img,
short *distx,
short *disty)
{
@@ -60,7 +50,7 @@
const int numel = img.numel ();
int x, y, i;
- double olddist2, newdist2, newdistx, newdisty;
+ float olddist2, newdist2, newdistx, newdisty;
bool changed;
// Initialize index offsets for the current image width
@@ -76,7 +66,7 @@
// Initialize the distance images to be all large values
for (i = 0; i < numel; i++)
{
- if(img(i) <= 0.0)
+ if(img(i) == 0.0)
{
distx[i] = 32000; // Large but still representable in a short, and
disty[i] = 32000; // 32000^2 + 32000^2 does not overflow an int
@@ -96,19 +86,18 @@
// Scan rows, except first row
for (y = 1; y < h; y++)
{
-
// move index to leftmost pixel of current row
i = y*w;
/* scan right, propagate distances from above & left */
/* Leftmost pixel is special, has no left neighbors */
- olddist2 = func::apply(distx[i], disty[i]);
+ olddist2 = (*func)(distx[i], disty[i]);
if(olddist2 > 0) // If not already zero distance
{
newdistx = distx[i+offset_u];
newdisty = disty[i+offset_u]+1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -119,7 +108,7 @@
newdistx = distx[i+offset_ur]-1;
newdisty = disty[i+offset_ur]+1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -132,12 +121,13 @@
/* Middle pixels have all neighbors */
for(x=1; x 0) // If not already zero distance
{
newdistx = distx[i+offset_l]+1;
newdisty = disty[i+offset_l];
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -196,7 +186,7 @@
newdistx = distx[i+offset_lu]+1;
newdisty = disty[i+offset_lu]+1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -207,7 +197,7 @@
newdistx = distx[i+offset_u];
newdisty = disty[i+offset_u]+1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -224,12 +214,13 @@
/* scan left, propagate distance from right */
for(x=w-2; x>=0; x--, i--)
{
- olddist2 = func::apply(distx[i], disty[i]);
+ OCTAVE_QUIT;
+ olddist2 = (*func)(distx[i], disty[i]);
if(olddist2 == 0) continue; // Already zero distance
newdistx = distx[i+offset_r]-1;
newdisty = disty[i+offset_r];
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -242,18 +233,19 @@
/* Scan rows in reverse order, except last row */
for(y=h-2; y>=0; y--)
{
+ OCTAVE_QUIT;
/* move index to rightmost pixel of current row */
i = y*w + w-1;
/* Scan left, propagate distances from below & right */
/* Rightmost pixel is special, has no right neighbors */
- olddist2 = func::apply(distx[i], disty[i]);
+ olddist2 = (*func)(distx[i], disty[i]);
if(olddist2 > 0) // If not already zero distance
{
newdistx = distx[i+offset_d];
newdisty = disty[i+offset_d]-1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -264,7 +256,7 @@
newdistx = distx[i+offset_dl]+1;
newdisty = disty[i+offset_dl]-1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -277,12 +269,13 @@
/* Middle pixels have all neighbors */
for(x=w-2; x>0; x--, i--)
{
- olddist2 = func::apply(distx[i], disty[i]);
+ OCTAVE_QUIT;
+ olddist2 = (*func)(distx[i], disty[i]);
if(olddist2 == 0) continue; // Already zero distance
newdistx = distx[i+offset_r]-1;
newdisty = disty[i+offset_r];
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -293,7 +286,7 @@
newdistx = distx[i+offset_rd]-1;
newdisty = disty[i+offset_rd]-1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -304,7 +297,7 @@
newdistx = distx[i+offset_d];
newdisty = disty[i+offset_d]-1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -315,7 +308,7 @@
newdistx = distx[i+offset_dl]+1;
newdisty = disty[i+offset_dl]-1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -324,12 +317,12 @@
}
}
/* Leftmost pixel is special, has no left neighbors */
- olddist2 = func::apply(distx[i], disty[i]);
+ olddist2 = (*func)(distx[i], disty[i]);
if(olddist2 > 0) // If not already zero distance
{
newdistx = distx[i+offset_r]-1;
newdisty = disty[i+offset_r];
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -340,7 +333,7 @@
newdistx = distx[i+offset_rd]-1;
newdisty = disty[i+offset_rd]-1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -351,7 +344,7 @@
newdistx = distx[i+offset_d];
newdisty = disty[i+offset_d]-1;
- newdist2 = func::apply(newdistx, newdisty);
+ newdist2 = (*func)(newdistx, newdisty);
if(newdist2 < olddist2)
{
distx[i]=newdistx;
@@ -366,13 +359,14 @@
i = y*w + 1;
for(x=1; xabs(y) ? (abs(x) + sqrt2_1 * abs(y)) :
- (sqrt2_1 * abs(x) + abs(y)) ;
- }
-};
+ static const float sqrt2_1 = sqrt (2) - 1;
+ return abs(x)>abs(y) ? (abs(x) + sqrt2_1 * abs(y)) :
+ (sqrt2_1 * abs(x) + abs(y)) ;
+}
-template
-void loop_distance (const Matrix &bw,
- Matrix &dist,
- short *xdist,
- short *ydist)
+FloatMatrix calc_distances (float (*func)(short, short),
+ Matrix bw,
+ short *xdist,
+ short *ydist)
{
- edtfunc (bw, xdist, ydist);
+ FloatMatrix dist (bw.dims ());
+ edtfunc (func, bw, xdist, ydist);
+ const int numel = dist.numel ();
+ for (int i = 0; i < numel; i++)
+ dist(i) = (*func)(xdist[i], ydist[i]);
+ return dist;
+}
+
+template
+T calc_index (Matrix bw, short *xdist, short *ydist)
+{
+ T idx (bw.dims ());
const int numel = bw.numel ();
- for (int i = 0; i < numel; i++)
- dist(i) = func::apply (xdist[i], ydist[i]);
+ const int rows = bw.rows ();
+ for(int i = 0; i < numel; i++)
+ idx (i) = i+1 - xdist[i] - ydist[i]*rows;
+ return idx;
}
DEFUN_DLD (bwdist, args, nargout,
@@ -433,12 +435,14 @@
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\
+check is performed, all non-zero values are considered object pixels.\n\
The return value @var{dist}, is the distance of each background pixel to the\n\
-closest object pixel.\n\
+closest object pixel in a matrix of class @code{single}.\n\
\n\
@var{idx} is the linear index for the closest object, used to calculate the\n\
-distance for each of the pixels.\n\
+distance for each of the pixels. Its class is dependent on the number of\n\
+elements in @var{bw}, @code{uint64} if less than 2^32 elements, @code{uint32}\n\
+otherwise.\n\
\n\
The distance can be measured through different @var{method}s:\n\
\n\
@@ -468,9 +472,6 @@
// 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)
{
@@ -505,15 +506,22 @@
OCTAVE_LOCAL_BUFFER (short, xdist, numel);
OCTAVE_LOCAL_BUFFER (short, ydist, numel);
- Matrix dist (bw.dims ()); // the output distance matrix
+ FloatMatrix dist;
if (method == "euclidean")
- loop_distance (bw, dist, xdist, ydist);
+ {
+ dist = calc_distances (euclidean, bw, xdist, ydist);
+ const Array positions = (!bw).find ();
+ const int zpos = positions.numel();
+ for (int i = 0; i < zpos; i++) {
+ dist (positions(i)) = sqrt(dist(positions(i)));
+ }
+ }
else if (method == "chessboard")
- loop_distance (bw, dist, xdist, ydist);
+ dist = calc_distances (chessboard, bw, xdist, ydist);
else if (method == "cityblock")
- loop_distance (bw, dist, xdist, ydist);
+ dist = calc_distances (cityblock, bw, xdist, ydist);
else if (method == "quasi-euclidean")
- loop_distance (bw, dist, xdist, ydist);
+ dist = calc_distances (quasi_euclidean, bw, xdist, ydist);
else
error ("bwdist: unknown METHOD '%s'", method.c_str ());
@@ -522,12 +530,10 @@
// Compute optional 'index to closest object pixel', only if requested
if (nargout > 1)
{
- Matrix idx (bw.dims ());
- const int rows = bw.rows ();
- for(int i = 0; i < numel; i++)
- idx (i) = i+1 - xdist[i] - ydist[i]*rows;
-
- retval(1) = idx;
+ if (numel >= pow (2, 32))
+ retval(1) = calc_index (bw, xdist, ydist);
+ else
+ retval(1) = calc_index (bw, xdist, ydist);
}
return retval;
@@ -553,6 +559,7 @@
%! 0.00000 0.00000 0.00000 0.00000 1.00000 1.00000 1.00000 0.00000
%! 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];
+%! out = single (out);
%!
%!assert (bwdist (bw), out, 0.0001); # default is euclidean
%!assert (bwdist (bw, "euclidean"), out, 0.0001);
@@ -566,6 +573,7 @@
%! 0 0 0 0 1 1 1 0
%! 0 0 0 1 1 1 0 1
%! 1 1 0 1 2 1 0 0];
+%! out = single (out);
%!
%!assert (bwdist (bw, "chessboard"), out);
%!
@@ -577,6 +585,7 @@
%! 0 0 0 0 1 1 1 0
%! 0 0 0 1 2 1 0 1
%! 1 1 0 1 2 1 0 0];
+%! out = single (out);
%!
%!assert (bwdist (bw, "cityblock"), out);
%!
@@ -588,11 +597,14 @@
%! 0.00000 0.00000 0.00000 0.00000 1.00000 1.00000 1.00000 0.00000
%! 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];
+%! out = single (out);
%!
%!assert (bwdist (bw, "quasi-euclidean"), out, 0.0001);
%!
%! 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);
+%! bw(logical (bw)) = -2; # anything non-zero is considered object
+%!assert (bwdist (bw, "quasi-euclidean"), out, 0.0001);
%!
%!error bwdist (bw, "not a valid method");
*/