Mercurial > hg > octave-image
changeset 799:08e2890c96cd
Expand functions that make use of imdilate and imerode to use strel.
* private/prepare_strel.m: small private function common to all other
which checks validity of a structuring element and creates a strel
object if input was a binary matrix.
* imbothat.m, imclose.m, imopen.m, imtophat.m, mmgradm.m: make use of
new prepare_strel, uniform the documentation, add many tests for
N dimensions, and weird SEs, follow Octave coding guidelines,
* mmgradm.m: change default SEs for elementary cross, fix gradient
when input is logical matrix (use boolean logic instead of
subtraction), expand documentation with examples, and how to perform
half gradients.
* imerode.cc: small change on documentation for uniformity.
author | Carnë Draug <carandraug@octave.org> |
---|---|
date | Wed, 09 Oct 2013 07:57:08 +0100 (2013-10-09) |
parents | 088376c53e6c |
children | 1e708656e295 |
files | inst/imbothat.m inst/imclose.m inst/imopen.m inst/imtophat.m inst/mmgradm.m inst/private/prepare_strel.m src/imerode.cc |
diffstat | 7 files changed, 540 insertions(+), 108 deletions(-) [+] |
line wrap: on
line diff
--- a/inst/imbothat.m +++ b/inst/imbothat.m @@ -1,5 +1,5 @@ -## Copyright (C) 2005 Carvalho-Mariel -## Copyright (C) 2010-2011 Carnë Draug <carandraug+dev@gmail.com> +## Copyright (C) 2005 Carvalho-Mariel +## Copyright (C) 2010-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 @@ -15,12 +15,24 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{B} = imbothat (@var{A}, @var{se}) +## @deftypefn {Function File} {} imbothat (@var{img}, @var{SE}) ## Perform morphological bottom hat filtering. ## -## The image @var{A} must be a grayscale or binary image, and @var{se} must be a -## structuring element. Both must have the same class, e.g., if @var{A} is a -## logical matrix, @var{se} must also be logical. +## The matrix @var{img} must be numeric while @var{SE} can be a: +## @itemize @bullet +## @item +## strel object; +## @item +## array of strel objects as returned by `@strel/getsequence'; +## @item +## matrix of 0's and 1's. +## @end itemize +## +## A bottom hat transform corresponds to the difference between the closing +## of @var{img} and @var{img} itself, i.e., it is equivalent to: +## @example +## imclose (img, se) - img; +## @end example ## ## A bottom-hat transform is also known as 'black', or 'closing', top-hat ## transform. @@ -28,28 +40,137 @@ ## @seealso{imerode, imdilate, imopen, imclose, imtophat, mmgradm} ## @end deftypefn -function retval = imbothat (im, se) +function black = imbothat (img, se) - ## Checkinput if (nargin != 2) - print_usage(); + print_usage (); + elseif (! isimage (img)) + error("imbothat: IMG must be a numeric matrix"); endif - if (!ismatrix(im) || !isreal(im)) - error("imbothat: first input argument must be a real matrix"); - elseif (!ismatrix(se) || !isreal(se)) - error("imbothat: second input argument must be a real matrix"); - elseif ( !strcmp(class(im), class(se)) ) - error("imbothat: image and structuring element must have the same class"); - endif + se = prepare_strel ("imbothat", se); ## Perform filtering ## Note that in case that the transform is to applied to a logical image, ## subtraction must be handled in a different way (x & !y) instead of (x - y) ## or it will return a double precision matrix - if (islogical(im)) - retval = imclose(im, se) & !im; + if (islogical (img)) + black = imclose (img, se) & ! img; else - retval = imclose(im, se) - im; + black = imclose (img, se) - img; endif endfunction + +%!assert (imbothat (ones (3), [1 1; 0 1]), zeros (3)); +%!assert (imbothat (true (3), [1 1; 0 1]), false (3)); + +%!shared in, out, se +%! in = [ 0 0 0 1 1 1 0 0 1 1 +%! 0 1 0 1 1 1 0 0 0 1 +%! 1 1 1 1 1 0 0 0 0 0 +%! 0 1 1 1 1 0 0 0 0 0 +%! 0 0 0 1 0 0 0 0 1 0 +%! 0 0 0 0 0 0 0 1 1 1 +%! 0 0 0 0 1 0 1 0 1 0 +%! 0 0 0 1 1 1 1 1 0 0 +%! 0 0 0 0 1 1 1 0 0 0 +%! 0 0 0 1 1 1 0 0 0 0]; +%! +%! out = [ 1 1 1 0 0 0 1 1 0 0 +%! 1 0 1 0 0 0 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 1 +%! 1 0 0 0 0 0 0 0 0 1 +%! 0 0 0 0 1 0 0 0 0 1 +%! 0 0 0 1 1 1 1 0 0 0 +%! 0 0 0 1 0 1 0 1 0 1 +%! 0 0 0 0 0 0 0 0 0 0 +%! 0 0 0 1 0 0 0 0 0 0 +%! 0 0 0 0 0 0 1 0 0 0]; +%!assert (imbothat (logical (in), ones (3)), logical (out)); +%! +%! out = [ 7 0 15 8 1 6 0 13 6 24 +%! 0 8 9 2 0 0 16 7 0 23 +%! 89 7 0 41 39 7 12 7 0 23 +%! 8 1 69 40 58 1 6 2 0 43 +%! 7 0 63 59 52 0 0 0 14 32 +%! 62 55 6 7 0 7 0 23 16 1 +%! 56 74 0 2 0 0 16 14 7 0 +%! 0 73 69 0 0 19 15 8 1 0 +%! 8 6 0 0 6 13 9 2 0 6 +%! 7 0 0 19 0 14 7 0 23 0]; +%!assert (imbothat (magic (10), ones (3)), out); +%!assert (imbothat (uint8 (magic (10)), strel ("square", 3)), uint8 (out)); +%! +%! ## using a se that will be decomposed in 2 pieces +%! out =[ 7 0 87 66 59 7 0 19 12 30 +%! 0 13 81 60 58 1 19 13 6 29 +%! 89 12 0 54 52 20 18 7 0 23 +%! 8 6 69 53 71 14 12 2 0 43 +%! 7 0 63 73 66 14 7 0 23 41 +%! 76 69 14 7 0 30 23 46 39 7 +%! 70 88 9 2 0 24 42 40 33 6 +%! 14 87 80 0 0 43 41 34 27 0 +%! 84 82 0 0 19 37 35 28 26 19 +%! 89 82 0 20 13 36 29 22 45 13]; +%!assert (imbothat (magic (10), ones(5)), out); +%! +%! ## using a weird non-symmetric and even-size se +%! out =[ 0 0 15 8 1 3 0 7 0 18 +%! 0 8 53 59 0 0 14 13 0 17 +%! 84 0 0 40 38 6 13 6 0 23 +%! 2 0 42 47 58 0 6 0 0 41 +%! 0 0 62 59 52 0 0 0 16 35 +%! 6 58 13 6 0 3 19 19 35 1 +%! 0 18 0 0 0 0 15 13 6 0 +%! 0 17 69 0 0 17 17 8 0 0 +%! 8 67 0 0 0 15 9 2 0 6 +%! 7 0 0 17 10 42 7 0 19 0]; +%!assert (imbothat (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out); +%! +%! ## N dimensional and weird se +%! in = reshape (magic(16), [4 8 4 2]); +%! se = ones (3, 3, 3); +%! se(:,:,1) = [1 0 1; 0 1 1; 0 0 0]; +%! se(:,:,3) = [1 0 1; 0 1 1; 0 0 1]; +%! out = zeros (size (in)); +%! out(:,:,1,1) = [ +%! 0 17 81 145 237 146 64 0 +%! 205 128 64 0 0 37 83 147 +%! 175 111 47 0 0 64 117 181 +%! 0 64 128 209 173 109 45 0]; +%! out(:,:,2,1) = [ +%! 235 142 78 18 0 23 69 133 +%! 0 35 103 163 215 128 46 0 +%! 0 64 128 195 183 123 48 0 +%! 153 93 43 0 14 78 146 215]; +%! out(:,:,3,1) = [ +%! 0 25 89 153 229 142 64 0 +%! 201 128 64 0 0 41 91 155 +%! 167 103 57 0 0 64 125 189 +%! 0 64 146 217 165 101 37 0]; +%! out(:,:,4,1) = [ +%! 227 142 78 14 0 31 77 141 +%! 0 43 107 171 211 128 46 0 +%! 0 64 128 203 179 115 48 0 +%! 149 99 35 0 18 82 146 223]; +%! out(:,:,1,2) = [ +%! 0 33 97 161 221 146 64 0 +%! 189 125 61 0 0 53 99 163 +%! 159 95 31 0 0 64 128 197 +%! 0 64 128 225 157 93 29 0]; +%! out(:,:,2,2) = [ +%! 219 142 78 18 0 39 85 149 +%! 0 51 119 179 199 128 46 0 +%! 0 64 128 211 167 107 43 0 +%! 137 77 27 0 14 78 146 231]; +%! out(:,:,3,2) = [ +%! 0 41 105 169 213 142 64 0 +%! 185 121 64 0 0 57 107 171 +%! 151 87 41 0 0 64 128 205 +%! 0 64 146 233 149 85 21 0]; +%! out(:,:,4,2) = [ +%! 211 142 78 14 0 47 93 157 +%! 0 59 123 187 195 128 46 0 +%! 0 64 128 219 163 99 35 0 +%! 133 83 19 0 18 82 146 239]; +%!assert (imbothat (in, se), out);
--- a/inst/imclose.m +++ b/inst/imclose.m @@ -14,31 +14,101 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{B} = imclose (@var{A}, @var{se}) -## Perform morphological closing on a given image. -## The image @var{A} must be a grayscale or binary image, and @var{se} must be a -## structuring element. +## @deftypefn {Function File} {} imclose (@var{img}, @var{SE}) +## Perform morphological closing. ## -## The closing corresponds to a dilation followed by an erosion of the image, i.e. +## The matrix @var{img} must be numeric while @var{SE} can be a: +## @itemize @bullet +## @item +## strel object; +## @item +## array of strel objects as returned by `@strel/getsequence'; +## @item +## matrix of 0's and 1's. +## @end itemize +## +## The closing corresponds to a dilation followed by an erosion of @var{img}, +## using the same @var{SE}, i.e., it is equivalent to: ## @example -## B = imerode(imdilate(A, se), se); +## imerode (imdilate (img, se), se); ## @end example -## @seealso{imdilate, imerode, imclose} +## +## @seealso{imdilate, imerode, imopen} ## @end deftypefn -function retval = imclose (im, se) - ## Checkinput +function closed = imclose (img, se) + if (nargin != 2) - print_usage(); + print_usage (); + elseif (! isimage (img)) + error("imclose: IMG must be a numeric matrix"); endif - if (!ismatrix(im) || !isreal(im)) - error("imclose: first input argument must be a real matrix"); - endif - if (!ismatrix(se) || !isreal(se)) - error("imclose: second input argument must be a real matrix"); - endif - + se = prepare_strel ("imclose", se); + ## Perform filtering - retval = imerode(imdilate(im, se), se); + closed = imerode (imdilate (img, se), se); endfunction + +%!shared in, out +%! in = [ 0 0 0 1 1 1 0 0 1 1 +%! 0 1 0 1 1 1 0 0 0 1 +%! 1 1 1 1 1 0 0 0 0 0 +%! 0 1 1 1 1 0 0 0 0 0 +%! 0 0 0 1 0 0 0 0 1 0 +%! 0 0 0 0 0 0 0 1 1 1 +%! 0 0 0 0 1 0 1 0 1 0 +%! 0 0 0 1 1 1 1 1 0 0 +%! 0 0 0 0 1 1 1 0 0 0 +%! 0 0 0 1 1 1 0 0 0 0]; +%! +%! out = [ 1 1 1 1 1 1 1 1 1 1 +%! 1 1 1 1 1 1 0 0 0 1 +%! 1 1 1 1 1 0 0 0 0 1 +%! 1 1 1 1 1 0 0 0 0 1 +%! 0 0 0 1 1 0 0 0 1 1 +%! 0 0 0 1 1 1 1 1 1 1 +%! 0 0 0 1 1 1 1 1 1 1 +%! 0 0 0 1 1 1 1 1 0 0 +%! 0 0 0 1 1 1 1 0 0 0 +%! 0 0 0 1 1 1 1 0 0 0]; +%!assert (imclose (logical (in), ones (3)), logical (out)); +%! +%! out = [99 99 16 16 16 73 74 64 64 64 +%! 98 88 16 16 16 73 71 64 64 64 +%! 93 88 88 61 61 61 68 70 70 70 +%! 93 88 88 61 61 61 68 71 71 71 +%! 93 93 88 61 61 61 68 75 66 66 +%! 79 79 82 90 90 49 49 49 49 66 +%! 79 79 82 91 91 48 46 46 46 66 +%! 79 79 82 95 97 48 46 46 46 72 +%! 18 18 94 96 84 48 46 46 46 59 +%! 18 18 100 96 84 50 50 50 50 59]; +%!assert (imclose (magic (10), ones (3)), out); +%!assert (imclose (uint8 (magic (10)), strel ("square", 3)), uint8 (out)); +%! +%! ## using a se that will be decomposed in 2 pieces +%! out =[ 99 99 88 74 74 74 74 70 70 70 +%! 98 93 88 74 74 74 74 70 70 70 +%! 93 93 88 74 74 74 74 70 70 70 +%! 93 93 88 74 74 74 74 71 71 71 +%! 93 93 88 75 75 75 75 75 75 75 +%! 93 93 90 90 90 72 72 72 72 72 +%! 93 93 91 91 91 72 72 72 72 72 +%! 93 93 93 95 97 72 72 72 72 72 +%! 94 94 94 96 97 72 72 72 72 72 +%! 100 100 100 97 97 72 72 72 72 72]; +%!assert (imclose (magic (10), ones(5)), out); +%! +%! ## using a weird non-symmetric and even-size se +%! out =[ 92 99 16 16 16 70 74 58 58 58 +%! 98 88 60 73 16 73 69 70 64 58 +%! 88 81 88 60 60 60 69 69 70 70 +%! 87 87 61 68 61 60 68 69 71 69 +%! 86 93 87 61 61 61 68 75 68 69 +%! 23 82 89 89 90 45 68 45 68 66 +%! 23 23 82 89 91 48 45 45 45 66 +%! 79 23 82 95 97 46 48 46 45 72 +%! 18 79 94 96 78 50 46 46 46 59 +%! 18 18 100 94 94 78 50 50 46 59]; +%!assert (imclose (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out);
--- a/inst/imopen.m +++ b/inst/imopen.m @@ -14,31 +14,101 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{B} = imopen (@var{A}, @var{se}) -## Perform morphological opening on a given image. -## The image @var{A} must be a grayscale or binary image, and @var{se} must be a -## structuring element. +## @deftypefn {Function File} {} imopen (@var{img}, @var{SE}) +## Perform morphological opening. ## -## The opening corresponds to an erosion followed by a dilation of the image, i.e. +## The matrix @var{img} must be numeric while @var{SE} can be a: +## @itemize @bullet +## @item +## strel object; +## @item +## array of strel objects as returned by `@strel/getsequence'; +## @item +## matrix of 0's and 1's. +## @end itemize +## +## The opening corresponds to an erosion followed by a dilation of @var{img}, +## using the same @var{SE}, i.e., it is equivalent to: ## @example -## B = imdilate(imerode(A, se), se); +## imdilate (imerode (img, se), se); ## @end example +## ## @seealso{imdilate, imerode, imclose} ## @end deftypefn -function retval = imopen(im, se) - ## Checkinput +function opened = imopen (img, se) + if (nargin != 2) - print_usage(); + print_usage (); + elseif (! isimage (img)) + error("imopen: IMG must be a numeric matrix"); endif - if (!ismatrix(im) || !isreal(im)) - error("imopen: first input argument must be a real matrix"); - endif - if (!ismatrix(se) || !isreal(se)) - error("imopen: second input argument must be a real matrix"); - endif - + se = prepare_strel ("imopen", se); + ## Perform filtering - retval = imdilate(imerode(im, se), se); + opened = imdilate (imerode (img, se), se); endfunction + +%!shared in, out +%! in = [ 0 0 0 1 1 1 0 0 1 1 +%! 0 1 0 1 1 1 0 0 0 1 +%! 1 1 1 1 1 0 0 0 0 0 +%! 0 1 1 1 1 0 0 0 0 0 +%! 0 0 0 1 0 0 0 0 1 0 +%! 0 0 0 0 0 0 0 1 1 1 +%! 0 0 0 0 1 0 1 0 1 0 +%! 0 0 0 1 1 1 1 1 0 0 +%! 0 0 0 0 1 1 1 0 0 0 +%! 0 0 0 1 1 1 0 0 0 0]; +%! +%! out = [ 0 0 0 1 1 1 0 0 0 0 +%! 0 0 0 1 1 1 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 0 +%! 0 0 0 0 0 0 0 0 0 0]; +%!assert (imopen (logical (in), ones (3)), logical (out)); +%! +%! out = [80 80 1 8 15 51 51 51 51 40 +%! 80 80 7 8 15 54 55 55 55 40 +%! 4 7 7 8 15 54 55 55 55 40 +%! 17 17 17 7 3 54 55 55 55 28 +%! 17 17 17 2 9 54 54 54 52 33 +%! 17 17 17 29 29 29 29 26 33 33 +%! 5 5 13 29 29 29 30 32 39 39 +%! 6 6 13 29 29 29 30 32 39 39 +%! 10 12 77 77 77 35 35 35 39 39 +%! 10 12 77 77 77 35 35 35 27 27]; +%!assert (imopen (magic (10), ones (3)), out); +%!assert (imopen (uint8 (magic (10)), strel ("square", 3)), uint8 (out)); +%! +%! ## using a se that will be decomposed in 2 pieces +%! out =[ 1 1 1 8 15 40 40 40 40 40 +%! 4 4 4 8 15 40 40 40 40 40 +%! 4 4 4 8 15 40 40 40 40 40 +%! 5 5 5 3 3 28 28 28 28 28 +%! 5 5 5 2 9 28 28 28 28 28 +%! 5 5 13 26 26 26 26 26 26 26 +%! 5 5 13 29 29 29 29 29 27 27 +%! 6 6 13 29 29 29 29 29 27 27 +%! 6 6 13 29 29 29 29 29 27 27 +%! 6 6 13 29 29 29 29 29 27 27]; +%!assert (imopen (magic (10), ones(5)), out); +%! +%! ## using a weird non-symmetric and even-size se +%! out =[ 7 7 1 8 15 55 51 51 41 40 +%! 7 7 7 8 16 55 55 55 51 41 +%! 4 9 7 7 16 54 55 54 55 47 +%! 25 25 9 9 3 52 54 52 54 28 +%! 25 24 25 2 9 33 52 34 52 34 +%! 17 24 29 31 29 30 33 26 33 34 +%! 17 5 29 31 31 31 30 32 39 33 +%! 10 6 13 35 35 29 31 32 45 39 +%! 10 12 77 36 36 35 35 31 45 45 +%! 11 12 77 77 77 36 36 35 27 45]; +%!assert (imopen (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out);
--- a/inst/imtophat.m +++ b/inst/imtophat.m @@ -15,52 +15,162 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{B} = imtophat (@var{A}, @var{se}) +## @deftypefn {Function File} {} imtophat (@var{img}, @var{SE}) ## Perform morphological top hat filtering. ## -## The image @var{A} must be a grayscale or binary image, and @var{se} must be a -## structuring element. Both must have the same class, e.g., if @var{A} is a -## logical matrix, @var{se} must also be logical. +## The matrix @var{img} must be numeric while @var{SE} can be a: +## @itemize @bullet +## @item +## strel object; +## @item +## array of strel objects as returned by `@strel/getsequence'; +## @item +## matrix of 0's and 1's. +## @end itemize ## -## A 'black', or 'closing', top-hat transform is also known as bottom-hat -## transform and so that is the same @code{imbothat}. +## A top hat transform corresponds to the difference between @var{img}, +## and the opening of @var{img}, i.e., it is equivalent to: +## @example +## img - imopen (img, se); +## @end example +## +## Use @code{imbothat} to perform a 'black' or 'closing', top-hat transform +## (is is also known as bottom-hat transform). ## ## @seealso{imerode, imdilate, imopen, imclose, imbothat, mmgradm} ## @end deftypefn -function retval = imtophat (im, se) +function white = imtophat (img, se) - ## Checkinput if (nargin != 2) print_usage (); + elseif (! isimage (img)) + error("imtophat: IMG must be a numeric matrix"); endif - if (!ismatrix(im) || !isreal(im)) - error("imtophat: first input argument must be a real matrix"); - elseif (!ismatrix(se) || !isreal(se)) - error("imtophat: second input argument must be a real matrix"); - elseif ( !strcmp(class(im), class(se)) ) - error("imtophat: image and structuring element must have the same class"); - endif + se = prepare_strel ("imtophat", se); ## Perform filtering ## Note that in case that the transform is to applied to a logical image, ## subtraction must be handled in a different way (x & !y) instead of (x - y) ## or it will return a double precision matrix - if (islogical(im)) - retval = im & !imopen(im,se); + if (islogical (img)) + white = img & ! imopen (img, se); else - retval = im - imopen(im, se); + white = img - imopen (img, se); endif endfunction -%!test -%! I = [1 1 1; 1 1 1; 1 1 1;]; -%! se = [1 1; 0 1;]; -%! ## class of input should be the same as the output -%! result = imtophat(logical(I), logical(se)); -%! expected = 0.5 < [0 0 1; 0 0 1; 1 1 1]; -%! assert(expected, result); -%! result = imtophat((I), (se)); -%! expected = [0 0 1; 0 0 1; 1 1 1]; -%! assert(expected, result); +%!assert (imtophat (ones (3), [1 1; 0 1]), zeros (3)); +%!assert (imtophat (true (3), [1 1; 0 1]), false (3)); + +%!shared in, out, se +%! in = [ 0 0 0 1 1 1 0 0 1 1 +%! 0 1 0 1 1 1 0 0 0 1 +%! 1 1 1 1 1 0 0 0 0 0 +%! 0 1 1 1 1 0 0 0 0 0 +%! 0 0 0 1 0 0 0 0 1 0 +%! 0 0 0 0 0 0 0 1 1 1 +%! 0 0 0 0 1 0 1 0 1 0 +%! 0 0 0 1 1 1 1 1 0 0 +%! 0 0 0 0 1 1 1 0 0 0 +%! 0 0 0 1 1 1 0 0 0 0]; +%! +%! out = [ 0 0 0 0 0 0 0 0 1 1 +%! 0 1 0 0 0 0 0 0 0 1 +%! 1 1 1 1 1 0 0 0 0 0 +%! 0 1 1 1 1 0 0 0 0 0 +%! 0 0 0 1 0 0 0 0 1 0 +%! 0 0 0 0 0 0 0 1 1 1 +%! 0 0 0 0 1 0 1 0 1 0 +%! 0 0 0 1 1 1 1 1 0 0 +%! 0 0 0 0 1 1 1 0 0 0 +%! 0 0 0 1 1 1 0 0 0 0]; +%!assert (imtophat (logical (in), ones (3)), logical (out)); +%! +%! out = [12 19 0 0 0 16 23 0 7 0 +%! 18 0 0 6 1 19 0 2 9 1 +%! 0 74 81 12 7 0 1 8 15 7 +%! 68 70 2 14 0 6 7 14 16 0 +%! 69 76 8 0 0 7 14 21 0 1 +%! 0 7 59 54 61 13 20 0 0 32 +%! 18 0 69 60 62 19 0 0 0 27 +%! 73 0 0 66 68 0 1 6 6 33 +%! 0 0 17 19 1 0 2 9 7 14 +%! 1 6 23 0 7 1 8 15 0 32]; +%!assert (imtophat (magic (10), ones (3)), out); +%!assert (imtophat (uint8 (magic (10)), strel ("square", 3)), uint8 (out)); +%! +%! ## using a se that will be decomposed in 2 pieces +%! out =[91 98 0 0 0 27 34 11 18 0 +%! 94 76 3 6 1 33 15 17 24 1 +%! 0 77 84 12 7 14 16 23 30 7 +%! 80 82 14 18 0 32 34 41 43 0 +%! 81 88 20 0 0 33 40 47 24 6 +%! 12 19 63 57 64 16 23 0 7 39 +%! 18 0 69 60 62 19 1 3 12 39 +%! 73 0 0 66 68 0 2 9 18 45 +%! 4 6 81 67 49 6 8 15 19 26 +%! 5 12 87 48 55 7 14 21 0 32]; +%!assert (imtophat (magic (10), ones(5)), out); +%! +%! ## using a weird non-symmetric and even-size se +%! out =[85 92 0 0 0 12 23 0 17 0 +%! 91 73 0 6 0 18 0 2 13 0 +%! 0 72 81 13 6 0 1 9 15 0 +%! 60 62 10 12 0 8 8 17 17 0 +%! 61 69 0 0 0 28 16 41 0 0 +%! 0 0 47 52 61 12 16 0 0 31 +%! 6 0 53 58 60 17 0 0 0 33 +%! 69 0 0 60 62 0 0 6 0 33 +%! 0 0 17 60 42 0 2 13 1 8 +%! 0 6 23 0 7 0 7 15 0 14]; +%!assert (imtophat (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out); +%! +%! ## N dimensional and weird se +%! in = reshape (magic(16), [4 8 4 2]); +%! se = ones (3, 3, 3); +%! se(:,:,1) = [1 0 1; 0 1 1; 0 0 0]; +%! se(:,:,3) = [1 0 1; 0 1 1; 0 0 1]; +%! out = zeros (size (in)); +%! out(:,:,1,1) = [ +%! 239 146 82 18 0 19 83 133 +%! 0 35 99 163 219 128 64 0 +%! 0 46 128 195 187 123 59 0 +%! 157 93 47 0 14 78 142 211]; +%! out(:,:,2,1) = [ +%! 0 21 85 149 233 146 64 0 +%! 205 128 64 0 0 41 87 151 +%! 171 107 57 0 0 64 121 185 +%! 0 64 142 213 169 105 41 0]; +%! out(:,:,3,1) = [ +%! 231 146 78 14 0 27 77 137 +%! 0 43 107 167 211 128 64 0 +%! 0 46 128 199 179 119 51 0 +%! 149 85 39 0 18 78 142 219]; +%! out(:,:,4,1) = [ +%! 0 29 93 157 225 128 64 0 +%! 197 128 64 0 0 31 95 159 +%! 163 99 53 0 0 61 125 189 +%! 0 64 146 221 161 97 33 0]; +%! out(:,:,1,2) = [ +%! 223 146 82 18 0 35 99 149 +%! 0 48 115 179 203 128 64 0 +%! 0 46 128 211 171 107 43 0 +%! 141 77 31 0 14 78 142 227]; +%! out(:,:,2,2) = [ +%! 0 37 101 165 217 146 64 0 +%! 189 125 64 0 0 57 103 167 +%! 155 91 41 0 0 64 128 201 +%! 0 64 142 229 153 89 25 0]; +%! out(:,:,3,2) = [ +%! 215 146 78 14 0 43 93 153 +%! 0 48 123 183 195 128 64 0 +%! 0 46 128 215 163 103 35 0 +%! 133 69 23 0 18 78 142 235]; +%! out(:,:,4,2) = [ +%! 0 45 109 173 209 128 64 0 +%! 181 117 64 0 0 47 111 175 +%! 147 83 37 0 0 64 128 205 +%! 0 64 146 237 145 81 17 0]; +%!assert (imtophat (in, se), out);
--- a/inst/mmgradm.m +++ b/inst/mmgradm.m @@ -1,4 +1,4 @@ -## Copyright (C) 2010 Carnë Draug <carandraug+dev@gmail.com> +## Copyright (C) 2010, 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 @@ -14,37 +14,65 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{grad} = mmgradm(@var{A}, @var{se}) -## @deftypefnx {Function File} @var{grad} = mmgradm(@var{A}, @var{se_dil}, @var{se_ero}) -## Calculates the morphological gradient @var{grad} of a given image @var{A}. +## @deftypefn {Function File} {} mmgradm (@var{img}) +## @deftypefnx {Function File} {} mmgradm (@var{img}, @var{se_dil}) +## @deftypefnx {Function File} {} mmgradm (@var{img}, @var{se_dil}, @var{se_ero}) +## Perform morphological gradient. +## +## The matrix @var{img} must be numeric whose gradients is calculated, while +## @var{se_dil} and @var{se_ero} are the structuring elements for the dilation +## and erosion respectively. They can be a: +## @itemize @bullet +## @item +## strel object; +## @item +## array of strel objects as returned by `@strel/getsequence'; +## @item +## matrix of 0's and 1's. +## @end itemize ## -## In the first form, the same structuring element @var{se} is used for dilation -## and erosion. In the second form, @var{se_dil} and @var{se_ero} are the -## corresponding structuring elements used for dilation and erosion +## The @var{se_dil} and @var{se_ero} default to the elementary cross, i.e.: +## @example +## [ 0 1 0 +## 1 1 1 +## 0 1 0]; +## @end example ## -## The image @var{A} must be a grayscale or a binary image. +## The basic morphological gradient corresponds to a matrix erosion +## subtracted to its dilation, which is equivalent to: +## @example +## imdilate (img, se_dil) - imerode (img, se_ero) +## @end example ## -## The morphological gradient of a image corresponds to its erosion subtracted -## to its dilation. +## To perform the half-gradients by erosion or dilation, or the internal or +## external gradients, simply pass an empty matrix as structuring element: +## @example +## mmgradm (img, [], se_ero) # half-gradient by erosion or internal gradient +## mmgradm (img, se_dil, []) # half-gradient by dilation or external gradient +## @end example ## ## @seealso{imerode, imdilate, imopen, imclose, imtophat, imbothat} ## @end deftypefn -function grad = mmgradm (im, se_dil, se_ero) +function grad = mmgradm (img, se_dil = strel ("diamond", 1), se_ero = strel ("diamond", 1)) + + ## This function does not exist in Matlab. It is meant to be compatible + ## with the mmgradm function from the SDC morphology toolbox - ## sanity checks - if (nargin == 1) - error ("Structuring element must be specified"); - elseif (nargin == 2) # if only one SE is specified, use it for both erosion and dilation - se_ero = se_dil; - elseif (nargin == 3) - # all is good + if (nargin < 1 || nargin > 3) + print_usage (); + elseif (! isimage (img)) + error("imtophat: IMG must be a numeric matrix"); + endif + se_dil = prepare_strel ("mmgradm", se_dil); + se_ero = prepare_strel ("mmgradm", se_ero); + + dilated = imdilate (img, se_dil); + eroded = imerode (img, se_ero); + + if (islogical (img)) + grad = dilated & ! eroded; else - print_usage; + grad = dilated - eroded; endif - - dilated = imdilate (im, se_dil); - eroded = imerode (im, se_ero); - - grad = dilated - eroded; endfunction
new file mode 100644 --- /dev/null +++ b/inst/private/prepare_strel.m @@ -0,0 +1,33 @@ +## 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 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/>. + +## This function will be common to imopen, imclose, imbothat, imtophat, +## and any other function that can take a strel object or a matrix of 0 +## and 1. It checks the input and gets a strel object in the later case. + +function se = prepare_strel (func, se) + + ## We could do a lot more of input checking but it'd be repeated again + ## in imerode and imdilate. We just do the minimum for strel as well. + ## Since imerode and imdilate will create a strel object out of the SE + ## we pass them, we can create it now ourselves. + if (! strcmpi (class (se), "strel")) + if (! ismatrix (se) || any (se(:) == 1 & se(:) == 0)) + error ("%s: SE must be a strel object or matrix of 0 and 1", func); + endif + se = strel ("arbitrary", se); + endif + +endfunction
--- a/src/imerode.cc +++ b/src/imerode.cc @@ -368,7 +368,7 @@ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {} imerode (@var{im}, @var{SE})\n\ @deftypefnx {Loadable Function} {} imerode (@var{im}, @var{SE}, @var{shape})\n\ -Perform morphological erosion on image.\n\ +Perform morphological erosion.\n\ \n\ The image @var{im} must be a numeric matrix with any number of dimensions.\n\ The erosion is performed with the structuring element @var{se} which can\n\ @@ -666,7 +666,7 @@ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {} imdilate (@var{im}, @var{SE})\n\ @deftypefnx {Loadable Function} {} imdilate (@var{im}, @var{SE}, @var{shape})\n\ -Perform morphological dilation on image.\n\ +Perform morphological dilation.\n\ \n\ The image @var{im} must be a numeric matrix with any number of dimensions.\n\ The dilation is performed with the structuring element @var{se} which can\n\