Provides functions for processing images, such as feature
extraction, image statistics, spatial and geometric
transformations, morphological operations, linear filtering, and
much more.
image processingfeature extractionspatial transformgeometric transformmorphological operationlinear filterconvolutionbwregionregionpropshttp://octave.sourceforge.net/imagehttps://savannah.gnu.org/bugs/?func=additem&group=octaveGPL-3.0+Octave-Forge Communityoctave-maintainers@gnu.orgFSFAP
image-2.12.0/PaxHeaders.25054/inst 0000644 0000000 0000000 00000000132 13615547225 013324 x ustar 00 30 mtime=1580650133.874897313
30 atime=1580650134.434913451
30 ctime=1580650134.434913451
image-2.12.0/inst/ 0000755 0001750 0001750 00000000000 13615547225 016145 5 ustar 00carandraug carandraug 0000000 0000000 image-2.12.0/inst/PaxHeaders.25054/im2single.m 0000644 0000000 0000000 00000000062 13615546210 015443 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/im2single.m 0000644 0001750 0001750 00000005523 13615546210 020212 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2012-2014 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} im2single (@var{img})
## @deftypefnx {Function File} {} im2single (@var{img}, "indexed")
## Convert image to single precision.
##
## The conversion of @var{img} to single precision, is dependent
## on the type of input image. The following input classes are supported:
##
## @table @samp
## @item uint8, uint16, and int16
## The whole range of values from the class (see @code{getrangefromclass})
## are scaled for the interval [0 1], e.g., if input image was uint8,
## intensity values of 0, 127, and 255, are converted to intensity of
## 0, 0.498, and 1.
##
## @item logical
## True and false values are assigned a value of 0 and 1 respectively.
##
## @item double
## Values are cast to double precision.
##
## @item single
## Returns the same image.
##
## @end table
##
## If the second argument is the string @qcode{"indexed"}, then values are
## cast to single precision, and a +1 offset is applied if input is
## an integer class.
##
## @seealso{im2bw, imcast, im2uint8, im2double, im2int16, im2uint16}
## @end deftypefn
function imout = im2single (img, varargin)
if (nargin < 1 || nargin > 2)
print_usage ();
elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
error ("im2single: second input argument must be the string \"indexed\"");
endif
imout = imcast (img, "single", varargin{:});
endfunction
%!assert (im2single (single ([1 2 3])), single ([1 2 3]));
%!assert (im2single ([1 2 3]), single ([1 2 3]));
%!assert (im2single (uint8 ([0 127 128 255])), single ([0 127/255 128/255 1]));
%!assert (im2single (uint16 ([0 127 128 65535])), single ([0 127/65535 128/65535 1]));
%!assert (im2single (int16 ([-32768 -32767 -32766 32767])), single ([0 1/65535 2/65535 1]));
%!assert (im2single (uint8 ([0 1 255]), "indexed"), single ([1 2 256]));
%!assert (im2single (uint16 ([0 1 2557]), "indexed"), single ([1 2 2558]));
%!assert (im2single ([3 25], "indexed"), single ([3 25]));
%!error im2single ([0 1 2], "indexed");
%!error im2single (int16 ([17 8]), "indexed");
%!error im2single (int16 ([-7 8]), "indexed");
%!error im2single ([false true], "indexed");
image-2.12.0/inst/PaxHeaders.25054/xyz2rgb.m 0000644 0000000 0000000 00000000062 13615546210 015161 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/xyz2rgb.m 0000644 0001750 0001750 00000011174 13615546210 017727 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2015 Hartmut Gimpel
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{rgb} =} xyz2rgb (@var{xyz})
## @deftypefnx {Function File} {@var{rgb_map} =} xyz2rgb (@var{xyz_map})
## Transform a colormap or image from CIE XYZ to sRGB color space.
##
## A color in the CIE XYZ color space consists of three values X, Y and Z.
## Those values are designed to be colorimetric, meaning that their values
## do not depend on the display device hardware.
##
## A color in the RGB space consists of red, green, and blue intensities.
## The output RGB values are calculated to be nonlinear sRGB values
## with the white point D65. This means the output values are in the
## colorimetric (sRGB) colorspace.
##
## Input values of class single and double are acceptecd.
## The shape and the class of the input are conserved.
##
## note: outside the definition range (0<=R, G, B<=1) this function might
## return different (but also nonsense) values than Matlab.
##
## @seealso{rgb2xyz, rgb2lab, rgb2hsv, rgb2ind, rgb2ntsc}
## @end deftypefn
## Author: Hartmut Gimpel
## algorithm taken from the following book:
## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015)
function rgb = xyz2rgb (xyz)
if (nargin != 1)
print_usage ();
endif
[xyz, cls, sz, is_im, is_nd, is_int] ...
= colorspace_conversion_input_check ("xyz2rgb", "XYZ", xyz, 1);
# only accept single and double inputs because valid xyz values can be >1
## transform from CIE XYZ to linear sRGB values with whitepoint D65
## (source of matrix: book of Burger)
matrix_xyz2rgb_D65 = ...
[3.240479, -1.537150, -0.498535;
-0.969256, 1.875992, 0.041556;
0.055648, -0.204043, 1.057311];
# Matlab uses the following slightly different conversion matrix
# matrix_xyz2rgb_D65 = ...
# [3.2406, -1.5372, -0.4986;
# -0.9689, 1.8758, 0.0415;
# 0.0557, -0.2040, 1.0570];
rgb_lin = xyz * matrix_xyz2rgb_D65';
## transform from linear sRGB values to non-linear sRGB values
## (modified gamma transform)
rgb = rgb_lin;
mask = rgb_lin <= 0.0031308;
rgb(mask) = 12.92 .* rgb_lin(mask);
rgb(! mask) = 1.055 .* (rgb_lin(! mask) .^ (1/2.4)) -0.055;
rgb = colorspace_conversion_revert (rgb, cls, sz, is_im, is_nd, is_int, 0);
endfunction
## Test pure colors, gray and some other colors
## (This set of test values is taken from the book by Burger.)
%!assert (xyz2rgb ([0, 0, 0]), [0 0 0], 1e-3)
%!assert (xyz2rgb ([0.4125, 0.2127, 0.0193]), [1 0 0], 1e-3)
%!assert (xyz2rgb ([0.7700, 0.9278, 0.1385]), [1 1 0], 1e-3)
%!assert (xyz2rgb ([0.3576, 0.7152, 0.1192]), [0 1 0], 1e-3)
%!assert (xyz2rgb ([0.5380, 0.7873, 1.0694]), [0 1 1], 1e-3)
%!assert (xyz2rgb ([0.1804, 0.07217, 0.9502]), [0 0 1], 1e-3)
%!assert (xyz2rgb ([0.5929, 0.28484, 0.9696]), [1 0 1], 1e-3)
%!assert (xyz2rgb ([0.9505, 1.0000, 1.0888]), [1 1 1], 1e-3)
%!assert (xyz2rgb ([0.2034, 0.2140, 0.2330]), [0.5 0.5 0.5], 1e-3)
%!assert (xyz2rgb ([0.2155, 0.1111, 0.0101]), [0.75 0 0], 1e-3)
%!assert (xyz2rgb ([0.0883, 0.0455, 0.0041]), [0.5 0 0], 1e-3)
%!assert (xyz2rgb ([0.0210, 0.0108, 0.0010]), [0.25 0 0], 1e-3)
%!assert (xyz2rgb ([0.5276, 0.3812, 0.2482]), [1 0.5 0.5], 1e-3)
## Test tolarant input checking on floats
%!assert (xyz2rgb ([1.5 1 1]), [1.5712, 0.7109 0.9717], 1e-3)
%!test
%! xyz_map = rand (64, 3);
%! assert (rgb2xyz (xyz2rgb (xyz_map)), xyz_map, 3e-4);
%!test
%! xyz_img = rand (64, 64, 3);
%! assert (rgb2xyz (xyz2rgb (xyz_img)), xyz_img, 3e-4);
## support sparse input (the only useful xyz value with zeros is black)
%!assert (xyz2rgb (sparse ([0 0 0])), [0 0 0], 1e-3)
## conserve class of single input
%!assert (class (xyz2rgb (single([0.5 0.5 0.5]))), 'single')
## Test input validation
%!error xyz2rgb ()
%!error xyz2rgb (1,2)
%!error xyz2rgb ({1})
%!error xyz2rgb (ones (2,2))
## Test ND input
%!test
%! xyz = rand (16, 16, 3, 5);
%! rgb = zeros (size (xyz));
%! for i = 1:5
%! rgb(:,:,:,i) = xyz2rgb (xyz(:,:,:,i));
%! endfor
%! assert (xyz2rgb (xyz), rgb)
image-2.12.0/inst/PaxHeaders.25054/imnoise.m 0000644 0000000 0000000 00000000062 13615546210 015215 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imnoise.m 0000644 0001750 0001750 00000012331 13615546210 017757 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2000 Paul Kienzle
## Copyright (C) 2004 Stefan van der Walt
## Copyright (C) 2012 Carlo de Falco
## Copyright (C) 2012 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} imnoise (@var{A}, @var{type})
## @deftypefnx {Function File} {} imnoise (@dots{}, @var{options})
## Add noise to image.
##
## @end deftypefn
## @deftypefn {Function File} {} imnoise (@var{A}, "gaussian", @var{mean}, @var{variance})
## Additive gaussian noise with @var{mean} and @var{variance} defaulting to 0
## and 0.01.
##
## @end deftypefn
## @deftypefn {Function File} {} imnoise (@var{A}, "poisson")
## Creates poisson noise in the image using the intensity value of each pixel as
## mean.
##
## @end deftypefn
## @deftypefn {Function File} {} imnoise (@var{A}, "salt & pepper", @var{density})
## Create "salt and pepper"/"lost pixels" in @var{density}*100 percent of the
## image. @var{density} defaults to 0.05.
##
## @end deftypefn
## @deftypefn {Function File} {} imnoise (@var{A}, "speckle", @var{variance})
## Multiplicative gaussian noise with @var{B} = @var{A} + @var{A} * noise with
## mean 0 and @var{variance} defaulting to 0.04.
##
## @seealso{rand, randn, randp}
## @end deftypefn
function A = imnoise (A, stype, a, b)
## we do not set defaults right at the start because they are different
## depending on the method used to generate noise
if (nargin < 2 || nargin > 4)
print_usage;
elseif (! isimage (A))
error ("imnoise: first argument must be an image.");
elseif (! ischar (stype))
error ("imnoise: second argument must be a string with name of noise type.");
endif
in_class = class (A);
fix_class = false; # for cases when we need to use im2double
switch (lower (stype))
case "poisson"
switch (in_class)
case ("double")
A = randp (A * 1e12) / 1e12;
case ("single")
A = single (randp (A * 1e6) / 1e6);
case {"uint8", "uint16"}
A = cast (randp (A), in_class);
otherwise
A = imnoise (im2double (A), "poisson");
fix_class = true;
endswitch
case "gaussian"
A = im2double (A);
fix_class = true;
if (nargin < 3), a = 0.00; endif
if (nargin < 4), b = 0.01; endif
A = A + (a + randn (size (A)) * sqrt (b));
## Variance of Gaussian data with mean 0 is E[X^2]
case {"salt & pepper", "salt and pepper"}
if (nargin < 3), a = 0.05; endif
noise = rand (size (A));
if (isfloat (A))
black = 0;
white = 1;
else
black = intmin (in_class);
white = intmax (in_class);
endif
A(noise <= a/2) = black;
A(noise >= 1-a/2) = white;
case "speckle"
A = im2double (A);
fix_class = true;
if (nargin < 3), a = 0.04; endif
A = A .* (1 + randn (size (A)) * sqrt (a));
otherwise
error ("imnoise: unknown or unimplemented type of noise `%s'", stype);
endswitch
if (fix_class)
A = imcast (A, in_class);
elseif (isfloat (A))
## this includes not even cases where the noise made it go outside of the
## [0 1] range, but also images that were already originally outside that
## range. This is by design and matlab compatibility. And we do this after
## fixing class because the imcast functions already take care of such
## adjustment
A(A < 0) = cast (0, class (A));
A(A > 1) = cast (1, class (A));
endif
endfunction
%!assert(var(imnoise(ones(10)/2,'gaussian')(:)),0.01,0.005) # probabilistic
%!assert(length(find(imnoise(ones(10)/2,'salt & pepper')~=0.5)),5,10) # probabilistic
%!assert(var(imnoise(ones(10)/2,'speckle')(:)),0.01,0.005) # probabilistic
%!test
%! A = imnoise (.5 * ones (100), 'poisson');
%! assert (class (A), 'double')
%!test
%! A = imnoise (.5 * ones (100, 'single'), 'poisson');
%! assert (class (A), 'single')
%!test
%! A = imnoise (128 * ones (100, 'uint8'), 'poisson');
%! assert (class (A), 'uint8')
%!test
%! A = imnoise (256 * ones (100, 'uint16'), 'poisson');
%! assert (class (A), 'uint16')
%!demo
%! A = imnoise (2^7 * ones (100, 'uint8'), 'poisson');
%! subplot (2, 2, 1)
%! imshow (A)
%! title ('uint8 image with poisson noise')
%! A = imnoise (2^15 * ones (100, 'uint16'), 'poisson');
%! subplot (2, 2, 2)
%! imshow (A)
%! title ('uint16 image with poisson noise')
%! A = imnoise (.5 * ones (100), 'poisson');
%! subplot (2, 2, 3)
%! imshow (A)
%! title ('double image with poisson noise')
%! A = imnoise (.5 * ones (100, 'single'), 'poisson');
%! subplot (2, 2, 4)
%! imshow (A)
%! title ('single image with poisson noise')
image-2.12.0/inst/PaxHeaders.25054/bwmorph.m 0000644 0000000 0000000 00000000062 13615546210 015230 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/bwmorph.m 0000644 0001750 0001750 00000131437 13615546210 020003 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2004 Josep Mones i Teixidor
## Copyright (C) 2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} bwmorph (@var{bw}, @var{operation})
## @deftypefnx {Function File} {} bwmorph (@var{bw}, @var{operation}, @var{n})
## Perform morphological operation on binary image.
##
## For a binary image @var{bw}, performs the morphological @var{operation},
## @var{n} times. All possible values of @var{operation} are listed on the
## table below. By default, @var{n} is 1. If @var{n} is @code{Inf}, the
## operation is continually performed until it no longer changes the image.
##
## In some operations, @var{bw} can be a binary matrix with any number of
## dimensions (see details on the table of operations).
##
## Note that the output will always be of class logical, independently of
## the class of @var{bw}.
##
## @table @samp
## @item bothat
## Performs a bottom hat operation, a closing operation (which is a
## dilation followed by an erosion) and finally subtracts the original
## image (see @code{imbothat}). @var{bw} can have any number of
## dimensions, and @code{strel ("hypercube", ndims (@var{bw}), 3)} is
## used as structuring element.
##
## @item bridge
## Performs a bridge operation. Sets a pixel to 1 if it has two nonzero
## neighbours which are not connected, so it "bridges" them. There are
## 119 3-by-3 patterns which trigger setting a pixel to 1.
##
## @item clean
## Performs an isolated pixel remove operation. Sets a pixel to 0 if all
## of its eight-connected neighbours are 0. @var{bw} can have any number
## of dimensions in which case connectivity is @code{(3^ndims(@var{bw})) -1},
## i.e., all of the elements around it.
##
## @item close
## Performs closing operation, which is a dilation followed by erosion
## (see @code{imclose}). @var{bw} can have any number of dimensions,
## and @code{strel ("hypercube", ndims (@var{bw}), 3)} is used as
## structuring element.
##
## @item diag
## Performs a diagonal fill operation. Sets a pixel to 1 if that
## eliminates eight-connectivity of the background.
##
## @item dilate
## Performs a dilation operation (see @code{imdilate}). @var{bw} can have
## any number of dimensions, and
## @code{strel ("hypercube", ndims (@var{bw}), 3)} is used as
## structuring element.
##
## @item endpoints
## Finds the endpoints of a skeleton. The skeleton can be
## computed using @code{bwmorph (bw, "skel")}.
##
## @item erode
## Performs an erosion operation (see @code{imerode}). @var{bw} can have
## any number of dimensions, and
## @code{strel ("hypercube", ndims (@var{bw}), 3)} is used as
## structuring element.
##
## @item fill
## Performs a interior fill operation. Sets a pixel to 1 if all
## four-connected pixels are 1. @var{bw} can have any number
## of dimensions in which case connectivity is @code{(2*ndims(@var{bw}))}.
##
## @item hbreak
## Performs a H-break operation. Breaks (sets to 0) pixels that are
## H-connected.
##
## @item majority
## Performs a majority black operation. Sets a pixel to 1 if the majority
## of the pixels (5 or more for a two dimensional image) in a 3-by-3 window
## is 1. If not set to 0. @var{bw} can have any number of dimensions in
## which case the window has dimensions @code{repmat (3, 1, ndims (@var{bw}))}.
##
## @item open
## Performs an opening operation, which is an erosion followed by a
## dilation (see @code{imopen}). @var{bw} can have any number of
## dimensions, and @code{strel ("hypercube", ndims (@var{bw}), 3)}
## is used as structuring element.
##
## @item remove
## Performs a iterior pixel remove operation. Sets a pixel to 0 if
## all of its four-connected neighbours are 1. @var{bw} can have any number
## of dimensions in which case connectivity is @code{(2*ndims(@var{bw}))}.
##
## @item shrink
## Performs a shrink operation. Sets pixels to 0 such that an object
## without holes erodes to a single pixel (set to 1) at or near its
## center of mass. An object with holes erodes to a connected ring lying
## midway between each hole and its nearest outer boundary. It preserves
## Euler number.
##
## @item skel
## Performs a skeletonization operation. It calculates a "median axis
## skeleton" so that points of this skeleton are at the same distance of
## its nearby borders. It preserver Euler number. Please read
## compatibility notes for more info.
##
## It uses the same algorithm as skel-pratt but this could change for
## compatibility in the future.
##
## @item skel-lantuejoul
## Performs a skeletonization operation as described in Gonzalez & Woods
## "Digital Image Processing" pp 538-540. The text references Lantuejoul
## as author of this algorithm.
##
## It has the beauty of being a clean and simple approach, but skeletons
## are thicker than they need to and, in addition, not guaranteed to be
## connected.
##
## This algorithm is iterative. It will be applied the minimum value of
## @var{n} times or number of iterations specified in algorithm
## description. It's most useful to run this algorithm with @code{n=Inf}.
##
## @var{bw} can have any number of dimensions.
##
## @item skel-pratt
## Performs a skeletonization operation as described by William K. Pratt
## in "Digital Image Processing".
##
## @item spur
## Performs a remove spur operation. It sets pixel to 0 if it has only
## one eight-connected pixel in its neighbourhood.
##
## @item thicken
## Performs a thickening operation. This operation "thickens" objects
## avoiding their fusion. Its implemented as a thinning of the
## background. That is, thinning on negated image. Finally a diagonal
## fill operation is performed to avoid "eight-connecting" objects.
##
## @item thin-pratt
## Performs a thinning operation, according to W. K. Pratt,
## "Digital Image Processing", 3rd Edition, pp 413-414.
##
## @item thin
## Performs a thinning operation. When n=Inf, thinning sets pixels to 0
## such that an object without holes is converted to a stroke
## equidistant from its nearest outer boundaries. If the object has
## holes it creates a ring midway between each hole and its near outer
## boundary. This differ from shrink in that shrink converts objects
## without holes to a single pixels and thin to a stroke. It preserves
## Euler number.
##
## @item tophat
## Performs a top hat operation, a opening operation (which is an
## erosion followed by a dilation) and finally subtracts the original
## image (see @code{imtophat}). @var{bw} can have any number of
## dimensions, and @code{strel ("hypercube", ndims (@var{bw}), 3)}
## is used as structuring element.
## @end table
##
## Some useful concepts to understand operators:
##
## Operations are defined on 3-by-3 blocks of data, where the pixel in
## the center of the block. Those pixels are numerated as follows:
##
## @multitable @columnfractions 0.05 0.05 0.05
## @item X3 @tab X2 @tab X1
## @item X4 @tab X @tab X0
## @item X5 @tab X6 @tab X7
## @end multitable
##
## @strong{Neighbourhood definitions used in operation descriptions:}
## @table @code
## @item 'four-connected'
## It refers to pixels which are connected horizontally or vertically to
## X: X1, X3, X5 and X7.
## @item 'eight-connected'
## It refers to all pixels which are connected to X: X0, X1, X2, X3, X4,
## X5, X6 and X7.
## @end table
##
## @strong{Compatibility notes:}
## @table @code
## @item 'skel'
## Algorithm used here is described in Pratt's book. When applying it to
## the "circles" image in MATLAB documentation, results are not the
## same. Perhaps MATLAB uses Blum's algorithm (for further info please
## read comments in code).
## @item 'skel-pratt'
## This option is not available in MATLAB.
## @item 'skel-lantuejoul'
## This option is not available in MATLAB.
## @item 'thin-pratt'
## This option is not available in MATLAB.
## @item 'thicken'
## This implementation also thickens image borders. This can easily be
## avoided i necessary. MATLAB documentation doesn't state how it behaves.
## @end table
##
## References:
## W. K. Pratt, "Digital Image Processing"
## Gonzalez and Woods, "Digital Image Processing"
##
## @seealso{imdilate, imerode, imtophat, imbothat, makelut, applylut}
## @end deftypefn
function bw2 = bwmorph (bw, operation, n = 1)
if (nargin < 2 || nargin > 3)
print_usage ();
elseif (! isimage (bw))
error ("bwmorph: BW must be a binary image");
elseif (! ischar (operation))
error ("bwmorph: OPERATION must be a string");
elseif (! isnumeric (n) || ! isscalar (n))
error ("bwmorph: N must be a scalar number");
endif
## For undocumented Matlab compatibility
if (n < 0)
n = 1;
endif
## Anything is valid, just convert it to logical
bw = logical (bw);
## Some operations have no effect after being applied the first time.
## Those will set this to true and later set N to 1 (only exception is
## if N is set to 0 but even then we can't skip since we will display
## the image or return it depending on nargout)
loop_once = false;
post_morph = []; # post processing command (only if needed)
switch (tolower (operation))
case "bothat"
loop_once = true;
se = strel ("hypercube", ndims (bw), 3);
morph = @(x) imbothat (x, se);
# case "branchpoints"
# ## not implemented
case "bridge"
loop_once = true;
## see __bridge_lut_fun__ for rules
## lut = makelut ("__bridge_lut_fun__", 3);
lut = logical ([0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;0;0;0;1;0;0;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;0;0;0;1;0;0;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
morph = @(x) applylut (x, lut);
case "clean"
## Remove elements that are surrounded by false elements. So we
## create a hypercube kernel of side length 3 and values 1, and
## center value with the connectivity value. After convolution,
## only true elements with another one surrounding it, will have
## values above the connectivity (remember that the input matrix
## is binary).
loop_once = true;
kernel = ones (repmat (3, 1, ndims (bw)));
connectivity = numel (kernel) -1;
kernel(ceil (numel (kernel) /2)) = connectivity; # n-dimensional center
morph = @(x) convn (x, kernel, "same") > connectivity;
case "close"
loop_once = true;
se = strel ("hypercube", ndims (bw), 3);
morph = @(x) imclose (x, se);
case "diag"
## see __diagonal_fill_lut_fun__ for rules
## lut = makelut ("__diagonal_fill_lut_fun__", 3);
lut = logical ([0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;1;1;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
morph = @(x) applylut (x, lut);
case "dilate"
se = strel ("hypercube", ndims (bw), 3);
morph = @(x) imdilate (x, se);
case "endpoints"
## lut = makelut ("__endpoints_fun__", 3);
lut = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;0;1;1;1;1;0;1;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;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;1;
0;0;0;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;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;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;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;1;
0;0;0;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;0;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;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;1;
0;0;0;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;0;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;1;1;1;1;0;]);
morph = @(x) applylut (x, lut);
case "erode"
## Matlab bwmorph acts different than their imerode. I'm unsure
## the cause of the bug but it seems to manifest on the image border
## only. It may be they have implemented this routines in both the
## im* functions, and in bwmorph. The rest of bwmorph that uses
## erosion (open, close, bothat, and tophat a least), suffer the
## same problem. We do not replicate the bug and use imerode.
## In 2013, Mathworks has confirmed this bug and will try to fix it
## for their next releases. So, do NOT fix this for "compatibility".
se = strel ("hypercube", ndims (bw), 3);
morph = @(x) imerode (x, se);
case "fill"
## Fill elements that are surrounded by true elements (with
## connectivity 4 and its equivalent to N dimensions).
## So we create a hypercube kernel with the connected pixels as 1
## and the center with the connectivity value. After convolution,
## only true elements, or elements surrounded by true elements
## will have a values >= connectivity.
loop_once = true;
kernel = conndef (ndims (bw), "minimal");
connectivity = nnz (kernel) -1;
kernel(ceil (numel (kernel) /2)) = connectivity;
morph = @(x) convn (x, kernel, "same") >= connectivity;
case "hbreak"
loop_once = true;
## lut = makelut (inline ("x(2,2)&&!(all(x==[1,1,1;0,1,0;1,1,1])||all(x==[1,0,1;1,1,1;1,0,1]))", "x"), 3);
## which is the same as
lut = repmat ([false(16, 1); true(16, 1)], 16, 1); # identity
lut([382 472]) = false; # the 2 exceptions
morph = @(x) applylut (x, lut);
case "majority"
## If the majority of the elements surrounding an element is true,
## it changes to true. We do this using convolution, with an
## hypercube kernel, any value of the convolution above the half
## number of elements becomes tru
kernel = ones (repmat (3, 1, ndims (bw)));
majority = numel (kernel) /2;
morph = @(x) convn (x, kernel, "same") >= majority;
case "open"
loop_once = true;
se = strel ("hypercube", ndims (bw), 3);
morph = @(x) imopen (x, se);
case "remove"
## Remove elements that are surrounded by true elements by 4 connectivity.
## We create a 4 connectivity kernel with -1 values, and a center with
## the number of -1 values. Only true elements can have positives values,
## with a maximum of 4 (or whatever is the connectivity value for that
## number of dimensions) from the kernel center, and -1 per each of the
## connectivity. If all are true, its value will go down to 0.
loop_once = true;
kernel = - conndef (ndims (bw), "minimal");
kernel(ceil (numel (kernel) /2)) = nnz (kernel) -1;
morph = @(x) convn (x, kernel, "same") > 0;
case "shrink"
## lut1 = makelut ("__conditional_mark_patterns_lut_fun__", 3, "S");
lut1 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;0;1;1;1;1;0;1;0;0;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;1;1;0;0;0;0;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;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;1;
0;0;0;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;0;0;0;
0;0;0;0;0;0;0;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;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
0;0;0;0;0;0;0;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;0;0;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;0;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;1;0;1;1;0;0;0;0;0;0;0;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;1;
0;0;0;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;0;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;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;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;0;1;1;0;0]);
## lut2 = makelut (inline ("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'S')", "m"), 3);
lut2 = logical ([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;0;0;1;0;0;0;0;0;1;0;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;0;0;0;0;0;0;1;1;0;0;1;0;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;0;0;0;0;1;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;0;0;1;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;1;0;1;0;0;1;0;1;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;1;1;0;1;0;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;0;0;1;0;1;0;0;1;0;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;0;0;0;1;0;1;0;0;1;0;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
morph = @(x) x & applylut (applylut (x, lut1), lut2);
case {"skel", "skel-pratt"}
## WARNING: Result doesn't look as MATLAB's sample. It has been
## WARNING: coded following Pratt's guidelines for what he calls
## WARNING: is a "reasonably close approximation". I couldn't find
## WARNING: any bug.
## WARNING: Perhaps MATLAB uses Blum's algorithm (which Pratt
## WARNING: refers to) in: H. Blum, "A Transformation for
## WARNING: Extracting New Descriptors of Shape", Symposium Models
## WARNING: for Perception of Speech and Visual Form, W.
## WARNING: Whaten-Dunn, Ed. MIT Press, Cambridge, MA, 1967.
## lut1 = makelut ("__conditional_mark_patterns_lut_fun__", 3, "K");
lut1 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;0;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0;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;1;0;1;0;0;0;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;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;1;0;0;0;0;0;0;0;
0;0;0;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;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;1;0;1;0;0;0;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;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;1;0;1;1;0;0;0;0;0;0;0;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;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;1;0;1;1;0;0;0;0;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;1;1;1;1;0]);
## lut2 = makelut (inline ("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'K')", "m") ,3);
lut2 = logical([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;0;1;0;0;0;1;0;1;1;0;0;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;0;0;0;1;1;0;0;1;1;0;0;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;0;0;0;1;0;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;1;1;0;1;1;1;0;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;0;0;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
morph = @(x) x & applylut (applylut (x, lut1), lut2);
post_morph = @(x) bwmorph (x, "bridge");
case "skel-lantuejoul"
## This transform does not fit well in the same loop as the others,
## since each iteration requires values from the previous one. Because
## of this, we will set n to 0 in the end. However, we must take care
## to not touch the input image if n already is zero.
if (n > 0)
se = strel ("hypercube", ndims (bw), 3);
bw_tmp = false (size (bw)); # skeleton result
i = 1;
while (i <= n)
if (! any (bw(:)))
## If erosion from the previous result is 0-matrix then we are
## over because the top-hat transform below will also be a 0-matrix
## and we will be |= to a 0-matrix.
break
endif
ebw = imerode (bw, se);
## the right hand side of |= is the top-hat transform. However,
## we are not using imtophat because we will also want the output
## of the erosion for the next iteration. This saves us calling
## imerode twice for the same thing.
bw_tmp |= bw & ! imdilate (ebw, se);
bw = ebw;
i++;
endwhile
bw = bw_tmp;
n = 0; # don't do anything else
endif
case "spur"
## lut = makelut(inline("xor(x(2,2),(sum((x&[0,1,0;1,0,1;0,1,0])(:))==0)&&(sum((x&[1,0,1;0,0,0;1,0,1])(:))==1)&&x(2,2))","x"),3);
## which is the same as
lut = repmat ([false(16, 1); true(16,1)], 16, 1); # identity
lut([18, 21, 81, 273]) = false; # 4 qualifying patterns
morph = @(x) applylut (x, lut);
case "thicken"
if (n > 0)
pads = repmat (2 * min ([max(size (bw)) n]),
[1 ndims(bw)]);
bw = padarray (bw, pads, false);
bw = bwmorph (! bw, "thin-pratt", n);
loop_once = true;
morph = @(x) bwmorph (x, "diag");
## Remove ND padding
idx = arrayfun (@colon, pads +1, size (bw) -pads,
"UniformOutput", false);
post_morph = @(x) ! x(idx{:});
endif
case "thin-pratt"
## lut1 = makelut ("__conditional_mark_patterns_lut_fun__", 3, "T");
lut1 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;0;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;1;1;0;0;0;0;0;0;0;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;1;0;1;0;0;0;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;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;1;0;0;0;0;0;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;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;1;1;0;1;0;0;0;1;
0;0;0;0;0;0;0;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;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;1;1;1;0;1;1;0;0;0;0;0;0;0;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;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;1;0;0;0;1;0;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;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;0;1;1;0;0]);
## lut2 = makelut (inline ("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'T')", "m"), 3);
lut2 = logical ([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;1;1;0;1;0;1;1;0;0;0;0;1;0;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;0;0;0;0;0;1;1;0;0;1;0;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;1;0;0;0;1;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;0;0;1;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;0;1;0;0;1;0;1;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;1;1;0;1;0;1;0;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;0;1;0;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;0;1;0;0;1;0;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;
1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]);
morph = @(x) x & applylut (applylut (x, lut1), lut2);
case "thin"
## lut1 = makelut (@__thin_fun1__, 3);
lut1 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;0;1;0;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;0;0;1;0;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;1;0;0;1;1;0;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;0;0;1;0;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;1;0;0;1;1;0;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;0;0;1;0;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;1;0;0;1;1;0;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;0;0;1;0;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;1;0;0;1;1;0;1;1;1;]);
## lut2 = makelut (@__thin_fun2__, 3);
lut2 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;0;1;1;0;0;1;1;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;1;1;0;0;1;1;0;0;1;1;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;0;0;0;1;1;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;0;0;1;1;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;0;0;1;1;0;0;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;0;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;]);
morph = @(x) applylut (applylut (x, lut1), lut2);
case "tophat"
## top hat filtering has no effect after being performed once
## (inherits this behaviour from closing and opening)
loop_once = true;
se = strel ("hypercube", ndims (bw), 3);
morph = @(x) imtophat (x, se);
otherwise
error ("bwmorph: unknown OPERATION '%s' requested", operation);
endswitch
if (loop_once && n > 1)
n = 1;
endif
bw2_tmp = bw; ## make sure bw2_tmp will exist later, even if n == 0
i = 1;
while (i <= n) ## a for loop wouldn't work because n can be Inf
bw2_tmp = morph (bw);
if (isequal (bw, bw2_tmp))
## if it doesn't change we don't need to process it further
break
endif
bw = bw2_tmp;
i++;
endwhile
## process post processing commands if needed
if (! isempty (post_morph) && n > 0)
bw2_tmp = post_morph (bw2_tmp);
endif
if (nargout > 0)
bw2 = bw2_tmp;
else
imshow (bw2_tmp);
endif
endfunction
%!demo
%! bwmorph (true (11), "shrink", Inf)
%! # Should return 0 matrix with 1 pixel set to 1 at (6,6)
## Test skel-lantuejoul using Gonzalez & Woods example (fig 8.39)
%!test
%! slBW = logical ([ 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0
%! 0 0 1 1 0 0 0
%! 0 0 1 1 0 0 0
%! 0 0 1 1 1 0 0
%! 0 0 1 1 1 0 0
%! 0 1 1 1 1 1 0
%! 0 1 1 1 1 1 0
%! 0 1 1 1 1 1 0
%! 0 1 1 1 1 1 0
%! 0 1 1 1 1 1 0
%! 0 0 0 0 0 0 0]);
%!
%! rslBW = logical ([ 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0
%! 0 0 1 1 0 0 0
%! 0 0 1 1 0 0 0
%! 0 0 0 0 0 0 0
%! 0 0 0 1 0 0 0
%! 0 0 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 0 0 0 0 0
%! 0 0 0 0 0 0 0]);
%! assert (bwmorph (slBW, "skel-lantuejoul", 1), [rslBW(1:5,:); false(7, 7)]);
%! assert (bwmorph (slBW, "skel-lantuejoul", 2), [rslBW(1:8,:); false(4, 7)]);
%! assert (bwmorph (slBW, "skel-lantuejoul", 3), rslBW);
%! assert (bwmorph (slBW, "skel-lantuejoul", Inf), rslBW);
## Test for bug #39293
%!test
%! bw = [
%! 0 1 1 1 1 1
%! 0 1 1 1 1 1
%! 0 1 1 1 1 1
%! 1 1 1 1 1 1
%! 1 1 1 1 1 1
%! 1 1 1 1 1 1
%! 1 1 1 1 1 0
%! 1 1 1 1 1 0
%! 1 1 1 1 1 0];
%!
%! final = logical ([
%! 0 1 0 0 0 1
%! 0 0 1 0 1 0
%! 0 0 0 1 0 0
%! 0 0 0 1 0 0
%! 0 0 1 1 0 0
%! 0 0 1 0 0 0
%! 0 0 1 0 0 0
%! 0 1 0 1 0 0
%! 1 0 0 0 1 0]);
%! assert (bwmorph (bw, "skel", Inf), final)
%! assert (bwmorph (bw, "skel", 3), final)
%!error bwmorph ("not a matrix", "dilate")
## this makes sense to be an error but for Matlab compatibility, it is not
%!assert (bwmorph (magic (10), "dilate"), imdilate (logical (magic (10)), ones (3)));
%!test
%! in = logical ([1 1 0 0 1 0 1 0 0 0 1 1 1 0 1 1 0 1 0 0
%! 1 1 1 0 1 0 1 1 1 1 0 1 0 1 0 0 0 0 0 0
%! 0 1 1 1 0 1 1 0 0 0 1 1 0 0 1 1 0 0 1 0
%! 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 1 1 0 0 1
%! 0 1 0 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 1 0
%! 0 0 1 1 1 1 1 0 0 1 0 1 1 1 0 0 1 0 0 1
%! 0 1 1 1 1 1 1 0 1 1 1 0 0 0 1 0 0 1 0 0
%! 1 0 1 1 1 0 1 1 0 1 0 0 1 1 1 0 0 1 0 0
%! 1 0 1 1 1 0 1 0 0 1 0 0 1 1 0 0 1 1 1 0
%! 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 1 1 0 0
%! 1 1 1 1 1 1 0 1 0 1 0 0 0 0 0 0 1 0 1 1
%! 0 1 0 1 1 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0
%! 0 0 1 1 0 1 1 1 1 0 0 1 0 0 0 0 1 0 1 1
%! 0 0 1 1 0 0 1 1 1 0 0 0 1 1 1 1 0 0 0 0
%! 0 0 1 0 0 0 0 0 0 1 0 0 1 1 1 1 0 0 0 0
%! 0 0 0 0 0 0 1 1 1 0 0 0 1 1 1 1 1 0 0 0
%! 0 1 0 0 0 1 1 0 1 1 0 0 1 1 1 0 1 1 1 1
%! 1 0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 1 1 1
%! 0 0 1 1 0 1 1 1 1 0 0 0 0 1 1 0 1 1 1 1
%! 0 1 1 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 0 1]);
%! se = strel ("arbitrary", ones (3));
%!
%! assert (bwmorph (in, "dilate"), imdilate (in, se));
%! assert (bwmorph (in, "dilate", 3), imdilate (imdilate (imdilate (in, se), se), se));
%! assert (bwmorph (in, "bothat"), imbothat (in, se));
%! assert (bwmorph (in, "tophat"), imtophat (in, se));
%! assert (bwmorph (in, "open"), imopen (in, se));
%! assert (bwmorph (in, "close"), imclose (in, se));
%!assert (bwmorph ([1 0 0; 1 0 1; 0 0 1], "bridge"), logical ([1 1 0; 1 1 1; 0 1 1]));
%!assert (bwmorph ([0 0 0; 1 0 1; 0 0 1], "clean"), logical ([0 0 0; 0 0 1; 0 0 1]));
%!assert (bwmorph ([0 0 0; 0 1 0; 0 0 0], "clean"), false (3));
%!assert (bwmorph ([0 1 0; 1 0 0; 0 0 0], "diag"), logical ([1 1 0; 1 1 0; 0 0 0]));
%!test
%! in = logical ([0 1 0 1 0
%! 1 1 1 0 1
%! 1 0 0 1 0
%! 1 1 1 0 1
%! 1 1 1 1 1]);
%! out = logical ([0 1 0 1 0
%! 1 1 1 1 1
%! 1 0 0 1 0
%! 1 1 1 1 1
%! 1 1 1 1 1]);
%! assert (bwmorph (in, "fill"), out);
%!assert (bwmorph ([1 1 1; 0 1 0; 1 1 1], "hbreak"), logical ([1 1 1; 0 0 0; 1 1 1]));
%!test
%! in = logical ([0 1 0 0 0
%! 1 0 0 1 0
%! 1 0 1 0 0
%! 1 1 1 1 1
%! 1 1 1 1 1]);
%!
%! out = logical ([0 1 0 0 0
%! 1 0 0 1 0
%! 1 0 1 0 0
%! 1 1 0 1 1
%! 1 1 1 1 1]);
%! assert (bwmorph (in, "remove"), out);
%!
%! out = logical ([0 1 0 0 0
%! 1 0 0 1 0
%! 1 0 1 0 0
%! 1 1 0 1 1
%! 1 1 1 1 1]);
%! assert (bwmorph (in, "remove", Inf), out);
%!xtest
%! ## tests for spur are failing (matlab incompatible)
%! in = logical ([0 1 0 0 0
%! 1 0 0 1 0
%! 1 0 1 0 0
%! 1 1 1 1 1
%! 1 1 1 1 1]);
%!
%! out = logical ([0 1 0 0 0
%! 1 0 0 0 0
%! 1 0 1 0 0
%! 1 1 1 1 1
%! 1 1 1 1 1]);
%! assert (bwmorph (in, "spur"), out);
%!
%! out = logical ([0 1 0 0 0
%! 1 0 0 0 0
%! 1 0 0 0 0
%! 1 1 1 1 1
%! 1 1 1 1 1]);
%! assert (bwmorph (in, "spur", Inf), out);
## several tests for "thicken":
%!test
%! bw = false (3, 3);
%! bw(3, 1) = true;
%! out = bwmorph (bw, "thicken", 0);
%! assert (out, bw)
%!test
%! bw = false (8, 7);
%! bw(8, 1) = true;
%! expected = logical ([
%! 0 0 0 0 0 0 0
%! 1 0 0 0 0 0 0
%! 1 1 0 0 0 0 0
%! 1 1 1 0 0 0 0
%! 1 1 1 1 0 0 0
%! 1 1 1 1 1 0 0
%! 1 1 1 1 1 1 0
%! 1 1 1 1 1 1 1]);
%! out = bwmorph (bw, "thicken", 6);
%! assert (out, expected)
%!test
%! bw = false (8, 7);
%! bw(2, 4) = true;
%! expected = logical ([
%! 0 0 1 1 1 0 0
%! 0 1 1 1 1 1 0
%! 0 0 1 1 1 0 0
%! 0 0 0 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]);
%! out = bwmorph (bw, "thicken", 2);
%! assert (out, expected)
%!test
%! bw = false (8, 7);
%! bw (6, 3) = true ;
%! expected1 = logical ([
%! 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 1 0 0 0 0
%! 0 1 1 1 0 0 0
%! 0 0 1 0 0 0 0
%! 0 0 0 0 0 0 0]);
%! expected3 = logical ([
%! 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0
%! 0 0 1 0 0 0 0
%! 0 1 1 1 0 0 0
%! 1 1 1 1 1 0 0
%! 1 1 1 1 1 1 0
%! 1 1 1 1 1 0 0
%! 0 1 1 1 0 0 0]);
%! out1 = bwmorph (bw, "thicken", 1);
%! out3 = bwmorph (bw, "thicken", 3);
%! assert (out1, expected1)
%! assert (out3, expected3)
%!test
%! bw = false (10, 10);
%! bw(2, 3) = true;
%! bw(7, 7) = true;
%! out_inf = bwmorph (bw, "thicken", Inf);
%! assert (out_inf(1, 9), false)
%!test
%! bw = false (3, 3);
%! bw(3, 1) = true;
%! out = bwmorph (bw, "thicken", 4);
%! assert (out, true (3, 3))
%!xtest
%! ## bug #44396
%! in = [
%! 0 0 0 1 0
%! 1 1 1 1 0
%! 0 0 1 1 0
%! 0 0 1 1 0
%! 0 0 0 1 0];
%! out = [
%! 0 0 0 0 0
%! 0 1 1 0 0
%! 0 0 0 1 0
%! 0 0 0 0 0
%! 0 0 0 0 0];
%! assert (bwmorph (in, "shrink"), logical (out));
%!test
%! H = false (7,7);
%! H(2:3,2:3) = 1;
%! H(5:6,5:6) = 1;
%! T = logical([0 0 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;
%! 0 0 0 0 0 0 0;
%! 0 0 0 0 1 0 0;
%! 0 0 0 0 0 0 0]);
%! out = bwmorph (H, "thin", 1);
%! assert (T, out)
%!
%! H(4:6,4:6) = 1;
%! T = logical([0 0 0 0 0 0 0;
%! 0 0 0 0 0 0 0;
%! 0 1 1 0 0 0 0;
%! 0 0 0 1 0 0 0;
%! 0 0 0 0 1 0 0;
%! 0 0 0 0 0 0 0;
%! 0 0 0 0 0 0 0]);
%! out = bwmorph (H, "thin", 1);
%! assert (T, out)
%!
%! H3 = [0 0 0 0 0 0;
%! 0 1 1 1 0 0;
%! 0 1 1 1 0 0;
%! 0 0 0 1 0 1;
%! 0 0 0 0 1 1;
%! 0 0 0 1 1 1];
%! out3 = bwmorph (H3, "thin", 1);
%! expected3 = logical(
%! [0 0 0 0 0 0;
%! 0 0 0 0 0 0;
%! 0 1 1 0 0 0;
%! 0 0 0 1 0 1;
%! 0 0 0 0 1 0;
%! 0 0 0 1 1 0]);
%! assert (out3, expected3)
%!
%! out33 = bwmorph (H3, "thin", 2);
%! expected33 = logical(
%! [0 0 0 0 0 0;
%! 0 0 0 0 0 0;
%! 0 1 1 0 0 0;
%! 0 0 0 1 0 1;
%! 0 0 0 0 1 0;
%! 0 0 0 1 0 0]);
%! assert (out33, expected33)
%!
%! out333 = bwmorph (H3, "thin", inf);
%! assert (out333, expected33)
%!test
## test "endpoints"
%! in = logical ([
%! 1 0 0 0
%! 0 1 0 0
%! 0 0 1 0
%! 0 0 0 0]);
%! out = logical ([
%! 1 0 0 0
%! 0 0 0 0
%! 0 0 1 0
%! 0 0 0 0]);
%! assert (bwmorph (in, "endpoints"), out);
%!
%! A = logical ([0 0 0 0 0; 0 0 1 0 0; 0 1 1 1 0; 0 0 1 0 0; 0 0 0 0 0]);
%! B = logical ([0 0 0 0 0; 0 0 1 0 0; 0 1 0 1 0; 0 0 1 0 0; 0 0 0 0 0]);
%! assert (bwmorph (A, "endpoints"), B);
%!
%! A = logical ([0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 1 1
%! 0 0 1 1 1 1 0 0
%! 0 0 0 1 1 0 0 0
%! 0 0 1 1 1 1 0 0
%! 0 1 0 0 0 0 1 0
%! 1 0 0 0 0 0 0 1]);
%! B = logical ([0 0 0 0 0 0 0 0
%! 1 0 0 0 0 0 0 1
%! 0 0 0 0 0 0 0 0
%! 0 0 0 1 1 0 0 0
%! 0 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0
%! 1 0 0 0 0 0 0 1]);
%! assert (bwmorph (A, "endpoints"), B);
%!
%! A = logical([0 0 0 0 0; 0 1 1 1 0; 0 1 1 1 0; 0 1 1 1 0; 0 0 0 0 0]);
%! B = logical([0 0 0 0 0; 0 1 1 1 0; 0 1 0 1 0; 0 1 1 1 0; 0 0 0 0 0]);
%! assert (bwmorph (A, "endpoints"), B);
%! assert (bwmorph (B, "endpoints"), zeros (5, "logical"));
%!
%! A = logical([0,0,0,0,0,0,0,0,0,0,0,0,0,0
%! 0,0,1,1,1,1,1,1,1,1,1,1,0,0
%! 0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
%! B = logical([0,0,0,0,0,0,0,0,0,0,0,0,0,0
%! 0,0,1,0,0,0,0,0,0,0,0,1,0,0
%! 0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
%! C = logical([1,0,0,0,0,0,0,0,0,0,0,0,0,1
%! 1,1,0,0,0,0,0,0,0,0,0,0,1,1
%! 1,0,0,0,0,0,0,0,0,0,0,0,0,1]);
%! assert (bwmorph (!A, "endpoints"), C);
image-2.12.0/inst/PaxHeaders.25054/imsubtract.m 0000644 0000000 0000000 00000000062 13615546210 015727 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imsubtract.m 0000644 0001750 0001750 00000010221 13615546210 020465 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2011 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{out} =} imsubtract (@var{a}, @var{b})
## @deftypefnx {Function File} {@var{out} =} imsubtract (@var{a}, @var{b}, @var{class})
## Subtract image or constant to an image.
##
## If @var{a} and @var{b} are two images of same size and class, @var{b} is subtracted
## to @var{a}. Alternatively, if @var{b} is a floating-point scalar, its value is subtracted
## to the image @var{a}.
##
## The class of @var{out} will be the same as @var{a} unless @var{a} is logical
## in which case @var{out} will be double. Alternatively, it can be
## specified with @var{class}.
##
## @emph{Note 1}: you can force output class to be logical by specifying
## @var{class}. This is incompatible with @sc{matlab} which will @emph{not} honour
## request to return a logical matrix.
##
## @emph{Note 2}: the values are truncated to the mininum value of the output
## class.
##
## @emph{Note 3}: values are truncated before the operation so if input images are
## unsigned integers and the request output class is a signed integer, it may lead
## to unexpected results:
##
## @example
## @group
## imsubtract (uint8 ([23 190]), uint8 ([24 200]), "int8")
## @result{} -1 0
## @end group
## @end example
##
## Because both 190 and 200 were truncated to 127 before subtraction, their difference
## is zero.
## @seealso{imabsdiff, imadd, imcomplement, imdivide, imlincomb, immultiply}
## @end deftypefn
function img = imsubtract (img, val, out_class = class (img))
if (nargin < 2 || nargin > 3)
print_usage;
elseif (any (isa (img, {"uint8", "uint16", "uint32", "uint64"})) && any (strcmpi (out_class, {"int8", "int16", "int32", "int64"})))
## because we convert the images before the subtraction, if input is:
## imsubtract (uint8(150), uint8 (200), "int8");
## rsult will be 0 because both values are truncated to 127 before subtraction.
## There is no matlab compatibility issue because matlab does not have the option
## to specify output class in imsubtract
warning ("input images are unsigned integers but requested output is signed integer. This may lead to unexpected results.");
endif
[img, val] = imarithmetics ("imsubtract", img, val, out_class);
## The following makes the code imcompatible with matlab on certain cases.
## This is on purpose. Read comments in imadd source for the reasons
if (nargin > 2 && strcmpi (out_class, "logical"))
img = img > val;
else
img = img - val;
endif
endfunction
%!assert (imsubtract (uint8 ([23 250]), uint8 ([24 50])), uint8 ([ 0 200])); # default to first class and truncate
%!assert (imsubtract (uint8 ([23 250]), 10), uint8 ([13 240])); # works subtracting a scalar
%!assert (imsubtract (uint8 ([23 250]), uint8 ([24 50]), "uint16"), uint16 ([ 0 200])); # defining output class works (not in matlab)
%!assert (imsubtract (logical ([ 1 0]), logical ([ 1 1])), double ([ 0 -1])); # return double for two logical images
%!assert (imsubtract (logical ([ 1 0]), logical ([ 1 1]), "logical"), logical ([ 0 0])); # this is matlab incompatible on purpose
## input need to have same class
%!error imsubtract (uint8 ([23 250]), uint16 ([23 250]));
## signed integers kinda work (not in matlab)
%!warning imsubtract (uint8 ([23 250]), uint8 ([24 255]), "int8");
%!test
%! warning ("off", "all");
%! assert (imsubtract (uint8 ([23 250]), uint8 ([24 255]), "int8"),
%! int8 ([-1 0]))
image-2.12.0/inst/PaxHeaders.25054/analyze75read.m 0000644 0000000 0000000 00000000062 13615546210 016225 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/analyze75read.m 0000644 0001750 0001750 00000005023 13615546210 020767 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2012-2013 Adam H Aitkenhead
## Copyright (C) 2012 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{image} =} analyze75read (@var{filename})
## @deftypefnx {Function File} {@var{image} =} analyze75read (@var{header})
## Read image data of an Analyze 7.5 file.
##
## @var{filename} must be the path for an Analyze 7.5 file or the path for a
## directory with a single .hdr file can be specified. Alternatively, the file
## @var{header} can be specified as returned by @code{analyze75info}.
##
## @seealso{analyze75info, analyze75write}
## @end deftypefn
function data = analyze75read (filename)
if (nargin != 1)
print_usage;
elseif (isstruct (filename))
if (!isfield (filename, "Filename"))
error ("analyze75read: structure given does not have a `Filename' field.");
else
header = filename;
filename = filename.Filename;
endif
elseif (!ischar (filename))
error ("analyze75read: `filename' must be either a string or a structure.");
endif
fileprefix = analyze75filename (filename);
if (!exist ("header", "var"))
header = analyze75info ([fileprefix, ".hdr"]);
endif
## Finally start reading the actual file
[fidI, err] = fopen ([fileprefix, ".img"]);
if (fidI < 0)
error ("analyze75read: unable to fopen `%s': %s", [fileprefix, ".img"], err);
endif
if (strcmp (header.ImgDataType, "DT_SIGNED_SHORT"));
datatype = "int16";
elseif (strcmp (header.ImgDataType, "DT_SIGNED_INT"));
datatype = "int32";
elseif (strcmp (header.ImgDataType, "DT_FLOAT"));
datatype = "single";
elseif (strcmp (header.ImgDataType, "DT_DOUBLE"));
datatype = "double";
endif
data = zeros (header.Dimensions, datatype);
data(:) = fread (fidI, datatype, header.ByteOrder);
fclose (fidI);
## Rearrange the data
data = permute (data, [2,1,3]);
endfunction
image-2.12.0/inst/PaxHeaders.25054/imremap.m 0000644 0000000 0000000 00000000062 13615546210 015204 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imremap.m 0000644 0001750 0001750 00000007312 13615546210 017751 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2006 Søren 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 .
## -*- texinfo -*-
## @deftypefn {Function File} @var{warped} = imremap(@var{im}, @var{XI}, @var{YI})
## @deftypefnx{Function File} @var{warped} = imremap(@var{im}, @var{XI}, @var{YI}, @var{interp}, @var{extrapval})
## Applies any geometric transformation to the image @var{im}.
##
## The arguments @var{XI} and @var{YI} are lookup tables that define the resulting
## image
## @example
## @var{warped}(y,x) = @var{im}(@var{YI}(y,x), @var{XI}(y,x))
## @end example
## where @var{im} is assumed to be a continuous function, which is achieved
## by interpolation. Note that the image @var{im} is expressed in a (X, Y)-coordinate
## system and not a (row, column) system.
##
## The optional argument @var{method} defines the interpolation method to be
## used. All methods supported by @code{interp2} can be used. By default, the
## @code{linear} method is used.
##
## For @sc{matlab} compatibility, the methods @code{bicubic} (same as
## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as
## @code{linear}) are also supported.
##
## All values of the result that fall outside the original image will
## be set to @var{extrapval}. The default value of @var{extrapval} is 0.
##
## @seealso{imperspectivewarp, imrotate, imresize, imshear, interp2}
## @end deftypefn
function [warped] = imremap(im, XI, YI, interp = "linear", extrapval = 0)
if (nargin < 3 || nargin > 5)
print_usage ();
elseif (! isimage (im) || ndims (im) > 3)
error ("imremap: IM must be a grayscale or RGB image.")
elseif (! size_equal (XI, YI) || ! ismatrix (XI) || ! isnumeric (XI))
error ("imremap: XI and YI must be matrices of the same size");
elseif (! ischar (interp))
error ("imremap: INTERP must be a string with interpolation method")
elseif (! isscalar (extrapval))
error ("imremap: EXTRAPVAL must be a scalar");
endif
interp = interp_method (interp);
## Interpolate
sz = size (im);
n_planes = prod (sz(3:end));
sz(1:2) = size (XI);
warped = zeros(sz);
for i = 1:n_planes
warped(:,:,i) = interp2 (double(im(:,:,i)), XI, YI, interp, extrapval);
endfor
## we return image on same class as input
warped = cast (warped, class (im));
endfunction
%!demo
%! ## Generate a synthetic image and show it
%! I = tril(ones(100)) + abs(rand(100)); I(I>1) = 1;
%! I(20:30, 20:30) = !I(20:30, 20:30);
%! I(70:80, 70:80) = !I(70:80, 70:80);
%! figure, imshow(I);
%! ## Resize the image to the double size and show it
%! [XI, YI] = meshgrid(linspace(1, 100, 200));
%! warped = imremap(I, XI, YI);
%! figure, imshow(warped);
%!demo
%! ## Generate a synthetic image and show it
%! I = tril(ones(100)) + abs(rand(100)); I(I>1) = 1;
%! I(20:30, 20:30) = !I(20:30, 20:30);
%! I(70:80, 70:80) = !I(70:80, 70:80);
%! figure, imshow(I);
%! ## Rotate the image around (0, 0) by -0.4 radians and show it
%! [XI, YI] = meshgrid(1:100);
%! R = [cos(-0.4) sin(-0.4); -sin(-0.4) cos(-0.4)];
%! RXY = [XI(:), YI(:)] * R;
%! XI = reshape(RXY(:,1), [100, 100]); YI = reshape(RXY(:,2), [100, 100]);
%! warped = imremap(I, XI, YI);
%! figure, imshow(warped);
image-2.12.0/inst/PaxHeaders.25054/nlfilter.m 0000644 0000000 0000000 00000000062 13615546210 015371 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/nlfilter.m 0000644 0001750 0001750 00000016534 13615546210 020144 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} nlfilter (@var{A}, @var{block_size}, @var{func})
## @deftypefnx {Function File} {} nlfilter (@var{A}, @var{block_size}, @var{func}, @dots{})
## @deftypefnx {Function File} {} nlfilter (@var{A}, "indexed", @dots{})
## Process matrix in sliding blocks with user-supplied function.
##
## Executes the function @var{fun} on each sliding block of
## size @var{block_size}, taken from the matrix @var{A}. Both the matrix
## @var{A}, and the block can have any number of dimensions. This function
## is specially useful to perform sliding/moving window functions such as
## moving average.
##
## The output will have the same dimensions @var{A}, each one of its values
## corresponding to the processing of a block centered at the same
## coordinates in @var{A}, with @var{A} being padded with zeros for the
## borders (see below for indexed images). In case any side of the block
## is of even length, the center is considered at indices
## @code{floor ([@var{block_size}/2] + 1)}.
##
## The argument @var{func} must be a function handle that takes matrices of
## size @var{block_size} as input and returns a single scalar. Any extra
## input arguments to @code{nlfilter} are passed to @var{func} after the
## block matrix.
##
## If @var{A} is an indexed image, the second argument should be the
## string @qcode{"indexed"} so that any required padding is done correctly
## as done by @code{im2col}.
##
## @emph{Note}: if @var{func} is a column compression function, i.e., it acts
## along a column to return a single value, consider using @code{colfilt}
## which usually performs faster. If @var{func} makes use of the colon
## operator to select all elements in the block, e.g., if @var{func} looks
## anything like @code{@@(x) sum (x(:))}, it is a good indication that
## @code{colfilt} should be used. In addition, many sliding block operations
## have their own specific implementations (see help text of @code{colfilt}
## for a list).
##
## @seealso{blockproc, col2im, colfilt, im2col}
## @end deftypefn
function B = nlfilter (A, varargin)
## Input check
[p, block_size, padval] = im2col_check ("nlfilter", nargin, A, varargin{:});
if (nargin < p)
print_usage ();
endif
func = varargin{p++};
if (! isa (func, "function_handle"))
error ("nlfilter: FUNC must be a function handle");
elseif (! isscalar (func (ones (block_size, class (A)), varargin{p:nargin-1})))
error ("nlfilter: FUNC must take a matrix of size BLOCK_SIZE and return a scalar");
endif
## Remaining params are parameters to fun. We need to place them into
## a separate variable, we can't varargin{p:nargin-1} inside the anonymous
## function because nargin will have a different value there
extra_args = varargin(p:nargin -1);
## Pad image as required
padded = pad_for_sliding_filter (A, block_size, padval);
## Get the blocks (one per column)
blks = im2col (padded, block_size, "sliding");
## New function that reshapes the array into a block before
## passing it to the actual user supplied function
blk_func = @(x, z) func (reshape (blks(x:z), block_size), extra_args{:});
## Perform the filtering
blk_step = 1:(rows (blks)):(rows (blks) * columns (blks));
B = arrayfun (blk_func, blk_step, blk_step + rows (blks) -1);
## Back into its original shape
B = reshape (B, size (A));
endfunction
%!demo
%! ## creates a "wide" diagonal (although it can be performed more
%! ## efficiently with "imdilate (A, true (3))")
%! nlfilter (eye (10), [3 3], @(x) any (x(:) > 0))
%!assert (nlfilter (eye (4), [2 3], @(x) sum (x(:))),
%! [2 2 1 0
%! 1 2 2 1
%! 0 1 2 2
%! 0 0 1 1]);
%!assert (nlfilter (eye (4), "indexed", [2 3], @(x) sum (x(:))),
%! [4 2 1 2
%! 3 2 2 3
%! 2 1 2 4
%! 4 3 4 5]);
%!assert (nlfilter (eye (4), "indexed", [2 3], @(x, y) sum (x(:)) == y, 2),
%! logical ([0 1 0 1
%! 0 1 1 0
%! 1 0 1 0
%! 0 0 0 0]));
## Check uint8 and uint16 padding (only the padding since the class of
## the output is dependent on the function and sum() always returns double)
%!assert (nlfilter (uint8 (eye (4)), "indexed", [2 3], @(x) sum (x(:))),
%! [2 2 1 0
%! 1 2 2 1
%! 0 1 2 2
%! 0 0 1 1]);
%!assert (nlfilter (int16 (eye (4)), "indexed", [2 3], @(x) sum (x(:))),
%! [4 2 1 2
%! 3 2 2 3
%! 2 1 2 4
%! 4 3 4 5]);
## Check if function class is preserved
%!assert (nlfilter (uint8 (eye (4)), "indexed", [2 3], @(x) int8 (sum (x(:)))),
%! int8 ([2 2 1 0
%! 1 2 2 1
%! 0 1 2 2
%! 0 0 1 1]));
%!test
%! ## Effect of out of border elements.
%! expected = [
%! 0.5 6.0 6.0 0.5 0
%! 5.5 10.5 13.5 10.5 4.0
%! 6.5 12.5 13.5 13.5 1.5
%! 10.5 12.5 15.5 11.0 1.0
%! 5.0 10.5 6.0 1.0 0
%! ];
%! assert (nlfilter (magic (5), [3 4], @(x) median (x(:))), expected)
%!test
%! ## The center pixel of a sliding window when its length is even
%! ## sized is ceil ((size (NHOOD) +1) /2)
%! expected = [
%! 24 24 24 16 16
%! 24 24 24 22 22
%! 23 23 22 22 22
%! 25 25 25 25 22
%! 25 25 25 25 21
%! ];
%! assert (nlfilter (magic (5), [3 4], @(x) max (x(:))), expected)
## nlfilter and imdilate use a different center pixel when the
## window/nhood have an even sized length. So to have imdilate behave
## like nlfilter, we need to flip those dimensions.
%!function dilated = imdilate_like_nlfilter (im, nhood)
%! even_nhood_dims = find (mod (size (nhood), 2) == 0);
%! for i = 1:even_nhood_dims
%! im = flip (im, i);
%! endfor
%! dilated = imdilate (im, nhood);
%! for i = 1:even_nhood_dims
%! dilated = flip (dilated, i);
%! endfor
%!endfunction
## Test N-dimensional
%!test
%! a = randi (65535, 20, 20, 20, "uint16");
%! ## extra dimensions on matrix only
%! assert (nlfilter (a, [5 5], @(x) max(x(:))), imdilate (a, ones (5)))
%! ## extra dimensions on both matrix and block
%! assert (nlfilter (a, [5 5 5], @(x) max(x(:))), imdilate (a, ones ([5 5 5])))
%! ## extra dimensions and padding
%! assert (nlfilter (a, [3 7], @(x) max(x(:))), imdilate (a, ones ([3 7])))
%! assert (nlfilter (a, [3 7 3], @(x) max(x(:))), imdilate (a, ones ([3 7 3])))
%!test
%! a = randi (65535, 15, 15, 4, 8, 3, "uint16");
%! assert (nlfilter (a, [3 4 7 5], @(x) max(x(:))),
%! imdilate_like_nlfilter (a, ones ([3 4 7 5])))
## Just to make sure it's not a bug in imdilate
%!test
%! a = randi (65535, 15, 15, 4, 3, 8, "uint16");
%! ord = ordfiltn (a, 3, ones ([3 7 3 1 5]));
%! assert (nlfilter (a, [3 7 3 1 5], @(x) sort (x(:))(3)), ord)
%! assert (nlfilter (a, [3 7 3 1 5], @(x, y) sort (x(:))(y), 3), ord)
image-2.12.0/inst/PaxHeaders.25054/mat2gray.m 0000644 0000000 0000000 00000000062 13615546210 015300 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/mat2gray.m 0000644 0001750 0001750 00000011477 13615546210 020054 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 1999, 2000 Kai Habel
## Copyright (C) 2011, 2012 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{I} =} mat2gray (@var{M})
## @deftypefnx {Function File} {@var{I} =} mat2gray (@var{M}, [@var{min} @var{max}])
## Convert a matrix to an intensity image.
##
## The returned matrix @var{I} is a grayscale image, of double class and in the
## range of values [0, 1]. The optional arguments @var{min} and @var{max} will
## set the limits of the conversion; values in @var{M} below @var{min} and
## above @var{max} will be set to 0 and 1 on @var{I} respectively.
##
## @var{max} and @var{min} default to the maximum and minimum values of @var{M}.
##
## If @var{min} is larger than @var{max}, the `inverse' will be returned. Values
## in @var{M} above @var{max} will be set to 0 while the ones below @var{min}
## will be set to 1.
##
## @strong{Caution:} For compatibility with @sc{matlab}, if @var{min} and @var{max}
## are equal (either from being actually being set manually or automatically
## calculated from the @var{M} min and max values, Octave's mat2gray will truncate
## all values between [0 1]. For example
##
## @example
## @group
## mat2gray ([-2 0 0.5 0.9 5], [2 2])
## @result{} [0 0 0.5 0.9 1]
## mat2gray ([0.5 0.5 0.5])
## @result{} [0.5 0.5 0.5]
## mat2gray ([4 4 4])
## @result{} [1 1 1]
## @end group
## @end example
##
## @seealso{gray2ind, ind2gray, rgb2gray, im2double, im2uin16, im2uint8, im2int16}
## @end deftypefn
function in = mat2gray (in, scale)
if (nargin < 1 || nargin > 2)
print_usage;
elseif (! isnumeric (in) && ! islogical (in))
error ("mat2gray: IN must be a matrix");
elseif (nargin == 2 && (!isvector (scale) || numel (scale) != 2))
error ("mat2gray: second argument must be a vector with 2 elements");
endif
if (nargin == 1)
out_min = min (in(:));
out_max = max (in(:));
else
## see more at the end for the cases where max and min are swapped
out_min = min (scale (1), scale (2));
out_max = max (scale (1), scale (2));
endif
## since max() and min() return a value of same class as input,
## need to make this values double or the calculations later may fail
out_min = double (out_min);
out_max = double (out_max);
## if max and min are the same, matlab seems to simple truncate the input
## between 0 and 1, and ignores the min/max values set. Don't get the logic
## but hey! Matlab compatibility
if (out_min == out_max)
in(in>1) = 1;
in(in<0) = 0;
return
endif
## we are editing the input matrix rather than creating a new one to save
## memory. We need to make sure it's double though
in = double(in);
## it's faster to get the index of values between max and min only once
## than to have it calculated on both sides of the assignment later on. We
## need to get the index before starting editing
idx = (in > out_min & in < out_max);
idx_max = (in >= out_max);
in(in <= out_min) = 0;
in(idx_max) = 1;
in(idx) = (1/(out_max - out_min)) * (double(in(idx)) - out_min);
## if the given min and max are in the inverse order...
if (nargin > 1 && scale(1) > scale (2))
## matlab seems to allow setting the min higher than the max but not by
## checking which one is actually correct. Seems to just invert it
in = abs (in - 1);
endif
endfunction
%!assert(mat2gray([1 2 3]), [0 0.5 1]); # standard use
%!assert(mat2gray(repmat ([1 2; 3 3], [1 1 3])), repmat ([0 0.5; 1 1], [1 1 3])); # setting min and max
%!assert(mat2gray([1 2 3], [2 2]), [1 1 1]); # equal min and max
%!assert(mat2gray([-1 0 0.5 3], [2 2]), [0 0 0.5 1]); # equal min and max
%!test
%! ## SCALE is unset and all values in the input IMAGE are the same:
%! ## case 1: all values are in the [0 1] range]
%! assert (mat2gray ([.5 .5; .5 .5]), [.5 .5; .5 .5])
%! ## case 2: all values are above the [0 1] range
%! assert (mat2gray ([3 3; 3 3]), [1 1; 1 1])
%! ## case 2: all values are below the [0 1] range
%! assert (mat2gray ([-3 -3; -3 -3]), [0 0; 0 0])
%!assert(mat2gray([1 2 3], [3 1]), [1 0.5 0]); # max and min inverted
## bug #47516 (when MIN is greater than input max value)
%!assert (mat2gray ([-3 -2 -1]), [0 0.5 1])
image-2.12.0/inst/PaxHeaders.25054/impyramid.m 0000644 0000000 0000000 00000000062 13615546210 015545 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/impyramid.m 0000644 0001750 0001750 00000021516 13615546210 020314 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2015 Avinoam Kalma
## Copyright (C) 2015 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} impyramid (@var{im}, @var{direction})
## Compute gaussian pyramid expansion or reduction.
##
## Create image which is one level up or down in the Gaussian
## pyramid. @var{direction} must be @qcode{"reduce"} or
## @qcode{"expand"}. These operations are only done in the first
## two dimensions, so that even if @var{im} is a N dimensional
## array, only the number of rows and columns will change.
##
## The @qcode{"reduce"} stage is done by low-pass filtering and
## subsampling of 1:2 in each axis. If the size of the original
## image is [M N], the size of the reduced image is
## @qcode{[ceil((M+1)/2) ceil((N+1)/2)]}.
##
## The @qcode{"expand"} stage is done by upsampling the image
## (2:1 in each axis), and then low-pass filtering. If the size
## of the original image is [M N], the size of the expanded image
## is @code{[2M-1 2N-1]}.
##
## Note that image processing pyramids are upside down, so
## @qcode{"reduce"} is going one level @emph{down} in the pyramid,
## while @qcode{"expand"} is going one level @emph{up} in the pyramid.
##
## @example
## @group
## impyramid (im, "reduce"); # return reduced image (one level down)
## impyramid (im, "expand"); # return expanded image (one level up)
## @end group
## @end example
##
## The low-pass filter is defined according to Burt & Adelson [1]
## @code{W(i,j) = w(i)w(j)} where
## @code{w = [0.25-alpha/2 0.25 alpha 0.25 0.25-alpha/2]} with
## @code{alpha = 0.375}
##
## [1] Peter J. Burt and Edward H. Adelson (1983). The Laplacian Pyramid
## as a Compact Image Code. IEEE Transactions on Communications,
## vol. COM-31(4), 532-540.
##
## @seealso{imresize, imfilter}
## @end deftypefn
## Author: Avinoam Kalma
function imp = impyramid (im, direction)
if (nargin != 2)
print_usage ();
elseif (! isnumeric (im) && ! isbool (im))
error ("impyramid: IM must be numeric or logical")
endif
## low pass filters to be used
alpha = 0.375;
filt_horz = [(0.25-alpha/2) 0.25 alpha 0.25 (0.25-alpha/2)];
filt_vert = filt_horz.';
nd = ndims (im);
sz = size (im);
cl = class (im);
switch (tolower (direction))
case "reduce"
## vertical low pass filtering
im = padarray (im, floor (size (filt_vert) /2), "replicate");
im = convn (im, filt_vert, "valid");
## horizontal low pass filtering
im = padarray (im, floor (size (filt_horz) /2), "replicate");
im = convn (im, filt_horz, "valid");
im = cast (im, cl);
## subsampling
idx = repmat ({":"}, 1, nd);
idx([1 2]) = {1:2:sz(1), 1:2:sz(2)};
imp = im(idx{:});
case "expand"
## Create image, twice the size (rows and columns only),
## with the original image on the odd pixels.
imp_sz = sz .* postpad ([2 2], nd, 1);
imp_sz([1 2]) -= 1;
imp = zeros (imp_sz, cl);
idx = repmat ({":"}, 1, nd);
idx([1 2]) = {1:2:imp_sz(1), 1:2:imp_sz(2)};
imp(idx{:}) = im;
## horizontal low pass filtering
imp = padarray (imp, floor (size (filt_horz) /2));
imp = convn (imp, filt_horz, "valid");
imp *= 2;
## vertical low pass filtering
imp = padarray (imp, floor (size (filt_vert) /2));
imp = convn (imp, filt_vert, "valid");
imp *= 2;
imp = cast (imp, cl);
otherwise
error ("impyramid: DIRECTION must be 'reduce' or 'expand'")
endswitch
endfunction
## Note that there are small differences, 1 and 2 gray levels, between
## the results here (the ones we get in Octave), and the ones we should
## have for Matlab compatibility. This is specially true for elements
## in the border, and worse when expanding.
%!xtest
%! ## bug #51979 (results are not matlab compatible)
%! in = [116 227 153 69 146 194 59 130 139 106
%! 2 47 137 249 90 75 16 24 158 44
%! 155 68 46 84 166 156 69 204 32 152
%! 71 221 137 230 210 153 192 115 30 118
%! 107 143 108 52 51 73 101 21 175 90
%! 54 158 143 77 26 168 113 229 165 225
%! 9 47 133 135 130 207 236 43 19 73];
%!
%! reduced = [
%! 114 139 131 103 111
%! 97 122 141 111 100
%! 103 123 112 123 122
%! 47 107 134 153 94];
%!
%! expanded = [
%! 115 154 185 178 150 122 105 116 138 159 158 117 78 86 112 129 133 120 103
%! 69 98 128 141 146 152 152 139 125 127 121 87 55 58 81 113 131 112 84
%! 40 54 74 100 131 167 184 157 119 104 92 64 41 44 66 100 121 103 74
%! 76 69 65 75 97 130 153 148 131 122 108 80 61 79 103 105 98 97 98
%! 120 105 88 77 78 96 121 143 155 154 140 112 98 124 143 109 74 91 123
%! 117 129 134 119 107 125 153 173 180 172 156 143 138 146 140 96 60 83 122
%! 99 139 170 157 139 156 181 188 180 164 151 154 156 140 112 81 65 84 110
%! 101 136 163 153 133 132 138 136 130 122 120 130 133 108 82 86 99 104 104
%! 103 126 143 136 116 97 81 73 73 82 94 105 105 87 78 108 138 133 116
%! 90 116 139 139 122 96 69 52 53 80 109 114 111 116 128 148 163 164 160
%! 66 99 131 140 131 109 83 62 62 102 142 144 138 154 169 164 157 169 184
%! 41 68 99 121 130 122 107 92 95 133 173 182 172 156 135 114 105 121 142
%! 21 38 64 98 124 131 127 123 129 160 194 212 199 144 82 52 48 65 85];
%!
%! assert (impyramid (uint8 (in), "reduce"), uint8 (reduced))
%! assert (impyramid (uint8 (in), "expand"), uint8 (expanded))
## Test that that reduction and expansion are done in the
## first 2 dimensions only.
%!test
%! in = randi ([0 255], [40 39 3 5], "uint8");
%! red = impyramid (in, "reduce");
%! for p = 1:3
%! for n = 1:5
%! assert (red(:,:,p,n), impyramid (in(:,:,p,n), "reduce"))
%! endfor
%! endfor
%!
%! exp = impyramid (in, "expand");
%! for p = 1:3
%! for n = 1:5
%! assert (exp(:,:,p,n), impyramid (in(:,:,p,n), "expand"))
%! endfor
%! endfor
%!xtest
%! ## bug #51979 (results are not matlab compatible)
%! in = repmat (uint8 (255), [10 10]);
%! assert (impyramid (in, "reduce"), repmat (uint8 (255), [5 5]))
%! assert (impyramid (in, "expand"), repmat (uint8 (255), [19 19]))
%!xtest
%! ## bug #51979 (results are not matlab compatible)
%! in = logical ([
%! 1 0 1 1 0 0 1 1 0 0
%! 1 1 0 0 0 1 0 0 1 0
%! 0 1 1 0 1 1 1 1 1 1
%! 1 0 1 0 1 0 1 0 1 1
%! 1 1 1 0 0 0 1 1 1 1
%! 0 0 1 1 0 0 1 0 0 0
%! 0 0 1 1 0 1 1 0 1 1
%! 1 1 0 0 1 0 0 0 1 0
%! 1 1 1 1 1 1 0 1 0 0
%! 1 1 0 0 1 0 0 0 1 0]);
%!
%! reduced = logical ([
%! 1 1 0 1 0
%! 1 1 0 1 1
%! 1 1 0 1 1
%! 0 1 0 0 0
%! 1 1 1 0 0]);
%!
%! expanded = logical ([
%! 1 1 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0
%! 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
%! 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 1 1 0 0
%! 1 1 1 1 0 0 0 0 0 1 1 1 1 0 1 1 1 1 1
%! 0 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1
%! 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1
%! 1 1 0 1 1 0 0 0 1 0 0 1 1 1 0 1 1 1 1
%! 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1
%! 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1
%! 0 0 1 1 1 1 0 0 0 0 0 0 1 1 1 0 0 0 0
%! 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0
%! 0 0 0 0 1 1 1 0 0 0 0 1 1 0 0 0 0 0 0
%! 0 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1
%! 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
%! 1 1 1 1 0 0 0 1 1 1 0 0 0 0 0 0 1 0 0
%! 1 1 1 1 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0
%! 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0
%! 1 1 1 1 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0
%! 1 1 1 1 0 0 0 1 1 1 0 0 0 0 0 0 1 0 0]);
%!
%! assert (impyramid (in, "reduce"), reduced)
%! assert (impyramid (in, "expand"), expanded)
image-2.12.0/inst/PaxHeaders.25054/padarray.m 0000644 0000000 0000000 00000000062 13615546210 015355 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/padarray.m 0000644 0001750 0001750 00000061273 13615546210 020130 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} padarray (@var{A}, @var{padsize})
## @deftypefnx {Function File} {} padarray (@dots{}, @var{padval})
## @deftypefnx {Function File} {} padarray (@dots{}, @var{pattern})
## @deftypefnx {Function File} {} padarray (@dots{}, @var{direction})
## Pad array or matrix.
##
## Adds padding of length @var{padsize}, to a numeric matrix @var{A}.
## @var{padsize} must be a vector of non-negative values, each of them
## defining the length of padding to its corresponding dimension. For
## example, if @var{padsize} is [4 5], it adds 4 rows (1st dimension)
## and 5 columns (2nd dimension), to both the start and end of @var{A}.
##
## If there's less values in @var{padsize} than number of dimensions in @var{A},
## they're assumed to be zero. Singleton dimensions of @var{A} are also
## padded accordingly (except when @var{pattern} is @qcode{"reflect"}).
##
## The values used in the padding can either be a scalar value @var{padval}, or
## the name of a specific @var{pattern}. Available patterns are:
##
## @table @asis
## @item @qcode{"zeros"} (default)
## Pads with the value 0 (same as passing a @var{padval} of 0). This is the
## default.
##
## @item @qcode{"circular"}
## Pads with a circular repetition of elements in @var{A} (similar to
## tiling @var{A}).
##
## @item @qcode{"replicate"}
## Pads replicating the values at the border of @var{A}.
##
## @item @qcode{"symmetric"}
## Pads with a mirror reflection of @var{A}.
##
## @item @qcode{"reflect"}
## Same as "symmetric", but the borders are not used in the padding. Because
## of this, it is not possible to pad singleton dimensions.
##
## @end table
##
## By default, padding is done in both directions. To change this,
## @var{direction} can be one of the following values:
##
## @table @asis
## @item @qcode{"both"} (default)
## Pad each dimension before the first element of @var{A} the number
## of elements defined by @var{padsize}, and the same number again after
## the last element. This is the default.
##
## @item @qcode{"pre"}
## Pad each dimension before the first element of @var{A} the number of
## elements defined by @var{padsize}.
##
## @item @qcode{"post"}
## Pad each dimension after the last element of @var{A} the number of
## elements defined by @var{padsize}.
##
## @end table
##
## @seealso{cat, flip, resize, prepad, postpad}
## @end deftypefn
function B = padarray(A, padsize, varargin)
if (nargin < 2 || nargin > 4)
print_usage ();
elseif (! isvector (padsize) || ! isnumeric (padsize) || any (padsize < 0) ||
any (padsize != fix (padsize)))
error ("padarray: PADSIZE must be a vector of non-negative integers");
endif
## Assure padsize is a row vector
padsize = padsize(:).';
if (! any (padsize))
## Nothing to do here
B = A;
return
endif
## Default values
padval = 0;
pattern = "";
direction = "both";
## There won't be more than 2 elements in varargin
## We have to support setting the padval (shape) and direction in any
## order. Both examples must work:
## padarray (A, padsize, "circular", "pre")
## padarray (A, padsize, "pre", "circular")
for opt = 1:numel(varargin)
val = varargin{opt};
if (ischar (val))
if (any (strcmpi (val, {"pre", "post", "both"})))
direction = val;
elseif (any (strcmpi (val, {"circular", "replicate", "reflect", "symmetric"})))
pattern = val;
elseif (strcmpi (val, "zeros"))
padval = 0;
else
error ("padarray: unrecognized string option `%s'", val);
endif
elseif (isscalar (val))
padval = val;
else
error ("padarray: PADVAL and DIRECTION must be a string or a scalar");
endif
endfor
fancy_pad = false;
if (! isempty (pattern))
fancy_pad = true;
endif
## Check direction
pre = any (strcmpi (direction, {"pre", "both"}));
post = any (strcmpi (direction, {"post", "both"}));
## Create output matrix
B_ndims = max ([numel(padsize) ndims(A)]);
A_size = size (A);
P_size = padsize;
A_size(end+1:B_ndims) = 1; # add singleton dimensions
P_size(end+1:B_ndims) = 0; # assume zero for missing dimensions
pre_pad_size = P_size * pre;
B_size = A_size + pre_pad_size + (P_size * post);
## insert input matrix into output matrix
A_idx = cell (B_ndims, 1);
for dim = 1:B_ndims
A_idx{dim} = (pre_pad_size(dim) +1):(pre_pad_size(dim) + A_size(dim));
endfor
if (post && ! pre && (padval == 0 || fancy_pad))
## optimization for post padding only with zeros
B = resize (A, B_size);
else
B = repmat (cast (padval, class (A)), B_size);
B(A_idx{:}) = A;
endif
if (fancy_pad)
## Init a template "index all" cell array
template_idx = repmat ({":"}, [B_ndims 1]);
circular = replicate = symmetric = reflect = false;
switch (tolower (pattern))
case "circular", circular = true;
case "replicate", replicate = true;
case "symmetric", symmetric = true;
case "reflect", reflect = true;
otherwise
error ("padarray: unknown PADVAL `%s'.", pattern);
endswitch
## For a dimension of the input matrix of size 1, since reflect does
## not includes the borders, it is not possible to pad singleton dimensions.
if (reflect && any ((! (A_size -1)) & P_size))
error ("padarray: can't add %s padding to singleton dimensions", pattern);
endif
## For symmetric and reflect:
##
## The idea is to split the padding into 3 different cases:
## bits
## Parts of the input matrix that are used for the padding.
## In most user cases, there will be only this padding,
## complete will be zero, and so bits will be equal to padsize.
## complete
## Number of full copies of the input matrix are used for
## the padding (for reflect, "full" size is actually minus 1).
## This is divided into pair and unpaired complete. In most
## cases, this will be zero.
## pair complete
## Number of pairs of complete copies.
## unpaired complete
## This is either 1 or 0. If 1, then the complete copy closer
## to the output borders has already been flipped so that if
## there's bits used to pad as well, they don't need to be flipped.
##
## Reasoning pair and unpaired complete: when the pad is much larger
## than the input matrix, we must pay we must pay special attention to
## symmetric and reflect. In a normal case (the padding is smaller than
## the input), we just use the flipped matrix to pad and we're done.
## In other cases, if the input matrix is used multiple times on the
## pad, every other copy of it must NOT be flipped (the padding must be
## symmetric itself) or the padding will be circular.
if (reflect)
A_cut_size = A_size -1;
complete = floor (P_size ./ A_cut_size);
bits = rem (P_size, A_cut_size);
pair_size = A_cut_size * 2;
pair_complete = floor (complete / 2);
unpaired_complete = mod (complete, 2);
else
complete = floor (P_size ./ A_size);
bits = rem (P_size, A_size);
if (circular)
complete_size = complete .* A_size;
elseif (symmetric)
pair_complete = floor (complete / 2);
pair_size = A_size * 2;
unpaired_complete = mod (complete, 2);
endif
endif
dim = 0;
for s = padsize
dim++;
if (s == 0)
## skip this dimension if no padding requested
continue
endif
if (circular)
dim_idx = template_idx;
source_idx = template_idx;
A_idx_end = A_idx{dim}(end);
A_idx_ini = A_idx{dim}(1);
if (complete(dim))
dim_pad_size(1:B_ndims) = 1;
dim_pad_size(dim) = complete(dim)*pre + complete(dim)*post;
dim_idx{dim} = [];
if (pre)
dim_idx{dim} = [(bits(dim) +1):(complete_size(dim) + bits(dim))];
endif
if (post)
dim_idx{dim} = [dim_idx{dim} (A_idx_end +1):(A_idx_end + complete_size(dim))];
endif
source_idx{dim} = A_idx{dim};
B(dim_idx{:}) = repmat (B(source_idx{:}), dim_pad_size);
endif
if (pre)
if (bits(dim))
dim_idx{dim} = 1:bits(dim);
source_idx{dim} = (A_idx_end - bits(dim) +1):A_idx_end;
B(dim_idx{:}) = B(source_idx{:});
endif
endif
if (post)
if (bits(dim))
dim_idx{dim} = (B_size(dim) -bits(dim) +1):B_size(dim);
source_idx{dim} = A_idx_ini:(A_idx_ini + bits(dim) -1);
B(dim_idx{:}) = B(source_idx{:});
endif
endif
elseif (replicate)
dim_pad_size(1:B_ndims) = 1;
dim_pad_size(dim) = P_size(dim);
dim_idx = template_idx;
source_idx = template_idx;
if (pre)
dim_idx{dim} = 1:P_size(dim);
source_idx{dim} = P_size(dim) +1;
B(dim_idx{:}) = repmat (B(source_idx{:}), dim_pad_size);
endif
if (post)
dim_idx{dim} = (A_idx{dim}(end) +1):B_size(dim);
source_idx{dim} = A_idx{dim}(end);
B(dim_idx{:}) = repmat (B(source_idx{:}), dim_pad_size);
endif
## The idea behind symmetric and reflect passing is the same so the
## following cases have similar looking code. However, there's small
## adjustements everywhere that makes it really hard to merge as a
## common case.
elseif (symmetric)
dim_idx = template_idx;
source_idx = template_idx;
A_idx_ini = A_idx{dim}(1);
A_idx_end = A_idx{dim}(end);
if (pre)
if (bits(dim))
dim_idx{dim} = 1:bits(dim);
if (unpaired_complete(dim))
source_idx{dim} = (A_idx_end - bits(dim) +1):A_idx_end;
B(dim_idx{:}) = B(source_idx{:});
else
source_idx{dim} = A_idx_ini:(A_idx_ini + bits(dim) -1);
B(dim_idx{:}) = flip (B(source_idx{:}), dim);
endif
endif
endif
if (post)
if (bits(dim))
dim_idx{dim} = (B_size(dim) - bits(dim) +1):B_size(dim);
if (unpaired_complete(dim))
source_idx{dim} = A_idx_ini:(A_idx_ini + bits(dim) -1);
B(dim_idx{:}) = B(source_idx{:});
else
source_idx{dim} = (A_idx_end - bits(dim) +1):A_idx_end;
B(dim_idx{:}) = flip (B(source_idx{:}), dim);
endif
endif
endif
if (complete(dim))
dim_pad_size(1:B_ndims) = 1;
source_idx{dim} = A_idx{dim};
flipped_source = flip (B(source_idx{:}), dim);
endif
if (pair_complete(dim))
dim_pad_size(dim) = pair_complete(dim);
dim_idx{dim} = [];
if (pre)
dim_idx{dim} = [(1 + bits(dim) + (A_size(dim)*unpaired_complete(dim))):(A_idx_ini -1)];
B(dim_idx{:}) = repmat (cat (dim, B(source_idx{:}), flipped_source), dim_pad_size);
endif
if (post)
dim_idx{dim} = [(A_idx_end +1):(A_idx_end + (pair_size(dim) * pair_complete(dim)))];
B(dim_idx{:}) = repmat (cat (dim, flipped_source, B(source_idx{:})), dim_pad_size);
endif
endif
if (unpaired_complete(dim))
source_idx = template_idx;
if (pre)
dim_idx{dim} = (1 + bits(dim)):(bits(dim) + A_size(dim));
B(dim_idx{:}) = flipped_source(source_idx{:});
endif
if (post)
dim_idx{dim} = (B_size(dim) - bits(dim) - A_size(dim) +1):(B_size(dim) - bits(dim));
B(dim_idx{:}) = flipped_source(source_idx{:});
endif
endif
elseif (reflect)
dim_idx = template_idx;
source_idx = template_idx;
A_idx_ini = A_idx{dim}(1);
A_idx_end = A_idx{dim}(end);
if (pre)
if (bits(dim))
dim_idx{dim} = 1:bits(dim);
if (unpaired_complete(dim))
source_idx{dim} = (A_idx_end - bits(dim)):(A_idx_end -1);
B(dim_idx{:}) = B(source_idx{:});
else
source_idx{dim} = (A_idx_ini +1):(A_idx_ini + bits(dim));
B(dim_idx{:}) = flip (B(source_idx{:}), dim);
endif
endif
endif
if (post)
if (bits(dim))
dim_idx{dim} = (B_size(dim) - bits(dim) +1):B_size(dim);
if (unpaired_complete(dim))
source_idx{dim} = (A_idx_ini +1):(A_idx_ini + bits(dim));
B(dim_idx{:}) = B(source_idx{:});
else
source_idx{dim} = (A_idx_end - bits(dim)):(A_idx_end -1);
B(dim_idx{:}) = flip (B(source_idx{:}), dim);
endif
endif
endif
if (complete(dim))
dim_pad_size(1:B_ndims) = 1;
source_idx{dim} = A_idx{dim};
flipped_source = flip (B(source_idx{:}), dim);
endif
if (pair_complete(dim))
dim_pad_size(dim) = pair_complete(dim);
dim_idx{dim} = [];
if (pre)
flipped_source_idx = source_idx;
flipped_source_idx{dim} = 1:A_cut_size(dim);
source_idx{dim} = A_idx_ini:(A_idx_end -1);
dim_idx{dim} = [(1 + bits(dim) + (A_cut_size(dim)*unpaired_complete(dim))):(A_idx_ini -1)];
B(dim_idx{:}) = repmat (cat (dim, B(source_idx{:}), flipped_source(flipped_source_idx{:})), dim_pad_size);
endif
if (post)
flipped_source_idx = source_idx;
flipped_source_idx{dim} = 2:A_size(dim);
source_idx{dim} = (A_idx_ini +1):A_idx_end;
dim_idx{dim} = [(A_idx_end +1):(A_idx_end + (pair_size(dim) * pair_complete(dim)))];
B(dim_idx{:}) = repmat (cat (dim, flipped_source(flipped_source_idx{:}), B(source_idx{:})), dim_pad_size);
endif
endif
if (unpaired_complete(dim))
source_idx = template_idx;
if (pre)
source_idx{dim} = 1:(A_size(dim)-1);
dim_idx{dim} = (1 + bits(dim)):(bits(dim) + A_size(dim) -1);
B(dim_idx{:}) = flipped_source(source_idx{:});
endif
if (post)
source_idx{dim} = 2:A_size(dim);
dim_idx{dim} = (B_size(dim) - bits(dim) - A_size(dim) +2):(B_size(dim) - bits(dim));
B(dim_idx{:}) = flipped_source(source_idx{:});
endif
endif
endif
endfor
endif
endfunction
%!demo
%! padarray([1,2,3;4,5,6],[2,1])
%! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns of 0
%!demo
%! padarray([1,2,3;4,5,6],[2,1],5)
%! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns of 5
%!demo
%! padarray([1,2,3;4,5,6],[2,1],0,'pre')
%! % pads [1,2,3;4,5,6] with a left and top border of 2 rows and 1 columns of 0
%!demo
%! padarray([1,2,3;4,5,6],[2,1],'circular')
%! % pads [1,2,3;4,5,6] with a whole 'circular' border of 2 rows and 1 columns
%! % border 'repeats' data as if we tiled blocks of data
%!demo
%! padarray([1,2,3;4,5,6],[2,1],'replicate')
%! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns which
%! % 'replicates' edge data
%!demo
%! padarray([1,2,3;4,5,6],[2,1],'symmetric')
%! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns which
%! % is symmetric to the data on the edge
## Test default padval and direction
%!assert (padarray ([1;2], [1]), [0;1;2;0]);
%!assert (padarray ([3 4], [0 2]), [0 0 3 4 0 0]);
%!assert (padarray ([1 2 3; 4 5 6], [1 2]),
%! [zeros(1, 7); 0 0 1 2 3 0 0; 0 0 4 5 6 0 0; zeros(1, 7)]);
## Test padding on 3D array
%!test
%! assert (padarray ([1 2 3; 4 5 6], [3 2 1]),
%! cat(3, zeros(8, 7),
%! [ [ zeros(3, 7) ]
%! [zeros(2, 2) [1 2 3; 4 5 6] zeros(2, 2) ]
%! [ zeros(3,7)] ],
%! zeros (8, 7)));
## Test if default param are ok
%!assert (padarray ([1 2], [4 5]), padarray ([1 2], [4 5], 0));
%!assert (padarray ([1 2], [4 5]), padarray ([1 2], [4 5], "both"));
## Test literal padval
%!assert (padarray ([1;2], [1], i), [i; 1; 2; i]);
## Test directions (horizontal)
%!assert (padarray ([1;2], [1], i, "pre"), [i; 1; 2]);
%!assert (padarray ([1;2], [1], i, "post"), [1; 2; i]);
%!assert (padarray ([1;2], [1], i, "both"), [i; 1; 2; i]);
## Test directions (vertical)
%!assert (padarray ([1 2], [0 1], i, "pre"), [i 1 2]);
%!assert (padarray ([1 2], [0 1], i, "post"), [1 2 i]);
%!assert (padarray ([1 2], [0 1], i, "both"), [i 1 2 i]);
## Test vertical padsize
%!assert (padarray ([1 2], [0;1], i, "both"), [i 1 2 i]);
## Test circular padding
%!test
%! A = [1 2 3; 4 5 6];
%! B = repmat (A, 7, 9);
%! assert (padarray (A, [1 2], "circular", "pre"), B(2:4,2:6));
%! assert (padarray (A, [1 2], "circular", "post"), B(3:5,4:8));
%! assert (padarray (A, [1 2], "circular", "both"), B(2:5,2:8));
%! ## This tests when padding is bigger than data
%! assert (padarray (A, [5 10], "circular", "both"), B(2:13,3:25));
% Test circular padding with int* uint* class types
%!test
%! A = int8 ([1 2 3; 4 5 6]);
%! B = repmat (A, 7, 9);
%! assert (padarray (A, [1 2], "circular", "pre"), B(2:4,2:6));
%! assert (padarray (A, [1 2], "circular", "post"), B(3:5,4:8));
%! assert (padarray (A, [1 2], "circular", "both"), B(2:5,2:8));
%! ## This tests when padding is bigger than data
%! assert (padarray (A, [5 10], "circular", "both"), B(2:13,3:25));
## Test replicate padding
%!test
%! A = [1 2; 3 4];
%! B = kron (A, ones (10, 5));
%! assert (padarray (A, [9 4], "replicate", "pre"), B(1:11,1:6));
%! assert (padarray (A, [9 4], "replicate", "post"), B(10:20,5:10));
%! assert (padarray (A, [9 4], "replicate", "both"), B);
%! ## same with uint class
%! assert (padarray (uint8 (A), [9 4], "replicate", "pre"), uint8 (B(1:11,1:6)));
%! assert (padarray (uint8 (A), [9 4], "replicate", "post"), uint8 (B(10:20,5:10)));
%! assert (padarray (uint8 (A), [9 4], "replicate", "both"), uint8 (B));
## Test symmetric padding
%!test
%! A = [1:3
%! 4:6];
%! HA = [3:-1:1
%! 6:-1:4];
%! VA = [4:6
%! 1:3];
%! VHA = [6:-1:4
%! 3:-1:1];
%! B = [VHA VA VHA
%! HA A HA
%! VHA VA VHA];
%! assert (padarray (A, [1 2], "symmetric", "pre"), B(2:4,2:6));
%! assert (padarray (A, [1 2], "symmetric", "post"), B(3:5,4:8));
%! assert (padarray (A, [1 2], "symmetric", "both"), B(2:5,2:8));
%! ## same with int class
%! assert (padarray (int16 (A), [1 2], "symmetric", "pre"), int16 (B(2:4,2:6)));
%! assert (padarray (int16 (A), [1 2], "symmetric", "post"), int16 (B(3:5,4:8)));
%! assert (padarray (int16 (A), [1 2], "symmetric", "both"), int16 (B(2:5,2:8)));
## Repeat some tests with int* uint* class types
%!assert (padarray (int8 ([1; 2]), [1]), int8 ([0; 1; 2; 0]));
%!assert (padarray (uint8 ([3 4]), [0 2]), uint8 ([0 0 3 4 0 0]));
%!assert (padarray (int16 ([1; 2]), [1], 4), int16 ([4; 1; 2; 4]));
%!assert (padarray (uint16 ([1; 2]), [1], 0), uint16 ([0; 1; 2; 0]));
%!assert (padarray (uint32 ([1; 2]), [1], 6, "post"), uint32 ([1; 2; 6]));
%!assert (padarray (int32 ([1; 2]), [1], int32 (4), "pre"), int32 ([4; 1; 2]));
## Test symmetric and reflect for multiple lengths of padding (since the way
## it's done changes based on this). By iterating from 10 on a matrix of size
## 10, we catch the cases where there's only part of the matrix on the pad, a
## single copy of the matrix, a single copy with bits of non-flipped matrix, two
##copies of the matrix (flipped and non-flipped), the two copies with bits.
%!test
%! in = [ 7 5 1 3
%! 5 3 3 4
%! 7 5 2 3
%! 6 1 3 8];
%! padded = [
%! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3
%! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1
%! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1
%! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3
%! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2
%! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3
%! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3
%! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2
%! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3
%! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1
%! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1
%! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3
%! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2
%! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3
%! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3
%! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2
%! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3
%! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1
%! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1
%! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3
%! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2
%! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3
%! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3
%! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2];
%! for ite = 1:10
%! assert (padarray (in, [ite ite], "symmetric"), padded((11-ite):(14+ite),(11-ite):(14+ite)));
%! assert (padarray (in, [ite ite], "symmetric", "pre"), padded((11-ite):14,(11-ite):14));
%! assert (padarray (in, [ite ite], "symmetric", "post"), padded(11:(14+ite),11:(14+ite)));
%! endfor
%!test
%! in = [ 7 5 4 9
%! 6 4 5 1
%! 5 3 3 3
%! 2 6 7 3];
%! padded = [
%! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3
%! 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6
%! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3
%! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4
%! 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5
%! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4
%! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3
%! 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6
%! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3
%! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4
%! 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5
%! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4
%! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3
%! 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6
%! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3
%! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4
%! 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5
%! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4
%! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3
%! 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6
%! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3
%! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4
%! 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5
%! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4];
%! for ite = 1:10
%! assert (padarray (in, [ite ite], "reflect"), padded((11-ite):(14+ite),(11-ite):(14+ite)));
%! assert (padarray (in, [ite ite], "reflect", "pre"), padded((11-ite):14,(11-ite):14));
%! assert (padarray (in, [ite ite], "reflect", "post"), padded(11:(14+ite),11:(14+ite)));
%! endfor
image-2.12.0/inst/PaxHeaders.25054/imquantize.m 0000644 0000000 0000000 00000000062 13615546210 015740 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imquantize.m 0000644 0001750 0001750 00000015260 13615546210 020506 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2015 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {[@var{quant}, @var{idx}] =} imquantize (@var{img}, @var{levels})
## @deftypefnx {Function File} {[@dots{}] =} imquantize (@var{img}, @var{levels}, @var{values})
## Quantize image with multiple threshold levels and values.
##
## This function reduces the number of unique values in @var{img} by
## performing multiple thresholds, one for each element in @var{levels},
## and assigning it values from @var{values}.
##
## The output @var{quant} is the quantized image, of same size as @var{img}
## and same class as @var{values}. @var{idx} are the indices into
## @var{values} such that @code{@var{quant} == @var{values}(@var{idx})}.
##
## For the simpler case where @var{values} is not defined, it defaults
## to @code{1:N+1} and this function becomes equivalent to:
##
## @example
## out = ones (size (@var{img}));
## for i = 1:numel(@var{levels})
## out(@var{img} > @var{levels}(i)) += 1;
## endfor
## @end example
##
## So that for example:
##
## @example
## >> img = [1 2 3; 4 5 6; 7 8 9];
## >> imquantize (img, [3 6])
## @result{} ans =
##
## 1 1 1
## 2 2 2
## 3 3 3
## @end example
##
## For the more general case, when @var{values} is defined, this function
## is equivalent to:
##
## @example
## @var{levels} = [-Inf @var{levels} Inf];
## out = zeros (size (@var{img}), class (@var{values}));
## for i = 1:numel(@var{values})
## out(@var{img} > @var{levels}(i) & @var{img} <= @var{levels}(i+1)) = @var{values}(i);
## endfor
## @end example
##
## So that for example:
##
## @example
## >> img = [1 2 3; 4 5 6; 7 8 9];
## >> imquantize (img, [3 6], [0 5 8])
## @result{} ans =
##
## 0 0 0
## 5 5 5
## 8 8 8
## @end example
##
## @seealso{gray2ind, ind2gray, ind2rgb, intlut, label2rgb, lookup, rgb2ind}
## @end deftypefn
function [quant, idx] = imquantize (img, levels, values)
if (nargin < 2 || nargin > 3)
print_usage ();
elseif (! isimage (img))
error ("imquantize: IMG must be an image - numeric array with real values");
endif
validateattributes (levels, {"numeric"}, {"nondecreasing", "vector"},
"imquantize", "LEVELS");
## Add eps to levels because lookup does
## "table(idx(i)) <= y(i) < table(idx(i+1))"
## But we want:
## "table(idx(i)) < y(i) <= table(idx(i+1))"
levels = double (levels);
if (isfloat (img))
## Beware when img is of class single. The comparison in lookup()
## will be done in the class of the image if it is float.
levels += eps (cast (levels, class (img)));
else
levels += eps (levels);
endif
idx = lookup ([-Inf; levels(:); Inf], img);
if (nargin < 3)
quant = idx;
else
if (! isvector (values) || ! isnumeric (values))
error ("imquantize: VALUES must be a numeric vector")
elseif (numel (values) != numel (levels) +1)
error ("imquantize: VALUES must have 1 element more than LEVELS");
endif
quant = values(idx);
endif
endfunction
%!error
%! imquantize (rand (5), [3 4 2 5])
%!error
%! imquantize (rand (5), [1 2 3], "foo")
%!error
%! imquantize (rand (5), [1 2 3 4], 1:6)
%!error
%! imquantize (rand (5), [1 2 3 4], 1:2)
%!test
%! img = [-inf 0 10000000; -100000 -3 1/1000000; 5 5 10];
%! [q, q_idx] = imquantize (img, 5);
%! assert (q, [1 1 2; 1 1 1; 1 1 2])
%! assert (q_idx, q)
%!test
%! img = [1:10; 11:20; 21:30; 31:40; 41:50; 51:60; 61:70];
%!
%! expected_q = [
%! 0 0 0 0 0 1 1 1 1 1
%! 1 1 1 1 1 5 5 5 5 5
%! 5 5 5 5 5 10 10 10 10 10
%! 20 20 20 20 20 20 20 20 20 20
%! 30 30 30 30 30 30 30 30 30 30
%! 30 30 30 30 30 30 30 30 30 30
%! 15 15 15 15 15 15 15 15 15 15];
%!
%! expected_q_idx = [
%! 1 1 1 1 1 2 2 2 2 2
%! 2 2 2 2 2 3 3 3 3 3
%! 3 3 3 3 3 4 4 4 4 4
%! 5 5 5 5 5 5 5 5 5 5
%! 6 6 6 6 6 6 6 6 6 6
%! 6 6 6 6 6 6 6 6 6 6
%! 7 7 7 7 7 7 7 7 7 7];
%!
%! [q, q_idx] = imquantize (img, [5 15 25 30 40 60], [0 1 5 10 20 30 15]);
%! assert (q, expected_q)
%! assert (q_idx, expected_q_idx)
%!
%! [q, q_idx] = imquantize (single (img), [5 15 25 30 40 60],
%! [0 1 5 10 20 30 15]);
%! assert (q, expected_q)
%! assert (q_idx, expected_q_idx)
%!
%! [q, q_idx] = imquantize (uint8 (img), [5 15 25 30 40 60],
%! [0 1 5 10 20 30 15]);
%! assert (q, expected_q)
%! assert (q_idx, expected_q_idx)
%!
%! [q, q_idx] = imquantize (uint8 (img), uint8 ([5 15 25 30 40 60]),
%! [0 1 5 10 20 30 15]);
%! assert (q, expected_q)
%! assert (q_idx, expected_q_idx)
%!
%! [q, q_idx] = imquantize (uint8 (img), uint8 ([5 15 25 30 40 60]),
%! uint8 ([0 1 5 10 20 30 15]));
%! assert (q, uint8 (expected_q))
%! assert (q_idx, expected_q_idx)
## Test output class
%!test
%! img = randi ([0 255], 10, "uint8");
%! [q, q_idx] = imquantize (img, [50 100 150 200]);
%! assert (class (q), "double")
%! assert (class (q_idx), "double")
%!
%! [q, q_idx] = imquantize (img, [50 100 150 200], uint16 ([5 7 8 9 2]));
%! assert (class (q), "uint16")
%! assert (class (q_idx), "double")
%!
%! [q, q_idx] = imquantize (img, [50 100 150 200], uint8 ([5 7 8 9 2]));
%! assert (class (q), "uint8")
%! assert (class (q_idx), "double")
## A test with non-ordered pixel values, tested against ordered
%!test
%! img = [1:10; 11:20; 21:30; 31:40; 41:50; 51:60; 61:70].';
%! r_idx = reshape (randperm (numel (img)), size (img));
%!
%! [quant, quant_idx] = imquantize (img, [5 15 25 30 40 60]);
%! [quant_r, quant_r_idx] = imquantize (img(r_idx), [5 15 25 30 40 60]);
%!
%! assert (imquantize (img(r_idx), [5 15 25 30 40 60]), quant(r_idx))
%! assert (quant_r, quant_r_idx)
image-2.12.0/inst/PaxHeaders.25054/rgb2xyz.m 0000644 0000000 0000000 00000000062 13615546210 015161 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/rgb2xyz.m 0000644 0001750 0001750 00000012105 13615546210 017722 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2015 Hartmut Gimpel
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{xyz} =} rgb2xyz (@var{rgb})
## @deftypefnx {Function File} {@var{xyz_map} =} rgb2xyz (@var{rgb_map})
## Transform a colormap or image from sRGB to CIE XYZ color space.
##
## A color in the RGB space consists of red, green, and blue intensities.
## The input RGB values are interpreted as nonlinear sRGB values
## with the white point D65. This means the input values are assumed to
## be in the colorimetric (sRGB) colorspace.
##
## A color in the CIE XYZ color space consists of three values X, Y and Z.
## Those values are designed to be colorimetric, meaning that their values
## do not depend on the display device hardware.
##
## Input values of class double, single, uint8 or uint16 are accepted.
## Output class is generally of type double, only input type single will
## result in an output type of single. The shape of the input is
## conserved.
##
## note: This function returns slightly different values than the Matlab
## version. But it has a better "round trip accuracy" (<2e-5)
## for RGB -> XYZ -> RGB.
##
## @seealso{xyz2rgb, rgb2lab, rgb2hsv, rgb2ind, rgb2ntsc}
## @end deftypefn
## Author: Hartmut Gimpel
## algorithm taken from the following book:
## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015)
function xyz = rgb2xyz (rgb)
if (nargin != 1)
print_usage ();
endif
[rgb, cls, sz, is_im, is_nd, is_int] ...
= colorspace_conversion_input_check ("rgb2xyz", "RGB", rgb, 0);
## transform from non-linear sRGB values to linear sRGB values
## (inverse modified gamma transform)
rgb_lin = rgb;
mask = rgb <= 0.04045;
rgb_lin(mask) = rgb(mask) ./ 12.92;
rgb_lin(! mask) = ((rgb(! mask) + 0.055) ./ 1.055) .^2.4;
## transform from linear sRGB values to CIE XYZ with whitepoint D65
## (source of this matrix: book of Burger)
matrix_rgb2xyz_D65 = ...
[0.412453, 0.357580, 0.180423;
0.212671, 0.715160, 0.072169;
0.019334, 0.119193, 0.950227];
# Matlab uses the following slightly different conversion matrix.
# matrix_rgb2xyz_D65 = ...
# [0.4124, 0.3576, 0.1805;
# 0.2126, 0.7152, 0.0722;
# 0.0193, 0.1192, 0.9505];
# open source of this transformation matrix:
# https://de.wikipedia.org/wiki/CIE-Normvalenzsystem#Umrechnung_der_Farbr.C3.A4ume
# on July 30, 2015
xyz = rgb_lin * matrix_rgb2xyz_D65';
# always return values of type double for Matlab compatibility (exception: type single)
xyz = colorspace_conversion_revert (xyz, cls, sz, is_im, is_nd, is_int, 1);
endfunction
## Test pure colors, gray and some other colors
## (This set of test values is taken from the book by Burger.)
%!assert (rgb2xyz ([0 0 0]), [0, 0, 0], 1e-3)
%!assert (rgb2xyz ([1 0 0]), [0.4125, 0.2127, 0.0193], 1e-3)
%!assert (rgb2xyz ([1 1 0]), [0.7700, 0.9278, 0.1385], 1e-3)
%!assert (rgb2xyz ([0 1 0]), [0.3576, 0.7152, 0.1192], 1e-3)
%!assert (rgb2xyz ([0 1 1]), [0.5380, 0.7873, 1.0694], 1e-3)
%!assert (rgb2xyz ([0 0 1]), [0.1804, 0.0722, 0.9502], 1e-3)
%!assert (rgb2xyz ([1 0 1]), [0.5929, 0.2848, 0.9696], 1e-3)
%!assert (rgb2xyz ([1 1 1]), [0.9505, 1.0000, 1.0888], 1e-3)
%!assert (rgb2xyz ([0.5 0.5 0.5]), [0.2034, 0.2140, 0.2330], 1e-3)
%!assert (rgb2xyz ([0.75 0 0]), [0.2155, 0.1111, 0.0101], 1e-3)
%!assert (rgb2xyz ([0.5 0 0]), [0.0883, 0.0455, 0.0041], 1e-3)
%!assert (rgb2xyz ([0.25 0 0]), [0.0210, 0.0108, 0.0010], 1e-3)
%!assert (rgb2xyz ([1 0.5 0.5]), [0.5276, 0.3812, 0.2482], 1e-3)
## Test tolarant input checking on floats
%!assert (rgb2xyz ([1.5 1 1]), [1.5845, 1.3269, 1.1185], 1e-3)
%!test
%! rgb_map = rand (64, 3);
%! assert (xyz2rgb (rgb2xyz (rgb_map)), rgb_map, 2e-5);
%!test
%! rgb_img = rand (64, 64, 3);
%! assert (xyz2rgb (rgb2xyz (rgb_img)), rgb_img, 2e-5);
## support sparse input
%!assert (rgb2xyz (sparse ([0 0 0])), [0 0 0], 1e-3)
%!assert (rgb2xyz (sparse ([0 0 1])), [0.1804, 0.0722, 0.9502], 1e-3)
## support integer input (and double output)
%!assert (rgb2xyz (uint8([255 255 255])), [0.9505, 1.0000, 1.0888], 1e-3)
## conserve class of single input
%!assert (class (rgb2xyz (single([1 1 1]))), 'single')
## Test input validation
%!error rgb2xyz ()
%!error rgb2xyz (1,2)
%!error rgb2xyz ({1})
%!error rgb2xyz (ones (2,2))
## Test ND input
%!test
%! rgb = rand (16, 16, 3, 5);
%! xyz = zeros (size (rgb));
%! for i = 1:5
%! xyz(:,:,:,i) = rgb2xyz (rgb(:,:,:,i));
%! endfor
%! assert (rgb2xyz (rgb), xyz)
image-2.12.0/inst/PaxHeaders.25054/maketform.m 0000644 0000000 0000000 00000000062 13615546210 015537 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/maketform.m 0000644 0001750 0001750 00000015714 13615546210 020311 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2012 Pantxo Diribarne
##
## 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 Octave; see the file COPYING. If not, see
## .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{T} =} maketform (@var{ttype}, @var{tmat})
## @deftypefnx {Function File} {@var{T} =} maketform (@var{ttype}, @var{inc}, @var{outc})
## @deftypefnx {Function File} {@var{T} =} maketform ("custom", @var{ndims_in}, @var{ndims_out}, @var{forward_fcn}, @var{inverse_fcn}, @var{tdata})
## Create a transform structure @var{T} to be used for spatial
## transformations between an input space and an output space.
##
## The fields of the transform structure are:
##
## @table @asis
## @item @qcode{"ndims_in"}, @qcode{"ndims_out"}: the number of
## dimensions of the input and output space.
##
## @item @qcode{"forward_fcn"}, @qcode{"inverse_fcn"}: the callback
## functions that are called for forward (input to output) and inverse
## transform.
##
## @item @qcode{"tdata"}: an inverse transform matrix or a structure
## containing forward and inverse transform matrices.
## @end table
##
## The content of each field depends on the requested transform type
## @var{ttype}:
##
## @table @asis
## @item "projective"
## A ndims_in = ndims_out = N projective transform structure
## is returned.
## If a second input argument @var{tmat} is provided, it must be a
## (N+1)-by-(N+1) inverse transformation matrix. The (N+1)th column must
## contain projection coefficients. As an example a two
## dimensional inverse transform from [x y] coordinates to [u v] coordinates
## is represented by an inverse transformation matrix defined so that:
##
## @example
## [xx yy zz] = [u v 1] * [a d g;
## b e h;
## c f i]
## [x y] = [xx./zz yy./zz];
## @end example
##
## Alternatively the transform can be specified using the coordinates
## of a quadrilateral (typically the 4 corners of the
## image) in the input space (@var{inc}, 4-by-ndims_in matrix) and in
## the output space (@var{outc}, 4-by-ndims_out matrix). This is
## equivalent to building the transform using
## @code{T = cp2tform (@var{inc}, @var{outc}, "projective")}.
##
## @item "affine"
## Affine is a subset of projective transform (see above). A
## ndims_in = ndims_out = N affine transformation structure is
## returned.
## If a second input argument @var{tmat} is provided, it must be a
## (N+1)-by-(N+1) or (N+1)-by-(N) transformation matrix. If present, the
## (N+1)th column must contain [zeros(N,1); 1] so that projection is
## suppressed.
##
## Alternatively the transform can be specified using the coordinates
## of a triangle (typically 3 corners of the
## image) in the input space (@var{inc}, 3-by-ndims_in matrix) and in
## the output space (@var{outc}, 3-by-ndims_out matrix). This is
## equivalent to building the transform using
## @code{T = cp2tform (@var{inc}, @var{outc}, "affine")}.
##
## @item "custom"
## For user defined transforms every field of the transform structure
## must be supplied. The prototype of the transform functions,
## @var{forward_fcn} and @var{inverse_fcn}, should be X' =
## transform_fcn (X, T). X and X' are respectively p-by-ndims_in and
## p-by-ndims_out arrays for forward_fcn and reversed for inverse_fcn.
## The argument T is the transformation structure which will contain
## the user supplied transformation matrix @var{tdata}.
## @end table
##
## @seealso{tformfwd, tforminv, cp2tform}
## @end deftypefn
## Author: Pantxo Diribarne
function T = maketform (ttype, varargin)
if (nargin < 2 || ! any (strcmpi (ttype, {"affine", "projective", "custom"})))
print_usage ();
endif
if (numel (varargin) == 1)
tmat = varargin {1};
ndin = rows (tmat) - 1;
ndout = columns (tmat) - 1;
if (ndin < 2);
error ("maketform: expect at least 3-by-2 transform matrix")
elseif ((ndin-ndout) > 1 || (ndout > ndin))
print_usage ();
endif
switch (tolower (ttype))
case "affine"
if ((ndin - ndout) == 1)
tmat = [tmat [zeros(ndin, 1); 1]];
ndout += 1;
elseif (!all (tmat(:,end) == [zeros(ndin, 1); 1]))
error ("maketform: \"%s\" expect [zeros(N,1); 1] as (N+1)th column", ttype);
endif
forward_fcn = @fwd_affine;
inverse_fcn = @inv_affine;
case "projective"
if ((ndin - ndout) == 1)
print_usage ();
endif
forward_fcn = @fwd_projective;
inverse_fcn = @inv_projective;
endswitch
T.ndims_in = ndin;
T.ndims_out = ndout;
T.forward_fcn = forward_fcn;
T.inverse_fcn = inverse_fcn;
T.tdata.T = tmat;
T.tdata.Tinv = inv (tmat);
elseif (numel (varargin) == 2)
inc = varargin{1};
outc = varargin{2};
if (strcmp (ttype, "affine"))
if (all (size (inc) == size (outc)) &&
all (size (inc) == [3 2]))
T = cp2tform (inc, outc, ttype);
else
error ("maketform: expect INC and OUTC to be 3-by-2 vectors.");
endif
elseif (strcmp (ttype, "projective"))
if (all (size (inc) == size (outc)) &&
all (size (inc) == [4 2]))
T = cp2tform (inc, outc, ttype);
else
error ("maketform: expect INC and OUTC to be 4-by-2 vectors.");
endif
endif
elseif (numel (varargin) == 5 && strcmpi (ttype, "custom"))
if (isscalar (varargin{1}) && isscalar (varargin{2})
&& varargin{1} > 0 && varargin{2} > 0)
T.ndims_in = varargin{1};
T.ndims_out = varargin{2};
else
error ("maketform: expect positive scalars as ndims.")
endif
if (is_function_handle (varargin{3}) || isempty (varargin{3}))
T.forward_fcn = varargin{3};
else
error ("maketform: expect function handle as forward_fcn.")
endif
if (is_function_handle (varargin{4}) || isempty (varargin{4}))
T.inverse_fcn = varargin{4};
else
error ("maketform: expect function handle as inverse_fcn.")
endif
T.tdata = varargin{5};
else
print_usage ();
endif
endfunction
function X = fwd_affine (U, T)
U = [U, ones(rows(U), 1)];
X = U * T.tdata.T(:,1:end-1);
endfunction
function U = inv_affine (X, T)
X = [X, ones(rows(X), 1)];
U = X * T.tdata.Tinv(:,1:end-1);
endfunction
function X = fwd_projective (U, T)
U = [U, ones(rows(U), 1)];
XX = U * T.tdata.T;
X = [XX(:,1)./XX(:,3) XX(:,2)./XX(:,3)];
endfunction
function U = inv_projective (X, T)
X = [X, ones(rows(X), 1)];
UU = X * T.tdata.Tinv;
U = [UU(:,1)./UU(:,3) UU(:,2)./UU(:,3)];
endfunction
image-2.12.0/inst/PaxHeaders.25054/imsharpen.m 0000644 0000000 0000000 00000000062 13615546210 015540 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imsharpen.m 0000644 0001750 0001750 00000021205 13615546210 020302 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2017 Avinoam Kalma
## Copyright (C) 2017 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} imsharpen (@var{im})
## @deftypefnx {Function File} {} imsharpen (@var{im}, @var{option}, @var{value}, @dots{})
## Sharpen image using unsharp masking.
##
## @var{im} must be a grayscale or RGB image. The unsharp masking can
## be controlled with @var{option}-@var{value} pairs.
##
## The unsharp masking technique is equivalent to:
##
## @example
## @var{im} + @var{k} * (@var{im} - smooth (@var{im}))
## @end example
##
## where @var{im} is a grayscale image and @command{smooth} performs
## gaussian smoothing. RGB images are transformed to Lab colorspace,
## the L channel is sharpen to L', and L'ab is transformed back to RGB.
## See @url{https://en.wikipedia.org/wiki/Unsharp_masking,
## "Unsharp masking" in Wikipedia}
##
## The following options control the unsharp masking:
##
## @table @asis
## @item @qcode{"Radius"}
## Sigma of Gaussian Filter for the smoothing stage. Must be a
## positive number. Defaults to 1.
##
## @item @qcode{"Amount"}
## Magnitude of the overshoot @var{k}. Must be a non-negative
## number. Defaults to 0.8.
##
## @item @qcode{"Threshold"}
## Minimum brightness change that will be sharpened. Must be in the
## range [0 1]. Defaults to 0.
##
## @end table
##
## Examples:
##
## @example
## @group
## out = imsharpen (im); # Using imsharpen with default values
## out = imsharpen (im, "Radius", 1.5);
## out = imsharpen (im, "Amount", 1.2);
## out = imsharpen (im, "Threshold", 0.5);
## out = imsharpen (im, "Radius", 1.5, "Amount", 1.2, "Threshold", 0.5);
## @end group
## @end example
##
## @seealso{imfilter, fspecial}
## @end deftypefn
function [sharp] = imsharpen (im, varargin)
if (nargin == 0)
print_usage ();
elseif (! isnumeric (im) && ! isbool (im))
error ("imsharpen: IM must be numeric or logical");
elseif (ndims (im) > 4 || all (size (im, 3) != [1 3]))
error ("imsharpen: IM must be a grayscale or RGB image");
endif
p = inputParser ();
p.addParamValue ("Radius", 1, @(x) isnumeric (x) && isscalar (x));
p.addParamValue ("Amount", 0.8, @(x) isnumeric (x) && isscalar (x));
p.addParamValue ("Threshold", 0, @(x) isnumeric (x) && isscalar (x));
p.parse (varargin{:});
if (p.Results.Radius <= 0)
error ("imsharpen: RADIUS should be positive");
elseif (p.Results.Amount < 0)
error ("imsharpen: AMOUNT should be non-negative");
elseif (p.Results.Threshold < 0 || p.Results.Threshold > 1)
error ("imsharpen: THRESHOLD should be in the range [0:1]");
endif
imsharpen_size = ceil (max (4 * p.Results.Radius +1, 3));
if (mod (imsharpen_size, 2) == 0)
imsharpen_size += 1;
endif
if (size (im, 3) == 1)
sharp = USMGray (im, imsharpen_size, p.Results.Radius,
p.Results.Amount, p.Results.Threshold);
else
sharp = USMColor (im, imsharpen_size, p.Results.Radius,
p.Results.Amount, p.Results.Threshold);
endif
sharp = imcast (sharp, class (im));
endfunction
## UnSharp Masking of gray images
function [sharp] = USMGray (im, hsize, sigma, amount, thresh)
f = fspecial ("gaussian", hsize, sigma);
sharp = im2double (im);
filtered = imfilter (sharp, f, "replicate");
g = sharp - filtered;
if (thresh > 0)
absg = abs (g);
thresh *= max (absg(:));
g(absg <= thresh) = 0;
endif
sharp += amount*g;
endfunction
## UnSharp Masking of color images
## Transform image to CIELab color space, perform UnSharp Masking on L channel,
## and transform back to RGB.
function [sharp] = USMColor (im, hsize, sigma, amount, thresh)
lab = rgb2lab (im);
lab(:,:,1) = USMGray (lab(:,:,1), hsize, sigma, amount, thresh);
sharp = lab2rgb (lab);
endfunction
%!test
%! A = zeros (7, 7);
%! A(4,4) = 1;
%! B = [
%! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000
%! 0.00000 -0.00238 -0.01064 -0.01755 -0.01064 -0.00238 0.00000
%! 0.00000 -0.01064 -0.04771 -0.07866 -0.04771 -0.01064 0.00000
%! 0.00000 -0.01755 -0.07866 1.67032 -0.07866 -0.01755 0.00000
%! 0.00000 -0.01064 -0.04771 -0.07866 -0.04771 -0.01064 0.00000
%! 0.00000 -0.00238 -0.01064 -0.01755 -0.01064 -0.00238 0.00000
%! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000];
%! assert (imsharpen (A), B, 5e-6)
%!test
%! A = zeros (7, 7);
%! A(4,4) = 1;
%! B = [
%! -0.0035147 -0.0065663 -0.0095539 -0.0108259 -0.0095539 -0.0065663 -0.0035147
%! -0.0065663 -0.0122674 -0.0178490 -0.0202255 -0.0178490 -0.0122674 -0.0065663
%! -0.0095539 -0.0178490 -0.0259701 -0.0294280 -0.0259701 -0.0178490 -0.0095539
%! -0.0108259 -0.0202255 -0.0294280 1.7666538 -0.0294280 -0.0202255 -0.0108259
%! -0.0095539 -0.0178490 -0.0259701 -0.0294280 -0.0259701 -0.0178490 -0.0095539
%! -0.0065663 -0.0122674 -0.0178490 -0.0202255 -0.0178490 -0.0122674 -0.0065663
%! -0.0035147 -0.0065663 -0.0095539 -0.0108259 -0.0095539 -0.0065663 -0.0035147];
%! assert (imsharpen (A, "radius", 2), B, 5e-8)
%!test
%! A = zeros (7, 7);
%! A(4,4) = 1;
%! assert (imsharpen (A, "radius", 0.01), A)
%!test
%! A = zeros (7, 7);
%! A(4,4) = 1;
%! B = A;
%! B(3:5,3:5) = -0.000000000011110;
%! B(3:5,4) = -0.000002981278097;
%! B(4,3:5) = -0.000002981278097;
%! B(4,4) = 1.000011925156828;
%! assert (imsharpen (A, "radius", 0.2), B, eps*10)
%!test
%! A = zeros (7, 7);
%! A(4,4) = 1;
%! B = [
%! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000
%! 0.00000 -0.00297 -0.01331 -0.02194 -0.01331 -0.00297 0.00000
%! 0.00000 -0.01331 -0.05963 -0.09832 -0.05963 -0.01331 0.00000
%! 0.00000 -0.02194 -0.09832 1.83790 -0.09832 -0.02194 0.00000
%! 0.00000 -0.01331 -0.05963 -0.09832 -0.05963 -0.01331 0.00000
%! 0.00000 -0.00297 -0.01331 -0.02194 -0.01331 -0.00297 0.00000
%! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000];
%! assert (imsharpen (A, "amount", 1), B, 5e-6)
%!test
%! A = zeros (7, 7);
%! A(4,4) = 1;
%! B = zeros (7, 7);
%! B(4,4) = 1.670317742690299;
%! B(4,3) = -0.078656265079077;
%! B(3,4) = -0.078656265079077;
%! B(4,5) = -0.078656265079077;
%! B(5,4) = -0.078656265079077;
%! assert (imsharpen (A, "Threshold", 0.117341762), B, eps*10)
%!test
%! A = zeros (7, 7);
%! A(4,4) = 1;
%! B = zeros (7, 7);
%! B(4,4) = 1.670317742690299;
%! assert (imsharpen (A, "Threshold", 0.117341763), B, eps*10)
## uint8 test
%!test
%! A = zeros (7, 7, "uint8");
%! A(3:5,3:5) = 150;
%! B = zeros (7, 7, "uint8");
%! B(3:5,3:5) = 211;
%! B(4,3:5) = 195;
%! B(3:5,4) = 195;
%! B(4,4) = 175;
%! assert (imsharpen (A), B)
## uint8 test
%!test
%! A = zeros (7, 7, "uint8");
%! A(3:5,3:5) = 100;
%! B = zeros (7, 7, "uint8");
%! B(3:5,3:5) = 173;
%! assert (imsharpen (A, "radius", 4), B)
## color image test #1
%!test
%! A = zeros (7, 7, 3, "uint8");
%! A(4,4,:) = 255;
%! assert (imsharpen (A), A)
## Matlab result is different by 1 grayscale
%!xtest
%! A = zeros(7,7,3, "uint8");
%! A(4,4,1) = 255;
%! B = A;
%! B(4,4,2) = 146; # Octave result is 145;
%! B(4,4,3) = 100; # Octave result is 99;
%! assert (imsharpen (A), B)
## Matlab result is different by 1 grayscale
%!xtest
%! A = zeros (7, 7, 3, "uint8");
%! A(3:5,3:5,1) = 100;
%! A(3:5,3:5,2) = 150;
%! B = A;
%! B(3:5,3:5,1) = 164;
%! B(3:5,4,1) = 146; # Octave result is 147
%! B(4,3:5,1) = 146; # Octave result is 145
%! B(4,4,1) = 125; # Octave result is 126
%! B(3:5,3:5,2) = 213;
%! B(3:5,4,2) = 195; # Octave result is 196
%! B(4,3:5,2) = 195; # Octave result is 196
%! B(4,4,2) = 175;
%! B(3:5,3:5,3) = 79;
%! B(3:5,4,3) = 62;
%! B(4,3:5,3) = 62;
%! B(4,4,3) = 40; # Octave result is 39
%! assert (imsharpen (A), B)
## Test input validation
%!error imsharpen ()
%!error imsharpen (ones (3, 3), "Radius")
%!error imsharpen (ones (3, 3), "Radius", 0)
%!error imsharpen (ones (3, 3), "Amount", -1)
%!error imsharpen (ones (3, 3), "Threshold", 1.5)
%!error imsharpen (ones (3, 3), "Threshold", -1)
%!error imsharpen (ones (3, 3), "foo")
%!error imsharpen ("foo")
image-2.12.0/inst/PaxHeaders.25054/tiff_tag_read.m 0000644 0000000 0000000 00000000062 13615546210 016330 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/tiff_tag_read.m 0000644 0001750 0001750 00000033266 13615546210 021104 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2010-2012 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag})
## @deftypefnx {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag}, @var{ifd})
## @deftypefnx {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag}, "all")
## Read value of @var{tag}s from TIFF files.
##
## @var{file} must be a TIFF file and @var{tag} should be a tag ID. To check
## multiple tags, @var{tag} can be a vector. If @var{ifd} is supplied, only
## those IFDs (Image File Directory) will be read. As with @var{tag}, multiple
## IFDs can be checked by using a vector or with the string `all'. By default,
## only the first IFD is read.
##
## @var{value} and @var{offset} will be a matrix with a number of rows and
## columns equal to the number of @var{tag}s and @var{ifd}s requested. The index
## relate to the same order as the input. @var{offset} has the same structure as
## @var{value} and when equal to 1 its matching value on @var{value} will be an
## offset to a position in the file.
##
## @var{tag}s that can't be found will have a value of 0 and the corresponding
## @var{offset} will be 2.
##
## If an error occurs when reading @var{file} (such as lack of permissions of file
## is not a TIFF file), @var{offset} is set to -1 and @var{value} contains the
## error message.
##
## See the following examples:
## @example
## @group
## ## read value of tag 258 on IFD 1 (`off' will be 1 if `val' is an offset or 2 if not found)
## [val, off] = tiff_tag_read (filepath, 258);
## @end group
## @end example
##
## @example
## @group
## ## read value 258, 262, 254 o IFD 1 (`val' and `off' will be a 1x3 matrix)
## [val, off] = tiff_tag_read (filepath, [258 262 254]);
## if (off(1) == -1), error ("something happened: %s", val); endif
## off(2,1) # will be 1 if val(2,1) is an offset to a file position or 2 if tag was not found
## val(2,1) # value of tag 262 on IFD 1
## @end group
## @end example
##
## @example
## @group
## ## read value 258, 262, 254 on the first 10 IFDs 1 (`val' and `off' will be a 1x10 matrix)
## [val, off] = tiff_tag_read (filepath, [258 262 254], 1:10);
## val(2,5) # value of tag 262 on IFD 5
## @end group
## @end example
##
## @example
## @group
## ## read value 258, 262, 254 o IFD 1 (`val' and `off' will be a 1x3 matrix)
## [val, off] = tiff_tag_read (filepath, [258 262 254], "all");
## val(2,end) # value of tag 262 on the last IFD
## @end group
## @end example
##
## @seealso{imread, imfinfo}
## @end deftypefn
## Based on the documentation at
## * http://en.wikipedia.org/wiki/Tagged_Image_File_Format
## * http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
## * http://ibb.gsf.de/homepage/karsten.rodenacker/IDL/Lsmfile.doc
## * http://www.awaresystems.be/imaging/tiff/faq.html
##
## and the function tiff_read by F. Nedelec, EMBL (www.cytosim.org)
## * http://www.cytosim.org/misc/index.html
##
## Explanation of the TIFF file structure:
##
## The idea of multi-page images needs to be understood first. These allow one file
## to have multiple images. This may sound strange but consider situations such as
## an MRI scan (one file can then contain one scan which is multiple images across
## one of the axis) or time-lapse experiment (where one file is not unlike a movie).
## TIFF files support this by being like a container of single images, called IFD
## (Image File Directory). For each page there will be a single IFD. One can see a
## TIFF as an archive file of multiple images files that many times have a single file.
##
## Each TIFF file starts with a small header that identifies the file as TIFF. The
## header ends with the position on the file for the first IFD. Each IFD has multiple
## entries that hold information about the image of that IFD including where on the
## file is the actual image. Each IFD entry is identified by a tag. Each tag has a
## unique meaning; for example, the IFD entry with tag 259 will say the compression
## type (if any), of the image in that IFD.
##
## A TIFF file will always have at least one IFD and each IFD will always have at
## least one IFD entry.
##
## * On the TIFF image file header:
## bytes 00-01 --> byte order used within the file: "II" for little endian
## and "MM" for big endian byte ordering.
## bytes 02-03 --> number 42 that identifies the file as TIFF
## bytes 04-07 --> file offset (in bytes) of the first IFD (Image File Directory)
##
## Note: offset is always from the start of the file ("bof" in fread) and first
## byte has an offset of zero.
##
## * On a TIFF's IFD structure:
## bytes 00-01 --> number of entries (or tags or fields or directories)
## bytes 02-13 --> the IFD entry #0
## bytes 14+=11 -> the IFD entry #N. Each will have exactly 12 bytes (the
## number of IFD entries was specified at the start of the IFD)
## bytes XX-XX --> file offset for next IFD (last 4 bytes of the IFD) or 0
## if it's the last IFD
##
## * On an IFD entry structure:
## bytes 00-01 --> tag that identifies the entry
## bytes 02-03 --> entry type
## 1 --> BYTE (uint8)
## 2 --> ASCII
## 3 --> SHORT (uint16)
## 4 --> LONG (uint32)
## 5 --> RATIONAL (two LONGS)
## 6 --> SBYTE (int8)
## 7 --> UNDEFINED (8 bit)
## 8 --> SSHORT (int16)
## 9 --> SLONG (int32)
## 10 --> FLOAT (single IEEE precision)
## 11 --> DOUBLE (double IEEE precision)
## bytes 04-07 --> number of values (count)
## bytes 08-11 --> file offset (from the beginning of file) or value (only if
## it fits in 4 bytes). It is possible that the offset is for
## a structure and not a value so we return the offset
##
## Note: file offset of the value may point anywhere in the file, even after the image.
##
## Tags numbered >= 32768 are private tags
## Tags numbered on the 65000--65535 range are reusable tags
function [val, off] = tiff_tag_read (file, tag, ifd = 1)
if (nargin < 2 || nargin > 3)
print_usage;
elseif (!isnumeric (tag) || !isvector (tag))
error ("`tag' must be either a numeric scalar or vector with tags -- identifying number of a field");
elseif (!(ischar (ifd) && strcmpi (ifd, "all")) && !(isnumeric (ifd) && isvector (ifd) && all (ifd == fix (ifd)) && all (ifd > 0)))
error ("`ifd' must be either the string `all' or numeric scalar or vector of positive integers with the IFD index");
endif
[FID, msg] = fopen (file, "r", "native");
if (FID == -1)
[val, off] = bad_exit (FID, sprintf ("Unable to fopen '%s': %s.", file, msg));
return
endif
## read byte order
byte_order = fread (FID, 2, "char=>char")'; # if we are retrieving a char, we need to transpose to get the string
if (strcmp (byte_order, "II"))
arch = "ieee-le"; # IEEE little endian format
elseif (strcmp (byte_order,"MM"))
arch = "ieee-be"; # IEEE big endian format
else
[val, off] = bad_exit (FID, sprintf ("First 2 bytes of '%s' returned '%s'. For TIFF should either be 'II' or 'MM'. Are you sure it's a TIFF.", file, byte_order));
return
endif
## read number 42
nTIFF = fread (FID, 1, "uint16", arch);
if (nTIFF != 42)
[val, off] = bad_exit (FID, sprintf ("'%s' is not a TIFF (missing value 42 on header at offset 2. Instead got '%g').", file, tiff_id));
return
endif
if (ischar (ifd) && strcmpi (ifd, "all"))
all_ifd = true;
else
all_ifd = false;
endif
## default values for val and off
def_val = 0;
def_off = 2;
## start output values with default values
if (ischar (ifd) && strcmpi (ifd, "all"))
val = def_val * ones (numel (tag), 1);
off = def_off * ones (numel (tag), 1);
else
val = def_val * ones (numel (tag), numel (ifd));
off = def_off * ones (numel (tag), numel (ifd));
endif
## read offset for the first IFD and move into it
offset_IFD = fread (FID, 1, "uint32", arch);
cIFD = 1; # current IFD
while (offset_IFD != 0 && (all_ifd || any (ifd >= cIFD)))
status = fseek (FID, offset_IFD, "bof");
if (status != 0)
[val, off] = bad_exit (FID, sprintf ("error on fseek when moving to IFD #%g", cIFD));
return
endif
## if checking on all IFD, add one column to the output
if (all_ifd)
val(:, end+1) = def_val;
off(:, end+1) = def_off;
endif
## read number of entries (nTag) and look for the desired tag ID
nTag = fread (FID, 1, "uint16", arch); # number of tags in the IFD
cTag = 1; # current tag
while (nTag >= cTag)
tagID = fread (FID, 1, "uint16", arch); # current tag ID
if (any(tagID == tag)) # found one
## column number of this IFD in the output matrix:
## we don't know at start the number of IFD so if all IFD have been requested
## we can't find them in `ifd', we need to set the index for output manually
if (all_ifd)
iCol = cIFD;
else
iCol = (ifd == cIFD);
endif
[val(tagID == tag, iCol), ...
off(tagID == tag, iCol) ] = read_value (FID, arch); # read tag value
elseif (all (tag < tagID))
## tags are in numeric order so if they wanted tags are all below current tag ID
## we can jump over to the next IFD
skip_bytes = 10 + (12 * (nTag - cTag));
status = fseek (FID, skip_bytes, "cof"); # Move to the next IFD
break
else
status = fseek (FID, 10, "cof"); # Move to the next tag
if (status != 0)
[val, off] = bad_exit (FID, sprintf ("error on fseek when moving out of tag #%g (tagID %g) on IFD %g.", cTag, tagID, cIFD));
return
endif
endif
cTag++;
endwhile
offset_IFD = fread (FID, 1, "uint32", arch);
cIFD++;
endwhile
fclose (FID);
endfunction
function [val, off] = read_value (FID, arch)
position = ftell (FID);
field_type = fread (FID, 1, "uint16", arch);
count = fread (FID, 1, "uint32", arch);
switch (field_type)
case 1, nBytes = 1; precision = "uint8"; # BYTE = 8-bit unsigned integer
case 2, nBytes = 1; precision = "uchar"; # ASCII = 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero)
case 3, nBytes = 2; precision = "uint16"; # SHORT = 16-bit (2-byte) unsigned integer
case 4, nBytes = 4; precision = "uint32"; # LONG = 32-bit (4-byte) unsigned integer
case 5, nBytes = 8; precision = "uint32"; # RATIONAL = Two LONGs: the first represents the numerator of a fraction; the second, the denominator
case 6, nBytes = 1; precision = "int8"; # SBYTE = An 8-bit signed (twos-complement) integer
case 7, nBytes = 1; precision = "uchar"; # UNDEFINED = An 8-bit byte that may contain anything, depending on the definition of the field
case 8, nBytes = 2; precision = "int16"; # SSHORT = A 16-bit (2-byte) signed (twos-complement) integer
case 9, nBytes = 4; precision = "int32"; # SLONG = A 32-bit (4-byte) signed (twos-complement) integer
case 10, nBytes = 8; precision = "int32"; # SRATIONAL = Two SLONG’s: the first represents the numerator of a fraction, the second the denominator
case 11, nBytes = 4; precision = "float32"; # FLOAT = Single precision (4-byte) IEEE format
case 12, nBytes = 8; precision = "float64"; # DOUBLE = Double precision (8-byte) IEEE format
otherwise
## From the TIFF file specification (page 16, section 2: TIFF structure):
## "Warning: It is possible that other TIFF field types will be added in the
## future. Readers should skip over fields containing an unexpected field type."
##
## However, we only get to this point of the code if we are in the tag requested
## by the use so it makes sense to error if we don't supported it yet.
error ("TIFF type %i not supported", field_type);
endswitch
if ((nBytes*count) > 4)
off = true;
val = fread (FID, 1, "uint32", arch);
if (rem (val, 2) != 0) # file offset must be an even number
warning ("Found an offset with an odd value %g (offsets should always be even numbers.", val);
endif
else
off = false;
switch precision
case {5, 10} val = fread (FID, 2*count, precision, arch); val = val(1)/val(2); # the first represents the numerator of a fraction; the second, the denominator
case {2} val = fread (FID, count, [precision "=>char"], arch)'; # if we are retrieving a char, we need to transpose to get the string
otherwise val = fread (FID, count, precision, arch);
endswitch
## adjust position to end of IFD entry (not all take up 4 Bytes)
fseek (FID, 4 - (nBytes*count), "cof");
endif
endfunction
function [val, off] = bad_exit (FID, msg)
off = -1;
val = sprintf (msg);
fclose (FID);
endfunction
image-2.12.0/inst/PaxHeaders.25054/@imref3d 0000644 0000000 0000000 00000000132 13615547225 014755 x ustar 00 30 mtime=1580650133.830896045
30 atime=1580650134.434913451
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/ 0000755 0001750 0001750 00000000000 13615547225 017576 5 ustar 00carandraug carandraug 0000000 0000000 image-2.12.0/inst/@imref3d/PaxHeaders.25054/worldToIntrinsic.m 0000644 0000000 0000000 00000000062 13615546210 020520 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/worldToIntrinsic.m 0000644 0001750 0001750 00000006777 13615546210 023303 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {[@var{xIntrinsic}, @var{yIntrinsic}, @var{zIntrinsic}] =} worldToIntrinsic (@var{r}, @var{xWorld}, @var{yWorld}, @var{zWorld})
## Convert from world to intrinsic coordinates.
##
## Converts world coordinates @var{xWorld}, @var{yWorld} and @var{zWorld} to
## intrinsic coordinates @var{xIntrinsic}, @var{yIntrinsic} and @var{zIntrinsic}
## of an image associated with the spatial referencing object @var{r}. If a
## point (@var{xWorld}(i), @var{yWorld}(i), @var{zWorld}(i)) falls outside
## the bounds of the image, its intrinsic coordinates are extrapolated,
## possibly resulting in negative values.
##
## @seealso{imref2d, imref3d, intrinsicToWorld}
## @end deftypefn
function [xIntrinsic, yIntrinsic, zIntrinsic] = worldToIntrinsic (r, xWorld, yWorld, zWorld)
if (nargin != 4)
print_usage ();
endif
validateattributes (xWorld, {"numeric"}, ...
{"real"}, "imref3d", "xWorld");
validateattributes (yWorld, {"numeric"}, ...
{"real"}, "imref3d", "yWorld");
validateattributes (zWorld, {"numeric"}, ...
{"real"}, "imref3d", "zWorld");
if (! all (size (xWorld) == size (yWorld)) ...
|| ! all (size (xWorld) == size (zWorld)))
error ("Octave:invalid-input-arg", ...
"xWorld, yWorld and zWorld must be of the same size");
endif
xIntrinsicLimits = r.XIntrinsicLimits;
yIntrinsicLimits = r.YIntrinsicLimits;
zIntrinsicLimits = r.ZIntrinsicLimits;
xWorldLimits = r.XWorldLimits;
yWorldLimits = r.YWorldLimits;
zWorldLimits = r.ZWorldLimits;
xIntrinsic = xIntrinsicLimits(1) + (xWorld - xWorldLimits(1)) ...
/ r.PixelExtentInWorldX;
yIntrinsic = yIntrinsicLimits(1) + (yWorld - yWorldLimits(1)) ...
/ r.PixelExtentInWorldY;
zIntrinsic = zIntrinsicLimits(1) + (zWorld - zWorldLimits(1)) ...
/ r.PixelExtentInWorldZ;
endfunction
%!error id=Octave:invalid-fun-call worldToIntrinsic (imref3d)
%!error id=Octave:invalid-fun-call worldToIntrinsic (imref3d, 1, 2)
%!error id=Octave:invalid-fun-call worldToIntrinsic (imref3d, 1, 2, 3, 4)
%!error id=Octave:expected-real worldToIntrinsic (imref3d, 1j, 2, 3)
%!error id=Octave:expected-real worldToIntrinsic (imref3d, 1, 2j, 3)
%!error id=Octave:expected-real worldToIntrinsic (imref3d, 1, 2, 3j)
%!error id=Octave:invalid-input-arg worldToIntrinsic (imref3d, [1, 2], 3, 4)
%!error id=Octave:invalid-input-arg worldToIntrinsic (imref3d, 1, [2, 3], 4)
%!error id=Octave:invalid-input-arg worldToIntrinsic (imref3d, 1, 2, [3, 4])
%!test
%! r = imref3d ([128, 128, 27], 2, 2, 4);
%! xW = [108, 108, 108.2, 2];
%! yW = [92, 92, 92, -1];
%! zW = [52, 55, 52, 0.33];
%! [xI, yI, zI] = worldToIntrinsic (r, xW, yW, zW);
%! assert (xI, [54, 54, 54.1, 1], 1e-6)
%! assert (yI, [46, 46, 46, -0.5], 1e-6)
%! assert (zI, [13, 13.75, 13, 0.0825], 1e-6) image-2.12.0/inst/@imref3d/PaxHeaders.25054/worldToSubscript.m 0000644 0000000 0000000 00000000062 13615546210 020534 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/worldToSubscript.m 0000644 0001750 0001750 00000006742 13615546210 023307 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {[@var{i}, @var{j}, @var{k}] =} worldToSubscript (@var{r}, @var{xWorld}, @var{yWorld}, @var{zWorld})
## Convert world coordinates to row, column and plane subscripts.
##
## Converts world coordinates to row, column and plane subscripts of an image
## associated with the spatial referencing object @var{r}. A point located at
## (@var{xWorld}(i), @var{yWorld}(i), @var{zWorld}(i)) world coordinates maps
## to row, column and plane subscripts @var{i}(i), @var{j}(i), @var{k}(i)
## respectively. Note the reversed order of the first two dimensions. If the
## point falls outside the bounds of the image, all of its subscripts are NaN.
##
## @seealso{imref2d, imref3d, worldToIntrinsic}
## @end deftypefn
function [i, j, k] = worldToSubscript (r, xWorld, yWorld, zWorld)
if (nargin != 4)
print_usage ();
endif
validateattributes (xWorld, {"numeric"}, ...
{"real"}, "imref3d", "xWorld");
validateattributes (yWorld, {"numeric"}, ...
{"real"}, "imref3d", "yWorld");
validateattributes (zWorld, {"numeric"}, ...
{"real"}, "imref3d", "zWorld");
if (! all (size (xWorld) == size (yWorld)) ...
|| ! all (size (xWorld) == size (zWorld)))
error ("Octave:invalid-input-arg", ...
"xWorld, yWorld and zWorld must be of the same size");
endif
[xIntrinsic, yIntrinsic, zIntrinsic] ...
= worldToIntrinsic (r, xWorld, yWorld, zWorld);
xIntrinsicLimits = r.XIntrinsicLimits;
yIntrinsicLimits = r.YIntrinsicLimits;
zIntrinsicLimits = r.ZIntrinsicLimits;
inImage = contains (r, xWorld, yWorld, zWorld);
xIntrinsic(! inImage) = NaN;
yIntrinsic(! inImage) = NaN;
zIntrinsic(! inImage) = NaN;
i = round (yIntrinsic);
j = round (xIntrinsic);
k = round (zIntrinsic);
endfunction
%!error id=Octave:invalid-fun-call worldToSubscript (imref3d)
%!error id=Octave:invalid-fun-call worldToSubscript (imref3d, 1)
%!error id=Octave:invalid-fun-call worldToSubscript (imref3d, 1, 2)
%!error id=Octave:invalid-fun-call worldToSubscript (imref3d, 1, 2, 3, 4)
%!error id=Octave:expected-real worldToSubscript (imref3d, 1j, 2, 3)
%!error id=Octave:expected-real worldToSubscript (imref3d, 1, 2j, 3)
%!error id=Octave:expected-real worldToSubscript (imref3d, 1, 2, 3j)
%!error id=Octave:invalid-input-arg worldToSubscript (imref3d, [1, 2], 3, 4)
%!error id=Octave:invalid-input-arg worldToSubscript (imref3d, 1, [2, 3], 4)
%!error id=Octave:invalid-input-arg worldToSubscript (imref3d, 1, 2, [3, 4])
%!test
%! r = imref3d ([128, 128, 27], 2, 2, 4);
%! xW = [108, 108, 113.2, 2];
%! yW = [92, 92, 92, -1];
%! zW = [52, 55, 52, 0.33];
%! [rS, cS, pS] = worldToSubscript (r, xW, yW, zW);
%! assert (rS, [46, 46, 46, NaN])
%! assert (cS, [54, 54, 57, NaN])
%! assert (pS, [13, 14, 13, NaN]) image-2.12.0/inst/@imref3d/PaxHeaders.25054/contains.m 0000644 0000000 0000000 00000000062 13615546210 017021 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/contains.m 0000644 0001750 0001750 00000005576 13615546210 021600 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{tf} =} contains (@var{r}, @var{xWorld}, @var{yWorld}, @var{zWorld})
## Determine if image contains points in world coordinate system.
##
## Outputs a logical array @var{tf}, where the i-th nonzero value means the
## point (@var{xWorld}(i), @var{yWorld}(i), @var{zWorld}(i)) lies within the
## bounds of an image associated with a spatial referencing object @var{r}.
##
## @seealso{imref2d, imref3d}
## @end deftypefn
function tf = contains (r, xWorld, yWorld, zWorld)
if (nargin != 4)
print_usage();
endif
validateattributes (xWorld, {"numeric"}, ...
{"real"}, "imref3d", "xWorld");
validateattributes (yWorld, {"numeric"}, ...
{"real"}, "imref3d", "yWorld");
validateattributes (zWorld, {"numeric"}, ...
{"real"}, "imref3d", "zWorld");
if (! all (size (xWorld) == size (yWorld)) ...
|| ! all (size (xWorld) == size (zWorld)))
error ("Octave:invalid-input-arg", ...
"imref3d/contains: xWorld, yWorld and zWorld must be of the same size");
endif
xWorldLimits = r.XWorldLimits;
yWorldLimits = r.YWorldLimits;
zWorldLimits = r.ZWorldLimits;
containsX = xWorld >= xWorldLimits(1) & xWorld <= xWorldLimits(2);
containsY = yWorld >= yWorldLimits(1) & yWorld <= yWorldLimits(2);
containsZ = zWorld >= zWorldLimits(1) & zWorld <= zWorldLimits(2);
tf = containsX & containsY & containsZ;
endfunction
%!error id=Octave:invalid-fun-call contains (imref3d)
%!error id=Octave:invalid-fun-call contains (imref3d, 1)
%!error id=Octave:invalid-fun-call contains (imref3d, 1, 2)
%!error id=Octave:invalid-fun-call contains (imref3d, 1, 2, 3, 4)
%!error id=Octave:invalid-input-arg contains (imref3d, [1, 2], 3, 4)
%!error id=Octave:invalid-input-arg contains (imref3d, 1, [2, 3], 4)
%!error id=Octave:invalid-input-arg contains (imref3d, 1, 2, [3, 4])
%!error id=Octave:expected-real contains (imref3d, 1j, 2, 3)
%!error id=Octave:expected-real contains (imref3d, 1, 2j, 3)
%!error id=Octave:expected-real contains (imref3d, 1, 2, 3j)
%!test
%! r = imref3d ([128, 128, 27]);
%! assert (contains (r, [5, 6, 6, 8], [5, 10, 10, 257], [1, 27.5, 28, 1]), logical ([1, 1, 0, 0])) image-2.12.0/inst/@imref3d/PaxHeaders.25054/imref3d.m 0000644 0000000 0000000 00000000062 13615546210 016534 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/imref3d.m 0000644 0001750 0001750 00000032710 13615546210 021301 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{r} =} imref3d
## @deftypefnx {} {@var{r} =} imref3d (@var{imageSize})
## @deftypefnx {} {@var{r} =} imref3d (@var{imageSize}, @var{pixelExtentInWorldX}, @var{pixelExtentInWorldY}, @var{pixelExtentInWorldZ})
## @deftypefnx {} {@var{r} =} imref3d (@var{imageSize}, @var{xWorldLimits}, @var{yWorldLimits}, @var{zWorldLimits})
## Reference 3-D image to world coordinates.
##
## Creates an imref3d object referencing a 3-D m-by-n-by-p image with the size
## @var{imageSize} to world coordinates. The world extent is either given
## by @var{xWorldLimits}, @var{yWorldLimits} and @var{xWorldLimits} or computed
## from @var{pixelExtentInWorldX}, @var{pixelExtentInWorldY} and
## @var{pixelExtentInWorldZ}. @var{imageSize} is [2, 2, 2] by default.
##
## Intrinsic coordinates are x = 1.0, y = 1.0, z = 1.0 in the center of the
## top left pixel in the first plane and x = n, y = m, z = p in the center
## of the bottom right pixel in the last plane. Spatial resolution in each
## dimension can be different.
##
## imref3d object has the following properties:
##
## ImageSize - two element integer vector with image height and width
## in pixels.
##
## XWorldLimits - limits of the image along the x-axis in world units
## specified as a two element real vector @code{[xMin, xMax]}.
##
## YWorldLimits - limits of the image along the y-axis in world units
## specified as a two element real vector @code{[yMin, yMax]}.
##
## ZWorldLimits - limits of the image along the z-axis in world units
## specified as a two element real vector @code{[zMin, zMax]}.
##
## PixelExtentInWorldX - pixel extent along the x-axis in world units
## specified as a real scalar.
##
## PixelExtentInWorldY - pixel extent along the y-axis in world units
## specified as a real scalar.
##
## PixelExtentInWorldZ - pixel extent along the z-axis in world units
## specified as a real scalar.
##
## ImageExtentInWorldX - image extent along the x-axis in world units
## specified as a real scalar.
##
## ImageExtentInWorldY - image extent along the y-axis in world units
## specified as a real scalar.
##
## ImageExtentInWorldZ - image extent along the z-axis in world units
## specified as a real scalar.
##
## XIntrinsicLimits - limits of the image along the x-axis in intrinsic
## units, equals to @code{[n - 0.5, n + 0.5]}.
##
## YIntrinsicLimits - limits of the image along the y-axis in intrinsic
## units, equals to @code{[m - 0.5, m + 0.5]}.
##
## ZIntrinsicLimits - limits of the image along the z-axis in intrinsic
## units, equals to @code{[p - 0.5, p + 0.5]}.
##
## @seealso{imref2d}
## @end deftypefn
function r = imref3d (imageSize, varargin)
if (nargin > 4)
print_usage ();
endif
if (nargin == 0)
imageSize = [2, 2, 2];
elseif (nargin > 0)
validateattributes (imageSize, {"numeric"}, ...
{"positive", "integer", "vector", "size", [1, 3]}, "imref3d", "imageSize");
imageSize = imageSize(1:3);
endif
m = imageSize(1);
n = imageSize(2);
p = imageSize(3);
if (numel (varargin) == 0)
xWorldLimits = [0.5, n + 0.5];
yWorldLimits = [0.5, m + 0.5];
zWorldLimits = [0.5, p + 0.5];
r2 = @imref2d (imageSize);
elseif (numel (varargin) == 3)
if (isscalar (varargin{1}))
validateattributes (varargin{1}, {"numeric"}, ...
{"real", "positive", "scalar"}, "imref3d", "pixelExtentInWorldX");
validateattributes (varargin{2}, {"numeric"}, ...
{"real", "positive", "scalar"}, "imref3d", "pixelExtentInWorldY");
validateattributes (varargin{3}, {"numeric"}, ...
{"real", "positive", "scalar"}, "imref3d", "pixelExtentInWorldZ");
pixelExtentInWorldX = varargin{1};
pixelExtentInWorldY = varargin{2};
pixelExtentInWorldZ = varargin{3};
else
validateattributes (varargin{1}, {"numeric"}, ...
{"real", "increasing", "vector", "size", [1, 2]}, "imref3d", ...
"xWorldLimits");
validateattributes (varargin{2}, {"numeric"}, ...
{"real", "increasing", "vector", "size", [1, 2]}, "imref3d", ...
"yWorldLimits");
validateattributes (varargin{3}, {"numeric"}, ...
{"real", "increasing", "vector", "size", [1, 2]}, "imref3d", ...
"zWorldLimits");
xWorldLimits = varargin{1};
yWorldLimits = varargin{2};
zWorldLimits = varargin{3};
endif
endif
if (exist ("pixelExtentInWorldX") && exist ("pixelExtentInWorldY") ...
&& exist ("pixelExtentInWorldZ"))
imageExtentInWorldX = pixelExtentInWorldX * m;
imageExtentInWorldY = pixelExtentInWorldY * n;
imageExtentInWorldZ = pixelExtentInWorldZ * p;
xWorldLimits = [pixelExtentInWorldX / 2, imageExtentInWorldX + ...
pixelExtentInWorldX / 2];
yWorldLimits = [pixelExtentInWorldY / 2, imageExtentInWorldY + ...
pixelExtentInWorldY / 2];
zWorldLimits = [pixelExtentInWorldZ / 2, imageExtentInWorldZ + ...
pixelExtentInWorldZ / 2];
elseif (exist ("xWorldLimits") && exist ("yWorldLimits") ...
&& exist ("zWorldLimits"))
imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1);
imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1);
imageExtentInWorldZ = zWorldLimits(2) - zWorldLimits(1);
pixelExtentInWorldX = imageExtentInWorldX / n;
pixelExtentInWorldY = imageExtentInWorldY / m;
pixelExtentInWorldZ = imageExtentInWorldZ / p;
endif
xIntrinsicLimits = [0.5, n + 0.5];
yIntrinsicLimits = [0.5, m + 0.5];
zIntrinsicLimits = [0.5, p + 0.5];
r.ImageSize = imageSize;
r.XWorldLimits = xWorldLimits;
r.YWorldLimits = yWorldLimits;
r.ZWorldLimits = zWorldLimits;
r.PixelExtentInWorldX = pixelExtentInWorldX;
r.PixelExtentInWorldY = pixelExtentInWorldY;
r.PixelExtentInWorldZ = pixelExtentInWorldZ;
r.ImageExtentInWorldX = imageExtentInWorldX;
r.ImageExtentInWorldY = imageExtentInWorldY;
r.ImageExtentInWorldZ = imageExtentInWorldZ;
r.XIntrinsicLimits = xIntrinsicLimits;
r.YIntrinsicLimits = yIntrinsicLimits;
r.ZIntrinsicLimits = zIntrinsicLimits;
## in MATLAB imref3d isa imref2d
r = class (r, "imref3d", @imref2d());
endfunction
%!error id=Octave:invalid-fun-call imref3d (1, 2, 3, 4, 5)
%!error id=Octave:incorrect-size imref3d (42)
%!error id=Octave:incorrect-size imref3d ([42])
%!error id=Octave:incorrect-size imref3d ([4, 2])
%!error id=Octave:incorrect-size imref3d ([4, 2, 3, 3])
%!error id=Octave:expected-integer imref3d ([4.2, 42])
%!error id=Octave:expected-positive imref3d ([0, 0])
%!error id=Octave:expected-positive imref3d ([-4, 2])
%!error id=Octave:expected-positive imref3d ([4, 2, 3], 0, 1, 2)
%!error id=Octave:expected-positive imref3d ([4, 2, 3], 1, 0, 2)
%!error id=Octave:expected-positive imref3d ([4, 2, 3], 1, 2, 0)
%!error id=Octave:expected-real imref3d ([4, 2, 3], j, 1, 2)
%!error id=Octave:expected-real imref3d ([4, 2, 3], 1, j, 2)
%!error id=Octave:expected-real imref3d ([4, 2, 3], 1, 2, j)
%!error id=Octave:expected-real imref3d ([4, 2, 3], [j, 2], [3, 4], [5, 6])
%!error id=Octave:expected-real imref3d ([4, 2, 3], [1, 2], [j, 4], [5, 6])
%!error id=Octave:expected-real imref3d ([4, 2, 3], [1, 2], [3, 4], [5, j])
%!error id=Octave:expected-vector imref3d ([4, 2, 3], [], [], [])
%!error id=Octave:expected-vector imref3d ([4, 2, 3], [], [1], [2])
%!error id=Octave:expected-scalar imref3d ([4, 2, 3], [1], [], [])
%!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3, 4], [0])
%!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3, 4, 5], [6, 7])
%!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3, 4], [5, 6, 7])
%!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1; 2], [3, 4], [5, 6])
%!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3; 4], [5, 6])
%!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3, 4], [5; 6])
%!error id=Octave:invalid-indexing imref3d().InvalidProperty
%!error id=Octave:expected-increasing imref3d ([100, 200, 3], [1.5 0.5], [2.5, 3.5], [0.5, 1.5])
%!error id=Octave:expected-increasing imref3d ([100, 200, 3], [1.5 2.5], [2.5, 1.5], [0.5, 1.5])
%!error id=Octave:expected-increasing imref3d ([100, 200, 3], [1.5 2.5], [2.5, 3.5], [1.5, 0.5])
%!assert (imref3d ([4, 2, 3]).ImageSize, [4, 2, 3])
%!test
%! r = imref3d;
%! assert (r.XWorldLimits, [0.5, 2.5])
%! assert (r.YWorldLimits, [0.5, 2.5])
%! assert (r.ZWorldLimits, [0.5, 2.5])
%! assert (r.ImageSize, [2, 2, 2])
%! assert (r.PixelExtentInWorldX, 1)
%! assert (r.PixelExtentInWorldY, 1)
%! assert (r.PixelExtentInWorldZ, 1)
%! assert (r.ImageExtentInWorldX, 2)
%! assert (r.ImageExtentInWorldY, 2)
%! assert (r.ImageExtentInWorldZ, 2)
%! assert (r.XIntrinsicLimits, [0.5, 2.5])
%! assert (r.YIntrinsicLimits, [0.5, 2.5])
%! assert (r.ZIntrinsicLimits, [0.5, 2.5])
%!test
%! r = imref3d ([128, 128, 27]);
%! assert (r.XWorldLimits, [0.5, 128.5])
%! assert (r.YWorldLimits, [0.5, 128.5])
%! assert (r.ZWorldLimits, [0.5, 27.5])
%! assert (r.ImageSize, [128, 128, 27])
%! assert (r.PixelExtentInWorldX, 1)
%! assert (r.PixelExtentInWorldY, 1)
%! assert (r.PixelExtentInWorldZ, 1)
%! assert (r.ImageExtentInWorldX, 128)
%! assert (r.ImageExtentInWorldY, 128)
%! assert (r.ImageExtentInWorldZ, 27)
%! assert (r.XIntrinsicLimits, [0.5, 128.5])
%! assert (r.YIntrinsicLimits, [0.5, 128.5])
%! assert (r.ZIntrinsicLimits, [0.5, 27.5])
%!test
%! r = imref3d ([128, 128, 27], 2, 2, 4);
%! assert (r.XWorldLimits, [1, 257])
%! assert (r.YWorldLimits, [1, 257])
%! assert (r.ZWorldLimits, [2, 110])
%! assert (r.ImageSize, [128, 128, 27])
%! assert (r.PixelExtentInWorldX, 2)
%! assert (r.PixelExtentInWorldY, 2)
%! assert (r.PixelExtentInWorldZ, 4)
%! assert (r.ImageExtentInWorldX, 256)
%! assert (r.ImageExtentInWorldY, 256)
%! assert (r.ImageExtentInWorldZ, 108)
%! assert (r.XIntrinsicLimits, [0.5, 128.5])
%! assert (r.YIntrinsicLimits, [0.5, 128.5])
%! assert (r.ZIntrinsicLimits, [0.5, 27.5])
## changing ImageSize
%!test
%! r = imref3d;
%! assert (r.XWorldLimits, [0.5, 2.5])
%! assert (r.YWorldLimits, [0.5, 2.5])
%! assert (r.ZWorldLimits, [0.5, 2.5])
%! assert (r.ImageSize, [2, 2, 2])
%! assert (r.PixelExtentInWorldX, 1)
%! assert (r.PixelExtentInWorldY, 1)
%! assert (r.PixelExtentInWorldZ, 1)
%! assert (r.ImageExtentInWorldX, 2)
%! assert (r.ImageExtentInWorldY, 2)
%! assert (r.ImageExtentInWorldZ, 2)
%! assert (r.XIntrinsicLimits, [0.5, 2.5])
%! assert (r.YIntrinsicLimits, [0.5, 2.5])
%! assert (r.ZIntrinsicLimits, [0.5, 2.5])
%! r.ImageSize = [128, 128, 27];
%! assert (r.XWorldLimits, [0.5, 2.5])
%! assert (r.YWorldLimits, [0.5, 2.5])
%! assert (r.ZWorldLimits, [0.5, 2.5])
%! assert (r.ImageSize, [128, 128, 27])
%! assert (r.PixelExtentInWorldX, 0.015625, 1e-6)
%! assert (r.PixelExtentInWorldY, 0.015625, 1e-6)
%! assert (r.PixelExtentInWorldZ, 0.074074, 1e-6)
%! assert (r.ImageExtentInWorldX, 2)
%! assert (r.ImageExtentInWorldY, 2)
%! assert (r.ImageExtentInWorldZ, 2)
%! assert (r.XIntrinsicLimits, [0.5, 128.5])
%! assert (r.YIntrinsicLimits, [0.5, 128.5])
%! assert (r.ZIntrinsicLimits, [0.5, 27.5])
## changing XWorldLimits, YWorldLimits and ZWorldLimits
%!test
%! r = imref3d;
%! assert (r.XWorldLimits, [0.5, 2.5])
%! assert (r.YWorldLimits, [0.5, 2.5])
%! assert (r.ZWorldLimits, [0.5, 2.5])
%! assert (r.ImageSize, [2, 2, 2])
%! assert (r.PixelExtentInWorldX, 1)
%! assert (r.PixelExtentInWorldY, 1)
%! assert (r.PixelExtentInWorldZ, 1)
%! assert (r.ImageExtentInWorldX, 2)
%! assert (r.ImageExtentInWorldY, 2)
%! assert (r.ImageExtentInWorldZ, 2)
%! assert (r.XIntrinsicLimits, [0.5, 2.5])
%! assert (r.YIntrinsicLimits, [0.5, 2.5])
%! assert (r.ZIntrinsicLimits, [0.5, 2.5])
%! r.XWorldLimits = [-60, 13.33];
%! r.YWorldLimits = [-900.8, -560.26];
%! r.ZWorldLimits = [-302.48, 1500.333];
%! assert (r.XWorldLimits, [-60, 13.33])
%! assert (r.YWorldLimits, [-900.8, -560.26])
%! assert (r.ZWorldLimits, [-302.48, 1500.333])
%! assert (r.ImageSize, [2, 2, 2])
%! assert (r.PixelExtentInWorldX, 36.6650)
%! assert (r.PixelExtentInWorldY, 170.27, 1e-5)
%! assert (r.PixelExtentInWorldZ, 901.4065)
%! assert (r.ImageExtentInWorldX, 73.33, 1e-5)
%! assert (r.ImageExtentInWorldY, 340.54, 1e-5)
%! assert (r.ImageExtentInWorldZ, 1802.813, 1e-5)
%! assert (r.XIntrinsicLimits, [0.5, 2.5])
%! assert (r.YIntrinsicLimits, [0.5, 2.5])
%! assert (r.ZIntrinsicLimits, [0.5, 2.5])
%!test
%! r = imref3d;
%! fail ("r.XWorldLimits = []", "")
%! fail ("r.XWorldLimits = [1]", "")
%! fail ("r.XWorldLimits = [j]", "")
%! fail ("r.XWorldLimits = [1; 2]", "")
%! fail ("r.YWorldLimits = []", "")
%! fail ("r.YWorldLimits = [1]", "")
%! fail ("r.YWorldLimits = [j]", "")
%! fail ("r.YWorldLimits = [1; 2]", "")
%! fail ("r.ZWorldLimits = []", "")
%! fail ("r.ZWorldLimits = [1]", "")
%! fail ("r.ZWorldLimits = [j]", "")
%! fail ("r.ZWorldLimits = [1; 2]", "") image-2.12.0/inst/@imref3d/PaxHeaders.25054/intrinsicToWorld.m 0000644 0000000 0000000 00000000062 13615546210 020520 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/intrinsicToWorld.m 0000644 0001750 0001750 00000007755 13615546210 023300 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {[@var{xWorld}, @var{yWorld}, @var{zWorld}] =} intrinsicToWorld (@var{r}, @var{xIntrinsic}, @var{yIntrinsic}, @var{zIntrinsic})
## Convert from intrinsic to world coordinates.
##
## Converts intrinsic coordinates of the image associated with the spatial
## referencing object @var{r} to world coordinates @var{xWorld}, @var{yWorld}
## and @var{zWorld}. If a point
## (@var{xIntrinsic}(i), @var{yIntrinsic}(i), @var{zIntrinsic}(i))
## falls outside the intrinsic bounds of the image, the world coordinates are
## extrapolated, possibly resulting in negative values.
##
## @seealso{imref2d, imref3d, worldToIntrinsic}
## @end deftypefn
function [xWorld, yWorld, zWorld] = intrinsicToWorld (r, xIntrinsic, yIntrinsic, zIntrinsic)
if (nargin != 4)
print_usage ();
endif
validateattributes (xIntrinsic, {"numeric"}, ...
{"real"}, "imref3d", "xIntrinsic");
validateattributes (yIntrinsic, {"numeric"}, ...
{"real"}, "imref3d", "yIntrinsic");
validateattributes (zIntrinsic, {"numeric"}, ...
{"real"}, "imref3d", "zIntrinsic");
if (! all (size (xIntrinsic) == size (yIntrinsic)) ...
|| ! all (size (xIntrinsic) == size (zIntrinsic)))
error ("Octave:invalid-input-arg", ...
"xIntrinsic, yIntrinsic and zIntrinsic must be of the same size");
endif
xIntrinsicLimits = r.XIntrinsicLimits;
yIntrinsicLimits = r.YIntrinsicLimits;
zIntrinsicLimits = r.ZIntrinsicLimits;
xWorldLimits = r.XWorldLimits;
yWorldLimits = r.YWorldLimits;
zWorldLimits = r.ZWorldLimits;
xWorld = xWorldLimits(1) + r.PixelExtentInWorldX * ...
(xIntrinsic - xIntrinsicLimits(1));
yWorld = yWorldLimits(1) + r.PixelExtentInWorldY * ...
(yIntrinsic - yIntrinsicLimits(1));
zWorld = zWorldLimits(1) + r.PixelExtentInWorldZ * ...
(zIntrinsic - zIntrinsicLimits(1));
endfunction
%!error id=Octave:invalid-fun-call intrinsicToWorld (imref3d)
%!error id=Octave:invalid-fun-call intrinsicToWorld (imref3d, 1)
%!error id=Octave:invalid-fun-call intrinsicToWorld (imref3d, 1, 2)
%!error id=Octave:invalid-fun-call intrinsicToWorld (imref3d, 1, 2, 3, 4)
%!error id=Octave:expected-real intrinsicToWorld (imref3d, 1j, 2, 3)
%!error id=Octave:expected-real intrinsicToWorld (imref3d, 1, 2j, 3)
%!error id=Octave:expected-real intrinsicToWorld (imref3d, 1, j, 3j)
%!error id=Octave:invalid-input-arg intrinsicToWorld (imref3d, [1, 2], 3, 4)
%!error id=Octave:invalid-input-arg intrinsicToWorld (imref3d, 1, [2, 3], 4)
%!error id=Octave:invalid-input-arg intrinsicToWorld (imref3d, 1, 2, [3, 4])
%!test
%! r = imref3d ([128, 128, 27], 2, 2, 4);
%! xI = [54, 71, 57, 70];
%! yI = [46, 48, 79, 80];
%! zI = [13, 13, 13, 13];
%! [xW, yW, zW] = intrinsicToWorld (r, xI, yI, zI);
%! assert (xW, [108, 142, 114, 140])
%! assert (yW, [92, 96, 158, 160])
%! assert (zW, [52, 52, 52, 52])
%!test
%! [xW, yW, zW] = intrinsicToWorld (imref3d, -5.3, -2.8, -15.88);
%! assert (xW, -5.3)
%! assert (yW, -2.8)
%! assert (zW, -15.88, 1e-6)
%!test
%! [xW, yW, zW] = intrinsicToWorld (imref3d, [1, 2; 3, 4],
%! [2, 3; 5, 9],
%! [-5, 8; 19, 42.8]);
%! assert (xW, [1, 2; 3, 4])
%! assert (yW, [2, 3; 5, 9])
%! assert (zW, [-5, 8; 19, 42.8])
image-2.12.0/inst/@imref3d/PaxHeaders.25054/subsasgn.m 0000644 0000000 0000000 00000000062 13615546210 017030 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/subsasgn.m 0000644 0001750 0001750 00000007327 13615546210 021603 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{rout} =} subsasgn (@var{r}, @var{index}, @var{val})
##
## @seealso{}
## @end deftypefn
function rout = subsasgn (r, index, val)
switch (index.type)
case "."
fld = index.subs;
switch (fld)
case "ImageSize"
imageSize = val;
if (length (imageSize) < 3)
error ("Octave:invalid-input-arg", ...
"ImageSize must have three elements");
endif
validateattributes (imageSize, {"numeric"}, ...
{"positive", "integer", "vector"}, "imref3d", "imageSize");
m = imageSize(1);
n = imageSize(2);
p = imageSize(3);
rout = r;
rout.ImageSize = imageSize;
rout.PixelExtentInWorldX = r.ImageExtentInWorldX / n;
rout.PixelExtentInWorldY = r.ImageExtentInWorldY / m;
rout.PixelExtentInWorldZ = r.ImageExtentInWorldZ / p;
rout.XIntrinsicLimits = [0.5, n + 0.5];
rout.YIntrinsicLimits = [0.5, m + 0.5];
rout.ZIntrinsicLimits = [0.5, p + 0.5];
case "XWorldLimits"
xWorldLimits = val;
validateattributes (xWorldLimits, {"numeric"}, ...
{"increasing", "real", "vector", "size", [1, 2]}, ...
"imref3d", "xWorldLimits");
imageSize = r.ImageSize;
imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1);
rout = r;
rout.XWorldLimits = val;
rout.ImageExtentInWorldX = imageExtentInWorldX;
rout.PixelExtentInWorldX = imageExtentInWorldX / imageSize(2);
case "YWorldLimits"
yWorldLimits = val;
validateattributes (yWorldLimits, {"numeric"}, ...
{"increasing", "real", "vector", "size", [1, 2]}, ...
"imref3d", "yWorldLimits");
imageSize = r.ImageSize;
imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1);
rout = r;
rout.YWorldLimits = val;
rout.ImageExtentInWorldY = imageExtentInWorldY;
rout.PixelExtentInWorldY = imageExtentInWorldY / imageSize(1);
case "ZWorldLimits"
zWorldLimits = val;
validateattributes (zWorldLimits, {"numeric"}, ...
{"increasing", "real", "vector", "size", [1, 2]}, ...
"imref3d", "zWorldLimits");
imageSize = r.ImageSize;
imageExtentInWorldZ = zWorldLimits(2) - zWorldLimits(1);
rout = r;
rout.ZWorldLimits = val;
rout.ImageExtentInWorldZ = imageExtentInWorldZ;
rout.PixelExtentInWorldZ = imageExtentInWorldZ / imageSize(3);
otherwise
error ("Octave:invalid-indexing", ...
"@imref3d/subsasgn: invalid property '%s'", fld);
endswitch
otherwise
error ("Octave:invalid-indexing", "@imref3d/subsasgn: invalid index type")
endswitch
endfunction image-2.12.0/inst/@imref3d/PaxHeaders.25054/subsref.m 0000644 0000000 0000000 00000000062 13615546210 016654 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/subsref.m 0000644 0001750 0001750 00000004347 13615546210 021426 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{r} =} subref (@var{val}, @var{idx})
##
## @seealso{}
## @end deftypefn
function r = subsref (val, idx)
if (strcmp (idx(1).type, "."))
switch (idx(1).subs)
case "ImageSize"
r = val.ImageSize;
case "XWorldLimits"
r = val.XWorldLimits;
case "YWorldLimits"
r = val.YWorldLimits;
case "ZWorldLimits"
r = val.ZWorldLimits;
case "PixelExtentInWorldX"
r = val.PixelExtentInWorldX;
case "PixelExtentInWorldY"
r = val.PixelExtentInWorldY;
case "PixelExtentInWorldZ"
r = val.PixelExtentInWorldZ;
case "ImageExtentInWorldX"
r = val.ImageExtentInWorldX;
case "ImageExtentInWorldY"
r = val.ImageExtentInWorldY;
case "ImageExtentInWorldZ"
r = val.ImageExtentInWorldZ;
case "XIntrinsicLimits"
r = val.XIntrinsicLimits;
case "YIntrinsicLimits"
r = val.YIntrinsicLimits;
case "ZIntrinsicLimits"
r = val.ZIntrinsicLimits;
otherwise
error ("Octave:invalid-indexing", ...
strcat ("unknown property '", idx(1).subs, "' for class imref3d"));
endswitch
if (length (idx) > 1)
switch (idx(2).type)
case "()"
i = idx(2).subs;
r = r(i{1});
otherwise
error ("Octave:invalid-indexing", ...
strcat ("can't index '", idx(1).subs, "' with ", idx(2).type));
endswitch
endif
endif
endfunction
image-2.12.0/inst/@imref3d/PaxHeaders.25054/disp.m 0000644 0000000 0000000 00000000062 13615546210 016142 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref3d/disp.m 0000644 0001750 0001750 00000003347 13615546210 020713 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} disp (@var{r})
##
## @seealso{}
## @end deftypefn
function disp (r)
printf("%s with properties:\n", class (r));
printf("\n");
printf(" XWorldLimits: [%d %d]\n", r.XWorldLimits);
printf(" YWorldLimits: [%d %d]\n", r.YWorldLimits);
printf(" ZWorldLimits: [%d %d]\n", r.ZWorldLimits);
printf(" ImageSize: [%d %d %d]\n", r.ImageSize);
printf(" PixelExtentInWorldX: %d\n", r.PixelExtentInWorldX);
printf(" PixelExtentInWorldY: %d\n", r.PixelExtentInWorldY);
printf(" PixelExtentInWorldZ: %d\n", r.PixelExtentInWorldZ);
printf(" ImageExtentInWorldX: %d\n", r.ImageExtentInWorldX);
printf(" ImageExtentInWorldY: %d\n", r.ImageExtentInWorldY);
printf(" ImageExtentInWorldZ: %d\n", r.ImageExtentInWorldZ);
printf(" XIntrinsicLimits: [%d %d]\n", r.XIntrinsicLimits);
printf(" YIntrinsicLimits: [%d %d]\n", r.YIntrinsicLimits);
printf(" ZIntrinsicLimits: [%d %d]\n", r.ZIntrinsicLimits);
endfunction
image-2.12.0/inst/PaxHeaders.25054/im2bw.m 0000644 0000000 0000000 00000000062 13615546210 014572 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/im2bw.m 0000644 0001750 0001750 00000013474 13615546210 017345 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2000 Kai Habel
## Copyright (C) 2012-2016 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} im2bw (@var{img})
## @deftypefnx {Function File} {} im2bw (@var{X}, @var{cmap})
## @deftypefnx {Function File} {} im2bw (@dots{}, @var{threshold})
## @deftypefnx {Function File} {} im2bw (@dots{}, @var{method})
## Convert image to binary, black and white, by threshold.
##
## The input image @var{img} can either be a grayscale or RGB image. In the later
## case, @var{img} is first converted to grayscale with @code{rgb2gray}. Input
## can also be an indexed image @var{X} in which case the colormap @var{cmap}
## needs to be specified.
##
## The value of @var{threshold} should be in the range [0,1] independently of the
## class of @var{img}. Values from other classes can be converted to the correct
## value with @code{im2double}:
##
## @example
## bw = im2bw (img_of_class_uint8, im2double (thresh_of_uint8_class));
## @end example
##
## For an automatic threshold value, consider using @code{graythresh}.
## The argument @var{method} is a string that specifies a valid algorithm
## available in @code{graythresh}. The following are equivalent:
##
## @example
## bw = im2bw (img, "moments");
## bw = im2bw (img, graythresh (img, "moments"));
## @end example
##
## @seealso{graythresh, ind2gray, otsuthresh, rgb2gray}
## @end deftypefn
function BW = im2bw (img, cmap, thresh = 0.5)
if (nargin < 1 || nargin > 3)
print_usage ();
elseif (nargin == 3 && ! isind (img))
error ("im2bw: IMG must be an indexed image when are 3 input arguments");
elseif (nargin == 3 && ! iscolormap (cmap))
error ("im2bw: CMAP must be a colormap");
elseif (nargin == 2)
thresh = cmap;
endif
if (! isimage (img))
error ("im2bw: IMG must be an image");
elseif (! ischar (thresh) && ! (isnumeric (thresh) && isscalar (thresh)
&& thresh >= 0 && thresh <= 1))
error ("im2bw: THRESHOLD must be a string or a scalar in the interval [0 1]");
endif
if (islogical (img))
warning ("im2bw: IMG is already binary so nothing is done");
tmp = img;
else
## Convert img to gray scale
if (nargin == 3)
## indexed image (we already checked that is indeed indexed earlier)
img = ind2gray (img, cmap);
elseif (isrgb (img))
img = rgb2gray (img);
else
## Everything else, we do nothing, no matter how many dimensions
endif
if (ischar (thresh))
thresh = graythresh (img, thresh);
endif
## Convert the threshold value to same class as the image which
## is faster and saves more memory than the opposite.
if (isinteger (img))
## We do the conversion from double to int ourselves (instead
## of using im2uint* functions), because those functions round
## during the conversion but we need thresh to be the limit.
## See bug #46390.
cls = class(img);
I_min = double (intmin (cls));
I_range = double (intmax (cls)) - I_min;
thresh = cast (floor ((thresh * I_range) + I_min), cls);
elseif (isfloat (img))
## do nothing
else
## we should have never got here in the first place anyway
error ("im2bw: unsupported image of class '%s'", class (img));
endif
tmp = (img > thresh);
endif
if (nargout > 0)
BW = tmp;
else
imshow (tmp);
endif
endfunction
%!assert(im2bw ([0 0.4 0.5 0.6 1], 0.5), logical([0 0 0 1 1])); # basic usage
%!assert(im2bw (uint8 ([0 100 255]), 0.5), logical([0 0 1])); # with a uint8 input
## We use "bw = im2bw (...)" because otherwise it would display a figure
%!warning bw = im2bw (logical ([0 1 0]));
%!warning bw = im2bw (logical ([0 1 0]), 1);
%!test
%! warning ("off", "all", "local");
%! assert (im2bw (logical ([0 1 0])), logical ([0 1 0]))
%! assert (im2bw (logical ([0 1 0]), 0), logical ([0 1 0]))
%! assert (im2bw (logical ([0 1 0]), 1), logical ([0 1 0]))
## bug #46390 (on the rounding/casting of the threshold value)
%!assert (nnz (im2bw (uint8 ([0:255]), 0.9)), 26)
%!test
%! img = uint8 ([0:255]);
%! s = 0;
%! for i=0:.1:1
%! s += nnz (im2bw (img, i));
%! endfor
%! assert (s, 1405)
## threshold may be a negative value in the image class so care must
## taken when casting and rounding it.
%!assert (nnz (im2bw (int16 ([-128:127]), 0.499)), 194)
%!assert (nnz (im2bw (int16 ([-128:127]), 0.500)), 128)
%!assert (nnz (im2bw (int16 ([-128:127]), 0.501)), 62)
%!test
%! img = uint16 ([0:intmax("uint16")]);
%! s = 0;
%! for i=0:.1:1
%! s += nnz (im2bw (img, i));
%! endfor
%! assert (s, 360445)
%!test
%! img = int16 ([intmin("int16"):intmax("int16")]);
%! s = 0;
%! for i=0:.1:1
%! s += nnz (im2bw (img, i));
%! endfor
%! assert (s, 360445)
%!test
%! im = [((randn(10)/10)+.3) ((randn(10)/10)+.7)];
%! assert (im2bw (im, "Otsu"), im2bw (im, graythresh (im, "Otsu")))
%! assert (im2bw (im, "moments"), im2bw (im, graythresh (im, "moments")))
%!test
%! im = [((randn(10)/10)+.3) ((randn(10)/10)+.7)];
%! im = reshape (im, [10 10 1 2]);
%! assert (im2bw (im, "Otsu"), im2bw (im, graythresh (im, "Otsu")))
%! assert (im2bw (im, "moments"), im2bw (im, graythresh (im, "moments")))
image-2.12.0/inst/PaxHeaders.25054/colfilt.m 0000644 0000000 0000000 00000000062 13615546210 015206 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/colfilt.m 0000644 0001750 0001750 00000015444 13615546210 017760 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} colfilt (@var{A}, @var{block_size}, @var{block_type}, @var{func})
## @deftypefnx {Function File} {} colfilt (@var{A}, @var{block_size}, @var{subsize}, @var{block_type}, @var{func}, @dots{})
## @deftypefnx {Function File} {} colfilt (@var{A}, "indexed", @dots{})
## @deftypefnx {Function File} {} colfilt (@dots{}, @var{func}, @var{extra_args}, @dots{})
## Apply function to matrix blocks
##
## Executes the function @var{func} on blocks of size @var{block_size},
## taken from the matrix @var{A}. Both the matrix @var{A}, and the block
## can have any number of dimensions.
##
## The different blocks are organized into a matrix, each block as a
## single column, and passed as the first to the function handle
## @var{func}. Any input arguments to @code{colfilt} after @var{func}
## are passed to @var{func} after the blocks matrix.
##
## Blocks can be of two different types as defined by the string @var{block_type}:
##
## @table @asis
## @item @qcode{"distinct"}
## Each block is completely distinct from the other, with no overlapping
## elements. @var{func} must return a matrix of exactly the same size as
## its input.
##
## @item @qcode{"sliding"}
## Each possible block of size @var{block_size} inside @var{A} is used.
## @var{func} should act along the column dimension (be a column
## compression function) and return a vector of length equal to the
## number of columns of its input.
##
## @end table
##
## The optional argument @var{subsize} divides @var{A} into smaller pieces
## before generating the matrices with one block per column in order to
## save memory. It is currently only accepted for @sc{Matlab} compatibility.
##
## If @var{A} is an indexed image, the second argument should be the
## string @qcode{"indexed"} so that any required padding is done correctly.
## The padding value will be 0 except for indexed images of class uint8
## and uint16.
##
## This function is mostly useful to apply moving or sliding window filter
## when @var{block_type} is "sliding". However, for many cases, specialized
## functions perform much faster. For the following common cases, consider
## the suggested alternatives;
##
## @table @asis
## @item moving average
## A moving average filter is equivalent to convolve with a matrix
## of @code{1/@var{N}} sized @var{block_size}, where @var{N} is the total
## number of elements in a block. Use
## @code{convn (@var{A}, (1/@var{N}) * ones (@var{block_size}) *, "same")}
##
## @item maximum or minimum
## This is the equivalent to a dilation and erosion. Use @code{imdilate} or
## @code{imerode}.
##
## @item any or all
## Same as dilation and erosion but with logical input. Use @code{imdilate}
## or @code{imerode} with @code{logical (@var{A})}.
##
## @item median
## Use @code{medfilt2} if @var{A} is only 2 dimensional, and @code{ordfiltn}
## with the @code{floor (prod (@var{N}/ 2)} th element, where @var{N} is the
## total number of elements in a block (add 1 if it is an even number).
##
## @item sort or nth_element
## Use @code{ordfiltn}.
##
## @item standard deviation
## Use @code{stdfilt}.
##
## @item sum
## Use a matrix of 1 to perform convolution,
## @code{convn (@var{A}, ones (@var{block_size}), "same")}
##
## @end table
##
## @seealso{bestblk, blockproc, col2im, im2col, nlfilter}
## @end deftypefn
function B = colfilt (A, varargin)
## Input check
if (nargin < 4)
print_usage ();
endif
[p, block_size, padval] = im2col_check ("colfilt", nargin, A, varargin{:});
subsize = size (A);
if (numel (varargin) < p)
print_usage ();
elseif (isnumeric (varargin{p}) && isvector (varargin{p}))
subsize = varargin{p++};
subsize = postpad (subsize, ndims (A), 1);
subsize = min (subsize, size (A));
subsize = max (subsize, block_size);
endif
## We need at least 2 more arguments (block type and function)
if (numel (varargin) < p +1)
print_usage ();
endif
## Next one needs to be block type
block_type = varargin{p++};
if (! ischar (block_type))
error ("colfilt: BLOCK_TYPE must be a string");
endif
## followed by the function
func = varargin{p++};
if (! isa (func, "function_handle"))
error ("colfilt: FUNC must be a function handle");
endif
## anything after this are extra arguments to func
extra_args = varargin(p:end);
switch (tolower (block_type))
case "sliding"
## Function must return a single vector, one element per column,
## i.e., should act along the elements of each column.
## TODO for some large blocks, we may easily try to create matrix
## too large for Octave (see im2col documentation about the
## size). May be a good idea to split it into smaller images
## even if subsize is that large, so that we never go above
## sizemax ().
## However, this can be tricky. After splitting the image in
## smaller blocks, they can't be distinct, some parts need
## to overlap otherwise when we put them back together, we'll
## introduce many artifacts.
padded = pad_for_sliding_filter (A, block_size, padval);
cols = im2col (padded, block_size, "sliding");
B = col2im (func (cols, extra_args{:}), block_size, size (padded), "sliding");
case "distinct"
## Function must return a matrix with the same number of elements
## as its input, specially the same number of rows.
## One of the options of this function is to do the block
## processing already from big blocks from the original matrix
## in order to save memory. While this may make sense with
## sliding blocks, not so much here since cols will have the same
## size as A, and so will B.
cols = im2col (A, block_size, "distinct");
B = col2im (func (cols, extra_args{:}), block_size, size (A), "distinct");
otherwise
error ("colfilt: invalid BLOCK_TYPE `%s'.", block_type);
endswitch
endfunction
%!demo
%! ## Perform moving average filter with a 4x4 window
%! A = magic (12)
%! colfilt (A, [4 4], "sliding", @mean)
%!test
%! A = reshape (1:36, [6 6]);
%! assert (colfilt (A, [2 2], [3 3], "sliding", @sum),
%! conv2 (A, ones (2), "same"));
image-2.12.0/inst/PaxHeaders.25054/imregionalmax.m 0000644 0000000 0000000 00000000062 13615546210 016406 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imregionalmax.m 0000644 0001750 0001750 00000006306 13615546210 021155 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2014 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} imregionalmax (@var{img})
## @deftypefnx {Function File} {} imregionalmax (@var{img}, @var{conn})
## Compute regional maxima.
##
## Returns a logical matrix, same size as the input @var{img}, with the
## regional maxima.
##
## The optional argument @var{conn}, defines the connectivity. It can
## be a scalar value or a boolean matrix (see @code{conndef} for details).
## Defaults to @code{conndef (ndims (@var{img}), "maximal")}
##
## Regional maxima should not be mistaken with local maxima. Local maxima
## are pixels whose value is greater or equal to all of its neighbors.
## A regional maxima is the connected component of pixels whose values are
## all higher than the neighborhood of the maxima (the connected component,
## not its individual pixels).
## All pixels belonging to a regional maximum are local maxima, but the
## inverse is not true.
##
## @seealso{immaximas, imreconstruct, imregionalmin}
## @end deftypefn
function bw = imregionalmax (img, conn)
if (nargin < 1 || nargin > 2)
print_usage ();
endif
if (nargin < 2)
conn = conndef (ndims (img), "maximal");
else
conn = conndef (conn);
endif
if (islogical (img))
bw = img;
else
## we could probably still make this more efficient
if (isfloat (img))
recon = imreconstruct (img, img + __eps__ (img), conn);
else
recon = imreconstruct (img, img + 1, conn);
endif
bw = (recon == img);
endif
endfunction
%!test
%! a = [
%! 7 3 9 3 10 3
%! 4 2 3 10 1 3
%! 1 4 6 9 4 10
%! 8 7 9 3 4 8
%! 5 9 3 3 8 9
%! 3 6 9 4 1 10];
%!
%! a4 = [
%! 1 0 1 0 1 0
%! 0 0 0 1 0 0
%! 0 0 0 0 0 1
%! 1 0 1 0 0 0
%! 0 1 0 0 0 0
%! 0 0 1 0 0 1];
%! assert (imregionalmax (a, 4), logical (a4))
%! a8 = [
%! 1 0 0 0 1 0
%! 0 0 0 1 0 0
%! 0 0 0 0 0 1
%! 0 0 0 0 0 0
%! 0 0 0 0 0 0
%! 0 0 0 0 0 1];
%! assert (imregionalmax (a, 8), logical (a8))
%! assert (imregionalmax (a), logical (a8))
%!test
%! ## test float input images
%! im0 = peaks ();
%! im1 = im0 ./ 100;
%! max_pos_expected = [1000; 1214; 1691; 2353];
%! max0 = imregionalmax (im0);
%! max0_pos = find (max0);
%! max1 = imregionalmax (im1);
%! assert (max1, max0)
%! assert (max0_pos, max_pos_expected)
image-2.12.0/inst/PaxHeaders.25054/radon.m 0000644 0000000 0000000 00000000062 13615546210 014655 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/radon.m 0000644 0001750 0001750 00000004543 13615546210 017425 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2007 Alexander Barth
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {[@var{RT},@var{xp}] =} radon(@var{I}, @var{theta})
## @deftypefnx {Function File} {[@var{RT},@var{xp}] =} radon(@var{I})
##
## Calculates the 2D-Radon transform of the matrix @var{I} at angles given in
## @var{theta}. To each element of @var{theta} corresponds a column in @var{RT}.
## The variable @var{xp} represents the x-axis of the rotated coordinate.
## If @var{theta} is not defined, then 0:179 is assumed.
## @end deftypefn
function [RT,xp] = radon (I,theta)
## Input checking
if (nargin == 0 || nargin > 2)
print_usage ();
elseif (nargin == 1)
theta = 0:179;
endif
if (! isnumeric (I) || ndims (I) != 2)
error("radon: I must be a MxN numeric matrix");
endif
if (!isvector(theta))
error("radon: second input must be a vector");
endif
[m, n] = size (I);
# center of image
xc = floor ((m+1)/2);
yc = floor ((n+1)/2);
# divide each pixel into 2x2 subpixels
d = reshape (I,[1 m 1 n]);
d = d([1 1],:,[1 1],:);
d = reshape (d,[2*m 2*n])/4;
b = ceil (sqrt (sum (size (I).^2))/2 + 1);
xp = [-b:b]';
sz = size(xp);
[X,Y] = ndgrid (0.75 - xc + [0:2*m-1]/2,0.75 - yc + [0:2*n-1]/2);
X = X(:)';
Y = Y(:)';
d = d(:)';
th = theta*pi/180;
for l=1:length (theta)
# project each pixel to vector (-sin(th),cos(th))
Xp = -sin (th(l)) * X + cos (th(l)) * Y;
ip = Xp + b + 1;
k = floor (ip);
frac = ip-k;
RT(:,l) = accumarray (k',d .* (1-frac),sz) + accumarray (k'+1,d .* frac,sz);
endfor
endfunction
%!test
%! A = radon(ones(2,2),30);
%! assert (A,[0 0 0.608253175473055 2.103325780167649 1.236538105676658 0.051882938682637 0]',1e-10)
image-2.12.0/inst/PaxHeaders.25054/grayslice.m 0000644 0000000 0000000 00000000062 13615546210 015534 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/grayslice.m 0000644 0001750 0001750 00000022263 13615546210 020303 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2014-2018 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {} grayslice (@var{I})
## @deftypefnx {} {} grayslice (@var{I}, @var{n})
## @deftypefnx {} {} grayslice (@var{I}, @var{v})
## Create indexed image from intensity image using multilevel thresholding.
##
## The intensity image @var{I} is split into multiple threshold levels.
## For regularly spaced intervals, the number of levels can be specified as the
## numeric scalar @var{n} (defaults to 10), which will use the intervals:
##
## @tex
## \def\frac#1#2{{\begingroup#1\endgroup\over#2}}
## $$ \frac{1}{n}, \frac{2}{n}, \dots{}, \frac{n - 1}{n} $$
## @end tex
## @ifnottex
## @verbatim
## 1 2 n-1
## -, -, ..., ---
## n n n
## @end verbatim
## @end ifnottex
##
## For irregularly spaced intervals, a numeric vector @var{v} of
## threshold values can be used instead.
##
## The output image will be of class uint8 if the number of levels is
## less than 256, otherwise it will be double.
##
## @seealso{im2bw, gray2ind}
## @end deftypefn
function sliced = grayslice (I, n = 10)
if (nargin < 1 || nargin > 2)
print_usage ();
elseif (! isnumeric (n))
error ("Octave:invalid-invalid-input-arg",
"grayslice: N and V must be numeric");
endif
if (isscalar (n) && n >= 1)
## For Matlab compatibility, don't check if N is an integer but
## don't allow n < 1 either.
n = double (n);
v = (1:(n-1)) ./ n;
v = imcast (v, class (I));
elseif ((isvector (n) && ! isscalar (n)) || (isscalar (n) && n > 0 && n <1))
## For Matlab compatibility, a 0>N>1 is handled like V.
v = sort (n(:));
n = numel (v) + 1;
## The range is [0 1] but if the image is floating point we may
## need to increase the range (but never decrease it).
if (isfloat (I))
imax = max (I(:));
imin = min (I(:));
v(v < imin) = imin;
v(v > imax) = imax;
endif
else
if (isscalar (n) && n <= 0)
error ("Octave:invalid-invalid-input-arg",
"grayslice: N must be a positive number");
endif
error ("Octave:invalid-invalid-input-arg",
"grayslice: N and V must be a numeric scalar an vector");
endif
sliced_tmp = lookup (v, I);
if (n < 256)
sliced_tmp = uint8 (sliced_tmp);
else
## Indexed images of class double have indices base 1
sliced_tmp++;
endif
if (nargout < 1)
imshow (sliced_tmp, jet (n));
else
sliced = sliced_tmp;
endif
endfunction
%!test
%! expected = uint8 ([0 4 5 5 9]);
%! im = [0 0.45 0.5 0.55 1];
%! assert (grayslice (im), expected)
%! assert (grayslice (im, 10), expected)
%! assert (grayslice (im, uint8 (10)), expected)
%! assert (grayslice (im, [.1 .2 .3 .4 .5 .6 .7 .8 .9]), expected)
%!test
%! im = [0 0.45 0.5 0.55 1];
%! assert (grayslice (im, 2), uint8 ([0 0 1 1 1]))
%! assert (grayslice (im, 3), uint8 ([0 1 1 1 2]))
%! assert (grayslice (im, 4), uint8 ([0 1 2 2 3]))
%! assert (grayslice (im, [0 0.5 1]), uint8 ([1 1 2 2 3]))
%! assert (grayslice (im, [0.5 1]), uint8 ([0 0 1 1 2]))
%! assert (grayslice (im, [0.6 1]), uint8 ([0 0 0 0 2]))
%!test
%% ## non-integer values of N when N>1 are used anyway
%! im = [0 .55 1];
%! assert (grayslice (im, 9), uint8 ([0 4 8]))
%! assert (grayslice (im, 9.1), uint8 ([0 5 8]))
%! assert (grayslice (im, 10), uint8 ([0 5 9]))
## handle unsorted V
%!assert (grayslice ([0 .5 1], [0 1 .5]), uint8 ([1 2 3]))
%!test
%! ## 0 > N > 1 values are treated as if they are V and N=2
%! im = [0 .5 .55 .7 1];
%! assert (grayslice (im, .5), uint8 ([0 1 1 1 1]))
%! assert (grayslice (im, .51), uint8 ([0 0 1 1 1]))
%! assert (grayslice (im, .7), uint8 ([0 0 0 1 1]))
%! assert (grayslice (im, 1), uint8 ([0 0 0 0 0]))
%! assert (grayslice (im, 1.2), uint8 ([0 0 0 0 0]))
## V is outside the [0 1] and image range
%!assert (grayslice ([0 .5 .7 1], [0 .5 1 2]), uint8 ([1 2 2 4]))
## repeated values in V
%!assert (grayslice ([0 .45 .5 .65 .7 1], [.4 .5 .5 .7 .7 1]),
%! uint8 ([0 1 3 3 5 6]))
## Image an V with values outside [0 1] range
%!assert (grayslice ([-.5 .1 .8 1.2], [-1 -.4 .05 .6 .9 1.1 2]),
%! uint8 ([1 3 4 7]))
%!assert (grayslice ([0 .5 1], [-1 .5 1 2]), uint8 ([1 2 4]))
%!assert (grayslice ([-2 -1 .5 1], [-1 .5 1]), uint8 ([0 1 2 3]))
%!test
%! sliced = [
%! repmat(0, [26 1])
%! repmat(1, [25 1])
%! repmat(2, [26 1])
%! repmat(3, [25 1])
%! repmat(4, [26 1])
%! repmat(5, [25 1])
%! repmat(6, [26 1])
%! repmat(7, [25 1])
%! repmat(8, [26 1])
%! repmat(9, [26 1])
%! ];
%! sliced = uint8 (sliced(:).');
%! assert (grayslice (uint8 (0:255)), sliced)
%!assert (grayslice (uint8 (0:255), 255), uint8 ([0:254 254]))
## Returns class double if n >= 256 and not n > 256
%!assert (class (grayslice (uint8 (0:255), 256)), "double")
%!xtest
%! assert (grayslice (uint8 (0:255), 256), [1:256])
%!
%! ## While the above fails, this passes and should continue to do so
%! ## since it's the actual formula in the documentation.
%! assert (grayslice (uint8 (0:255), 256),
%! grayslice (uint8 (0:255), (1:255)./256))
%!test
%! ## Use of threshold in the [0 1] range for images of integer type does
%! ## not really work despite the Matlab documentation. It's Matlab
%! ## documentation that is wrong, see bug #55059
%!
%! assert (grayslice (uint8([0 100 200 255]), [.1 .4 .5]),
%! uint8 ([0 3 3 3]))
%! assert (grayslice (uint8([0 100 200 255]), [100 199 200 210]),
%! uint8 ([0 1 3 4]))
%!
%! ## P (penny) is a 2d image of class double in [1 255] range
%! q = warning ("query", "Octave:data-file-in-path");
%! warning ("off", "Octave:data-file-in-path");
%! load ("penny.mat");
%! warning (q.state, "Octave:data-file-in-path");
%! assert (grayslice (P), repmat (uint8 (9), size (P)))
%!function gs = test_grayslice_v (I, v)
%! ## This is effectively what grayslice does but slower with a for
%! ## loop internally.
%! gs = zeros (size (I));
%! for idx = 1:numel (v)
%! gs(I >= v(idx)) = idx;
%! endfor
%! if (numel (v) >= 256)
%! gs = gs +1;
%! else
%! gs = uint8 (gs);
%! endif
%!endfunction
%!test
%! q = warning ("query", "Octave:data-file-in-path");
%! warning ("off", "Octave:data-file-in-path");
%! load ("penny.mat");
%! warning (q.state, "Octave:data-file-in-path");
%!
%! ## The loaded P in penny.mat is of size 128x128, class double, and
%! ## with values in the [1 255] range
%! penny_uint8 = uint8 (P);
%! penny_double = im2double (penny_uint8); # rescales to [0 1] range]
%!
%! ## default of N = 10
%! expected = test_grayslice_v (penny_uint8,
%! [26 51 77 102 128 153 179 204 230]);
%! assert (grayslice (penny_uint8, 10), expected)
%! assert (grayslice (penny_uint8), expected)
%!
%! expected = test_grayslice_v (penny_double,
%! [.1 .2 .3 .4 .5 .6 .7 .8 .9]);
%! assert (grayslice (penny_double, 10), expected)
%! assert (grayslice (penny_double), expected)
%!test
%! ## For images with more than 2d
%! q = warning ("query", "Octave:data-file-in-path");
%! warning ("off", "Octave:data-file-in-path");
%! load ("penny.mat");
%! warning (q.state, "Octave:data-file-in-path");
%! penny_double = im2double (uint8 (P));
%! P_3d = repmat (penny_double, [1 1 3]);
%! P_5d = repmat (penny_double, [1 1 3 2 3]);
%!
%! v = [.3 .5 .7];
%! expected_2d = test_grayslice_v (penny_double, v);
%! assert (grayslice (P_3d, v), repmat (expected_2d, [1 1 3]))
%! assert (grayslice (P_5d, v), repmat (expected_2d, [1 1 3 2 3]))
%!test
%! q = warning ("query", "Octave:data-file-in-path");
%! warning ("off", "Octave:data-file-in-path");
%! load ("penny.mat");
%! warning (q.state, "Octave:data-file-in-path");
%! penny_double = uint8 (P);
%!
%! ## Test that change from uint8 to double happens at 256 exactly
%! assert (class (grayslice (penny_double, 255)), "uint8")
%! assert (class (grayslice (penny_double, 256)), "double")
%!
%! ## If returns in class double, it's +1.
%! v = [10 150 200];
%! v_long = [v 256:600];
%! assert (double (grayslice (penny_double, v)) +1,
%! grayslice (penny_double, v_long))
%!test
%! ## If there's a vector for floating point and goes outside the
%! ## range, it uses the last index of the vector.
%! q = warning ("query", "Octave:data-file-in-path");
%! warning ("off", "Octave:data-file-in-path");
%! load ("penny.mat");
%! warning (q.state, "Octave:data-file-in-path");
%! penny_double = im2double (uint8 (P));
%! v = [.3 .5 .7 2:10];
%! idx_1 = find (penny_double == 1);
%! assert (grayslice (penny_double, v)(idx_1), uint8 ([12; 12]))
%!error x = grayslice ([1 2; 3 4], 0)
%!error x = grayslice ([1 2; 3 4], -1)
%!error x = grayslice ([1 2; 3 4], "foo")
image-2.12.0/inst/PaxHeaders.25054/bwselect.m 0000644 0000000 0000000 00000000062 13615546210 015362 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/bwselect.m 0000644 0001750 0001750 00000002631 13615546210 020126 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 1999 Andy Adler
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {[@var{imout}, @var{idx}] =} bwselect(@var{im}, @var{cols}, @var{rows}, @var{connect})
## Select connected regions in a binary image.
##
## @table @code
## @item @var{im}
## binary input image
## @item [@var{cols}, @var{rows}]
## vectors of starting points (x,y)
## @item @var{connect}
## connectedness 4 or 8. default is 8
## @item @var{imout}
## the image of all objects in image im that overlap
## pixels in (cols,rows)
## @item @var{idx}
## index of pixels in imout
## @end table
## @end deftypefn
function [imout, idx] = bwselect (im, cols, rows, connect = 8)
[~, idx] = bwfill (! im, cols, rows, connect);
imout = false (size (im));
imout(idx) = true;
endfunction
image-2.12.0/inst/PaxHeaders.25054/immaximas.m 0000644 0000000 0000000 00000000062 13615546210 015537 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/immaximas.m 0000644 0001750 0001750 00000011173 13615546210 020304 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (c) 2003-2005 Peter Kovesi
## School of Computer Science & Software Engineering
## The University of Western Australia
## http://www.csse.uwa.edu.au/
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in
## all copies or substantial portions of the Software.
##
## The software is provided "as is", without warranty of any kind, express or
## implied, including but not limited to the warranties of merchantability,
## fitness for a particular purpose and noninfringement. In no event shall the
## authors or copyright holders be liable for any claim, damages or other
## liability, whether in an action of contract, tort or otherwise, arising from,
## out of or in connection with the software or the use or other dealings in the
## software.
##
## I've made minor changes compared to the original 'nonmaxsuppts' function developed
## by Peter Kovesi. The original is available at
## http://www.csse.uwa.edu.au/~pk/research/matlabfns/Spatial/nonmaxsuppts.m
## -- Søren Hauberg, 2008
## -*- texinfo -*-
## @deftypefn {Function File} {[@var{r}, @var{c}] =} immaximas (@var{im}, @var{radius})
## @deftypefnx{Function File} {[@var{r}, @var{c}] =} immaximas (@var{im}, @var{radius}, @var{thresh})
## @deftypefnx{Function File} {[@var{r}, @var{c}, @dots{}] =} immaximas (@dots{})
## @deftypefnx{Function File} {[@dots{}, @var{val}] =} immaximas (@dots{})
## Find local spatial maximas.
##
## Local spatial maximas should not be mistaken with regional maxima.
## See @code{imregionalmax} for the later.
##
## A local spatial maxima is
## defined as an image point with a value that is larger than all neighbouring
## values in a square region of width 2*@var{radius}+1. By default @var{radius}
## is 1, such that a 3 by 3 neighbourhood is searched. If the @var{thresh} input
## argument is supplied, only local maximas with a value greater than @var{thresh}
## are retained.
##
## The output vectors @var{r} and @var{c} contain the row-column coordinates
## of the local maximas. The actual values are computed to sub-pixel precision
## by fitting a parabola to the data around the pixel. If @var{im} is
## @math{N}-dimensional, then @math{N} vectors will be returned.
##
## If @var{im} is @math{N}-dimensional, and @math{N}+1 outputs are requested,
## then the last output will contain the image values at the maximas. Currently
## this value is not interpolated.
##
## @seealso{imregionalmax, ordfilt2, ordfiltn}
## @end deftypefn
function varargout = immaximas(im, radius, thresh)
## Check input
if (nargin == 0)
error("immaximas: not enough input arguments");
endif
if (nargin <= 1 || isempty(radius))
radius = 1;
endif
if (nargin <= 2)
thresh = [];
endif
if (! isnumeric (im))
error("immaximas: IM must be a numeric array");
endif
if (!isscalar(radius))
error("immaximas: second input argument must be a scalar or an empty matrix");
endif
if (!isscalar(thresh) && !isempty(thresh))
error("immaximas: third input argument must be a scalar or an empty matrix");
endif
## Find local maximas
nd = ndims(im);
s = size(im);
sze = 2*radius+1;
mx = ordfiltn(im, sze^nd, ones(repmat(sze,1, nd), "logical"), "reflect");
mx2 = ordfiltn(im, sze^nd-1, ones(repmat(sze,1, nd), "logical"), "reflect");
# Find maxima, threshold
immx = (im == mx) & (im != mx2);
if (!isempty(thresh))
immx &= (im>thresh);
endif
## Find local maximas and fit parabolas locally
ind = find(immx);
[sub{1:nd}] = ind2sub(s, ind);
if (!isempty(ind))
w = 1; # Width that we look out on each side of the feature point to fit a local parabola
ws = w*cumprod([1; s(:)]);
## We fit a parabola to the points in each dimension
for d = 1:nd
## Indices of points above, below, left and right of feature point
indminus1 = max(ind-ws(d), 1);
indplus1 = min(ind+ws(d), numel(immx));
## Solve quadratic
c = im(ind);
a = (im(indminus1) + im(indplus1))/2 - c;
b = a + c - im(indminus1);
shift = -w*b./(2*a); # Maxima of quadratic
## Move point
sub{d} += shift;
endfor
endif
## Output
varargout(1:nd) = sub(1:nd);
if (nargout > nd)
varargout{nd+1} = im(ind);
endif
endfunction
image-2.12.0/inst/PaxHeaders.25054/xyz2lab.m 0000644 0000000 0000000 00000000062 13615546210 015145 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/xyz2lab.m 0000644 0001750 0001750 00000011526 13615546210 017714 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2015 Hartmut Gimpel
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{lab} =} xyz2lab (@var{xyz})
## @deftypefnx {Function File} {@var{lab_map} =} xyz2lab (@var{xyz_map})
## Transform a colormap or image from CIE XYZ to CIE L*a*b* color space.
##
## A color in the CIE XYZ color space consists of three values X, Y and Z.
## Those values are designed to be colorimetric, meaning that their values
## do not depend on the display device hardware.
##
## A color in the CIE L*a*b* (or CIE Lab) space consists of lightness L* and
## two color-opponent dimensions a* and b*. The whitepoint is taken as D65.
## The CIE L*a*b* colorspace is also a colorimetric colorspace. It is designed
## to incorporate the human perception of color differences.
##
## Input values of class single and double are accepted.
## The shape and the class of the input are conserved.
##
## The return values of L* are normally in the inteval [0, 100]
## and the values of a* and b* in the interval [-127, 127]
##
## @seealso{lab2xyz, rgb2lab, rgb2hsv, rgb2ind, rgb2ntsc}
## @end deftypefn
## Author: Hartmut Gimpel
## algorithm taken from the following book:
## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015)
function lab = xyz2lab (xyz)
if (nargin != 1)
print_usage ();
endif
[xyz, cls, sz, is_im, is_nd, is_int] ...
= colorspace_conversion_input_check ("xyz2lab", "XYZ", xyz, 1);
# only accept single and double inputs because valid xyz values can be >1
## normalize with the whitepoint D65
## (reference: en.wikipedia.org/wiki/Illuminant_D65)
D65 = [0.95047, 1, 1.08883];
xyz_D65 = xyz ./ D65;
# Matlab truncates to D65_Matlab = [0.9504, 1.0000, 1.0888];
## transformation xyz -> xyz'
epsilon = (6/29)^3;
kappa = 1/116 * (29/3)^3;
xyz_prime = xyz_D65;
mask = xyz_D65 <= epsilon;
xyz_prime(mask) = kappa .* xyz_D65(mask) + 16/116;
xyz_prime(! mask) = xyz_D65(! mask) .^(1/3);
x_prime = xyz_prime(:,1);
y_prime = xyz_prime(:,2);
z_prime = xyz_prime(:,3);
## transformation xyz' -> lab
L = 116 .* y_prime - 16;
a = 500 .* (x_prime - y_prime);
b = 200 .* (y_prime - z_prime);
lab = [L, a, b];
# always return values of type double for Matlab compatibility (exception: type single)
lab = colorspace_conversion_revert (lab, cls, sz, is_im, is_nd, is_int, 1);
endfunction
## Test pure colors, gray and some other colors
## (This set of test values is taken from the book by Burger.)
%!assert (xyz2lab ([0, 0, 0]), [0 0 0], 5e-2)
%!assert (xyz2lab ([0.4125, 0.2127, 0.0193]), [53.24, 80.09, 67.20], 5e-2)
%!assert (xyz2lab ([0.7700, 0.9278, 0.1385]), [97.14, -21.55, 94.48], 5e-2)
%!assert (xyz2lab ([0.3576, 0.7152, 0.1192]), [87.74, -86.18, 83.18], 5e-2)
%!assert (xyz2lab ([0.5380, 0.7873, 1.0694]), [91.11, -48.09, -14.13], 5e-2)
%!assert (xyz2lab ([0.1804, 0.07217, 0.9502]), [32.30, 79.19, -107.86], 5e-2)
%!assert (xyz2lab ([0.5929, 0.28484, 0.9696]), [60.32, 98.24, -60.83], 5e-2)
%!assert (xyz2lab ([0.9505, 1.0000, 1.0888]), [100, 0.00, 0.00], 5e-2)
%!assert (xyz2lab ([0.2034, 0.2140, 0.2330]), [53.39, 0.00, 0.00], 5e-2)
%!assert (xyz2lab ([0.2155, 0.1111, 0.0101]), [39.77, 64.51, 54.13], 5e-2)
%!assert (xyz2lab ([0.0883, 0.0455, 0.0041]), [25.42, 47.91, 37.91], 5e-2)
%!assert (xyz2lab ([0.02094, 0.0108, 0.00098]), [9.66, 29.68, 15.24], 5e-2)
%!assert (xyz2lab ([0.5276, 0.3812, 0.2482]), [68.11, 48.39, 22.83], 5e-2)
## Test tolarant input checking on floats
%!assert (xyz2lab ([1.5 1 1]), [100, 82.15, 5.60], 5e-2)
%! xyz_map = rand (64, 3);
%! assert (lab2xyz (xyz2lab (xyz_map)), xyz_map, 1e-5);
%!test
%! xyz_img = rand (64, 64, 3);
%! assert (lab2xyz (xyz2lab (xyz_img)), xyz_img, 1e-5);
## support sparse input (the only useful xyz value with zeros is black)
%!assert (xyz2lab (sparse ([0 0 0])), [0 0 0], 5e-2)
## conserve class of single input
%!assert (class (xyz2lab (single([0.5 0.5 0.5]))), 'single')
## Test input validation
%!error xyz2lab ()
%!error xyz2lab (1,2)
%!error xyz2lab ({1})
%!error xyz2lab (ones (2,2))
## Test ND input
%!test
%! xyz = rand (16, 16, 3, 5);
%! lab = zeros (size (xyz));
%! for i = 1:5
%! lab(:,:,:,i) = xyz2lab (xyz(:,:,:,i));
%! endfor
%! assert (xyz2lab (xyz), lab)
image-2.12.0/inst/PaxHeaders.25054/bwareafilt.m 0000644 0000000 0000000 00000000062 13615546210 015672 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/bwareafilt.m 0000644 0001750 0001750 00000024512 13615546210 020440 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2014 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} bwareafilt (@var{bw}, @var{range})
## @deftypefnx {Function File} {} bwareafilt (@var{bw}, @var{n})
## @deftypefnx {Function File} {} bwareafilt (@var{bw}, @var{n}, @var{keep})
## @deftypefnx {Function File} {} bwareafilt (@dots{}, @var{conn})
## Filter objects from image based on their sizes.
##
## Returns a logical matrix with the objects of @var{bw} filtered based
## on their area (defined by thei number of pixels). This function is
## equivalent to @code{bwpropfilt (@var{bw}, "Area", @dots{})}.
##
## To filter objects with a value on a specific interval, @var{range} must be
## a two-element vector with the interval @code{[@var{low} @var{high}]}
## (values are inclusive).
##
## Alternatively, a scalar @var{n} will select the objects with the N highest
## values. The @var{keep} option defaults to @qcode{"largest"} but can also
## be set to @qcode{"smallest"} to select the N objects with lower values.
##
## The last optional argument, @var{conn}, can be a connectivity matrix, or
## the number of elements connected to the center (see @command{conndef}).
##
## @seealso{bwareaopen, bwlabel, bwlabeln, bwconncomp, bwpropfilt, regionprops}
## @end deftypefn
function bwfiltered = bwareafilt (bw, varargin)
if (nargin < 2 || nargin > 4)
print_usage ();
endif
bwfiltered = bwpropfilt (bw, "Area", varargin{:});
endfunction
%!shared a2d, a3d
%! a2d = [1 0 0 0 0 0 1 0 0 1
%! 1 0 0 1 0 1 0 1 0 1
%! 1 0 1 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0 0 0 0
%! 1 1 0 1 1 1 0 0 0 0
%! 1 1 0 1 0 0 0 1 0 0
%! 1 1 0 0 0 0 1 0 1 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 1 1 0 0 1];
%!
%! a3d = a2d;
%! a3d(:,:,2) = [
%! 0 0 0 0 0 0 0 0 0 0
%! 1 0 0 1 1 0 0 1 0 0
%! 0 0 0 1 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 1 1 0 0 0 0
%! 1 1 0 1 0 0 0 0 0 0
%! 1 0 0 0 0 0 1 0 0 0
%! 0 1 0 0 0 0 0 0 0 1
%! 1 1 0 0 0 0 1 0 0 0];
%!
%! a3d(:,:,3) = [
%! 1 0 0 0 0 0 0 0 0 0
%! 0 1 0 1 1 0 0 1 0 0
%! 0 0 0 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 1 1 1 0 0 0 0
%! 0 0 0 0 0 0 0 0 0 0
%! 1 0 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 1
%! 1 1 0 0 0 0 0 0 0 0];
%!test
%! f2d = [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 1 0 0 0 0 0 0 0 0
%! 1 1 0 1 1 1 0 0 0 0
%! 1 1 0 1 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0];
%! assert (bwareafilt (a2d, 2), logical (f2d));
%! assert (bwareafilt (a2d, 2, 8), logical (f2d));
%! assert (bwareafilt (a2d, 2, 4), logical (f2d));
%!test
%! f2d = [1 0 0 0 0 0 1 0 0 0
%! 1 0 0 0 0 1 0 1 0 0
%! 1 0 0 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0 0 0 0
%! 1 1 0 1 1 1 0 0 0 0
%! 1 1 0 1 0 0 0 1 0 0
%! 1 1 0 0 0 0 1 0 1 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0];
%! assert (bwareafilt (a2d, 5), logical (f2d));
%! assert (bwareafilt (a2d, 5, 8), logical (f2d));
%!test
%! f2d = [0 0 0 0 0 0 1 0 0 1
%! 0 0 0 1 0 1 0 1 0 1
%! 0 0 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 1 0 0
%! 0 0 0 0 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 1];
%! assert (bwareafilt (a2d, 11, "smallest", 4), logical (f2d));
%!test
%! f2d = [1 0 0 0 0 0 1 0 0 0
%! 1 0 0 0 0 1 0 1 0 0
%! 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 1 1 1 0 0 0 0
%! 0 0 0 1 0 0 0 1 0 0
%! 0 0 0 0 0 0 1 0 1 0
%! 0 0 0 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0 0 0];
%! assert (bwareafilt (a2d, [3 5]), logical (f2d));
%! assert (bwareafilt (a2d, [3 5], 8), logical (f2d));
%!test
%! f2d = [1 0 0 0 0 0 0 0 0 0
%! 1 0 0 0 0 0 0 0 0 0
%! 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 1 1 1 0 0 0 0
%! 0 0 0 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];
%! assert (bwareafilt (a2d, [3 4], 4), logical (f2d));
%! assert (bwareafilt (a2d, [3 4], [0 1 0; 1 1 1; 0 1 0]), logical (f2d));
%!test
%! f2d = [1 0 0 0 0 0 1 0 0 1
%! 1 0 0 1 0 1 0 1 0 1
%! 1 0 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 1 1 1 0 0 0 0
%! 0 0 0 1 0 0 0 1 0 0
%! 0 0 0 0 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];
%! assert (bwareafilt (a2d, [2 4]), logical (f2d));
%! assert (bwareafilt (a2d, [2 4], 8), logical (f2d));
%! assert (bwareafilt (a2d, [2 4], ones (3)), logical (f2d));
%!test
%! f3d = [0 0 0 0 0 0 1 0 0 0
%! 0 0 0 1 0 1 0 1 0 0
%! 0 0 1 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0];
%!
%! f3d(:,:,2) = [
%! 0 0 0 0 0 0 0 0 0 0
%! 0 0 0 1 1 0 0 1 0 0
%! 0 0 0 1 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 0 0 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0];
%!
%! f3d(:,:,3) = [
%! 0 0 0 0 0 0 0 0 0 0
%! 0 0 0 1 1 0 0 1 0 0
%! 0 0 0 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
%! 1 0 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0];
%! assert (bwareafilt (a3d, 2), logical (f3d));
%! assert (bwareafilt (a3d, 2, 26), logical (f3d));
%! assert (bwareafilt (a3d, 2, ones (3, 3, 3)), logical (f3d));
%!test
%! f3d = [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 1 0 0 0 0 0 0 0 0
%! 1 1 0 1 1 1 0 0 0 0
%! 1 1 0 1 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0];
%!
%! f3d(:,:,2) = [
%! 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 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 1 1 0 0 0 0
%! 1 1 0 1 0 0 0 0 0 0
%! 1 0 0 0 0 0 0 0 0 0
%! 0 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0];
%!
%! f3d(:,:,3) = [
%! 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 1 1 1 0 0 0 0
%! 0 0 0 0 0 0 0 0 0 0
%! 1 0 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0
%! 1 1 0 0 0 0 0 0 0 0];
%! assert (bwareafilt (a3d, 2, 6), logical (f3d));
%! assert (bwareafilt (a3d, 2, conndef (3, "minimal")), logical (f3d));
image-2.12.0/inst/PaxHeaders.25054/im2col.m 0000644 0000000 0000000 00000000062 13615546210 014737 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/im2col.m 0000644 0001750 0001750 00000021372 13615546210 017506 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} im2col (@var{A}, @var{block_size})
## @deftypefnx {Function File} {} im2col (@var{A}, @var{block_size}, @var{block_type})
## @deftypefnx {Function File} {} im2col (@var{A}, "indexed", @dots{})
## Rearrange blocks from matrix into columns.
##
## Rearranges blocks of size @var{block_size}, sampled from the matrix @var{A},
## into a serie of columns. This effectively transforms any image into a
## 2 dimensional matrix, a block per column, which can then be passed to
## other functions that perform calculations along columns.
##
## Both blocks and matrix @var{A} can have any number of dimensions (though
## for sliding blocks, a block can't be larger than @var{A} in any dimension).
## Blocks are always accessed in column-major order (like Octave arrays are
## stored) so that a matrix can be easily reconstructed with @code{reshape}
## and @code{col2im}. For a 2 dimensional matrix, blocks are taken first from
## the top to the bottom, and then from the left to the right of the matrix.
##
## The sampling can be performed in two different ways as defined by
## @var{block_type} (defaults to @qcode{"sliding"}):
##
## @table @asis
## @item @qcode{"distinct"}
## Each block is completely distinct from the other, with no overlapping
## elements. The matrix @var{A} is padded as required with a value of 0
## (or 1 for non-integer indexed images).
##
## @item @qcode{"sliding"}
## A single block slides across @var{A} without any padding.
##
## While this can be used to perform sliding window operations such as maximum
## and median filters, specialized functions such as @code{imdilate} and
## @code{medfilt2} will be more efficient.
##
## Note that large images being arranged in large blocks can easily exceed the
## maximum matrix size (see @code{sizemax}). For example, a matrix @var{A} of
## size 500x500, with sliding block of size [100 100], would require a matrix
## with 2.4108e+09 elements, i.e., the number of elements in a block,
## @code{100*100}, times the number of blocks, @code{(500-10+1) * (500-10+1)}.
##
## @end table
##
## If @var{A} is an indexed image, the second argument should be the
## string @qcode{"indexed"} so that any required padding is done correctly.
## The padding value will be 0 except for indexed images of class uint8
## and uint16.
##
## @seealso{blockproc, bestblk, col2im, colfilt, nlfilter, reshape}
## @end deftypefn
## Matlab behaves weird with N-dimensional images. It ignores block_size
## elements after the first 2, and treat N-dimensional as if the extra
## dimensions were concatenated horizontally. We are performing real
## N-dimensional conversion of image blocks into colums.
function B = im2col (A, varargin)
## Input check
if (nargin > 4)
print_usage ();
endif
[p, block_size, padval] = im2col_check ("im2col", nargin, A, varargin{:});
if (nargin > p)
## we have block_type param
if (! ischar (varargin{p}))
error("im2col: BLOCK_TYPE must be a string");
endif
block_type = varargin{p++};
else
block_type = "sliding";
endif
if (nargin > p)
print_usage ();
endif
## After all the input check, start the actual im2col. The idea is to
## calculate the linear indices for each of the blocks (using broadcasting
## for each dimension), and then reshape it with one block per column.
switch (tolower (block_type))
case "distinct"
## We may need to expand the size vector to include singletons
size_singletons = @(x, ndim) postpad (size (x), ndim, 1);
## Calculate needed padding
A_size = size_singletons (A, numel (block_size));
sp = mod (-A_size, block_size);
if (any (sp))
A = padarray (A, sp, padval, "post");
endif
A_size = size_singletons (A, numel (block_size));
## Get linear indixes for the first block
[ind, stride] = get_1st_ind (A_size, block_size);
## Get linear indices for all of the blocks
blocks = A_size ./ block_size;
step = stride .* block_size;
limit = step .* (blocks -1);
for dim = 1:numel (A_size)
ind = ind(:) .+ (0:step(dim):limit(dim));
endfor
n_blocks = prod (blocks);
case "sliding"
if (numel (block_size) > ndims (A))
error ("im2col: BLOCK_SIZE can't have more elements than the dimensions of A");
endif
## Get linear indixes for the first block
[ind, stride] = get_1st_ind (size (A), block_size);
## Get linear indices for all of the blocks
slides = size (A) - block_size;
limit = stride .* slides;
for dim = 1:ndims (A)
## We need to use bsxfun here because of
## https://savannah.gnu.org/bugs/?47085
ind = bsxfun (@plus, ind(:), (0:stride(dim):limit(dim)));
endfor
n_blocks = prod (slides +1);
otherwise
error ("im2col: invalid BLOCK_TYPE `%s'.", block_type);
endswitch
B = reshape (A(ind(:)), prod (block_size), n_blocks);
endfunction
## Get linear indices and for the first block, and stride size per dimension
function [ind, stride] = get_1st_ind (A_size, block_size)
stride = [1 cumprod(A_size(1:end-1))];
limit = (block_size -1) .* stride;
ind = 1;
for dim = 1:numel (A_size)
ind = ind(:) .+ (0:stride(dim):limit(dim));
endfor
endfunction
%!demo
%! ## Divide A using distinct blocks and then reverse the operation
%! A = [ 1:10
%! 11:20
%! 21:30
%! 31:40];
%! B = im2col (A, [2 5], "distinct")
%! C = col2im (B, [2 5], [4 10], "distinct")
## test default block type
%!test
%! a = rand (10);
%! assert (im2col (a, [5 5]), im2col (a, [5 5], "sliding"))
## indexed makes no difference when sliding
%!test
%! a = rand (10);
%! assert (im2col (a, [5 5]), im2col (a, "indexed", [5 5]))
%!error im2col (rand (20), [2 5], 10)
%!error im2col (rand (20), [2 5], "wrong_block_type")
%!error im2col (rand (10), [5 5], "sliding", 5)
%!error im2col (rand (10), "indexed", [5 5], "sliding", 5)
%!shared B, A, Bs, As, Ap, Bp0, Bp1, Bp0_3s
%! v = [1:10]';
%! r = reshape (v, 2, 5);
%! B = [v v+20 v+40 v+10 v+30 v+50];
%! A = [r r+10; r+20 r+30; r+40 r+50];
%! As = [ 1 2 3 4 5
%! 6 7 8 9 10
%! 11 12 13 14 15];
%! b1 = As(1:2, 1:4)(:);
%! b2 = As(2:3, 1:4)(:);
%! b3 = As(1:2, 2:5)(:);
%! b4 = As(2:3, 2:5)(:);
%! Bs = [b1, b2, b3, b4];
%! Ap = A(:, 1:9);
%! Bp1 = Bp0 = B;
%! Bp0(9:10, 4:6) = 0;
%! Bp1(9:10, 4:6) = 1;
%! Bp0_3s = Bp0;
%! Bp0_3s(11:30, :) = 0;
## test distinct block type
%!assert (im2col (A, [2 5], "distinct"), B);
## padding for distinct
%!assert (im2col (Ap, [2 5], "distinct"), Bp0);
%!assert (im2col (Ap, [2 5 3], "distinct"), Bp0_3s);
%!assert (im2col (Ap, "indexed", [2 5], "distinct"), Bp1);
%!assert (im2col (uint8 (Ap), "indexed", [2 5], "distinct"), uint8 (Bp0));
%!assert (im2col (uint16 (Ap), "indexed", [2 5], "distinct"), uint16 (Bp0));
%!assert (im2col (int16 (Ap), "indexed", [2 5], "distinct"), int16 (Bp1));
%!assert (im2col (uint32 (Ap), "indexed", [2 5], "distinct"), uint32 (Bp1));
## Always return correct class
%!assert (im2col (uint8 (A), [2 5], "distinct"), uint8 (B));
%!assert (im2col (single (A), [2 5], "distinct"), single (B));
%!assert (im2col (logical (A), [2 5], "distinct"), logical (B));
%!assert (im2col (uint8 (As), [2 4], "sliding"), uint8 (Bs));
%!assert (im2col (single (As), [2 4], "sliding"), single (Bs));
%!assert (im2col (logical (As), [2 4], "sliding"), logical (Bs));
## test sliding block type
%!assert (im2col (As, [2 4], "sliding"), Bs);
%!assert (im2col (As, [3 5], "sliding"), As(:));
## Test N-dimensional
%!test
%! A = randi (9, 10, 9, 5);
%!assert (convn (A, ones (3, 3, 3), "valid"),
%! reshape (sum (im2col (A, [3 3 3])), [8 7 3]));
%!
%! A = randi (9, 10, 9, 5, 7);
%!assert (convn (A, ones (3, 3, 3), "valid"),
%! reshape (sum (im2col (A, [3 3 3])), [8 7 3 7]));
%!assert (convn (A, ones (3, 4, 3), "valid"),
%! reshape (sum (im2col (A, [3 4 3])), [8 6 3 7]));
%!assert (convn (A, ones (3, 5, 3, 2), "valid"),
%! reshape (sum (im2col (A, [3 5 3 2])), [8 5 3 6]));
## Corner case for Matlab compatibility -- bug #46774
%!assert (im2col (1:8, [2 1]), zeros (2, 0))
image-2.12.0/inst/PaxHeaders.25054/wavelength2rgb.m 0000644 0000000 0000000 00000000062 13615546210 016473 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/wavelength2rgb.m 0000644 0001750 0001750 00000014441 13615546210 021241 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2011 William Krekeler
## Copyright (C) 2012 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{rgb} =} wavelength2rgb (@var{wavelength})
## @deftypefnx {Function File} {@var{rgb} =} wavelength2rgb (@var{wavelength}, @var{class})
## @deftypefnx {Function File} {@var{rgb} =} wavelength2rgb (@var{wavelength}, @var{class}, @var{gamma})
## Convert wavelength in nm into an RGB value set.
##
## Given a N-dimensional matrix @var{wavelength} with color values in nm, returns
## a RGB image with N+3 dimensions.
##
## @group
## @example
## wavelength2rgb (400)
## @result{} [0.51222 0.00000 0.70849]
##
## wavelength2rgb ([400 410])
## @result{(:,:,1)} 0.51222 0.49242
## @result{(:,:,2)} 0 0
## @result{(:,:,3)} 0.70849 0.85736
## @end example
## @end group
##
## The @var{rgb} class can be specified with @var{class}. Possible values are
## double (default), single, uint8, uint16, and int16.
##
## @group
## @example
## wavelength2rgb (400)
## @result{} 0.51222 0.00000 0.70849
##
## wavelength2rgb (400, "uint8")
## @result{} 131 0 181
## @end example
## @end group
##
## The luminance of colors can be adjusted with @var{gamma} which must a scalar
## value in the range [0 1]. Defaults to 0.8.
##
## Reference:
## @itemize @bullet
## @item @uref{http://stackoverflow.com/questions/2374959/algorithm-to-convert-any-positive-integer-to-an-rgb-value}
## @item @uref{http://www.midnightkite.com/color.html} per Dan Bruton
## @end itemize
## @end deftypefn
function rgb = wavelength2rgb (wavelength, out_class = "double", gamma = 0.8)
if (nargin < 1 || nargin > 3)
print_usage;
elseif (!isnumeric (wavelength) || any (wavelength <= 0))
error ("wavelength2rgb: wavelength must a positive numeric");
elseif (!ischar (out_class) || all (!strcmpi (out_class, {"single", "double", "uint8", "uint16", "int16"})))
error ("wavelength2rgb: unsupported class `%s'", char (out_class));
elseif (!isnumeric (gamma) || !isscalar (gamma) || gamma > 1 || gamma < 0)
error ("wavelength2rgb: gamma must a numeric scalar between 1 and 0");
endif
## initialize rgb. One extra dimension of size 3 for RGB. Later on, we will
## use ndims and when input is a scalar, ndims still returns 2 which means
## output would be 1x1x3. Check this and adjust later on
if (isscalar (wavelength))
rgb = zeros (1, 3);
size_adjust = 1;
else
rgb = zeros ([size(wavelength), 3]);
size_adjust = 0;
endif
## this RGBmask's will be used for broadcasting later
Rmask = Gmask = Bmask = false ([ones(1, ndims (wavelength) - size_adjust) 3]);
Rmask(1) = true;
Gmask(2) = true;
Bmask(3) = true;
## for each group of wavelengths we calculate the mask, expand for the 3
## channels and use Rmask, Gmask and Bmask with broadcasting to select the
## right one. Will skip some channels since their values would be zero and
## we already initialized the matrix with zeros()
get_rgb_mask = @(mask) repmat (mask, [ones(1, ndims (mask) - size_adjust) 3]);
mask = wavelength >= 380 & wavelength < 440;
rgbmask = get_rgb_mask (mask);
rgb(rgbmask & Rmask) = -(wavelength(mask) - 440) / 60; # 60 comes from 440-380
## skiping green channel (values of zero)
rgb(rgbmask & Bmask) = 1;
mask = wavelength >= 440 & wavelength < 490;
rgbmask = get_rgb_mask (mask);
## skiping red channel (values of zero)
rgb(rgbmask & Gmask) = (wavelength(mask) - 440) / 50; # 50 comes from 490-440
rgb(rgbmask & Bmask) = 1;
mask = wavelength >= 490 & wavelength < 510;
rgbmask = get_rgb_mask (mask);
## skiping red channel (values of zero)
rgb(rgbmask & Gmask) = 1;
rgb(rgbmask & Bmask) = -(wavelength(mask) - 510) / 20; # 20 comes from 510-490
mask = wavelength >= 510 & wavelength < 580;
rgbmask = get_rgb_mask (mask);
rgb(rgbmask & Rmask) = (wavelength(mask) - 510) / 70; # 70 comes from 580-510
rgb(rgbmask & Gmask) = 1;
## skiping blue channel (values of zero)
mask = wavelength >= 580 & wavelength < 645;
rgbmask = get_rgb_mask (mask);
rgb(rgbmask & Rmask) = 1;
rgb(rgbmask & Gmask) = -(wavelength(mask) - 645) / 65; # 65 comes from 645-580
## skiping blue channel (values of zero)
mask = wavelength >= 645 & wavelength <= 780;
rgbmask = get_rgb_mask (mask);
rgb(rgbmask & Rmask) = 1;
## skiping green channel (values of zero)
## skiping blue channel (values of zero)
## all other wavelengths have values of zero in all channels (black)
## let intensity fall off near the vision limits
## set the factor
factor = zeros (size (wavelength));
mask = wavelength >= 380 & wavelength < 420;
factor(mask) = 0.3 + 0.7*(wavelength(mask) - 380) / 40; # 40 = 420 - 380
mask = wavelength >= 420 & wavelength <= 700;
factor(mask) = 1;
mask = wavelength > 700 & wavelength <= 780;
factor(mask) = 0.3 + 0.7*(780 - wavelength(mask)) / 80; # 80 = 780 - 700
## for other wavelengths, factor is 0
## expand factor for the 3 channels
factor = repmat (factor, [ones(1, ndims (factor) - size_adjust) 3]);
## correct rgb
rgb = (rgb .* factor) .^gamma;
## scale to requested class
switch tolower (out_class)
case {"single"} rgb = im2single (rgb);
case {"double"} ## do nothing, already class double
case {"uint8"} rgb = im2uint8 (rgb);
case {"uint16"} rgb = im2uint16 (rgb);
case {"int16"} rgb = im2int16 (rgb);
otherwise error ("wavelength2rgb: unsupported class `%s'", out_class)
endswitch
endfunction
%!demo
%!
%! ##draw RGB values for wavelengths between 350 and 800 nm
%! RGB = wavelength2rgb (350:800);
%! rgbplot (squeeze (RGB), "composite");
%! axis off;
image-2.12.0/inst/PaxHeaders.25054/entropy.m 0000644 0000000 0000000 00000000062 13615546210 015252 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/entropy.m 0000644 0001750 0001750 00000010625 13615546210 020020 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2008 Søren 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{E} =} entropy (@var{im})
## @deftypefnx{Function File} {@var{E} =} entropy (@var{im}, @var{nbins})
## Computes the entropy of an image.
##
## The entropy of the elements of the image @var{im} is computed as
##
## @example
## @var{E} = -sum (@var{P} .* log2 (@var{P})
## @end example
##
## where @var{P} is the distribution of the elements of @var{im}. The distribution
## is approximated using a histogram with @var{nbins} cells. If @var{im} is
## @code{logical} then two cells are used by default. For other classes 256 cells
## are used by default.
##
## When the entropy is computed, zero-valued cells of the histogram are ignored.
##
## @seealso{entropyfilt}
## @end deftypefn
function retval = entropy (I, nbins = 0)
if (nargin < 1 || nargin > 2)
print_usage ();
endif
if ( (! isnumeric (I) && ! islogical (I)) || issparse (I) || (! isreal (I)) )
error ("entropy: I must be real, non-sparse and numeric");
endif
if (! isscalar (nbins))
error ("entropy: NBINS must be a scalar");
endif
## Get number of histogram bins
if (nbins <= 0)
if (islogical (I))
nbins = 2;
else
nbins = 256;
endif
endif
## transform all non-logical images to uint8 (as Matlab):
if (! islogical (I))
I = im2uint8 (I);
end
## Compute histogram, using imhist (as Matlab claims to do)
P = imhist (I(:), nbins);
## ignore zero-entries of the histogram, and normalize it to a sum of 1
P(P==0) = [];
P = P ./ sum (P(:));
## Compute entropy
retval = -sum (P .* log2 (P));
endfunction
%!assert (entropy ([0 1]), 1)
%!assert (entropy (uint8 ([0 1])), 1)
%!assert (entropy ([0 0]), 0)
%!assert (entropy ([0]), 0)
%!assert (entropy ([1]), 0)
%!assert (entropy ([0 .5; 2 0]), 1.5)
## rgb images are treated like nd grayscale images
%!assert (entropy (repmat ([0 .5; 2 0], 1, 1, 3)),
%! entropy ([0 .5; 2 0]))
## test some 9x9 float input images
%!test
%! A = zeros (3,3);
%! B = ones (3,3);
%! C = [1 1 1; 2 2 2; 3 3 3];
%! D = C';
%! E = ones (3,3);
%! E(2,2)=2;
%! F = 3 .* ones (3,3);
%! F(2,2)=1;
%! G = [-1 2 7; -5 2 8; -7 pi 9];
%! H = [5 2 8; 1 -3 1; 5 1 0];
%! pG = [1 2] ./ 3;
%! G_out = -sum (pG.*log2 (pG));
%! pH = [2 7] ./ 9;
%! H_out = -sum (pH.*log2 (pH));
%! assert (entropy (A), 0, eps);
%! assert (entropy (B), 0, eps);
%! assert (entropy (C), 0, eps);
%! assert (entropy (D), 0, eps);
%! assert (entropy (E), 0, eps);
%! assert (entropy (F), 0, eps);
%! assert (entropy (G), G_out, eps);
%! assert (entropy (H), H_out, eps);
## test some 9x9 uint8 input images
%!test
%! A = uint8 (zeros (3,3));
%! B = uint8 (ones (3,3));
%! C = uint8 ([1 1 1; 2 2 2; 3 3 3]);
%! D = C';
%! E = uint8 (ones (3,3));
%! E(2,2)=2;
%! F = 3 .* uint8 (ones (3,3));
%! F(2,2)=1;
%! G = uint8 ([0 2 7; 0 2 8; 0 3 9]);
%! H = uint8 ([5 2 8; 1 0 1; 5 1 0]);
%! pC = [1 1 1] ./ 3;
%! C_out = -sum (pC.*log2 (pC));
%! D_out = C_out;
%! pE = [8 1] ./ 9;
%! E_out = -sum (pE.*log2 (pE));
%! F_out = E_out;
%! pG = [3 2 1 1 1 1] ./ 9;
%! G_out = -sum (pG.*log2 (pG));
%! pH = [2 3 1 2 1] ./ 9;
%! H_out = -sum (pH.*log2 (pH));
%! assert (entropy (A), 0);
%! assert (entropy (B), 0);
%! assert (entropy (C), C_out, eps);
%! assert (entropy (D), D_out, eps);
%! assert (entropy (E), E_out, eps);
%! assert (entropy (F), F_out, eps);
%! assert (entropy (G), G_out, eps);
%! assert (entropy (H), H_out, eps);
## test some 9x9 logical input images
%!test
%! L1 = false (3,3);
%! L1(2,2)=true;
%! L2 = true (3,3);
%! L2(2,2)=false;
%! L3 = logical ([0 1 1; 0 1 1; 0 0 1]);
%! p12 = [1 8] ./ 9;
%! out12 = -sum (p12.*log2 (p12));
%! p3 = [5 4] ./9;
%! out3 = -sum (p3.*log2 (p3));
%! assert (entropy (L1), out12, eps);
%! assert (entropy (L2), out12, eps);
%! assert (entropy (L3), out3, eps);
image-2.12.0/inst/PaxHeaders.25054/cp2tform.m 0000644 0000000 0000000 00000000062 13615546210 015306 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/cp2tform.m 0000644 0001750 0001750 00000026154 13615546210 020060 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2012 Pantxo Diribarne
##
## 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 Octave; see the file COPYING. If not, see
## .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{T} =} cp2tform (@var{in_cp}, @var{out_cp}, @var{ttype})
## @deftypefnx {Function File} {@var{T} =} cp2tform (@dots{}, @var{opt})
## Return a transformation structure @var{T} (see "help maketform"
## for the form of the structure) that can be further used to
## transform coordinates between an input space and an ouput space.
##
## The transform is inferred from two n-by-2 arrays, @var{in_cp} and
## @var{out_cp}, which contain the coordinates of n control points in
## the two 2D spaces.
## Transform coefficients are stored in @var{T}.tdata. Interpretation of
## transform coefficients depends on the requested transform type @var{ttype}:
##
## @table @asis
## @item "affine"
## Return both forward (input space to output space) and inverse transform
## coefficients @var{T}.tdata.T and @var{T}.tdata.Tinv. Transform
## coefficients are 3x2 matrices which can be used as follows:
##
## @example
## @group
## @var{in_cp} = [@var{out_cp} ones(rows (out_cp) ,1)] * T.tdata.Tinv
## @var{out_cp} = [@var{in_cp} ones(rows (in_cp) ,1)] * T.tdata.T
## @end group
## @end example
## This transformation is well suited when parallel lines in one space
## are still parallel in the other space (e.g. shear, translation, @dots{}).
##
## @item "nonreflective similarity"
## Same as "affine" except that the transform matrices T and Tinv have
## the form
## @example
## @group
## Tcoefs = [a -b;
## b a;
## c d]
## @end group
## @end example
## This transformation may represent rotation, scaling and
## translation. Reflection is not included.
##
## @item "similarity"
## Same as "nonreflective similarity" except that the transform matrices T and Tinv may also have
## the form
## @example
## @group
## Tcoefs = [a b;
## b -a;
## c d]
## @end group
## @end example
## This transformation may represent reflection, rotation, scaling and
## translation. Generates a warning if the nonreflective similarity is
## better suited.
##
## @item "projective"
## Return both forward (input space to output space) and inverse transform
## coefficients @var{T}.tdata.T and @var{T}.tdata.Tinv. Transform
## coefficients are 3x3 matrices which can
## be used as follows:
##
## @example
## @group
## [u v w] = [@var{out_cp} ones(rows (out_cp) ,1)] * T.tdata.Tinv
## @var{in_cp} = [u./w, v./w];
## [x y z] = [@var{in_cp} ones(rows (in_cp) ,1)] * T.tdata.T
## @var{out_cp} = [x./z y./z];
## @end group
## @end example
## This transformation is well suited when parallel lines in one space
## all converge toward a vanishing point in the other space.
##
## @item "polynomial"
## Here the @var{opt} input argument is the order of the polynomial
## fit. @var{opt} must be 2, 3 or 4 and input control points number must
## be respectively at least 6, 10 or 15. Only the inverse transform
## (output space to input space) is included in the structure @var{T}.
## Denoting x and y the output space coordinate vector and u, v the
## the input space coordinates. Inverse transform coefficients are
## stored in a (6,10 or 15)-by-2 matrix which can be used as follows:
##
## @example
## @group
## Second order:
## [u v] = [1 x y x*y x^2 y^2] * T.tdata.Tinv
## @end group
## @group
## Third order:
## [u v] = [1 x y x*y x^2 y^2 y*x^2 x*y^2 x^3 y^3] * T.tdata.Tinv
## @end group
## @group
## Fourth order:
## [u v] = [1 x y x*y x^2 y^2 y*x^2 x*y^2 x^3 y^3 x^3*y x^2*y^2 x*y^3 x^4 y^4] * T.tdata.Tinv
## @end group
## @end example
## This transform is well suited when lines in one space become curves
## in the other space.
## @end table
## @seealso{tformfwd, tforminv, maketform}
## @end deftypefn
function trans = cp2tform (crw, cap, ttype, opt)
if (nargin < 3)
print_usage ();
endif
if (! all (size (crw) == size (cap)) || columns (crw) != 2)
error ("cp2tform: expect the 2 first input arguments to be (m x 2) matrices")
elseif (! ischar (ttype))
error ("cp2tform: expect a string as third input argument")
endif
ttype = lower (ttype);
switch ttype
case {'nonreflective similarity', 'similarity', 'affine', 'projective'}
trans = gettrans (ttype, cap, crw);
case 'polynomial'
if (nargin < 4)
error ("cp2tform: expect a fourth input argument for 'polynomial'")
elseif (! isscalar (opt))
error ("cp2tform: expect a scalar as fourth argument")
endif
trans = gettrans (ttype, cap, crw, round (opt));
otherwise
error ("cp2tform: expect 'nonreflective similarity', 'similarity', 'affine' or 'polynomial' as third input argument")
endswitch
endfunction
function trans = gettrans (ttype, cap, crw, ord = 0)
switch ttype
case "nonreflective similarity"
x = cap(:,1);
y = cap(:,2);
u = crw(:,1);
v = crw(:,2);
tmp0 = zeros(size(u));
tmp1 = ones(size(u));
A = [u v tmp1 tmp0 ; v -u tmp0 tmp1];
B = [x; y];
tmat = A\B;
tmat = [tmat(1) -tmat(2);
tmat(2) tmat(1);
tmat(3) tmat(4)];
trans = maketform ("affine", tmat);
case "similarity"
x = cap(:,1);
y = cap(:,2);
u = crw(:,1);
v = crw(:,2);
tmp0 = zeros(size(u));
tmp1 = ones(size(u));
#without reflection
A = [u v tmp1 tmp0 ; v -u tmp0 tmp1];
B = [x; y];
tmat1 = A\B;
resid = norm (A*tmat1 - B);
#with reflection
A = [u v tmp1 tmp0 ; -v u tmp0 tmp1];
tmat2 = A\B;
if (norm (A*tmat2 - B) < resid)
tmat = [tmat2(1) tmat2(2);
tmat2(2) -tmat2(1);
tmat2(3) tmat2(4)];
else
tmat = [tmat1(1) -tmat1(2);
tmat1(2) tmat1(1);
tmat1(3) tmat1(4)];
warning ("cp2tform: reflection not included.")
endif
trans = maketform ("affine", tmat);
case "affine"
tmat = [crw ones(rows(crw), 1)]\cap;
trans = maketform ("affine", tmat);
case "projective"
x = cap(:,1);
y = cap(:,2);
u = crw(:,1);
v = crw(:,2);
tmp0 = zeros(size(u));
tmp1 = ones(size(u));
A = [-u -v -tmp1 tmp0 tmp0 tmp0 x.*u x.*v x;
tmp0 tmp0 tmp0 -u -v -tmp1 y.*u y.*v y];
B = - A(:,end);
A(:,end) = [];
tmat = A\B;
tmat(9) = 1;
tmat = reshape (tmat, 3, 3);
trans = maketform ("projective", tmat);
case "polynomial"
x = cap(:,1);
y = cap(:,2);
u = crw(:,1);
v = crw(:,2);
tmp1 = ones(size(x));
ndims_in = 2;
ndims_out = 2;
forward_fcn = [];
inverse_fcn = @inv_polynomial;
A = [tmp1, x, y, x.*y, x.^2, y.^2];
B = [u v];
switch ord
case 2
case 3
A = [A, y.*x.^2 x.*y.^2 x.^3 y.^3];
case 4
A = [A, y.*x.^2 x.*y.^2 x.^3 y.^3];
A = [A, x.^3.*y x.^2.*y.^2 x.*y.^3 x.^4 y.^4];
otherwise
error ("cp2tform: supported polynomial orders are 2, 3 and 4.")
endswitch
tmat = A\B;
trans = maketform ("custom", ndims_in, ndims_out, ...
forward_fcn, inverse_fcn, tmat);
otherwise
error ("cp2tform: invalid TTYPE %s.", ttype);
endswitch
endfunction
function out = inv_polynomial (x, pst)
out = [];
for ii = 1:2
p = pst.tdata(:,ii);
if (rows (p) == 6)
## 2nd order
out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + ...
p(5)*x(:,1).^2 + p(6)*x(:,2).^2;
elseif (rows (p) == 10)
## 3rd order
out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + ...
p(5)*x(:,1).^2 + p(6)*x(:,2).^2 + p(7)*x(:,2).*x(:,1).^2 + ...
p(8)*x(:,1).*x(:,2).^2 + p(9)*x(:,1).^3 + p(10)*x(:,2).^3;
elseif (rows (p) == 15)
## 4th order
out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + ...
p(5)*x(:,1).^2 + p(6)*x(:,2).^2 + p(7)*x(:,2).*x(:,1).^2 + ...
p(8)*x(:,1).*x(:,2).^2 + p(9)*x(:,1).^3 + p(10)*x(:,2).^3 + ...
p(11)*x(:,2).*x(:,1).^3 + p(12)*x(:,2).^2.*x(:,1).^2+ ...
p(13)*x(:,1).*x(:,2).^3 + p(14)*x(:,1).^4 + p(15)*x(:,2).^4;
endif
endfor
endfunction
%!function [crw, cap] = coords (npt = 1000, scale = 2, dtheta = pi/3,
%! dx = 2, dy = -6, sig2noise = 1e32)
%! theta = (rand(npt, 1)*2-1)*2*pi;
%! R = rand(npt,1);
%! y = R.*sin(theta);
%! x = R.*cos(theta);
%! crw = [y x];
%!
%! thetap = theta + dtheta;
%! Rap = R * scale;
%!
%! yap = Rap.*sin(thetap);
%! yap = yap + dy;
%! yap = yap + rand (size (yap)) * norm (yap) / sig2noise;
%!
%! xap = Rap.*cos(thetap);
%! xap = xap + dx;
%! xap = xap + rand (size (xap)) * norm (xap) / sig2noise;
%! cap = [yap xap];
%!endfunction
%!test
%! npt = 100000;
%! [crw, cap] = coords (npt);
%! ttype = 'projective';
%! T = cp2tform (crw, cap, ttype);
%! crw2 = tforminv (T, cap);
%! finalerr = norm (crw - crw2)/npt;
%! assert (finalerr < 2*eps, "norm = %3.2e ( > 2*eps)", finalerr)
%!test
%! npt = 100000;
%! [crw, cap] = coords (npt);
%! ttype = 'affine';
%! T = cp2tform (crw, cap, ttype);
%! crw2 = tforminv (T, cap);
%! finalerr = norm (crw - crw2)/npt;
%! assert (finalerr < eps, "norm = %3.2e ( > eps)", finalerr)
%!test
%! npt = 100000;
%! [crw, cap] = coords (npt);
%! ttype = 'nonreflective similarity';
%! T = cp2tform (crw, cap, ttype);
%! crw2 = tforminv (T, cap);
%! finalerr = norm (crw - crw2)/npt;
%! assert (finalerr < 2*eps, "norm = %3.2e ( > 2*eps)", finalerr)
%!test
%! npt = 100000;
%! [crw, cap] = coords (npt);
%! cap(:,2) *= -1; % reflection around y axis
%! ttype = 'similarity';
%! T = cp2tform (crw, cap, ttype);
%! crw2 = tforminv (T, cap);
%! finalerr = norm (crw - crw2)/npt;
%! assert (finalerr < 3*eps, "norm = %3.2e ( > 3*eps)", finalerr)
%!xtest
%! npt = 100000;
%! [crw, cap] = coords (npt);
%! ttype = 'polynomial';
%! ord = 2;
%! T = cp2tform (crw, cap, ttype, ord);
%! crw2 = tforminv (T, cap);
%! finalerr = norm (crw - crw2)/npt;
%! assert (finalerr < eps, "norm = %3.2e ( > eps)", finalerr)
%!xtest
%! npt = 100000;
%! [crw, cap] = coords (npt);
%! ttype = 'polynomial';
%! ord = 3;
%! T = cp2tform (crw, cap, ttype, ord);
%! crw2 = tforminv (T, cap);
%! finalerr = norm (crw - crw2)/npt;
%! assert (finalerr < eps, "norm = %3.2e ( > eps)", finalerr)
%!xtest
%! npt = 100000;
%! [crw, cap] = coords (npt);
%! ttype = 'polynomial';
%! ord = 4;
%! T = cp2tform (crw, cap, ttype, ord);
%! crw2 = tforminv (T, cap);
%! finalerr = norm (crw - crw2)/npt;
%! assert (finalerr < 6*eps, "norm = %3.2e ( > 6*eps)", finalerr)
image-2.12.0/inst/PaxHeaders.25054/imabsdiff.m 0000644 0000000 0000000 00000000062 13615546210 015476 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imabsdiff.m 0000644 0001750 0001750 00000006052 13615546210 020243 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2011 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{out} =} imabsdiff (@var{a}, @var{b})
## @deftypefnx {Function File} {@var{out} =} imabsdiff (@var{a}, @var{b}, @var{class})
## Return absolute difference of image or constant to an image.
##
## If @var{a} and @var{b} are two images of same size and class, returns the absolute
## difference between @var{b} and @var{a}.
##
## The class of @var{out} will be the same as @var{a} unless @var{a} is logical
## in which case @var{out} will be double. Alternatively, the class can be
## specified with @var{class}.
##
## @emph{Note 1}: you can force output class to be logical by specifying
## @var{class}. This is incompatible with @sc{matlab} which will @emph{not} honour
## request to return a logical matrix.
##
## @emph{Note 2}: the values are truncated to the mininum value of the output
## class.
## @seealso{imadd, imcomplement, imdivide, imlincomb, immultiply, imsubtract}
## @end deftypefn
function img = imabsdiff (img, val, out_class = class (img))
if (nargin < 2 || nargin > 3)
print_usage;
endif
## we want to make subtraction as double so this is it
[img, val] = imarithmetics ("imabsdiff", img, val, "double");
converter = str2func (tolower (out_class));
if (nargin < 3 && strcmp (out_class, "logical"))
## it is using logical as default. Use double instead. We only have this
## problem on this function because we are are not actually giving out_class
## to imarithmetics
converter = @double;
else
converter = str2func (tolower (out_class));
endif
img = converter (abs (img - val));
endfunction
%!assert (imabsdiff (uint8 ([23 250]), uint8 ([26 50])), uint8 ([ 3 200])); # default to first class and abs works
%!assert (imabsdiff (uint8 ([23 250]), uint8 ([24 50]), "uint16"), uint16 ([ 1 200])); # defining output class works (not in matlab)
%!assert (imabsdiff (uint8 ([23 250]), uint8 ([24 255]), "int8"), int8 ([ 1 5])); # signed integers kinda work (not in matlab)
%!assert (imabsdiff (logical ([ 1 0]), logical ([ 1 1])), double ([ 0 1])); # return double for two logical images
%!fail ("imabsdiff (uint8 ([23 250]), 30"); # fails subtracting a scalar
%!fail ("imabsdiff (uint8 ([23 250]), uint16 ([23 250]))"); # input need to have same class
image-2.12.0/inst/PaxHeaders.25054/edge.m 0000644 0000000 0000000 00000000062 13615546210 014456 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/edge.m 0000644 0001750 0001750 00000073157 13615546210 017235 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 1999 Andy Adler
## Copyright (C) 2008 Søren Hauberg
## Copyright (C) 2015 Hartmut Gimpel
## Copyright (C) 2015 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {[@var{bw}, @var{thresh}] =} edge (@var{im}, @var{method}, @dots{})
## Find edges using various methods.
##
## The image @var{im} must be 2 dimensional and grayscale. The @var{method}
## must be a string with the string name. The other input arguments are
## dependent on @var{method}.
##
## @var{bw} is a binary image with the identified edges. @var{thresh} is
## the threshold value used to identify those edges. Note that @var{thresh}
## is used on a filtered image and not directly on @var{im}.
##
## @seealso{fspecial}
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Canny"})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Canny"}, @var{thresh})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Canny"}, @var{thresh}, @var{sigma})
## Find edges using the Canny method.
##
## @var{thresh} is two element vector for the hysteresis thresholding.
## The lower and higher threshold values are the first and second elements
## respectively. If it is a scalar value, the lower value is set to
## @code{0.4 * @var{thresh}}.
##
## @var{sigma} is the standard deviation to be used on the Gaussian filter
## that is used to smooth the input image prior to estimating gradients.
## Defaults to @code{sqrt (2)}.
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Kirsch"})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Kirsch"}, @var{thresh})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Kirsch"}, @var{thresh}, @var{direction})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Kirsch"}, @var{thresh}, @var{direction}, @var{thinning})
## Find edges using the Kirsch approximation to the derivatives.
##
## Edge points are defined as points where the length of the gradient exceeds
## a threshold @var{thresh}.
##
## @var{thresh} is the threshold used and defaults to twice the square root of the
## mean of the gradient squared of @var{im}.
##
## @var{direction} is the direction of which the gradient is
## approximated and can be @qcode{"vertical"}, @qcode{"horizontal"},
## or @qcode{"both"} (default).
##
## @var{thinning} can be the string @qcode{"thinning"} (default) or
## @qcode{"nothinning"}. This controls if a simple thinning procedure
## is applied to the edge image such that edge points also need to have
## a larger gradient than their neighbours. The resulting "thinned"
## edges are only one pixel wide.
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Lindeberg"})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Lindeberg"}, @var{sigma})
## Find edges using the the differential geometric single-scale edge
## detector by Tony Lindeberg.
##
## @var{sigma} is the scale (spread of Gaussian filter) at which the edges
## are computed. Defaults to @code{2}.
##
## This method does not use a threshold value and therefore does not return
## one.
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"LoG"})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"LoG"}, @var{thresh}, @var{sigma})
## Find edges by convolving with the Laplacian of Gaussian (LoG) filter,
## and finding zero crossings.
##
## Only zero crossings where the filter response is larger than @var{thresh}
## are retained. @var{thresh} is automatically computed as 0.75*@math{M},
## where @math{M} is the mean of absolute value of LoG filter response.
##
## @var{sigma} sets the spread of the LoG filter. Default to @code{2}.
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Prewitt"}, @dots{})
## Find edges using the Prewitt approximation to the derivatives.
##
## This method is the same as Kirsch except a different approximation
## gradient is used.
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Roberts"})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Roberts"}, @var{thresh})
## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Roberts"}, @var{thresh}, @var{thinning})
## Find edges using the Roberts approximation to the derivatives.
##
## This method is similar to Kirsch except a different approximation
## gradient is used, and the default @var{thresh} is multiplied by sqrt(1.5).
## In addition, there it does not accept the @var{direction} argument.
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Sobel"}, @dots{})
## Find edges using the Sobel approximation to the derivatives.
##
## This method is the same as Kirsch except a different approximation
## gradient is used.
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"zerocross"}, @var{thresh}, @var{filter})
## Find edges by convolving with a user-supplied filter and finding zero
## crossings.
##
## @end deftypefn
## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Andy"}, @var{thresh}, @var{params})
## Find edges by the original (Andy Adlers) @code{edge} algorithm.
## The steps are
## @enumerate
## @item
## Do a Sobel edge detection and to generate an image at
## a high and low threshold.
## @item
## Edge extend all edges in the LT image by several pixels,
## in the vertical, horizontal, and 45 degree directions.
## Combine these into edge extended (EE) image.
## @item
## Dilate the EE image by 1 step.
## @item
## Select all EE features that are connected to features in
## the HT image.
## @end enumerate
##
## The parameters for the method is given in a vector:
## @table @asis
## @item params(1)==0 or 4 or 8
## Perform x connected dilatation (step 3).
## @item params(2)
## Dilatation coefficient (threshold) in step 3.
## @item params(3)
## Length of edge extention convolution (step 2).
## @item params(4)
## Coefficient of extention convolution in step 2.
## @end table
## defaults = [8, 1, 3, 3]
##
## @end deftypefn
function [bw_out, thresh, varargout] = edge (im, method = "sobel", varargin)
if (nargin < 1 || nargin > 5)
print_usage ();
endif
if (! isgray (im))
error ("edge: IM must be a grayscale image");
endif
method = tolower (method);
switch (method)
case {"sobel", "prewitt", "kirsch"}
[bw, thresh] = edge_kirsch_prewitt_sobel (im, method, varargin{:});
case "roberts"
[bw, thresh, varargout{1:2}] = edge_roberts (im, varargin{:});
case "log"
[bw, thresh] = edge_log (im, varargin{:});
case "zerocross"
[bw, thresh] = edge_zerocross (im, varargin{:});
case "canny"
[bw, thresh] = edge_canny (im, varargin{:});
case "lindeberg"
[bw] = edge_lindeberg (im, varargin{:});
case "andy"
[bw, thresh] = edge_andy (im, varargin{:});
otherwise
error ("edge: unsupported edge detector `%s'", method);
endswitch
if (nargout == 0)
imshow (bw);
else
bw_out = bw;
endif
endfunction
function [bw, thresh] = edge_kirsch_prewitt_sobel (im, method, varargin)
if (numel (varargin) > 3)
error ("edge: %s method takes at most 3 extra arguments", method);
endif
## This supports thinning, direction, and thresh arguments in any
## order. Matlab madness I tell you.
varargin = tolower (varargin);
direction_mask = (strcmp (varargin, "both")
| strcmp (varargin, "horizontal")
| strcmp (varargin, "vertical"));
thinning_mask = (strcmp (varargin, "thinning")
| strcmp (varargin, "nothinning"));
thresh_mask = cellfun (@(x) isnumeric (x) && (isscalar (x) || isempty (x)),
varargin);
if (! all (direction_mask | thinning_mask | thresh_mask))
error ("edge: %s method takes only THRESH, DIRECTION, and THINNING arguments",
method);
endif
if (nnz (direction_mask) > 1)
error ("edge: more than 1 direction argument defined");
elseif (any (direction_mask))
direction = varargin{direction_mask};
else
direction = "both";
endif
if (nnz (thinning_mask) > 1)
error ("edge: more than 1 thinning argument defined");
elseif (any (thinning_mask)
&& strcmp (varargin{thinning_mask}, "nothinning"))
thinning = false;
else
thinning = true;
endif
if (nnz (thresh_mask) > 1)
error ("edge: more than 1 threshold argument defined");
elseif (any (thresh_mask))
thresh = varargin{thresh_mask};
else
thresh = [];
endif
## For better speed, the calculation is done with
## squared edge strenght values (strength2) and
## squared threshold values (thresh2).
h1 = fspecial (method);
h1 ./= sum (abs (h1(:))); # normalize h1
im = im2double (im);
switch (direction)
case "horizontal"
strength2 = imfilter (im, h1, "replicate").^2;
case "vertical"
strength2 = imfilter (im, h1', "replicate").^2;
case "both"
strength2 = (imfilter (im, h1, "replicate").^2
+ imfilter (im, h1', "replicate").^2);
otherwise
error ("edge: unknown DIRECTION `%s' for %s method", direction, method);
endswitch
if (isempty (thresh))
thresh2 = 4 * mean (strength2(:));
thresh = sqrt (thresh2);
else
thresh2 = thresh .^ 2;
endif
if (thinning)
## Keep edge strengths for use in non-maximum
## suppresion in the simple_thinning step.
strength2(strength2<=thresh2) = 0;
bw = simple_thinning (strength2);
else
bw = strength2 > thresh2;
endif
endfunction
function [bw, thresh, g45, g135] = edge_roberts (im, varargin)
if (numel (varargin) > 2)
error ("edge: Roberts method takes at most 2 extra arguments");
endif
## This supports thinning, direction, and thresh arguments in any
## order. Matlab madness I tell you.
varargin = tolower (varargin);
thinning_mask = (strcmp (varargin, "thinning")
| strcmp (varargin, "nothinning"));
thresh_mask = cellfun (@(x) isnumeric (x) && (isscalar (x) || isempty (x)),
varargin);
if (! all (thinning_mask | thresh_mask))
error ("edge: roberts method takes only THRESH and THINNING arguments");
endif
if (nnz (thinning_mask) > 1)
error ("edge: more than 1 thinning argument defined");
elseif (any (thinning_mask)
&& strcmp (varargin{thinning_mask}, "nothinning"))
thinning = false;
else
thinning = true;
endif
if (nnz (thresh_mask) > 1)
error ("edge: more than 1 threshold argument defined");
elseif (any (thresh_mask))
thresh = varargin{thresh_mask};
else
thresh = [];
endif
h1 = [1 0; 0 -1] ./ 2;
h2 = [0 1; -1 0] ./ 2;
g45 = imfilter (im, h1, "replicate");
g135 = imfilter (im, h2, "replicate");
strength2 = g45.^2 + g135.^2;
if (isempty (thresh))
thresh2 = 6 * mean (strength2(:));
thresh = sqrt (thresh2);
else
thresh2 = thresh .^ 2;
endif
if (thinning)
## Keep edge strengths for use in non-maximum
## suppresion in the simple_thinning step.
strength2(strength2<=thresh2) = 0;
bw = simple_thinning (strength2);
else
bw = strength2 > thresh2;
endif
endfunction
function [bw, thresh] = edge_log (im, thresh = [], sigma = 2)
if (nargin > 1 && (! isnumeric (thresh) || all (numel (thresh) != [0 1])))
error ("edge: THRESH for LoG method must be a numeric scalar or empty");
endif
if (nargin > 2 && (! isnumeric (sigma) || ! isscalar (sigma)))
error ("edge: SIGMA for LoG method must be a numeric scalar");
endif
f = fspecial ("log", (2 * ceil (3*sigma)) +1, sigma);
g = conv2 (im, f, "same");
if (isempty (thresh))
thresh = 0.75*mean(abs(g(:)));
endif
bw = (abs (g) > thresh) & zerocrossings (g);
endfunction
function [bw, thresh] = edge_zerocross (im, thresh, f)
## because filter is a required argument, so is thresh
if (nargin != 3)
error ("edge: a FILTER and THRESH are required for the zerocross method");
elseif (! isnumeric (thresh) || all (numel (thresh) != [0 1]))
error ("edge: THRESH for zerocross method must be a numeric scalar or empty");
elseif (! isnumeric (f))
error ("edge: FILTER for zerocross method must be numeric");
endif
g = conv2 (im, f, "same");
if (isempty (thresh))
thresh = mean (abs (g(:)));
endif
bw = (abs (g) > thresh) & zerocrossings(g);
endfunction
function [bw, thresh] = edge_canny (im, thresh = [], sigma = sqrt (2))
if (nargin > 1 && (! isnumeric (thresh) || all (numel (thresh) != [0 1 2])))
error ("edge: THRESH for Canny method must have 0, 1, or 2 elements");
endif
if (nargin > 2 && (! isnumeric (sigma) || ! isscalar (sigma)))
error ("edge: SIGMA for Canny method must be a numeric scalar");
endif
## Gaussian filtering to change the edge scale.
## Treat each dimensions separately for performance.
gauss = fspecial ("gaussian", [1 (8*ceil(sigma))], sigma);
im = im2double (im);
J = imfilter (im, gauss, "replicate");
J = imfilter (J, gauss', "replicate");
## edge detection with Prewitt filter (treat dimensions separately)
p = [1 0 -1]/2;
Jx = imfilter (J, p, "replicate");
Jy = imfilter (J, p', "replicate");
Es = sqrt (Jx.^2 + Jy.^2);
Es_max = max (Es(:));
if (Es_max > 0)
Es ./= Es_max;
endif
Eo = pi - mod (atan2 (Jy, Jx) - pi, pi);
if (isempty (thresh))
tmp = mean(abs(Es(:)));
thresh = [0.4*tmp, tmp];
elseif (numel (thresh) == 1)
thresh = [0.4*thresh thresh];
else
thresh = thresh(:).'; # always return a row vector
endif
bw = nonmax_suppress(Es, Eo, thresh(1), thresh(2));
endfunction
function [bw] = edge_lindeberg (im, sigma = 2)
if (nargin > 1 && (! isnumeric (sigma) || ! isscalar (sigma)))
error ("edge: SIGMA for Lindeberg method must be a numeric scalar");
endif
## Filters for computing the derivatives
Px = [-1 0 1; -1 0 1; -1 0 1];
Py = [1 1 1; 0 0 0; -1 -1 -1];
Pxx = conv2 (Px, Px, "full");
Pyy = conv2 (Py, Py, "full");
Pxy = conv2 (Px, Py, "full");
Pxxx = conv2 (Pxx, Px, "full");
Pyyy = conv2 (Pyy, Py, "full");
Pxxy = conv2 (Pxx, Py, "full");
Pxyy = conv2 (Pyy, Px, "full");
## Change scale
L = imsmooth (double (im), "Gaussian", sigma);
## Compute derivatives
Lx = conv2 (L, Px, "same");
Ly = conv2 (L, Py, "same");
Lxx = conv2 (L, Pxx, "same");
Lyy = conv2 (L, Pyy, "same");
Lxy = conv2 (L, Pxy, "same");
Lxxx = conv2 (L, Pxxx, "same");
Lyyy = conv2 (L, Pyyy, "same");
Lxxy = conv2 (L, Pxxy, "same");
Lxyy = conv2 (L, Pxyy, "same");
## Compute directional derivatives
Lvv = Lx.^2.*Lxx + 2.*Lx.*Ly.*Lxy + Ly.^2.*Lyy;
Lvvv = Lx.^3.*Lxxx + 3.*Lx.^2.*Ly.*Lxxy ...
+ 3.*Lx.*Ly.^2.*Lxyy + 3.*Ly.^3.*Lyyy;
## Perform edge detection
bw = zerocrossings (Lvv) & Lvvv < 0;
endfunction
## The 'andy' edge detector that was present in older versions of 'edge'.
function [imout, thresh] = edge_andy (im, thresh, param2)
[n,m]= size(im);
xx= 2:m-1;
yy= 2:n-1;
filt= [1 2 1;0 0 0; -1 -2 -1]/8; tv= 2;
imo= conv2(im, rot90(filt), 'same').^2 + conv2(im, filt, 'same').^2;
if nargin<2 || thresh==[];
thresh= sqrt( tv* mean(mean( imo(yy,xx) )) );
end
# sum( imo(:)>thresh ) / prod(size(imo))
dilate= [1 1 1;1 1 1;1 1 1]; tt= 1; sz=3; dt=3;
if nargin>=3
# 0 or 4 or 8 connected dilation
if length(param2) > 0
if param2(1)==4 ; dilate= [0 1 0;1 1 1;0 1 0];
elseif param2(1)==0 ; dilate= 1;
end
end
# dilation threshold
if length(param2) > 2; tt= param2(2); end
# edge extention length
if length(param2) > 2; sz= param2(3); end
# edge extention threshold
if length(param2) > 3; dt= param2(4); end
end
fobliq= [0 0 0 0 1;0 0 0 .5 .5;0 0 0 1 0;0 0 .5 .5 0;0 0 1 0 0;
0 .5 .5 0 0;0 1 0 0 0;.5 .5 0 0 0;1 0 0 0 0];
fobliq= fobliq( 5-sz:5+sz, 3-ceil(sz/2):3+ceil(sz/2) );
xpeak= imo(yy,xx-1) <= imo(yy,xx) & imo(yy,xx) > imo(yy,xx+1) ;
ypeak= imo(yy-1,xx) <= imo(yy,xx) & imo(yy,xx) > imo(yy+1,xx) ;
imht= ( imo >= thresh^2 * 2); # high threshold image
imht(yy,xx)= imht(yy,xx) & ( xpeak | ypeak );
imht([1,n],:)=0; imht(:,[1,m])=0;
% imlt= ( imo >= thresh^2 / 2); # low threshold image
imlt= ( imo >= thresh^2 / 1); # low threshold image
imlt(yy,xx)= imlt(yy,xx) & ( xpeak | ypeak );
imlt([1,n],:)=0; imlt(:,[1,m])=0;
# now we edge extend the low thresh image in 4 directions
imee= ( conv2( imlt, ones(2*sz+1,1) , 'same') > tt ) | ...
( conv2( imlt, ones(1,2*sz+1) , 'same') > tt ) | ...
( conv2( imlt, eye(2*sz+1) , 'same') > tt ) | ...
( conv2( imlt, rot90(eye(2*sz+1)), 'same') > tt ) | ...
( conv2( imlt, fobliq , 'same') > tt ) | ...
( conv2( imlt, fobliq' , 'same') > tt ) | ...
( conv2( imlt, rot90(fobliq) , 'same') > tt ) | ...
( conv2( imlt, flipud(fobliq) , 'same') > tt );
# imee(yy,xx)= conv2(imee(yy,xx),ones(3),'same') & ( xpeak | ypeak );
imee= conv2(imee,dilate,'same') > dt; #
% ff= find( imht==1 );
% imout = bwselect( imee, rem(ff-1, n)+1, ceil(ff/n), 8);
imout = imee;
endfunction
## An auxiliary function that performs a very simple thinning.
## Strength is an image containing the edge strength.
## bw contains a 1 in (r,c) if
## 1) strength(r,c) is greater than both neighbours in the
## vertical direction, OR
## 2) strength(r,c) is greater than both neighbours in the
## horizontal direction.
## Note the use of OR.
function bw = simple_thinning(strength)
[r c] = size(strength);
x = ( strength > [ zeros(r,1) strength(:,1:end-1) ] & ...
strength > [ strength(:,2:end) zeros(r,1) ] );
y = ( strength > [ zeros(1,c); strength(1:end-1,:) ] & ...
strength > [ strength(2:end,:); zeros(1,c) ] );
bw = x | y;
endfunction
## Auxiliary function. Finds the zero crossings of the
## 2-dimensional function f. (By Etienne Grossmann)
function z = zerocrossings(f)
z0 = f<0; ## Negative
[R,C] = size(f);
z = zeros(R,C);
z(1:R-1,:) |= z0(2:R,:); ## Grow
z(2:R,:) |= z0(1:R-1,:);
z(:,1:C-1) |= z0(:,2:C);
z(:,2:C) |= z0(:,1:C-1);
z &= !z0; ## "Positive zero-crossings"?
endfunction
## Test the madness of arbitrary order of input for prewitt, kirsch,
## and sobel methods.
%!test
%! im = [
%! 249 238 214 157 106 69 60 90 131 181 224 247 252 250 250
%! 250 242 221 165 112 73 62 91 133 183 225 248 252 250 251
%! 252 246 228 173 120 78 63 90 130 181 224 248 253 251 251
%! 253 248 232 185 132 87 62 80 116 170 217 244 253 251 252
%! 253 249 236 198 149 101 66 71 101 155 206 238 252 252 252
%! 254 250 240 210 164 115 73 69 92 143 196 232 252 253 252
%! 70 70 68 61 49 36 24 22 26 38 52 63 70 70 70
%! 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
%! 62 63 62 59 51 42 33 25 22 26 36 45 56 60 62
%! 252 253 252 246 221 190 157 114 90 90 118 157 203 235 248
%! 251 253 254 251 233 209 182 136 103 92 107 139 185 225 245
%! 251 253 254 253 243 227 206 163 128 108 110 133 175 217 242
%! 252 253 254 254 249 241 228 195 164 137 127 139 172 212 239
%! ] / 255;
%!
%! methods = {"kirsch", "prewitt", "sobel"};
%! for m_i = 1:numel (methods)
%! method = methods{m_i};
%!
%! bw = edge (im, method, 0.2, "both", "thinning");
%! assert (edge (im, method, 0.2), bw)
%!
%! args = perms ({0.2, "both", "thinning"});
%! for i = 1:rows (args)
%! assert (edge (im, method, args{i,:}), bw)
%! endfor
%!
%! bw = edge (im, method, 0.2, "vertical", "nothinning");
%! args = perms ({0.2, "vertical", "nothinning"});
%! for i = 1:rows (args)
%! assert (edge (im, method, args{i,:}), bw)
%! endfor
%!
%! bw = edge (im, method, 0.2, "vertical", "thinning");
%! args = perms ({0.2, "vertical"});
%! for i = 1:rows (args)
%! assert (edge (im, method, args{i,:}), bw)
%! endfor
%!
%! bw = edge (im, method, 0.2, "both", "nothinning");
%! args = perms ({0.2, "nothinning"});
%! for i = 1:rows (args)
%! assert (edge (im, method, args{i,:}), bw)
%! endfor
%! endfor
%!error
%! bw = edge (rand (10), "sobel", 0.2, 0.4)
%!error
%! bw = edge (rand (10), "sobel", "thinning", "nothinning")
%!error
%! bw = edge (rand (10), "sobel", "both", "both")
%!error
%! bw = edge (rand (10), "sobel", [0.2 0.7], "both", "thinning")
%!error
%! bw = edge (rand (10), "kirsch", 0.2, 0.4)
%!error
%! bw = edge (rand (10), "kirsch", "thinning", "nothinning")
%!error
%! bw = edge (rand (10), "kirsch", "both", "both")
%!error
%! bw = edge (rand (10), "kirsch", [0.2 0.7], "both", "thinning")
%!error
%! bw = edge (rand (10), "prewitt", 0.2, 0.4)
%!error
%! bw = edge (rand (10), "prewitt", "thinning", "nothinning")
%!error
%! bw = edge (rand (10), "prewitt", "both", "both")
%!error
%! bw = edge (rand (10), "prewitt", [0.2 0.7], "both", "thinning")
%!test
%! im = [
%! 249 238 214 157 106 69 60 90 131 181 224 247 252 250 250
%! 250 242 221 165 112 73 62 91 133 183 225 248 252 250 251
%! 252 246 228 173 120 78 63 90 130 181 224 248 253 251 251
%! 253 248 232 185 132 87 62 80 116 170 217 244 253 251 252
%! 253 249 236 198 149 101 66 71 101 155 206 238 252 252 252
%! 254 250 240 210 164 115 73 69 92 143 196 232 252 253 252
%! 70 70 68 61 49 36 24 22 26 38 52 63 70 70 70
%! 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
%! 62 63 62 59 51 42 33 25 22 26 36 45 56 60 62
%! 252 253 252 246 221 190 157 114 90 90 118 157 203 235 248
%! 251 253 254 251 233 209 182 136 103 92 107 139 185 225 245
%! 251 253 254 253 243 227 206 163 128 108 110 133 175 217 242
%! 252 253 254 254 249 241 228 195 164 137 127 139 172 212 239
%! ] / 255;
%!
%! bw = edge (im, "roberts", .2, "thinning");
%! assert (edge (im, "roberts", 0.2), bw)
%! assert (edge (im, "roberts", "thinning", 0.2), bw)
%!
%! bw = edge (im, "roberts", .2, "nothinning");
%! assert (edge (im, "roberts", "nothinning", 0.2), bw)
%!error
%! bw = edge (rand (10), "roberts", 0.2, 0.4)
%!error
%! bw = edge (rand (10), "roberts", "thinning", "nothinning")
%!error
%! bw = edge (rand (10), "roberts", "both", "thinning")
## test how canny threshold arguments are always a row vector
%!test
%! im = rand (10);
%! [~, thresh] = edge (im, "canny");
%! assert (size (thresh), [1 2])
%! [~, thresh] = edge (im, "canny", [.2 .6]);
%! assert (thresh, [.2 .6])
%! [~, thresh] = edge (im, "canny", [.2; .6]);
%! assert (thresh, [.2 .6])
## test SOBEL edge detector
%!test
%! in = zeros (5);
%! in(3,3) = 1;
%!
%! E = logical ([
%! 0 0 0 0 0
%! 0 0 1 0 0
%! 0 1 0 1 0
%! 0 0 1 0 0
%! 0 0 0 0 0]);
%! assert (edge (in), E)
%! assert (edge (uint8 (in.*100)), E)
%! assert (edge (in, "sobel"), E)
%! assert (edge (in, "sobel", 0), E)
%! assert (edge (in, "sobel", 1), false (5))
%!
%! [E, auto_thresh] = edge (in);
%! assert (auto_thresh, 0.2449, 1e-4)
%!
%! V = logical([
%! 0 0 0 0 0
%! 0 1 0 1 0
%! 0 1 0 1 0
%! 0 1 0 1 0
%! 0 0 0 0 0]);
%! assert (edge (in, "sobel", 0, "vertical"), V)
%!
%! H = logical ([
%! 0 0 0 0 0
%! 0 1 1 1 0
%! 0 0 0 0 0
%! 0 1 1 1 0
%! 0 0 0 0 0]);
%! assert (edge (in, "sobel", 0, "horizontal"), H)
%!
%! V = false (5);
%! V(3,2) = true;
%! V(3,4) = true;
%! assert (edge (in, "sobel", [], "vertical"), V)
%!
%! H = false (5);
%! H(2,3) = true;
%! H(4,3) = true;
%! assert (edge (in, "sobel", [], "horizontal"), H)
%!test
%! A = ones (5);
%! A(3, 3) = 0;
%! expected = logical ([
%! 0 0 0 0 0
%! 0 0 1 0 0
%! 0 1 0 1 0
%! 0 0 1 0 0
%! 0 0 0 0 0]);
%! assert (edge (A), expected)
## test PREWITT edge detector
%!test
%! in = zeros (5);
%! in(3, 3) = 1;
%!
%! E = logical ([
%! 0 0 0 0 0
%! 0 1 0 1 0
%! 0 0 0 0 0
%! 0 1 0 1 0
%! 0 0 0 0 0]);
%!
%! assert (edge (in, "prewitt"), E)
%!
%! [~, auto_thresh] = edge (in, "prewitt");
%! assert (auto_thresh, 0.2309, 1e-4)
%!
%! V = logical([
%! 0 0 0 0 0
%! 0 1 0 1 0
%! 0 1 0 1 0
%! 0 1 0 1 0
%! 0 0 0 0 0]);
%! assert (edge (in, "prewitt", 0, "vertical"), V)
%!
%! H = logical ([
%! 0 0 0 0 0
%! 0 1 1 1 0
%! 0 0 0 0 0
%! 0 1 1 1 0
%! 0 0 0 0 0]);
%! assert (edge (in, "prewitt", 0, "horizontal"), H)
## test ROBERTS edge detector
%!test
%! in = zeros (5);
%! in(3,3) = 1;
%! in(3,4) = 0.9;
%!
%! E = logical ([
%! 0 0 0 0 0
%! 0 0 1 0 0
%! 0 0 1 0 0
%! 0 0 0 0 0
%! 0 0 0 0 0]);
%!
%! assert (edge (in, "roberts"), E)
%!
%! [~, auto_thresh] = edge (in, "roberts");
%! assert (auto_thresh, 0.6591, 1e-4)
%!
%! E45 = [0 0 0 0 0
%! 0 -0.5 -0.45 0 0
%! 0 0 0.50 0.45 0
%! 0 0 0 0 0
%! 0 0 0 0 0];
%! E135 = [0 0 0 0 0
%! 0 0 -0.50 -0.45 0
%! 0 0.5 0.45 0 0
%! 0 0 0 0 0
%! 0 0 0 0 0];
%!
%! [~, ~, erg45, erg135] = edge (in, "roberts");
%! assert (erg45, E45)
%! assert (erg135, E135)
## test CANNY edge detector
%!xtest
%! ## The edge image is correct and Matlab compatible so those should
%! ## pass. However, the threshold values used to generate the edge
%! ## image are not the same as Matlab.
%!
%! in_8 = fspecial ("gaussian", [8 8], 2);
%! in_8 /= in_8(4,4);
%! in_8_uint8 = im2uint8 (in_8);
%!
%! ## Matlab changed their implementation of the Canny method in
%! ## release 2011a. We are compatible with their new implementation
%! ## but for testing purposes, this is the expected result for the
%! ## old implementation.
%! out_8_old = logical ([
%! 0 0 0 0 0 0 0 0
%! 0 0 0 1 1 0 0 0
%! 0 0 1 0 0 1 0 0
%! 0 1 0 0 0 0 1 0
%! 0 1 0 0 0 0 1 0
%! 0 0 1 0 0 1 0 0
%! 0 0 0 1 1 0 0 0
%! 0 0 0 0 0 0 0 0]);
%!
%! out_8 = logical ([
%! 0 0 0 0 0 0 0 0
%! 0 1 1 1 1 1 0 0
%! 0 1 0 0 0 1 0 0
%! 0 1 0 0 0 1 0 0
%! 0 1 0 0 0 1 0 0
%! 0 1 1 1 1 1 0 0
%! 0 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0]);
%! out_thresh = [0.34375 0.859375];
%!
%! [obs_edge, obs_thresh] = edge (in_8, "Canny");
%! assert (obs_edge, out_8)
%! assert (obs_thresh, out_thresh)
%!
%! [obs_edge_givethresh, obs_thresh_givethresh] ...
%! = edge (in_8, "Canny", out_thresh);
%! assert (obs_edge_givethresh, out_8)
%! assert (obs_thresh_givethresh, out_thresh)
%!
%! [obs_edge_uint8, obs_thresh_uint8] = edge (in_8_uint8, "Canny");
%! assert (obs_edge_uint8, out_8)
%! assert (obs_thresh_uint8, out_thresh)
%!xtest
%! ## The edge image is correct and Matlab compatible so those should
%! ## pass. However, the threshold values used to generate the edge
%! ## image are not the same as Matlab.
%!
%! in_9 = fspecial ("gaussian", [9 9], 2);
%! in_9 /= in_9(5,5);
%!
%! ## Matlab changed their implementation of the Canny method in
%! ## release 2011a. We are compatible with their new implementation
%! ## but for testing purposes, this is the expected result for the
%! ## old implementation.
%! out_9_old = logical ([
%! 0 0 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0 0
%! 0 0 0 1 1 1 0 0 0
%! 0 0 1 0 0 0 1 0 0
%! 0 0 1 0 0 0 1 0 0
%! 0 0 1 0 0 0 1 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]);
%!
%! out_9 = logical ([
%! 0 0 0 0 0 0 0 0 0
%! 0 0 1 1 1 1 0 0 0
%! 0 1 1 0 0 1 1 0 0
%! 0 1 0 0 0 0 1 0 0
%! 0 1 0 0 0 0 1 0 0
%! 0 1 1 0 0 1 1 0 0
%! 0 0 1 1 1 1 0 0 0
%! 0 0 0 0 0 0 0 0 0
%! 0 0 0 0 0 0 0 0 0]);
%! out_thresh = [0.35 0.875];
%!
%! [obs_edge, obs_thresh] = edge (in_9, "Canny");
%! assert (obs_edge, out_9)
%! assert (obs_thresh, out_thresh)
%!
%! [obs_edge_givethresh, obs_thresh_givethresh] ...
%! = edge (in_9, "Canny", out_thresh);
%! assert (obs_edge_givethresh, out_9)
%! assert (obs_thresh_givethresh, out_thresh)
image-2.12.0/inst/PaxHeaders.25054/mean2.m 0000644 0000000 0000000 00000000062 13615546210 014554 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/mean2.m 0000644 0001750 0001750 00000003337 13615546210 017324 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2000 Kai Habel
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} mean2 (@var{I})
## Compute mean value of array.
##
## While the function name suggests that it computes the mean value of
## a 2D array, it will actually computes the mean value of an entire
## array. It is equivalent to @code{mean (I(:))}.
##
## The return value will be of class double independently of the input
## class.
##
## @seealso{mean, std2}
## @end deftypefn
function m = mean2 (I)
if (nargin != 1)
print_usage();
endif
m = mean (I(:));
endfunction
## Corner cases for Matlab compatibility (bug #51144)
%!test
%! ## This throws a division by zero warning which Matlab does not, but
%! ## that's because Matlab does not throw such warnings in the first
%! ## place. Octave does, so we do not turn the warning off.
%! warning ("off", "Octave:divide-by-zero", "local");
%! assert (mean2 ([]), NaN)
%!assert (mean2 (logical ([1 1; 0 0])), 0.5)
%!assert (mean2 (ones (3, 3, 3)), 1)
%!assert (mean2 (i), i)
%!assert (mean2 ([1 i]), [0.5+0.5i])
%!assert (mean2 (speye (3)), sparse (1/3))
image-2.12.0/inst/PaxHeaders.25054/roicolor.m 0000644 0000000 0000000 00000000062 13615546210 015402 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/roicolor.m 0000644 0001750 0001750 00000004143 13615546210 020146 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2004 Josep Mones i Teixidor
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{BW} =} roicolor (@var{A}, @var{low}, @var{high})
## @deftypefnx {Function File} {@var{BW} = } roicolor (@var{A},@var{v})
## Select a Region Of Interest of an image based on color.
##
## BW = roicolor(A,low,high) selects a region of interest (ROI) of an
## image @var{A} returning a black and white image in a logical array (1 for
## pixels inside ROI and 0 outside ROI), which is formed by all pixels
## whose values lie within the colormap range specified by [@var{low}
## @var{high}].
##
## BW = roicolor(A,v) selects a region of interest (ROI) formed by all
## pixels that match values in @var{v}.
## @end deftypefn
function BW = roicolor (A, p1, p2)
if (nargin < 2 || nargin > 3)
print_usage;
endif
if (nargin == 2)
if (!isvector(p1))
error("BW = roicolor(A, v): v should be a vector.");
endif
BW=logical(zeros(size(A)));
for c=p1
BW|=(A==c);
endfor
elseif (nargin==3)
if (!isscalar(p1) || !isscalar(p2))
error("BW = roicolor(A, low, high): low and high must be scalars.");
endif
BW=logical((A>=p1)&(A<=p2));
endif
endfunction
%!demo
%! roicolor([1:10],2,4);
%! % Returns '1' where input values are between 2 and 4 (both included).
%!assert(roicolor([1:10],2,4),logical([0,1,1,1,zeros(1,6)]));
%!assert(roicolor([1,2;3,4],3,3),logical([0,0;1,0]));
%!assert(roicolor([1,2;3,4],[1,4]),logical([1,0;0,1]));
image-2.12.0/inst/PaxHeaders.25054/applylut.m 0000644 0000000 0000000 00000000062 13615546210 015424 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/applylut.m 0000644 0001750 0001750 00000004203 13615546210 020165 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2004 Josep Mones i Teixidor
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{A} =} applylut (@var{BW}, @var{LUT})
## Uses lookup tables to perform a neighbour operation on binary images.
##
## A = applylut(BW,LUT) returns the result of a neighbour operation
## using the lookup table @var{LUT} which can be created by makelut.
##
## It first computes a matrix with the index of each element in the
## lookup table. To do this, it convolves the original matrix with a
## matrix which assigns each of the neighbours a bit in the resulting
## index. Then @var{LUT} is accessed to compute the result.
##
## @seealso{makelut}
## @end deftypefn
function A = applylut (BW, LUT)
if (nargin != 2)
print_usage;
endif
nq=log2(length(LUT));
n=sqrt(nq);
if (floor(n)!=n)
error ("applylut: LUT length is not as expected. Use makelut to create it.");
endif
w=reshape(2.^[nq-1:-1:0],n,n);
A=LUT(filter2(w,BW)+1);
endfunction
%!demo
%! lut = makelut (inline ('sum (x (:)) >= 3', 'x'), 3);
%! S = applylut (eye (5), lut);
%! disp (S)
%! ## Everything should be 0 despite a diagonal which doesn't reach borders.
## 2-by-2 test
%!assert (prod (applylut (eye (3), makelut (@(x) x(1) == 1, 2)) == eye (3)), [1 1 1]);
## 3-by-3 test
%!assert (prod (applylut (eye (3), makelut (@(x) x(2,2) == 1, 3)) == eye (3)), [1 1 1]);
%!assert (prod (applylut (eye (3), makelut (@(x) x(3,3) == 1, 3)) ==
%! applylut (eye (3), makelut (@(x) x(2,2) == 1, 2))),
%! [1 1 1]);
image-2.12.0/inst/PaxHeaders.25054/imdivide.m 0000644 0000000 0000000 00000000062 13615546210 015344 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imdivide.m 0000644 0001750 0001750 00000005350 13615546210 020111 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2011 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{out} =} imdivide (@var{a}, @var{b})
## @deftypefnx {Function File} {@var{out} =} imdivide (@var{a}, @var{b}, @var{class})
## Divide image by another image or constant.
##
## If @var{a} and @var{b} are two images of same size and class, @var{a} is divided
## by @var{b}. Alternatively, if @var{b} is a floating-point scalar, @var{a} is divided
## by it.
##
## The class of @var{out} will be the same as @var{a} unless @var{a} is logical
## in which case @var{out} will be double. Alternatively, it can be
## specified with @var{class}.
##
## @emph{Note}: the values are truncated to the mininum value of the output
## class.
## @seealso{imabsdiff, imadd, imcomplement, immultiply, imlincomb, imsubtract}
## @end deftypefn
function img = imdivide (img, val, out_class = class (img))
if (nargin < 2 || nargin > 3)
print_usage;
endif
[img, val] = imarithmetics ("imdivide", img, val, out_class);
## matlab doesn't even gives a warning in this situation, it simply returns
## a double precision float
if (nargin > 2 && strcmpi (out_class, "logical"))
warning ("Ignoring request to return logical as output of division.");
endif
warning ("off", "Octave:divide-by-zero", "local");
img = img ./ val;
endfunction
%!assert (imdivide (uint8 ([23 250]), uint8 ([ 2 50])), uint8 ([ 12 5])); # default to first class
%!assert (imdivide (uint8 ([56 255]), uint8 ([ 0 0])), uint8 ([255 255])); # dividing by zero works (tested in matlab)
%!assert (imdivide (uint8 ([23 250]), 2), uint8 ([ 12 125])); # works subtracting a scalar
%!assert (imdivide (uint8 ([23 250]), uint8 ([ 2 50]), "uint16"), uint16 ([ 12 5])); # defining output class works (not in matlab)
%!assert (imdivide (logical ([1 1 0 0]), logical ([1 0 1 0])), double ([1 Inf 0 NaN])); # dividing logical matrix (tested in matlab)
%!fail ("imdivide (uint8 ([23 250]), uint16 ([23 250]))"); # input needs to have same class
image-2.12.0/inst/PaxHeaders.25054/imtophat.m 0000644 0000000 0000000 00000000062 13615546210 015377 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imtophat.m 0000644 0001750 0001750 00000015307 13615546210 020147 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2005 Carvalho-Mariel
## Copyright (C) 2010-2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} imtophat (@var{img}, @var{SE})
## Perform morphological top hat filtering.
##
## 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 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 white = imtophat (img, se)
if (nargin != 2)
print_usage ();
elseif (! isimage (img))
error("imtophat: IMG must be a numeric matrix");
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 (img))
white = img & ! imopen (img, se);
else
white = img - imopen (img, se);
endif
endfunction
%!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);
image-2.12.0/inst/PaxHeaders.25054/imperspectivewarp.m 0000644 0000000 0000000 00000000062 13615546210 017323 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imperspectivewarp.m 0000644 0001750 0001750 00000012167 13615546210 022074 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2006 Søren 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 .
## -*- texinfo -*-
## @deftypefn {Function File} @var{warped} = imperspectivewarp(@var{im}, @var{P}, @var{interp}, @var{bbox}, @var{extrapval})
## Applies the spatial perspective homogeneous transformation @var{P} to the image @var{im}.
## The transformation matrix @var{P} must be a 3x3 homogeneous matrix, or 2x2 or 2x3
## affine transformation matrix.
##
## The optional argument @var{method} defines the interpolation method to be
## used. All methods supported by @code{interp2} can be used. By default, the
## @code{linear} method is used.
##
## For @sc{matlab} compatibility, the methods @code{bicubic} (same as
## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as
## @code{linear}) are also supported.
##
## By default the resulting image contains the entire warped image. In some situation
## you only parts of the warped image. The argument @var{bbox} controls this, and can
## be one of the following strings
## @table @code
## @item "loose"
## The entire warped result is returned. This is the default behavior.
## @item "crop"
## The central part of the image of the same size as the input image is returned.
## @item "same"
## The size and coordinate system, of the input image, are kept.
## @end table
##
## All values of the result that fall outside the original image will
## be set to @var{extrapval}. The default value of @var{extrapval} is 0.
##
## @seealso{imremap, imrotate, imresize, imshear, interp2}
## @end deftypefn
function [warped] = imperspectivewarp(im, P, interp = "linear", bbox = "loose", extrapolation_value = 0)
if (nargin < 2 || nargin > 5)
print_usage ();
elseif (! isimage (im))
error ("imperspectivewarp: IM must be a grayscale or RGB image.")
elseif (! ischar (interp))
error ("imperspectivewarp: INTERP must be a string with interpolation method")
elseif (! ischar (bbox) || ! any (strcmpi (bbox, {"loose", "crop", "same"})))
error ("imperspectivewarp: BBOX must be 'loose', 'crop' or 'same'");
elseif (! isscalar (extrapolation_value))
error ("imperspectivewarp: EXTRAPVAL must be a scalar");
endif
interp = interp_method (interp);
if (isnumeric (P) && ismatrix (P))
if (issquare(P) && rows(P) == 3) # 3x3 matrix
if (P(3,3) != 0)
P /= P(3,3);
else
error ("imperspectivewarp: P(3,3) must be non-zero");
endif
elseif (rows(P) == 2 && (columns(P) == 2 || columns(P) == 3)) # 2x2 or 2x3 matrix
P(3,3) = 1;
else # unsupported matrix size
error ("imperspectivewarp: transformation matrix must be 2x2, 2x3, or 3x3");
endif
else
error ("imperspectivewarp: transformation matrix not valid");
endif
## Do the transformation
[y, x, tmp] = size(im);
## Transform corners
corners = [1, 1, 1;
1, y, 1;
x, 1, 1;
x, y, 1]';
Tcorners = P*corners;
Tx = Tcorners(1,:)./Tcorners(3,:);
Ty = Tcorners(2,:)./Tcorners(3,:);
## Do cropping?
x1 = round(min(Tx)); x2 = round(max(Tx));
y1 = round(min(Ty)); y2 = round(max(Ty));
# FIXME: This seems to work fine for rotations, but
# somebody who knows computational geometry should
# be able to come up with a better algorithm.
if (strcmpi(bbox, "crop"))
xl = x2 - x1 + 1;
yl = y2 - y1 + 1;
xd = (xl - x)/2;
yd = (yl - y)/2;
x1 += xd; x2 -= xd;
y1 += yd; y2 -= yd;
elseif (strcmpi(bbox, "same"))
x1 = 1; x2 = x;
y1 = 1; y2 = y;
endif
## Transform coordinates
[X, Y] = meshgrid(x1:x2, y1:y2);
[sy, sx] = size(X);
D = [X(:), Y(:), ones(sx*sy, 1)]';
PD = inv(P)*D;
XI = PD(1,:)./PD(3,:);
YI = PD(2,:)./PD(3,:);
XI = reshape(XI, sy, sx);
YI = reshape(YI, sy, sx);
clear X Y D PD;
warped = imremap (im, XI, YI, interp, extrapolation_value);
endfunction
%!demo
%! ## Generate a synthetic image and show it
%! I = tril(ones(100)) + abs(rand(100)); I(I>1) = 1;
%! I(20:30, 20:30) = !I(20:30, 20:30);
%! I(70:80, 70:80) = !I(70:80, 70:80);
%! figure(), imshow(I);
%! ## Resize the image to the double size and show it
%! P = diag([1, 1, 0.5]);
%! warped = imperspectivewarp(I, P);
%! figure(), imshow(warped);
%!demo
%! ## Generate a synthetic image and show it
%! I = tril(ones(100)) + abs(rand(100)); I(I>1) = 1;
%! I(20:30, 20:30) = !I(20:30, 20:30);
%! I(70:80, 70:80) = !I(70:80, 70:80);
%! figure(), imshow(I);
%! ## Rotate the image around (0, 0) by -0.4 radians and show it
%! R = [cos(-0.4) sin(-0.4); -sin(-0.4) cos(-0.4)];
%! warped = imperspectivewarp(I, R, :, :, 0);
%! figure(), imshow(warped);
image-2.12.0/inst/PaxHeaders.25054/imbothat.m 0000644 0000000 0000000 00000000062 13615546210 015361 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imbothat.m 0000644 0001750 0001750 00000015745 13615546210 020137 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2005 Carvalho-Mariel
## Copyright (C) 2010-2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} imbothat (@var{img}, @var{SE})
## Perform morphological bottom hat filtering.
##
## 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.
##
## @seealso{imerode, imdilate, imopen, imclose, imtophat, mmgradm}
## @end deftypefn
function black = imbothat (img, se)
if (nargin != 2)
print_usage ();
elseif (! isimage (img))
error("imbothat: IMG must be a numeric matrix");
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 (img))
black = imclose (img, se) & ! img;
else
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);
image-2.12.0/inst/PaxHeaders.25054/otf2psf.m 0000644 0000000 0000000 00000000062 13615546210 015135 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/otf2psf.m 0000644 0001750 0001750 00000007535 13615546210 017711 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2015 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} otf2psf (@var{otf})
## @deftypefnx {Function File} {} otf2psf (@var{otf}, @var{outsize})
## Compute PSF from OTF.
##
## Returns the Point Spread Function (OTF) of the Optical Transfer
## Function @var{otf}.
##
## The optional argument @var{outsize} defines the size of the returned
## @var{psf}.
##
## @seealso{circshift, ifft2, ifftn, psf2otf}
## @end deftypefn
function psf = otf2psf (otf, outsize)
if (nargin < 1 || nargin > 2)
print_usage ();
elseif (! isnumeric (otf))
error ("otf2psf: OTF must be numeric")
endif
insize = size (otf);
psf = ifftn (otf);
psf = circshift (psf, floor (insize / 2));
if (nargin > 1)
if (! isnumeric (outsize) || ! isvector (outsize))
error ("otf2psf: OUTSIZE must be a numeric vector");
endif
insize = size (otf);
n = max (numel (outsize), numel (insize));
outsize = postpad (outsize(:), n, 1);
insize = postpad (insize(:) , n, 1);
pad = (insize - outsize) / 2;
if (any (pad < 0))
error ("otf2psf: OUTSIZE must be smaller than or equal than OTF size");
endif
prepad = floor (pad);
postpad = ceil (pad);
idx = arrayfun (@colon, prepad + 1, insize - postpad,
"UniformOutput", false);
psf = psf(idx{:});
endif
endfunction
## We are assuming psf2otf is working correctly
%!function otf = rand_otf (varargin)
%! otf = complex (rand (varargin{:}), rand (varargin{:}));
%!endfunction
## Basic usage, 1, 2, and 3 dimensional
%!test
%! otf = rand_otf (6, 1);
%! assert (otf2psf (otf), circshift (ifft (otf), 3));
%!test
%! otf = rand_otf (6, 6);
%! assert (otf2psf (otf), circshift (ifft2 (otf), [3 3]));
%!test
%! otf = rand_otf (6, 6, 6);
%! assert (otf2psf (otf), circshift (ifftn (otf), [3 3 3]));
## Test when length of some sides are odd
%!test
%! otf = rand_otf (7, 1);
%! assert (otf2psf (otf), circshift (ifft (otf), 3));
%!test
%! otf = rand_otf (7, 7);
%! assert (otf2psf (otf), circshift (ifft2 (otf), [3 3]));
%!test
%! otf = rand_otf (6, 7, 8);
%! assert (otf2psf (otf), circshift (ifftn (otf), [3 3 4]));
## Test the outsize/unpadding option
%!test
%! otf = rand_otf (7, 1);
%! ppsf = circshift (ifft (otf), 3);
%! assert (otf2psf (otf, 6), ppsf(1:6));
%! assert (otf2psf (otf, [6 1]), ppsf(1:6));
%!test
%! otf = rand_otf (7, 7);
%! ppsf = circshift (ifft2 (otf), [3 3]);
%! assert (otf2psf (otf, [6 1]), ppsf(1:6,4));
%!test
%! otf = rand_otf (6, 7);
%! ppsf = circshift (ifft2 (otf), [3 3]);
%! assert (otf2psf (otf, [6 6]), ppsf(:,1:6));
%!error otf2psf ("not a otf")
%!error otf2psf (rand_otf (16), 18)
%!error otf2psf (rand_otf (16), [14 18])
%!error otf2psf (rand_otf (16), [18 18])
%!error otf2psf (rand_otf (16, 1), 18)
## Some less random tests
%!test
%! psf = fspecial ("gaussian", 16);
%! otf = psf2otf (psf);
%! assert (otf2psf (otf), psf, eps);
%!test
%! psf = rand (16);
%! otf = psf2otf (psf);
%! assert (otf2psf (otf), psf, 4*eps);
%!test
%! psf = rand (8);
%! otf = psf2otf (psf, [16 16]);
%! assert (otf2psf (otf, [8 8]), psf, 2*eps);
image-2.12.0/inst/PaxHeaders.25054/imadd.m 0000644 0000000 0000000 00000000062 13615546210 014630 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imadd.m 0000644 0001750 0001750 00000007151 13615546210 017376 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2011 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{out} =} imadd (@var{a}, @var{b})
## @deftypefnx {Function File} {@var{out} =} imadd (@var{a}, @var{b}, @var{class})
## Add image or constant to an image.
##
## If @var{a} and @var{b} are two images of same size and class, the images are
## added. Alternatively, if @var{b} is a floating-point scalar, its value is added
## to the image @var{a}.
##
## The class of @var{out} will be the same as @var{a} unless @var{a} is logical
## in which case @var{out} will be double. Alternatively, it can be
## specified with @var{class}.
##
## @emph{Note 1}: you can force output class to be logical by specifying
## @var{class}. This is incompatible with @sc{matlab} which will @emph{not} honour
## request to return a logical matrix.
##
## @emph{Note 2}: the values are truncated to the maximum value of the output
## class.
## @seealso{imabsdiff, imcomplement, imdivide, imlincomb, immultiply, imsubtract}
## @end deftypefn
function img = imadd (img, val, out_class = class (img))
if (nargin < 2 || nargin > 3)
print_usage;
endif
[img, val] = imarithmetics ("imadd", img, val, out_class);
## output class is the same as input img, unless img is logical in which case
## it should be double. Tested in matlab by Freysh at ##matlab:
## - if you imadd 2 logical matrix, it's not the union. You actually get values of 2
## - the previous is true even if you specify "logical" as output class. It does
## not honors the request, output will be double class anyway, not even a
## warning will be issued (but we in octave are nicer and will)
## - you can specify smaller integer types for output than input and values
## are truncated. Input uint16 and request uint8, it will be respected
## this is matlab imcompatible on purpose. We are compatible and return double
## anyway, even if both input are logical (and wether this is correct is
## already debatable), but if the user forcedly requests output class to be
## logical, then he must be expecting it (matlab returns double anyway and
## ignores request).
if (nargin > 2 && strcmpi (out_class, "logical"))
img = img | val;
else
img = img + val;
endif
endfunction
%!assert (imadd (uint8 ([23 250]), uint8 ([23 250])), uint8 ([46 255])); # default to first class and truncate
%!assert (imadd (uint8 ([23 250]), 10), uint8 ([33 255])); # works adding a scalar
%!assert (imadd (uint8 ([23 250]), uint8 ([23 250]), "uint16"), uint16 ([46 500])); # defining output class works
%!assert (imadd (logical ([ 1 0]), logical ([ 1 1])), double ([ 2 1])); # return double for two logical images
%!assert (imadd (logical ([ 1 0]), logical ([ 1 1]), "logical"), logical ([ 1 1])); # this is matlab incompatible on purpose
%!fail ("imadd (uint8 ([23 250]), uint16 ([23 250]))"); # input need to have same class
image-2.12.0/inst/PaxHeaders.25054/imregionalmin.m 0000644 0000000 0000000 00000000062 13615546210 016404 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imregionalmin.m 0000644 0001750 0001750 00000007653 13615546210 021161 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2014 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} imregionalmin (@var{img})
## @deftypefnx {Function File} {} imregionalmin (@var{img}, @var{conn})
## Compute regional minima.
##
## Returns a logical matrix, same size as the input @var{img}, with the
## regional minima.
##
## The optional argument @var{conn}, defines the connectivity. It can
## be a scalar value or a boolean matrix (see @code{conndef} for details).
## Defaults to @code{conndef (ndims (@var{img}), "maximal")}
##
## Regional minima should not be mistaken with local minima. Local minima
## are pixels whose value is less or equal to all of its neighbors.
## A regional minima is the connected component of pixels whose values are
## all less than the neighborhood of the minima (the connected component,
## not its individual pixels).
## All pixels belonging to a regional minima are local minima, but the
## inverse is not true.
##
## @seealso{imreconstruct, imregionalmax}
## @end deftypefn
function bw = imregionalmin (img, conn)
if (nargin < 1 || nargin > 2)
print_usage ();
endif
if (nargin < 2)
conn = conndef (ndims (img), "maximal");
else
conn = conndef (conn);
endif
if (isfloat (img))
img = - img;
else
img = imcomplement (img);
endif
bw = imregionalmax (img, conn);
endfunction
%!test
%! a = [
%! 7 3 9 3 10 3
%! 4 2 3 10 1 3
%! 1 4 6 9 4 10
%! 8 7 9 3 4 8
%! 5 9 3 3 8 9
%! 3 6 9 4 1 10];
%!
%! a4 = logical ([
%! 0 0 0 1 0 0
%! 0 1 0 0 1 0
%! 1 0 0 0 0 0
%! 0 0 0 1 0 0
%! 0 0 1 1 0 0
%! 1 0 0 0 1 0]);
%! assert (imregionalmin (a, 4), a4)
%! assert (imregionalmin (uint8 (a), 4), a4)
%! assert (imregionalmin (int8 (a), 4), a4)
%!
%! a8 = logical ([
%! 0 0 0 0 0 0
%! 0 0 0 0 1 0
%! 1 0 0 0 0 0
%! 0 0 0 0 0 0
%! 0 0 0 0 0 0
%! 1 0 0 0 1 0]);
%! assert (imregionalmin (a), a8)
%! assert (imregionalmin (a, 8), a8)
%! assert (imregionalmin (uint8 (a), 8), a8)
%! assert (imregionalmin (int8 (a), 8), a8)
%!test
%! a = [
%! 4 8 5 -1 8 7
%! -1 4 0 7 1 1
%! 6 1 2 6 7 0
%! 6 1 5 -2 5 9
%! 1 4 -1 0 0 2
%! 4 6 1 0 7 1];
%!
%! a4 = logical ([
%! 0 0 0 1 0 0
%! 1 0 1 0 0 0
%! 0 1 0 0 0 1
%! 0 1 0 1 0 0
%! 1 0 1 0 0 0
%! 0 0 0 0 0 1]);
%! assert (imregionalmin (a, 4), a4)
%! assert (imregionalmin (int8 (a), 4), a4)
%!
%! a8 = logical ([
%! 0 0 0 1 0 0
%! 1 0 0 0 0 0
%! 0 0 0 0 0 1
%! 0 0 0 1 0 0
%! 0 0 0 0 0 0
%! 0 0 0 0 0 0]);
%! assert (imregionalmin (a), a8)
%! assert (imregionalmin (a, 8), a8)
%! assert (imregionalmin (int8 (a), 8), a8)
%!test
%! ## test float input images
%! im0 = peaks ();
%! im1 = im0 ./ 100;
%! max_pos_expected = [1; 49; 664; 1286; 1302; 2401];
%! max0 = imregionalmin (im0);
%! max0_pos = find (max0);
%! max1 = imregionalmin (im1);
%! assert (max1, max0)
%! assert (max0_pos, max_pos_expected)
image-2.12.0/inst/PaxHeaders.25054/subimage.m 0000644 0000000 0000000 00000000062 13615546210 015346 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/subimage.m 0000644 0001750 0001750 00000005070 13615546210 020112 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2014 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} subimage (@var{bw})
## @deftypefnx {Function File} {} subimage (@var{img})
## @deftypefnx {Function File} {} subimage (@var{rgb})
## @deftypefnx {Function File} {} subimage (@var{ind}, @var{cmap})
## @deftypefnx {Function File} {} subimage (@var{x}, @var{y}, @dots{})
## @deftypefnx {Function File} {@var{h} =} subimage (@dots{})
## Display images in subplots.
##
## A single figure, even with multiple subplots, is limited to a single
## colormap. With the exception of truecolor images, images will use the
## figure colormap which make it impossible to have multiple images with
## different display. This function transforms any image in truecolor
## to workaround this limitation.
##
## The new subimage is displayed as if using @code{image}. The optional
## arguments @var{x} and @var{y} are passed to @code{image} to specify the
## range of the axis labels.
##
## @seealso{image, imagesc, imshow, subplot}
## @end deftypefn
function h = subimage (varargin)
if (nargin < 1 || nargin > 4)
print_usage ();
endif
if (nargin < 3)
alternative_xy = false;
im_ind = 1;
else
alternative_xy = true;
im_ind = 3;
if (numel (varargin{1}) == 2 && numel (varargin{2}) == 2)
x = varargin{1};
y = varargin{2};
else
error ("subimage: X and Y must be two element vectors each");
endif
endif
im = varargin{im_ind};
if (numel (varargin) > im_ind)
rgb = ind2rgb (im, varargin{im_ind +1});
elseif (isbw (im))
rgb = repmat (im2uint8 (im), [1 1 3 1]);
elseif (isgray (im))
rgb = repmat (im, [1 1 3 1]);
elseif (isrgb (im))
rgb = im;
else
error ("subimage: no valid BW, IMG, IND, or RGB images in input arguments");
endif
if (alternative_xy)
tmp_h = image (x, y, rgb);
else
tmp_h = image (rgb);
endif
if (nargout > 0)
h = tmp_h;
endif
endfunction
image-2.12.0/inst/PaxHeaders.25054/houghlines.m 0000644 0000000 0000000 00000000062 13615546210 015717 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/houghlines.m 0000644 0001750 0001750 00000031007 13615546210 020462 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2017 Hartmut Gimpel
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{lines} =} @ houghlines (@var{BW}, @var{theta}, @var{rho}, @var{peaks})
## @deftypefnx {Function File} {@var{lines} =} @ houghlines (@dots{}, @var{property}, @var{value}, @dots{})
## Extract line segments from a Hough transform.
##
## This function takes as inputs the binary 2d image @var{BW} and the vectors @var{theta} and @var{rho} with the coodinates
## of the Hough transform (as returned by the @code{hough} function). Its @var{peaks} input is an n-by-2 array where each row
## contains the coodinates of a peak of interest in the Hough transform. (Those peaks in the Hough transform can be
## found with the @code{houghpeaks} function.)
##
## The result @var{lines} of this function contains information about all the line segments
## in the image @var{BW} that correspond to the given @var{peak} positions of the Hough transform.
## The @var{lines} output is a struct array where each of the elements has the following four
## components to describe a single line segment: @code{point1} has the xy-coordinates of the first pixel,
##@code{point2} the xy-coordinates of the last pixel, @code{theta} its angle to the vertical axis and @code{rho} its
## distance to the image origin. (output coordinate convention: [x, y] = [column, row])
##
## Additionally the following optional property-value-pairs can be used:
## @table @asis
## @item @var{FillGap}
## Gaps between line segments that are shorter or equal than @var{FillGap} will be ignored and both sides will still be
## considered as part of the same line segment.
## This value defaults to 20.
##
## @item @var{MinLength}
## Line segments that are shorter than @var{MinLength} will be suppressed in the output.
## This value defaults to 40.
## @end table
##
## @seealso{hough, houghpeaks}
## @end deftypefn
## Algorithm:
## The Matlab help page does not cite any reference
## for the algorithm of this function.
##
## For this Octave implementation the information
## on Matlab's help page, as well as the information
## from this book was used:
## "Digital Image Processing using Matlab"
## by R.C. Gonzalez, R. E. Woods and S. L. Eddins
## McGrawHill, 2nd edition 2010.
## (Chapter 10.2.2. "Toolbox Hough Functions")
##
## The result is the following straight forward (brute force?)
## implementation. The individual steps are commented
## in the code below.
function lines = houghlines (BW, theta, rho, peaks, varargin)
## retrieve the input parameters:
fillgap = [];
minlength = [];
if ((nargin < 4) || (nargin > 8) || any (nargin == [5, 7]))
print_usage ();
endif
for n = 5:2:(nargin-1) # process parameter-values pairs
if (strcmpi (varargin{n-4}, "fillgap"))
fillgap = varargin{n-4+1};
elseif (strcmpi (varargin{n-4}, "minlength"))
minlength = varargin{n-4+1};
else
error ("houghlines: invalid PROPERTY given")
endif
endfor
## set default parameters:
if (isempty (fillgap))
fillgap = 20;
endif
if (isempty (minlength))
minlength = 40;
endif
## check input parameters:
if (! isimage (BW) || ndims (BW) != 2)
error ("houghlines: BW must be a logical or numeric 2d array");
endif
if (! isimage (theta) || ! isnumeric (theta) || ! isvector (theta))
error ("houghlines: THETA must be a numeric vector");
endif
if (! isimage (rho) || ! isnumeric (rho) || ! isvector (rho))
error ("houghlines: RHO must be a numeric vector");
endif
if (! isimage (peaks) || ! isnumeric (peaks) || ndims (peaks) > 2 || size (peaks, 2) != 2)
error ("houghlines: PEAKS must be a n-by-2 numeric array");
endif
if (! isnumeric (fillgap) || ! isreal (fillgap) || fillgap <= 0 || ! isscalar (fillgap))
error ("houghlines: FILLGAP must be a positive scalar number");
endif
if (! isnumeric (minlength) || ! isreal (minlength) || minlength <= 0 || ! isscalar (minlength))
error ("houghlines: MINLENGTH must be a positive scalar number");
endif
## start the calculation:
lines = struct ([]);
numpeaks = size (peaks, 1);
numlines = 0;
## find all foreground pixels and transform their
## coordinates to conventions of Hough transform
## xy and (1,1) based
[allpixels_r, allpixels_c] = find (BW);
origin = [1 1];
allpixels_x = allpixels_c - origin(1);
allpixels_y = allpixels_r - origin(2);
## process each given Hough peak individually
for n = 1:numpeaks
rho_p_idx = peaks(n, 1);
theta_p_idx = peaks(n, 2);
rho_p = rho(rho_p_idx); # distance from "origin" pixel at (1,1)
theta_p = theta(theta_p_idx); # measured clockwise to the vertical axis, in degrees
## Find all the image pixels that belong to this
## Hough accumulator cell with theta_p and rho_p:
## (What rho would those pixels have, if they really had theta_p?)
## (rho2idx_factor is a precaution for when hough.m will be able
## to deal with the RhoResolution parameter.)
rho_all = allpixels_x .* cosd (theta_p) +allpixels_y .* sind (theta_p);
rho2idx_factor = (length (rho) -1) ./ (rho(end) - rho(1));
rho_all_idx = round(( rho_all - rho(1) ) .* rho2idx_factor) + 1;
peak_pixels_idx = find (rho_all_idx == rho_p_idx);
## transform coordinates to output convention: xy and (0,0) based
peak_pixels_x = allpixels_x(peak_pixels_idx) + origin(1);
peak_pixels_y = allpixels_y(peak_pixels_idx) + origin(2);
if (length (peak_pixels_x) == 0)
continue # avoid special cases for empty peak_pixel vectors
endif
## order those image pixels, the "faster" axis first:
## (to avoid excessive index jumps in "wide" lines)
x_span = max (peak_pixels_x) - min (peak_pixels_x);
y_span = max (peak_pixels_y) - min (peak_pixels_y);
if (x_span > y_span)
peak_pixels_yx = sortrows ([peak_pixels_y, peak_pixels_x], [1 2]);
else
peak_pixels_yx = sortrows ([peak_pixels_y, peak_pixels_x], [2 1]);
endif
peak_pixels = [peak_pixels_yx(:,2), peak_pixels_yx(:,1)]; # weired re-ordering needed for compatibility
## calculate the euclidean distance between adjacent (ordered) pixels:
dist = sqrt (diff (peak_pixels(:,1)).^2 + diff (peak_pixels(:,2)).^2);
## split line into segments, which are separated by more than fillgap:
## (always use very first and very last pixel in peak_pixels)
endpoint_idx = find (dist > fillgap);
num_peak_pixels = size (peak_pixels, 1);
endpoint_idx = [0; endpoint_idx; num_peak_pixels];
for m = 2 : length (endpoint_idx)
first_pixel = peak_pixels(endpoint_idx(m-1)+1, :); # point after last endpoint
last_pixel = peak_pixels(endpoint_idx(m), :); # this endpoint
length_segment = sqrt (sum((last_pixel - first_pixel).^2));
## save this segment if it is long enough:
if (length_segment < minlength)
continue;
else
numlines += 1;
lines(numlines).point1 = first_pixel;
lines(numlines).point2 = last_pixel;
lines(numlines).theta = theta_p;
lines(numlines).rho = rho_p;
endif
endfor # line segments
endfor # peaks
endfunction
%!shared BW0, theta0, rho0, peaks0_1, peaks0_2, lines0_1, lines0_2, BW1, theta1, rho1, peaks1, lines1
%! BW0 = logical([0 0 0 0 1; 0 0 0 1 0; 1 0 1 0 0; 0 1 0 0 0; 1 1 1 1 1]);
%! theta0 = [-90:89];
%! rho0 = [-7:7];
%! peaks0_1 = [11 130];
%! peaks0_2 = [11 130; 4 1];
%! lines0_1 = struct ("point1", {[1,5]}, "point2", {[5,1]}, "theta", {39}, "rho", {3});
%! lines0_2 = struct ("point1", {[1,5], [1,5]}, "point2", {[5,1],[5,5]}, "theta", {39,-90}, "rho", {3, -4});
%! BW1 = diag(ones(50,1));
%! theta1 = [-90:89];
%! rho1 = -70:70;
%! peaks1 = [71 46];
%! lines1 = struct ("point1", {[1 1]}, "point2", {[50 50]}, "theta", {-45}, "rho", {0});
## test input syntax:
%!error houghlines ()
%!error houghlines (BW1)
%!error houghlines (BW1, theta1)
%!error houghlines (BW1, theta1, rho1)
%!assert (houghlines (BW1, theta1, rho1, peaks1), lines1)
%!error (houghlines (BW1, theta1, rho1, peaks1, [1 2 3]))
%!assert (houghlines (BW1, theta1, rho1, peaks1, "FillGap", 5), lines1)
%!assert (houghlines (BW1, theta1, rho1, peaks1, "MinLength", 2), lines1)
%!assert (houghlines (BW1, theta1, rho1, peaks1, "FillGap", 5, "MinLength", 2), lines1)
%!assert (houghlines (BW1, theta1, rho1, peaks1, "MinLength", 2, "FillGap", 5), lines1)
%!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", 2, [1 2 3])
%!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", 2, "FillGap", 5, [1 2 3])
%!assert (houghlines (double (BW1), theta1, rho1, peaks1), lines1)
%!error houghlines (ones(5, 5, 5), theta1, rho1, peaks1)
%!error houghlines ("nonsense", theta1, rho1, peaks1)
%!error houghlines (BW1, ones(5), rho1, peaks1)
%!error houghlines (BW1, "nonsense", rho1, peaks1)
%!error houghlines (BW1, theta1, ones(5), peaks1)
%!error houghlines (BW1, theta1, "nonsense", peaks1)
%!error houghlines (BW1, theta1, rho1, ones(5))
%!error houghlines (BW1, theta1, rho1, ones(2,2,2))
%!error houghlines (BW1, theta1, rho1, "nonsense")
%!error houghlines (BW1, theta1, rho1, peaks1, "nonsense", 5)
%!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", -5)
%!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", [3 4])
%!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", "nonsense")
%!error houghlines (BW1, theta1, rho1, peaks1, "FillGap", -5)
%!error houghlines (BW1, theta1, rho1, peaks1, "FillGap", [3 4])
%!error houghlines (BW1, theta1, rho1, peaks1, "FillGap", "nonsense")
## output class and structure:
%!test
%! out = houghlines(BW0, theta0, rho0, peaks0_2, "MinLength", 1);
%! assert (out, lines0_2) # includes class = struct, size = [1,2]
%!test # for empty output
%! n = 100;
%! BW = false (n);
%! a = 50; % line starts at left side at row a
%! b = 3; % slope of line is 1:b
%! for column = 1:n
%! if (rem (column, b) == 0)
%! row = a - column/b;
%! BW(row, column) = true;
%! BW(row, column+1) = true;
%! end
%! end
%! theta = [-90: 89];
%! rho = [-141:141];
%! peaks = [188, 163];
%! out = houghlines(BW, theta, rho, peaks, 'FillGap', 1, 'MinLength', 5);
%! assert (out, struct([]))
## test calculation results:
%!test
%! out0_1 = houghlines(BW0, theta0, rho0, peaks0_1, 'MinLength', 1);
%! out0_2 = houghlines(BW0, theta0, rho0, peaks0_2, 'MinLength', 1);
%! assert (out0_1, lines0_1);
%! assert (out0_2, lines0_2);
%!test
%! out = houghlines(BW1, theta1, rho1, peaks1);
%! assert (out, lines1);
%!test
%! n = 100;
%! BW = false (n);
%! a = 50; % line starts at left side at row a
%! b = 3; % slope of line is 1:b
%! for column = 1:n
%! if (rem (column, b) == 0)
%! row = a - column/b;
%! BW(row, column) = true;
%! BW(row, column+1) = true;
%! end
%! end
%! theta = [-90:89];
%! rho = [-141:141];
%! peaks = [188, 163];
%! lines_1 = struct ("point1", {[99 17]}, "point2", {[3 49]}, "theta", {72}, "rho", {46});
%! out_1 = houghlines(BW, theta, rho, peaks);
%! out_n = houghlines(BW, theta, rho, peaks, 'FillGap', 1, 'MinLength', 1);
%! assert (out_1, lines_1)
%! assert (size (out_n), [1, 29])
## show instructive demo:
%!demo
%! I = checkerboard (30, 1, 1);
%! I = imnoise(I, "salt & pepper", 0.2);
%! figure, imshow (I);
%! title ("noisy image with some lines");
%! BW = edge (I, "canny");
%! figure, imshow(BW);
%! title ("edge image");
%! [H, theta, rho] = hough (BW);
%! figure, imshow (mat2gray (H), [], "XData", theta, "YData", rho);
%! title ("Hough transform of edge image \n 2 peaks marked");
%! axis on; xlabel("theta [degrees]"); ylabel("rho [pixels]");
%! peaks = houghpeaks (H, 2);
%! peaks_rho = rho(peaks(:,1));
%! peaks_theta = theta(peaks(:,2));
%! hold on; plot (peaks_theta, peaks_rho, "sr"); hold off;
%! lines = houghlines (BW, theta, rho, peaks);
%! figure, imshow (I), hold on;
%! for n = 1:length (lines)
%! points = [lines(n).point1; lines(n).point2];
%! plot (points(:,1), points(:,2), "r");
%! endfor
%! title ("the two strongest lines (edges) in the image"), hold off;
image-2.12.0/inst/PaxHeaders.25054/imtranslate.m 0000644 0000000 0000000 00000000062 13615546210 016075 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imtranslate.m 0000644 0001750 0001750 00000005104 13615546210 020637 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2002 Jeff Orchard
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{Y}} = imtranslate (@var{M}, @var{x}, @var{y})
## @deftypefnx {Function File} {@var{Y}} = imtranslate (@var{M}, @var{x}, @var{y}, @var{bbox})
## Translate a 2D image by (x,y) using Fourier interpolation.
##
## @var{M} is a matrix, and is translated to the right by @var{X} pixels
## and translated up by @var{Y} pixels.
##
## @var{bbox} can be either 'crop' or 'wrap' (default).
##
## @end deftypefn
function Y = imtranslate (X, nc, nr, bbox = "wrap")
if (strcmp (bbox, "crop"))
pre = post = [0 0];
if (nc > 0)
post(2) = ceil (nc);
else
pre(2) = ceil (nc);
endif
if (nr > 0)
pre(1) = ceil (nr);
else
post(1) = ceil (nr);
endif
pre = abs (pre);
post = abs (post);
X = padarray (X, abs (pre), "pre");
X = padarray (X, abs (post), "post");
endif
[dimy, dimx] = size(X);
x = fft2(X);
px = exp(-2*pi*i*nc*(0:dimx-1)/dimx);
py = exp(-2*pi*i*nr*(0:dimy-1)/dimy)'; % actually to correspond to index notation 'b' should be
% replaced with '-b'
% but I do not want to brake previous version compatibility
% note: it also must be done in the cropping iand padding code
P = py * px;
y = x .* P;
Y = real(ifft2(y)); % fft return complex number
% for integer shifts imaginary part is 0
% so real takes care of transfer from complex number to real
if (strcmp (bbox, "crop"))
Y = Y(pre(1)+1:dimy-post(1) , pre(2)+1:dimx-post(2));
endif
endfunction
%!test
%! obs = imtranslate (ones (5, 5), 2, 1, "crop");
%! exp = zeros (5, 5);
%! exp(1:4, 3:5) = 1;
%! assert (obs, exp, eps * 10)
%!
%! obs = imtranslate (ones (5, 5), -2, -1, "crop");
%! exp = zeros (5, 5);
%! exp(2:5, 1:3) = 1;
%! assert (obs, exp, eps * 10)
image-2.12.0/inst/PaxHeaders.25054/ordfiltn.m 0000644 0000000 0000000 00000000062 13615546210 015373 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/ordfiltn.m 0000644 0001750 0001750 00000011400 13615546210 020131 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2008 Søren 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} ordfiltn (@var{A}, @var{nth}, @var{domain})
## @deftypefnx {Function File} {} ordfiltn (@var{A}, @var{nth}, @var{domain}, @var{S})
## @deftypefnx {Function File} {} ordfiltn (@dots{}, @var{padding})
## N dimensional ordered filtering.
##
## Ordered filter replaces an element of @var{A} with the @var{nth} element
## element of the sorted set of neighbours defined by the logical
## (boolean) matrix @var{domain}.
## Neighbour elements are selected to the sort if the corresponding
## element in the @var{domain} matrix is true.
##
## The optional variable @var{S} is a matrix of size(@var{domain}).
## Values of @var{S} corresponding to nonzero values of domain are
## added to values obtained from @var{A} when doing the sorting.
##
## Optional variable @var{padding} determines how the matrix @var{A}
## is padded from the edges. See @code{padarray} for details.
##
## @seealso{medfilt2, padarray, ordfilt2}
## @end deftypefn
## This function is based on 'ordfilt2' by Teemu Ikonen
## which is released under GPLv2 or later.
function retval = ordfiltn (A, nth, domain, varargin)
## Check input
if (nargin < 3)
print_usage ();
elseif (! isnumeric (A) && ! islogical (A))
error ("ordfiltn: A must be a numeric or logical array");
elseif (! isscalar (nth) || nth <= 0 || fix (nth) != nth)
error ("ordfiltn: second input argument must be a positive integer");
elseif (! isnumeric (domain) && ! islogical (domain))
error ("ordfiltn: DOMAIN must be a numeric or logical array or scalar");
elseif (isscalar (domain) && (domain <= 0 || fix (domain) != domain))
error ("ordfiltn: third input argument must be a positive integer, when it is a scalar");
endif
if (isscalar (domain))
domain = true (repmat (domain, 1, ndims (A)));
endif
if (ndims (A) != ndims (domain))
error ("ordfiltn: first and second argument must have same dimensionality");
elseif (any (size (A) < size (domain)))
error ("ordfiltn: domain array cannot be larger than the data array");
endif
## Parse varargin
S = zeros (size (domain));
padding = 0;
for idx = 1:length(varargin)
opt = varargin{idx};
if (ischar (opt) || isscalar (opt))
padding = opt;
elseif (isnumeric (opt) && size_equal (opt, domain))
S = opt;
else
error ("ordfiltn: unrecognized option from input argument #%i and class %s", 3 + idx, class (opt));
endif
endfor
A = pad_for_sliding_filter (A, size (domain), padding);
## Perform the filtering
retval = __spatial_filtering__ (A, logical (domain), "ordered", S, nth);
endfunction
%!shared b, f, s
%! b = [ 0 1 2 3
%! 1 8 12 12
%! 4 20 24 21
%! 7 22 25 18];
%!
%! f = [ 8 12 12 12
%! 20 24 24 24
%! 22 25 25 25
%! 22 25 25 25];
%!assert (ordfiltn (b, 9, true (3)), f);
%!
%! f = [ 1 8 12 12
%! 8 20 21 21
%! 20 24 24 24
%! 20 24 24 24];
%!assert (ordfiltn (b, 8, true (3)), f);
%!
%! f = [ 1 2 8 12
%! 4 12 20 21
%! 8 22 22 21
%! 20 24 24 24];
%!assert (ordfiltn (b, 7, true (3), "symmetric"), f);
%!
%! f = [ 1 8 12 12
%! 4 20 24 21
%! 7 22 25 21
%! 7 22 25 21];
%!assert (ordfiltn (b, 3, true (3, 1)), f);
%!
%! f = [ 1 8 12 12
%! 4 20 24 18
%! 4 20 24 18
%! 4 20 24 18];
%!assert (ordfiltn (b, 3, true (4, 1)), f);
%!
%! f = [ 4 20 24 21
%! 7 22 25 21
%! 7 22 25 21
%! 7 22 25 21];
%!assert (ordfiltn (b, 4, true (4, 1)), f);
%!
%! s = [0 0 1
%! 0 0 1
%! 0 0 1];
%! f = [ 2 8 12 12
%! 9 20 22 21
%! 21 25 24 24
%! 21 25 24 24];
%!assert (ordfiltn (b, 8, true (3), s), f);
%!
%! b(:,:,2) = b(:,:,1) - 1;
%! b(:,:,3) = b(:,:,2) - 1;
%! f(:,:,1) = [ 1 8 11 11
%! 8 20 21 21
%! 20 24 24 24
%! 20 24 24 24];
%! f(:,:,2) = [ 6 10 11 11
%! 18 22 22 22
%! 20 24 24 24
%! 20 24 24 24];
%! f(:,:,3) = [ 0 7 10 10
%! 7 19 20 20
%! 19 23 23 23
%! 19 23 23 23];
%!assert (ordfiltn (b, 25, true (3, 3, 3)), f);
image-2.12.0/inst/PaxHeaders.25054/integralImage3.m 0000644 0000000 0000000 00000000062 13615546210 016405 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/integralImage3.m 0000644 0001750 0001750 00000007545 13615546210 021162 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2019 Avinoam Kalma
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} integralImage (@var{img})
## Calculate the 3D integral image.
##
## @var{img} is the input image for 3D integral image calculation.
##
## The value of the 3D integral image is J = cumsum (cumsum (cumsum (I), 2), 3)
## with padding.
## The padding adds a zeros plane, zero rows and zero column, so size (J) = size (I) + 1.
##
## @seealso{integralImage, cumsum}
## @end deftypefn
function J = integralImage3 (I)
if (nargin != 1)
print_usage ();
endif
if (! isimage (I))
error ("integralImage3: I should be an image");
endif
if (ndims (I) > 3)
error ("integralImage3: I should be a 3-dimensional image");
endif
if (! isa (I, "double"))
I = double (I);
endif
J = cumsum (cumsum (cumsum (I), 2), 3);
J = padarray (J, [1 1 1], "pre");
endfunction
%!test
%! assert (integralImage3 (zeros (4)), zeros (5, 5, 2));
%!test
%! J_res = zeros (2, 2, 2);
%! J_res(2, 2, 2) = 10;
%! assert (integralImage3 (10), J_res);
%!test
%! J = integralImage3 (10);
%! assert (class (J), "double");
%! J = integralImage3 (uint8 (10));
%! assert (class (J), "double");
%!test
%! I = [1, 2; 3, 4];
%! J = integralImage3 (I);
%! J_res = zeros (3, 3, 2);
%! J_res(2:3, 2:3, 2) = [1 3; 4 10];
%! assert (J, J_res)
%!test
%! I1 = [1, 2; 3, 4];
%! I2 = [5, 6; 7, 8];
%! I3 = [9, 10; 11, 12];
%! I = cat (3, I1, I2, I3);
%! J = integralImage3 (I);
%! J2 = [0 0 0; 0 1 3; 0 4 10];
%! J3 = [0 0 0; 0 6 14; 0 16 36];
%! J4 = [0 0 0; 0 15 33; 0 36 78];
%! J_res = cat (3, zeros (3), J2, J3, J4);
%! assert (J, J_res)
%!test
%! I = magic (5);
%! J = integralImage3 (I);
%! J_res = zeros (6, 6, 2);
%! J_res(:, :, 2) = [0 0 0 0 0 0;
%! 0 17 41 42 50 65;
%! 0 40 69 77 99 130;
%! 0 44 79 100 142 195;
%! 0 54 101 141 204 260;
%! 0 65 130 195 260 325];
%! assert (J, J_res)
%!# test of 3d input image:
%!test
%! K = magic (8);
%! K = reshape (K, [4 4 4]);
%! L = integralImage3 (K);
%! L1_ML = zeros (5);
%! L2_ML = [0 0 0 0 0;
%! 0 64 96 98 132;
%! 0 73 146 203 260;
%! 0 90 212 316 388;
%! 0 130 260 390 520];
%! L3_ML = [0 0 0 0 0;
%! 0 67 134 197 260;
%! 0 130 260 390 520;
%! 0 193 386 583 780;
%! 0 260 520 780 1040];
%! L4_ML = [0 0 0 0 0;
%! 0 127 222 291 392;
%! 0 203 406 593 780;
%! 0 287 606 903 1168;
%! 0 390 780 1170 1560];
%! L5_ML = [0 0 0 0 0;
%! 0 134 268 394 520;
%! 0 260 520 780 1040;
%! 0 386 772 1166 1560;
%! 0 520 1040 1560 2080];
%! L_ML = cat (3, L1_ML, L2_ML, L3_ML, L4_ML, L5_ML);
%! assert (L, L_ML)
%!# test of 2d input image:
%!test
%! X = ones (3);
%! Y = integralImage3 (X);
%! Y_ML = zeros (4, 4, 2);
%! Y_ML(:, :, 2) = [0 0 0 0; 0 1 2 3; 0 2 4 6; 0 3 6 9];
%! assert(Y, Y_ML);
%!error
%! integralImage3 ();
%!error
%! integralImage3 (zeros (3), zeros (3));
%!error
%! integralImage3 ("abcd");
%!error
%! integralImage3 (1+i);
%!error
%! integralImage3 (reshape (1:81, 3, 3, 3, 3));
image-2.12.0/inst/PaxHeaders.25054/houghtf.m 0000644 0000000 0000000 00000000062 13615546210 015216 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/houghtf.m 0000644 0001750 0001750 00000007607 13615546210 017772 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2008 Søren 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 .
## -*- texinfo -*-
## @deftypefn {Function File} @var{H} = houghtf (@var{bw})
## @deftypefnx{Function File} @var{H} = houghtf (@var{bw}, @var{method})
## @deftypefnx{Function File} @var{H} = houghtf (@var{bw}, @var{method}, @var{arg})
## Perform the Hough transform for lines or circles.
##
## The @var{method} argument chooses between the Hough transform for lines and
## circles. It can be either "line" (default) or "circle".
##
## @strong{Line Detection}
##
## If @var{method} is "line", the function will compute the Hough transform for
## lines. A line is parametrised in @var{r} and @var{theta} as
## @example
## @var{r} = x*cos(@var{theta}) + y*sin(@var{theta}),
## @end example
## where @var{r} is distance between the line and the origin, while @var{theta}
## is the angle of the vector from the origin to this closest point. The result
## @var{H} is an @var{N} by @var{M} matrix containing the Hough transform. Here,
## @var{N} is the number different values of @var{r} that has been attempted.
## This is computed as @code{2*diag_length - 1}, where @code{diag_length} is
## the length of the diagonal of the input image. @var{M} is the number of
## different values of @var{theta}. These can be set through the third input
## argument @var{arg}. This must be a vector of real numbers, and is by default
## @code{pi*(-90:90)/180}.
##
## @strong{Circle Detection}
##
## If @var{method} is "circle" the function will compute the Hough transform for
## circles. The circles are parametrised in @var{r} which denotes the radius of
## the circle. The third input argument @var{arg} must be a real vector containing
## the possible values of @var{r}.
## If the input image is @var{N} by @var{M}, then the result @var{H} will be an
## @var{N} by @var{M} by @var{K} array, where @var{K} denotes the number of
## different values of @var{r}.
##
## As an example, the following shows how to compute the Hough transform for circles
## with radius 3 or 7 in the image @var{im}
## @example
## bw = edge(im);
## H = houghtf(bw, "circle", [3, 7]);
## @end example
## Here @var{H} will be an NxMx2 array, where @var{H}(:,:,1) will contain the
## Hough transform for circles with radius 3, and @var{H}(:,:,2) for radius 7.
## To find good circles you now need to find local maximas in @var{H}. If you
## find a local maxima in @var{H}(row, col, 1) it means that a good circle exists
## with center (row,col) and radius 3. One way to locate maximas is to use the
## @code{immaximas} function.
##
## @seealso{hough_line, hough_circle, immaximas}
## @end deftypefn
function [accum, R] = houghtf(bw, varargin)
## Default arguments
method = "line";
args = {};
## Check input arguments
if (nargin == 0)
error("houghtf: not enough input arguments");
endif
if (! ismatrix (bw))
error("houghtf: BW must be a 2-dimensional matrix");
endif
if (nargin > 1)
if (ischar(varargin{1}))
method = varargin{1};
args = varargin(2:end);
else
args = varargin;
endif
endif
## Choose method
switch (lower(method))
case "line"
[accum, R] = hough_line(bw, args{:});
case "circle"
accum = hough_circle(bw, args{:});
otherwise
error("houghtf: unsupported method '%s'", method);
endswitch
endfunction
image-2.12.0/inst/PaxHeaders.25054/bwunpack.m 0000644 0000000 0000000 00000000062 13615546210 015364 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/bwunpack.m 0000644 0001750 0001750 00000007166 13615546210 020140 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
## Copyright (C) 2018 David Miguel Susano Pinto
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {} bwunpack (@var{bwp})
## @deftypefnx {} {} bwunpack (@var{bwp}, @var{m})
## Unpack binary image.
##
## Each row of the packed binary image @var{bwp}, represented as an
## uint32 matrix, is unpacked into 32 rows of logical values. The
## unpacking is done such that the least significant bit of the first
## element in @var{bwp} maps to the first element in the unpacked
## image, and the most significant bit to the 32th element.
##
## The unpacked image will be a logical array with @var{m} rows.
## The length of the other dimensions will be the same as @var{bwp}.
## If @var{m} is not specified, it will unpack all bits in @var{bwp},
## otherwise the extra bits will be considered padding resulting
## from the packing. See the help text for @code{bwpack} for
## details.
##
## @seealso{bwpack, bitpack, bitunpack}
## @end deftypefn
function bw = bwunpack (bwp, m)
if (nargin < 1)
print_usage ();
endif
if (! isa (bwp, "uint32"))
error ("Octave:invalid-input-arg", "bwunpack: BWP must be an uint32 array");
endif
class_size = 32; # number of elements packed into a uint32
if (nargin < 2)
m = rows (bwp) * class_size;
elseif (m < 0 || fix (m) != m)
error ("Octave:invalid-input-arg",
"bwunpack: M must be a non-negative integer");
elseif (m > (rows (bwp) * class_size))
error ("Octave:invalid-input-arg",
["bwunpack: M must not be larger than the number of bits " ...
"on each column"]);
endif
packed_rows = rows (bwp) * class_size;
dims = size (bwp);
bw = reshape (bitunpack (bwp), [packed_rows dims(2:end)]);
bw(m+1:end,:) = []; # remove padding if any
endfunction
%!error id=Octave:invalid-fun-call bwunpack ()
%!error bwunpack (uint8 (1))
%!error bwunpack (uint32 (1), -1)
%!error bwunpack (uint32 (1), 4.2)
%!xtest
%! ## bug #55521
%! assert (bwunpack (uint32 (2.^[0:31])), logical (eye (32)))
%!xtest
%! ## bug #55521
%! assert (bwunpack (uint32 (repmat (7, [1 3 3 3])), 3), true (3, 3, 3, 3))
%!assert (bwunpack (uint32 (zeros (0, 0))), false (0, 0))
%!assert (bwunpack (uint32 (zeros (0, 0)), 0), false (0, 0))
%!assert (bwunpack (uint32 (zeros (0, 5)), 0), false (0, 5))
%!assert (bwunpack (uint32 (zeros (0, 5, 7)), 0), false (0, 5, 7))
%!assert (bwunpack (uint32 (zeros (1, 0))), false (32, 0))
%!assert (bwunpack (uint32 (zeros (2, 0, 7))), false (64, 0, 7))
%!assert (bwunpack (uint32 (zeros (2, 0, 7))), false (64, 0, 7))
%!assert (bwunpack (uint32 (zeros (2, 0, 7)), 60), false (60, 0, 7))
## Unpacking more bytes than what's available on the input needs to be
## an error. This works in Matlab but it's their bug and the result
## is not even reproducible.
%!error
%! bwunpack (uint32 (1), 1042)
image-2.12.0/inst/PaxHeaders.25054/lab2single.m 0000644 0000000 0000000 00000000062 13615546210 015574 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/lab2single.m 0000644 0001750 0001750 00000007656 13615546210 020354 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2016 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} lab2double (@var{lab})
## Convert L*a*b* data to single precision.
##
## @var{lab} must be a L*a*b* image or colormap, i.e., its dimensions
## must be MxNx3xK or Mx3. Its type must be double, single, uint16,
## or uint8.
##
## When converted to single, L* values range from 0 to 100, while a* and
## b* range from -128 to 127. When converting from uint16, the upper limit
## is 65280 (higher values will be converted above the range).
##
## @seealso{lab2double, lab2rgb, lab2single, lab2uint8, lab2uin16, lab2xyz}
## @end deftypefn
function [lab] = lab2single (lab)
if (nargin () != 1)
print_usage ();
endif
lab = lab2cls (lab, "single");
endfunction
## Instead of testing the lab2single function here, we test the
## conversion from single type. The actual tests for lab2single,
## are spread all other lab2* functions. This makes the tests
## simpler.
%!test
%! l_max_f = 100 + (25500 / 65280);
%! ab_max_f = 127 + (255 / 256);
%! cm = [
%! -Inf
%! Inf
%! NaN
%! l_max_f
%! ab_max_f
%! -200
%! -129
%! -128
%! -128+(255/65280)*(0.499)
%! -128+(255/65280)*(0.500)
%! -128+(255/65280)*(0.501)
%! -127
%! -1
%! 0
%! (100/65280)*(0.499999)
%! (100/65280)*(0.51)
%! (100/65280)*(0.500001)
%! 1
%! 99
%! 100
%! 101
%! 126
%! 127
%! 128
%! 254
%! 255
%! 256
%! 257];
%! cm = repmat (single (cm), [1 3]);
%! im2d = reshape (cm, [7 4 3]);
%! imnd = permute (im2d, [1 4 3 2]);
%!
%! cm_uint8 = uint8 ([
%! 0 0 0
%! 255 255 255
%! 255 255 255
%! 255 228 228
%! 255 255 255
%! 0 0 0
%! 0 0 0
%! 0 0 0
%! 0 0 0
%! 0 0 0
%! 0 0 0
%! 0 1 1
%! 0 127 127
%! 0 128 128
%! 0 128 128
%! 0 128 128
%! 0 128 128
%! 3 129 129
%! 252 227 227
%! 255 228 228
%! 255 229 229
%! 255 254 254
%! 255 255 255
%! 255 255 255
%! 255 255 255
%! 255 255 255
%! 255 255 255
%! 255 255 255]);
%!
%! assert (lab2uint8 (cm), cm_uint8)
%! im2d_uint8 = reshape (cm_uint8, [7 4 3]);
%! assert (lab2uint8 (im2d), im2d_uint8)
%! assert (lab2uint8 (imnd), permute (im2d_uint8, [1 4 3 2]))
%!
%! cm_uint16 = uint16 ([
%! 0 0 0
%! 65535 65535 65535
%! 65535 65535 65535
%! 65535 58468 58468
%! 65535 65535 65535
%! 0 0 0
%! 0 0 0
%! 0 0 0
%! 0 0 0
%! 0 1 1
%! 0 1 1
%! 0 256 256
%! 0 32512 32512
%! 0 32768 32768
%! 0 32768 32768
%! 1 32768 32768
%! 1 32768 32768
%! 653 33024 33024
%! 64627 58112 58112
%! 65280 58368 58368
%! 65535 58624 58624
%! 65535 65024 65024
%! 65535 65280 65280
%! 65535 65535 65535
%! 65535 65535 65535
%! 65535 65535 65535
%! 65535 65535 65535
%! 65535 65535 65535]);
%!
%! assert (lab2uint16 (cm), cm_uint16)
%! im2d_uint16 = reshape (cm_uint16, [7 4 3]);
%! assert (lab2uint16 (im2d), im2d_uint16)
%! assert (lab2uint16 (imnd), permute (im2d_uint16, [1 4 3 2]))
%!
%! assert (lab2double (cm), double (cm))
%! assert (lab2double (im2d), double (im2d))
%! assert (lab2double (imnd), double (imnd))
image-2.12.0/inst/PaxHeaders.25054/imshear.m 0000644 0000000 0000000 00000000062 13615546210 015202 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imshear.m 0000644 0001750 0001750 00000010207 13615546210 017744 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2002 Jeff Orchard
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} imshear (@var{M}, @var{axis}, @var{alpha}, @var{bbox})
## Applies a shear to the image @var{M}.
##
## The argument @var{M} is either a matrix or an RGB image.
##
## @var{axis} is the axis along which the shear is to be applied, and can
## be either 'x' or 'y'.
## For example, to shear sideways is to shear along the 'x' axis. Choosing
## 'y' causes an up/down shearing.
##
## @var{alpha} is the slope of the shear. For an 'x' shear, it is the
## horizontal shift (in pixels) applied to the pixel above the
## center. For a 'y' shear, it is the vertical shift (in pixels)
## applied to the pixel just to the right of the center pixel.
##
## NOTE: @var{alpha} does NOT need to be an integer.
##
## @var{bbox} can be one of 'loose', 'crop' or 'wrap'.
## 'loose' allows the image to grow to accommodate the new transformed image.
## 'crop' keeps the same size as the original, clipping any part of the image
## that is moved outside the bounding box.
## 'wrap' keeps the same size as the original, but does not clip the part
## of the image that is outside the bounding box. Instead, it wraps it back
## into the image.
##
## If called with only 3 arguments, @var{bbox} is set to 'loose' by default.
## @end deftypefn
function g = imshear(m, axis, alpha, bbox, noshift)
# The code below only does y-shearing. This is because of
# the implementation of fft (operates on columns, but not rows).
# So, transpose first for x-shearing.
if ( strcmp(axis, "x")==1 )
m = m';
endif
if ( nargin < 4 )
bbox = "loose";
noshift = 0;
elseif ( nargin < 5 )
noshift = 0;
endif
[ydim_orig xdim_orig] = size(m);
if ( strcmp(bbox, "wrap") == 0 )
ypad = ceil( (xdim_orig+1)/2 * abs(alpha) );
m = padarray (m, ypad);
endif
[ydim_new xdim_new] = size(m);
xcentre = ( xdim_new + 1 ) / 2;
ycentre = ( ydim_new + 1 ) / 2;
# This applies FFT to columns of m (x-axis remains a spatial axis).
# Because the way that fft and fftshift are implemented, the origin
# will move by 1/2 pixel, depending on the polarity of the image
# dimensions.
#
# If dim is even (=2n), then the origin of the fft below is located
# at the centre of pixel (n+1). ie. if dim=16, then centre is at 9.
#
# If dim is odd (=2n+1), then the origin of the fft below is located
# at the centre of pixel (n). ie. if dim=15, then centre is at 8.
if ( noshift==1 )
M = fft(m);
else
#M = imtranslate(fft(imtranslate(m, -xcentre, ycentre, "wrap")), xcentre, -ycentre, "wrap");
M = fftshift(fft(fftshift(m)));
endif
[ydim xdim] = size(m);
x = zeros(ydim, xdim);
# Find coords of the origin of the image.
if ( noshift==1 )
xc_coord = 1;
yc_coord = 1;
l = (1:ydim)' - yc_coord;
r = (1:xdim) - xc_coord;
if ( strcmp(bbox, "wrap")==1 )
l((ydim/2):ydim) = l((ydim/2):ydim) - ydim;
r((xdim/2):xdim) = r((xdim/2):xdim) - xdim;
endif
else
xc_coord = (xdim+1)/2;
yc_coord = (ydim+1)/2;
l = (1:ydim)' - yc_coord;
r = (1:xdim) - xc_coord;
endif
x = l * r;
Ms = M.* exp(2*pi*I*alpha/ydim * x);
if ( noshift==1 )
g = abs(ifft(Ms));
else
#g = abs(imtranslate( ifft( imtranslate(Ms, -xcentre, ycentre, "wrap") ), xcentre, -ycentre, "wrap"));
g = abs( fftshift(ifft(ifftshift(Ms))) );
endif
if ( strcmp(bbox, "crop")==1 )
g = g(ypad+1:ydim_orig+ypad, :);
endif
# Un-transpose if x-shearing was wanted
if ( strcmp(axis, "x")==1 )
g = g';
endif
endfunction
image-2.12.0/inst/PaxHeaders.25054/bwperim.m 0000644 0000000 0000000 00000000062 13615546210 015217 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/bwperim.m 0000644 0001750 0001750 00000023000 13615546210 017754 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} bwperim (@var{bw})
## @deftypefnx {Function File} {} bwperim (@var{bw}, @var{conn})
## Find perimeter of objects in binary images.
##
## Values from the matrix @var{bw} are considered part of an object perimeter
## if their value is non-zero and is connected to at least one zero-valued
## element, or to the outside of @var{bw}.
##
## Element connectivity @var{conn}, to define the size of objects, can be
## specified with a numeric scalar (number of elements in the neighborhood):
##
## @table @samp
## @item 4 or 8
## for 2 dimensional matrices;
## @item 6, 18 or 26
## for 3 dimensional matrices;
## @end table
##
## or with a binary matrix representing a connectivity array. Defaults to
## @code{conndef (ndims (@var{bw}), "minimal")} which is equivalent to
## @var{conn} of 4 and 6 for 2 and 3 dimensional matrices respectively.
##
## @seealso{bwarea, bwboundaries, imerode, mmgrad}
## @end deftypefn
function varargout = bwperim (bw, conn)
if (nargin < 1 || nargin > 2)
print_usage ();
endif
if (! isnumeric (bw) && ! islogical (bw))
error("bwperim: BW must be a numeric or logical matrix");
endif
bw = logical (bw);
if (nargin < 2)
conn = conndef (ndims (bw), "minimal");
else
conn = conndef (conn);
endif
## Recover the elements that would get removed by erosion
perim = (! imerode (bw, conn)) & bw;
## Get the borders back which are removed during erosion
## FIXME this is a bit too convoluted and not elegant at all. I am also
## unsure if it is correct for N dimensional stuff and unusual
## connectivities. We should probably be using the output from
## bwboundaries() but bwboundaries() seems buggy in the case of
## holes.
tmp_idx = repmat ({":"}, [1 ndims(perim)]);
tmp_conn_idx = repmat ({":"}, [1 ndims(conn)]);
p_size = size (perim);
for dim = 1:min (ndims (perim), ndims (conn))
conn_idx = tmp_conn_idx;
conn_idx{dim} = [1 3];
if (! any (conn(conn_idx{:})(:)))
continue
endif
idx = tmp_idx;
idx{dim} = [1 p_size(dim)];
perim(idx{:}) = bw(idx{:});
endfor
if (nargout > 0)
varargout{1} = perim;
else
imshow (perim);
endif
endfunction
%!test
%! in = [ 1 1 1 1 0 1 1 0 1 1
%! 1 1 0 1 1 1 1 1 1 0
%! 1 1 1 0 1 1 1 1 1 1
%! 1 1 1 1 0 1 1 1 0 1
%! 1 1 1 0 1 1 1 1 1 0
%! 1 1 1 1 1 1 0 1 0 1
%! 1 1 1 1 1 1 1 1 1 0
%! 1 1 1 1 1 1 1 1 1 1
%! 1 1 1 1 1 1 0 0 1 1
%! 1 1 1 1 0 1 0 1 1 0];
%!
%! out = [1 1 1 1 0 1 1 0 1 1
%! 1 1 0 1 1 0 0 1 1 0
%! 1 0 1 0 1 0 0 0 1 1
%! 1 0 0 1 0 1 0 1 0 1
%! 1 0 1 0 1 0 1 0 1 0
%! 1 0 0 1 0 1 0 1 0 1
%! 1 0 0 0 0 0 1 0 1 0
%! 1 0 0 0 0 0 1 1 0 1
%! 1 0 0 0 1 1 0 0 1 1
%! 1 1 1 1 0 1 0 1 1 0];
%! assert (bwperim (in), logical (out))
%! assert (bwperim (in, 4), logical (out))
%!
%! out = [1 1 1 1 0 1 1 0 1 1
%! 1 1 0 1 1 1 1 1 1 0
%! 1 1 1 0 1 1 0 1 1 1
%! 1 0 1 1 0 1 0 1 0 1
%! 1 0 1 0 1 1 1 1 1 0
%! 1 0 1 1 1 1 0 1 0 1
%! 1 0 0 0 0 1 1 1 1 0
%! 1 0 0 0 0 1 1 1 1 1
%! 1 0 0 1 1 1 0 0 1 1
%! 1 1 1 1 0 1 0 1 1 0];
%! assert (bwperim (in, 8), logical (out))
%!
%! out = [1 1 1 1 0 1 1 0 1 1
%! 1 0 0 0 0 1 0 0 1 0
%! 1 0 0 0 0 0 0 1 0 1
%! 1 0 1 0 0 0 0 0 0 1
%! 1 0 0 0 0 1 0 1 0 0
%! 1 0 0 0 1 0 0 0 0 1
%! 1 0 0 0 0 0 0 1 0 0
%! 1 0 0 0 0 1 1 0 0 1
%! 1 0 0 1 0 1 0 0 1 1
%! 1 1 1 1 0 1 0 1 1 0];
%! assert (bwperim (in, [1 0 0; 0 1 0; 0 0 1]), logical (out))
## test that any non-zero value is valid (even i and Inf)
%!test
%! in = [ 0 0 0 0 0 0 0
%! 0 0 5 0 0 1 9
%! 0 Inf 9 7 0 0 0
%! 0 1.5 5 7 1 0 0
%! 0 0.5 -1 89 i 0 0
%! 0 4 10 15 1 0 0
%! 0 0 0 0 0 0 0];
%! out = [0 0 0 0 0 0 0
%! 0 0 1 0 0 1 1
%! 0 1 0 1 0 0 0
%! 0 1 0 0 1 0 0
%! 0 1 0 0 1 0 0
%! 0 1 1 1 1 0 0
%! 0 0 0 0 0 0 0];
%! assert (bwperim (in), logical (out))
## test for 3D
%!test
%! in = reshape (magic(16), [8 8 4]) > 50;
%! out(:,:,1) = [
%! 1 1 0 1 0 1 1 1
%! 0 1 1 1 1 1 0 1
%! 0 1 1 1 1 1 0 1
%! 1 1 0 1 1 1 1 1
%! 1 1 1 1 1 1 1 1
%! 1 1 1 0 1 0 1 1
%! 1 1 1 0 1 0 1 1
%! 1 0 1 1 1 1 1 0];
%! out(:,:,2) = [
%! 1 1 0 1 0 1 1 1
%! 0 1 1 0 1 1 0 1
%! 0 1 0 0 0 1 0 1
%! 1 0 1 0 0 0 1 1
%! 1 0 0 1 0 1 0 1
%! 1 0 1 0 1 0 1 1
%! 1 1 1 0 1 0 1 1
%! 1 0 1 1 1 1 1 0];
%! out(:,:,3) = [
%! 1 1 0 1 0 1 1 1
%! 0 1 1 0 1 1 0 1
%! 0 1 0 0 0 1 0 1
%! 1 0 0 0 0 0 1 1
%! 1 0 0 1 0 1 0 1
%! 1 0 1 0 1 0 1 1
%! 1 1 1 0 1 0 1 1
%! 1 0 1 1 1 1 1 0];
%! out(:,:,4) = [
%! 1 1 0 1 0 1 1 1
%! 0 1 1 1 1 1 0 1
%! 0 1 1 1 1 1 0 1
%! 1 1 1 1 1 1 1 1
%! 1 1 1 1 1 1 1 0
%! 1 1 1 0 1 0 1 1
%! 1 1 1 0 1 0 1 1
%! 1 0 1 1 1 1 1 0];
%! assert (bwperim (in), logical (out))
%!
%! out(:,:,1) = [
%! 1 1 0 1 0 1 1 1
%! 0 1 1 1 1 1 0 1
%! 0 1 1 1 1 1 0 1
%! 1 1 0 1 1 1 1 1
%! 1 1 1 1 1 1 1 1
%! 1 1 1 0 1 0 1 1
%! 1 1 1 0 1 0 1 1
%! 1 0 1 1 1 1 1 0];
%! out(:,:,2) = [
%! 1 1 0 1 0 1 1 1
%! 0 1 1 1 1 1 0 1
%! 0 1 1 0 0 1 0 1
%! 1 1 1 1 0 1 1 1
%! 1 0 1 1 1 1 1 1
%! 1 0 1 0 1 0 1 1
%! 1 1 1 0 1 0 1 1
%! 1 0 1 1 1 1 1 0];
%! out(:,:,3) = [
%! 1 1 0 1 0 1 1 1
%! 0 1 1 1 1 1 0 1
%! 0 1 0 0 0 1 0 1
%! 1 1 0 0 0 1 1 1
%! 1 0 1 1 1 1 1 1
%! 1 0 1 0 1 0 1 1
%! 1 1 1 0 1 0 1 1
%! 1 0 1 1 1 1 1 0];
%! out(:,:,4) = [
%! 1 1 0 1 0 1 1 1
%! 0 1 1 1 1 1 0 1
%! 0 1 1 1 1 1 0 1
%! 1 1 1 1 1 1 1 1
%! 1 1 1 1 1 1 1 0
%! 1 1 1 0 1 0 1 1
%! 1 1 1 0 1 0 1 1
%! 1 0 1 1 1 1 1 0];
%! assert (bwperim (in, 18), logical (out))
%!error bwperim ("text")
%!error bwperim (rand (10), 5)
%!error bwperim (rand (10), "text")
%!test
%! a = false (5);
%! a(1:4,2:4) = true;
%!
%! p = false (5);
%! p(1:4,[2 4]) = true;
%! assert (bwperim (a, [0 0 0; 1 1 1; 0 0 0]), p)
## This is not a bug. Since connectivity defaults to maximum for the
## number of dimensions, a single slice will be displayed completely
## (this is obvious but very easy to forget)
%!test
%! a = false (8, 8, 5);
%! a(4:5,4:5,2:4) = true;
%! a(2:7,2:7,3) = true;
%! assert (bwperim (a, 26), a)
%!
%! ## It is easy to forget that is correct
%! b = a;
%! b(4:5, 4:5, 3) = false;
%! assert (bwperim (a), b)
%!
%! c = a;
%! c(3:6,3:6,3) = false;
%! assert (bwperim (a, 4), c)
## test dimensions of length 1 (1x1, Nx1, etc) (bug #50153)
%!test
%! conn_self = logical ([0 0 0; 0 1 0; 0 0 0]);
%! assert (bwperim (true), true)
%! assert (bwperim (true, conn_self), false)
%! assert (bwperim (true (1, 6)), true (1, 6))
%! assert (bwperim (true (1, 6), conn_self), false (1, 6))
%! assert (bwperim (true (6, 1)), true (6, 1))
%!
%! bw_3d = true (1, 1, 6);
%! assert (bwperim (bw_3d), bw_3d)
%! assert (bwperim (bw_3d, conn_self), false (1, 1, 6))
%! assert (bwperim (bw_3d, true (3)), bw_3d)
%!
%! perim_3d = bw_3d;
%! perim_3d(1, 1, 2:end-1) = false;
%! conn_3d = false (3, 3, 3);
%! conn_3d(2, 2, :) = true;
%! assert (bwperim (true (1, 1, 6), conn_3d), perim_3d)
image-2.12.0/inst/PaxHeaders.25054/imfindcircles.m 0000644 0000000 0000000 00000000062 13615546210 016365 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imfindcircles.m 0000644 0001750 0001750 00000053262 13615546210 021137 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2017 Hartmut Gimpel
## Copyright (C) 2018 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{centers} =} imfindcircles (@var{im}, @var{radius})
## @deftypefnx {Function File} {@var{centers} =} imfindcircles (@var{im}, @var{RadiusRange})
## @deftypefnx {Function File} {@var{centers} =} imfindcircles (@dots{}, @var{property}, @var{value})
## @deftypefnx {Function File} {[@var{centers}, @var{radii}, @var{strengths}] =} imfindcircles (@dots{})
## Find circles in image using the circular Hough transform.
##
## This function finds circles in a given 2d image @var{im}.
## The image data can be grayscale, rgb color, or binary.
## The search is done only for circular shapes of approximatly
## the given @var{radius} (single value) or @var{RadiusRange}
## (a two element vector [r_min, r_max]).
##
## The output @var{centers} is a two column matrix,
## where each row contains the (fractional) x and y coordinates of
## the center of one found circle. The output rows are ordered
## according the @var{strengths} of the circles, the strongest
## circle first.
##
## The output @var{radii} is a column vector of the (fractional)
## radii of the found circles, in the same order as the circle centers.
## If @var{radius} is specified, instead of @var{RadiusRange}, then
## all values in @var{radii} are the same as @var{radius}.
##
## The output @var{strengths} is a measure of how "strong"
## the corresponding circle is. (A sharp, smooth and full circle gives
## roughly a value of one. The minimum value is zero.)
##
## Additionally the following optional property-value-pairs can be used:
## @table @var
## @item ObjectPolarity
## Either "bright" circles on darker background can be searched (default),
## or "dark" circles on brighter background.
##
## @item Method
## The default method "PhaseCode" is faster as the
## (currently unimplemented) method "TwoStage".
## For literature and details on the algorithm, see comments
## in the code.
##
## @item Sensitivity
## A value between 0 and 1 to say how strong
## a circle must be in order to be found. With a value
## of 1 no circles are discarded, default value is 0.85.
## (Use bigger values of Sensitivity if your circles are
## not properly found, until unwanted "ghost" circles
## start to appear.)
##
## @item EdgeThreshold
## A value between 0 and 1 to say how strong
## an edge point of the image must be, to be
## considered as a possible point on a circle circumference.
## With a value of 0 all possible
## edge points are considered, with a value of 1 only the
## edge point with the stronges gradient will be considered.
## As default value the output of @code{graythresh} (Otsu's threshold)
## of the gradient image is taken.
## @end table
##
## Notes:
## @itemize @bullet
## @item
## Only center points inside the image region can be found.
## @item
## For concentric cirles the output is unpredictable.
## @item
## For optimal speed, keep the radius range and the image size
## as small as possible.
## @item
## For radius values above 100 pixels the sensitivity and accuracy
## of this algorithm starts to decrease.
## @item
## For big radius ranges the sensitivity decreases. Try to keep
## r_max < 3 * r_min .
## @item
## RGB color images will automatically be converted to grayscale
## using the @code{rgb2gray} function.
## @item
## Binary images will automatically be converted to grayscale
## and be slightly smoothed before processing.
## @end itemize
##
## Compatibility note: The @var{centers} and @var{radii} outputs
## have good compatibility to the Matlab outputs. The @var{strengths}
## outputs differ a bit, but have mostly the same ordering.
## Currently only the (default) Matlab algorithm "PhaseCode" is implemented
## in Octave.
##
## @seealso{hough, hough_circle}
## @end deftypefn
## Algorithm:
## The following papers and books were used for the
## implementation of this function:
##
## [1] (for general information on the circle Hough transform
## and the basic algorithmic approach)
## E. R. Davies: "Computer & Machine Vision"
## Academic Press (2012), 4th edition
## chapter 12 "Circle and Ellipse Detection"
## [2] (for the 'phase code' algorithm to search for a radius range
## with just a 2-dimensional accumulator array)
## T. J. Artherton and D. J. Kerbyson:
## "Size invariant circle detection"
## Image and Vision Computing 17 (1999), p. 795-803.
## [3] (for 'state of the art' peak finding in the circular Hough
## transform accumulator array)
## C. Zhang, F. Huber, M. Knop, F. A. Hamprecht:
## "Yest cell detection and segmentation in bright field microscopy"
## IEEE Int. Symp. Biomed. Imag. (ISBI), April 2014, p. 1267-1270.
##
## A more detailed description of the individual steps of the
## algorithm is given in the following code itself.
function [centers, radii, strengths] = imfindcircles (im, radiusrange, varargin)
if (nargin < 2 || nargin > 10 || mod (numel (varargin), 2) != 0)
print_usage ();
endif
p = inputParser ();
p.FunctionName = "imfindcircles";
p.addParamValue ("ObjectPolarity", "bright",
@(x) any (strcmpi (x, {"bright", "dark"})));
p.addParamValue ("Method", "PhaseCode",
@(x) any (strcmpi (x, {"PhaseCode", "TwoStage"})));
p.addParamValue ("Sensitivity", 0.85,
@(x) isnumeric (x) && isreal (x) && isscalar (x));
## The default value of EdgeThreshold must be computed later.
p.addParamValue ("EdgeThreshold", [],
@(x) isnumeric (x) && isreal (x) && isscalar (x));
p.parse (varargin{:});
dark_circles = strcmpi (p.Results.ObjectPolarity, "dark");
sensitivity = p.Results.Sensitivity;
edge_thresh = p.Results.EdgeThreshold;
if (! isimage (im) || ! any (ndims (im) == [2, 3]))
error ("imfindcircles: IM must be a logical or numeric 2d or 3d array");
elseif (ndims (im) == 3 && size (im, 3) != 3)
error ("imfindcircles: the 3d image IM must be a RGB image");
endif
if (all (numel (radiusrange) != [1 2]))
error ("imfindcircles: RADIUS or RADIUSRANGE must be a vector of length 1 or 2");
elseif (! isnumeric (radiusrange) || any (radiusrange <= 0))
error ("imfindcircles: RADIUS or RADIUSRANGE must be positive")
elseif (numel (radiusrange) == 2 && radiusrange(1) > radiusrange(2))
error ("imfindcircles: RADIUSRANGE(1) must be smaller than RADIUSRANGE(2)")
endif
if (strcmpi (p.Results.Method, "TwoStage"))
error ("imfindcirles: the 'TwoStage' method is not yet implemented. Try 'PhaseCode' instead.");
endif
if (sensitivity < 0 || sensitivity > 1)
error ("imfindcircles: 'Sensitivity' must be between 0 and 1");
endif
if (! isempty (edge_thresh) && (edge_thresh < 0 || edge_thresh > 1))
error ("imfindcircles: 'EdgeThreshold' must be between 0 and 1");
endif
H = cht_accumulator (im, radiusrange, edge_thresh, dark_circles);
[centers, centers_ind, strengths] = cht_centers (H, sensitivity);
## Output values must be sorted by strength.
[strengths, idx_sorted] = sort (strengths, "descend");
centers = centers(idx_sorted,:);
if (isargout (2))
if (isscalar (radiusrange))
radii = repmat (radiusrange, numel (strengths), 1);
else
radii = cht_radii (H(centers_ind), radiusrange);
## Output values must be sorted by strength.
radii = radii(idx_sorted);
endif
endif
endfunction
## Convert image from RGB or logical types into a floating point 2D
## image that can be used for CHT.
function im = cht_image_preparation (im)
if (isinteger (im))
im = im2double (im);
endif
if (size (im, 3) == 3)
im = rgb2gray (im);
endif
## The Matlab help page mentions "preprocessing" of
## binary input images to "improve the accuracy".
## Unsure what they do here.
## We'll just do a little (carefully rotation symmetrical)
## smoothing as grayscale images, because we need
## the accurate gradient directions later on. (This wouldn't
## work properly with the binary images itself.)
if (islogical (im))
im = im2single (im);
gauss_filter = fspecial ("gaussian", [9 9], 1.5);
im = imfilter (im, gauss_filter, "replicate");
endif
endfunction
## calculate the intensity gradients values at edge pixels
## using the Sobel operator (see ref. [1], chapter 12.2)
## (we'll always call the first coordinate "x" here)
function [G0, GxE, GyE, E_ind] = edge_intensity_gradients (im, edge_thresh)
sobel_x = fspecial ("sobel");
sobel_y = sobel_x';
Gx = imfilter (im, sobel_x, "replicate");
Gy = imfilter (im, sobel_y, "replicate");
G = sqrt (Gx.^ 2 + Gy.^ 2); # sqrt is expensive, but yields higher angle precision
## get rid of empty gradient images, they would cause trouble later on:
Gmax = max (G(:));
if (Gmax == 0)
E_ind = [](:);
else
## Find the edge pixels by thresholding the gradient image. If
## not set, estimate threshold with Otsu's algorithm.
if (isempty (edge_thresh))
edge_thresh = graythresh (G ./ Gmax);
endif
E_ind = find (G > (edge_thresh * Gmax)); # edge pixels indices
endif
G0 = G(E_ind);
GxE = Gx(E_ind);
GyE = Gy(E_ind);
endfunction
function [xs, ys] = candidate_centers (im, R, edge_thresh)
[G0, GxE, GyE, E_ind] = edge_intensity_gradients (im, edge_thresh);
[Ex, Ey] = ind2sub (size (im), E_ind);
## generate stroke pixels (xs, ys):
## This does not involve sin and cos calculations,
## which helps for speed (see ref. [1].)
## (automatic broadcasting here,
## R is a row vector, the G's are column vectors.)
xs = Ex - R .* (GxE ./ G0); # eq. 12.3 (ref. [1])
ys = Ey - R .* (GyE ./ G0); # eq. 12.4 (ref. [1])
## round to integer pixel positions:
xs = round (xs);
ys = round (ys);
endfunction
## visit all edge pixels and generate
## a "spoke" in the circular Hough transform:
## A spoke is a line of pixels which are a distance r1 to r2
## away from the edge point, this distance is taken in
## the edge direction (gradient) of this very edge point.
## The resulting points on the spokes are candidates for circle
## center points. And each gradient pixel can vote for
## one center pixel candidate (for each radius value).
## All spoke points are summed up in the accumulator array H.
## (see equations 12.3 and 12.4 for xs and ys in ref. [1])
function H = cht_accumulator (im, radiusrange, edge_thresh, dark_circles)
im = cht_image_preparation (im);
if (isscalar (radiusrange))
R = radiusrange;
else
## Range of circle radii. Step size of 0.5 covers all integer
## circle diameters.
R = radiusrange(1):0.5:radiusrange(2);
endif
## code the circle polarity in the radius sign:
## (different direction of "spoke", see ref. [1], chapter 12.2)
if (dark_circles)
[xs, ys] = candidate_centers (im, -R, edge_thresh);
else
[xs, ys] = candidate_centers (im, R, edge_thresh);
endif
ns = rows (xs);
## limit pixel position and code values to the image size:
xs = xs(:);
ys = ys(:);
idx_outside = (xs < 1 | xs > rows (im) | ys < 1 | ys > columns (im));
xs(idx_outside) = [];
ys(idx_outside) = [];
if (isscalar (radiusrange))
codes = 1 ./ R; # phase coding is not useful for a single radius value
else
## pre-calculate the phases and (complex) code values for all radii:
## We use the "log phase coding", which is best, according to
## ref. [2].
logR = log (R);
phase = 2 .* pi .* ((logR - logR(1))
./ (logR(end) - logR(1))); # eq. 8 (ref. [2])
code = exp (i .* phase); # eq. 5 (ref. [2])
code ./= R; # eq. 11 (ref. [2]) , radius normalization
## -> a full and perfect circle will give a value of 2pi in H
codes = repmat (code, ns, 1)(:);
codes(idx_outside) = [];
endif
## add the code value of all those spoke pixels
## to the corresponding pixel position in H:
H = accumarray ([xs, ys], codes, size (im));
endfunction
## Find local (regional) maximas in H, because they are the circle
## center pixels. This part of the algorithm is taken from ref. [3],
## section 2.2 "Detecting cell center candidates".
function [centers, centers_ind, strengths] = cht_centers (H, sensitivity)
## Take the absolute value of the H accumulator array which may be
## complex if we had a radius range and phase coded annulus.
H = abs (H);
## do some smoothing before searching for peak positions
## use a (rotation symmetric) gaussian filter for this (see ref. [3])
gauss_filter = fspecial ("gaussian", [9 9], 2); # a bigger filter might join nearby centers
H = imfilter (H, gauss_filter, 0); # introduces small shifts of centers near the edge,
# but padding "replicate" would be worse
## define threshold to suppress smaller peaks in H:
peak_thresh = 1 - sensitivity;
peak_thresh *= 2*pi; # because a full circle can add up to 2pi in H
## use the h-maxima transform to suppress maximas with a too low height:
Hbig = imhmax (H, peak_thresh);
## find the regional maxima of those smoothed big peaks in H:
Hmax_BW = imregionalmax (Hbig ./ max (Hbig(:)));
## Use those maxima as well as their surrounding peak
## to calculate a weighted average for the peak position:
## (This gives the circle center positions with the necessary
## subpixel accuracy.)
props = regionprops (Hmax_BW, Hbig, "WeightedCentroid");
centers = cell2mat ({props.WeightedCentroid}');
## for the "strength" return value, take the (smoothed) H:
## (Matlab seems to do this a bit different.)
centers_ind = sub2ind (size (H), round (centers(:,2)), round (centers(:,1)));
strengths = H(centers_ind) ./ (2*pi); # normalize a "full" circle to 1
endfunction
## calculate the circle radius from the complex phase in H:
## (i.e. undo the phase coding)
function radii = cht_radii (H, radiusrange)
r1 = radiusrange(1);
r2 = radiusrange(2);
c_phase = arg (H);
c_phase(c_phase < 0) += (2*pi);
radii = exp ((c_phase ./ (2*pi) .* (log (r2) - log (r1))) + log (r1));
endfunction
%!shared im0, rgb0, im1
%! im0 = [0 0 0 0 0;
%! 0 1 2 1 0;
%! 0 2 5 2 0;
%! 0 1 2 1 0;
%! 0 0 0 0 0];
%! rgb0 = cat (3, im0, 3.*im0, 2.*im0);
%! im1 = zeros (20);
%! im1(2:6, 5:9) = 1;
%! im1(13:19, 13:19) = 1;
%!function image = circlesimage (numx, numy, centersx, centersy, rs, values)
%! ## create an image with circles of given parameters
%! num = length (centersx);
%! image = zeros (numy, numx);
%! [indy, indx] = meshgrid (1:numx, 1:numy);
%! for n = 1:num
%! centerx = centersx(n);
%! centery = centersy(n);
%! r = rs(n);
%! value = values(n);
%! dist_squared = (indx - centerx).^ 2 + (indy - centery).^ 2;
%! image(dist_squared <= (r-0.5)^2) = value;
%! endfor
%!endfunction
## test input syntax:
%!error imfindcircles ()
%!error imfindcircles (im0)
%!error imfindcircles (im0, [1 2 3])
%!error imfindcircles (im0, -3)
%!error imfindcircles (im0, 4+2*i)
%!error imfindcircles (ones (5,5,4), 2)
%!error imfindcircles (ones (5,5,5,5), 2)
%!error imfindcircles (im0, [2 1])
%!error imfindcircles (im0, 2, "rubbish")
%!error imfindcircles (im0, 2, "more", "rubbish")
%!error imfindcircles (im0, 2, "ObjectPolarity", "rubbish")
%!error imfindcircles (im0, 2, "ObjectPolarity", 5)
%!error imfindcircles (im0, 2, "ObjectPolarity")
%!error imfindcircles (im0, 2, "Method", "rubbish")
%!error imfindcircles (im0, 2, "Method", 5)
%!error imfindcircles (im0, 2, "Method")
%!error imfindcircles (im0, 2, "Sensitivity", "rubbish")
%!error imfindcircles (im0, 2, "Sensitivity")
%!error imfindcircles (im0, 2, "Sensitivity", -0.1)
%!error imfindcircles (im0, 2, "Sensitivity", 1.1)
%!error imfindcircles (im0, 2, "Sensitivity", [0.1 0.2])
%!error imfindcircles (im0, 2, "EdgeThreshold", "rubbish")
%!error imfindcircles (im0, 2, "EdgeThreshold")
%!error imfindcircles (im0, 2, "EdgeThreshold", -0.1)
%!error imfindcircles (im0, 2, "EdgeThreshold", 1.1)
%!error imfindcircles (im0, 2, "EdgeThreshold", [0.1 0.2])
%!error imfindcircles (im0, 2, "EdgeThreshold", 0.1, "ObjectPolarity", "bright",
%! "Sensitivity", 0.3, "Method", "PhaseCode", "more", 1)
%!test # none of this should fail
%! imfindcircles (im0, 2);
%! imfindcircles (im0, [1 2]);
%! imfindcircles (logical (im0), 2);
%! imfindcircles (logical (im0), [1 2]);
%! imfindcircles (rgb0, 2);
%! imfindcircles (rgb0, [1 2]);
%! imfindcircles (uint8 (im0), 2);
%! imfindcircles (uint8 (im0), [1 2]);
%! imfindcircles (im0, 2, "ObjectPolarity", "bright");
%! imfindcircles (im0, 2, "ObjectPolarity", "dark");
%! imfindcircles (im0, 2, "Method", "PhaseCode");
%! imfindcircles (im0, 2, "Sensitivity", 0.5);
%! imfindcircles (im0, 2, "EdgeThreshold", 0.5);
%! imfindcircles (im0, 2, "ObjectPolarity", "bright", "Method", "PhaseCode");
%! imfindcircles (im0, 2, "ObjectPolarity", "bright", "Sensitivity", 0.3,
%! "Method", "PhaseCode");
%! imfindcircles (im0, 2, "EdgeThreshold", 0.1, "ObjectPolarity", "bright",
%! "Sensitivity", 0.3, "Method", "PhaseCode");
## output class, number and shape:
%!test
%! centers = imfindcircles (im1, 2);
%! assert (size (centers, 2), 2)
%! assert (class (centers), "double")
%!test
%! [centers, radii] = imfindcircles (im1, [1 5]);
%! assert (size (centers, 2), 2)
%! assert (size (radii, 2), 1)
%! assert (class (radii), "double")
%!test
%! [centers, radii, strengths] = imfindcircles (im1, [1 5]);
%! assert (size (strengths, 2), 1)
%! assert (class (strengths), "double")
%!error [a b c d] = imfindcircles (im0, 2);
## test calculation results:
%!test ## sub-pixel accuracy of circle center
%! xs = [95.7];
%! ys = [101.1];
%! rs = [50];
%! vals = [0.5];
%! im = circlesimage (200, 200, xs, ys, rs, vals);
%! filt = ones (3) ./ 9;
%! im = imfilter (im, filt);
%! [centers, radii] = imfindcircles (im, [40 60]);
%! assert (centers, [101.1, 95.7], 0.1);
%! assert (radii, 50, 1);
%!test
%! ## specificity to circular shapes and strengths output value
%! xs = [100 202];
%! ys = [101, 203];
%! rs = [40, 41];
%! vals = [0.8, 0.9];
%! im = circlesimage (300, 300, xs, ys, rs, vals);
%! filt = ones (3) ./ 9;
%! im = imfilter (im, filt);
%! im(30:170, 50:100) = 0;
%! im(20:120, 180:280) = 1;
%! [centers, radii, strengths] = imfindcircles (im, [30 50], "Sensitivity", 0.9);
%! assert (size (centers), [2 2]);
%! assert (centers, [203, 202; 101, 100], 1.5);
%! assert (radii, [40; 41], 2.5);
%! assert (strengths(1) / strengths(2) > 1.8, true);
%!test # radius range parameter & dark circles
%! xs = [50, 420, 180];
%! ys = [80, 100, 200];
%! rs = [35, 30, 40];
%! vals = [0.7, 0.8, 0.9];
%! im = circlesimage (300, 500, xs, ys, rs, vals);
%! filt = ones (3) ./ 9;
%! im = imfilter (im, filt);
%! [centers1, radii1] = imfindcircles (im, [28 36]);
%! [centers2, radii2] = imfindcircles (im, [28 42]);
%! assert (size (centers1), [2 2]);
%! assert (centers1, [100 420; 80 50], 0.2);
%! assert (radii1, [30; 35], 2);
%! assert (size (centers2), [3 2]);
%! im_dark = 1-im;
%! [centers_dark, radii_dark, strengths_dark] = imfindcircles (im_dark, [25 42], "ObjectPolarity", "dark");
%! assert (sortrows (centers_dark), [80 50; 100 420; 200 180], 0.2);
%! assert (sortrows (radii_dark), [30; 35; 40], 1);
%!test # ability to find circles with big radius
%! xs = [111, 555, 341];
%! ys = [222, 401, 161];
%! rs = [45, 50, 150];
%! vals = [0.6, 0.8, 0.7];
%! im = circlesimage (400, 701, xs, ys, rs, vals);
%! [centers, radii] = imfindcircles (im, [140 160], "Sensitivity", 0.98);
%! assert (centers, [161, 341], 0.2);
%! assert (radii, 150, 1);
%!test # overlapping circles
%! xs = [105, 155];
%! ys = [202, 221];
%! rs = [45, 50];
%! vals = [0.5, 0.8];
%! im = circlesimage(385, 422, xs, ys, rs, vals);
%! filt = ones (3) ./ 9;
%! im = imfilter (im, filt);
%! [centers, radii] = imfindcircles (im, [30 80]);
%! assert (centers, [221, 155; 202, 105], 0.5);
%! assert (radii, [50; 45], 1);
%!test # overlapping circles, only 10 pixels apart
%! xs = [155, 155];
%! ys = [175, 157];
%! rs = [50, 50];
%! vals = [0.7, 0.8];
%! im = circlesimage (300, 300, xs, ys, rs, vals);
%! filt = ones (3) ./ 9;
%! im = imfilter (im, filt);
%! [centers, radii] = imfindcircles (im, [30 80], "Sensitivity", 0.95);
%! assert (centers, [157, 155; 175, 155], 1);
%! assert (radii, [50; 50], 1);
%!test # edge threshold parameter
%! xs = [100 202];
%! ys = [101, 203];
%! rs = [40, 41];
%! vals = [0.1, 0.9];
%! im = circlesimage (300, 300, xs, ys, rs, vals);
%! filt = ones (3) ./ 9;
%! im= imfilter (im, filt);
%! [centers_auto, radii_auto] = imfindcircles (im, [30 50]);
%! [centers_0, radii_0] = imfindcircles (im, [30 50], "EdgeThreshold", 0);
%! [centers_05, radii_05] = imfindcircles (im, [30 50], "EdgeThreshold", 0.5);
%! assert (centers_auto, [203, 202], 0.2);
%! assert (radii_auto, 41, 1);
%! assert (centers_0, [101, 100; 203, 202], 0.2);
%! assert (radii_0, [40; 41], 1);
%! assert (centers_05, [203, 202], 0.2);
%! assert (radii_05, 41, 1);
%!demo
%! ## First generate an input image:
%! model = [ 1.0 0.2 0.2 0.2 0.5 0
%! 1.0 0.3 0.3 -0.1 -0.2 0
%! -0.5 0.7 0.7 -0.5 0.5 0];
%! im = phantom (model);
%! im(170:230,170:230) = 1;
%! im = imfilter (im, fspecial ("average", 3));
%! im = imnoise (im, "salt & pepper");
%! imshow (im);
%!
%! ## Find and show circles with radius between 20 and 50:
%! [centers, radii] = imfindcircles (im, [20 50]);
%! viscircles (centers, radii)
%! title ("found circles in red")
image-2.12.0/inst/PaxHeaders.25054/fftconvn.m 0000644 0000000 0000000 00000000062 13615546210 015375 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/fftconvn.m 0000644 0001750 0001750 00000013743 13615546210 020147 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2015 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} fftconvn (@var{A}, @var{B})
## @deftypefnx {Function File} {} fftconvn (@var{A}, @var{B}, @var{shape})
## Convolve N dimensional signals using the FFT for computation.
##
## This function is equivalent to @code{convn} but using the FFT. It
## convolves the two N dimensional @var{A} and @var{B}. The size of
## output is controlled by the option @var{shape} which removes the
## borders where boundary effects may be seen:
##
## @table @asis
## @item @qcode{"full"} (default)
## Return the full convolution.
##
## @item @qcode{"same"}
## Return central part of the convolution with the same size as @var{A}.
##
## @item @qcode{"valid"}
## Return only the parts which do not include zero-padded edges.
##
## @end table
##
## Using the FFT may be faster but this is not always the case and can
## be a lot worse, specially for smalls @var{A} and @var{B}. This performance
## increase also comes at the cost of increased memory usage, as well as a loss
## of precision.
##
## @example
## @group
## a = randi (255, 1024, 1024);
## b = randi (255, 10, 10);
## t = cputime (); convn (a, b); cputime () -t
## @result{} 0.096000
## t = cputime (); fftconvn (a, b); cputime () -t
## @result{} 1.2560
##
## b = randi (255, 50, 50);
## t = cputime (); convn (a, b); cputime () -t
## @result{} 2.3400
## t = cputime (); fftconvn (a, b); cputime () -t
## @result{} 1.2560
## @end group
## @end example
##
## Note how computation time for @code{convn} increased with the size of
## @var{B} but remained constant when using @code{fftconvn}. When
## performing the convolution, @code{fftconvn} zero pads both @var{A} and
## @var{B} so their lengths are a power of two on all dimensions.
## This may further increase memory usage but will also increase
## performance. In this example, the computation time will remain constant
## until @code{size (@var{A}) + size (@var{B}) -1} is greater than 2048
## after which it will remain constant again until it reaches 4096.
##
## @example
## @group
## a = randi (255, 1024, 1024);
## b = randi (255, 50, 50);
## t = cputime (); fftconvn (a, b); cputime () -t
## @result{} 1.2760
## a = randi (255, 2048-50+1, 2048-50+1);
## t = cputime (); fftconvn (a, b); cputime () -t
## @result{} 1.2120
## a = randi (255, 2049-50+1, 2049-50+1);
## t = cputime (); fftconvn (a, b); cputime () -t
## @result{} 6.1520
## a = randi (255, 4096-50+1, 4096-50+1);
## t = cputime (); fftconvn (a, b); cputime () -t
## @result{} 6.2360
## a = randi (255, 4097-50+1, 4097-50+1);
## t = cputime (); fftconvn (a, b); cputime () -t
## @result{} 38.120
## @end group
## @end example
##
## @seealso{convn, fftconv2, fftconv, padarray}
## @end deftypefn
function C = fftconvn (A, B, shape = "full")
if (nargin < 2 || nargin > 3)
print_usage ();
elseif (! isnumeric (A) || ! isnumeric (B))
error ("fftconvn: A and B must be numeric")
endif
nd = max (ndims (A), ndims (B));
A_size = get_sizes (A, nd);
B_size = get_sizes (B, nd);
fft_size = 2 .^ nextpow2 (A_size + B_size - 1);
C = ifftn (fftn (A, fft_size(1:ndims(A))) .* fftn (B, fft_size(1:ndims(B))));
if (iscomplex (C) && isreal (A) && isreal (B))
C = real (C);
endif
switch (tolower (shape))
case "full"
starts = repmat (1, [1 nd]);
ends = A_size + B_size - 1;
case "same"
prepad = floor (B_size / 2);
starts = prepad + 1;
ends = A_size + prepad;
case "valid"
starts = B_size;
ends = A_size;
otherwise
error ("fftconvn: unknown SHAPE `%s'", shape);
endswitch
if (any (starts > 1) || any (ends != fft_size))
idx = get_ndim_idx (starts, ends);
C = C(idx{:});
endif
endfunction
## returns the size of x but padded with 1 (singleton dimensions), to
## allow operations to be performed when the ndims do not match
function sizes = get_sizes (x, n)
sizes = postpad (size (x), n, 1, 2);
endfunction
## starts and ends must have same length
function idx = get_ndim_idx (starts, ends)
idx = arrayfun (@colon, starts, ends, "UniformOutput", false);
endfunction
%!function test_shapes (a, b, precision)
%! shapes = {"valid", "same", "full"};
%! for i = 1:3
%! shape = shapes{i};
%! assert (fftconvn (a, b, shape), convn (a, b, shape), precision);
%! endfor
%! assert (fftconvn (a, b), fftconvn (a, b, "full"));
%!endfunction
## simplest case
%!test test_shapes (randi (255, 100), randi (255, 10), 0.1)
%!test test_shapes (randi (255, 100, 100), randi (255, 10, 10), 0.1)
%!test test_shapes (randi (255, 100, 100, 100), randi (255, 10, 10, 10), 0.1)
## mix of number of dimensions
%!test test_shapes (randi (255, 100, 50, 20), randi (255, 10, 7), 0.1)
%!test test_shapes (randi (255, 100, 50, 20), randi (255, 10), 0.1)
## test near powers of 2 sizes
%!test
%! for s = [55 56 57 58]
%! test_shapes (randi (255, 200, 200), randi (255, s, s), 0.1)
%! endfor
%!test
%! for s = [203 204 205 206]
%! test_shapes (randi (255, s, s), randi (255, 52, 52), 0.1)
%! endfor
## test with other classes
%!test test_shapes (randi (255, 100, 100, "uint8"), randi (255, 10, 10, "uint8"), 0.1)
%!test test_shapes (randi (255, 100, 100, "uint8"), randi (255, 10, 10), 0.1)
%!test test_shapes (randi (255, 100, 100, "single"), randi (255, 10, 10, "single"), 0.9)
%!test test_shapes (randi (255, 100, 100, "single"), randi (255, 10, 10), 0.9)
image-2.12.0/inst/PaxHeaders.25054/phantom.m 0000644 0000000 0000000 00000000062 13615546210 015220 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/phantom.m 0000644 0001750 0001750 00000020414 13615546210 017763 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2010 Alex Opie
## Copyright (C) 2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{P}} = phantom ()
## @deftypefnx {Function File} {@var{P}} = phantom (@var{model})
## @deftypefnx {Function File} {@var{P}} = phantom (@var{E})
## @deftypefnx {Function File} {@var{P}} = phantom (@dots{}, @var{n})
## @deftypefnx {Function File} {[@var{P}, @var{E}]} = phantom (@dots{})
## Create computational phantom head.
##
## A phantom is a known object (either real or purely mathematical) that is
## used for testing image reconstruction algorithms. The Shepp-Logan phantom
## is a popular mathematical model of a cranial slice, made up of a set of
## overlaying ellipses. This allows rigorous testing of computed tomography
## (CT) algorithms as it can be analytically transformed with the radon
## transform (see the functions @code{radon} and @code{iradon}).
##
## The phantom @var{P}, is created by overlaying ellipses as defined by the
## matrix @var{E} or one of the standard @var{model}s, in a square of size
## @var{n} by @var{n} (defaults to 256).
##
## The available standard @var{model}s (use the output argument @var{E} to
## inspect the details of the different ellipses) are:
##
## @table @asis
## @item @qcode{"Shepp-Logan"}
## This is the original Shepp-Logan model with 10 ellipses as described in
## Table 1 of @cite{Shepp, Lawrence A., and Benjamin F. Logan. "The Fourier
## reconstruction of a head section." Nuclear Science, IEEE Transactions on
## 21, no. 3 (1974): 21-43.}
##
## @item @qcode{"Modified Shepp-Logan"} (default)
## A modification of the original Shepp-Logan model to give a better contrast,
## as described in Table B.3 of @cite{Toft, Peter Aundal. "The radon
## transform-theory and implementation." PhD diss., Department of Mathematical
## Modelling, Technical University of Denmark, 1996.}
##
## @end table
##
## A 6 column matrix @var{E} can be used to generate a custom image by
## superimposing arbitrary ellipses. Each row defines a single ellipse, with
## each column for the values of @{I, a, b, x0, y0, phi@}:
##
## @table @abbr
## @item I
## is the additive intensity of the ellipse
##
## @item a
## is the length of the major axis
##
## @item b
## is the length of the minor axis
##
## @item x0
## is the horizontal offset of the centre of the ellipse
##
## @item y0
## is the vertical offset of the centre of the ellipse
##
## @item phi
## is the counterclockwise rotation of the ellipse in degrees,
## measured as the angle between the x axis and the ellipse major axis.
##
## @end table
##
## The image bounding box in the algorithm is @{[-1, -1], [1, 1]@}, so the
## values of a, b, x0, y0 should all be specified with this in mind.
##
## Example:
##
## @example
## @group
## P = phantom (512);
## imshow (P);
## @end group
## @end example
##
## @seealso{iradon, radon}
## @end deftypefn
function [head, ellipses] = phantom (varargin)
if (nargin > 2)
print_usage ()
endif
## Would be really cool if we implemented a 3D phantom as already described
## in Cheng Guan Koay, Joelle E. Sarlls, and Evren Ozarslan (2007).
## "Three-Dimensional Analytical Magnetic Resonance Imaging Phantom in the
## Fourier Domain". Magnetic Resonance in Medicine 58:430 - 436.
## The Table 1 on their paper to generate the 3D model, would take 8 columns,
## an extra value for z axis coordinates, and extra axis length.
## They mention other phantom heads as more canonical 3D head phantoms (read
## the introduction)
## Defaults
ellipses = mod_shepp_logan ();
n = 256;
if (nargin)
## Check validity of N
chk_n = @(x) isnumeric (x) && isscalar (x) && ceil (x) == x;
in = varargin{1};
if (ischar (in))
switch (tolower (in))
case "shepp-logan", ellipses = shepp_logan ();
case "modified shepp-logan", ellipses = mod_shepp_logan ();
otherwise
error ("phantom: unknown MODEL `%s'", in);
endswitch
elseif (isnumeric (in) && ndims (in) == 2 && columns (in) == 6)
ellipses = in;
elseif (chk_n (in))
n = in;
## If N is the first argument, we can't have more
if (nargin > 1)
print_usage ();
endif
else
error ("phantom: first argument must either be MODEL, E, or N");
endif
## If there is a second input argument, must be N
if (nargin > 1)
if (chk_n (varargin{2}))
n = varargin{2};
else
error ("phantom: N must be numeric scalar");
endif
endif
endif
## Initialize blank image
head = zeros (n);
# Create the pixel grid
xvals = (-1 : 2 / (n - 1) : 1);
xgrid = repmat (xvals, n, 1);
for i = 1:rows (ellipses)
I = ellipses (i, 1);
a2 = ellipses (i, 2)^2;
b2 = ellipses (i, 3)^2;
x0 = ellipses (i, 4);
y0 = ellipses (i, 5);
phi = ellipses (i, 6) * pi / 180; # Rotation angle in radians
## Create the offset x and y values for the grid
x = xgrid - x0;
y = rot90 (xgrid) - y0;
cos_p = cos (phi);
sin_p = sin (phi);
## Find the pixels within the ellipse
locs = find (((x .* cos_p + y .* sin_p).^2) ./ a2 ...
+ ((y .* cos_p - x .* sin_p).^2) ./ b2 <= 1);
## Add the ellipse intensity to those pixels
head(locs) += I;
endfor
endfunction
function ellipses = shepp_logan ()
## Standard head phantom, taken from Shepp & Logan
##
## Note that the first element of this matrix, the gray value for the first
## ellipse (human skull), has a value of 1.0 even though the paper gives it a
## a value of 2.0 (see Table 1 on page 32 and Figure 1 on page 34). This
## change is so that the **head** intensity values appear in the range [0 1]
## rather than the range [1 2].
##
## **The problem with this**
##
## The background still need an intensity value which is going to be 0. This
## means that we can't distinguish between the background and the ventricles
## (ellipse "c" and "d" whose intensities are a + b + c and a + b + d, see
## Figure 1) since they will have an intensity value of 0 (actually, because
## of machine precision the ventricules will be almost 0). But if we didn't
## made this change, the ** image** range would be [0 2] with all of the head
## details compressed in half of the display range. Also, Matlab seems to be
## doing the same.
ellipses = [ 1 0.69 0.92 0 0 0
-0.98 0.6624 0.874 0 -0.0184 0
-0.02 0.11 0.31 0.22 0 -18
-0.02 0.16 0.41 -0.22 0 18
0.01 0.21 0.25 0 0.35 0
0.01 0.046 0.046 0 0.1 0
0.01 0.046 0.046 0 -0.1 0
0.01 0.046 0.023 -0.08 -0.605 0
0.01 0.023 0.023 0 -0.606 0
0.01 0.023 0.046 0.06 -0.605 0];
endfunction
function ellipses = mod_shepp_logan ()
## Modified version of Shepp & Logan's head phantom, adjusted to improve
## contrast. Taken from Peter Toft PhD thesis, Table B.3
ellipses = [ 1.0 0.69 0.92 0.0 0.0 0
-0.8 0.6624 0.874 0.0 -0.0184 0
-0.2 0.11 0.31 0.22 0.0 -18
-0.2 0.16 0.41 -0.22 0.0 18
0.1 0.21 0.25 0.0 0.35 0
0.1 0.046 0.046 0.0 0.1 0
0.1 0.046 0.046 0.0 -0.1 0
0.1 0.046 0.023 -0.08 -0.605 0
0.1 0.023 0.023 0.0 -0.606 0
0.1 0.023 0.046 0.06 -0.605 0];
endfunction
%!demo
%! P = phantom (512);
%! imshow (P);
image-2.12.0/inst/PaxHeaders.25054/im2int16.m 0000644 0000000 0000000 00000000062 13615546210 015123 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/im2int16.m 0000644 0001750 0001750 00000004265 13615546210 017674 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2012-2014 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} im2int16 (@var{img})
## Convert image to int16.
##
## The conversion of @var{img} to a 16-bit signed integer, is dependent
## on the type of input image. The following input classes are supported:
##
## @table @samp
## @item uint8 or uint16
## Values are rescaled to the range of the int16 class [-32768 32767].
##
## @item logical
## True and false values are assigned a value of 32767 and -32768 respectively.
##
## @item double or single
## Values are truncated to the interval [0 1] and then rescaled to the range
## of values of the int16 class [-32768 32767].
##
## @item int16
## Returns the same image.
##
## @end table
##
## @seealso{im2bw, imcast, im2uint8, im2double, im2single, im2uint16}
## @end deftypefn
function imout = im2int16 (img)
if (nargin != 1)
print_usage ();
endif
imout = imcast (img, "int16");
endfunction
%!assert (im2int16 (int16 ([-2 2 3])), int16 ([-2 2 3]));
%!assert (im2int16 (uint16 ([0 65535])), int16 ([-32768 32767]));
%!assert (im2int16 ([false true]), int16 ([-32768 32767]));
%!assert (im2int16 ([true false]), int16 ([32767 -32768]));
%!assert (im2int16 (uint8 ([0 127 128 255])), int16 ([-32768 -129 128 32767]));
%!assert (im2int16 ([0 1.4/65535 1.5/65535 2/65535 1]), int16 ([-32768 -32767 -32766 -32766 32767]));
%!assert (im2int16 ([0 0.5 1]), int16 ([-32768 0 32767]));
%!assert (im2int16 ([-1 0 1 2]), int16 ([-32768 -32768 32767 32767]));
%!error im2int16 ([1 2], "indexed");
image-2.12.0/inst/PaxHeaders.25054/imimposemin.m 0000644 0000000 0000000 00000000062 13615546210 016100 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imimposemin.m 0000644 0001750 0001750 00000017607 13615546210 020655 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2017 Hartmut Gimpel
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} @ imimposemin (@var{im}, @var{bw})
## @deftypefnx {Function File} {} @ imimposemin (@var{im}, @var{bw}, @var{conn})
## Modify the input impage @var{im} to only have regional minima at
## the marker positions given by the nonzero pixels of @var{bw}.
##
## This function returns a grayscale image that is similar to @var{im} but
## only has regional minima at pixel positions where the marker image @var{bw} is nonzero.
##
## The input image @var{im} needs to be a real and nonsparse numeric array
## (of any dimension). And the marker image @var{bw} needs to be a real or
## logical nonsparse array of identical size.
## (The values in @var{bw} will first be converted to logical values.)
##
## The definition of "neighborhood" for this morphological operation can be set
## with the connectivity parameter @var{conn},
## which defaults to 8 for 2D images, to 26 for 3D images and to
## @code{conn(ndims(n), "maximal")} in general. @var{conn} can be given as scalar value
## or as a boolean matrix (see @code{conndef} for details).
##
## @seealso{imextendedmin, imhmin, imregionalmin, imreconstruct}
## @end deftypefn
## Algorithm:
## * The 'classical' reference for this morphological "minima imposition" function
## is the book "Morphological Image Analysis" by P. Soille
## (Springer, 2nd edition, 2004), chapter 6.4.6 "Minima imposition".
## It says (own translation from the german book, two typos corrected):
## "The marker image f_m is defined for every pixel as follows:
## f_m(x) = 0 if x is part of a marker
## = t_max else.
## The minima imposition of the input image f is performed
## in two steps. First, the pointwise minimum between the input
## image and the marker image is calculated: f /\ f_m. [...]
## it is necessary to rather consider (f+1) /\ f_m instead of f /\ f_m.
## The second step consists of a morphological reconstruction
## by erosion of (f+1) /\ f_m from the marker image fm."
## (We will call the grayscale image im instead of f.)
## * A more easily accessible reference is for example the following
## web page by Régis Clouard:
## https://clouard.users.greyc.fr/Pantheon/experiments/morphology/index-en.html#ch4-D
## It says: "IV.D.2. Maxima/Minima Imposition
## [..., it follows an example script with Pandore functions, the input image is in.pan,
## the generated marker image is i1.pan and the output is swamping.pan]
## padcst 1 in.pan i2.pan [i2 = in + 1]
## pmin i1.pan i2.pan i3.pan [i3 = min(i1, i2)]
## perosionreconstruction 8 i1.pan i3.pan swamping.pan [out = erosionreconstruction(i1, i3, 8)]
function im2 = imimposemin (im, bw, varargin)
## retrieve input parameters, set default value:
if (nargin == 3)
conn = varargin{1};
iptcheckconn (conn, "imimposemin", "CONN");
elseif (nargin == 2)
## Buggy Matlab doc claims "minimum" connectivity instead,
## but defaults of 8 and 26, which are "maximal" connectivities.
conn = conndef (ndims (im), "maximal");
else
print_usage ();
endif
## check input parameters:
if (! isnumeric (im) || ! isreal (im) || issparse (im) )
error ("imimposemin: IM must be a real and nonsparse numeric array");
endif
if (((! isnumeric (bw) || ! isreal (bw)) && (! islogical (bw))) || issparse (bw) )
error ("imimposemin: BW must be a logical or numeric nonsparse array");
endif
if (! (ndims (im) == ndims (bw)) || ! all (size (im) == size (bw)))
error ("imimposemin: BW must have the same size as IM");
endif
## do the actual calculation
## convert bw to class logical if necessary:
if (! islogical (bw))
bw = logical (bw);
endif
## define the marker image fm (see algorithm above):
fm = zeros (size (im), class (im));
fm(bw) = -Inf; # min value of class(im)
fm(!bw) = Inf; # max value of class(im)
## define the difference delta:
## for integer images this value is 1 (see algorithm above)
## for float images this is done in analogy
## (A possible value for float images would be delta = eps (im),
## see bug #51724, but Matlab seems to do the following instead.)
if isfloat (im)
delta = ( max (im(:)) - min (im(:)) ) / 1000;
else # integer images
delta = 1;
endif
## calculate pointwise minimum (see algorithm above):
min_im = min (im + delta, fm);
## do the morphological reconstruction by erosion (see algorithm above):
## (Calculate dilations of the inverse images, instead of erosions of the
## original images, because this is what imreconstruct can do.)
im2 = imreconstruct (imcomplement (fm), imcomplement (min_im), conn);
im2 = imcomplement (im2);
endfunction
%!shared im0, bw0, out0, out0_4
%! im0 = uint8 ([5 5 5 5 5;
%! 5 4 3 4 5;
%! 5 3 0 3 5;
%! 5 4 3 4 5;
%! 5 5 5 5 5]);
%! bw0 = false (5);
%! bw0(4, 4) = true;
%! out0 = im0 + 1;
%! out0(4, 4) = 0;
%! out0_4 = out0;
%! out0_4(3, 3) = 4;
## test input syntax:
%!error imimposemin ()
%!error imimposemin (im0)
%!error imimposemin ("hello", bw0)
%!error imimposemin (i.*im0, bw0)
%!error imimposemin (sparse (im0), bw0)
%!error imimposemin (im0, ones (2))
%!error imimposemin (im0, 'hello')
%!error imimposemin (im0, i .* double (bw0))
%!error imimposemin (im0, sparse (bw0))
%!error imimposemin (im0, bw0, 'hello')
%!error imimposemin (im0, bw0, 3)
%!assert (imimposemin (im0, bw0), out0)
%!assert (imimposemin (im0, bw0, 8), out0)
%!assert (imimposemin (im0, bw0, 4), out0_4)
%!assert (imimposemin (im0, bw0, true (3)), out0)
## test output class and shape:
%!test
%! out = imimposemin (im0, bw0);
%! assert (size (out), size (im0))
%! assert (class (out), "uint8")
%!test
%! out = imimposemin (double (im0), bw0);
%! assert (size (out), size (im0))
%! assert (class (out), "double")
%!test
%! out = imimposemin (single (im0), bw0);
%! assert (size (out), size (im0))
%! assert (class (out), "single")
%!test
%! out = imimposemin (uint16 (im0), bw0);
%! assert (size (out), size (im0))
%! assert (class (out), "uint16")
%!test
%! im = cat (3, im0, im0, im0, im0);
%! bw = cat (3, bw0, bw0, bw0, bw0);
%! out = imimposemin (im, bw);
%! assert (size (out), size (im))
## test calculation result:
%!test
%! expected_double = double (im0);
%! expected_double += 0.005;
%! expected_double(4, 4) = -inf;
%! out = imimposemin (double (im0), bw0);
%! assert (out, expected_double, eps)
%!test
%! im = uint8 (10 .* ones (10));
%! im(6:8, 6:8) = 2;
%! im(2:4, 2:4) = 7;
%! im(3, 3) = 5;
%! im(2, 9) = 9;
%! im(3, 8) = 9;
%! im(9, 2) = 9;
%! im(8, 3) = 9;
%! bw = false (10);
%! bw(3, 3) = true;
%! bw(6:8, 6:8) = true;
%! expected = uint8 (11 .* ones(10));
%! expected(2:4, 2:4) = 8;
%! expected(3, 3) = 0;
%! expected(6:8, 6:8) = 0;
%! expected_double = double (expected);
%! expected_double -= 0.992;
%! expected_double (expected_double < 0) = -inf;
%! out = imimposemin (im, bw);
%! assert (out, expected, eps)
%! out = imimposemin (double (im), bw);
%! assert (out, expected_double, eps)
image-2.12.0/inst/PaxHeaders.25054/isgray.m 0000644 0000000 0000000 00000000062 13615546210 015050 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/isgray.m 0000644 0001750 0001750 00000005541 13615546210 017617 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2000 Kai Habel
## Copyright (C) 2011, 2015 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} isgray (@var{img})
## Return true if @var{img} is a grayscale image.
##
## A variable can be considered a grayscale image if it is a non-sparse,
## real array of size @nospell{MxNx1xK}, and:
##
## @itemize @bullet
## @item of floating point class with values in the [0 1] range or NaN;
## @item of class uint8, uint16, or int16.
## @end itemize
##
## @emph{Note}: despite their suggestive names, the functions isbw,
## isgray, isind, and isrgb, are ambiguous since it is not always possible
## to distinguish between those image types. For example: an uint8 matrix
## can be both a grayscale and indexed image; a grayscale image may have
## values outside the range [0 1]. They are good to dismiss input as an
## invalid image type, but not for identification.
##
## @seealso{gray2ind, isbw, isind, isrgb}
## @end deftypefn
function bool = isgray (img)
if (nargin () != 1)
print_usage ();
endif
bool = false;
if (isimage (img) && ndims (img) < 5 && size (img, 3) == 1)
if (isfloat (img))
bool = ispart (@is_float_image, img);
elseif (any (isa (img, {"uint8", "uint16", "int16"})))
bool = true;
endif
endif
endfunction
%!assert (isgray ([0 0 1; 1 0 1]), true)
%!assert (isgray (zeros (3)), true)
%!assert (isgray (ones (3)), true)
%!test
%! a = rand (10);
%! assert (isgray (a), true);
%! a(5, 5) = 2;
%! assert (isgray (a), false);
%!test
%! a = uint8 (randi (255, 10));
%! assert (isgray (a), true);
%! a = int8 (a);
%! assert (isgray (a), false);
%!test
%! a = rand (10);
%! a(50) = NaN;
%! assert (isgray (a), true);
%!assert (isgray (rand (5, 5, 1, 4)), true);
%!assert (isgray (rand (5, 5, 3, 4)), false);
%!assert (isgray (rand (5, 5, 3)), false);
%!assert (isgray (rand (5, 5, 1, 3, 4)), false);
%!assert (isgray (rand (5, "single")), true)
## While having some NaN is ok, having all NaN is not
%!assert (isgray ([.1 .2 .3; .4 NaN .6; .7 .8 .9]), true)
%!assert (isgray ([.1 .2 .3; NA NaN .6; .7 .8 .9]), true)
%!assert (isgray ([.1 .2 .3; NA .5 .6; .7 .8 .9]), true)
%!assert (isgray (NaN (5)), false)
%!assert (isgray (NA (5)), false)
image-2.12.0/inst/PaxHeaders.25054/colorgradient.m 0000644 0000000 0000000 00000000062 13615546210 016406 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/colorgradient.m 0000644 0001750 0001750 00000003007 13615546210 021150 0 ustar 00carandraug carandraug 0000000 0000000 ## Author: Paul Kienzle
## This program is granted to the public domain.
## -*- texinfo -*-
## @deftypefn {Function File} {@var{M} =} colorgradient (@var{C}, @var{w}, @var{n})
## Define a colour map which smoothly traverses the given colors.
## @var{C} contains the colours, one row per r,g,b value.
## @var{w}(i) is the relative length of the transition from colour i to colour i+1
## in the entire gradient. The default is ones(rows(C)-1,1).
## n is the length of the colour map. The default is rows(colormap).
##
## E.g.,
## @example
## colorgradient([0,0,1; 1,1,0; 1,0,0]) # blue -> yellow -> red
## x = linspace(0,1,200);
## imagesc(x(:,ones(30,1)))';
## @end example
## @end deftypefn
function ret = colorgradient (C, w, n)
if nargin < 1 || nargin > 3
print_usage;
endif
if nargin == 1
n = rows(colormap);
w = ones(length(C)-1,1);
elseif nargin == 2
if (length(w) == 1)
n = w;
w = ones(rows(C)-1,1);
else
n = rows(colormap);
endif
endif
if (length(w)+1 != rows(C))
error("must have one weight for each color interval");
endif
w = 1+round((n-1)*cumsum([0;w(:)])/sum(w));
map = zeros(n,3);
for i=1:length(w)-1
if (w(i) != w(i+1))
map(w(i):w(i+1),1) = linspace(C(i,1),C(i+1,1),w(i+1)-w(i)+1)';
map(w(i):w(i+1),2) = linspace(C(i,2),C(i+1,2),w(i+1)-w(i)+1)';
map(w(i):w(i+1),3) = linspace(C(i,3),C(i+1,3),w(i+1)-w(i)+1)';
endif
endfor
if nargout == 0
colormap(map);
else
ret = map;
endif
endfunction
image-2.12.0/inst/PaxHeaders.25054/bwarea.m 0000644 0000000 0000000 00000000062 13615546210 015013 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/bwarea.m 0000644 0001750 0001750 00000003516 13615546210 017562 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2005 Søren 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{total} =} bwarea (@var{bw})
## Estimate total area of objects on the image @var{bw}.
##
## The image @var{bw} can be of any class, even non-logical, in which case non
## zero valued pixels are considered to be an object.
##
## This algorithm is not the same as counting the number of pixels belonging to
## an object as it tries to estimate the area of the original object. The value
## of each pixel to the total area is weighted in relation to its neighbour
## pixels.
##
## @seealso{im2bw, bweuler, bwperim, regionprops}
## @end deftypefn
function total = bwarea (bw)
if (nargin != 1)
print_usage;
elseif (!isimage (bw) || ndims (bw) != 2)
error("bwarea: input image must be a 2D image");
elseif (!islogical (bw))
bw = (bw != 0)
endif
four = ones (2);
two = diag ([1 1]);
fours = conv2 (bw, four);
twos = conv2 (bw, two);
nQ1 = sum (fours(:) == 1);
nQ3 = sum (fours(:) == 3);
nQ4 = sum (fours(:) == 4);
nQD = sum (fours(:) == 2 & twos(:) != 1);
nQ2 = sum (fours(:) == 2 & twos(:) == 1);
total = 0.25*nQ1 + 0.5*nQ2 + 0.875*nQ3 + nQ4 + 0.75*nQD;
endfunction
image-2.12.0/inst/PaxHeaders.25054/edgetaper.m 0000644 0000000 0000000 00000000062 13615546210 015512 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/edgetaper.m 0000644 0001750 0001750 00000010411 13615546210 020251 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2015 Carnë Draug
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {Function File} {} edgetaper (@var{img}, @var{psf})
## Blur border (edges) of image to prevent ringing artifacts.
##
## @emph{Warning}: this function is not @sc{Matlab} compatible and
## is likely to change in the future.
##
## @end deftypefn
function imgt = edgetaper (img, psf)
if (nargin != 2)
print_usage ();
elseif (! isnumeric (img))
error ("edgetaper: IMG must be numeric")
elseif (! isnumeric (psf))
error ("edgetaper: PSF must be numeric")
endif
img_size = size (img);
psf_size = size (psf);
n = max (numel (img_size), numel (psf_size));
img_size = postpad (img_size(:), n, 1);
psf_size = postpad (psf_size(:), n, 1);
## beware of psf_size = [16 16 1 8] paired with img_size [512 512 1 50]
## which are valid, singleton dimensions do not count for this check
if (any ((psf_size > (img_size / 2)) & (psf_size > 1)))
error ("edgetaper: PSF must be smaller than half of IMG dimensions")
endif
psf = psf ./ sum (psf(:)); # we use it for blurring so the sum must be 1
## FIXME this function is not Matlab compatible. I have no clue what
## Matlab is doing but is definitely not what they claim on the
## documentation. There are no references for the function and
## the documentation is sparse and incorrect.
##
## I have found papers that compare their method for reducing
## boundary artifacts against Matlab's edgetaper but even they
## do not comment it.
##
## It an implementation of edgetaper that is close to Matlab's
## documentation for the function. If anyone has patience, please
## fix this.
##
## Some questions about it:
##
## 1. the autocorrelation of the PSF is twice the size of the PSF
## but it will still be way smaller than the image. How can it be
## used to make a weighted mean between the blurred and the original
## image? We pretty much split it and only use it on the borders
## but looks like we end up with an image too blurred.
##
## 2. how do they blur the image? I will guess they pad the
## image but how?
##
## Note: always test this function with input as double precision
## since it returns the same class as input and may hide our
## differences.
blurred = fftconvn (img, psf, "same");
xpsf = normalized_autocorrelation (psf);
## The following will expand a ND matrix into a larger size
## repeating the center elements, e.g.,
##
## 1 2 2 2 3
## 1 2 3 4 5 5 5 6
## 4 5 6 => 4 5 5 5 6
## 7 8 9 4 5 5 5 6
## 7 8 8 8 9
## note the xpsf will always have odd sizes (2*psf_size +1)
xdims = ndims (xpsf);
xpsf_size = size (xpsf)(:);
idim = arrayfun (@(c, n, f) [1:c repmat(c, [1 n]) c:f], ceil (xpsf_size /2),
img_size(1:xdims) - xpsf_size -1, xpsf_size,
"UniformOutput", false);
subs = cell (xdims, 1);
[subs{:}] = ndgrid (idim{:});
inds = sub2ind (xpsf_size, subs{:});
weights = xpsf(inds);
imgt = (img .* weights) + (blurred .* (1 - weights));
imgt = cast (imgt, class (img));
endfunction
function acn = normalized_autocorrelation (psf)
idx = arrayfun (@colon, size (psf), repmat (-1, [1 ndims(psf)]),
repmat (1, [1 ndims(psf)]), "UniformOutput", false);
ac = convn (psf, conj (psf(idx{:})));
acn = ac / max (ac(:));
endfunction
%!assert (class (edgetaper (rand (100), rand (16))), "double")
%!assert (class (edgetaper (randi (255, 100, "uint8"), rand (16))), "uint8")
image-2.12.0/inst/PaxHeaders.25054/rgb2ycbcr.m 0000644 0000000 0000000 00000000062 13615546210 015431 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/rgb2ycbcr.m 0000644 0001750 0001750 00000005417 13615546210 020202 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2013 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{YCbCrmap} =} rgb2ycbcr (@var{cmap})
## @deftypefnx {Function File} {@var{YCbCr} =} rgb2ycbcr (@var{RGB})
## @deftypefnx {Function File} {@dots{} =} rgb2ycbcr (@dots{}, [@var{Kb} @var{Kr}])
## @deftypefnx {Function File} {@dots{} =} rgb2ycbcr (@dots{}, @var{standard})
## Convert RGB values to YCbCr.
##
## The conversion changes the image @var{RGB} or colormap @var{cmap}, from
## the RGB color model to YCbCr (luminance, chrominance blue, and chrominance
## red). @var{RGB} must be of class double, single, uint8, or uint16.
##
## The formula used for the conversion is dependent on two constants, @var{Kb}
## and @var{Kr} which can be specified individually, or according to existing
## standards:
##
## @table @asis
## @item "601" (default)
## According to the ITU-R BT.601 (formerly CCIR 601) standard. Its values
## of @var{Kb} and @var{Kr} are 0.114 and 0.299 respectively.
## @item "709"
## According to the ITU-R BT.709 standard. Its values of @var{Kb} and
## @var{Kr} are 0.0722 and 0.2116 respectively.
## @item "2020"
## According to the ITU-R BT.2020 standard. Its values of @var{Kb} and
## @var{Kr} are 0.0593 and 0.2627 respectively.
## @end table
##
## @seealso{hsv2rgb, ntsc2rgb, rgb2hsv, rgb2ntsc}
## @end deftypefn
function ycbcr = rgb2ycbcr (rgb, standard = "601")
if (nargin < 1 || nargin > 2)
print_usage ();
endif
ycbcr = ycbcrfunc ("rgb2ycbcr", rgb, standard);
endfunction
%!test
%! in(:,:,1) = magic (5);
%! in(:,:,2) = magic (5);
%! in(:,:,3) = magic (5);
%! out(:,:,1) = [31 37 17 23 29
%! 36 20 22 28 30
%! 19 21 27 33 35
%! 25 26 32 34 19
%! 25 31 37 18 24];
%! out(:,:,2) = 128;
%! out(:,:,3) = 128;
%! assert (rgb2ycbcr (uint8 (in)), uint8 (out));
%!shared cbcr
%! cbcr = 0.5019607843137255;
%! out(1:10, 1) = linspace (16/255, 235/255, 10);
%! out(:, [2 3]) = cbcr;
%! assert (rgb2ycbcr (gray (10)), out, 0.00001);
%!assert (rgb2ycbcr ([1 1 1]), [0.92157 cbcr cbcr], 0.0001);
%!assert (class (rgb2ycbcr (single (rand (5, 5, 3)))), "single")
image-2.12.0/inst/PaxHeaders.25054/imsmooth.m 0000644 0000000 0000000 00000000062 13615546210 015411 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imsmooth.m 0000644 0001750 0001750 00000045131 13615546210 020157 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2007 Søren 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 .
## -*- texinfo -*-
## @deftypefn {Function File} @var{J} = imsmooth(@var{I}, @var{name}, @var{options})
## Smooth the given image using several different algorithms.
##
## The first input argument @var{I} is the image to be smoothed. If it is an RGB
## image, each color plane is treated separately.
## The variable @var{name} must be a string that determines which algorithm will
## be used in the smoothing. It can be any of the following strings
##
## @table @asis
## @item "Gaussian"
## Isotropic Gaussian smoothing. This is the default.
## @item "Average"
## Smoothing using a rectangular averaging linear filter.
## @item "Disk"
## Smoothing using a circular averaging linear filter.
## @item "Median"
## Median filtering.
## @item "Bilateral"
## Gaussian bilateral filtering.
## @item "Perona & Malik"
## @itemx "Perona and Malik"
## @itemx "P&M"
## Smoothing using nonlinear isotropic diffusion as described by Perona and Malik.
## @item "Custom Gaussian"
## Gaussian smoothing with a spatially varying covariance matrix.
## @end table
##
## In all algorithms the computation is done in double precision floating point
## numbers, but the result has the same type as the input. Also, the size of the
## smoothed image is the same as the input image.
##
## @strong{Gaussian - Isotropic Gaussian smoothing}
##
## The image is convolved with a Gaussian filter with spread @var{sigma}.
## By default @var{sigma} is @math{0.5}, but this can be changed. If the third
## input argument is a scalar it is used as the filter spread.
##
## The image is extrapolated symmetrically before the convolution operation.
##
## @strong{Average - Rectangular averaging linear filter}
##
## The image is convolved with @var{N} by @var{M} rectangular averaging filter.
## By default a 3 by 3 filter is used, but this can e changed. If the third
## input argument is a scalar @var{N} a @var{N} by @var{N} filter is used. If the third
## input argument is a two-vector @code{[@var{N}, @var{M}]} a @var{N} by @var{M}
## filter is used.
##
## The image is extrapolated symmetrically before the convolution operation.
##
## @strong{Disk - Circular averaging linear filter}
##
## The image is convolved with circular averaging filter. By default the filter
## has a radius of 5, but this can e changed. If the third input argument is a
## scalar @var{r} the radius will be @var{r}.
##
## The image is extrapolated symmetrically before the convolution operation.
##
## @strong{Median - Median filtering}
##
## Each pixel is replaced with the median of the pixels in the local area. By
## default, this area is 3 by 3, but this can be changed. If the third input
## argument is a scalar @var{N} the area will be @var{N} by @var{N}, and if it's
## a two-vector [@var{N}, @var{M}] the area will be @var{N} by @var{M}.
##
## The image is extrapolated symmetrically before the filtering is performed.
##
## @strong{Bilateral - Gaussian bilateral filtering}
##
## The image is smoothed using Gaussian bilateral filtering as described by
## Tomasi and Manduchi [2]. The filtering result is computed as
## @example
## @var{J}(x0, y0) = k * SUM SUM @var{I}(x,y) * w(x, y, x0, y0, @var{I}(x0,y0), @var{I}(x,y))
## x y
## @end example
## where @code{k} a normalisation variable, and
## @example
## w(x, y, x0, y0, @var{I}(x0,y0), @var{I}(x,y))
## = exp(-0.5*d([x0,y0],[x,y])^2/@var{sigma_d}^2)
## * exp(-0.5*d(@var{I}(x0,y0),@var{I}(x,y))^2/@var{sigma_r}^2),
## @end example
## with @code{d} being the Euclidian distance function. The two parameters
## @var{sigma_d} and @var{sigma_r} control the amount of smoothing. @var{sigma_d}
## is the size of the spatial smoothing filter, while @var{sigma_r} is the size
## of the range filter. When @var{sigma_r} is large the filter behaves almost
## like the isotropic Gaussian filter with spread @var{sigma_d}, and when it is
## small edges are preserved better. By default @var{sigma_d} is 2, and @var{sigma_r}
## is @math{10/255} for floating points images (with integer images this is
## multiplied with the maximal possible value representable by the integer class).
##
## The image is extrapolated symmetrically before the filtering is performed.
##
## @strong{Perona and Malik}
##
## The image is smoothed using nonlinear isotropic diffusion as described by Perona and
## Malik [1]. The algorithm iteratively updates the image using
##
## @example
## I += lambda * (g(dN).*dN + g(dS).*dS + g(dE).*dE + g(dW).*dW)
## @end example
##
## @noindent
## where @code{dN} is the spatial derivative of the image in the North direction,
## and so forth. The function @var{g} determines the behaviour of the diffusion.
## If @math{g(x) = 1} this is standard isotropic diffusion.
##
## The above update equation is repeated @var{iter} times, which by default is 10
## times. If the third input argument is a positive scalar, that number of updates
## will be performed.
##
## The update parameter @var{lambda} affects how much smoothing happens in each
## iteration. The algorithm can only be proved stable is @var{lambda} is between
## 0 and 0.25, and by default it is 0.25. If the fourth input argument is given
## this parameter can be changed.
##
## The function @var{g} in the update equation determines the type of the result.
## By default @code{@var{g}(@var{d}) = exp(-(@var{d}./@var{K}).^2)} where @var{K} = 25.
## This choice gives privileges to high-contrast edges over low-contrast ones.
## An alternative is to set @code{@var{g}(@var{d}) = 1./(1 + (@var{d}./@var{K}).^2)},
## which gives privileges to wide regions over smaller ones. The choice of @var{g}
## can be controlled through the fifth input argument. If it is the string
## @code{"method1"}, the first mentioned function is used, and if it is @var{"method2"}
## the second one is used. The argument can also be a function handle, in which case
## the given function is used. It should be noted that for stability reasons,
## @var{g} should return values between 0 and 1.
##
## The following example shows how to set
## @code{@var{g}(@var{d}) = exp(-(@var{d}./@var{K}).^2)} where @var{K} = 50.
## The update will be repeated 25 times, with @var{lambda} = 0.25.
##
## @example
## @var{g} = @@(@var{d}) exp(-(@var{d}./50).^2);
## @var{J} = imsmooth(@var{I}, "p&m", 25, 0.25, @var{g});
## @end example
##
## @strong{Custom Gaussian - Custom Gaussian Smoothing}
##
## The image is smoothed using a Gaussian filter with a spatially varying covariance
## matrix. The third and fourth input arguments contain the Eigenvalues of the
## covariance matrix, while the fifth contains the rotation of the Gaussian.
## These arguments can be matrices of the same size as the input image, or scalars.
## In the last case the scalar is used in all pixels. If the rotation is not given
## it defaults to zero.
##
## The following example shows how to increase the size of an Gaussian
## filter, such that it is small near the upper right corner of the image, and
## large near the lower left corner.
##
## @example
## [@var{lambda1}, @var{lambda2}] = meshgrid (linspace (0, 25, columns (@var{I})), linspace (0, 25, rows (@var{I})));
## @var{J} = imsmooth (@var{I}, "Custom Gaussian", @var{lambda1}, @var{lambda2});
## @end example
##
## The implementation uses an elliptic filter, where only neighbouring pixels
## with a Mahalanobis' distance to the current pixel that is less than 3 are
## used to compute the response. The response is computed using double precision
## floating points, but the result is of the same class as the input image.
##
## @strong{References}
##
## [1] P. Perona and J. Malik,
## "Scale-space and edge detection using anisotropic diffusion",
## IEEE Transactions on Pattern Analysis and Machine Intelligence,
## 12(7):629-639, 1990.
##
## [2] C. Tomasi and R. Manduchi,
## "Bilateral Filtering for Gray and Color Images",
## Proceedings of the 1998 IEEE International Conference on Computer Vision, Bombay, India.
##
## @seealso{imfilter, fspecial}
## @end deftypefn
## TODO: Implement Joachim Weickert's anisotropic diffusion (it's soo cool)
function J = imsmooth(I, name = "Gaussian", varargin)
## Check inputs
if (nargin == 0)
print_usage();
endif
if (! isimage (I))
error("imsmooth: I must be an image");
endif
[imrows, imcols, imchannels, tmp] = size(I);
if ((imchannels != 1 && imchannels != 3) || tmp != 1)
error("imsmooth: first input argument must be an image");
endif
if (nargin == 2 && isscalar (name))
varargin {1} = name;
name = "Gaussian";
endif
if (!ischar(name))
error("imsmooth: second input must be a string");
endif
len = length(varargin);
## Save information for later
C = class(I);
## Take action depending on 'name'
switch (lower(name))
##############################
### Gaussian smoothing ###
##############################
case "gaussian"
## Check input
s = 0.5;
if (len > 0)
if (isscalar(varargin{1}) && varargin{1} > 0)
s = varargin{1};
else
error("imsmooth: third input argument must be a positive scalar when performing Gaussian smoothing");
endif
endif
## Compute filter
h = ceil(3*s);
f = exp( (-(-h:h).^2)./(2*s^2) ); f /= sum(f);
## Pad image
I = double (padarray (I, [h h], "symmetric"));
## Perform the filtering
for i = imchannels:-1:1
J(:,:,i) = conv2(f, f, I(:,:,i), "valid");
endfor
############################
### Square averaging ###
############################
case "average"
## Check input
s = [3, 3];
if (len > 0)
if (isscalar(varargin{1}) && varargin{1} > 0)
s = [varargin{1}, varargin{1}];
elseif (isvector(varargin{1}) && length(varargin{1}) == 2 && all(varargin{1} > 0))
s = varargin{1};
else
error("imsmooth: third input argument must be a positive scalar or two-vector when performing averaging");
endif
endif
## Compute filter
f2 = ones(1,s(1))/s(1);
f1 = ones(1,s(2))/s(2);
## Pad image
I = padarray (I, floor ( [s(1) s(2)] /2), "symmetric", "pre");
I = padarray (I, floor (([s(1) s(2)]-1)/2), "symmetric", "post");
I = double (I);
## Perform the filtering
for i = imchannels:-1:1
J(:,:,i) = conv2 (f1, f2, I(:,:,i), "valid");
endfor
##############################
### Circular averaging ###
##############################
case "disk"
## Check input
r = 5;
if (len > 0)
if (isscalar(varargin{1}) && varargin{1} > 0)
r = varargin{1};
else
error("imsmooth: third input argument must be a positive scalar when performing averaging");
endif
endif
## Compute filter
f = fspecial("disk", r);
## Pad image
i = double (padarray (I, [r r], "symmetric"));
## Perform the filtering
for i = imchannels:-1:1
J(:,:,i) = conv2(I(:,:,i), f, "valid");
endfor
############################
### Median Filtering ###
############################
case "median"
## Check input
s = [3, 3];
if (len > 0)
opt = varargin{1};
if (isscalar(opt) && opt > 0)
s = [opt, opt];
elseif (isvector(opt) && numel(opt) == 2 && all(opt>0))
s = opt;
else
error("imsmooth: third input argument must be a positive scalar or two-vector");
endif
s = round(s); # just in case the use supplies non-integers.
endif
## Perform the filtering
for i = imchannels:-1:1
J(:,:,i) = medfilt2(I(:,:,i), s, "symmetric");
endfor
###############################
### Bilateral Filtering ###
###############################
case "bilateral"
## Check input
if (len > 0 && !isempty(varargin{1}))
if (isscalar(varargin{1}) && varargin{1} > 0)
sigma_d = varargin{1};
else
error("imsmooth: spread of closeness function must be a positive scalar");
endif
else
sigma_d = 2;
endif
if (len > 1 && !isempty(varargin{2}))
if (isscalar(varargin{2}) && varargin{2} > 0)
sigma_r = varargin{2};
else
error("imsmooth: spread of similarity function must be a positive scalar");
endif
else
sigma_r = 10/255;
if (isinteger(I)), sigma_r *= intmax(C); endif
endif
## Pad image
s = max([round(3*sigma_d),1]);
I = padarray (I, [s s], "symmetric");
## Perform the filtering
J = __bilateral__(I, sigma_d, sigma_r);
############################
### Perona and Malik ###
############################
case {"perona & malik", "perona and malik", "p&m"}
## Check input
K = 25;
method1 = @(d) exp(-(d./K).^2);
method2 = @(d) 1./(1 + (d./K).^2);
method = method1;
lambda = 0.25;
iter = 10;
if (len > 0 && !isempty(varargin{1}))
if (isscalar(varargin{1}) && varargin{1} > 0)
iter = varargin{1};
else
error("imsmooth: number of iterations must be a positive scalar");
endif
endif
if (len > 1 && !isempty(varargin{2}))
if (isscalar(varargin{2}) && varargin{2} > 0)
lambda = varargin{2};
else
error("imsmooth: fourth input argument must be a scalar when using 'Perona & Malik'");
endif
endif
if (len > 2 && !isempty(varargin{3}))
fail = false;
if (ischar(varargin{3}))
if (strcmpi(varargin{3}, "method1"))
method = method1;
elseif (strcmpi(varargin{3}, "method2"))
method = method2;
else
fail = true;
endif
elseif (strcmp(typeinfo(varargin{3}), "function handle"))
method = varargin{3};
else
fail = true;
endif
if (fail)
error("imsmooth: fifth input argument must be a function handle or the string 'method1' or 'method2' when using 'Perona & Malik'");
endif
endif
## Perform the filtering
I = double(I);
for i = imchannels:-1:1
J(:,:,i) = pm(I(:,:,i), iter, lambda, method);
endfor
#####################################
### Custom Gaussian Smoothing ###
#####################################
case "custom gaussian"
## Check input
if (length (varargin) < 2)
error ("imsmooth: not enough input arguments");
elseif (length (varargin) == 2)
varargin {3} = 0; # default theta value
endif
arg_names = {"third", "fourth", "fifth"};
for k = 1:3
if (isscalar (varargin {k}))
varargin {k} = repmat (varargin {k}, imrows, imcols);
elseif (isnumeric (varargin {k}) && ismatrix (varargin {k}))
if (rows (varargin {k}) != imrows || columns (varargin {k}) != imcols)
error (["imsmooth: %s input argument must have same number of rows "
"and columns as the input image"], arg_names {k});
endif
else
error ("imsmooth: %s input argument must be a scalar or a matrix", arg_names {k});
endif
if (!strcmp (class (varargin {k}), "double"))
error ("imsmooth: %s input argument must be of class 'double'", arg_names {k});
endif
endfor
## Perform the smoothing
for i = imchannels:-1:1
J(:,:,i) = __custom_gaussian_smoothing__ (I(:,:,i), varargin {:});
endfor
######################################
### Mean Shift Based Smoothing ###
######################################
# NOT YET IMPLEMENTED
#case "mean shift"
# J = mean_shift(I, varargin{:});
#############################
### Unknown filtering ###
#############################
otherwise
error("imsmooth: unsupported smoothing type '%s'", name);
endswitch
## Cast the result to the same class as the input
J = cast(J, C);
endfunction
## Perona and Malik for gray-scale images
function J = pm(I, iter, lambda, g)
## Initialisation
[imrows, imcols] = size(I);
J = I;
for i = 1:iter
## Pad image
padded = padarray (J, [1 1], "circular");
## Spatial derivatives
dN = padded(1:imrows, 2:imcols+1) - J;
dS = padded(3:imrows+2, 2:imcols+1) - J;
dE = padded(2:imrows+1, 3:imcols+2) - J;
dW = padded(2:imrows+1, 1:imcols) - J;
gN = g(dN);
gS = g(dS);
gE = g(dE);
gW = g(dW);
## Update
J += lambda*(gN.*dN + gS.*dS + gE.*dE + gW.*dW);
endfor
endfunction
## Mean Shift smoothing for gray-scale images
## XXX: This function doesn't work!!
#{
function J = mean_shift(I, s1, s2)
sz = [size(I,2), size(I,1)];
## Mean Shift
[x, y] = meshgrid(1:sz(1), 1:sz(2));
f = ones(s1);
tmp = conv2(ones(sz(2), sz(1)), f, "same"); # We use normalised convolution to handle the border
m00 = conv2(I, f, "same")./tmp;
m10 = conv2(I.*x, f, "same")./tmp;
m01 = conv2(I.*y, f, "same")./tmp;
ms_x = round( m10./m00 ); # konverter ms_x og ms_y til linære indices og arbejd med dem!
ms_y = round( m01./m00 );
ms = sub2ind(sz, ms_y, ms_x);
%for i = 1:10
i = 0;
while (true)
disp(++i)
ms(ms) = ms;
#new_ms = ms(ms);
if (i >200), break; endif
#idx = ( abs(I(ms)-I(new_ms)) < s2 );
#ms(idx) = new_ms(idx);
%for j = 1:length(ms)
% if (abs(I(ms(j))-I(ms(ms(j)))) < s2)
% ms(j) = ms(ms(j));
% endif
%endfor
endwhile
%endfor
## Compute result
J = I(ms);
endfunction
#}
%!test
%! ## checking Bilateral Filter
%!
%! ## constant image remain the same after Bilateral Filter
%! A = uint8(255*ones(128,128));
%! B = uint8(imsmooth(A, 'Bilateral', 2, 10));
%! assert (A,B);
%!
%! ## Bilateral Filter does not smear outlayers
%! A = zeros(256,256);
%! A(128,128) = 256;
%! ## bilateral filter does not smear outlayers
%! B = imsmooth(A, 'Bilateral', 2, 10);
%! assert (A,B,1.e-140);
%!
%! ## When sigma_r is large the filter behaves almost
%! ## like the isotropic Gaussian filter
%!
%! A0 = fspecial ('gaussian',100,100);
%! A = uint8(A0/max(max(A0))*255);
%! B1 = imsmooth(A, 'Bilateral', 2, 100);
%! B2 = imsmooth(A, 'Gaussian', 2);
%! assert (B1,B2);
image-2.12.0/inst/PaxHeaders.25054/imextendedmin.m 0000644 0000000 0000000 00000000062 13615546210 016404 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imextendedmin.m 0000644 0001750 0001750 00000013553 13615546210 021155 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2017 Hartmut Gimpel
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} @ imextendedmin (@var{im}, @var{h})
## @deftypefnx {Function File} {} @ imextendedmin (@var{im}, @var{h}, @var{conn})
## Caculate the (morphological) extended minima of an image @var{im}.
##
## This function returns a binary image that marks the extended minima
## of the input image @var{im}. Those extended minima are definded as the
## regional minima of the h-minima transform of the input image (which removed
## all regional minima of a depth less then h beforehand).
##
## The input image @var{im} needs to be a real and nonsparse numeric array (of any dimension),
## and the height parameter @var{h} a non-negative scalar number.
##
## The definition of "neighborhood" for this morphological operation can be set
## with the connectivity parameter @var{conn},
## which defaults to 8 for 2D images, to 26 for 3D images and to
## @code{conn(ndims(n), "maximal")} in general. @var{conn} can be given as scalar value
## or as a boolean matrix (see @code{conndef} for details).
##
## The output is a binary image of same shape as the input image @var{im}.
##
## @seealso{imextendedmax, imhmin, imregionalmin, imreconstruct}
## @end deftypefn
## Algorithm:
## * The 'classical' reference for this morphological "extended maximum" function
## is the book "Morphological Image Analysis" by P. Soille
## (Springer, 2nd edition, 2004), chapter 6.3.4 "Extended and h-extrema".
## It says: "The extended minima EMIN are definded as the regional minima
## of the corresponding h-minima transformation, the extended maxima
## EMAX being definded by duality:
## EMAX_h(f) = RMAX[HMAX_h(f)],
## EMIN_h(f) = RMIN[HMIN_h(f)].".
## * A more easily accessible reference is for example the following
## web page by Régis Clouard:
## https://clouard.users.greyc.fr/Pantheon/experiments/morphology/index-en.html#extremum
## It says: "The extended minima EMIN are definded as the regional minima of
## the corresponding h-minima transformation:
## EMIN_h(f) = RMIN( HMIN_h(f) )"
## (We will call the grayscale image im instead of f.)
function bw = imextendedmin (im, h, varargin)
## retrieve input parameters, set default value:
if (nargin == 3)
conn = varargin{1};
iptcheckconn (conn, "imextendedmin", "CONN");
elseif (nargin == 2)
conn = conndef (ndims (im), "maximal");
else
print_usage ();
endif
## check input parameters:
if (! isnumeric (im) || ! isreal (im) || issparse (im) )
error ("imextendedmin: IM must be a real and nonsparse numeric array");
endif
if (! isnumeric (h) || ! isscalar (h) || ! isreal (h) || (h<0) )
error ("imextendedmin: H must be a non-negative scalar number");
endif
## do the actual calculation:
bw = imregionalmin (imhmin (im, h, conn), conn);
endfunction
%!shared im0, bw0_h2_out
%! im0 = uint8 ([5 5 5 5 5;
%! 5 4 3 4 5;
%! 5 3 0 3 5;
%! 5 4 3 4 5;
%! 5 5 5 5 5]);
%! bw0_h2_out = false (5);
%! bw0_h2_out(3,3) = true;
## test input syntax:
%!error imextendedmin ()
%!error imextendedmin (im0)
%!error imextendedmin ("hello", 2)
%!error imextendedmin (i.*im0, 2)
%!error imextendedmin (sparse (im0), 2)
%!error imextendedmin (im0, -2)
%!error imextendedmin (im0, 'a')
%!error imextendedmin (im0, ones (2))
%!error imextendedmin (im0, 2*i)
%!assert (imextendedmin (im0, 2), bw0_h2_out)
%!assert (imextendedmin (double (im0), 2), bw0_h2_out)
%!assert (imextendedmin (im0, 2, 8), bw0_h2_out)
%!assert (imextendedmin (im0, 2, 4), bw0_h2_out)
%!assert (imextendedmin (im0, 2, true (3)), bw0_h2_out)
## test output class and shape:
%!test
%! out = imextendedmin (im0, 2);
%! assert (size (out), size (im0))
%! assert (class (out), "logical")
%!test
%! out = imextendedmin (single (im0), 2);
%! assert (size (out), size (im0))
%! assert (class (out), "logical")
%!test
%! out = imextendedmin (uint8 (im0), 2);
%! assert (size (out), size (im0))
%! assert (class (out), "logical")
%!test
%! out = imextendedmin (uint16 (im0), 2);
%! assert (size (out), size (im0))
%! assert (class (out), "logical")
%!test
%! im = cat (3, im0, im0, im0, im0);
%! out = imextendedmin (im, 2);
%! assert (size (out), size (im))
## test calculation result:
%!test
%! im = 10 .* ones (10);
%! im(2:4, 2:4) = 7;
%! im(6:8, 6:8) = 2;
%! expected_4 = false (10);
%! expected_4(6:8, 6:8) = true;
%! expected_2 = expected_4;
%! expected_2(2:4, 2:4) = true;
%! out = imextendedmin (im, 4);
%! assert (out, expected_4, eps)
%! out = imextendedmin (0.1.*im, 0.4);
%! assert (out, expected_4, eps)
%! out = imextendedmin (im, 2);
%! assert (out, expected_2, eps)
%!test
%! im2 = 10 .* ones (10);
%! im2(2:4, 2:4) = 7;
%! im2(6:9, 6:9)=2;
%! im2(5, 5)=2;
%! im2(6, 7)=10;
%! im2(7, 8)=10;
%! expected_8 = false (10);
%! expected_8(6:9, 6:9) = true;
%! expected_8(5, 5) = true;
%! expected_8(6, 7) = false;
%! expected_8(7, 8) = false;
%! expected_4 = expected_8;
%! expected_4(2:4, 2:4) = true;
%! out2 = imextendedmin (im2, 2);
%! assert (out2, expected_8, eps)
%! out2 = imextendedmin (im2, 2, 4);
%! assert (out2, expected_4, eps)
%! out2 = imextendedmin (im2, 2, 8);
%! assert (out2, expected_8, eps)
image-2.12.0/inst/PaxHeaders.25054/imadjust.m 0000644 0000000 0000000 00000000062 13615546210 015372 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/imadjust.m 0000644 0001750 0001750 00000044435 13615546210 020146 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 1999,2000 Kai Habel
## Copyright (C) 2004 Josep Monés i Teixidor
## Copyright (C) 2015 Carnë Draug
## Copyright (C) 2015 Hartmut Gimpel
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} imadjust (@var{I})
## @deftypefnx {Function File} {} imadjust (@var{I}, [@var{low_in}; @var{high_in}])
## @deftypefnx {Function File} {} imadjust (@var{I}, [@var{low_in}; @var{high_in}],[@var{low_out}; @var{high_out}])
## @deftypefnx {Function File} {} imadjust (@dots{}, @var{gamma})
## @deftypefnx {Function File} {} imadjust (@var{cmap}, @dots{})
## @deftypefnx {Function File} {} imadjust (@var{RGB}, @dots{})
## Adjust image or colormap intensity (values).
##
## Returns an image of equal dimensions to @var{I}, @var{cmap}, or
## @var{RGB}, with its intensity values adjusted, usually for the
## purpose of increasing the image contrast.
##
## The values are rescaled according to the input and output limits,
## @var{low_in} and @var{high_in}, and @var{low_out} and @var{high_out}
## respectively. The first pair sets the lower and upper limits
## on the input image, values above and below them being clipped.
## The second pair sets the lower and upper limits for the output
## image, the interval to which the image will be scaled after
## clipping the input limits.
##
## For example:
##
## @example
## imadjust (img, [0.2; 0.9], [0; 1])
## @end example
##
## will clip all values in @var{img} outside the range [0.2 0.9],
## and then rescale them linearly into the range [0 1].
##
## The input and output limits must be defined as arrays of 2 rows
## with values in the [0 1] range. Each 2 rows column corresponds
## to a single plane in the input image (or each column of a
## colormap), thus supporting images with any number of dimensions.
## If the limits have only 2 elements, the same limits are on all planes.
## This format is matched to @code{stretchlim} which is designed
## to create the input limits for @code{imadjust}.
##
## By default, the limits are adjusted to maximize the contrast, using
## the whole range of values in the image class; and cause a 2%
## saturation (1% on the lower and upper end of the image). It is
## equivalent to:
##
## @example
## imadjust (@var{I}, stretchlim (@var{I}, 0.01), [0; 1])
## @end example
##
## A common usage is to maximize the display range without saturation:
##
## @example
## imadjust (img, stretchlim (img, 0)) # adjustment performed per plane
## imadjust (img, stretchlim (img(:), 0)) # equal adjustment to all planes
## @end example
##
## For sake of @sc{Matab} compatibility, an empty array in any of
## the limits is interpreted as @code{[0; 1]}.
##
## If @var{low_out} is higher than @var{high_out}, the output image
## will be reversed (image negative or complement).
##
## The @var{gamma} value shapes the mapping curve between the input
## and output elements. It defaults to 1, a linear mapping. Higher
## values of @var{gamma} will curve the mapping downwards and to the right,
## increasing the contrast in the brighter (higher) values of the
## input image. Lower values of @var{gamma} will curve the mapping
## upwards and to the left, increasing the contrast in the darker (lower)
## values of the input image.
##
## As with the limits, @var{gamma} can have different values for each
## plane, a 1 row column per plane, thus supporting input images with
## any number of dimensions. If only a scalar, the same value is used
## in all planes.
##
## The formula used to perform the mapping (omitting saturation) is:
##
## @example
## low_out + (high_out - low_out) .* ((I - low_in) / (high_in - low_in)) .^ gamma
## @end example
##
## @seealso{brighten, contrast, histeq, stretchlim}
## @end deftypefn
function adj = imadjust (img, in, out = [0; 1], gamma = 1)
if (nargin () < 1 || nargin () > 4)
print_usage ();
endif
if (! isimage (img))
error ("imadjust: I, RGB, or CMAP must be an image or a colormap");
elseif (! isnumeric (img))
## isimage() allows for boolean images which imadjust should not
error ("imadjust: I, RGB, or CMAP must be numeric");
endif
sz = size (img);
if (iscolormap (img))
was_colormap = true;
img = reshape (img, [sz(1) 1 sz(2)]);
sz = size (img);
else
was_colormap = false;
endif
n_planes = prod (sz(3:end));
if (nargin () < 2)
in = stretchlim (img, 0.01);
else
in = parse_limits (in, sz);
endif
out = parse_limits (out, sz);
if (! isfloat (gamma) || any (gamma < 0))
error ("imadjust: GAMMA must be a non-negative floating point")
elseif (isscalar (gamma))
gamma = repmat (gamma, [1 n_planes]);
elseif (! isequal (size (gamma)(2:end), sz(3:end)))
error ("imadjust: GAMMA must be a scalar or 1 row per plane")
endif
if (isfloat (img))
## To make the computations in N dimensions, we make heavy use of
## broadcasting so reshape to have a single value per plane.
in = reshape (in, [2 1 sz(3:end)]);
out = reshape (out, [2 1 sz(3:end)]);
gamma = reshape (gamma, [1 1 sz(3:end)]);
adj = imadjust_direct (img, in, out, gamma);
else # must be integer
## We create a LUT and use intlut instead of a simply converting the
## whole image to single or double. This is mainly for memory
## efficiency but also less computationally intensive. Do not
## forget that in scientific images, 500MB uint8 images are not
## uncommon so we don't want to convert them to double.
cls = class(img);
lut = linspace (0, 1, double (intmax (cls)) - double (intmin (cls)) + 1);
## If there's a single plane or the adjustment are all the same,
## we only need to create one LUT.
if (n_planes == 1 || (all ((in(:,:) == in(:,1))(:))
&& all ((out(:,:) == out(:,1))(:))
&& all (gamma == gamma(1))))
lut = imadjust_direct (lut, in(:,1), out(:,1), gamma(1));
adj = intlut (img, imcast (lut, cls));
else
## Seems like we have different adjustments for each plane. We could
## be smarter than loop over each plane. We could check the unique
## adjustment configurations and loop over them instead. However,
## I'll guess that if the adjustments are not all equal, they are
## likely all different too so this is simpler.
adj = zeros (size (img), cls);
for i = 1:n_planes
lut_adj = imadjust (lut, in(:,i), out(:,i), gamma(i));
adj(:,:,i) = intlut (img(:,:,i), imcast (lut_adj, cls));
endfor
endif
endif
if (was_colormap)
adj = reshape (adj, [sz(1) sz(3)]);
endif
endfunction
function limits = parse_limits (limits, sz)
if (isempty (limits))
limits = repmat ([0; 1], [1 sz(3:end)]);
else
if (! isfloat (limits))
error ("imadjust: IN and OUT must be numeric floating-point arrays");
elseif (min (limits(:)) < 0 || max (limits(:)) > 1)
error ("imadjust: IN and OUT must be on the range [0 1]");
endif
## Only reshape back into 2 row column for a single plane.
## Require the correct format otherwise.
if (numel (limits) == 2)
limits = repmat (limits(:), [1 sz(3:end)]);
elseif (rows (limits) != 2 || ! isequal (sz(3:end), size (limits)(2:end)))
error ("imadjust: IN and OUT must be a 2 row column per plane");
endif
endif
endfunction
## The code that actually does imadjust without any input checking and
## reshaping. So we can call it from imadjust when we know things are good.
function adj = imadjust_direct (img, in, out, gamma)
max_scale = all ((out == [0; 1])(:));
max_scale_complement = all ((out == [1; 0])(:));
lo_idx = [1 repmat({":"}, 1, ndims (in))];
hi_idx = [2 repmat({":"}, 1, ndims (in))];
li = in(lo_idx{:});
hi = in(hi_idx{:});
if (max_scale)
## This is the most common case. Used to stretch all the values
## into the [0 1], which can be computed much more efficiently.
adj = ((img .- li) ./ (hi - li)) .^ gamma;
adj(adj > 1) = 1;
adj(adj < 0) = 0;
elseif (max_scale_complement)
adj = ((img .- li) ./ (hi - li)) .^ gamma;
adj = 1 - adj;
adj(adj > 1) = 1;
adj(adj < 0) = 0;
else # this covers all cases but may be slower than needed
lo = out(lo_idx{:});
ho = out(hi_idx{:});
## Image negative is computed if ho < lo although nothing special is
## needed, since formula automatically handles it.
adj = (img < li) .* lo;
adj += (img >= li & img < hi) .* (lo + (ho - lo) .* ((img - li) ./ (hi - li)) .^ gamma);
adj += (img >= hi) .* ho;
endif
endfunction
%!error imadjust ("bad argument");
%!error imadjust ([1:100], "bad argument", [], 1);
%!error <2 row column per plane> imadjust ([1:100], [0 1 1], [], 1);
%!error <2 row column per plane> imadjust ([1:100], [], [0 1 1], 1);
%!error imadjust ([1:100], [], [], [0; 1]);
%!error imadjust (rand (5, 5, 3), [], [], [0 1]);
%!error imadjust ([1:100], [0; 1], [], -1);
%!error imadjust ([1:100], [0; 5], []);
%!error imadjust ([1:100], [-2; 1], []);
%!error imadjust ([1:100], [], [0; 4]);
%!error imadjust ([1:100], [], [-2; 1]);
%!error imadjust (rand (5) > .5);
## Test default values to 1% on each end saturated and [] as [0; 1]
%!test
%! im = [0.01:0.01:1];
%! assert (imadjust (im), [0 linspace(0, 1, 98) 1], eps)
%! assert (imadjust (im), imadjust (im, stretchlim (im, 0.01), [0; 1], 1))
%! assert (imadjust (im, []), imadjust (im, [0; 1], [0; 1], 1))
%! assert (imadjust (im, [], []), imadjust (im, [0; 1], [0; 1], 1))
%! assert (imadjust (im, [], [.25 .75]), imadjust (im, [0; 1], [.25; .75], 1))
%! assert (imadjust (im, [.25; .75], []), imadjust (im, [.25; .75], [0; 1], 1))
%!assert (imadjust (linspace (0, 1), [], [.25 .75]), linspace (.25, .75, 100), eps)
## test with only input arg
%!assert (imadjust (linspace (0, 1, 100),[1/99; 98/99]),
%! [0 linspace(0, 1, 98) 1], eps)
%!shared cm
%! cm = [[0:8]' [1:9]' [2:10]'] / 10;
## a colormap
%!assert (imadjust (cm, [0; 1], [0.5; 1]), (cm /2) + .5)
## with params in row
%!assert (imadjust (cm, [0 1], [0.5 1]), (cm /2) + .5)
## a colormap, different output adjustment on each channel
%!assert (imadjust (cm, [0; 1], [.1 .2 .3; .7 .8 .9]),
%! (cm*.6) .+ [.1 .2 .3], eps)
## a colormap, different input adjustment on each channel
%!assert (imadjust (cm, [.2 .4 .6; .7 .8 .9], [0; 1]),
%! [[0 0 linspace(0, 1, 6) 1]' ...
%! [0 0 0 linspace(0, 1, 5) 1]' ...
%! [0 0 0 0 linspace(0, 1, 4) 1]'], eps)
### a colormap, different input and output on each
%!assert (imadjust (cm, [.2 .4 .6; .7 .8 .9], [0 .1 .2; .8 .9 1]),
%! [[0 0 linspace(0, .8, 6) .8]' ...
%! [.1 .1 .1 linspace(.1, .9, 5) .9]' ...
%! [.2 .2 .2 .2 linspace(.2, 1, 4) 1]'], eps)
## a colormap, different gamma, input and output on each
%!assert (imadjust (cm, [.2 .4 .6; .7 .8 .9], [0 .1 .2; .8 .9 1], [0.5 1 2]),
%! [[0 0 0 (((([.3 .4 .5 .6]-.2)/.5).^.5)*.8) .8 .8]' ...
%! [.1 .1 .1 linspace(.1, .9, 5) .9]' ...
%! [.2 .2 .2 .2 .2 ((((([.7 .8]-.6)/.3).^2).*.8)+.2) 1 1]'], eps*10)
## Handling values outside the [0 1] range
%!test
%! im = [-0.4:.1:0.8
%! 0.0:.1:1.2
%! 0.1:.1:1.3
%! -0.4:.2:2.0];
%!
%! ## just clipping
%! assert (imadjust (im, [0; 1], [0; 1]),
%! [0 0 0 0 (0:.1:.8)
%! (0:.1:1) 1 1
%! (.1:.1:1) 1 1 1
%! 0 0 (0:.2:1) 1 1 1 1 1], eps)
%!
%! ## clipping and invert
%! assert (imadjust (im, [0; 1], [1; 0]),
%! [1 1 1 1 (1:-.1:.2)
%! (1:-.1:0) 0 0
%! (.9:-.1:0) 0 0 0
%! 1 1 (1:-.2:0) 0 0 0 0 0], eps)
%!
%! ## rescale
%! assert (imadjust (im, [.2; .7], [.1; .9]),
%! [1 1 1 1 1 1 1 2.6 4.2 5.8 7.4 9 9
%! 1 1 1 2.6 4.2 5.8 7.4 9 9 9 9 9 9
%! 1 1 2.6 4.2 5.8 7.4 9 9 9 9 9 9 9
%! 1 1 1 1 4.2 7.4 9 9 9 9 9 9 9]/10, eps)
%!
%! ## rescale and invert
%! assert (imadjust (im, [.2; .7], [.9; .1]),
%! [9 9 9 9 9 9 9 7.4 5.8 4.2 2.6 1 1
%! 9 9 9 7.4 5.8 4.2 2.6 1 1 1 1 1 1
%! 9 9 7.4 5.8 4.2 2.6 1 1 1 1 1 1 1
%! 9 9 9 9 5.8 2.6 1 1 1 1 1 1 1]/10, eps)
## adjusting only the gamma and nothing else
%!assert (imadjust (linspace (0, 1), [], [], 2), linspace (0, 1) .^ 2)
%!shared oRGB
%! oRGB = zeros (10, 1, 3);
%! oRGB(:,:,1) = [0 linspace(0,1,6) 1 1 1]';
%! oRGB(:,:,2) = [0 0 linspace(0,1,6) 1 1]';
%! oRGB(:,:,3) = [0 0 0 linspace(0,1,6) 1]';
%!assert (imadjust (oRGB, [0; 1], [0; 1]), oRGB)
%!assert (imadjust (oRGB, [.2; .8], [0; 1]),
%! reshape ([[0 0 0 1/3 2/3 1 1 1 1 1]'
%! [0 0 0 0 1/3 2/3 1 1 1 1]'
%! [0 0 0 0 0 1/3 2/3 1 1 1]'], [10 1 3]), eps)
%!assert (imadjust (oRGB, [.2; .8], [.1; .9]),
%! reshape ([[.1 .1 .1 (1/3)+(.1/3) (2/3)-(.1/3) .9 .9 .9 .9 .9]'
%! [.1 .1 .1 .1 (1/3)+(.1/3) (2/3)-(.1/3) .9 .9 .9 .9]'
%! [.1 .1 .1 .1 .1 (1/3)+(.1/3) (2/3)-(.1/3) .9 .9 .9]'],
%! [10 1 3]), eps)
%!assert (imadjust (oRGB, [.2; .8], [.2; .8]),
%! reshape ([[2 2 2 4 6 8 8 8 8 8]'
%! [2 2 2 2 4 6 8 8 8 8]'
%! [2 2 2 2 2 4 6 8 8 8]']/10, [10 1 3]), eps)
## aRGB, different output for each channel
%!assert (imadjust (oRGB, [0; 1], [.1 .2 .3; .9 .8 .7]),
%! reshape ([[1 1 2.6 4.2 5.8 7.4 9 9 9 9]'
%! [2 2 2 3.2 4.4 5.6 6.8 8 8 8]'
%! [3 3 3 3 3.8 4.6 5.4 6.2 7 7]']/10, [10 1 3]), eps)
## a RGB, different input for each channel
%!assert (imadjust (oRGB, [.1 .2 .3; .9 .8 .7], [0; 1]),
%! reshape ([[0 0 .125 .375 .625 .875 1 1 1 1]'
%! [0 0 0 0 1/3 2/3 1 1 1 1]'
%! [0 0 0 0 0 .25 .75 1 1 1]'], [10 1 3]), eps*10)
## a RGB, different input and output on each
%!assert (imadjust (oRGB, [.1 .2 .3; .9 .8 .7], [.2 0 .4; .5 1 .7 ]),
%! reshape ([[.2 .2 .2375 .3125 .3875 .4625 .5 .5 .5 .5]'
%! [0 0 0 0 1/3 2/3 1 1 1 1]'
%! [.4 .4 .4 .4 .4 .475 .625 .7 .7 .7]'], [10 1 3]), eps)
## Test for ND dimensional images
%!test
%! img = rand (4, 4, 2, 3, 4);
%! adj = zeros (4, 4, 2, 3, 4);
%! for p = 1:2
%! for q = 1:3
%! for r = 1:4
%! adj(:,:,p,q,r) = imadjust (img(:,:,p,q,r));
%! endfor
%! endfor
%! endfor
%! assert (imadjust (img), adj)
## Test for ND dimensional images with N dimensional arguments
%!test
%! img = rand (4, 4, 2, 3, 2);
%! adj = zeros (4, 4, 2, 3, 2);
%! in = reshape ([ 3 5 7 9 11 13 15 17 19 21 23 25;
%! 97 95 93 91 89 87 85 83 81 79 77 75] / 100, [2 2 3 2]);
%! out = reshape ([ 5 7 9 11 14 15 17 19 21 23 25 27;
%! 95 93 91 89 87 85 83 81 79 77 75 73] / 100, [2 2 3 2]);
%! gamma = reshape (0.6:.1:1.7, [1 2 3 2]);
%! for p = 1:2
%! for q = 1:3
%! for r = 1:2
%! adj(:,:,p,q,r) = imadjust (img(:,:,p,q,r), in(:,p,q,r),
%! out(:,p,q,r), gamma(1,p,q,r));
%! endfor
%! endfor
%! endfor
%! assert (imadjust (img, in, out, gamma), adj)
## Test how empty matrix is not really the default value
%!test
%! in = int16 (1:6);
%! assert (imadjust (in), int16 ([-32768 -19661 -6554 6553 19660 32767]))
%! assert (imadjust (in, []), in)
##
## Test images of integer class
##
%!test
%! in = uint8([
%! 35 1 6 26 19 24
%! 3 32 7 21 23 25
%! 31 9 2 22 27 20
%! 8 28 33 17 10 15
%! 30 5 34 12 14 16
%! 4 36 29 13 18 11]);
%! out = uint8([
%! 12 0 0 1 0 0
%! 0 8 0 0 0 0
%! 7 0 0 0 2 0
%! 0 3 9 0 0 0
%! 6 0 11 0 0 0
%! 0 13 4 0 0 0]);
%! assert (imadjust (in, [.1 .9], [0 1]), out);
%!test
%! in = uint8([
%! 140 4 24 104 76 96
%! 12 128 28 84 92 100
%! 124 36 8 88 108 80
%! 32 112 132 68 40 60
%! 120 20 136 48 56 64
%! 16 144 116 52 72 44]);
%! out = uint8([
%! 143 0 0 98 63 88
%! 0 128 3 73 83 93
%! 123 13 0 78 103 68
%! 8 108 133 53 18 43
%! 118 0 138 28 38 48
%! 0 148 113 33 58 23]);
%! assert (imadjust (in, [.1 .9], [0 1]), out);
%!test
%! in_u8 = randi ([0 255], 5, 5, 2, 3, "uint8");
%! in_u16 = randi ([0 65535], 5, 5, 2, 3, "uint16");
%! in_i16 = randi ([-32768 32767], 5, 5, 2, 3, "int16");
%! in_u8_d = im2double (in_u8);
%! in_u16_d = im2double (in_u16);
%! in_i16_d = im2double (in_i16);
%!
%! ## default values
%! assert (imadjust (in_u8), im2uint8 (imadjust (in_u8_d)))
%! assert (imadjust (in_u16), im2uint16 (imadjust (in_u16_d)))
%! assert (imadjust (in_i16), im2int16 (imadjust (in_i16_d)))
%!
%! ## single adjustment for all planes
%! args = {[.3; .7], [.1; .9], [1.5]};
%! assert (imadjust (in_u8, args{:}), im2uint8 (imadjust (in_u8_d, args{:})))
%! assert (imadjust (in_u16, args{:}), im2uint16 (imadjust (in_u16_d, args{:})))
%! assert (imadjust (in_i16, args{:}), im2int16 (imadjust (in_i16_d, args{:})))
%!
%! ## single adjustment for all planes (mixed with some complement)
%! args = {reshape([.2 .3 .25 .1 0 .1; .9 .7 .85 .9 1 .8], [2 2 3]),
%! reshape([.1 .2 .05 .9 1 .3; .9 .85 .7 .1 0 .9], [2 2 3]),
%! reshape([1 .75 1 1.2 1.5 2], [1 2 3])};
%! assert (imadjust (in_u8, args{:}), im2uint8 (imadjust (in_u8_d, args{:})))
%! assert (imadjust (in_u16, args{:}), im2uint16 (imadjust (in_u16_d, args{:})))
%! assert (imadjust (in_i16, args{:}), im2int16 (imadjust (in_i16_d, args{:})))
%!
%! ## test use of [] as limit and negative
%! args = {[], [.95; 0], 1.25};
%! assert (imadjust (in_u8, args{:}), im2uint8 (imadjust (in_u8_d, args{:})))
%! assert (imadjust (in_u16, args{:}), im2uint16 (imadjust (in_u16_d, args{:})))
%! assert (imadjust (in_i16, args{:}), im2int16 (imadjust (in_i16_d, args{:})))
image-2.12.0/inst/PaxHeaders.25054/@imref2d 0000644 0000000 0000000 00000000132 13615547225 014754 x ustar 00 30 mtime=1580650133.826895929
30 atime=1580650134.434913451
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/ 0000755 0001750 0001750 00000000000 13615547225 017575 5 ustar 00carandraug carandraug 0000000 0000000 image-2.12.0/inst/@imref2d/PaxHeaders.25054/imref2d.m 0000644 0000000 0000000 00000000062 13615546210 016532 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/imref2d.m 0000644 0001750 0001750 00000026006 13615546210 021300 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{r} =} imref2d
## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize})
## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize}, @var{pixelExtentInWorldX}, @var{pixelExtentInWorldY})
## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize}, @var{xWorldLimits}, @var{yWorldLimits})
## Reference 2-D image to world coordinates.
##
## Creates an imref2d object referencing a 2-D m-by-n image with the size
## @var{imageSize} to world coordinates. The world extent is either given
## by @var{xWorldLimits} and @var{yWorldLimits} or computed from
## @var{pixelExtentInWorldX} and @var{pixelExtentInWorldY}.
## @var{imageSize} is [2, 2] by default.
##
## Intrinsic coordinates are x = 1.0, y = 1.0 in the center of the top left
## pixel and x = n, y = m in the center of the bottom right pixel.
## Spatial resolution in each dimension can be different.
##
## imref2d object has the following properties:
##
## ImageSize - two element integer vector with image height and width
## in pixels.
##
## XWorldLimits - limits of the image along the x-axis in world units
## specified as a two element real vector @code{[xMin, xMax]}.
##
## YWorldLimits - limits of the image along the y-axis in world units
## specified as a two element real vector @code{[yMin, yMax]}.
##
## PixelExtentInWorldX - pixel extent along the x-axis in world units
## specified as a real scalar.
##
## PixelExtentInWorldY - pixel extent along the y-axis in world units
## specified as a real scalar.
##
## ImageExtentInWorldX - image extent along the x-axis in world units
## specified as a real scalar.
##
## ImageExtentInWorldY - image extent along the y-axis in world units
## specified as a real scalar.
##
## XIntrinsicLimits - limits of the image along the x-axis in intrinsic
## units, equals to @code{[n - 0.5, n + 0.5]}.
##
## YIntrinsicLimits - limits of the image along the y-axis in intrinsic
## units, equals to @code{[m - 0.5, m + 0.5]}.
##
## @seealso{imref3d}
## @end deftypefn
function r = imref2d (imageSize, varargin)
if (nargin > 1 && nargin != 3)
print_usage();
endif
if (nargin == 0)
imageSize = [2, 2];
endif
if (nargin > 0)
if (length (imageSize) < 2)
error ("Octave:invalid-input-arg", ...
"ImageSize must have at least two elements");
endif
validateattributes (imageSize, {"numeric"}, ...
{"positive", "integer", "vector"}, "imref2d", "imageSize");
endif
imageSize = imageSize(1:2);
m = imageSize(1);
n = imageSize(2);
if (numel (varargin) == 0)
xWorldLimits = [0.5, n + 0.5];
yWorldLimits = [0.5, m + 0.5];
elseif (numel (varargin) == 2)
if (isscalar (varargin{1}))
validateattributes (varargin{1}, {"numeric"}, ...
{"real", "positive", "scalar"}, "imref2d", "pixelExtentInWorldX");
validateattributes (varargin{2}, {"numeric"}, ...
{"real", "positive", "scalar"}, "imref2d", "pixelExtentInWorldY");
pixelExtentInWorldX = varargin{1};
pixelExtentInWorldY = varargin{2};
else
validateattributes (varargin{1}, {"numeric"}, ...
{"real", "increasing", "vector", "size", [1, 2]}, "imref2d", ...
"xWorldLimits");
validateattributes (varargin{2}, {"numeric"}, ...
{"real", "increasing", "vector", "size", [1, 2]}, "imref2d", ...
"yWorldLimits");
xWorldLimits = varargin{1};
yWorldLimits = varargin{2};
endif
endif
if (exist ("pixelExtentInWorldX") && exist ("pixelExtentInWorldY"))
imageExtentInWorldX = pixelExtentInWorldX * n;
imageExtentInWorldY = pixelExtentInWorldY * m;
xWorldLimits = [pixelExtentInWorldX / 2, imageExtentInWorldX + ...
pixelExtentInWorldX / 2];
yWorldLimits = [pixelExtentInWorldY / 2, imageExtentInWorldY + ...
pixelExtentInWorldY / 2];
elseif (exist ("xWorldLimits") && exist ("yWorldLimits"))
imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1);
imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1);
pixelExtentInWorldX = imageExtentInWorldX / n;
pixelExtentInWorldY = imageExtentInWorldY / m;
endif
xIntrinsicLimits = [0.5, n + 0.5];
yIntrinsicLimits = [0.5, m + 0.5];
r.ImageSize = imageSize;
r.XWorldLimits = xWorldLimits;
r.YWorldLimits = yWorldLimits;
r.PixelExtentInWorldX = pixelExtentInWorldX;
r.PixelExtentInWorldY = pixelExtentInWorldY;
r.ImageExtentInWorldX = imageExtentInWorldX;
r.ImageExtentInWorldY = imageExtentInWorldY;
r.XIntrinsicLimits = xIntrinsicLimits;
r.YIntrinsicLimits = yIntrinsicLimits;
r = class (r, "imref2d");
endfunction
%!error id=Octave:invalid-fun-call imref2d (1, 2, 3, 4)
%!error id=Octave:invalid-input-arg imref2d (42)
%!error id=Octave:invalid-input-arg imref2d ([42])
%!error id=Octave:expected-integer imref2d ([4.2, 42])
%!error id=Octave:expected-positive imref2d ([0, 0])
%!error id=Octave:expected-positive imref2d ([-4, 2])
%!error id=Octave:expected-positive imref2d ([4, 2], 0, 2)
%!error id=Octave:expected-positive imref2d ([4, 2], 2, 0)
%!error id=Octave:expected-real imref2d ([4, 2], j, 2)
%!error id=Octave:expected-real imref2d ([4, 2], 2, j)
%!error id=Octave:expected-real imref2d ([4, 2], [j, 2], [3, 4])
%!error id=Octave:expected-real imref2d ([4, 2], [1, 2], [j, 4])
%!error id=Octave:expected-vector imref2d ([4, 2], [], [])
%!error id=Octave:expected-vector imref2d ([4, 2], [], [1])
%!error id=Octave:expected-scalar imref2d ([4, 2], [1], [])
%!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [0])
%!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [1, 2, 3])
%!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2, 3], [1, 2])
%!error id=Octave:incorrect-size imref2d ([4, 2], [1; 2], [1, 2])
%!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [1; 2])
%!error id=Octave:invalid-indexing imref2d().InvalidProperty
%!error id=Octave:expected-increasing imref2d ([100 200], [1.5 0.5], [2.5 3.5])
%!error id=Octave:expected-increasing imref2d ([100 200], [1.5 2.5], [2.5 1.5])
%!test
%! r = imref2d;
%! assert (r.XWorldLimits, [0.5, 2.5])
%! assert (r.YWorldLimits, [0.5, 2.5])
%! assert (r.ImageSize, [2, 2])
%! assert (r.PixelExtentInWorldX, 1)
%! assert (r.PixelExtentInWorldY, 1)
%! assert (r.ImageExtentInWorldX, 2)
%! assert (r.ImageExtentInWorldY, 2)
%! assert (r.XIntrinsicLimits, [0.5, 2.5])
%! assert (r.YIntrinsicLimits, [0.5, 2.5])
%!test
%! r = imref2d ([100, 200]);
%! assert (r.XWorldLimits, [0.5, 200.5])
%! assert (r.YWorldLimits, [0.5, 100.5])
%! assert (r.ImageSize, [100, 200])
%! assert (r.PixelExtentInWorldX, 1)
%! assert (r.PixelExtentInWorldY, 1)
%! assert (r.ImageExtentInWorldX, 200)
%! assert (r.ImageExtentInWorldY, 100)
%! assert (r.XIntrinsicLimits, [0.5, 200.5])
%! assert (r.YIntrinsicLimits, [0.5, 100.5])
%!test
%! xWorldLimits = [2, 5];
%! yWorldLimits = [3, 6];
%! r = imref2d ([291, 240], xWorldLimits, yWorldLimits);
%! assert (r.XWorldLimits, [2, 5])
%! assert (r.YWorldLimits, [3, 6])
%! assert (r.ImageSize, [291, 240])
%! assert (r.PixelExtentInWorldX, 0.0125)
%! assert (r.PixelExtentInWorldY, 0.0103, 1e-3)
%! assert (r.ImageExtentInWorldX, 3)
%! assert (r.ImageExtentInWorldY, 3)
%! assert (r.XIntrinsicLimits, [0.5, 240.5])
%! assert (r.YIntrinsicLimits, [0.5, 291.5])
%!test
%! pixelExtentInWorldX = 0.3125;
%! pixelExtentInWorldY = 0.3125;
%! r = imref2d ([512, 512], pixelExtentInWorldX, pixelExtentInWorldY);
%! assert (r.XWorldLimits, [0.15625, 160.1562], 1e-4)
%! assert (r.YWorldLimits, [0.15625, 160.1562], 1e-4)
%! assert (r.ImageSize, [512, 512])
%! assert (r.PixelExtentInWorldX, 0.3125)
%! assert (r.PixelExtentInWorldY, 0.3125)
%! assert (r.ImageExtentInWorldX, 160)
%! assert (r.ImageExtentInWorldY, 160)
%! assert (r.XIntrinsicLimits, [0.5, 512.5])
%! assert (r.YIntrinsicLimits, [0.5, 512.5])
%!test
%! pixelExtentInWorldX = 0.1;
%! pixelExtentInWorldY = 0.4;
%! r = imref2d ([100, 200], pixelExtentInWorldX, pixelExtentInWorldY);
%! assert (r.XWorldLimits, [0.05, 20.05], 1e-4)
%! assert (r.YWorldLimits, [0.2, 40.2], 1e-4)
%! assert (r.ImageSize, [100, 200])
%! assert (r.PixelExtentInWorldX, 0.1)
%! assert (r.PixelExtentInWorldY, 0.4)
%! assert (r.ImageExtentInWorldX, 20)
%! assert (r.ImageExtentInWorldY, 40)
%! assert (r.XIntrinsicLimits, [0.5, 200.5])
%! assert (r.YIntrinsicLimits, [0.5, 100.5])
## changing ImageSize
%!test
%! r = imref2d;
%! assert (r.XWorldLimits, [0.5, 2.5])
%! assert (r.YWorldLimits, [0.5, 2.5])
%! assert (r.ImageSize, [2, 2])
%! assert (r.PixelExtentInWorldX, 1)
%! assert (r.PixelExtentInWorldY, 1)
%! assert (r.ImageExtentInWorldX, 2)
%! assert (r.ImageExtentInWorldY, 2)
%! assert (r.XIntrinsicLimits, [0.5, 2.5])
%! assert (r.YIntrinsicLimits, [0.5, 2.5])
%! r.ImageSize = [800, 600];
%! assert (r.XWorldLimits, [0.5, 2.5])
%! assert (r.YWorldLimits, [0.5, 2.5])
%! assert (r.ImageSize, [800, 600])
%! assert (r.PixelExtentInWorldX, 0.003333, 1e-5)
%! assert (r.PixelExtentInWorldY, 0.0025)
%! assert (r.ImageExtentInWorldX, 2)
%! assert (r.ImageExtentInWorldY, 2)
%! assert (r.XIntrinsicLimits, [0.5, 600.5])
%! assert (r.YIntrinsicLimits, [0.5, 800.5])
## changing XWorldLimits and YWorldLimits
%!test
%! r = imref2d;
%! assert (r.XWorldLimits, [0.5, 2.5])
%! assert (r.YWorldLimits, [0.5, 2.5])
%! assert (r.ImageSize, [2, 2])
%! assert (r.PixelExtentInWorldX, 1)
%! assert (r.PixelExtentInWorldY, 1)
%! assert (r.ImageExtentInWorldX, 2)
%! assert (r.ImageExtentInWorldY, 2)
%! assert (r.XIntrinsicLimits, [0.5, 2.5])
%! assert (r.YIntrinsicLimits, [0.5, 2.5])
%! r.XWorldLimits = [-60, 13.33];
%! r.YWorldLimits = [-900.8, -560.26];
%! assert (r.XWorldLimits, [-60, 13.33])
%! assert (r.YWorldLimits, [-900.8, -560.26])
%! assert (r.PixelExtentInWorldX, 36.6650)
%! assert (r.PixelExtentInWorldY, 170.27, 1e-5)
%! assert (r.ImageExtentInWorldX, 73.33, 1e-5)
%! assert (r.ImageExtentInWorldY, 340.54, 1e-5)
%! assert (r.XIntrinsicLimits, [0.5, 2.5])
%! assert (r.YIntrinsicLimits, [0.5, 2.5])
%!test
%! r = imref2d;
%! fail ("r.XWorldLimits = []", "")
%! fail ("r.XWorldLimits = [1]", "")
%! fail ("r.XWorldLimits = [j]", "")
%! fail ("r.XWorldLimits = [1; 2]", "")
%! fail ("r.YWorldLimits = []", "")
%! fail ("r.YWorldLimits = [1]", "")
%! fail ("r.YWorldLimits = [j]", "")
%! fail ("r.YWorldLimits = [1; 2]", "")
%!assert (imref2d ([4, 2, 3]).ImageSize, [4, 2]); image-2.12.0/inst/@imref2d/PaxHeaders.25054/worldToIntrinsic.m 0000644 0000000 0000000 00000000062 13615546210 020517 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/worldToIntrinsic.m 0000644 0001750 0001750 00000005515 13615546210 023267 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {[@var{xIntrinsic}, @var{yIntrinsic}] =} worldToIntrinsic (@var{r}, @var{xWorld}, @var{yWorld})
## Convert from world to intrinsic coordinates.
##
## Converts world coordinates @var{xWorld} and @var{yWorld} to intrinsic
## coordinates @var{xIntrinsic} and @var{yIntrinsic} of an image associated
## with the spatial referencing object @var{r}. If a point
## (@var{xWorld}(i), @var{yWorld}(i)) falls outside the bounds of the image,
## its intrinsic coordinates are extrapolated, possibly resulting in
## negative values.
##
## @seealso{imref2d, imref3d, intrinsicToWorld}
## @end deftypefn
function [xIntrinsic, yIntrinsic] = worldToIntrinsic (r, xWorld, yWorld)
if (nargin != 3)
print_usage ();
endif
validateattributes (xWorld, {"numeric"}, ...
{"real"}, "imref2d", "xWorld");
validateattributes (yWorld, {"numeric"}, ...
{"real"}, "imref2d", "yWorld");
if (! all (size (xWorld) == size (yWorld)))
error ("Octave:invalid-input-arg", ...
"xWorld and yWorld must be of the same size");
endif
xIntrinsicLimits = r.XIntrinsicLimits;
yIntrinsicLimits = r.YIntrinsicLimits;
xWorldLimits = r.XWorldLimits;
yWorldLimits = r.YWorldLimits;
xIntrinsic = xIntrinsicLimits(1) + (xWorld - xWorldLimits(1)) ...
/ r.PixelExtentInWorldX;
yIntrinsic = yIntrinsicLimits(1) + (yWorld - yWorldLimits(1)) ...
/ r.PixelExtentInWorldY;
endfunction
%!error id=Octave:invalid-fun-call worldToIntrinsic (imref2d)
%!error id=Octave:invalid-fun-call worldToIntrinsic (imref2d, 1, 2, 3)
%!error id=Octave:expected-real worldToIntrinsic (imref2d, 1j, 2)
%!error id=Octave:expected-real worldToIntrinsic (imref2d, 1, 2j)
%!error id=Octave:invalid-input-arg worldToIntrinsic (imref2d, [1, 2], 3)
%!error id=Octave:invalid-input-arg worldToIntrinsic (imref2d, [1], [2, 3])
%!test
%! r = imref2d ([512, 512], 0.3125, 0.3125);
%! xW = [38.44, 39.44, 38.44, -0.2];
%! yW = [68.75, 68.75, 75.75, -1];
%! [xI, yI] = worldToIntrinsic (r, xW, yW);
%! assert (xI, [123.008, 126.208, 123.008, -0.64], 1e-6)
%! assert (yI, [220, 220, 242.4, -3.2], 1e-6) image-2.12.0/inst/@imref2d/PaxHeaders.25054/worldToSubscript.m 0000644 0000000 0000000 00000000062 13615546210 020533 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/worldToSubscript.m 0000644 0001750 0001750 00000005447 13615546210 023307 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {[@var{i}, @var{j}] =} worldToSubscript (@var{r}, @var{xWorld}, @var{yWorld})
## Convert world coordinates to row and column subscripts.
##
## Converts world coordinates to row and column subscripts of an image
## associated with the spatial referencing object @var{r}. A point located at
## (@var{xWorld}(i), @var{yWorld}(i)) world coordinates maps to row and column
## subscripts @var{i}(i) and @var{j}(i) respectively. Note the reversed order
## of the dimensions. If the point falls outside the bounds of the image, both
## its row and column subscripts are NaN.
##
## @seealso{imref2d, imref3d, worldToIntrinsic}
## @end deftypefn
function [i, j] = worldToSubscript (r, xWorld, yWorld)
if (nargin != 3)
print_usage ();
endif
validateattributes (xWorld, {"numeric"}, ...
{"real"}, "imref2d", "xWorld");
validateattributes (yWorld, {"numeric"}, ...
{"real"}, "imref2d", "yWorld");
if (! all (size (xWorld) == size (yWorld)))
error ("Octave:invalid-input-arg", ...
"xWorld and yWorld must be of the same size");
endif
[xIntrinsic, yIntrinsic] = worldToIntrinsic (r, xWorld, yWorld);
xIntrinsicLimits = r.XIntrinsicLimits;
yIntrinsicLimits = r.YIntrinsicLimits;
inImage = contains (r, xWorld, yWorld);
xIntrinsic(! inImage) = NaN;
yIntrinsic(! inImage) = NaN;
i = round (yIntrinsic);
j = round (xIntrinsic);
endfunction
%!error id=Octave:invalid-fun-call worldToSubscript (imref2d)
%!error id=Octave:invalid-fun-call worldToSubscript (imref2d, 1, 2, 3)
%!error id=Octave:expected-real worldToSubscript (imref2d, 1j, 2)
%!error id=Octave:expected-real worldToSubscript (imref2d, 1, 2j)
%!error id=Octave:invalid-input-arg worldToSubscript (imref2d, [1, 2], 3)
%!error id=Octave:invalid-input-arg worldToSubscript (imref2d, [1], [2, 3])
%!test
%! r = imref2d ([512, 512], 0.3125, 0.3125);
%! xW = [38.44, 39.44, 38.44, -0.2];
%! yW = [68.75, 68.75, 75.75, -1];
%! [rS, cS] = worldToSubscript (r, xW, yW);
%! assert (rS, [220, 220, 242, NaN])
%! assert (cS, [123, 126, 123, NaN]) image-2.12.0/inst/@imref2d/PaxHeaders.25054/contains.m 0000644 0000000 0000000 00000000062 13615546210 017020 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/contains.m 0000644 0001750 0001750 00000004744 13615546210 021573 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{tf} =} contains (@var{r}, @var{xWorld}, @var{yWorld})
## Determine if image contains points in world coordinate system.
##
## Outputs a logical array @var{tf}, where the i-th nonzero value means the
## point (@var{xWorld}(i), @var{yWorld}(i)) lies within the bounds of
## an image associated with a spatial referencing object @var{r}.
##
## @seealso{imref2d, imref3d}
## @end deftypefn
function tf = contains (r, xWorld, yWorld)
if (nargin != 3)
print_usage();
endif
validateattributes (xWorld, {"numeric"}, ...
{"real"}, "imref2d", "xWorld");
validateattributes (yWorld, {"numeric"}, ...
{"real"}, "imref2d", "yWorld");
if (! all (size (xWorld) == size (yWorld)))
error ("Octave:invalid-input-arg", ...
"xWorld and yWorld must be of the same size");
endif
xWorldLimits = r.XWorldLimits;
yWorldLimits = r.YWorldLimits;
containsX = xWorld >= xWorldLimits(1) & xWorld <= xWorldLimits(2);
containsY = yWorld >= yWorldLimits(1) & yWorld <= yWorldLimits(2);
tf = containsX & containsY;
endfunction
%!error id=Octave:invalid-fun-call contains (imref2d)
%!error id=Octave:invalid-fun-call contains (imref2d, 1)
%!error id=Octave:invalid-fun-call contains (imref2d, 1, 2, 3)
%!error id=Octave:invalid-input-arg contains (imref2d, 1, [2, 3])
%!error id=Octave:invalid-input-arg contains (imref2d, [1, 2], 3)
%!error id=Octave:expected-real contains (imref2d, 0, j)
%!error id=Octave:expected-real contains (imref2d, j, 0)
%!assert (contains (imref2d, [], []), logical( zeros (0, 0)))
%!assert (contains (imref2d, [1, 2; 3, 4], [5, -6; 7, 8]), logical (zeros (2, 2)))
%!test
%! r = imref2d ([256, 256]);
%! assert (contains(r, [5, 8, 8], [5, 10, 257]), logical([1, 1, 0])) image-2.12.0/inst/@imref2d/PaxHeaders.25054/intrinsicToWorld.m 0000644 0000000 0000000 00000000062 13615546210 020517 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/intrinsicToWorld.m 0000644 0001750 0001750 00000006104 13615546210 023262 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {[@var{xWorld}, @var{yWorld}] =} intrinsicToWorld (@var{r}, @var{xIntrinsic}, @var{yIntrinsic})
## Convert from intrinsic to world coordinates.
##
## Converts intrinsic coordinates of the image associated with the spatial
## referencing object @var{r} to world coordinates @var{xWorld}
## and @var{yWorld}. If a point (@var{xIntrinsic}(i), @var{yIntrinsic}(i))
## falls outside the intrinsic bounds of the image, the world coordinates are
## extrapolated, possibly resulting in negative values.
##
## @seealso{imref2d, imref3d, worldToIntrinsic}
## @end deftypefn
function [xWorld, yWorld] = intrinsicToWorld (r, xIntrinsic, yIntrinsic)
if (nargin != 3)
print_usage ();
endif
validateattributes (xIntrinsic, {"numeric"}, ...
{"real"}, "imref2d", "xIntrinsic");
validateattributes (yIntrinsic, {"numeric"}, ...
{"real"}, "imref2d", "yIntrinsic");
if (! all (size (xIntrinsic) == size (yIntrinsic)))
error ("Octave:invalid-input-arg", ...
"xIntrinsic and yIntrinsic must be of the same size");
endif
xIntrinsicLimits = r.XIntrinsicLimits;
yIntrinsicLimits = r.YIntrinsicLimits;
xWorldLimits = r.XWorldLimits;
yWorldLimits = r.YWorldLimits;
xWorld = xWorldLimits(1) + r.PixelExtentInWorldX * ...
(xIntrinsic - xIntrinsicLimits(1));
yWorld = yWorldLimits(1) + r.PixelExtentInWorldY * ...
(yIntrinsic - yIntrinsicLimits(1));
endfunction
%!error id=Octave:invalid-fun-call intrinsicToWorld (imref2d)
%!error id=Octave:invalid-fun-call intrinsicToWorld (imref2d, 1, 2, 3)
%!error id=Octave:expected-real intrinsicToWorld (imref2d, 1j, 2)
%!error id=Octave:expected-real intrinsicToWorld (imref2d, 1, 2j)
%!error id=Octave:invalid-input-arg intrinsicToWorld (imref2d, [1, 2], 3)
%!error id=Octave:invalid-input-arg intrinsicToWorld (imref2d, [1], [2, 3])
%!test
%! r = imref2d ([512, 512], 0.3125, 0.3125);
%! xIntrinsic = [34, 442];
%! yIntrinsic = [172, 172];
%! [xWorld, yWorld] = intrinsicToWorld (r, xIntrinsic, yIntrinsic);
%! assert (xWorld, [10.625, 138.125])
%! assert (yWorld, [53.75, 53.75])
%!test
%! [xWorld, yWorld] = intrinsicToWorld (imref2d, -5.3, -2.8);
%! assert (xWorld, -5.3)
%! assert (yWorld, -2.8)
%!test
%! [xW, yW] = intrinsicToWorld (imref2d, [1, 2; 3, 4], [2, 3; 5, 9]);
%! assert (xW, [1, 2; 3, 4])
%! assert (yW, [2, 3; 5, 9]) image-2.12.0/inst/@imref2d/PaxHeaders.25054/subsasgn.m 0000644 0000000 0000000 00000000062 13615546210 017027 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/subsasgn.m 0000644 0001750 0001750 00000005717 13615546210 021603 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{rout} =} subsasgn (@var{r}, @var{index}, @var{val})
##
## @seealso{}
## @end deftypefn
function rout = subsasgn (r, index, val)
switch (index.type)
case "."
fld = index.subs;
switch (fld)
case "ImageSize"
imageSize = val;
if (length (imageSize) < 2)
error ("Octave:invalid-input-arg", ...
"ImageSize must have at least two elements");
endif
validateattributes (imageSize, {"numeric"}, ...
{"positive", "integer", "vector"}, "imref2d", "imageSize");
m = imageSize(1);
n = imageSize(2);
rout = r;
rout.ImageSize = imageSize;
rout.PixelExtentInWorldX = r.ImageExtentInWorldX / n;
rout.PixelExtentInWorldY = r.ImageExtentInWorldY / m;
rout.XIntrinsicLimits = [0.5, n + 0.5];
rout.YIntrinsicLimits = [0.5, m + 0.5];
case "XWorldLimits"
xWorldLimits = val;
validateattributes (xWorldLimits, {"numeric"}, ...
{"increasing", "real", "vector", "size", [1, 2]}, ...
"imref2d", "xWorldLimits");
imageSize = r.ImageSize;
imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1);
rout = r;
rout.XWorldLimits = val;
rout.ImageExtentInWorldX = imageExtentInWorldX;
rout.PixelExtentInWorldX = imageExtentInWorldX / imageSize(2);
case "YWorldLimits"
yWorldLimits = val;
validateattributes (yWorldLimits, {"numeric"}, ...
{"increasing", "real", "vector", "size", [1, 2]}, ...
"imref2d", "yWorldLimits");
imageSize = r.ImageSize;
imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1);
rout = r;
rout.YWorldLimits = val;
rout.ImageExtentInWorldY = imageExtentInWorldY;
rout.PixelExtentInWorldY = imageExtentInWorldY / imageSize(1);
otherwise
error ("Octave:invalid-indexing", ...
"@imref2d/subsasgn: invalid property '%s'", fld);
endswitch
otherwise
error ("Octave:invalid-indexing", "@imref2d/subsasgn: invalid index type")
endswitch
endfunction image-2.12.0/inst/@imref2d/PaxHeaders.25054/subsref.m 0000644 0000000 0000000 00000000062 13615546210 016653 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/subsref.m 0000644 0001750 0001750 00000003733 13615546210 021423 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{r} =} subref (@var{val}, @var{idx})
##
## @seealso{}
## @end deftypefn
function r = subsref (val, idx)
if (strcmp (idx(1).type, "."))
switch (idx(1).subs)
case "ImageSize"
r = val.ImageSize;
case "XWorldLimits"
r = val.XWorldLimits;
case "YWorldLimits"
r = val.YWorldLimits;
case "PixelExtentInWorldX"
r = val.PixelExtentInWorldX;
case "PixelExtentInWorldY"
r = val.PixelExtentInWorldY;
case "ImageExtentInWorldX"
r = val.ImageExtentInWorldX;
case "ImageExtentInWorldY"
r = val.ImageExtentInWorldY;
case "XIntrinsicLimits"
r = val.XIntrinsicLimits;
case "YIntrinsicLimits"
r = val.YIntrinsicLimits;
otherwise
error ("Octave:invalid-indexing", ...
strcat ("unknown property '", idx(1).subs, "' for class imref2d"));
endswitch
if (length (idx) > 1)
switch (idx(2).type)
case "()"
i = idx(2).subs;
r = r(i{1});
otherwise
error ("Octave:invalid-indexing", ...
strcat ("can't index '", idx(1).subs, "' with ", idx(2).type));
endswitch
endif
endif
endfunction
image-2.12.0/inst/@imref2d/PaxHeaders.25054/disp.m 0000644 0000000 0000000 00000000062 13615546210 016141 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/disp.m 0000644 0001750 0001750 00000002740 13615546210 020706 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} disp (@var{r})
##
## @seealso{}
## @end deftypefn
function disp (r)
printf("%s with properties:\n", class (r));
printf("\n");
printf(" XWorldLimits: [%d %d]\n", r.XWorldLimits);
printf(" YWorldLimits: [%d %d]\n", r.YWorldLimits);
printf(" ImageSize: [%d %d]\n", r.ImageSize);
printf(" PixelExtentInWorldX: %d\n", r.PixelExtentInWorldX);
printf(" PixelExtentInWorldY: %d\n", r.PixelExtentInWorldY);
printf(" ImageExtentInWorldX: %d\n", r.ImageExtentInWorldX);
printf(" ImageExtentInWorldY: %d\n", r.ImageExtentInWorldY);
printf(" XIntrinsicLimits: [%d %d]\n", r.XIntrinsicLimits);
printf(" YIntrinsicLimits: [%d %d]\n", r.YIntrinsicLimits);
endfunction
image-2.12.0/inst/@imref2d/PaxHeaders.25054/sizesMatch.m 0000644 0000000 0000000 00000000062 13615546210 017314 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/@imref2d/sizesMatch.m 0000644 0001750 0001750 00000003667 13615546210 022072 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Martin Janda
##
## 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
## .
## -*- texinfo -*-
## @deftypefn {} {@var{TF} =} sizesMatch (@var{r}, @var{A})
## Determine if object and image are size-compatible.
##
## Outputs logical 1 (true) if the first two dimensions of an n-dimensional
## image @var{A} match the image size of the spatial referencing object @var{r},
## otherwise outputs zero (false).
##
## @seealso{imref2d, imref3d}
## @end deftypefn
function TF = sizesMatch (r, A)
if (nargin != 2)
print_usage();
endif
sizeA = size(A);
TF = all(sizeA(1:2) == r.ImageSize);
endfunction
%!error id=Octave:invalid-fun-call sizesMatch (imref2d)
## example from MATLAB documentation
%!test
%! I = zeros (256, 256);
%! r = imref2d ([256, 256]);
%! assert (sizesMatch (r, I), true)
%! I2 = zeros (246, 300);
%! assert (sizesMatch (r, I2), false)
## accepts empty image
%!test
%! r = imref2d ([256, 256]);
%! assert (sizesMatch (r, []), false)
## accepts 1-D image
%!test
%! r = imref2d ([256, 256]);
%! assert (sizesMatch (r, 42), false)
## accepts N-D image
%!test
%! r = imref2d ([256, 256]);
%! assert (sizesMatch (r, zeros (256, 256, 3, 2)), true)
%!test
%! I = zeros (384, 512, 3);
%! r = imref2d (size (I));
%! assert (sizesMatch (r, I), true) image-2.12.0/inst/PaxHeaders.25054/regionprops.m 0000644 0000000 0000000 00000000062 13615546210 016121 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/regionprops.m 0000644 0001750 0001750 00000211114 13615546210 020663 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2010 Søren Hauberg
## Copyright (C) 2012 Jordi Gutiérrez Hermoso
## Copyright (C) 2015, 2017 Hartmut Gimpel
## Copyright (C) 2015 Carnë Draug
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {} regionprops (@var{BW})
## @deftypefnx {Function File} {} regionprops (@var{L})
## @deftypefnx {Function File} {} regionprops (@var{CC})
## @deftypefnx {Function File} {} regionprops (@dots{}, @var{properties})
## @deftypefnx {Function File} {} regionprops (@dots{}, @var{I}, @var{properties})
## Compute properties of image regions.
##
## Measures several properties for each region within an image. Returns
## a struct array, one element per region, whose field names are the
## measured properties.
##
## Individual regions can be defined in three different ways, a binary
## image, a labelled image, or a bwconncomp struct, each providing
## different advantages.
##
## @table @asis
## @item @var{BW}
## A binary image. Must be of class logical. Individual regions will be
## the connected component as computed by @code{bwconnmp} using the
## maximal connectivity for the number of dimensions of @var{bw} (see
## @code{conndef} for details). For alternative connectivities, call
## @code{bwconncomp} directly and use its output instead.
##
## @var{bw} must really be of class logical. If not, even if it is a
## numeric array of 0's and 1's, it will be treated as a labelled image
## with a single discontinuous region. For example:
##
## @example
## ## Handled as binary image with 3 regions
## bw = logical ([
## 1 0 1 0 1
## 1 0 1 0 1
## ]);
##
## ## Handled as labelled image with 1 region
## bw = [
## 1 0 1 0 1
## 1 0 1 0 1
## ];
## @end example
##
## @item @var{L}
## A labelled image. Each region is the collection of all positive
## elements with the same value. This allows computing properties of
## regions that would otherwise be considered separate or connected.
## For example:
##
## @example
## ## Recognizes 4 regions
## l = [
## 1 2 3 4
## 1 2 3 4
## ];
##
## ## Recognizes 2 (discontinuous) regions
## l = [
## 1 2 1 2
## 1 2 1 2
## ];
## @end example
##
## @item @var{CC}
## A @code{bwconnmp()} structure. This is a struct with the following
## 4 fields: Connectivity, ImageSize, NumObjects, and PixelIdxList. See
## @code{bwconncomp} for details.
##
## @end table
##
## The properties to be measured can be defined via a cell array or a
## comma separated list or strings. Some of the properties are only
## supported if the matching grayscale image @var{I} is also supplied.
## Others are only supported for 2 dimensional images. See the list
## below for details on each property limitation. If none is specified,
## it defaults to the @qcode{"basic"} set of properties.
##
## @table @asis
## @item @qcode{"Area"}
## The number of pixels in the region. Note that this differs from
## @code{bwarea} where each pixel has different weights.
##
## @item @qcode{"BoundingBox"}
## The smallest rectangle that encloses the region. This is represented
## as a row vector such as
## @code{[x y z @dots{} x_length y_length z_length @dots{}]}.
##
## The first half corresponds to the lower coordinates of each dimension
## while the second half, to the length in that dimension. For the two
## dimensional case, the first 2 elements correspond to the coordinates
## of the upper left corner of the bounding box, while the two last entries
## are the width and the height of the box.
##
## @item @qcode{"Centroid"}
## The coordinates for the region centre of mass. This is a row vector
## with one element per dimension, such as @code{[x y z @dots{}]}.
##
## @item @qcode{"ConvexArea"}
## Number of pixels in the ConvexImage.
## Only supported for 2D images.
##
## @item @qcode{"ConvexHull"}
## The coordinates of the smallest convex polygon that fully encloses
## the region. Returns a m*2 matrix with each row containing the
## x- and y-coordinate of one corner point of the polygon.
## Only supported for 2D images. (see also: convhull)
##
## @item @qcode{"ConvexImage"}
## A binary image containing all pixels inside the convex hull. The
## size of this image is the bounding box. Only supported for
## 2D images.
## (see also: poly2mask)
##
## @item @qcode{"Eccentricity"}
## The eccentricity of the ellipse that has the same normalized
## second central moments as the region (value between 0 and 1).
##
## @item @qcode{"EquivDiameter"}
## The diameter of a circle with the same area as the object.
##
## @item @qcode{"EulerNumber"}
## The Euler number of the region using connectivity 8. Only supported
## for 2D images. See @code{bweuler} for details.
##
## @item @qcode{"Extent"}
## The area of the object divided by the area of the bounding box.
##
## @item @qcode{"Extrema"}
## Returns an 8-by-2 matrix with the extrema points of the object.
## The first column holds the returned x- and the second column the y-values.
## The order of the 8 points is: top-left, top-right, right-top, right-bottom,
## bottom-right, bottom-left, left-bottom, left-top.
##
## @item @qcode{"FilledArea"}
## The area of the object including possible holes.
##
## @item @qcode{"FilledImage"}
## A binary image with the same size as the object's bounding box that contains
## the object with all holes removed.
##
## @item @qcode{"Image"}
## An image with the same size as the bounding box that contains the original
## pixels.
##
## @item @qcode{"MajorAxisLength"}
## The length of the major axis of the ellipse that has the same
## normalized second central moments as the object.
##
## @item @qcode{"MaxIntensity"}
## The maximum intensity value inside each region.
## Requires a grayscale image @var{I}.
##
## @item @qcode{"MeanIntensity"}
## The mean intensity value inside each region.
## Requires a grayscale image @var{I}.
##
## @item @qcode{"MinIntensity"}
## The minimum intensity value inside each region.
## Requires a grayscale image @var{I}.
##
## @item @qcode{"MinorAxisLength"}
## The length of the minor axis of the ellipse that has the same
## normalized second central moments as the object.
##
## @item @qcode{"Orientation"}
## The angle between the x-axis and the major axis of the ellipse that
## has the same normalized second central moments as the object
## (value in degrees between -90 and 90).
##
## @item @qcode{"Perimeter"}
## The length of the boundary of the object.
##
## @item @qcode{"PixelIdxList"}
## The linear indices for the elements of each region in a column vector.
##
## @item @qcode{"PixelList"}
## The subscript indices for the elements of each region. This is a p-by-Q
## matrix where p is the number of elements and Q is the number of
## dimensions. Each row is of the form @code{[x y z @dots{}]}.
##
## @item @qcode{"PixelValues"}
## The actual pixel values inside each region in a column vector.
## Requires a grayscale image @var{I}.
##
## @item @qcode{"Solidity"}
## Ratio of Area / ConvexArea.
## Only supported for 2D images.
##
## @item @qcode{"SubarrayIdx"}
## A cell array with subscript indices for the bounding box. This can
## be used as @code{@var{I}(@var{props}(@var{p}).SubarrayIdx@{:@})}, where
## @var{p} is one of the regions, to extract the image in its bounding box.
##
## @item @qcode{"WeightedCentroid"}
## The coordinates for the region centre of mass when using the intensity
## of each element as weight. This is a row vector with one element per
## dimension, such as @code{[x y z @dots{}]}.
## Requires a grayscale image @var{I}.
##
## @end table
##
## In addition, the strings @qcode{"basic"} and @qcode{"all"} can be
## used to select a subset of the properties:
##
## @table @asis
## @item @qcode{"basic"} (default)
## Compute @qcode{"Area"}, @qcode{"Centroid"}, and @qcode{"BoundingBox"}.
##
## @item @qcode{"all"}
## Computes all possible properties for the image, i.e., it will not
## compute properties that require grayscale unless the grayscale image
## is available, and it will not compute properties that are limited to
## 2 dimensions, unless the image is 2 dimensions.
##
## @end table
##
## @seealso{bwlabel, bwperim, bweuler, convhull, poly2mask}
## @end deftypefn
function props = regionprops (bw, varargin)
if (nargin < 1)
print_usage ();
endif
if (isstruct (bw))
if (! isempty (setxor (fieldnames (bw), {"Connectivity", "ImageSize", ...
"NumObjects", "PixelIdxList"})))
error ("regionprops: CC is an invalid bwconnmp() struct");
endif
cc = bw;
elseif (islogical (bw))
cc = bwconncomp (bw);
elseif (isnumeric (bw))
if (isinteger (bw))
if (intmin (class (bw)) < 0 && any (bw(:) < 0))
error ("regionprops: L must be non-negative integers only");
endif
else
if (any (bw(:) < 0) || any (fix (bw(:)) != bw(:)))
error ("regionprops: L must be non-negative integers only");
endif
endif
n_obj = max (bw(:));
if (! n_obj)
## workaround for https://savannah.gnu.org/bugs/index.php?47287
cc = struct ("ImageSize", size (bw), "NumObjects", n_obj,
"PixelIdxList", {cell(1, 0)});
else
l_idx = find (bw);
cc = struct ("ImageSize", size (bw), "NumObjects", n_obj,
"PixelIdxList", {accumarray(bw(l_idx)(:), l_idx, [1 n_obj],
@(x) {x})});
endif
else
error ("regionprops: no valid BW, CC, or L input");
endif
is_2d = numel (cc.ImageSize) == 2;
next_idx = 1;
has_gray = false;
if (numel (varargin) && isnumeric (varargin{1}))
next_idx++;
has_gray = true;
img = varargin{1};
sz = size (img);
if (! size_equal (sz, cc.ImageSize) || any (sz != cc.ImageSize))
error ("regionprops: BW and I sizes must be equal");
endif
endif
if (numel (varargin) >= next_idx)
if (iscell (varargin{next_idx}))
properties = varargin{next_idx++};
if (numel (varargin) >= next_idx)
print_usage ();
endif
else
properties = varargin(next_idx++:end);
endif
if (! iscellstr (properties))
error ("regionprops: PROPERTIES must be a string or a cell array of strings");
endif
properties = tolower (strrep (properties, "_", ""));
else
properties = {"basic"};
endif
properties = select_properties (properties, is_2d, has_gray);
## Some properties require the value of others. In addition, most
## properties have common code. Ideally, to avoid repeating
## computations, we would make use not only of the already measured
## properties. but also of their intermediary steps. We handle this
## with a stack of properties that need to be measured and we push
## dependencies into it as we find them. A scalar struct keeps all
## values whose fields are the properties and intermediary steps names.
##
## Note that we do not want to fill the return value just yet. The
## reason is that props is a struct array. Since the computation of
## the properties is vectorized, it would require a constant back and
## forth conversion between cell arrays and numeric arrays. So we
## keep everything in a numeric array and everything is much faster.
## At the end, we put everything in place in a struct array.
dependencies = struct (
"area", {{}},
"accum_subs", {{"area"}}, # private
"accum_subs_nd", {{"accum_subs"}}, # private
"boundingbox", {{"pixellist", "accum_subs_nd"}},
"centroid", {{"accum_subs_nd", "pixellist", "area"}},
"filledarea", {{"filledimage"}},
"filledimage", {{"image"}},
"image", {{"subarrayidx", "accum_subs", "pixelidxlist"}},
"pixelidxlist", {{}},
"pixellist", {{"pixelidxlist"}},
"subarrayidx", {{"boundingbox"}},
"convexarea", {{"conveximage"}},
"convexhull", {{"boundingbox", "image"}},
"conveximage", {{"boundingbox", "convexhull"}},
"eccentricity", {{"minoraxislength", "majoraxislength"}},
"equivdiameter", {{"area"}},
"eulernumber", {{"image"}},
"extent", {{"area", "boundingbox"}},
"extrema", {{"area", "accum_subs_nd", "pixellist"}},
"local_ellipse", {{"area", "pixellist"}}, # private
"majoraxislength", {{"local_ellipse"}},
"minoraxislength", {{"local_ellipse"}},
"orientation", {{"local_ellipse"}},
"perimeter", {{}},
"perimeterold", {{}},
"solidity", {{"area", "convexarea"}},
"maxintensity", {{"accum_subs", "pixelidxlist"}},
"meanintensity", {{"total_intensity", "area"}},
"minintensity", {{"accum_subs", "pixelidxlist"}},
"pixelvalues", {{"pixelidxlist"}},
"total_intensity", {{"accum_subs", "pixelidxlist"}},
"weightedcentroid", {{"accum_subs_nd", "total_intensity", "pixellist", "pixelidxlist", "area"}}
);
to_measure = properties;
values = struct ();
## There's too many indirectly dependent on "area", and even if not
## required, it will be required later to create the struct array.
values.area = rp_area (cc);
while (! isempty (to_measure))
pname = to_measure{end};
## Already computed. Pop it and move on.
if (isfield (values, pname))
to_measure(end) = [];
continue
endif
## There's missing dependencies. Push them and start again.
deps = dependencies.(pname);
missing = deps(! isfield (values, deps));
if (! isempty (missing))
to_measure(end+1:end+numel(missing)) = missing;
continue
endif
to_measure(end) = [];
switch (pname)
case "area"
values.area = rp_area (cc);
case "accum_subs"
values.accum_subs = rp_accum_subs (cc, values.area);
case "accum_subs_nd"
values.accum_subs_nd = rp_accum_subs_nd (cc, values.accum_subs);
case "boundingbox"
values.boundingbox = rp_bounding_box (cc, values.pixellist,
values.accum_subs_nd);
case "centroid"
values.centroid = rp_centroid (cc, values.pixellist, values.area,
values.accum_subs_nd);
case "filledarea"
values.filledarea = rp_filled_area (values.filledimage);
case "filledimage"
values.filledimage = rp_filled_image (values.image);
case "image"
values.image = rp_image (cc, bw, values.pixelidxlist,
values.accum_subs, values.subarrayidx);
case "pixelidxlist"
values.pixelidxlist = rp_pixel_idx_list (cc);
case "pixellist"
values.pixellist = rp_pixel_list (cc, values.pixelidxlist);
case "subarrayidx"
values.subarrayidx = rp_subarray_idx (cc, values.boundingbox);
case "convexarea"
values.convexarea = rp_convex_area (values.conveximage);
case "convexhull"
values.convexhull = rp_convex_hull (values.boundingbox,
values.image);
case "conveximage"
values.conveximage = rp_convex_image (values.boundingbox,
values.convexhull);
case "eccentricity"
values.eccentricity = rep_eccentricity (values.minoraxislength,
values.majoraxislength);
case "equivdiameter"
values.equivdiameter = rp_equivdiameter (values.area);
case "eulernumber"
values.eulernumber = rp_euler_number (values.image);
case "extent"
values.extent = rp_extent (values.area, values.boundingbox);
case "extrema"
values.extrema = rp_extrema (cc, values.pixellist, values.area,
values.accum_subs_nd);
case "local_ellipse"
values.local_ellipse = true;
[values.minoraxislength, values.majoraxislength, ...
values.orientation] = rp_local_ellipse (values.area, values.pixellist);
case {"majoraxislength", "minoraxislength", "orientation"}
## Do nothing. These are "virtual" targets which are computed
## in local_ellipse.
case "perimeter"
values.perimeter = rp_perimeter (cc);
case "perimeterold"
values.perimeterold = rp_perimeter_old (cc);
case "solidity"
values.solidity = rp_solidity (values.area, values.convexarea);
case "maxintensity"
values.maxintensity = rp_max_intensity (cc, img,
values.pixelidxlist,
values.accum_subs);
case "meanintensity"
values.meanintensity = rp_mean_intensity (cc, values.total_intensity,
values.area);
case "minintensity"
values.minintensity = rp_min_intensity (cc, img,
values.pixelidxlist,
values.accum_subs);
case "pixelvalues"
values.pixelvalues = rp_pixel_values (cc, img, values.pixelidxlist);
case "total_intensity"
values.total_intensity = rp_total_intensity (cc, img,
values.pixelidxlist,
values.accum_subs);
case "weightedcentroid"
values.weightedcentroid = rp_weighted_centroid (cc, img,
values.pixellist,
values.pixelidxlist,
values.total_intensity,
values.accum_subs_nd,
values.area);
otherwise
error ("regionprops: unknown property `%s'", pname);
endswitch
endwhile
## After we have made all the measurements, we need to pack everything
## into struct arrays.
Area = values.area;
props = repmat (struct (), cc.NumObjects, 1);
for ip = 1:numel (properties)
switch (properties{ip})
case "area"
[props.Area] = num2cell (Area){:};
case "boundingbox"
[props.BoundingBox] = mat2cell (values.boundingbox,
ones (cc.NumObjects, 1)){:};
case "centroid"
[props.Centroid] = mat2cell (values.centroid,
ones (cc.NumObjects, 1)){:};
case "filledarea"
[props.FilledArea] = num2cell (values.filledarea){:};
case "filledimage"
[props.FilledImage] = values.filledimage{:};
case "image"
[props.Image] = values.image{:};
case "pixelidxlist"
[props.PixelIdxList] = mat2cell (values.pixelidxlist, Area){:};
case "pixellist"
[props.PixelList] = mat2cell (values.pixellist, Area){:};
case "subarrayidx"
[props.SubarrayIdx] = values.subarrayidx{:};
case "convexarea"
[props.ConvexArea] = num2cell (values.convexarea){:};
case "convexhull"
[props.ConvexHull] = values.convexhull{:};
case "conveximage"
[props.ConvexImage] = values.conveximage{:};
case "eccentricity"
[props.Eccentricity] = num2cell (values.eccentricity){:};
case "equivdiameter"
[props.EquivDiameter] = num2cell (values.equivdiameter){:};
case "eulernumber"
[props.EulerNumber] = num2cell (values.eulernumber){:};
case "extent"
[props.Extent] = num2cell (values.extent){:};
case "extrema"
[props.Extrema] = mat2cell (values.extrema,
repmat (8, 1, cc.NumObjects)){:};
case "majoraxislength"
[props.MajorAxisLength] = num2cell (values.majoraxislength){:};
case "minoraxislength"
[props.MinorAxisLength] = num2cell (values.minoraxislength){:};
case "orientation"
[props.Orientation] = num2cell (values.orientation){:};
case "perimeter"
[props.Perimeter] = num2cell (values.perimeter){:};
case "perimeterold"
[props.PerimeterOld] = num2cell (values.perimeterold){:};
case "solidity"
[props.Solidity] = num2cell (values.solidity){:};
case "maxintensity"
[props.MaxIntensity] = num2cell (values.maxintensity){:};
case "meanintensity"
[props.MeanIntensity] = num2cell (values.meanintensity){:};
case "minintensity"
[props.MinIntensity] = num2cell (values.minintensity){:};
case "pixelvalues"
[props.PixelValues] = mat2cell (values.pixelvalues, Area){:};
case "weightedcentroid"
[props.WeightedCentroid] = mat2cell (values.weightedcentroid,
ones (cc.NumObjects, 1)){:};
otherwise
error ("regionprops: unknown property `%s'", pname);
endswitch
endfor
endfunction
function props = select_properties (props, is_2d, has_gray)
persistent props_basic = {
"area",
"boundingbox",
"centroid",
};
persistent props_2d = {
"convexarea",
"convexhull",
"conveximage",
"eccentricity",
"equivdiameter",
"extrema",
"majoraxislength",
"minoraxislength",
"orientation",
"perimeter",
"solidity",
};
persistent props_gray = {
"maxintensity",
"meanintensity",
"minintensity",
"pixelvalues",
"weightedcentroid",
};
persistent props_others = {
"eulernumber",
"extent", # Matlab limits Extent to 2D. Octave does not.
"filledarea",
"filledimage",
"image",
"pixelidxlist",
"pixellist",
"subarrayidx",
};
props = props(:);
p_basic = strcmp ("basic", props);
p_all = strcmp ("all", props);
props(p_basic | p_all) = [];
if (any (p_all))
props = vertcat (props, props_basic, props_others);
if (is_2d)
props = vertcat (props, props_2d);
endif
if (has_gray)
props = vertcat (props, props_gray);
endif
elseif (any (p_basic))
props = vertcat (props, props_basic);
endif
if (! is_2d)
non_2d = ismember (props, props_2d);
if (any (non_2d))
warning ("regionprops: ignoring %s properties for non 2 dimensional image",
strjoin (props(non_2d), ", "));
props(non_2d) = [];
endif
endif
if (! has_gray)
non_val = ismember (props, props_gray);
if (any (non_val))
warning ("regionprops: ignoring %s properties due to missing grayscale image",
strjoin (props(non_val), ", "));
props(non_val) = [];
endif
endif
endfunction
function area = rp_area (cc)
area = cellfun (@numel, cc.PixelIdxList(:));
endfunction
function centroid = rp_centroid (cc, pixel_list, area, subs_nd)
nd = numel (cc.ImageSize);
no = cc.NumObjects;
weighted_sub = pixel_list ./ vec (repelems (area, [1:no; vec(area, 2)]));
centroid = accumarray (subs_nd, weighted_sub(:), [no nd]);
endfunction
function bounding_box = rp_bounding_box (cc, pixel_list, subs_nd)
nd = numel (cc.ImageSize);
no = cc.NumObjects;
init_corner = accumarray (subs_nd, pixel_list(:), [no nd], @min) - 0.5;
end_corner = accumarray (subs_nd, pixel_list(:), [no nd], @max) + 0.5;
bounding_box = [(init_corner) (end_corner - init_corner)];
endfunction
function eccentricity = rep_eccentricity (minoraxislength, majoraxislength)
eccentricity = sqrt (1 - (minoraxislength ./ majoraxislength).^2);
endfunction
function equivdiameter = rp_equivdiameter (area)
equivdiameter = sqrt (4 * area / pi);
endfunction
function euler = rp_euler_number (bb_images)
## TODO there should be a way to vectorize this, right?
euler = cellfun (@bweuler, bb_images);
endfunction
function extent = rp_extent (area, bounding_box)
bb_area = prod (bounding_box(:,(end/2)+1:end), 2);
extent = area ./ bb_area;
endfunction
function extrema = rp_extrema (cc, pixel_list, area, subs_nd)
## Note that this property is limited to 2d regions
no = cc.NumObjects;
## Algorithm:
## 1. Find the max and min values for row and column values on
## each object. That is, max and min of each column in
## pixel_list, for each object.
##
## 2. Get a mask for pixel_list, for those rows and columns indices.
##
## 3. Use that mask on the other dimension to find the max and min
## values for each object.
##
## 4. Assign those values to a (8*no)x2 array.
##
## This gets a bit convoluted because we do the two dimensions and
## all objects at the same time.
## In the following, "head" and "base" are the top and bottom index for
## each dimension. We use the words "head" and "base" to avoid confusion
## with the rest where top and bottom only refer to the row dimension.
## So "head" has the lowest index values (rows for top left/right, and
## columns for left top/bottom), while "base" has the highest index
## values (rows for bottom left/right, and columns for right top/bottom).
## 1. Find the max and min values for row and column values on
## each object. That is, max and min of each column in
## pixel_list, for each object.
head = accumarray (subs_nd, pixel_list(:), [no 2], @min);
base = accumarray (subs_nd, pixel_list(:), [no 2], @max);
## 2. Get a mask for pixel_list, for those rows and columns indices.
##
## 3. Use that mask on the other dimension to find the max and min
## values for each object.
##
## head_head and head_base, have the lowest index (head) and the
## highest index (base) values, for the "head" indices.
## Same logic for base_head and base_base.
px_l_sz = size (pixel_list);
rep_extrema = @(x) reshape (repelems (x, [1:(no*2); area(:)' area(:)']),
px_l_sz);
head_mask = (pixel_list == rep_extrema (head))(:, [2 1]);
head_head = accumarray (subs_nd(head_mask), pixel_list(head_mask), [no 2], @min);
head_base = accumarray (subs_nd(head_mask), pixel_list(head_mask), [no 2], @max);
base_mask = (pixel_list == rep_extrema (base))(:, [2 1]);
base_head = accumarray (subs_nd(base_mask), pixel_list(base_mask), [no 2], @min);
base_base = accumarray (subs_nd(base_mask), pixel_list(base_mask), [no 2], @max);
## Adjust from idx integer to pixel border coordinates
head -= 0.5;
head_head -= 0.5;
head_base += 0.5;
base += 0.5;
base_head -= 0.5;
base_base += 0.5;
## 4. Assign those values to a (8*no)x2 array.
nr = 8 * no;
extrema = zeros (nr, 2);
extrema(1:8:nr, 2) = head(:,2); # y values for top left
extrema(2:8:nr, 2) = head(:,2); # y values for top right
extrema(7:8:nr, 1) = head(:,1); # x values for left bottom
extrema(8:8:nr, 1) = head(:,1); # x values for left top
extrema(5:8:nr, 2) = base(:,2); # y values for bottom right
extrema(6:8:nr, 2) = base(:,2); # y values for bottom left
extrema(3:8:nr, 1) = base(:,1); # x values for right top
extrema(4:8:nr, 1) = base(:,1); # x values for right bottom
extrema(1:8:nr, 1) = head_head(:,1); # x value for top left
extrema(8:8:nr, 2) = head_head(:,2); # y value for left top
extrema(2:8:nr, 1) = head_base(:,1); # x value for top right
extrema(7:8:nr, 2) = head_base(:,2); # y value for left bottom
extrema(6:8:nr, 1) = base_head(:,1); # x value for bottom left
extrema(3:8:nr, 2) = base_head(:,2); # y value for right top
extrema(5:8:nr, 1) = base_base(:,1); # x value for bottom right
extrema(4:8:nr, 2) = base_base(:,2); # y value for right bottom
endfunction
function filled_area = rp_filled_area (bb_filled_images)
filled_area = cellfun ('nnz', bb_filled_images);
endfunction
function bb_filled_images = rp_filled_image (bb_images)
## Beware if attempting to vectorize this. The bounding boxes of
## different regions may overlap, and a "hole" may be a hole for
## several regions (e.g., concentric circles). There should be tests
## this weird cases.
bb_filled_images = cellfun (@(x) imfill (x, "holes"), bb_images,
"UniformOutput", false);
endfunction
function bb_images = rp_image (cc, bw, idx, subs, subarray_idx)
## For this property, we must remember to remove elements from other
## regions (remember that bounding boxes may overlap). We do that by
## creating a labelled image, extracting the bounding boxes, and then
## comparing elements.
no = cc.NumObjects;
## If BW is numeric then it already is a labeled image.
if (isnumeric (bw))
L = bw;
else
if (no < 255)
cls = "uint8";
elseif (no < 65535)
cls = "uint16";
elseif (no < 4294967295)
cls = "uint32";
else
cls = "double";
endif
L = zeros (cc.ImageSize, cls);
L(idx) = subs;
endif
sub_structs = num2cell (struct ("type", "()", "subs", subarray_idx));
bb_images = cellfun (@subsref, {L}, sub_structs, "UniformOutput", false);
bb_images = cellfun (@eq, bb_images, num2cell (1:no)(:),
"UniformOutput", false);
endfunction
function perim = rp_perimeter (cc)
## FIXME: this should be vectorized. We were previously using
## bwboundaries (incorrectly, see bug #52926) but
## bwboundaries is doing a similar loop internally.
## regular CHAIN_CODE = [3, 2, 1, 4, -1, 0, 5, 6, 7];
CHAIN_CODE = [1, 2, 1, 4, 1, 0, 1, 6, 1];
no = cc.NumObjects;
boundaries = cell (no, 1);
bw = false (cc.ImageSize);
perim = zeros(no, 1);
for k = 1:no
idx = cc.PixelIdxList{k};
bw(idx) = true;
boundaries{k} = __boundary__ (bw, 8);
if (k != no)
bw(idx) = false;
endif
np = size (boundaries{k}, 1);
if (np == 2)
## single pixel - perimeter is 0
perim(k) = 0;
else
## calculating perimeter according to Vossepoel and Smeulders,
## Computer Graphics and Image Processing 20(4):347-364, 1982.
## see: Cris Luengo, "Measuring boundary length"
## http://www.crisluengo.net/index.php/archives/310
## Corners are counted using Hartmut Gimpel observations in
## http://savannah.gnu.org/bugs/?52933#comment12:
## 1. corners of 90 deg counted, but only when they are aligned with the coordinate system.
## 2. corners of 135 deg are counted
## 3. corners of 45 deg are counted
## 4. NO other types of corners are counted, especially not "spikes" of 360 deg (aligned with axes or not),
## and 90 deg cornes that are tilted (by 45 deg) with regard to the axes.
## regular CHAIN_CODE is = [3, 2, 1, 4, -1, 0, 5, 6, 7];
## we use: CHAIN_CODE = [1, 2, 1, 4, 1, 0, 1, 6, 1];
## we preserve even value, while changing odd values to 1
## corners of 90 deg which aligned with the coordinate system are change of 2 or 4
## in the chain code, but only when the two values are even,
## and that's the reason for the modified chain code.
## corners of 45 deg and 135 deg are changes from even to odd or from odd to even in the chain code
# boundary of component k
boundary = boundaries {k};
# distance between consecutive pixels in the boundary
dists = boundary (2:end,:) - boundary (1:end-1,:) + 2;
# converting x_y distances to vector
dists_vec = dists(:,2) + (dists(:,1)-1)*3;
# converting distances to chain code
chain_code = CHAIN_CODE (dists_vec);
# odd numbers in the chain code - diagonal movement
odd = sum(mod (chain_code, 2));
# even entries in the chain code - vetical or horizontal movement
even = np - 1 - odd;
# counting corners
chain_code_change = abs(chain_code - [chain_code(end), chain_code(1:end-1)]);
# 45 deg & 145 deg corners are places where chain code changes from
# odd to even or from even to odd
corners = numel (find (mod(chain_code_change, 2) == 1));
# 90 deg corners aligned with axes are places where both codes are even,
# and there is a jump of 2 or 6 in the chain code
corners += numel (find (chain_code_change == 2));
corners += numel (find (chain_code_change == 6));
# using Vossepoel and Smeulders formula
perim(k) = even*0.980 + odd*1.406 - corners*0.091;
endif
endfor
endfunction
function perim = rp_perimeter_old (cc)
## FIXME: this should be vectorized. We were previously using
## bwboundaries (incorrectly, see bug #52926) but
## bwboundaries is doing a similar loop internally.
no = cc.NumObjects;
boundaries = cell (no, 1);
bw = false (cc.ImageSize);
perim = zeros(no, 1);
for k = 1:no
idx = cc.PixelIdxList{k};
bw(idx) = true;
boundaries{k} = __boundary__ (bw, 8);
if (k != no)
bw(idx) = false;
endif
endfor
npx = cellfun ("size", boundaries, 1);
dists = diff (cell2mat (boundaries));
dists(cumsum (npx)(1:end-1),:) = [];
dists = sqrt (sumsq (dists, 2));
subs = repelems (1:no, [1:no; (npx-1)(:)']);
perim = accumarray (subs(:), dists(:), [no 1]);
endfunction
function idx = rp_pixel_idx_list (cc)
idx = cell2mat (cc.PixelIdxList(:));
endfunction
function pixel_list = rp_pixel_list (cc, idx)
nd = numel (cc.ImageSize);
pixel_list = cell2mat (nthargout (1:nd, @ind2sub, cc.ImageSize, idx));
## If idx is empty, pixel_list will have size (0x0) so we need to expand
## it to (0xnd). Unfortunately, in2sub() returns (0x0) and not (0x1)
pixel_list = postpad (pixel_list, nd, 0, 2);
pixel_list(:,[1 2]) = pixel_list(:,[2 1]);
endfunction
function pixel_values = rp_pixel_values (cc, img, idx)
pixel_values = img(idx);
endfunction
function max_intensity = rp_max_intensity (cc, img, idx, subs)
max_intensity = accumarray (subs, img(idx), [cc.NumObjects 1], @max);
endfunction
function mean_intensity = rp_mean_intensity (cc, totals, area)
mean_intensity = totals ./ area;
endfunction
function min_intensity = rp_min_intensity (cc, img, idx, subs)
min_intensity = accumarray (subs, img(idx), [cc.NumObjects 1], @min);
endfunction
function subarray_idx = rp_subarray_idx (cc, bounding_box)
nd = columns (bounding_box) / 2;
bb_limits = bounding_box;
## Swap x y coordinates back to row and column
bb_limits(:,[1 2 [1 2]+nd]) = bounding_box(:,[2 1 [2 1]+nd]);
## Set initial coordinates (it is faster to add 0.5 than to call ceil())
bb_limits(:,1:nd) += 0.5;
## Set the end coordinates
bb_limits(:,(nd+1):end) += bb_limits(:,1:nd);
bb_limits(:,(nd+1):end) -= 1;
subarray_idx = arrayfun (@colon, bb_limits(:,1:nd), bb_limits(:,(nd+1):end),
"UniformOutput", false);
subarray_idx = mat2cell (subarray_idx, ones (cc.NumObjects, 1));
endfunction
function weighted_centroid = rp_weighted_centroid (cc, img, pixel_list,
pixel_idx_list, totals,
subs_nd, area)
no = cc.NumObjects;
nd = numel (cc.ImageSize);
rep_totals = vec (repelems (totals, [1:no; vec(area, 2)]));
## Note that we need 1 column, even if pixel_idx_list is [], hence (:)
## so that we get (0x1) instead of (0x0)
vals = img(pixel_idx_list)(:);
weighted_pixel_list = pixel_list .* (double (vals) ./ rep_totals);
weighted_centroid = accumarray (subs_nd, weighted_pixel_list(:), [no nd]);
endfunction
function convexhull = rp_convex_hull (boundingbox, image)
convexhull = {};
for idx = 1:numel (image)
## 1. calculate perimeter image
perim_image = bwperim (image{idx}, 8);
if numel (image{idx}) == 1 # work around bug 50153
perim_image = [true];
endif
## 2. calculate global indices of perimeter pixels
[r, c] = find (perim_image);
r0 = boundingbox(idx, 2) - 0.5;
c0 = boundingbox(idx, 1) - 0.5;
R = r(:) + r0;
C = c(:) + c0;
RR = [R-0.5; R; R+0.5; R]; # use 4 corners of each pixel
CC = [C; C+0.5; C; C-0.5];
## 3. calculate convex hull around those perimeter pixels
if (isempty (RR))
convexhull{idx} = zeros (0,2);
else
hull_idx = convhull (RR, CC); # Matlab also gives points along the lines, we don't do that.
# (This is also closer to the definition of a 'convex hull'.)
convexhull{idx} = [CC, RR](hull_idx, :);
endif
endfor
endfunction
function conveximage = rp_convex_image (boundingbox, convexhull)
conveximage = {};
for idx = 1:numel (convexhull)
## to work around a Matlab incompatible poly2mask
## (bug #50188, currently does rounding of vertex
## coodinates to nearest integer)
## we do the following (see Matlab help for poly2mask):
## * subdivide each pixel into 5*5 pieces
## * make the pixel part of the convex image if
## more than half of its pieces are inside the hull
## (This uses 25 times more memory as the region itself.
## There might be a more memory saving way to do this.)
M = boundingbox(idx, 4) * 5;
N = boundingbox(idx, 3)* 5;
hull = convexhull{idx} * 5;
if (isempty (hull))
conveximage{idx} = false (M/5,N/5);
else
y0 = boundingbox(idx, 2) * 5;
x0 = boundingbox(idx, 1) * 5;
Y = hull(:,2) - y0 + 0;
X = hull(:,1) - x0 + 0;
X = round (X); # reason: independence of bug #50188
Y = round (Y);
cimage5 = poly2mask (X,Y,M,N);
collect = zeros (5, 5, M/5, N/5);
for m = 1:5
for n = 1:5
collect(m, n, :, :) = cimage5(m:5:end, n:5:end);
endfor
endfor
summed = sum (sum (collect,1), 2);
conveximage{idx} = reshape (summed, M/5, N/5) > 25*0.5;
endif
endfor
endfunction
function convexarea = rp_convex_area (conveximage)
n = numel (conveximage);
convexarea = zeros (n);
for idx = 1:n
convexarea(idx) = sum (conveximage{idx}(:));
endfor;
endfunction
function solidity = rp_solidity (area, convexarea)
solidity = area ./ convexarea;
solidity(area == 0) = NaN;
endfunction
##
## Intermediary steps -- no match to specific property
##
## Creates subscripts for use with accumarray, when computing a column vector.
function subs = rp_accum_subs (cc, area)
rn = 1:cc.NumObjects;
R = [rn; vec(area, 2)];
subs = vec (repelems (rn, R));
endfunction
## Creates subscripts for use with accumarray, when computing something
## with a column per number of dimensions
function subs_nd = rp_accum_subs_nd (cc, subs)
nd = numel (cc.ImageSize);
no = cc.NumObjects;
## FIXME workaround bug #47085
subs_nd = vec (bsxfun (@plus, subs, [0:no:(no*nd-1)]));
endfunction
## Total/Integrated density of each region.
function totals = rp_total_intensity (cc, img, idx, subs)
totals = accumarray (subs, img(idx), [cc.NumObjects 1]);
endfunction
function [minor, major, orientation] = rp_local_ellipse (area, pixellist)
## FIXME: this should be vectorized. See R.M. Haralick and Linda G.
## Shapiro, "Computer and Robot Vision: Volume 1", Appendix A
no = numel (area);
minor = zeros (no, 1);
major = minor;
orientation = minor;
c_idx = 1;
for idx = 1:no
sel = c_idx:(c_idx + area(idx) -1);
c_idx += area(idx);
X = pixellist(sel, 2);
Y = pixellist(sel, 1);
## calculate (centralised) second moment of region with pixels [X, Y]
## This is equivalent to "cov ([X(:) Y(:)], 1)" but will work as
## expected even if X and Y have only one row each.
C = center ([X(:) Y(:)], 1);
C = C' * C / (rows (C));
C = C + 1/12 .* eye (rows (C)); # centralised second moment of 1 pixel is 1/12
[V, lambda] = eig (C);
lambda_d = 4 .* sqrt (diag (lambda));
minor(idx) = min (lambda_d);
[major(idx), major_idx] = max (lambda_d);
major_vec = V(:, major_idx);
if (major(idx) == minor(idx))
orientation(idx) = 0;
elseif (major_vec(2) == 0)
orientation(idx) = 90;
else
orientation(idx) = -(180/pi) .* atan (major_vec(1) ./ major_vec(2));
endif
endfor
endfunction
%!shared bw2d, gray2d, bw2d_over_bb, bw2d_insides
%! bw2d = logical ([
%! 0 1 0 1 1 0
%! 0 1 1 0 1 1
%! 0 1 0 0 0 0
%! 0 0 0 1 1 1
%! 0 0 1 1 0 1]);
%!
%! gray2d = [
%! 2 4 0 7 5 2
%! 3 0 4 9 3 7
%! 0 5 3 4 8 1
%! 9 2 0 5 8 6
%! 8 9 7 2 2 5];
%!
%! ## For testing overlapping bounding boxes
%! bw2d_over_bb = logical ([
%! 0 1 1 1 0 1 1
%! 1 1 0 0 0 0 1
%! 1 0 0 1 1 0 1
%! 1 0 0 1 1 0 0
%! 0 0 0 1 1 1 1]);
%!
%! ## For testing when there's regions inside regions
%! bw2d_insides = logical ([
%! 0 0 0 0 0 0 0 0
%! 0 1 1 1 1 1 1 0
%! 0 1 0 0 0 0 1 0
%! 0 1 0 1 1 0 1 0
%! 0 1 0 1 1 0 1 0
%! 0 1 0 0 0 0 1 0
%! 0 1 1 1 1 1 1 0
%! 0 0 0 0 0 0 0 0]);
%!function c = get_2d_centroid_for (idx)
%! subs = ind2sub ([5 6], idx);
%! m = false ([5 6]);
%! m(idx) = true;
%! y = sum ((1:5)' .* sum (m, 2) /sum (m(:)));
%! x = sum ((1:6) .* sum (m, 1) /sum (m(:)));
%! c = [x y];
%!endfunction
%!assert (regionprops (bw2d, "Area"), struct ("Area", {8; 6}))
%!assert (regionprops (double (bw2d), "Area"), struct ("Area", {14}))
%!assert (regionprops (bwlabel (bw2d, 4), "Area"), struct ("Area", {4; 6; 4}))
## These are different from Matlab because the indices in PixelIdxList
## do not appear sorted. This is because we get them from bwconncomp()
## which does not sort them (it seems bwconncomp in Matlab returns them
## sorted but that's undocumented, just like the order here is undocumented)
%!assert (regionprops (bw2d, "PixelIdxList"),
%! struct ("PixelIdxList", {[6; 7; 12; 8; 16; 21; 22; 27]
%! [15; 19; 20; 24; 29; 30]}))
%!assert (regionprops (bwlabel (bw2d, 4), "PixelIdxList"),
%! struct ("PixelIdxList", {[6; 7; 8; 12]
%! [15; 19; 20; 24; 29; 30]
%! [16; 21; 22; 27]}))
%!assert (regionprops (bw2d, "PixelList"),
%! struct ("PixelList", {[2 1; 2 2; 3 2; 2 3; 4 1; 5 1; 5 2; 6 2]
%! [3 5; 4 4; 4 5; 5 4; 6 4; 6 5]}))
%!assert (regionprops (bwlabel (bw2d, 4), "PixelList"),
%! struct ("PixelList", {[2 1; 2 2; 2 3; 3 2]
%! [3 5; 4 4; 4 5; 5 4; 6 4; 6 5]
%! [4 1; 5 1; 5 2; 6 2]}))
## Also different from Matlab because we do not sort the values by index
%!assert (regionprops (bw2d, gray2d, "PixelValues"),
%! struct ("PixelValues", {[4; 0; 4; 5; 7; 5; 3; 7]
%! [7; 5; 2; 8; 6; 5]}))
%!assert (regionprops (bw2d, gray2d, "MaxIntensity"),
%! struct ("MaxIntensity", {7; 8}))
%!assert (regionprops (bw2d, gray2d, "MinIntensity"),
%! struct ("MinIntensity", {0; 2}))
%!assert (regionprops (bw2d, "BoundingBox"),
%! struct ("BoundingBox", {[1.5 0.5 5 3]; [2.5 3.5 4 2]}))
%!assert (regionprops (bw2d, "Centroid"),
%! struct ("Centroid", {get_2d_centroid_for([6 7 8 12 16 21 22 27])
%! get_2d_centroid_for([15 19 20 24 29 30])}),
%! 5 * eps)
%!test
%! props = struct ("Area", {8; 6},
%! "Centroid", {get_2d_centroid_for([6 7 8 12 16 21 22 27])
%! get_2d_centroid_for([15 19 20 24 29 30])},
%! "BoundingBox", {[1.5 0.5 5 3]; [2.5 3.5 4 2]});
%! assert (regionprops (bw2d, "basic"), props, 5 * eps)
%! assert (regionprops (bwconncomp (bw2d, 8), "basic"), props, 5 * eps)
%! assert (regionprops (bwlabeln (bw2d, 8), "basic"), props, 5 * eps)
%!test
%! props = struct ("Area", {4; 6; 4},
%! "Centroid", {get_2d_centroid_for([6 7 8 12])
%! get_2d_centroid_for([15 19 20 24 29 30])
%! get_2d_centroid_for([16 21 22 27])},
%! "BoundingBox", {[1.5 0.5 2 3]; [2.5 3.5 4 2]; [3.5 0.5 3 2]});
%! assert (regionprops (bwconncomp (bw2d, 4), "basic"), props, 5 * eps)
%! assert (regionprops (bwlabeln (bw2d, 4), "basic"), props, 5 * eps)
## This it is treated as labeled image with a single discontiguous region.
%!assert (regionprops (double (bw2d), "basic"),
%! struct ("Area", 14,
%! "Centroid", get_2d_centroid_for (find (bw2d)),
%! "BoundingBox", [1.5 0.5 5 5]), eps*1000)
%!assert (regionprops ([0 0 1], "Centroid").Centroid, [3 1])
%!assert (regionprops ([0 0 1; 0 0 0], "Centroid").Centroid, [3 1])
## bug #39701
%!assert (regionprops ([0 1 1], "Centroid").Centroid, [2.5 1])
%!assert (regionprops ([0 1 1; 0 0 0], "Centroid").Centroid, [2.5 1])
%!test
%! a = zeros (2, 3, 3);
%! a(:, :, 1) = [0 1 0; 0 0 0];
%! a(:, :, 3) = a(:, :, 1);
%! c = regionprops (a, "centroid");
%! assert (c.Centroid, [2 1 2])
%!test
%! d1=2; d2=4; d3=6;
%! a = ones (d1, d2, d3);
%! c = regionprops (a, "centroid");
%! assert (c.Centroid, [mean(1:d2), mean(1:d1), mean(1:d3)], eps*1000)
%!test
%! a = [0 0 2 2; 3 3 0 0; 0 1 0 1];
%! c = regionprops (a, "centroid");
%! assert (c(1).Centroid, [3 3])
%! assert (c(2).Centroid, [3.5 1])
%! assert (c(3).Centroid, [1.5 2])
%!test
%!assert (regionprops (bw2d, gray2d, "WeightedCentroid"),
%! struct ("WeightedCentroid",
%! {sum([2 1; 2 2; 3 2; 2 3; 4 1; 5 1; 5 2; 6 2]
%! .* ([4; 0; 4; 5; 7; 5; 3; 7] / 35))
%! sum([3 5; 4 4; 4 5; 5 4; 6 4; 6 5]
%! .* ([7; 5; 2; 8; 6; 5] / 33))}), 5 * eps)
%!test
%! img = zeros (3, 9);
%! img(2, 1:9) = 0:0.1:0.8;
%! bw = im2bw (img, 0.5);
%! props = regionprops (bw, img, "WeightedCentroid");
%! ix = 7:9;
%! x = sum (img(2,ix) .* (ix)) / sum (img(2,ix));
%! assert (props(1).WeightedCentroid(1), x, 10*eps)
%! assert (props(1).WeightedCentroid(2), 2, 10*eps)
%!assert (regionprops (bw2d, gray2d, "MeanIntensity"),
%! struct ("MeanIntensity", {mean([4 0 5 4 7 5 3 7])
%! mean([7 5 2 8 6 5])}))
%!assert (regionprops (bwlabel (bw2d, 4), gray2d, "MeanIntensity"),
%! struct ("MeanIntensity", {mean([4 0 5 4])
%! mean([7 5 2 8 6 5])
%! mean([7 5 3 7])}))
%!assert (regionprops (bw2d, "SubarrayIdx"),
%! struct ("SubarrayIdx", {{[1 2 3], [2 3 4 5 6]}
%! {[4 5], [3 4 5 6]}}))
%!assert (regionprops (bwlabel (bw2d, 4), "SubarrayIdx"),
%! struct ("SubarrayIdx", {{[1 2 3], [2 3]}
%! {[4 5], [3 4 5 6]}
%! {[1 2], [4 5 6]}}))
%!test
%! out = struct ("Image", {logical([1 0 1 1 0; 1 1 0 1 1; 1 0 0 0 0])
%! logical([0 1 1 1; 1 1 0 1])});
%! assert (regionprops (bw2d, "Image"), out)
%! assert (regionprops (bw2d, gray2d, "Image"), out)
%! assert (regionprops (bwlabel (bw2d), "Image"), out)
%!assert (regionprops (bwlabel (bw2d, 4), "Image"),
%! struct ("Image", {logical([1 0; 1 1; 1 0])
%! logical([0 1 1 1; 1 1 0 1])
%! logical([1 1 0; 0 1 1])}))
## Test overlapping bounding boxes
%!test
%! out = struct ("Image", {logical([0 1 1 1; 1 1 0 0; 1 0 0 0; 1 0 0 0])
%! logical([1 1 0 0; 1 1 0 0; 1 1 1 1])
%! logical([1 1; 0 1; 0 1])});
%! assert (regionprops (bw2d_over_bb, "Image"), out)
%! assert (regionprops (bwlabel (bw2d_over_bb), "Image"), out)
%!test
%! out = struct ("Image", {logical([1 1 1 1 1 1
%! 1 0 0 0 0 1
%! 1 0 0 0 0 1
%! 1 0 0 0 0 1
%! 1 0 0 0 0 1
%! 1 1 1 1 1 1])
%! logical([1 1; 1 1])});
%! assert (regionprops (bw2d_insides, "Image"), out)
%! assert (regionprops (bwlabel (bw2d_insides), "Image"), out)
%!test
%! l = uint8 ([
%! 0 0 0 0 0 0
%! 0 1 1 1 1 0
%! 0 1 2 2 1 0
%! 0 1 2 2 1 0
%! 0 1 1 1 1 0
%! 0 0 0 0 0 0
%! ]);
%! assert (regionprops (l, "EulerNumber"),
%! struct ("EulerNumber", {0; 1}))
%!
%! l = uint8 ([
%! 0 0 0 0 0 0 0
%! 0 1 1 1 1 1 0
%! 0 1 2 2 2 1 0
%! 0 1 2 3 2 1 0
%! 0 1 2 2 2 1 0
%! 0 1 1 1 1 1 0
%! 0 0 0 0 0 0 0
%! ]);
%! assert (regionprops (l, "EulerNumber"),
%! struct ("EulerNumber", {0; 0; 1}))
%!test
%! l = uint8 ([
%! 0 0 0 0 0 0 0
%! 0 1 1 1 1 1 0
%! 0 1 0 0 0 1 0
%! 0 1 0 1 0 1 0
%! 0 1 0 0 0 1 0
%! 0 1 1 1 1 1 0
%! 0 0 0 0 0 0 0
%! ]);
%! assert (regionprops (l, "EulerNumber"),
%! struct ("EulerNumber", 1))
%!test
%! l = uint8 ([
%! 1 1 1 1 1 1 1
%! 1 1 2 1 2 2 1
%! 1 2 1 2 1 2 1
%! 1 1 2 1 2 1 1
%! 1 2 1 2 1 2 1
%! 1 2 2 1 2 1 1
%! 1 1 1 1 1 1 1
%! ]);
%! assert (regionprops (l, "EulerNumber"),
%! struct ("EulerNumber", {-9; -4}))
%!test
%! l = uint8 ([
%! 1 1 1 1 1 1 1
%! 1 1 4 1 5 5 1
%! 1 3 1 4 1 5 1
%! 1 1 3 1 4 1 1
%! 1 2 1 3 1 4 1
%! 1 2 2 1 3 1 1
%! 1 1 1 1 1 1 1
%! ]);
%! assert (regionprops (l, "EulerNumber"),
%! struct ("EulerNumber", {-9; 1; 1; 1; 1}))
## Test connectivity for hole filling.
%!test
%! l = uint8 ([
%! 1 1 1 1 1 1 1
%! 0 1 2 1 2 2 1
%! 1 2 1 2 1 2 1
%! 1 1 2 1 2 1 1
%! 1 2 1 2 1 2 1
%! 1 2 2 1 2 1 1
%! 1 1 1 1 1 1 1
%! ]);
%! filled = {
%! logical([
%! 1 1 1 1 1 1 1
%! 0 1 1 1 1 1 1
%! 1 1 1 1 1 1 1
%! 1 1 1 1 1 1 1
%! 1 1 1 1 1 1 1
%! 1 1 1 1 1 1 1
%! 1 1 1 1 1 1 1
%! ]);
%! logical([
%! 0 1 0 1 1
%! 1 1 1 1 1
%! 0 1 1 1 0
%! 1 1 1 1 1
%! 1 1 0 1 0
%! ]);
%! };
%! assert (regionprops (l, {"FilledImage", "FilledArea"}),
%! struct ("FilledImage", filled, "FilledArea", {48; 19}))
## Disconnected regions without holes.
%!test
%! l = uint8 ([
%! 0 0 0 0 0 0 0
%! 0 1 0 1 0 1 0
%! 0 1 0 1 0 1 0
%! 0 0 0 0 0 0 0
%! ]);
%! filled = logical ([
%! 1 0 1 0 1
%! 1 0 1 0 1
%! ]);
%! assert (regionprops (l, {"FilledImage", "FilledArea"}),
%! struct ("FilledImage", filled, "FilledArea", 6))
%!
%! l = uint8 ([
%! 2 2 2 2 2 2 2
%! 2 1 2 1 2 1 2
%! 2 1 2 1 2 1 2
%! 2 2 2 2 2 2 2
%! ]);
%! filled = {
%! logical([
%! 1 0 1 0 1
%! 1 0 1 0 1
%! ]);
%! true(4, 7)
%! };
%! assert (regionprops (l, {"FilledImage", "FilledArea"}),
%! struct ("FilledImage", filled, "FilledArea", {6; 28}))
## Concentric regions to fill holes.
%!test
%! l = uint8 ([
%! 0 0 0 0 0 0 0
%! 0 1 1 1 1 1 0
%! 0 1 2 2 2 1 0
%! 0 1 2 3 2 1 0
%! 0 1 2 2 2 1 0
%! 0 1 1 1 1 1 0
%! 0 0 0 0 0 0 0
%! ]);
%! filled = {true(5, 5); true(3, 3); true};
%! assert (regionprops (l, {"FilledImage", "FilledArea"}),
%! struct ("FilledImage", filled, "FilledArea", {25; 9; 1}))
## Regions with overlapping holes.
%!test
%! l = uint8 ([
%! 1 1 1 2 0 0
%! 1 0 2 1 2 0
%! 1 2 0 1 0 2
%! 1 2 1 1 0 2
%! 0 1 2 2 2 2
%! ]);
%! filled = {
%! logical([
%! 1 1 1 0
%! 1 1 1 1
%! 1 1 1 1
%! 1 1 1 1
%! 0 1 0 0
%! ]);
%! logical([
%! 0 0 1 0 0
%! 0 1 1 1 0
%! 1 1 1 1 1
%! 1 1 1 1 1
%! 0 1 1 1 1
%! ])
%! };
%! assert (regionprops (l, {"FilledImage", "FilledArea"}),
%! struct ("FilledImage", filled, "FilledArea", {16; 18}))
## 3D region to fill which requires connectivity 6 (fails with 18 or 26).
%!test
%! bw = false (5, 5, 5);
%! bw(2:4, 2:4, [1 5]) = true;
%! bw(2:4, [1 5], 2:4) = true;
%! bw([1 5], 2:4, 2:4) = true;
%! filled = bw;
%! filled(2:4, 2:4, 2:4) = true;
%! assert (regionprops (bw, {"FilledImage", "FilledArea"}),
%! struct ("FilledImage", filled, "FilledArea", 81))
%!test
%! l = uint8 ([
%! 1 1 1 2 0 0
%! 1 0 2 1 2 0
%! 1 2 0 1 0 2
%! 1 2 1 1 0 2
%! 0 1 2 2 2 2
%! ]);
%! assert (regionprops (l, {"Extent"}), struct ("Extent", {0.55; 0.44}))
%!test
%! bw = logical ([0 0 0; 0 1 0; 0 0 0]);
%! assert (regionprops (bw, {"MinorAxisLength", "MajorAxisLength", ...
%! "Eccentricity", "Orientation"}),
%! struct ("MajorAxisLength", 4 .* sqrt (1/12),
%! "MinorAxisLength", 4 .* sqrt (1/12),
%! "Eccentricity", 0,
%! "Orientation", 0))
%!test
%! a = eye (4);
%! t = regionprops (a, "majoraxislength");
%! assert (t.MajorAxisLength, 6.4291, 1e-3);
%! t = regionprops (a, "minoraxislength");
%! assert(t.MinorAxisLength, 1.1547 , 1e-3);
%! t = regionprops (a, "eccentricity");
%! assert (t.Eccentricity, 0.98374 , 1e-3);
%! t = regionprops (a, "orientation");
%! assert (t.Orientation, -45);
%! t = regionprops (a, "equivdiameter");
%! assert (t.EquivDiameter, 2.2568, 1e-3);
%!test
%! b = ones (5);
%! t = regionprops (b, "majoraxislength");
%! assert (t.MajorAxisLength, 5.7735 , 1e-3);
%! t = regionprops (b, "minoraxislength");
%! assert (t.MinorAxisLength, 5.7735 , 1e-3);
%! t = regionprops (b, "eccentricity");
%! assert (t.Eccentricity, 0);
%! t = regionprops (b, "orientation");
%! assert (t.Orientation, 0);
%! t = regionprops (b, "equivdiameter");
%! assert (t.EquivDiameter, 5.6419, 1e-3);
%!test
%! c = [0 0 1; 0 1 1; 1 1 0];
%! t = regionprops (c, "minoraxislength");
%! assert (t.MinorAxisLength, 1.8037 , 1e-3);
%! t = regionprops (c, "majoraxislength");
%! assert (t.MajorAxisLength, 4.1633 , 1e-3);
%! t = regionprops (c, "eccentricity");
%! assert (t.Eccentricity, 0.90128 , 1e-3);
%! t = regionprops (c, "orientation");
%! assert (t.Orientation, 45);
%! t = regionprops (c, "equivdiameter");
%! assert (t.EquivDiameter, 2.5231, 1e-3);
## Tests for multiple 'Orientation' thin cases (bug #49613)
%!test
%! bw = logical ([0 0 0 0; 0 1 1 0; 0 0 0 0]);
%! props = regionprops (bw, "Orientation");
%! assert ([props.Orientation], 0, 0)
%!
%! props = regionprops (bw', "Orientation");
%! assert ([props.Orientation], 90, 0)
%!
%! bw = logical ([0 0 0 0; 0 1 1 0; 0 1 1 0; 0 0 0 0]);
%! props = regionprops (bw, "Orientation");
%! assert ([props.Orientation], 0, 0)
%!
%! bw = logical ([1 1 0 0 0 ; 0 0 1 1 0 ; 0 0 0 0 0; 0 0 0 0 0]);
%! props = regionprops (bw, "Orientation");
%! assert ([props.Orientation], -22.5, eps (22.5))
%!
%! bw = logical ([
%! 1 1 0 0 1
%! 0 0 0 0 1
%! 0 0 0 0 0
%! 0 0 1 1 0
%! 1 0 1 1 0
%! 1 0 0 0 0
%! 0 1 0 0 0
%! 0 1 0 0 0]);
%! props = regionprops (bw, "Orientation");
%! assert ([props.Orientation], [0 -67.5 0 90])
%!test
%! f = [0 0 0 0; 1 1 1 1; 0 1 1 1; 0 0 0 0];
%! t = regionprops (f, "Extrema");
%! shouldbe = [0.5 1.5; 4.5 1.5; 4.5 1.5; 4.5 3.5; 4.5 3.5; 1.5 3.5; 0.5 2.5; 0.5 1.5];
%! assert (t.Extrema, shouldbe, eps);
%!test
%! bw = false (5);
%! bw([8 12 13 14 18]) = true;
%! extrema = [2 1; 3 1; 4 2; 4 3; 3 4; 2 4; 1 3; 1 2] + 0.5;
%! assert (regionprops (bw, "extrema"), struct ("Extrema", extrema))
%!test
%! ext1 = [1 0; 5 0; 6 1; 6 2; 2 3; 1 3; 1 3; 1 0] + 0.5;
%! ext2 = [3 3; 6 3; 6 3; 6 5; 6 5; 2 5; 2 5; 2 4] + 0.5;
%! assert (regionprops (bw2d, "extrema"), struct ("Extrema", {ext1; ext2}))
%!assert (regionprops (bw2d, "equivDiameter"),
%! struct ("EquivDiameter", {sqrt(4*8/pi); sqrt(4*6/pi)}))
%!assert (regionprops (bw2d_over_bb, "equivDiameter"),
%! struct ("EquivDiameter", {sqrt(4*7/pi); sqrt(4*8/pi); sqrt(4*4/pi)}))
%!assert (regionprops (bw2d_insides, "equivDiameter"),
%! struct ("EquivDiameter", {sqrt(4*20/pi); sqrt(4*4/pi)}))
## Test the diameter of a circle of diameter 21.
%!test
%! I = zeros (40);
%! disk = fspecial ("disk",10);
%! disk = disk ./ max (disk(:));
%! I(10:30, 10:30) = disk;
%! bw = im2bw (I, 0.5);
%! props = regionprops (bw, "PerimeterOld");
%! assert (props.PerimeterOld, 10*4 + (sqrt (2) * 4)*4, eps*100)
%! props = regionprops (bw, "Perimeter");
%! assert (props.Perimeter, 59.876)
%!
%! props = regionprops (bwconncomp (bw), "PerimeterOld");
%! assert (props.PerimeterOld, 10*4 + (sqrt (2) * 4)*4, eps*100)
%! props = regionprops (bwconncomp (bw), "Perimeter");
%! assert (props.Perimeter, 59.876)
%!assert (regionprops (bw2d, "PerimeterOld"),
%! struct ("PerimeterOld", {(sqrt (2)*6 + 4); (sqrt (2)*3 + 4)}), eps*10)
%!assert (regionprops (bw2d, "Perimeter"),
%! struct ("Perimeter", {11.81; 7.683}))
## Test Perimeter with nested objects
%!assert (regionprops (bw2d_insides, "PerimeterOld"),
%! struct ("PerimeterOld", {20; 4}))
%!assert (regionprops (bw2d_insides, "Perimeter"),
%! struct ("Perimeter", {19.236; 3.556}))
%!assert (regionprops (bwconncomp (bw2d_insides), "PerimeterOld"),
%! struct ("PerimeterOld", {20; 4}))
%!assert (regionprops (bwconncomp (bw2d_insides), "Perimeter"),
%! struct ("Perimeter", {19.236; 3.556}))
## Test ConvexHull, ConvexImage, ConvexArea and Solidity
%!test
%! BW = false (5);
%! BW(2:4, 2:4) = true; # region with simple shape
%! hull_test = [4.5 4; 4.5 2; 4 1.5; 2 1.5; 1.5 2; 1.5 4; 2 4.5; 4 4.5];
%! cimage_test = true(3);
%! carea_test = 9;
%! csolid_test = 1;
%! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull = props.ConvexHull;
%! # test only for existence of the correct corner points
%! # because Matlab returns more points (than necessary)
%! # (The correct shape of the ConvexHull results will only
%! # be tested indirectly via the tests of ConvexArea.)
%! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test))
%! assert (all (hull(1,:) == hull(end,:)))
%! cimage = props.ConvexImage;
%! assert (cimage, cimage_test);
%! carea = props.ConvexArea;
%! assert (carea, carea_test);
%! csolid = props.Solidity;
%! assert (csolid, csolid_test);
%!test
%! BW = logical ([... # region with non-trivial shape
%! 0 0 0 0 0 0 0 0 0 0 0 0 0 0
%! 0 0 0 1 1 1 1 0 0 0 0 0 0 0
%! 0 0 1 1 1 1 1 0 0 0 0 0 0 0
%! 0 1 1 1 1 1 1 0 0 0 0 0 0 0
%! 0 0 1 1 1 1 1 1 1 1 1 0 0 0
%! 0 0 0 1 1 1 1 1 1 1 1 1 0 0
%! 0 0 0 0 1 1 1 1 1 1 1 1 1 0
%! 0 0 0 0 0 1 1 1 0 1 1 1 1 0
%! 0 0 0 0 0 0 1 0 0 0 1 1 1 0
%! 0 0 0 0 0 0 0 0 0 0 0 0 0 0]);
%! hull_test = [4 1.5; 1.5 4; 7 9.5; 13 9.5; 13.5 9; 13.5 7; 11 4.5; 7 1.5];
%! cimage_test = logical ([...
%! 0 0 1 1 1 1 0 0 0 0 0 0
%! 0 1 1 1 1 1 1 1 0 0 0 0
%! 1 1 1 1 1 1 1 1 1 0 0 0
%! 0 1 1 1 1 1 1 1 1 1 0 0
%! 0 0 1 1 1 1 1 1 1 1 1 0
%! 0 0 0 1 1 1 1 1 1 1 1 1
%! 0 0 0 0 1 1 1 1 1 1 1 1
%! 0 0 0 0 0 1 1 1 1 1 1 1]);
%! carea_test = 62;
%! csolid_test = 0.8548;
%! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull = props.ConvexHull;
%! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test))
%! assert (all (hull(1,:) == hull(end,:)))
%! cimage = props.ConvexImage;
%! assert (cimage, cimage_test);
%! carea = props.ConvexArea;
%! assert (carea, carea_test);
%! csolid = props.Solidity;
%! assert (csolid, csolid_test, 1e-4);
%!test
%! BW = false (7);
%! BW(2:6, 2:6) = true;
%! BW(4,4) = false; # region with hole
%! hull_test = [6.5 6; 6.5 2; 6 1.5; 2 1.5; 1.5 2; 1.5 6; 2 6.5; 6 6.5];
%! cimage_test = true(5);
%! carea_test = 25;
%! csolid_test = 0.96;
%! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull = props.ConvexHull;
%! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test))
%! assert (all (hull(1,:) == hull(end,:)))
%! cimage = props.ConvexImage;
%! assert (cimage, cimage_test);
%! carea = props.ConvexArea;
%! assert (carea, carea_test);
%! csolid = props.Solidity;
%! assert (csolid, csolid_test, 1e-4);
%!test
%! BW = false (5);
%! BW(3, 3) = true; # region with single pixel
%! hull_test = [3.5 3; 3 2.5; 2.5 3];
%! cimage_test = true;
%! carea_test = 1;
%! csolid_test = 1;
%! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull = props.ConvexHull;
%! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test))
%! assert (all (hull(1,:) == hull(end,:)))
%! cimage = props.ConvexImage;
%! assert (cimage, cimage_test);
%! carea = props.ConvexArea;
%! assert (carea, carea_test);
%! csolid = props.Solidity;
%! assert (csolid, csolid_test);
%!test
%! BW = false (5);
%! BW(3, 2:4) = true; # regions with pixel line
%! BW2 = BW';
%! hull_test = [2 2.5; 1.5 3; 2 3.5; 4 3.5; 4.5 3; 4 2.5];
%! hull_test2 = fliplr (hull_test);
%! cimage_test = true(1,3);
%! cimage_test2 = cimage_test';
%! carea_test = 3;
%! csolid_test = 1;
%! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull = props.ConvexHull;
%! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test))
%! assert (all (hull(1,:) == hull(end,:)))
%! cimage = props.ConvexImage;
%! assert (cimage, cimage_test);
%! carea = props.ConvexArea;
%! assert (carea, carea_test);
%! csolid = props.Solidity;
%! assert (csolid, csolid_test);
%! props2 = regionprops (BW2, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull2 = props2.ConvexHull;
%! assert (sum (ismember (hull_test2, hull2, "rows")), rows (hull_test2))
%! assert (all (hull2(1,:) == hull2(end,:)))
%! cimage2 = props2.ConvexImage;
%! assert (cimage2, cimage_test2);
%! carea2 = props2.ConvexArea;
%! assert (carea2, carea_test);
%! csolid2 = props2.Solidity;
%! assert (csolid2, csolid_test);
%!test
%! BW = logical ([ ...
%! 1 0 1 0
%! 1 0 1 0
%! 1 0 1 0
%! 1 0 1 0]); # two seperate regions
%! hull_test_1 = [1.5 1; 1 0.5; 0.5 1; 0.5 4; 1 4.5; 1.5 4];
%! hull_test_2 = [3.5 1; 3 0.5; 2.5 1; 2.5 4; 3 4.5; 3.5 4];
%! cimage_test_1 = true(4,1);
%! cimage_test_2 = true(4,1);
%! carea_test1 = 4;
%! carea_test2 = 4;
%! csolid_test1 = 1;
%! csolid_test2 = 1;
%! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull1 = {props.ConvexHull}{1};
%! assert (sum (ismember (hull_test_1, hull1, "rows")), rows (hull_test_1))
%! assert (all (hull1(1,:) == hull1(end,:)))
%! hull2 = {props.ConvexHull}{2};
%! assert (sum (ismember (hull_test_2, hull2, "rows")), rows (hull_test_2))
%! assert (all (hull2(1,:) == hull2(end,:)))
%! cimage1 = {props.ConvexImage}{1};
%! assert (cimage1, cimage_test_1);
%! cimage2 = {props.ConvexImage}{2};
%! assert (cimage2, cimage_test_2);
%! carea1 = {props.ConvexArea}{1};
%! assert (carea1, carea_test1);
%! carea2 = {props.ConvexArea}{2};
%! assert (carea2, carea_test2);
%! csolid1 = {props.Solidity}{1};
%! assert (csolid1, csolid_test1);
%! csolid2 = {props.Solidity}{2};
%! assert (csolid2, csolid_test2);
%!test
%! L = zeros (5);
%! L(1:2:5, :) = 1; # labelled region with 3 disconnected parts
%! hull_test = [5.5 5; 5.5 1; 5 0.5; 1 0.5; 0.5 1; 0.5 5; 1 5.5; 5 5.5];
%! cimage_test = true(5);
%! carea_test = 25;
%! csolid_test = 0.6;
%! props = regionprops (L, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull = props.ConvexHull;
%! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test))
%! assert (all (hull(1,:) == hull(end,:)))
%! cimage = props.ConvexImage;
%! assert (cimage, cimage_test);
%! carea = props.ConvexArea;
%! assert (carea, carea_test);
%! csolid = props.Solidity;
%! assert (csolid, csolid_test);
%!xtest
%! ## Matlab compatible, currently fails because of bug #50188
%! BW = false(4,16);
%! BW(2,2) = true;
%! BW(3,2:end-1) = true; # L-shaped region (small angle)
%! hull_test = [2 1.5; 1.5 2; 1.5 3; 2 3.5; 15 3.5; 15.5 3; 15 2.5];
%! cimage_test = true (2,14);
%! cimage_test(1, 8:end) = false; # this is the Matlab result
%! carea_test = 21;
%! csolid_test = 0.7143;
%! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'});
%! hull = props.ConvexHull;
%! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test))
%! assert (all (hull(1,:) == hull(end,:)))
%! cimage = props.ConvexImage;
%! assert (cimage, cimage_test);
%! carea = props.ConvexArea;
%! assert (carea, carea_test);
%! csolid = props.Solidity;
%! assert (csolid, csolid_test, 1e-4);
## Test guessing between labelled and binary image
%!assert (regionprops ([1 0 1; 1 0 1], "Area"), struct ("Area", 4))
%!assert (regionprops ([1 0 2; 1 1 2], "Area"), struct ("Area", {3; 2}))
## Test missing labels
%!assert (regionprops ([1 0 3; 1 1 3], "Area"), struct ("Area", {3; 0; 2}))
## Test dimensionality of struct array
%!assert (size (regionprops ([1 0 0; 0 0 2], "Area")), [2, 1])
%!error regionprops ([1 -2 0 3])
%!error regionprops ([1 1.5 0 3])
## Test for BW images with zero objects
%!test
%! im = rand (5);
%!
%! ## First do this so we get a list of all supported properties and don't
%! ## have to update the list each time.
%! bw = false (5);
%! bw(13) = true;
%! props = regionprops (bw, im, "all");
%! all_props = fieldnames (props);
%!
%! bw = false (5);
%! props = regionprops (bw, im, "all");
%! assert (size (props), [0 1])
%! assert (sort (all_props), sort (fieldnames (props)))
## Test for labeled images with zeros objects
%!test
%! im = rand (5);
%!
%! ## First do this so we get a list of all supported properties and don't
%! ## have to update the list each time.
%! labeled = zeros (5);
%! labeled(13) = 1;
%! props = regionprops (labeled, im, "all");
%! all_props = fieldnames (props);
%!
%! labeled = zeros (5);
%! props = regionprops (labeled, im, "all");
%! assert (size (props), [0 1])
%! assert (sort (all_props), sort (fieldnames (props)))
## Test for bwconncomp struct with zeros objects
%!test
%! im = rand (5);
%!
%! ## First do this so we get a list of all supported properties and don't
%! ## have to update the list each time.
%! bw = false (5);
%! bw(13) = true;
%! props = regionprops (bwconncomp (bw), im, "all");
%! all_props = fieldnames (props);
%!
%! bw = false (5);
%! props = regionprops (bwconncomp (bw), im, "all");
%! assert (size (props), [0 1])
%! assert (sort (all_props), sort (fieldnames (props)))
## Test MajorAxisLength, MinorAxisLength, and Orientation with
## multiple regions (bug #49613)
%!test
%! bw = logical ([
%! 0 1 1 1 1
%! 0 1 1 0 0
%! 0 0 0 0 0
%! 0 0 0 1 0
%! 0 1 1 1 0]);
%! props = regionprops (bw, "MajorAxisLength", "MinorAxisLength",
%! "Orientation");
%! assert ([props.MajorAxisLength] ,[4.51354115 3.65148372], 1.e-8)
%! assert ([props.MinorAxisLength], [2.01801654 1.82574186], 1.e-8)
%! assert ([props.Orientation], [12.93317840 18.43494882], 1.e-8)
## Test warnings about invalid props for nd images and missing grayscale
%!warning
%! regionprops (rand (5, 5, 5) > 0.5, {"perimeter", "extrema"});
%!warning
%! regionprops (rand (5, 5) > 0.5, {"minintensity", "weightedcentroid"});
## Input check for labeled images
%!error
%! regionprops ([0 -1 3 4; 0 -1 3 4])
%!error
%! regionprops ([0 1.5 3 4; 0 1.5 3 4])
%!error
%! regionprops (int8 ([0 -1 3 4; 0 -1 3 4]))
%!test # bug #52926
%! ## Perimeter of objects that would be connected with connectivity 8
%! ## but have been labeled with connectivity 4.
%! BW = logical ([1 1 1 0 0 0 0 0
%! 1 1 1 0 1 1 0 0
%! 1 1 1 0 1 1 0 0
%! 1 1 1 0 0 0 1 0
%! 1 1 1 0 0 0 1 0
%! 1 1 1 0 0 0 1 0
%! 1 1 1 0 0 1 1 0
%! 1 1 1 0 0 0 0 0]);
%!
%! L = bwlabel (BW, 4);
%! props = regionprops(L, "PerimeterOld");
%! assert ([props.PerimeterOld], [18 4 6+sqrt(2)])
%! props = regionprops(L, "Perimeter");
%! assert ([props.Perimeter], [17.276 3.556 7.013])
%! L = bwlabel (BW, 8);
%! props = regionprops(L, "PerimeterOld");
%! assert ([props.PerimeterOld], [18 10+3*sqrt(2)])
%! props = regionprops(L, "Perimeter");
%! assert ([props.Perimeter], [17.276 13.108])
%!test
%! I = zeros(5);
%! I(3,3) = 1;
%! props = regionprops(I, "Perimeter");
%! assert ([props.Perimeter], [0])
%! I = zeros(5);
%! I(3,3:4) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [1.96])
%! I = zeros(5);
%! I(3:4,3) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [1.96])
%! I = zeros(5);
%! I(3,3) = 1;
%! I(4,4) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [2.812])
%! I = zeros(5);
%! I(3,4) = 1;
%! I(4,3) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [2.812])
%! I = zeros(5);
%! I(3:4,3:4) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [3.556])
%! I = zeros(5);
%! I(3:4,3:4) = 1;
%! I(4,5) = 1;
%! props=regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [4.962])
%! I = zeros(5);
%! I(3:4,3:4) = 1;
%! I(5,5) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [6.277], 4*eps)
%! I = zeros(5);
%! I(2,3) = 1;
%! I(3,2:4) = 1;
%! I(4,3) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [5.624])
%! I = zeros(5);
%! I(2,3) = 1;
%! I(3,2:4) = 1;
%! I(4,3) = 1;
%! I(5,3) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [7.402], 4*eps)
%! I = zeros(5);
%! I(2,3) = 1;
%! I(3,2:4) = 1;
%! I(4,3) = 1;
%! I(5,4) = 1;
%! props = regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [8.436])
%! I = zeros(5);
%! I(2,1:4) = 1;
%! I(3,4) = 1;
%! props=regionprops (I, "Perimeter");
%! assert ([props.Perimeter], [7.013])
image-2.12.0/inst/PaxHeaders.25054/otsuthresh.m 0000644 0000000 0000000 00000000062 13615546210 015762 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/otsuthresh.m 0000644 0001750 0001750 00000012546 13615546210 020534 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2018 Avinoam Kalma
## Copyright (C) 2018 David Miguel Susano Pinto
##
## 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 .
## -*- texinfo -*-
## @deftypefn {} {[@var{level}, @var{sep}] =} otsuthresh (@var{hist})
## Compute global image threshold for histogram using Otsu's method.
##
## Given an image histogram @var{hist} finds the optimal threshold
## value @var{level} for conversion to a binary image with
## @code{im2bw}.
##
## The Otsu's method chooses the threshold value that minimises the
## intraclass variance between two classes, the background and
## foreground. The method is described in @cite{Nobuyuki Otsu
## (1979). "A threshold selection method from gray-level histograms",
## IEEE Trans. Sys., Man., Cyber. 9 (1): 62-66}.
##
## The second output, @var{sep} represents the ``goodness'' (or
## separability) of the threshold at @var{level}. It is a value
## within the range [0 1], the lower bound (zero) being attainable by,
## and only by, histograms having a single constant grey level, and
## the upper bound being attainable by, and only by, two-valued
## pictures.
##
## @seealso{graythresh, im2bw}
## @end deftypefn
function [varargout] = otsuthresh (hist)
if (nargin != 1)
print_usage ();
endif
if (! isvector (hist) || ! isnumeric (hist) || ! isreal (hist)
|| any (isinf (hist)) || any (isnan (hist)) || any (hist < 0)
|| any (hist != fix (hist)))
error ("otsuthresh: HIST must be a vector of non-negative integers");
endif
hist = double (hist);
[varargout{1:nargout}] = graythresh (hist(:).', "otsu");
endfunction
%!test
%! histo = zeros (1, 256);
%! histo([ 29 33 37 41 46 50 54 58 62 66 70 74 78 82 ...
%! 86 90 94 98 102 106 110 114 118 122 126 131 135 139 ...
%! 143 147 151 155 159 163 167 171 175 179 183 187 191 195 ...
%! 199 203 207 211 216 220 224 228 232 236 240 244 248 252]) = ...
%! [2 27 51 144 132 108 43 29 22 21 22 20 10 16 17 12 13 14 12 13 ...
%! 15 25 19 20 23 37 23 65 92 84 87 54 50 54 33 73 76 64 57 58 47 ...
%! 48 30 27 22 20 20 11 12 12 11 7 17 31 37 31];
%! assert (otsuthresh (histo), 114.5/255)
%!test
%! I = max (phantom (), 0);
%! H = imhist (I);
%! assert (otsuthresh (H), 178/255)
%! assert (otsuthresh (H'), 178/255)
%! H = imhist (I, 10);
%! assert (otsuthresh (H), 170/255)
%!assert (otsuthresh (100), 0)
%!assert (otsuthresh (zeros (256, 1)), 0)
%!assert (otsuthresh (zeros (5, 1)), 0)
%!assert (otsuthresh (uint8 ([10 20 30])), 0.5)
%!assert (otsuthresh (int32 ([100 200 300])), 0.5)
%!assert (otsuthresh (int32 ([100 200])), 0)
%!assert (otsuthresh (single ([10 20 30 40])), 1/3);
%!assert (otsuthresh (uint16 ([10 20 30 40 50 60 70 80 90 100])), 5/9)
%!assert (otsuthresh (int16 ([10 20 30 40 50 60 70 80 90 100])), 5/9)
%!assert (otsuthresh (int16 (1:255)), 156/254)
%!assert (otsuthresh (int16 (1:1023)), 631/1022)
%!assert (otsuthresh (int8 (1:1023)), 541/1022)
%!test
%! warning ("off", "Octave:data-file-in-path", "local");
%! S = load ("penny.mat");
%! h = imhist (uint8 (S.P));
%! assert (otsuthresh (h), 94/255);
%!test
%! I = max (phantom (), 0);
%! h = imhist (I, 5);
%! assert (otsuthresh (h), 0.625);
%!error id=Octave:invalid-fun-call otsuthresh ()
%!error id=Octave:invalid-fun-call otsuthresh (ones (10), 5)
%!error otsuthresh ([])
%!error otsuthresh ([Inf 10])
%!error otsuthresh ([10 NA])
%!error otsuthresh ([10 NaN])
%!error otsuthresh (zeros (5))
%!error otsuthresh ([10 -10])
%!error otsuthresh ("foo")
%!demo
%! I = max (phantom (), 0);
%! figure; imshow (I);
%! title ("Original image");
%! h = imhist (I);
%! t = otsuthresh (h);
%! J = im2bw (I);
%! figure; imshow (J);
%! title_line = sprintf ("Black and white image after thresholding, t=%g",
%! t*255);
%! title (title_line);
%!demo
%! warning ("off", "Octave:data-file-in-path", "local");
%! S = load ("penny.mat");
%! I = uint8 (S.P);
%! figure; imshow (I);
%! title ("Original penny image");
%! h = imhist (I);
%! t = otsuthresh (h);
%! J = im2bw (I);
%! figure; imshow (J);
%! title_line = sprintf ("Black and white penny image after thresholding, t=%g",
%! t*255);
%! title (title_line);
%! I = 255 - I;
%! figure; imshow(I);
%! title ("Negative penny image");
%! h = imhist (I);
%! t = otsuthresh (h);
%! J = im2bw (I);
%! figure; imshow (J);
%! title_line = sprintf ("Black and white negative penny image after thresholding, t=%g",
%! t*255);
%! title (title_line);
image-2.12.0/inst/PaxHeaders.25054/rho_filter.m 0000644 0000000 0000000 00000000062 13615546210 015707 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/rho_filter.m 0000644 0001750 0001750 00000013125 13615546210 020453 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2010 Alex Opie
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{filtered} =} rho_filter (@var{proj}, @var{type}, @var{scaling})
##
## Filters the parallel ray projections in the columns of @var{proj},
## according to the filter type chosen by @var{type}. @var{type}
## can be chosen from
## @itemize
## @item 'none'
## @item 'Ram-Lak' (default)
## @item 'Shepp-Logan'
## @item 'Cosine'
## @item 'Hann'
## @item 'Hamming'
## @end itemize
##
## If given, @var{scaling} determines the proportion of frequencies
## below the nyquist frequency that should be passed by the filter.
## The window function is compressed accordingly, to avoid an abrupt
## truncation of the frequency response.
##
## @end deftypefn
## @deftypefn {Function File} {[@var{filtered}, @var{filter}] =} rho_filter (@dots{})
##
## This form also returns the frequency response of the filter in
## the vector @var{filter}.
##
## Performs rho filtering on the parallel ray projections provided.
##
## Rho filtering is performed as part of the filtered back-projection
## method of CT image reconstruction. It is the filtered part of
## the name.
## The simplest rho filter is the Ramachadran-Lakshminarayanan (Ram-Lak),
## which is simply |rho|, where rho is the radial component of spatial
## frequency. However, this can cause unwanted amplification of noise,
## which is what the other types attempt to minimise, by introducing
## roll-off into the response. The Hann and Hamming filters multiply
## the standard response by a Hann or Hamming window, respectively.
## The cosine filter is the standard response multiplied by a cosine
## shape, and the Shepp-Logan filter multiplies the response with
## a sinc shape. The 'none' filter performs no filtering, and is
## included for completeness and to enable incorporating this function
## easily into scripts or functions that may offer the ability to choose
## to apply no filtering.
##
## This function is designed to be used by the function @command{iradon},
## but has been exposed to facilitate custom inverse radon transforms
## and to more clearly break down the process for educational purposes.
## The operations
## @example
## filtered = rho_filter (proj);
## reconstruction = iradon (filtered, 1, 'linear', 'none');
## @end example
## are exactly equivalent to
## @example
## reconstruction = iradon (proj, 1, 'linear', 'Ram-Lak');
## @end example
##
## Usage example:
## @example
## P = phantom ();
## projections = radon (P);
## filtered_projections = rho_filter (projections, 'Hamming');
## reconstruction = iradon (filtered_projections, 1, 'linear', 'none');
## figure, imshow (reconstruction, [])
## @end example
##
## @end deftypefn
function [filtered_proj, filt] = rho_filter (proj, type, scaling)
filtered_proj = proj;
if (nargin < 3)
scaling = 1;
endif
if (nargin < 2) || (size (type) == 0)
type = 'ram-lak';
endif
if (strcmpi (type, 'none'))
filt = 1;
return;
endif
if (scaling > 1) || (scaling < 0)
error ('Scaling factor must be in [0,1]');
endif
## Extend the projections to a power of 2
new_len = 2 * 2^nextpow2 (size (filtered_proj, 1));
filtered_proj (new_len, 1) = 0;
## Scale the frequency response
int_len = (new_len * scaling);
if (mod (floor (int_len), 2))
int_len = ceil (int_len);
else
int_len = floor (int_len);
endif
## Create the basic filter response
rho = scaling * (0:1 / (int_len / 2):1);
rho = [rho'; rho(end - 1:-1:2)'];
## Create the window to apply to the filter response
if (strcmpi (type, 'ram-lak'))
filt = 1;
elseif (strcmpi (type, 'hamming'))
filt = fftshift (hamming (length (rho)));
elseif (strcmpi (type, 'hann'))
filt = fftshift (hanning (length (rho)));
elseif (strcmpi (type, 'cosine'))
f = 0.5 * (0:length (rho) - 1)' / length (rho);
filt = fftshift (sin (2 * pi * f));
elseif (strcmpi (type, 'shepp-logan'))
f = (0:length (rho) / 2)' / length (rho);
filt = sin (pi * f) ./ (pi * f);
filt (1) = 1;
filt = [filt; filt(end - 1:-1:2)];
else
error ('rho_filter: Unknown window type');
endif
## Apply the window
filt = filt .* rho;
## Pad the response to the correct length
len_diff = new_len - int_len;
if (len_diff != 0)
pad = len_diff / 2;
filt = padarray (fftshift (filt), pad);
filt = fftshift (filt);
endif
filtered_proj = fft (filtered_proj);
## Perform the filtering
for i = 1:size (filtered_proj, 2)
filtered_proj (:, i) = filtered_proj (:, i) .* filt;
endfor
## Finally bring the projections back to the spatial domain
filtered_proj = real (ifft (filtered_proj));
## Chop the projections back to their original size
filtered_proj (size (proj, 1) + 1:end, :) = [];
endfunction
%!demo
%! P = phantom ();
%! projections = radon (P);
%! filtered_projections = rho_filter (projections, 'Hamming');
%! reconstruction = iradon (filtered_projections, 1, 'linear', 'none');
%! figure, imshow (reconstruction, [])
image-2.12.0/inst/PaxHeaders.25054/qtdecomp.m 0000644 0000000 0000000 00000000062 13615546210 015366 x ustar 00 20 atime=1580649608
30 ctime=1580650134.434913451
image-2.12.0/inst/qtdecomp.m 0000644 0001750 0001750 00000023150 13615546210 020131 0 ustar 00carandraug carandraug 0000000 0000000 ## Copyright (C) 2004 Josep Mones i Teixidor
##
## 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 .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{S} =} qtdecomp (@var{I})
## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{threshold})
## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{threshold}, @var{mindim})
## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{threshold}, [@var{mindim} @var{maxdim}])
## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{fun})
## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{fun}, @var{P1}, @var{P2}, @dots{})
## Performs quadtree decomposition.
##
## qtdecomp decomposes a square image @var{I} into four equal-sized
## blocks. Then it performs some kind of test on each block to decide if
## it should decompose them further. This process is repeated
## iteratively until there's no block left to be decomposed.
##
## Note that blocks are not decomposed if their dimensions are not even.
##
## The output is a sparse matrix whose non-zero elements determine the
## position of the block (the element is at top-left position in the
## block) and size of each block (the value of the element determines
## length of a side of the square-shaped block).
##
## S = qtdecomp(I) decomposes an intensity image @var{I} as described
## above. By default it doesn't split a block if all elements are equal.
##
## S = qtdecomp(I, threshold) decomposes an image as described, but only
## splits a block if the maximum value in the block minus the minimum
## value is greater than @var{threshold}, which is a value between 0 and
## 1. If @var{I} is of class uint8, @var{threshold} is multiplied by 255
## before use. Also, if@var{I} is of class uint16, @var{threshold} is
## multiplied by 65535.
##
## S = qtdecomp(I, threshold, mindim) decomposes an image using the
## @var{threshold} as just described, but doesn't produce blocks smaller
## than mindim.
##
## S = qtdecomp(I, threshold, [mindim maxdim]) decomposes an image as
## described, but produces blocks that can't be bigger than maxdim. It
## decomposes to maxdim even if it isn't needed if only @var{threshold}
## was considered.
##
## S = qtdecomp(I, fun) decomposes an image @var{I} and uses function
## @var{fun} to decide if a block should be splitted or not. @var{fun}
## is called with a m-by-m-by-k array of m-by-m blocks to be
## considered, and should return a vector of size k, whose elements
## represent each block in the stacked array. @var{fun} sets the
## corresponding value to 1 if the block should be split, and 0
## otherwise.
##
## S = qtdecomp(I, fun, @dots{}) behaves as qtdecomp(I, fun) but passes
## extra parameters to @var{fun}.
##
## @seealso{qtgetblk, qtsetblk}
## @end deftypefn
function S = qtdecomp (I, p1, varargin)
if (nargin < 1)
print_usage;
elseif (! issquare (I))
error("qtdecomp: I should be square.");
endif
## current size (assumed to be square)
curr_size=size(I,1);
## initial mindim to a sensible value
mindim=1;
## sensible default maxdim value
maxdim=curr_size;
if (nargin<2)
## Initialize decision method variable
## We could have implemented threshold as a function and use an
## uniform interface (function handle) to decide whether to split or
## not blocks. We have decided not to do so because block
## rearrangement that is needed as a parameter to functions is
## expensive.
decision_method=0;
elseif (isreal(p1))
## p1 is threshold
threshold=p1;
decision_method=1;
if(strcmp(typeinfo(I), 'uint8 matrix'))
threshold*=255;
elseif(strcmp(typeinfo(I), 'uint16 matrix'))
threshold*=65535;
endif
if (nargin>3)
print_usage;
elseif (nargin==3)
dims=varargin{1};
if (isvector(dims)&&length(dims)==2)
mindim=dims(1);
maxdim=dims(2);
elseif (isreal(dims))
mindim=dims;
else
error("qtdecomp: third parameter must be 'mindim' or '[mindim maxdim]'");
endif
## we won't check if mindim or maxdim are powers of 2. It's too
## restrictive and don't need it at all.
endif
elseif strcmp(typeinfo(p1),"function handle") ...
|| strcmp(typeinfo(p1),"inline function")
## function handles seem to return true to isscalar
fun=p1;
decision_method=2;
else
error("qtdecomp: second parameter must be a integer (threshold) or a function handle (fun).");
endif
## initialize results matrices
res=[];
## bool to flag end
finished=false;
## array of offsets to blocks to evaluate
offsets=[1,1];
if (maxdim0)
divs=2^initial_splits;
if (rem(curr_size,divs)!=0)
error("qtdecomp: Can't decompose I enough times to fulfill maxdim requirement.");
endif
## update curr_size
curr_size/=divs;
if(curr_size0)
## check other ending conditions:
## is size is odd?
## is splitted size < than mindim?
if ((rem(curr_size,2)!=0)||((curr_size/2)