Mercurial > hg > octave-image
changeset 889:ea45a8e63169
maint: Periodic merge of stable to default.
author | Carnë Draug <carandraug@octave.org> |
---|---|
date | Tue, 02 Sep 2014 15:01:16 +0100 |
parents | 33c44c229f74 (diff) d316de4780ce (current diff) |
children | 3d1d76c830c6 |
files | |
diffstat | 28 files changed, 809 insertions(+), 1408 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYING +++ b/COPYING @@ -1,9 +1,7 @@ inst/private/analyze75filename.m GPLv3+ inst/private/handle_colorspec.m GPLv3+ inst/private/im2col_check.m GPLv3+ -inst/private/im2float.m GPLv3+ inst/private/imarithmetics.m GPLv3+ -inst/private/imconversion.m GPLv3+ inst/private/interp_method.m GPLv3+ inst/private/is_double_image.m GPLv3+ inst/private/isimage.m GPLv3+ @@ -73,6 +71,7 @@ inst/imadd.m GPLv3+ inst/imadjust.m GPLv3+ inst/imbothat.m GPLv3+ +inst/imcast.m GPLv3+ inst/imclose.m GPLv3+ inst/imcomplement.m GPLv3+ inst/imcrop.m GPLv3+
--- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Name: Image -Version: 2.2.1 +Version: 2.3.0 Date: 2014-03-08 Author: various authors Maintainer: Carnë Draug <carandraug+dev@gmail.com> @@ -9,6 +9,6 @@ The package also provides functions for feature extraction, image statistics, spatial and geometric transformations, morphological operations, linear filtering, and much more. -Depends: octave (>= 3.8.0), signal (>= 1.2.0), general (>= 1.3.0) +Depends: octave (>= 3.8.0), general (>= 1.3.0) License: GPLv3+, MIT, FreeBSD Url: http://octave.sf.net
--- a/INDEX +++ b/INDEX @@ -133,6 +133,7 @@ im2single im2uint8 im2uint16 + imcast imdither isbw isgray
--- a/NEWS +++ b/NEWS @@ -1,3 +1,48 @@ + Summary of important user-visible changes for image 2.4.0 (yyyy/mm/dd): +------------------------------------------------------------------------- + + ** The following functions are new: + + imcast + + ** The implementation of normxcorr2 has been changed. The new method is + Matlab compatible and will return values in the range [-1 1]. + + ** The image package is no longer dependent on the signal package. + + ** The disk shaped filter of fspecial has been changed for Matlab + compatibility. The elements on the border of the disk are now + weighted by how much of them is covered by the disk. Note that + this change is backwards incompatible. + + ** The following functions will display the output image as a figure + instead of printing to the command line, when there are no output + arguments: + + im2bw + + ** For better compatibility with Matlab, the imrotate, imremap, and + imperspectivewarp functions now use 0 instead of NA for the default + extrapolation value. + + ** Deprecated functions. + + The following functions were deprecated in image 2.2.0 and have been + removed from image 2.4.0. + + bwborder iptchecknargin readexif + impad iptcheckstrs + imrotate_Fourier uintlut + + ** Other functions that have been changed for smaller bugfixes, increased + Matlab compatibility, or performance: + + im2double + im2int16 + im2single + im2uint8 + im2uint16 + Summary of important user-visible changes for image 2.2.1 (2014/03/08): ------------------------------------------------------------------------- @@ -15,6 +60,7 @@ ** Fix regression in bwdist when calculating the closest pixel map. + Summary of important user-visible changes for image 2.2.0 (2014/01/08): -------------------------------------------------------------------------
deleted file mode 100644 --- a/inst/bwborder.m +++ /dev/null @@ -1,51 +0,0 @@ -## Copyright (C) 2000 Etienne Grossmann <etienne@egdn.net> -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU General Public License as published by the Free Software -## Foundation; either version 3 of the License, or (at your option) any later -## version. -## -## This program is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, see <http://www.gnu.org/licenses/>. - -## -*- texinfo -*- -## @deftypefn {Function File} {@var{b} = } bwborder (@var{im}) -## Finds the borders of foreground objects in a binary image. -## -## @code{bwborder} has been deprecated in favor of -## @code{bwmorph (@var{im},"remove")}. This function will be removed from -## future versions of the `image' package. -## -## @var{b} is the borders in the 0-1 matrix @var{im}. 4-neighborhood is considered. -## -## A pixel is on the border if it is set in @var{im}, and it has at least one -## neighbor that is not set. -## @end deftypefn - -function b = bwborder(im) - - ## Deprecate bwborder because bwmorph does the same job, works for any - ## number of dimensions, performs faster, and exist in Matlab. - persistent warned = false; - if (! warned) - warned = true; - warning ("Octave:deprecated-function", - ["`bwborder' has been deprecated in favor of " ... - "`bwmorph (IM,\"remove\")'. This function will be removed " ... - "from future versions of the `image' package"]); - endif - -[R,C]=size(im); - -b = im & ... - !([im(2:R,:) ; zeros(1,C) ] & ... - [zeros(1,C); im(1:R-1,:) ] & ... - [im(:,2:C) , zeros(R,1) ] & ... - [zeros(R,1), im(:,1:C-1)] ) ; - -endfunction
--- a/inst/fspecial.m +++ b/inst/fspecial.m @@ -14,79 +14,99 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} {@var{filter} = } fspecial(@var{type}, @var{arg1}, @var{arg2}) +## @deftypefn {Function File} {} fspecial(@var{type}, @var{arg1}, @var{arg2}) ## Create spatial filters for image processing. ## ## @var{type} determines the shape of the filter and can be -## @table @t -## @item "average" +## @table @asis +## @item @qcode{"average"} ## Rectangular averaging filter. The optional argument @var{arg1} controls the ## size of the filter. If @var{arg1} is an integer @var{N}, a @var{N} by @var{N} ## filter is created. If it is a two-vector with elements @var{N} and @var{M}, the ## resulting filter will be @var{N} by @var{M}. By default a 3 by 3 filter is ## created. -## @item "disk" +## +## @item @qcode{"disk"} ## Circular averaging filter. The optional argument @var{arg1} controls the -## radius of the filter. If @var{arg1} is an integer @var{N}, a 2 @var{N} + 1 -## filter is created. By default a radius of 5 is used. -## @item "gaussian" +## radius of the filter. If @var{arg1} is an integer @var{R}, a 2 @var{R} + 1 +## filter is created. By default a radius of 5 is used. If the returned matrix +## corresponds to a cartesian grid, each element of the matrix is weighted by +## how much of the corresponding grid square is covered by a disk of radius +## @var{R} and centered at the middle of the element @var{R}+1,@var{R}+1. +## +## @item @qcode{"gaussian"} ## Gaussian filter. The optional argument @var{arg1} controls the size of the ## filter. If @var{arg1} is an integer @var{N}, a @var{N} by @var{N} ## filter is created. If it is a two-vector with elements @var{N} and @var{M}, the ## resulting filter will be @var{N} by @var{M}. By default a 3 by 3 filter is ## created. The optional argument @var{arg2} sets spread of the filter. By default ## a spread of @math{0.5} is used. -## @item "log" +## +## @item @qcode{"log"} ## Laplacian of Gaussian. The optional argument @var{arg1} controls the size of the ## filter. If @var{arg1} is an integer @var{N}, a @var{N} by @var{N} ## filter is created. If it is a two-vector with elements @var{N} and @var{M}, the ## resulting filter will be @var{N} by @var{M}. By default a 5 by 5 filter is ## created. The optional argument @var{arg2} sets spread of the filter. By default ## a spread of @math{0.5} is used. -## @item "laplacian" +## +## @item @qcode{"laplacian"} ## 3x3 approximation of the laplacian. The filter is approximated as +## ## @example -## (4/(@var{alpha}+1))*[@var{alpha}/4, (1-@var{alpha})/4, @var{alpha}/4; ... -## (1-@var{alpha})/4, -1, (1-@var{alpha})/4; ... -## @var{alpha}/4, (1-@var{alpha})/4, @var{alpha}/4]; +## (4/(@var{alpha}+1)) * [ @var{alpha}/4 (1-@var{alpha})/4 @var{alpha}/4 +## (1-@var{alpha})/4 -1 (1-@var{alpha})/4 +## @var{alpha}/4 (1-@var{alpha})/4 @var{alpha}/4 ]; ## @end example +## ## where @var{alpha} is a number between 0 and 1. This number can be controlled ## via the optional input argument @var{arg1}. By default it is @math{0.2}. -## @item "unsharp" +## +## @item @qcode{"unsharp"} ## Sharpening filter. The following filter is returned ## @example -## (1/(@var{alpha}+1))*[-@var{alpha}, @var{alpha}-1, -@var{alpha}; ... -## @var{alpha}-1, @var{alpha}+5, @var{alpha}-1; ... -## -@var{alpha}, @var{alpha}-1, -@var{alpha}]; +## (1/(@var{alpha}+1)) * [-@var{alpha} @var{alpha}-1 -@var{alpha} +## @var{alpha}-1 @var{alpha}+5 @var{alpha}-1 +## -@var{alpha} @var{alpha}-1 -@var{alpha}]; ## @end example +## ## where @var{alpha} is a number between 0 and 1. This number can be controlled ## via the optional input argument @var{arg1}. By default it is @math{0.2}. -## @item "motion" +## +## @item @qcode{"motion"} ## Moion blur filter of width 1 pixel. The optional input argument @var{arg1} ## controls the length of the filter, which by default is 9. The argument @var{arg2} ## controls the angle of the filter, which by default is 0 degrees. -## @item "sobel" +## +## @item @qcode{"sobel"} ## Horizontal Sobel edge filter. The following filter is returned +## ## @example -## [ 1, 2, 1; -## 0, 0, 0; -## -1, -2, -1 ] +## [ 1 2 1 +## 0 0 0 +## -1 -2 -1 ] ## @end example -## @item "prewitt" +## +## @item @qcode{"prewitt"} ## Horizontal Prewitt edge filter. The following filter is returned +## ## @example -## [ 1, 1, 1; -## 0, 0, 0; -## -1, -1, -1 ] +## [ 1 1 1 +## 0 0 0 +## -1 -1 -1 ] ## @end example +## ## @item "kirsch" ## Horizontal Kirsch edge filter. The following filter is returned -## @example -## [ 3, 3, 3; -## 3, 0, 3; -## -5, -5, -5 ] -## @end example +## +## @verbatim +## [ 3 3 3 +## 3 0 3 +## -5 -5 -5 ] +## @end verbatim ## @end table +## +## @seealso{conv2, convn, filter2, imfilter} ## @end deftypefn ## Remarks by Søren Hauberg (jan. 2nd 2007) @@ -95,11 +115,14 @@ ## http://www.csse.uwa.edu.au/~pk/research/matlabfns/OctaveCode/fspecial.m function f = fspecial (type, arg1, arg2) - if (!ischar (type)) + + if (nargin < 1) + print_usage (); + elseif (! ischar (type)) error ("fspecial: first argument must be a string"); endif - - switch lower(type) + + switch lower (type) case "average" ## Get filtersize if (nargin > 1 && isreal (arg1) && length (arg1 (:)) <= 2) @@ -114,17 +137,91 @@ case "disk" ## Get the radius - if (nargin > 1 && isreal (arg1) && length (arg1 (:)) == 1) - radius = arg1; + if (nargin > 1 && isreal (arg1) && isscalar (arg1)) + r = arg1; else - radius = 5; + r = 5; endif ## Create the filter - [x, y] = meshgrid (-radius:radius, -radius:radius); - r = sqrt (x.^2 + y.^2); - f = (r <= radius); - ## Normalize the filter to integral 1 - f = f / sum (f (:)); + if (r == 0) + f = 1; + else + ax = r + 1; # index of the "x-axis" and "y-axis" + corner = floor (r / sqrt (2)+0.5)-0.5; # corner corresponding to 45 degrees + rsq = r*r; + ## First set values for points completely covered by the disk + [X, Y] = meshgrid (-r:r, -r:r); + rhi = (abs (X) +0.5).^2 + (abs (Y)+0.5).^2; + f = (rhi <= rsq) / 1.0; + xx = linspace (0.5, r - 0.5, r); + ii = sqrt (rsq - xx.^2); # intersection points for sqrt (r^2 - x^2) + ## Set the values at the axis caps + tmp = sqrt (rsq -0.25); + rint = (0.5*tmp + rsq * atan (0.5/tmp))/2; # value of integral on the right + cap = 2*rint - r+0.5; # at the caps, lint = rint + f(ax ,ax+r) = cap; + f(ax ,ax-r) = cap; + f(ax+r,ax ) = cap; + f(ax-r,ax ) = cap; + if (r == 1) + y = ii(1); + lint = rint; + tmp = sqrt (rsq - y^2); + rint = (y*tmp + rsq * atan (y/tmp))/2; + val = rint - lint - 0.5 * (y-0.5); + f(ax-r,ax-r) = val; + f(ax+r,ax-r) = val; + f(ax-r,ax+r) = val; + f(ax+r,ax+r) = val; + else + ## Set the values elsewhere on the rim + idx = 1; # index in the vector ii + x = 0.5; # bottom left corner of the current square + y = r-0.5; + rx = 0.5; # x on the right of the integrable region + ybreak = false; # did we change our y last time + do + i = x +0.5; + j = y +0.5; + lint = rint; + lx = rx; + if (ybreak) + ybreak = false; + val = lx-x; + idx++; + x++; + rx = x; + val -= y*(x-lx); + elseif (ii(idx+1) < y) + ybreak = true; + y--; + rx = ii(y+1.5); + val = (y+1) * (x-rx); + else + val = -y; + idx++; + x++; + rx = x; + if (floor (ii(idx)-0.5) == y) + y++; + endif + endif + tmp = sqrt (rsq - rx*rx); + rint = (rx*tmp + rsq * atan (rx/tmp))/2; + val += rint - lint; + f(ax+i, ax+j) = val; + f(ax+i, ax-j) = val; + f(ax-i, ax+j) = val; + f(ax-i, ax-j) = val; + f(ax+j, ax+i) = val; + f(ax+j, ax-i) = val; + f(ax-j, ax+i) = val; + f(ax-j, ax-i) = val; + until (y < corner || x > corner) + endif + # Normalize + f /= pi * rsq; + endif case "gaussian" ## Get hsize @@ -250,3 +347,33 @@ error ("fspecial: filter type '%s' is not supported", type); endswitch endfunction + + +## Test that the disk filter's error does not grow unreasonably large +%!test +%! for i = 1:9 +%! n = 2^i; +%! assert (sum (fspecial ("disk", n)(:)), 1, eps*n*n); +%! endfor + +## Test that all squares completely under the disk or completely out of it are +## being assigned the correct values. +%!test +%! for r = [3 5 9 17] +%! f = fspecial ("disk", r); +%! [X, Y] = meshgrid (-r:r, -r:r); +%! rhi = (abs (X) + 0.5).^2 + (abs (Y) + 0.5).^2; +%! rlo = (abs (X) - 0.5).^2 + (abs (Y) - 0.5).^2; +%! fhi = (rhi <= (r^2)); +%! flo = (rlo >= (r^2)); +%! for i = 1:(2*r+1) +%! for j = 1:(2*r+1) +%! if (fhi(i,j)) +%! assert (f(i,j), 1/(pi*r^2), eps); +%! endif +%! if (flo(i,j)) +%! assert (f(i,j), 0); +%! endif +%! endfor +%! endfor +%! endfor
--- a/inst/im2bw.m +++ b/inst/im2bw.m @@ -59,37 +59,44 @@ if (islogical (img)) warning ("im2bw: IMG is already binary so nothing is done"); - BW = img; - return - endif + 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 - ## 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 + ## Convert the threshold value to same image class to do the thresholding which + ## is faster than converting the image to double and keep the threshold value + switch (class (img)) + case {"double", "single", "logical"} + ## do nothing + case {"uint8"} + thres = im2uint8 (thres); + case {"uint16"} + thres = im2uint16 (thres); + case {"int16"} + thres = im2int16 (thres); + otherwise + ## we should have never got here in the first place anyway + error("im2bw: unsupported image class"); + endswitch + + tmp = (img > thres); # matlab compatible (not "greater than or equal") endif - ## Convert the threshold value to same image class to do the thresholding which - ## is faster than converting the image to double and keep the threshold value - switch (class (img)) - case {"double", "single"} - ## do nothing - case {"uint8"} - thres = im2uint8 (thres); - case {"uint16"} - thres = im2uint16 (thres); - case {"int16"} - thres = im2int16 (thres); - otherwise - ## we should have never got here in the first place anyway - error("im2bw: unsupported image class"); - endswitch + if (nargout > 0) + BW = tmp; + else + imshow (tmp); + endif - BW = (img > thres); # matlab compatible (not "greater than or equal") endfunction %!assert(im2bw ([0 0.4 0.5 0.6 1], 0.5), logical([0 0 0 1 1])); # basic usage
--- a/inst/im2double.m +++ b/inst/im2double.m @@ -1,5 +1,5 @@ ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org> -## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com> +## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com> ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software @@ -15,32 +15,58 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{im2} = im2double (@var{im1}) -## @deftypefnx {Function File} @var{im2} = im2double (@var{im1}, "indexed") -## Convert input image @var{im1} to double precision. +## @deftypefn {Function File} {} im2double (@var{img}) +## @deftypefnx {Function File} {} im2double (@var{img}, "indexed") +## Convert image to double precision. ## -## The following images type are supported: double, single, uint8, uint16, int16, -## binary (logical), indexed. If @var{im1} is an indexed images, the second -## argument must be a string with the value `indexed'. +## The conversion of @var{img} to double precision, is dependent +## on the type of input image. The following input classes are supported: ## -## Processing will depend on the class of the input image @var{im1}: -## @itemize @bullet -## @item uint8, uint16, int16 - output will be rescaled for the interval [0 1] -## with the limits of the class; -## @item double - output will be the same as input; -## @item single - output will have the same values as input but the class will -## double; -## @item indexed, logical - converted to double class. +## @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 single +## Values are cast to single precision. +## +## @item double +## Returns the same image. ## @end itemize ## -## @seealso{im2bw, im2int16, im2single, im2uint8, im2uint16} +## If the second argument is the string @qcode{"indexed"}, then values are +## cast to double precision, and a +1 offset is applied if input is +## an integer class. +## +## @seealso{im2bw, imcast, im2uint8, im2int16, im2single, im2uint16} ## @end deftypefn -function im = im2double (im, indexed = false) - im = im2float ("double", nargin, im, indexed); +function imout = im2double (img, varargin) + if (nargin < 1 || nargin > 2) + print_usage (); + elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed")) + error ("im2double: second input argument must be the string \"indexed\""); + endif + imout = imcast (img, "double", varargin{:}); endfunction -%!assert(im2double([1 2 3]), [1 2 3]); # double returns the same -%!assert(im2double(uint8([0 255])), [0 1]); # basic usage -%!assert(im2double(uint8([1 25]), "indexed"), [2 26]); # test indexed -%!assert(im2double(int16([-32768 32768])), [0 1]); # test signed integer +%!assert (im2double ([1 2 3]), [1 2 3]); +%!assert (im2double (single ([1 2 3])), [1 2 3]); +%!assert (im2double (uint8 ([0 127 128 255])), [0 127/255 128/255 1]); +%!assert (im2double (uint16 ([0 127 128 65535])), [0 127/65535 128/65535 1]); +%!assert (im2double (int16 ([-32768 -32767 -32766 32767])), [0 1/65535 2/65535 1]); + +%!assert (im2double (uint8 ([0 1 255]), "indexed"), [1 2 256]); +%!assert (im2double (uint16 ([0 1 2557]), "indexed"), [1 2 2558]); +%!assert (im2double ([3 25], "indexed"), [3 25]); + +%!error <indexed> im2double (single ([0 1 2]), "indexed"); +%!error <indexed> im2double (int16 ([17 8]), "indexed"); +%!error <indexed> im2double (int16 ([-7 8]), "indexed"); +%!error <indexed> im2double ([false true], "indexed"); +
--- a/inst/im2int16.m +++ b/inst/im2int16.m @@ -1,4 +1,4 @@ -## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com> +## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com> ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software @@ -14,55 +14,47 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{im2} = im2int16 (@var{im1}) -## Convert input image @var{im1} to int16 precision. +## @deftypefn {Function File} {} im2int16 (@var{img}) +## Convert image to int16. ## -## The following images type are supported: double, single, uint8, uint16, int16, -## binary (logical). +## 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]. ## -## Processing will depend on the class of the input image @var{im1}: -## @itemize @bullet -## @item int16 - returns the same as input -## @item uint8, double, single, uint16, logical - output will be rescaled for the -## interval of the uint16 class [0 65535] +## @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 itemize ## -## @seealso{im2bw, im2double, im2single, im2uint8, im2uint16} +## @seealso{im2bw, imcast, im2uint8, im2double, im2single, im2uint16} ## @end deftypefn -function im = im2int16 (im) - - ## unlike the others for imconversion, this only accepts 1 argument so we check here +function imout = im2int16 (img) if (nargin != 1) - print_usage; + print_usage (); endif - ## Input checking (private function that is used for all im2class functions) - im_class = imconversion (nargin, "im2int16", false, im); - - ## for those who may wonder, 32767 = intmax ("int16") - ## for those who may wonder, 65535 = intmax ("uint16") - switch im_class - case "int16" - ## do nothing, return the same - case {"single", "double"} - im = int16 (double (im * uint16(65535)) - 32767 - 1); - case "logical" - im(im) = intmax ("int16"); - im(!im) = intmin ("int16"); - case "uint8" - ## 257 is the ratio between the max of uint8 and uint16 - ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257 - im = int16 (double (257 * uint16 (im)) - 32767 - 1); - case "uint16" - im = int16 (double (im) - 32767 - 1); - otherwise - error ("unsupported image class %s", im_class); - endswitch - + imout = imcast (img, "int16"); endfunction -%!assert(im2int16(int16([-2 2 3])), int16([-2 2 3])); # uint16 returns the same -%!assert(im2int16(uint8([0 255])), int16([-32768 32767])); # basic usage with uint8 -%!assert(im2int16(uint16([0 65535])), int16([-32768 32767])); # basic usage with uint16 -%!assert(im2int16([0 0.5 1]), int16([-32768 0 32767])); # basic usage with double -%!assert(im2int16([1 2]), int16([32767 32767])); # for double, above 1 is same as 1 +%!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"); +
--- a/inst/im2single.m +++ b/inst/im2single.m @@ -1,4 +1,4 @@ -## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com> +## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com> ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software @@ -14,32 +14,58 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{im2} = im2single (@var{im1}) -## @deftypefnx {Function File} @var{im2} = im2single (@var{im1}, "indexed") -## Convert input image @var{im1} to single precision. +## @deftypefn {Function File} {} im2single (@var{img}) +## @deftypefnx {Function File} {} im2single (@var{img}, "indexed") +## Convert image to single precision. ## -## The following images type are supported: double, single, uint8, uint16, int16, -## binary (logical), indexed. If @var{im1} is an indexed images, the second -## argument must be a string with the value `indexed'. +## The conversion of @var{img} to single precision, is dependent +## on the type of input image. The following input classes are supported: ## -## Processing will depend on the class of the input image @var{im1}: -## @itemize @bullet -## @item uint8, uint16, int16 - output will be rescaled for the interval [0 1] -## with the limits of the class; -## @item single - output will be the same as input; -## @item double - output will have the same values as input but the class will -## single; -## @item indexed, logical - converted to single class. +## @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 itemize ## -## @seealso{im2bw, im2double, im2int16, im2uint8, im2uint16} +## 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 im = im2single (im, indexed = false) - im = im2float ("single", nargin, im, indexed); +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([1 2 3]), single([1 2 3])); # double returns the same -%!assert(im2single(uint8([0 255])), single([0 1])); # basic usage -%!assert(im2single(uint8([1 25]), "indexed"), single([2 26])); # test indexed -%!assert(im2single(int16([-32768 32768])), single([0 1])); # test signed integer +%!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 <indexed> im2single ([0 1 2], "indexed"); +%!error <indexed> im2single (int16 ([17 8]), "indexed"); +%!error <indexed> im2single (int16 ([-7 8]), "indexed"); +%!error <indexed> im2single ([false true], "indexed"); +
--- a/inst/im2uint16.m +++ b/inst/im2uint16.m @@ -1,5 +1,5 @@ ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org> -## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com> +## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com> ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software @@ -15,67 +15,63 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{im2} = im2uint16 (@var{im1}) -## @deftypefnx {Function File} @var{im2} = im2uint16 (@var{im1}, "indexed") -## Convert input image @var{im1} to uint16 precision. +## @deftypefn {Function File} {} im2uint16 (@var{img}) +## @deftypefnx {Function File} {} im2uint16 (@var{img}, "indexed") +## Convert image to uint16. ## -## The following images type are supported: double, single, uint8, uint16, int16, -## binary (logical), indexed. If @var{im1} is an indexed images, the second -## argument must be a string with the value `indexed'. +## The conversion of @var{img} to a 16-bit unsigned integer, is dependent +## on the type of input image. The following input classes are supported +## for non-indexed images: ## -## Processing will depend on the class of the input image @var{im1}: -## @itemize @bullet -## @item uint16 - returns the same as input -## @item uint8, double, single, int16, logical - output will be rescaled for the -## interval of the uint16 class [0 65535] -## @item indexed - depends on the input class. If double, no value can be above -## the max of the uint16 class (65535). +## @table @samp +## @item int16 or uint8 +## Values are rescaled to the range of the uint16 class [0 65535]. +## +## @item logical +## True and false values are assigned a value of 0 and 255 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 [0 255]. +## +## @item uint16 +## Returns the same image. ## @end itemize ## -## @seealso{im2bw, im2double, im2int16, im2single, im2uint8} +## If the second argument is the string @qcode{"indexed"}, then values are +## cast to uint16, and a -1 offset is applied if input is +## a floating point class. Input checking is performed and an error will +## be throw is the range of values in uint16 is not enough for all the +## image indices. +## +## @seealso{im2bw, imcast, im2uint8, im2double, im2int16, im2single} ## @end deftypefn -function im = im2uint16 (im, indexed = false) - - ## Input checking (private function that is used for all im2class functions) - im_class = imconversion (nargin, "im2uint16", indexed, im); - - ## for those who may wonder, 65535 = intmax ("uint16") - switch im_class - case "uint16" - ## do nothing, return the same - case {"single", "double"} - if (indexed) - imax = max (im(:)); - if ( imax > 65535) - error ("Too many colors '%d' for an indexed uint16 image", imax); - endif - im = uint16 (im) - 1; - else - im = uint16 (im * 65535); - endif - case "logical" - im = uint16 (im) * uint16 (65535); - case "uint8" - if (indexed) - im = uint16 (im); - else - ## 257 is the ratio between the max of uint8 and uint16 - ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257 - im = 257 * uint16 (im); - endif - case "int16" - im = uint16 (double (im) + double (intmax (im_class)) + 1); - otherwise - error ("unsupported image class %s", im_class); - endswitch - +function imout = im2uint16 (im, varargin) + if (nargin < 1 || nargin > 2) + print_usage (); + elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed")) + error ("im2uint16: second input argument must be the string \"indexed\""); + endif + imout = imcast (im, "uint16", varargin{:}); endfunction -%!assert(im2uint16(uint16([1 2 3])), uint16([1 2 3])); # uint16 returns the same -%!assert(im2uint16(uint8([0 255])), uint16([0 65535])); # basic usage with uint8 -%!assert(im2uint16([0 0.5 1]), uint16([0 32768 65535])); # basic usage with double -%!assert(im2uint16([1 2]), uint16([65535 65535])); # for double, above 1 is same as 1 -%!assert(im2uint16(uint8([3 25]), "indexed"), uint16([3 25])); # test indexed uint8 -%!assert(im2uint16([3 25], "indexed"), uint16([2 24])); # test indexed double -%!assert(im2uint16(int16([-32768 32768])), uint16([0 65535])); # test signed integer +%!assert (im2uint16 (uint16 ([1 2 3])), uint16 ([1 2 3])); +%!assert (im2uint16 (uint8 ([0 127 128 255])), uint16 ([0 32639 32896 65535])); +%!assert (im2uint16 ([0 0.5 1]), uint16 ([0 32768 65535])); +%!assert (im2uint16 ([0 1/65535 1.4/65535 1.5/65535 1]), uint16 ([0 1 1 2 65535])); +%!assert (im2uint16 ([1 2]), uint16 ([65535 65535])); +%!assert (im2uint16 ([-1 0 0.5 1]), uint16 ([0 0 32768 65535])); +%!assert (im2uint16 (int16 ([-32768 -1 0 32768])), uint16 ([0 32767 32768 65535])); +%!assert (im2uint16 ([false true]), uint16 ([0 65535])); +%!assert (im2uint16 ([true false]), uint16 ([65535 0])); + +%!assert (im2uint16 (uint8 ([3 25]), "indexed"), uint16 ([3 25])); +%!assert (im2uint16 ([1 3 25], "indexed"), uint16 ([0 2 24])); + +%!error <indexed> im2uint16 ([0 1 2], "indexed"); +%!error <indexed> im2uint16 (int16 ([17 8]), "indexed"); +%!error <indexed> im2uint16 (int16 ([-7 8]), "indexed"); +%!error <indexed> im2uint16 ([false true], "indexed"); +%!error <range of values> im2uint16 (65537, "indexed"); +
--- a/inst/im2uint8.m +++ b/inst/im2uint8.m @@ -1,5 +1,5 @@ ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org> -## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com> +## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com> ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software @@ -15,70 +15,64 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{im2} = im2uint8 (@var{im1}) -## @deftypefnx {Function File} @var{im2} = im2uint8 (@var{im1}, "indexed") -## Convert input image @var{im1} to uint16 precision. +## @deftypefn {Function File} {} im2uint8 (@var{img}) +## @deftypefnx {Function File} {} im2uint8 (@var{img}, "indexed") +## Convert image to uint8. ## -## The following images type are supported: double, single, uint8, uint16, int16, -## binary (logical), indexed. If @var{im1} is an indexed images, the second -## argument must be a string with the value `indexed'. +## The conversion of @var{img} to an 8-bit unsigned integer, is dependent +## on the type of input image. The following input classes are supported +## for non-indexed images: ## -## Processing will depend on the class of the input image @var{im1}: -## @itemize @bullet -## @item uint8 - returns the same as input -## @item uint16, double, single, int16, logical - output will be rescaled for the -## interval of the uint8 class [0 255] -## @item indexed - depends on the input class. No value can be above the max of -## the uint8 class (255). +## @table @samp +## @item int16 or uint16 +## Values are rescaled to the range of the uint8 class [0 255]. +## +## @item logical +## True and false values are assigned a value of 0 and 255 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 [0 255]. +## +## @item uint8 +## Returns the same image. ## @end itemize ## -## @seealso{im2bw, im2double, im2int16, im2single, im2uint16} +## If the second argument is the string @qcode{"indexed"}, then values are +## cast to uint8, and a -1 offset is applied if input is +## a floating point class. Input checking is performed and an error will +## be throw is the range of values in uint8 is not enough for all the +## image indices. +## +## @seealso{im2bw, imcast, im2double, im2int16, im2single, im2uint16} ## @end deftypefn -function im = im2uint8 (im, indexed = false) - - ## Input checking (private function that is used for all im2class functions) - im_class = imconversion (nargin, "im2uint8", indexed, im); - - if (indexed) - imax = max (im(:)); - if (imax > 255) - error ("Too many colors '%d' for an indexed uint8 image", imax); - endif +function imout = im2uint8 (img, varargin) + if (nargin < 1 || nargin > 2) + print_usage (); + elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed")) + error ("im2uint8: second input argument must be the string \"indexed\""); endif - - ## for those who may wonder, 255 = intmax ("uint8") - switch im_class - case "uint8" - ## do nothing, return the same - case {"single", "double"} - if (indexed) - im = uint8 (im) - 1; - else - im = uint8 (im * 255); - endif - case "logical" - im = uint8 (im) * uint8 (255); - case "uint16" - if (indexed) - im = uint8 (im); - else - ## 257 is the ratio between the max of uint8 and uint16 - ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257 - im = uint8 (im / 257); - endif - case "int16" - im = uint8 ((double (im) + double (intmax (im_class)) + 1) / 257); - otherwise - error ("unsupported image class %s", im_class); - endswitch - + imout = imcast (img, "uint8", varargin{:}); endfunction -%!assert(im2uint8(uint8([1 2 3])), uint8([1 2 3])); # uint16 returns the same -%!assert(im2uint8(uint16([0 65535])), uint8([0 255])); # basic usage with uint16 -%!assert(im2uint8([0 0.5 1]), uint8([0 128 255])); # basic usage with double -%!assert(im2uint8([1 2]), uint8([255 255])); # for double, above 1 is same as 1 -%!assert(im2uint8(uint16([3 25]), "indexed"), uint8([3 25])); # test indexed uint8 -%!assert(im2uint8([3 25], "indexed"), uint8([2 24])); # test indexed double -%!assert(im2uint8(int16([-32768 0 32768])), uint8([0 128 255])); # test signed integer +%!assert (im2uint8 (uint8 ([1 2 3])), uint8 ([1 2 3])); +%!assert (im2uint8 (uint16 ([0 65535])), uint8 ([0 255])); +%!assert (im2uint8 ([0 0.5 1]), uint8 ([0 128 255])); +%!assert (im2uint8 ([1 2]), uint8 ([255 255])); +%!assert (im2uint8 ([-1 0 0.5 1 2]), uint8 ([0 0 128 255 255])); +%!assert (im2uint8 (int16 ([-32768 0 32768])), uint8 ([0 128 255])); +%!assert (im2uint8 ([false true]), uint8 ([0 255])); +%!assert (im2uint8 ([true false]), uint8 ([255 0])); + +%!assert (im2uint8 ([1 256], "indexed"), uint8 ([0 255])); +%!assert (im2uint8 ([3 25], "indexed"), uint8 ([2 24])); +%!assert (im2uint8 (uint16 ([3 25]), "indexed"), uint8 ([3 25])); + +%!error <indexed> im2uint8 ([0 1 2], "indexed"); +%!error <indexed> im2uint8 (int16 ([17 8]), "indexed"); +%!error <indexed> im2uint8 (int16 ([-7 8]), "indexed"); +%!error <indexed> im2uint8 ([false true], "indexed"); +%!error <range of values> im2uint8 (uint16 (256), "indexed"); +%!error <range of values> im2uint8 (257, "indexed"); +
new file mode 100644 --- /dev/null +++ b/inst/imcast.m @@ -0,0 +1,184 @@ +## Copyright (C) 2014 Carnë Draug <carandraug+dev@gmail.com> +## +## This program is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free Software +## Foundation; either version 3 of the License, or (at your option) any later +## version. +## +## This program is distributed in the hope that it will be useful, but WITHOUT +## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +## details. +## +## You should have received a copy of the GNU General Public License along with +## this program; if not, see <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} {} imcast (@var{img}, @var{type}) +## @deftypefnx {Function File} {} imcast (@var{img}, @var{type}, "indexed") +## Convert image to specific data type @var{type}. +## +## Converts a valid image @var{img} into another class @var{type}. A valid +## image must be of class logical, uint8, uint16, int16, single, or double. +## Conversion of images of class logical is valid, but not the inverse, i.e., +## conversion of images into logical class is not supported. Use of +## @code{im2bw} is recommended for such cases. +## +## If the image is indexed, the last argument may be the string +## @qcode{"indexed"}. An indexed image may not be of class int16 or +## single (see @code{isind} for details). +## +## Details on how the conversion is performed is class dependent, and can +## be seen on the help text of @code{im2double}, @code{im2single}, +## @code{im2uint8}, @code{im2uint16}, and @code{im2int16}. +## +## @seealso{im2bw, im2uint8, im2double, im2int16, im2single, im2uint16} +## @end deftypefn + +function imout = imcast (img, outcls, varargin) + + if (nargin < 2 || nargin > 3) + print_usage (); + elseif (nargin == 3 && ! strcmpi (varargin{1}, "indexed")) + error ("imcast: third argument must be the string \"indexed\""); + endif + + incls = class (img); + if (strcmp (outcls, incls)) + imout = img; + return + endif + + ## we are dealing with indexed images + if (nargin == 3) + if (! isind (img)) + error ("imcast: input should have been an indexed image but it is not."); + endif + + ## Check that the new class is enough to hold all the previous indices + ## If we are converting to floating point, then we don't bother + ## check the range of indices. Also, note that indexed images of + ## integer class are always unsigned. + + ## we will be converting a floating point image to integer class + if (strcmp (outcls, "single") || strcmp (outcls, "double")) + if (isinteger (img)) + imout = cast (img, outcls) +1; + else + imout = cast (img, outcls); + endif + + ## we will be converting an indexed image to integer class + else + if (isinteger (img) && intmax (incls) > intmax (outcls) && max (img(:)) > intmax (outcls)) + error ("imcast: IMG has too many colours '%d' for the range of values in %s", + max (img(:)), outcls); + elseif (isfloat (img)) + imax = max (img(:)) -1; + if (imax > intmax (outcls)) + error ("imcast: IMG has too many colours '%d' for the range of values in %s", + imax, outcls); + endif + img -= 1; + endif + imout = cast (img, outcls); + endif + + ## we are dealing with "normal" images + else + problem = false; # did we found a bad conversion? + switch (incls) + + case {"double", "single"} + switch (outcls) + case "uint8", imout = uint8 (img * 255); + case "uint16", imout = uint16 (img * 65535); + case "int16", imout = int16 (double (img * uint16 (65535)) -32768); + case {"double", "single"}, imout = cast (img, outcls); + otherwise, problem = true; + endswitch + + case {"uint8"} + switch (outcls) + case "double", imout = double (img) / 255; + case "single", imout = single (img) / 255; + case "uint16", imout = uint16 (img) * 257; # 257 comes from 65535/255 + case "int16", imout = int16 ((double (img) * 257) -32768); # 257 comes from 65535/255 + otherwise, problem = true; + endswitch + + case {"uint16"} + switch (outcls) + case "double", imout = double (img) / 65535; + case "single", imout = single (img) / 65535; + case "uint8", imout = uint8 (img / 257); # 257 comes from 65535/255 + case "int16", imout = int16 (double (img) -32768); + otherwise, problem = true; + endswitch + + case {"logical"} + switch (outcls) + case {"double", "single"} + imout = cast (img, outcls); + case {"uint8", "uint16", "int16"} + imout = repmat (intmin (outcls), size (img)); + imout(img) = intmax (outcls); + otherwise + problem = true; + endswitch + + case {"int16"} + switch (outcls) + case "double", imout = (double (img) + 32768) / 65535; + case "single", imout = (single (img) + 32768) / 65535; + case "uint8", imout = uint8 ((double (img) + 32768) / 257); # 257 comes from 65535/255 + case "uint16", imout = uint16 (double (img) + 32768); + otherwise, problem = true; + endswitch + + otherwise + error ("imcast: unknown image of class \"%s\"", incls); + + endswitch + if (problem) + error ("imcast: unsupported TYPE \"%s\"", outcls); + endif + endif + +endfunction + +%!test +%! im = randi ([0 255], 40, "uint8"); +%! assert (imcast (im, "uint8"), im2uint8 (im)) +%! assert (imcast (im, "uint16"), im2uint16 (im)) +%! assert (imcast (im, "single"), im2single (im)) +%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed")) +%! assert (imcast (im, "uint16", "indexed"), im2uint16 (im, "indexed")) +%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed")) + +%!test +%! im = randi ([1 256], 40, "double"); +%! assert (imcast (im, "uint8"), im2uint8 (im)) +%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed")) +%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed")) + +%!test +%! im = randi ([0 65535], 40, "uint16"); +%! assert (imcast (im, "uint8"), im2uint8 (im)) +%! assert (imcast (im, "single"), im2single (im)) +%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed")) + +%!test +%! im = randi ([1 255], 40, "double"); +%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed")) +%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed")) + +%!test +%! im = rand (40); +%! assert (imcast (im, "uint8"), im2uint8 (im)) + +%!error <unknown image of class> imcast (randi (127, 40, "int8"), "uint8") +%!error <unsupported TYPE> imcast (randi (255, 40, "uint8"), "uint32") +%!error <unsupported TYPE> imcast (randi (255, 40, "uint8"), "not a class") +%!error <range of values> imcast (randi ([0 65535], 40, "uint16"), "uint8", "indexed") +
--- a/inst/imnoise.m +++ b/inst/imnoise.m @@ -105,16 +105,13 @@ endswitch if (fix_class) - ## we probably should do this in a safer way... but hardcoding the list of - ## im2xxxx functions might not be a good idea since it then it requires to - ## be added here if a new im2xxx function is implemented - A = feval (["im2" in_class], A); + 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 im2XX functions already take care of such - ## adjustemt + ## 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
deleted file mode 100644 --- a/inst/impad.m +++ /dev/null @@ -1,180 +0,0 @@ -## Copyright (C) 2000 Teemu Ikonen <tpikonen@pcu.helsinki.fi> -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU General Public License as published by the Free Software -## Foundation; either version 3 of the License, or (at your option) any later -## version. -## -## This program is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, see <http://www.gnu.org/licenses/>. - -## -*- texinfo -*- -## @deftypefn {Function File} {} impad(@var{A}, @var{xpad}, @var{ypad}, [@var{padding}, [@var{const}]]) -## Pad (augment) a matrix for application of image processing algorithms. -## -## This function has been deprecated in favor of @code{padarray} and will be -## removed from future versions of image package. Notes when making -## the conversion: -## -## @itemize @bullet -## @item -## padarray will take the order of the padding arguments by order of dimension, -## i.e., swap @var{xpad} and @var{ypad}. Use -## @code{padarray (@var{A}, [@var{ypad} @var{xpad}], @dots{})} -## -## @item -## There is no @qcode{"ones"} as @var{padding} option. Simply set the value -## 1 as padding value. -## -## @item -## If @var{xpad} and @var{ipad} are 2 element vectors with different values, -## they will need to be replaced by two calls to padarray as follow: -## @group -## @example -## B = padarray (A, [ypad(1) xpad(1)], "pre"); -## B = padarray (B, [ypad(2) xpad(2)], "post"); -## @end example -## @end group -## -## @item -## The @qcode{"reflect"} @var{padding} option of @code{padarray} is different -## from @code{impad}. Use @qcode{"circular"} instead. -## @end itemize -## -## Pads the input image @var{A} with @var{xpad}(1) elements from left, -## @var{xpad}(2), elements from right, @var{ypad}(1) elements from above -## and @var{ypad}(2) elements from below. -## Values of padding elements are determined from the optional arguments -## @var{padding} and @var{const}. @var{padding} is one of -## -## @table @samp -## @item "zeros" -## pad with zeros (default) -## -## @item "ones" -## pad with ones -## -## @item "constant" -## pad with a value obtained from the optional fifth argument const -## -## @item "symmetric" -## pad with values obtained from @var{A} so that the padded image mirrors -## @var{A} starting from edges of @var{A} -## -## @item "reflect" -## same as symmetric, but the edge rows and columns are not used in the padding -## -## @item "replicate" -## pad with values obtained from A so that the padded image -## repeates itself in two dimensions -## -## @end table -## @end deftypefn - -## A nice test matrix for padding: -## A = 10*[1:5]' * ones(1,5) + ones(5,1)*[1:5] - -function retim = impad(im, xpad, ypad, padding = "zeros", const = 1) - - persistent warned = false; - if (! warned) - warned = true; - ## We are deprecating impad because padarray already does the same thing - ## (we don't want multiple functions with the same purpose), padarray - ## already exists in Matlab so it must stay, padarray works for matrices - ## with any number of dimensions, and is now also working faster. - warning ("Octave:deprecated-function", - "`impad' has been deprecated in favor of `padarray (IM, [YPAD XPAD], \"PADDING\")'. This function will be removed from future versions of the `image' package"); - endif - - - ## Input checking - if (!ismatrix(im) || ndims(im) < 2 || ndims(im) > 3) - error("impad: first input argument must be an image"); - endif - if (isscalar(xpad) && xpad >= 0) - xpad(2) = xpad; - elseif (!isreal(xpad) || numel(xpad) != 2 || any(xpad < 0)) - error("impad: xpad must be a positive scalar or 2-vector"); - endif - if (isscalar(ypad) && ypad >= 0) - ypad(2) = ypad; - elseif (!isreal(ypad) || numel(ypad) != 2 || any(ypad < 0)) - error("impad: ypad must be a positive scalar or 2-vector"); - endif - if (!isscalar(const)) - error("impad: fifth input argument must be a scalar"); - endif - - origx = size(im,2); - origy = size(im,1); - retx = origx + xpad(1) + xpad(2); - rety = origy + ypad(1) + ypad(2); - cl = class(im); - - for iter = size(im,3):-1:1 - A = im(:,:,iter); - switch (lower(padding)) - case "zeros" - retval = zeros(rety, retx, cl); - retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A; - case "ones" - retval = ones(rety, retx, cl); - retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A; - case "constant" - retval = const.*ones(rety, retx, cl); - retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A; - case "replicate" - y1 = origy-ypad(1)+1; - x1 = origx-xpad(1)+1; - if (y1 < 1 || x1 < 1 || ypad(2) > origy || xpad(2) > origx) - error("impad: too large padding for this padding type"); - else - yrange1 = y1:origy; - yrange2 = 1:ypad(2); - xrange1 = x1:origx; - xrange2 = 1:xpad(2); - retval = [ A(yrange1, xrange1), A(yrange1, :), A(yrange1, xrange2); - A(:, xrange1), A, A(:, xrange2); - A(yrange2, xrange1), A(yrange2, :), A(yrange2, xrange2) ]; - endif - case "symmetric" - y2 = origy-ypad(2)+1; - x2 = origx-xpad(2)+1; - if (ypad(1) > origy || xpad(1) > origx || y2 < 1 || x2 < 1) - error("impad: too large padding for this padding type"); - else - yrange1 = 1 : ypad(1); - yrange2 = y2 : origy; - xrange1 = 1 : xpad(1); - xrange2 = x2 : origx; - retval = [ fliplr(flipud(A(yrange1, xrange1))), flipud(A(yrange1, :)), fliplr(flipud(A(yrange1, xrange2))); - fliplr(A(:, xrange1)), A, fliplr(A(:, xrange2)); - fliplr(flipud(A(yrange2, xrange1))), flipud(A(yrange2, :)), fliplr(flipud(A(yrange2, xrange2))) ]; - endif - case "reflect" - y2 = origy-ypad(2); - x2 = origx-xpad(2); - if (ypad(1)+1 > origy || xpad(1)+1 > origx || y2 < 1 || x2 < 1) - error("impad: too large padding for this padding type"); - else - yrange1 = 2 : ypad(1)+1; - yrange2 = y2 : origy-1; - xrange1 = 2 : xpad(1)+1; - xrange2 = x2 : origx-1; - retval = [ fliplr(flipud(A(yrange1, xrange1))), flipud(A(yrange1, :)), fliplr(flipud(A(yrange1, xrange2))); - fliplr(A(:, xrange1)), A, fliplr(A(:, xrange2)); - fliplr(flipud(A(yrange2, xrange1))), flipud(A(yrange2, :)), fliplr(flipud(A(yrange2, xrange2))) ]; - endif - otherwise - error("impad: unknown padding type"); - endswitch - - retim(:,:,iter) = retval; - endfor -endfunction
--- a/inst/imperspectivewarp.m +++ b/inst/imperspectivewarp.m @@ -41,8 +41,7 @@ ## @end table ## ## All values of the result that fall outside the original image will -## be set to @var{extrapval}. For images of class @code{double} @var{extrapval} -## defaults to @code{NA} and for other classes it defaults to 0. +## be set to @var{extrapval}. The default value of @var{extrapval} is 0. ## ## The optional output @var{valid} is a matrix of the same size as @var{warped} ## that contains the value 1 in pixels where @var{warped} contains an interpolated @@ -50,7 +49,7 @@ ## @seealso{imremap, imrotate, imresize, imshear, interp2} ## @end deftypefn -function [warped, valid] = imperspectivewarp(im, P, interp = "linear", bbox = "loose", extrapolation_value = NA) +function [warped, valid] = imperspectivewarp(im, P, interp = "linear", bbox = "loose", extrapolation_value = 0) if (nargin < 2 || nargin > 5) print_usage ();
--- a/inst/imremap.m +++ b/inst/imremap.m @@ -37,8 +37,7 @@ ## @code{linear}) are also supported. ## ## All values of the result that fall outside the original image will -## be set to @var{extrapval}. For images of class @code{double} @var{extrapval} -## defaults to @code{NA} and for other classes it defaults to 0. +## be set to @var{extrapval}. The default value of @var{extrapval} is 0. ## ## The optional output @var{valid} is a matrix of the same size as @var{warped} ## that contains the value 1 in pixels where @var{warped} contains an interpolated @@ -46,7 +45,7 @@ ## @seealso{imperspectivewarp, imrotate, imresize, imshear, interp2} ## @end deftypefn -function [warped, valid] = imremap(im, XI, YI, interp = "linear", extrapval = NA) +function [warped, valid] = imremap(im, XI, YI, interp = "linear", extrapval = 0) if (nargin < 3 || nargin > 5) print_usage (); @@ -63,10 +62,10 @@ ## Interpolate if (size (im, 3) == 1) # Gray - warped = grayinterp(im, XI, YI, interp, NA); + warped = grayinterp(im, XI, YI, interp, 0); else # rgb image for i = 3:-1:1 - warped(:,:,i) = grayinterp(im(:,:,i), XI, YI, interp, NA); + warped(:,:,i) = grayinterp(im(:,:,i), XI, YI, interp, 0); endfor endif valid = !isna(warped); @@ -79,9 +78,9 @@ function [warped, valid] = grayinterp(im, XI, YI, interp, extrapval) if (strcmp(interp, "cubic")) - warped = graybicubic(double(im), XI, YI, NA); + warped = graybicubic(double(im), XI, YI, 0); else - warped = interp2(double(im), XI, YI, interp, NA); + warped = interp2(double(im), XI, YI, interp, 0); endif valid = !isna(warped); warped(!valid) = extrapval; @@ -97,7 +96,7 @@ ## @seealso{interp2} ## @end deftypefn -function ZI = graybicubic (Z, XI, YI, extrapval = NA) +function ZI = graybicubic (Z, XI, YI, extrapval = 0) ## Allocate output [X, Y] = meshgrid(1:columns(Z), 1:rows(Z));
--- a/inst/imrotate.m +++ b/inst/imrotate.m @@ -41,8 +41,7 @@ ## @end itemize ## ## @var{extrapval} sets the value used for extrapolation. The default value -## is @code{NA} for images represented using doubles, and 0 otherwise. -## This argument is ignored of Fourier interpolation is used. +## is 0. This argument is ignored of Fourier interpolation is used. ## ## Output parameters: ## @@ -57,7 +56,7 @@ ## not available if Fourier interpolation is used. ## @end deftypefn -function [imgPost, H, valid] = imrotate (imgPre, thetaDeg, interp = "nearest", bbox = "loose", extrapval = NA) +function [imgPost, H, valid] = imrotate (imgPre, thetaDeg, interp = "nearest", bbox = "loose", extrapval = 0) if (nargin < 2 || nargin > 5) print_usage (); @@ -154,10 +153,7 @@ endif valid = NA; - ## we probably should do this in a safer way... but hardcoding the list of - ## im2xxxx functions might not be a good idea since it then it requires to - ## be added here if a new im2xxx function is implemented - imgPost = feval (["im2" in_class], imgPost); + imgPost = imcast (imgPost, in_class); else [imgPost, valid] = imperspectivewarp(imgPre, H, interp, bbox, extrapval); endif
deleted file mode 100644 --- a/inst/imrotate_Fourier.m +++ /dev/null @@ -1,37 +0,0 @@ -## Copyright (C) 2002 Jeff Orchard <jorchard@cs.uwaterloo.ca> -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU General Public License as published by the Free Software -## Foundation; either version 3 of the License, or (at your option) any later -## version. -## -## This program is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, see <http://www.gnu.org/licenses/>. - -## -*- texinfo -*- -## @deftypefn {Function File} {} imrotate_Fourier (@var{M}, @var{theta}, @var{method}, @var{bbox}) -## Rotation of a 2D matrix. -## -## @emph{This function has been deprecated and will be removed. Instead, use -## @code{imrotate} and select the @code{fourier} method. This function is -## actually just a wrapper to that function.} -## -## @seealso{imrotate} -## @end deftypefn - -function fs = imrotate_Fourier (f, theta, method = "fourier", bbox = "loose") - - persistent warned = false; - if (! warned) - warned = true; - warning ("Octave:deprecated-function", - "`imrotate_Fourier' has been deprecated in favor of `imrotate (M, theta, \"fourier\")'. This function will be removed from future versions of the `image' package"); - endif - fs = imrotate (f, theta, "fourier", bbox) - -endfunction
deleted file mode 100644 --- a/inst/iptchecknargin.m +++ /dev/null @@ -1,81 +0,0 @@ -## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com> -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU General Public License as published by the Free Software -## Foundation; either version 3 of the License, or (at your option) any later -## version. -## -## This program is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, see <http://www.gnu.org/licenses/>. - -## -*- texinfo -*- -## @deftypefn {Function File} {} iptchecknargin (@var{low}, @var{high}, @var{in}, @var{func_name}) -## Checks for correct number of arguments. -## -## This function has been deprecated. For an exact replacement, use -## @code{narginchk (@var{low}, @var{high})} instead. Alternatively, -## @code{print_usage} is able to provide an even better error message -## provided that there is documentation for the function: -## -## @example -## @group -## if (nargin < min_inputs || nargin > max_inputs) -## print_usage (); -## endif -## @end group -## @end example -## -## This function returns an error unless @var{in} is between the values of -## @var{low} and @var{high}. It does nothing otherwise. They all must be non -## negative scalar integers. @var{high} can also be Inf. -## -## @var{func_name} is the name of the function to be used on the error message. -## -## @seealso{error, nargin, nargout, narginchk, nargoutchk} -## @end deftypefn - -function iptchecknargin (low, high, in, func_name) - - persistent warned = false; - if (! warned) - warned = true; - warning ("Octave:deprecated-function", - "iptchecknargin is obsolete and will be removed from a future version of the image package, please use narginchk instead"); - endif - - if (nargin != 4) - print_usage; - elseif (!isnumeric (low) || !isscalar (low) || !isreal (low) || low < 0 || !isfinite (low) || rem (low, 1) != 0) - error ("Argument 'low' must be a non-negative scalar integer"); - elseif (!isnumeric (high) || !isscalar (high) || !isreal (high) || low < 0 || (isfinite (high) && rem (low, 1) != 0)) - error ("Argument 'high' must be a non-negative scalar integer or Inf"); - elseif (!isnumeric (in) || !isscalar (in) || !isreal (in) || in < 0 || !isfinite (in) || rem (in, 1) != 0) - error ("Argument 'in' must be a non-negative scalar integer"); - elseif (!ischar (func_name)) - error ("Argument 'func_name' must be a string"); - elseif (low > high) - error ("Minimun number of arguments cannot be larger than maximum number of arguments") - endif - - ## error ends in \n so the back trace of the error is not show. This is on - ## purpose since the whole idea of this function is already to give a properly - ## formatted error message - if (in < low) - error ("Function %s expected at least %d input arguments(s) but was called instead with %d input argument(s).\n", ... - func_name, low, in); - elseif (in > high) - error ("Function %s expected at most %d input argument(s) but was called instead with %d input argument(s).\n", ... - func_name, high, in); - endif - -endfunction - -%!test ('iptchecknargin (0, 2, 1, "func")'); # check simple works -%!test ('iptchecknargin (0, Inf, 1, "func")'); # check Inf on max -%!fail ('iptchecknargin (3, 2, 1, "func")'); # check fail min >max -%!fail ('iptchecknargin (2, 3, 1, "func")'); # check fail in out of range
deleted file mode 100644 --- a/inst/iptcheckstrs.m +++ /dev/null @@ -1,76 +0,0 @@ -## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com> -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU General Public License as published by the Free Software -## Foundation; either version 3 of the License, or (at your option) any later -## version. -## -## This program is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, see <http://www.gnu.org/licenses/>. - -## -*- texinfo -*- -## @deftypefn {Function File} {@var{valid} =} iptcheckstrs (@var{in}, @var{valid_str}, @var{func_name}, @var{var_name}, @var{pos}) -## Check if argument is a valid string. -## -## This function has been deprecated. Use @code{validatestring} instead. -## -## If @var{in} is not a string, present in the cell array of strings -## @var{valid_str} gives a properly formatted error message. Otherwise, -## @var{valid} is the matched string. The performed matching is case-insensitive. -## -## @var{func_name} is the name of the function to be used on the error message, -## @var{var_name} the name of the argument being checked (for the error message), -## and @var{pos} the position of the argument in the input. -## -## @seealso{strcmp, strcmpi, find, validatestring} -## @end deftypefn - -function out = iptcheckstrs (in, valid_str, func_name, var_name, pos) - - persistent warned = false; - if (! warned) - warned = true; - warning ("Octave:deprecated-function", - "iptcheckstrs is obsolete and will be removed from a future version of the image package, please use validatestring instead"); - endif - - if (nargin != 5) - print_usage; - elseif (!ischar (in)) - error ("Argument 'in' must be a string."); - elseif (!iscellstr (valid_str)) - error ("Argument 'valid_str' must be a cell array of strings."); - elseif (!ischar (func_name)) - error ("Argument 'func_name' must be a string"); - elseif (!ischar (var_name)) - error ("Argument 'var_name' must be a string"); - elseif (!isnumeric (pos) || !isscalar (pos) || !isreal (pos) || pos <= 0 || rem (pos, 1) != 0) - error ("Argument 'pos' must be a real positive integer"); - endif - - idx = find (strcmpi (valid_str, in) == 1, 1, "first"); - - ## error ends in \n so the back trace of the error is not show. This is on - ## purpose since the whole idea of this function is already to give a properly - ## formatted error message - if (isempty (idx)) - valid_str = cellfun (@(x) cstrcat (x, ", "), valid_str, "UniformOutput", false); - valid_str = cstrcat (valid_str{:}); - error("Function %s expected its %s input argument, %s, to match one of these strings:\n\ - %s\n\ - The input, '%s', did not match any of the valid strings.\n", - func_name, iptnum2ordinal (pos), var_name, valid_str(1:end-2), in); - else - out = valid_str{idx}; - endif - -endfunction - -%!assert (iptcheckstrs ("two", {"one", "two", "three"}, "func", "var", 1) == "two" ); # check simple works -%!assert (iptcheckstrs ("Two", {"one", "two", "three"}, "func", "var", 1) == "two" ); # check case insensitive -%!fail ('iptcheckstrs ("four", {"one", "two", "three"}, "func", "var", 1)'); # check failure if not found
--- a/inst/normxcorr2.m +++ b/inst/normxcorr2.m @@ -1,4 +1,4 @@ -## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com> +## Copyright (C) 2014 Benjamin Eltzner <b.eltzner@gmx.de> ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software @@ -14,27 +14,127 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} {} normxcorr2 (@var{template}, @var{img}) -## @deftypefnx {Function File} {} normxcorr2 (@var{template}, @var{img}) -## Compute the normalized 2D cross-correlation. +## @deftypefn {Function File} {} normxcorr2 (@var{template}, @var{img}) +## Compute normalized cross-correlation. +## +## Returns the the cross-correlation coefficient of matrices @var{template} +## and @var{img}, a matrix of the same size as @var{img} with values ranging +## between -1 and 1. +## +## Normalized correlation is mostly used for template matching, finding an +## object or pattern, @var{template}, withing an image @var{img}. Higher +## values on the output show their locations, even in the presence of noise. ## -## Returns the normalized cross correlation matrix of @var{template} and -## @var{img} so that a value of 1 corresponds to the positions of @var{img} that -## match @var{template} perfectly. +## @group +## @example +## img = randi (255, 600, 400); +## template = imnoise (img(100:150, 300:320), "gaussian"); +## cc = normxcorr2 (template, img); +## [r, c] = find (cc == max (cc(:))) +## @result{r} 150 +## @result{c} 320 +## @end example +## @end group ## -## @emph{Note}: this function exists only for @sc{matlab} compatibility and is -## just a wrapper to the @code{coeff} option of @code{xcorr2} with the arguments -## inverted. See the @code{xcorr2} documentation for more details. Same results -## can be obtained with @code{xcorr2 (img, template, "coeff")} +## Despite the function name, this function will accept input with an arbitrary +## number of dimensions. ## -## @seealso{conv2, corr2, xcorr2} +## @seealso{conv2, convn, corr2, xcorr, xcorr2} ## @end deftypefn -function cc = normxcorr2 (temp, img) +## Author: Benjamin Eltzner <b.eltzner@gmx.de> + +function c = normxcorr2 (a, b) if (nargin != 2) print_usage (); - elseif (rows (temp) > rows (img) || columns (temp) > columns (img)) - error ("normxcorr2: template must be same size or smaller than image"); + endif + + ## If this happens, it is probably a mistake + if (ndims (a) > ndims (b) || any (postpad (size (a), ndims (b)) > size (b))) + warning ("normxcorr2: TEMPLATE larger than IMG. Arguments may be swapped."); endif - cc = xcorr2 (img, temp, "coeff"); + + a = double (a) - mean (a(:)); + b = double (b) - mean (b(:)); + + a1 = ones (size (a)); + ar = reshape (a(end:-1:1), size (a)); + + c = convn (b, conj (ar)); + b = convn (b.^2, a1) .- convn (b, a1).^2 ./ (prod (size (a))); + a = sumsq (a(:)); + c = reshape (c ./ sqrt (b * a), size (c)); + + c(isnan (c)) = 0; endfunction + +%!function offsets = get_max_offsets (c) +%! l = find (c == max (c(:))); +%! offsets = nthargout (1:ndims (c), @ind2sub, size (c), l); +%!endfunction + +## test basic usage +%!test +%! row_shift = 18; +%! col_shift = 20; +%! a = randi (255, 30, 30); +%! b = a(row_shift-10:row_shift, col_shift-7:col_shift); +%! c = normxcorr2 (b, a); +%! ## should return exact coordinates +%! assert (get_max_offsets (c), {row_shift col_shift}); +%! +%! ## Even with some small noise, should return exact coordinates +%! b = imnoise (b, "gaussian"); +%! c = normxcorr2 (b, a); +%! assert (get_max_offsets (c), {row_shift col_shift}); + +## The value for a "perfect" match should be 1. However, machine precision +## creeps in most of the times. +%!test +%! a = rand (10, 10); +%! c = normxcorr2 (a(5:7, 6:9), a); +%! assert (c(7, 9), 1, eps*2); + +## coeff of autocorrelation must be same as negative of correlation +## by additive inverse +%!test +%! a = 10 * randn (100, 100); +%! auto = normxcorr2 (a, a); +%! add_in = normxcorr2 (a, -a); +%! assert (auto, -add_in); + +## Normalized correlation should be independent of scaling and shifting +## up to rounding errors +%!test +%! a = 10 * randn (50, 50); +%! b = 10 * randn (100, 100); +%! do +%! scale = 100 * rand (); +%! until (scale != 0) +%! +%! assert (max ((normxcorr2 (scale*a,b) .- normxcorr2 (a,b))(:)), 0, 1e-10); +%! assert (max ((normxcorr2 (a,scale*b) .- normxcorr2 (a,b))(:)), 0, 1e-10); +%! +%! a_shift1 = a .+ scale * ones (size (a)); +%! b_shift1 = b .+ scale * ones (size (b)); +%! a_shift2 = a .- scale * ones (size (a)); +%! b_shift2 = b .- scale * ones (size (b)); +%! assert (max ((normxcorr2 (a_shift1,b) .- normxcorr2 (a,b))(:)), 0, 1e-10); +%! assert (max ((normxcorr2 (a,b_shift1) .- normxcorr2 (a,b))(:)), 0, 1e-10); +%! assert (max ((normxcorr2 (a_shift2,b) .- normxcorr2 (a,b))(:)), 0, 1e-10); +%! assert (max ((normxcorr2 (a,b_shift2) .- normxcorr2 (a,b))(:)), 0, 1e-10); + +## test n dimensional input +%!test +%! a = randi (100, 15, 15, 15); +%! c = normxcorr2 (a(5:10, 2:6, 3:7), a); +%! assert (get_max_offsets (c), {10 6 7}); +%! +%! a = randi (100, 15, 15, 15); +%! c = normxcorr2 (a(5:10, 2:6, 1:1), a); +%! assert (get_max_offsets (c), {10 6 1}); + +%!warning <swapped> normxcorr2 (rand (20), rand (5)); +%!error normxcorr2 (rand (5)); +%!error normxcorr2 (rand (5), rand (20), 2); +
deleted file mode 100644 --- a/inst/private/im2float.m +++ /dev/null @@ -1,43 +0,0 @@ -## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com> -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 3 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, see <http://www.gnu.org/licenses/>. - -## im2double and im2single are very similar so here's the common code, -## which is prety much all of it. - -function im = im2float (out_class, caller_nargin, im, indexed = false) - - ## Input checking (private function that is used for all im2class functions) - im_class = imconversion (caller_nargin, ["im2" out_class], indexed, im); - - converter = eval (["@" out_class]); - switch im_class - case {"single", "double", "logical"} - if (strcmp (im_class, out_class)) - ## do nothing, return the same - else - im = converter (im); - endif - case {"uint8", "uint16"} - if (indexed) - im = converter (im) + 1; - else - im = converter (im) / converter (intmax (im_class)); - endif - case "int16" - im = (converter (im) + converter (intmax (im_class)) + 1) / converter (intmax ("uint16")); - otherwise - error ("unsupported image class %s", im_class); - endswitch -endfunction
deleted file mode 100644 --- a/inst/private/imconversion.m +++ /dev/null @@ -1,34 +0,0 @@ -## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com> -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 3 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, see <http://www.gnu.org/licenses/>. - -## This is a private fucntion for the common code between the functions that -## convert an input image into another class. Mostly does the input checking for -## all of them and returns the class of the input image - -function im_class = imconversion (nargs, fname, ind, im1) - ## Input checking - if (nargs < 1 || nargs > 2) - print_usage (fname); - elseif (nargs == 2 && (!ischar (ind) || !strcmpi (ind, "indexed"))) - error ("%s: second argument must be a string with the word `indexed'\n", fname); - endif - - if (ind && !isind (im1)) - error ("%s: input should have been an indexed image but it is not.\n", fname); - endif - - im_class = class (im1); - -endfunction
deleted file mode 100644 --- a/inst/readexif.m +++ /dev/null @@ -1,539 +0,0 @@ -## Copyright (C) 2009 Roderick Koehle <koehle@users.sourceforge.net> -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU General Public License as published by the Free Software -## Foundation; either version 3 of the License, or (at your option) any later -## version. -## -## This program is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, see <http://www.gnu.org/licenses/>. - -## -*- texinfo -*- -## @deftypefn {Function File} {@var{exif} =} readexif(@var{filename}, @var{thumbnail}) -## Read EXIF information from JPEG image data. -## -## The exif tag information are returned in the @var{exif} data structure. -## Integer ratios are expressed as column vector. -## For example, a focal number of 2.8 is expressed -## as FNumber=[28; 10]. Otherwise all data are returned by the type -## as specified in the IFD structures. -## -## The filename for the thumbnail image is optional. -## If given, the thumbnail jpeg image will be stored to -## file @var{thumbnail}. -## -## Reference: -## JEITA CP-3451, Exchangeable image file format for digital still cameras: -## Exif Version 2.2 -## -## @seealso{imwrite, imfinfo} -## @end deftypefn - -function exif = readexif(file, thumbnail) - - if (nargin < 1 || nargin > 2) - print_usage; - endif - persistent warned = false; - if (! warned) - warned = true; - warning ("Octave:deprecated-function", - "`readexif' has been deprecated in favor of `imfinfo'. This function will be removed from future versions of the `image' package"); - endif - - - % Enable the debug flag to see more of the JPG sections. - - debug = false; - - [in, msg] = fopen(file); - if (in<0) - error ("readexif: could not open `%s': %s", file, msg); - end - - s = fread(in, 1, 'uint16', 'ieee-be'); - - JPEG.SOI = 0xffd8; - JPEG.APP0 = 0xffe0; - JPEG.APP1 = 0xffe1; - JPEG.APP2 = 0xffe2; - JPEG.DQT = 0xffdb; - JPEG.DHT = 0xffc4; - JPEG.DRI = 0xffdd; - JPEG.SOF = 0xffc0; - JPEG.SOS = 0xffda; - JPEG.EOI = 0xffd9; - - % Stop if no Start of Image found - - if s~=JPEG.SOI - error('JPEG Format error - missing start of image tag.'); - end - - exif = []; - - while ~feof(in) - s = fread(in, 1, 'uint16', 'ieee-be'); - - switch s - case JPEG.SOI - case JPEG.APP0 - l = fread(in, 1, 'uint16', 'ieee-be'); - if debug, printf('APP0: %i\n', l); end - fseek(in, l-2, 'cof'); - case JPEG.APP1 - l = fread(in, 1, 'uint16', 'ieee-be'); - if debug, printf('APP1: %i\n', l); end - app1 = fread(in, l-2, 'uchar'); - if nargin==1 - exif = parseexif(app1); - else - exif = parseexif(app1, thumbnail); - end - % stop reading further, remove following break - % if you want to extend this parser. - break; - case JPEG.APP2 - l = fread(in, 1, 'uint16', 'ieee-be'); - if debug, printf('APP2: %i\n', l); end - fseek(in, l-2, 'cof'); - case JPEG.DQT % define quantization table - l = fread(in, 1, 'uint16', 'ieee-be'); - if debug, printf('DQT: %i\n', l); end; - fseek(in, l-2, 'cof'); - case JPEG.DHT % define huffmann table - l = fread(in, 1, 'uint16', 'ieee-be'); - fseek(in, l-2, 'cof'); - if debug, printf('DHT: %i\n', l); end - case JPEG.DRI % define restart interoperability - l = fread(in, 1, 'uint16', 'ieee-be'); - fseek(in, l-2, 'cof'); - if debug, printf('DRI: %i\n', l); end - case JPEG.SOF % start of frame - l = fread(in, 1, 'uint16', 'ieee-be'); - fseek(in, l-2, 'cof'); - if debug, printf('SOF: %i\n', l); end - case JPEG.SOS % start of scan - l = fread(in, 1, 'uint16', 'ieee-be'); - fseek(in, l-2, 'cof'); - if debug, printf('SOS: %i\n', l); end - - % JPEG compressed data comes here ... - break; - - case JPEG.EOI % end of image - printf('EOI'); - otherwise % Skip unknown tags - l = fread(in, 1, 'uint16', 'ieee-be'); - if debug, printf('TAG %04X: %i\n', s, l); end - fseek(in, l-2, 'cof'); - end - end - - fclose(in); -end - -% -% Parse EXIF APP1 section -% -% This routine will parse the APP1 section of an jpeg image. -% If a filename "thumb" is given, the tumbnail image data -% will be exported to given file. -% -% exif = parseexif(data, thumb) -% -function exif = parseexif(data, thumb) - - id = char(data(1:6).'); - if strncmp(id, ['Exif' 0 0], 6) - - % TIFF header - - byteorder = char(data(7:8).'); - littleendian = strncmp(byteorder, 'II', 2); - bigendian = strncmp(byteorder, 'MM', 2); - - tag42 = intn(data(9:10), bigendian); - offset = intn(data(11:14), bigendian); - - if (~littleendian && ~bigendian) || tag42~=42 - error('invalid TIFF header'); - end - - % IFD fields - - exif = ifdparse(tifftags(), data, offset, bigendian); - else - exif = []; - end - - % export thumbnail image - - if nargin==2 && isfield(exif, 'JPEGInterchangeFormat') - i = exif.JPEGInterchangeFormat; - n = exif.JPEGInterchangeFormatLength; - - jpg = data(7+i:7+i+n-1); - out = fopen(thumb, 'w'); - if (out<0), - error('Cannot open file "%s" for writing thumbnail image.', thumb); - end - fwrite(out, jpg, 'uint8'); - fclose(out); - end -end - -function ifd = ifdparse(dict, data, offset, endian) - - debug = false; - - ifd = []; - - while offset - ifd_fields = intn(data(7+offset+(0:1)), endian); - - if debug, printf('Tag Type Count Offset\n'); end - for i=1:ifd_fields - j = 9+offset+(i-1)*12; - ifd_tag = intn(data( j:j+1), endian); - ifd_type = intn(data(j+2:j+3), endian); - ifd_count = intn(data(j+4:j+7), endian); - ifd_offset = intn(data(j+8:j+11), endian); - - name = ifdtagname(dict, ifd_tag); - - if debug, - printf('%04x %04x %08x %08x %s : ', ifd_tag, ifd_type, ... - ifd_count, ifd_offset, name); - end - if ifd_type>0 - n = ifdsize(ifd_type); - - if n*ifd_count<=4 - value = data(j+8:j+8+n*ifd_count-1); - value = reshape(value, n, ifd_count); - else - a = 7+ifd_offset; - b = 7+ifd_offset+n*ifd_count-1; - if (a>0 && b>0 && a<=length(data) && b<=length(data)) - value = data(7+ifd_offset:7+ifd_offset+n*ifd_count-1); - value = reshape(value, n, ifd_count); - else - value = []; - end - end - end - - switch ifd_type - case 01 % unsigned char - ifd.(name) = uint8(value); - if debug, - printf('%02x ', uint8(value)); - printf('\n'); - end - case 02 % Ascii - ifd.(name) = char(value); - if debug, printf('%s\n', char(value)); end - case 03 % 16 bit unsigned int - ifd.(name) = uintn(value, endian); - if debug - printf('%i ', intn(value), endian); - printf('\n'); - end - case 04 % 32 bit unsigned int - ifd.(name) = uintn(value, endian); - if debug, printf('%i\n', uintn(value, endian)); end - case 05 % 32 bit unsigned rational - ifd.(name) = [uintn(value(1:4,:), endian); uintn(value(5:8,:), endian)]; - if debug, printf('%i/%i\n',uintn(value(1:4), endian),uintn(value(5:8)), endian); end - case 07 % unknown - ifd.(name) = uint8(value); - if debug - printf('%02x ', value); - printf('\n'); - end - case 09 % 32 bit signed int - ifd.(name) = intn(value, endian); - if debug, printf('%i\n', intn(value, endian)); end - case 10 % 32 bit signed rational - ifd.(name) = [intn(value(1:4,:), endian); intn(value(5:8,:), endian)]; - if debug, printf('%i/%i\n',intn(value(1:4), endian),intn(value(5:8)), endian); end - otherwise - printf('%02x ', value); - printf('\n'); - end - - switch ifd_tag - case 0x8769, % Exif Pointer - ifd.(name) = ifdparse(exiftags(), data, ifd_offset, endian); - case 0x8825, % GPS Pointer - ifd.(name) = ifdparse(gpstags(), data, ifd_offset, endian); - case 0xa005 % Interoperatibility Pointer - ifd.(name) = ifdparse(dict, data, ifd_offset, endian); -% case 0x927c % Makernotes -% ifd.(name) = ifdparse([], data, ifd_offset, endian); - otherwise - end - end - j = 9+offset+ifd_fields*12; - ifd_next = intn(data(j:j+3), endian); - - offset = ifd_next; - end -end - -% -% Return bytelength for respective IFD type -% -function n = ifdsize(ifd_type) - switch ifd_type - case {1, 2, 7}, n = 1; - case 03 , n = 2; - case {4, 9} , n = 4; - case {5, 10} , n = 8; - otherwise , n = 1; - end -end - - -% -% Convert little endian character vector to integer -% -function y = intn(x, bigendian) - if bigendian - y = polycol(x, int32(256)); - else - y = polycol(flipud(x), int32(256)); - end -end - -function y = uintn(x, bigendian) - if bigendian - y = polycol(x, uint32(256)); - else - y = polycol(flipud(x), uint32(256)); - end -end - -% -% Use own polyval that works with integers, -% it evaluates the number polygon columnwise. -% -% number = polycol(digits, base) -% -function y = polycol(c, x) - y = c(1,:); - for i=2:size(c, 1) - y = y.*x+c(i,:); - end -end - -% -% Query EXIF IFD tagname -% -% Unfortunately, neither MATLAB nor Octave provide a hash functionality, -% so use structures as hash. -% -function name = ifdtagname(dict, key) - k = sprintf('K%04X', key); - if isfield(dict, k) - name = dict.(k); - else - name = sprintf('tag%04X', key); - end -end - -% -% Primary image IFD tags according to Exif 2.2 -% -function dict = tifftags() - t = { - - % TIFF Tags according to EXIF2.2, additional baseline TIFF tags are marked by a '%' - - '0FE' 'NewSubfileType' % - '0FF' 'SubfileType' % - '100' 'ImageWidth' - '101' 'ImageLength' - '102' 'BitsPerSample' - '103' 'Compression' - '106' 'PhotometricInterpretation' - '108' 'CellWidth' % - '109' 'CellLength' % - '10A' 'FillOrder' % - '10E' 'ImageDescription' - '10F' 'Make' - '110' 'Model' - '111' 'StripOffsets' - '112' 'Orientation' - '115' 'SamplesPerPixel' - '116' 'RowsPerStrip' - '117' 'StripByteCounts' - '118' 'MinSampleValue' % - '119' 'MaxSampleValue' % - '11A' 'XResolution' - '11B' 'YResolution' - '11C' 'PlanarConfiguration' - '120' 'FreeOffsets' % - '121' 'FreeByteCounts' % - '122' 'GrayResponseUnit' % - '123' 'GrayResponseCurve' % - '128' 'ResolutionUnit' - '12D' 'TransferFunction' - '131' 'Software' - '132' 'DateTime' - '13B' 'Artist' - '13C' 'HostComputer' % - '13E' 'WhitePoint' - '13F' 'PrimaryChromaticities' - '140' 'ColorMap' % - '152' 'ExtraSamples' % - '201' 'JPEGInterchangeFormat' - '202' 'JPEGInterchangeFormatLength' - '211' 'YCbCrCoefficients' - '212' 'YCbCrSubSampling' - '213' 'YCbCrPositioning' - '214' 'ReferenceBlackWhite' - '8298' 'Copyright' - '8769' 'Exif IFD Pointer' - '8825' 'GPS Info IFD Pointer' - }; - - dict = []; - for i=1:size(t,1) - key = sprintf('K%04X', hex2dec(t{i,1})); - value = t{i,2}; - dict.(key) = strrep(value, ' ', '_'); - end -end - -% -% EXIF private tags -% -function dict = exiftags() - t = { - - % EXIF Tags - - '829A' 'ExposureTime' - '829D' 'FNumber' - '8822' 'ExposureProgram' - '8824' 'SpectralSensitivity' - '8827' 'ISOSpeedRatings' - '8828' 'OECF' - '9000' 'ExifVersion' - '9003' 'DateTimeOriginal' - '9004' 'DateTimeDigitized' - '9101' 'ComponentsConfiguration' - '9102' 'CompressedBitsPerPixel' - '9201' 'ShutterSpeedValue' - '9202' 'ApertureValue' - '9203' 'BrightnessValue' - '9204' 'ExposureBiasValue' - '9205' 'MaxApertureValue' - '9206' 'SubjectDistance' - '9207' 'MeteringMode' - '9208' 'LightSource' - '9209' 'Flash' - '920A' 'FocalLength' - '9214' 'SubjectArea' - '927C' 'MakerNote' - '9286' 'UserComment' - '9290' 'SubsecTime' - '9291' 'SubsecTimeOriginal' - '9292' 'SubsecTimeDigitized' - 'A000' 'FlashpixVersion' - 'A001' 'ColorSpace' - 'A002' 'PixelXDimension' - 'A003' 'PixelYDimension' - 'A004' 'RelatedSoundFile' - 'A005' 'Interoperatibility IFD Pointer' - 'A20B' 'FlashEnergy' - 'A20C' 'SpatialFrequencyResponse' - 'A20E' 'FocalPlaneXResolution' - 'A20F' 'FocalPlaneYResolution' - 'A210' 'FocalPlaneResolutionUnit' - 'A214' 'SubjectLocation' - 'A215' 'ExposureIndex' - 'A217' 'SensingMethod' - 'A300' 'FileSource' - 'A301' 'SceneType' - 'A302' 'CFAPattern' - 'A401' 'CustomRendered' - 'A402' 'ExposureMode' - 'A403' 'WhiteBalance' - 'A404' 'DigitalZoomRatio' - 'A405' 'FocalLengthIn35mmFilm' - 'A406' 'SceneCaptureType' - 'A407' 'GainControl' - 'A408' 'Contrast' - 'A409' 'Saturation' - 'A40A' 'Sharpness' - 'A40B' 'DeviceSettingDescription' - 'A40C' 'SubjectDistanceRange' - 'A420' 'ImageUniqueID' - - % Interoperatibility tags - - '001' 'InteroperatibilityIndex' - '002' 'InteroperatibilityVersion' - '1000' 'RelatedImageFileFormat' - '1001' 'RelatedImageWidth' - '1002' 'RelatedImageLength' - }; - - dict = []; - for i=1:size(t,1) - key = sprintf('K%04X', hex2dec(t{i,1})); - value = t{i,2}; - dict.(key) = strrep(value, ' ', '_'); - end -end - -% -% EXIF GPS tags -% -function dict = gpstags() - t = { - 0 'GPSVersionID' - 1 'GPSLatitudeRef' - 2 'GPSLatitude' - 3 'GPSLongitudeRef' - 4 'GPSLongitude' - 5 'GPSAltitudeRef' - 6 'GPSAltitude' - 7 'GPSTimeStamp' - 8 'GPSSatellites' - 9 'GPSStatus' - 10 'GPSMeasureMode' - 11 'GPSDOP' - 12 'GPSSpeedRef' - 13 'GPSSpeed' - 14 'GPSTrackRef' - 15 'GPSTrack' - 16 'GPSImgDirectionRef' - 17 'GPSImgDirection' - 18 'GPSMapDatum' - 19 'GPSDestLatitudeRef' - 20 'GPSDestLatitude' - 21 'GPSDestLongitudeRef' - 22 'GPSDestLongitude' - 23 'GPSDestBearingRef' - 24 'GPSDestBearing' - 25 'GPSDestDistanceRef' - 26 'GPSDestDistance' - }; - - dict = []; - for i=1:size(t,1) - key = sprintf('K%04X', t{i,1}); - value = t{i,2}; - dict.(key) = strrep(value, ' ', '_'); - end -end -
--- a/inst/regionprops.m +++ b/inst/regionprops.m @@ -348,6 +348,8 @@ error ("regionprops: unsupported property '%s'", property); endswitch endfor + ## Matlab returns a column vector struct array. + retval = retval(:); endfunction function retval = local_area (bw) @@ -423,3 +425,5 @@ %! 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 (size (regionprops ([1 0 0; 0 0 2], "Area")), [2, 1]) \ No newline at end of file
deleted file mode 100644 --- a/inst/uintlut.m +++ /dev/null @@ -1,51 +0,0 @@ -## Copyright (C) 2004 Josep Mones i Teixidor <jmones@puntbarra.com> -## -## This program is free software; you can redistribute it and/or modify it under -## the terms of the GNU General Public License as published by the Free Software -## Foundation; either version 3 of the License, or (at your option) any later -## version. -## -## This program is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -## details. -## -## You should have received a copy of the GNU General Public License along with -## this program; if not, see <http://www.gnu.org/licenses/>. - -## -*- texinfo -*- -## @deftypefn {Function File} {@var{B} =} uintlut (@var{A}, @var{LUT}) -## Computes matrix B by using A as an index to lookup table LUT. -## -## This function has been deprecated. Use @code{intlut} instead. -## -## B = uintlut(A, LUT) calculates a matrix B by using @var{LUT} as a -## lookup table indexed by values in @var{A}. -## -## B class is the same as @var{LUT}. -## @end deftypefn - -function B = uintlut (A, LUT) - if (nargin != 2) - print_usage; - endif - persistent warned = false; - if (! warned) - warned = true; - warning ("Octave:deprecated-function", - ["`uintlut' has been deprecated in favor of `intlut'. This " ... - "function will be removed from future versions of the `image'" ... - " package"]); - endif - - B = LUT(A); -endfunction - -%!demo -%! uintlut(uint8([1,2,3,4]),uint8([255:-1:0])); -%! % Returns a uint8 array [255,254,253,252] - -%!assert(uintlut(uint8([1,2,3,4]),uint8([255:-1:0])), uint8([255:-1:252])); -%!assert(uintlut(uint16([1,2,3,4]),uint16([255:-1:0])), uint16([255:-1:252])); -%!assert(uintlut(uint32([1,2,3,4]),uint32([255:-1:0])), uint32([255:-1:252])); -%!assert(uintlut(uint64([1,2,3,4]),uint64([255:-1:0])), uint64([255:-1:252]));