Hi (good to see you on the lists again)
Sorry about the late reply; things have been a bit hectic in
real-life...
lør, 23 01 2010 kl. 00:08 -0600, skrev Muthiah Annamalai:
> I've been dormant for years now on Octave lists. I just
> thought there might be general interest in this problem of counting
> features in a binary image. I think you can do this in Matlab (TM)
> using bwconncomp(bw, 4) as documented here:
> http://www.mathworks.com/access/helpdesk/help/toolbox/images/f0-8778.html .
This is definitely of interest (it seems to be one of the most common
things people do with binary images). For quite a while, I've had a C++
implementation of the boundary tracing algorithm for Octave lying
around. Just when I was ready to submit it to the 'image' package, I
realised that Matlab had a function ('bwboundaries') for this, and since
I didn't have the time to make my code compatible and didn't submit it.
Until I read your mail I had completely forgotten about this.
Anyway, I'm attaching some code for boundary tracing. I think we should
make that code compatible with Matlab, and then implement 'bwconncomp'
as a wrapper. Do you agree with this?
Søren
## Copyright (C) 2010 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/>.
function [external, internal] = imboundary (bw, N = 8)
## Check input
if (nargin < 1)
error ("imboundary: not enough input arguments");
endif
if (!ismatrix (bw) || ndims (bw) != 2)
error ("imboundary: first input argument must be a NxM matrix");
endif
if (!isscalar (N) || !any (N == [4, 8]))
error ("imboundary: second input argument must be 4 or 8");
endif
## Make sure 'bw' is logical
bw = logical (bw);
## Found connected components in 'bw', and treat each of them seperatly
[L, num_labels] = bwlabel (bw, N);
external = cell (1, num_labels);
for n = 1:num_labels
segment = (L == n);
[R, C] = find (segment);
if (numel (R) > 1)
external {n} = __imboundary__ (segment, N, R (1), C (1));
else
external {n} = [R, C];
endif
endfor
## If requested, compute internal boundaries as well
if (nargout > 1)
filled = bwfill (bw, "holes", N);
holes = bwmorph (filled & !bw, "dilate");
internal = imboundary (holes, N);
endif
endfunction
%!demo
%! r = 10;
%! [X, Y] = meshgrid (-r:r);
%! bw = (X.^2 + Y.^2 <= r^2);
%! B4 = imboundary (bw, 4);
%! B8 = imboundary (bw, 8);
%! imshow (bw)
%! hold on
%! plot (B4 {1} (:,2), B4 {1} (:,1), "b")
%! plot (B8 {1} (:,2), B8 {1} (:,1), "r")
%! hold off
%!demo
%! r = 10;
%! [X, Y] = meshgrid (-r:r);
%! bw = (X.^2 + Y.^2 <= r^2);
%! bw (r:r+2, r:r+2) = 0;
%! [external, internal] = imboundary (bw, 4);
%! imshow (bw)
%! hold on
%! plot (external {1} (:,2), external {1} (:,1), "b")
%! plot (internal {1} (:,2), internal {1} (:,1), "r")
%! hold off
// Copyright (C) 2010 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 iseven (const int a)
{
if ((a % 2) == 0)
return 1;
else
return 0;
}
Matrix trace_boundary (const boolMatrix &im, const int N, const octave_idx_type r,
const octave_idx_type c)
{
// Get size information
const octave_idx_type rows = im.rows ();
const octave_idx_type cols = im.columns ();
// Create list of points
typedef std::pair<int, int> point;
std::list <point> P;
// Add first point
point P0 (r, c); // first list element
point P1 (-1, -1); // second list element
point Pn1 (-1, -1); // second last list element
P.push_back (P0);
int len = 1;
// Create a simple lookup table that translates 'dir' to a row and column offset
const int dir2row4 [] = { 0, -1, 0, +1};
const int dir2col4 [] = {+1, 0, -1, 0};
const int dir2row8 [] = { 0, -1, -1, -1, 0, +1, +1, +1};
const int dir2col8 [] = {+1, +1, 0, -1, -1, -1, 0, +1};
// Start searching from there...
int dir = N - 1;
int curr_dir = dir; //(N == 4) ? (dir + 3) % N : (dir + 6 + iseven (dir)) % N;
int delta_r, delta_c;
octave_idx_type row = r, col = c;
//while (true)
for (int z = 0; z < 1000; z++)
{
OCTAVE_QUIT;
// Get next search direction
if (N == 4)
{
//curr_dir = (dir + 3) % N;
delta_r = dir2row4 [curr_dir];
delta_c = dir2col4 [curr_dir];
}
else
{
//curr_dir = (dir + 6 + iseven (dir)) % N;
delta_r = dir2row8 [curr_dir];
delta_c = dir2col8 [curr_dir];
}
// Is a pixel available at the search direction
const octave_idx_type curr_r = row + delta_r;
const octave_idx_type curr_c = col + delta_c;
std::cerr << " curr_r = " << curr_r
<< " curr_c = " << curr_c
<< " curr_dir = " << curr_dir
<< " row = " << row
<< " colr = " << col
<< std::endl;
if (curr_r >= 0 && curr_r < rows && curr_c >= 0 && curr_c < cols && im (curr_r, curr_c))
{
// Update 'dir'
dir = curr_dir;
curr_dir = (N == 4) ? (dir + 3) % N : (dir + 6 + iseven (dir)) % N;
// Add point to list
const point Pn (curr_r, curr_c);
P.push_back (Pn);
len ++;
// Update 'row' and 'col'
row = curr_r;
col = curr_c;
// Save the second element of P for the stop criteria
if (len == 2)
{
P1.first = curr_r;
P1.second = curr_c;
}
// Should we stop?
if (Pn == P1 && Pn1 == P0)
break;
// Save current point for next time
Pn1 = Pn;
}
else
{
// Update search direction
curr_dir = (curr_dir+1) % N;
}
} // end while
// Copy data to output matrix
Matrix out (len-2, 2);
std::list<point>::const_iterator iter = P.begin ();
for (int idx = 0; idx < len-2; iter++, idx++)
{
out (idx, 0) = iter->first + 1;
out (idx, 1) = iter->second + 1;
}
return out;
}
DEFUN_DLD(__imboundary__, args, , "\
-*- texinfo -*-\n\
@deftypefn {Function File} __imboundary__ (@var{bw}, @var{N}, @var{r}, @var{c})\n\
Undocumented internal function.\n\
User interface is available in @code{imboundary}.\n\
@end deftypefn\n\
")
{
// Handle input
octave_value_list retval;
if (args.length () != 4) {
error ("__imboundary__: not enough input arguments");
return retval;
}
const boolMatrix im = args (0).bool_matrix_value ();
const int N = (int) args (1).scalar_value ();
const octave_idx_type r = (octave_idx_type) args (2).scalar_value () - 1;
const octave_idx_type c = (octave_idx_type) args (3).scalar_value () - 1;
if (error_state || (N != 4 && N != 8))
error ("__imboundary__: invalid input arguments");
else
{
Matrix out = trace_boundary (im, N, r, c);
retval.append (out);
}
return retval;
}
------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
_______________________________________________
Octave-dev mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/octave-dev