1## Copyright (C) 2018 Martin Janda <janda.martin1@gmail.com> 2## 3## This program is free software: you can redistribute it and/or modify it 4## under the terms of the GNU General Public License as published by 5## the Free Software Foundation, either version 3 of the License, or 6## (at your option) any later version. 7## 8## This program is distributed in the hope that it will be useful, but 9## WITHOUT ANY WARRANTY; without even the implied warranty of 10## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11## GNU General Public License for more details. 12## 13## You should have received a copy of the GNU General Public License 14## along with this program. If not, see 15## <https://www.gnu.org/licenses/>. 16 17## -*- texinfo -*- 18## @deftypefn {} {@var{r} =} imref2d 19## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize}) 20## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize}, @var{pixelExtentInWorldX}, @var{pixelExtentInWorldY}) 21## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize}, @var{xWorldLimits}, @var{yWorldLimits}) 22## Reference 2-D image to world coordinates. 23## 24## Creates an imref2d object referencing a 2-D m-by-n image with the size 25## @var{imageSize} to world coordinates. The world extent is either given 26## by @var{xWorldLimits} and @var{yWorldLimits} or computed from 27## @var{pixelExtentInWorldX} and @var{pixelExtentInWorldY}. 28## @var{imageSize} is [2, 2] by default. 29## 30## Intrinsic coordinates are x = 1.0, y = 1.0 in the center of the top left 31## pixel and x = n, y = m in the center of the bottom right pixel. 32## Spatial resolution in each dimension can be different. 33## 34## imref2d object has the following properties: 35## 36## ImageSize - two element integer vector with image height and width 37## in pixels. 38## 39## XWorldLimits - limits of the image along the x-axis in world units 40## specified as a two element real vector @code{[xMin, xMax]}. 41## 42## YWorldLimits - limits of the image along the y-axis in world units 43## specified as a two element real vector @code{[yMin, yMax]}. 44## 45## PixelExtentInWorldX - pixel extent along the x-axis in world units 46## specified as a real scalar. 47## 48## PixelExtentInWorldY - pixel extent along the y-axis in world units 49## specified as a real scalar. 50## 51## ImageExtentInWorldX - image extent along the x-axis in world units 52## specified as a real scalar. 53## 54## ImageExtentInWorldY - image extent along the y-axis in world units 55## specified as a real scalar. 56## 57## XIntrinsicLimits - limits of the image along the x-axis in intrinsic 58## units, equals to @code{[n - 0.5, n + 0.5]}. 59## 60## YIntrinsicLimits - limits of the image along the y-axis in intrinsic 61## units, equals to @code{[m - 0.5, m + 0.5]}. 62## 63## @seealso{imref3d} 64## @end deftypefn 65 66function r = imref2d (imageSize, varargin) 67 if (nargin > 1 && nargin != 3) 68 print_usage(); 69 endif 70 71 if (nargin == 0) 72 imageSize = [2, 2]; 73 endif 74 75 if (nargin > 0) 76 if (length (imageSize) < 2) 77 error ("Octave:invalid-input-arg", ... 78 "ImageSize must have at least two elements"); 79 endif 80 81 validateattributes (imageSize, {"numeric"}, ... 82 {"positive", "integer", "vector"}, "imref2d", "imageSize"); 83 endif 84 85 imageSize = imageSize(1:2); 86 m = imageSize(1); 87 n = imageSize(2); 88 89 if (numel (varargin) == 0) 90 xWorldLimits = [0.5, n + 0.5]; 91 yWorldLimits = [0.5, m + 0.5]; 92 elseif (numel (varargin) == 2) 93 if (isscalar (varargin{1})) 94 validateattributes (varargin{1}, {"numeric"}, ... 95 {"real", "positive", "scalar"}, "imref2d", "pixelExtentInWorldX"); 96 validateattributes (varargin{2}, {"numeric"}, ... 97 {"real", "positive", "scalar"}, "imref2d", "pixelExtentInWorldY"); 98 pixelExtentInWorldX = varargin{1}; 99 pixelExtentInWorldY = varargin{2}; 100 else 101 validateattributes (varargin{1}, {"numeric"}, ... 102 {"real", "increasing", "vector", "size", [1, 2]}, "imref2d", ... 103 "xWorldLimits"); 104 validateattributes (varargin{2}, {"numeric"}, ... 105 {"real", "increasing", "vector", "size", [1, 2]}, "imref2d", ... 106 "yWorldLimits"); 107 xWorldLimits = varargin{1}; 108 yWorldLimits = varargin{2}; 109 endif 110 endif 111 112 if (exist ("pixelExtentInWorldX") && exist ("pixelExtentInWorldY")) 113 imageExtentInWorldX = pixelExtentInWorldX * n; 114 imageExtentInWorldY = pixelExtentInWorldY * m; 115 xWorldLimits = [pixelExtentInWorldX / 2, imageExtentInWorldX + ... 116 pixelExtentInWorldX / 2]; 117 yWorldLimits = [pixelExtentInWorldY / 2, imageExtentInWorldY + ... 118 pixelExtentInWorldY / 2]; 119 elseif (exist ("xWorldLimits") && exist ("yWorldLimits")) 120 imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1); 121 imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1); 122 pixelExtentInWorldX = imageExtentInWorldX / n; 123 pixelExtentInWorldY = imageExtentInWorldY / m; 124 endif 125 126 xIntrinsicLimits = [0.5, n + 0.5]; 127 yIntrinsicLimits = [0.5, m + 0.5]; 128 129 r.ImageSize = imageSize; 130 r.XWorldLimits = xWorldLimits; 131 r.YWorldLimits = yWorldLimits; 132 r.PixelExtentInWorldX = pixelExtentInWorldX; 133 r.PixelExtentInWorldY = pixelExtentInWorldY; 134 r.ImageExtentInWorldX = imageExtentInWorldX; 135 r.ImageExtentInWorldY = imageExtentInWorldY; 136 r.XIntrinsicLimits = xIntrinsicLimits; 137 r.YIntrinsicLimits = yIntrinsicLimits; 138 r = class (r, "imref2d"); 139endfunction 140 141%!error id=Octave:invalid-fun-call imref2d (1, 2, 3, 4) 142%!error id=Octave:invalid-input-arg imref2d (42) 143%!error id=Octave:invalid-input-arg imref2d ([42]) 144%!error id=Octave:expected-integer imref2d ([4.2, 42]) 145%!error id=Octave:expected-positive imref2d ([0, 0]) 146%!error id=Octave:expected-positive imref2d ([-4, 2]) 147%!error id=Octave:expected-positive imref2d ([4, 2], 0, 2) 148%!error id=Octave:expected-positive imref2d ([4, 2], 2, 0) 149%!error id=Octave:expected-real imref2d ([4, 2], j, 2) 150%!error id=Octave:expected-real imref2d ([4, 2], 2, j) 151%!error id=Octave:expected-real imref2d ([4, 2], [j, 2], [3, 4]) 152%!error id=Octave:expected-real imref2d ([4, 2], [1, 2], [j, 4]) 153%!error id=Octave:expected-vector imref2d ([4, 2], [], []) 154%!error id=Octave:expected-vector imref2d ([4, 2], [], [1]) 155%!error id=Octave:expected-scalar imref2d ([4, 2], [1], []) 156%!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [0]) 157%!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [1, 2, 3]) 158%!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2, 3], [1, 2]) 159%!error id=Octave:incorrect-size imref2d ([4, 2], [1; 2], [1, 2]) 160%!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [1; 2]) 161%!error id=Octave:invalid-indexing imref2d().InvalidProperty 162%!error id=Octave:expected-increasing imref2d ([100 200], [1.5 0.5], [2.5 3.5]) 163%!error id=Octave:expected-increasing imref2d ([100 200], [1.5 2.5], [2.5 1.5]) 164 165%!test 166%! r = imref2d; 167%! assert (r.XWorldLimits, [0.5, 2.5]) 168%! assert (r.YWorldLimits, [0.5, 2.5]) 169%! assert (r.ImageSize, [2, 2]) 170%! assert (r.PixelExtentInWorldX, 1) 171%! assert (r.PixelExtentInWorldY, 1) 172%! assert (r.ImageExtentInWorldX, 2) 173%! assert (r.ImageExtentInWorldY, 2) 174%! assert (r.XIntrinsicLimits, [0.5, 2.5]) 175%! assert (r.YIntrinsicLimits, [0.5, 2.5]) 176 177%!test 178%! r = imref2d ([100, 200]); 179%! assert (r.XWorldLimits, [0.5, 200.5]) 180%! assert (r.YWorldLimits, [0.5, 100.5]) 181%! assert (r.ImageSize, [100, 200]) 182%! assert (r.PixelExtentInWorldX, 1) 183%! assert (r.PixelExtentInWorldY, 1) 184%! assert (r.ImageExtentInWorldX, 200) 185%! assert (r.ImageExtentInWorldY, 100) 186%! assert (r.XIntrinsicLimits, [0.5, 200.5]) 187%! assert (r.YIntrinsicLimits, [0.5, 100.5]) 188 189%!test 190%! xWorldLimits = [2, 5]; 191%! yWorldLimits = [3, 6]; 192%! r = imref2d ([291, 240], xWorldLimits, yWorldLimits); 193%! assert (r.XWorldLimits, [2, 5]) 194%! assert (r.YWorldLimits, [3, 6]) 195%! assert (r.ImageSize, [291, 240]) 196%! assert (r.PixelExtentInWorldX, 0.0125) 197%! assert (r.PixelExtentInWorldY, 0.0103, 1e-3) 198%! assert (r.ImageExtentInWorldX, 3) 199%! assert (r.ImageExtentInWorldY, 3) 200%! assert (r.XIntrinsicLimits, [0.5, 240.5]) 201%! assert (r.YIntrinsicLimits, [0.5, 291.5]) 202 203%!test 204%! pixelExtentInWorldX = 0.3125; 205%! pixelExtentInWorldY = 0.3125; 206%! r = imref2d ([512, 512], pixelExtentInWorldX, pixelExtentInWorldY); 207%! assert (r.XWorldLimits, [0.15625, 160.1562], 1e-4) 208%! assert (r.YWorldLimits, [0.15625, 160.1562], 1e-4) 209%! assert (r.ImageSize, [512, 512]) 210%! assert (r.PixelExtentInWorldX, 0.3125) 211%! assert (r.PixelExtentInWorldY, 0.3125) 212%! assert (r.ImageExtentInWorldX, 160) 213%! assert (r.ImageExtentInWorldY, 160) 214%! assert (r.XIntrinsicLimits, [0.5, 512.5]) 215%! assert (r.YIntrinsicLimits, [0.5, 512.5]) 216 217%!test 218%! pixelExtentInWorldX = 0.1; 219%! pixelExtentInWorldY = 0.4; 220%! r = imref2d ([100, 200], pixelExtentInWorldX, pixelExtentInWorldY); 221%! assert (r.XWorldLimits, [0.05, 20.05], 1e-4) 222%! assert (r.YWorldLimits, [0.2, 40.2], 1e-4) 223%! assert (r.ImageSize, [100, 200]) 224%! assert (r.PixelExtentInWorldX, 0.1) 225%! assert (r.PixelExtentInWorldY, 0.4) 226%! assert (r.ImageExtentInWorldX, 20) 227%! assert (r.ImageExtentInWorldY, 40) 228%! assert (r.XIntrinsicLimits, [0.5, 200.5]) 229%! assert (r.YIntrinsicLimits, [0.5, 100.5]) 230 231## changing ImageSize 232%!test 233%! r = imref2d; 234%! assert (r.XWorldLimits, [0.5, 2.5]) 235%! assert (r.YWorldLimits, [0.5, 2.5]) 236%! assert (r.ImageSize, [2, 2]) 237%! assert (r.PixelExtentInWorldX, 1) 238%! assert (r.PixelExtentInWorldY, 1) 239%! assert (r.ImageExtentInWorldX, 2) 240%! assert (r.ImageExtentInWorldY, 2) 241%! assert (r.XIntrinsicLimits, [0.5, 2.5]) 242%! assert (r.YIntrinsicLimits, [0.5, 2.5]) 243%! r.ImageSize = [800, 600]; 244%! assert (r.XWorldLimits, [0.5, 2.5]) 245%! assert (r.YWorldLimits, [0.5, 2.5]) 246%! assert (r.ImageSize, [800, 600]) 247%! assert (r.PixelExtentInWorldX, 0.003333, 1e-5) 248%! assert (r.PixelExtentInWorldY, 0.0025) 249%! assert (r.ImageExtentInWorldX, 2) 250%! assert (r.ImageExtentInWorldY, 2) 251%! assert (r.XIntrinsicLimits, [0.5, 600.5]) 252%! assert (r.YIntrinsicLimits, [0.5, 800.5]) 253 254## changing XWorldLimits and YWorldLimits 255%!test 256%! r = imref2d; 257%! assert (r.XWorldLimits, [0.5, 2.5]) 258%! assert (r.YWorldLimits, [0.5, 2.5]) 259%! assert (r.ImageSize, [2, 2]) 260%! assert (r.PixelExtentInWorldX, 1) 261%! assert (r.PixelExtentInWorldY, 1) 262%! assert (r.ImageExtentInWorldX, 2) 263%! assert (r.ImageExtentInWorldY, 2) 264%! assert (r.XIntrinsicLimits, [0.5, 2.5]) 265%! assert (r.YIntrinsicLimits, [0.5, 2.5]) 266%! r.XWorldLimits = [-60, 13.33]; 267%! r.YWorldLimits = [-900.8, -560.26]; 268%! assert (r.XWorldLimits, [-60, 13.33]) 269%! assert (r.YWorldLimits, [-900.8, -560.26]) 270%! assert (r.PixelExtentInWorldX, 36.6650) 271%! assert (r.PixelExtentInWorldY, 170.27, 1e-5) 272%! assert (r.ImageExtentInWorldX, 73.33, 1e-5) 273%! assert (r.ImageExtentInWorldY, 340.54, 1e-5) 274%! assert (r.XIntrinsicLimits, [0.5, 2.5]) 275%! assert (r.YIntrinsicLimits, [0.5, 2.5]) 276 277%!test 278%! r = imref2d; 279%! fail ("r.XWorldLimits = []", "") 280%! fail ("r.XWorldLimits = [1]", "") 281%! fail ("r.XWorldLimits = [j]", "") 282%! fail ("r.XWorldLimits = [1; 2]", "") 283%! fail ("r.YWorldLimits = []", "") 284%! fail ("r.YWorldLimits = [1]", "") 285%! fail ("r.YWorldLimits = [j]", "") 286%! fail ("r.YWorldLimits = [1; 2]", "") 287 288%!assert (imref2d ([4, 2, 3]).ImageSize, [4, 2]);