Mercurial > hg > octave-image
changeset 522:1334711928b6
image arithmetic functions: more matlab compatibility, test cases, better help text, bug fixes
author | carandraug |
---|---|
date | Sat, 10 Dec 2011 23:06:18 +0000 |
parents | ea4f9b00355e |
children | 19957ee273c5 |
files | inst/imadd.m inst/imdivide.m inst/immultiply.m inst/imsubtract.m inst/private/imarithmetics.m |
diffstat | 5 files changed, 73 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/inst/imadd.m +++ b/inst/imadd.m @@ -23,7 +23,7 @@ ## 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, the class can be +## 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 @@ -64,3 +64,10 @@ 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
--- a/inst/imdivide.m +++ b/inst/imdivide.m @@ -23,7 +23,7 @@ ## 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, the class can be +## 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 @@ -52,3 +52,10 @@ end_unwind_protect 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
--- a/inst/immultiply.m +++ b/inst/immultiply.m @@ -23,7 +23,7 @@ ## multiplie 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, the class can be +## 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 @@ -36,10 +36,11 @@ if (nargin < 2 || nargin > 3) print_usage; endif - [img, val] = imarithmetics ("immultiply", img, val, out_class); + [img, val] = imarithmetics ("immultiply", img, val, out_class, nargin); - ## matlab doesn't even gives a warning in this situation, it simply returns - ## a double precision float + ## have to check how matlab behaves in this situtation. Their documentation + ## does not say anything but add and subtract silently ignore it and return + ## double anyway. This may be done in the call to imarithmetics if (nargin > 2 && strcmpi (out_class, "logical")) warning ("Ignoring request to return logical as output of multiplication."); endif @@ -47,3 +48,9 @@ img = img .* val; endfunction + +%!assert (immultiply (uint8 ([255 50]), uint16 ([300 50])), uint8 ([255 255])); # default to first class and truncate +%!assert (immultiply (uint8 ([250 50]), uint16 ([ 3 4]), "uint32"), uint32 ([750 400])); # defining output class works (not in matlab?) +%!assert (immultiply (uint8 ([255 50]), 4), uint8 ([255 200])); # works multiplying by a scalar +%!assert (immultiply (logical ([ 1 0]), uint16 ([300 50])), uint16 ([300 0])); # output class defaults to whatever input is not logical +%!assert (immultiply (logical ([ 1 0]), logical ([ 1 1])), double ([ 1 0])); # tested on matlab for compatibility
--- a/inst/imsubtract.m +++ b/inst/imsubtract.m @@ -23,7 +23,7 @@ ## 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, the class can be +## 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 @@ -32,6 +32,20 @@ ## ## @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{imadd, imcomplement, imdivide, immultiply} ## @end deftypefn @@ -39,7 +53,15 @@ 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. @@ -51,3 +73,11 @@ 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 (uint8 ([23 250]), uint8 ([24 255]), "int8"), int8 ([-1 0])); # signed integers kinda work (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 +%!fail ("imsubtract (uint8 ([23 250]), uint16 ([23 250]))") # input need to have same class
--- a/inst/private/imarithmetics.m +++ b/inst/private/imarithmetics.m @@ -22,7 +22,7 @@ ## original function ## @end deftypefn -function [img, val] = imarithmetics (func, img, val, out_class) +function [img, val] = imarithmetics (func, img, val, out_class, in_args) is_valid = @(x) ((!isnumeric (x) && !islogical (x)) || isempty (x) || issparse (x) || !isreal (x)); @@ -34,9 +34,20 @@ error ("%s: third argument must be a string that specifies the output class", func) endif - if (all (size (img) == size (val)) && strcmpi (class (img), class (val))) + same_size = all (size (img) == size (val)); + if (same_size && strcmpi (class (img), class (val))) [img, val] = convert (out_class, img, val); - elseif (isscalar (val) && isfloat (val)) + + ## multiplication doesn't require same input class and output class defaults to + ## whatever input class is not logical (first img, then val) + elseif (strcmp (func, "immultiply") && same_size) + if (in_args > 2) + ## user defined, do nothing + elseif (islogical (img) && !islogical (val)) + out_class = class (val); + endif + [img, val] = convert (out_class, img, val); + elseif (isscalar (val) && isfloat (val) && !strcmp (func, "imabsdiff")) ## according to matlab's documentation, if val is not an image of same size ## and class as img, then it must be a double scalar. But why not also support ## a single scalar and use isfloat? @@ -50,7 +61,7 @@ ## in the case that we only want to convert one matrix, this subfunction is called ## with 2 arguments only. Then, b takes the value of zero so that the call to the ## functions that change the class is insignificant - if (!strcmpi (class (a), out_class)) + if ((nargin == 3 && any (!strcmpi ({class(a), class(b)}, out_class))) || (nargin == 2 && !strcmpi (class (a), out_class))) switch tolower (out_class) case {"logical"} a = logical (a); b = logical (b); case {"uint8"} a = uint8 (a); b = uint8 (b);