# HG changeset patch # User hauberg # Date 1265590518 0 # Node ID 870ecf0abb5401c0c32e0d9f47805404db83d08b # Parent 263112023cd689eee3838e19745fb03c9b1872b9 New function: bwboundaries diff --git a/INDEX b/INDEX --- a/INDEX +++ b/INDEX @@ -59,6 +59,7 @@ nonmax_supress Black and white image functions bwarea + bwboundaries bwcount bwdist bweuler diff --git a/inst/bwboundaries.m b/inst/bwboundaries.m new file mode 100644 --- /dev/null +++ b/inst/bwboundaries.m @@ -0,0 +1,68 @@ +## 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 . + +function [B, L, num_labels] = bwboundaries (bw, N = 4, options = "holes") + ## Check input + if (nargin < 1) + error ("bwboundaries: not enough input arguments"); + endif + if (!ismatrix (bw) || ndims (bw) != 2) + error ("bwboundaries: first input argument must be a NxM matrix"); + endif + if (!isscalar (N) || !any (N == [4])) #, 8])) + error ("bwboundaries: second input argument must be 4"); + endif + if (!ischar (options) || !any (strcmpi (options, {"holes", "noholes"}))) + error ("bwboundaries: third input must be either \"holes\" or \"noholes\""); + endif + + + ## Warn if the user request more output arguments than our implementation supports + if (nargout > 3) +% warning ("%s %s %s", ... +% "bwboundaries: adjacency matrix output is currently not supported." ... +% "Please contact the Octave-Forge community if you want to contribute" ... +%s "an implementation of this"); + 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); + B = cell (num_labels, 1); + for n = 1:num_labels + segment = (L == n); + [R, C] = find (segment); + if (numel (R) > 1) + ## XXX: support 8-neighbors + #B {n} = __imboundary__ (segment, N, R (1), C (1)); + B {n} = __imboundary__ (segment, 4, R (1), C (1)); + else + B {n} = [R, C]; + endif + endfor + + ## If requested, compute internal boundaries as well + if (strcmpi (options, "holes")) + filled = bwfill (bw, "holes", N); + holes = (filled & !bw); + [internal, in_label, lin] = bwboundaries (holes, N, "noholes"); + B (end+1:end+lin, 1) = internal; + + in_label (in_label != 0) += num_labels; + L += in_label; + endif +endfunction diff --git a/src/Makefile b/src/Makefile --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,7 @@ endif all: __spatial_filtering__.oct __bilateral__.oct __custom_gaussian_smoothing__.oct \ - bwlabel.oct bwfill.oct rotate_scale.oct hough_line.oct \ + __imboundary__.oct bwlabel.oct bwfill.oct rotate_scale.oct hough_line.oct \ graycomatrix.oct deriche.oct __bwdist.oct nonmax_supress.oct \ $(JPEG) $(PNG) diff --git a/src/__imboundary__.cc b/src/__imboundary__.cc new file mode 100644 --- /dev/null +++ b/src/__imboundary__.cc @@ -0,0 +1,162 @@ +// 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 . + +#include + +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 point; + std::list P; + + // Add first point + const 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.first == P1.first && Pn.second == P1.second && + Pn1.first == P0.first && Pn1.second == P0.second) + 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-1, 2); + std::list::const_iterator iter = P.begin (); + for (int idx = 0; idx < len-2; iter++, idx++) + { + out (idx, 0) = iter->second + 1; + out (idx, 1) = iter->first + 1; + } + out (len-2, 0) = P0.second + 1; + out (len-2, 1) = P0.first + 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{bwboundaries}.\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; +}