./PaxHeaders.61586/image-2.16.10000644000000000000000000000013215005111723012504 xustar0030 mtime=1746179027.274726131 30 atime=1746179027.274726131 30 ctime=1746179027.274726131 image-2.16.1/0000755000175000017500000000000015005111723014024 5ustar00avinoamavinoam00000000000000image-2.16.1/PaxHeaders.61586/DESCRIPTION0000644000000000000000000000006215005110255014133 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/DESCRIPTION0000644000175000017500000000101615005110255015527 0ustar00avinoamavinoam00000000000000Name: image Version: 2.16.1 Date: 2025-05-04 Author: various authors Maintainer: Carnë Draug , Hartmut Gimpel and Avinoam Kalma Title: Image Processing Description: The Octave-forge Image package provides functions for processing images. The package also provides functions for feature extraction, image statistics, spatial and geometric transformations, morphological operations, linear filtering, and much more. Depends: octave (>= 7.1) License: GPLv3+ Url: http://octave.sf.net image-2.16.1/PaxHeaders.61586/INDEX0000644000000000000000000000006215005110255013217 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/INDEX0000644000175000017500000000535415005110255014624 0ustar00avinoamavinoam00000000000000image >> Image processing Analysis and Statistics corr2 edge fftconv2 fftconvn mean2 std2 entropy entropyfilt qtdecomp qtgetblk qtsetblk graycomatrix hough houghlines houghpeaks houghtf hough_line hough_circle imboxfilt imfindcircles imgaussfilt imgradient imgradientxy imhist immaximas immse normxcorr2 otsuthresh psnr rangefilt regionprops stdfilt Arithmetics imabsdiff imadd imapplymatrix imcomplement imdivide imlincomb immultiply imsubtract Black and white image functions applylut bwarea bwareafilt bwboundaries bwconncomp bwdist bweuler bwfill bwhitmiss bwlabel bwlabeln bwmorph bwpack bwperim bwpropfilt bwselect bwunpack fchcode labelmatrix makelut Colour maps and Colour controls colorangle colorgradient rgb2ycbcr wavelength2rgb ycbcr2rgb Display axes2pix montage subimage viscircles Enhancement and Restoration histeq imadjust imnoise imsmooth medfilt2 ordfilt2 ordfiltn stretchlim Filtering and Transforms deconvwnr findbounds fspecial imfilter imtransform integralImage integralImage3 intlut iradon nonmax_suppress radon rho_filter wiener2 Morhophological Operations bwareaopen conndef imbothat imclearborder imclose imdilate imerode imextendedmax imextendedmin imfill imhmax imhmin imimposemin imopen imreconstruct imregionalmax imregionalmin imtophat mmgradm @strel/getheight @strel/getneighbors @strel/getnhood @strel/getsequence @strel/isflat @strel/reflect @strel/strel @strel/translate watershed Read/write analyze75info analyze75read analyze75write tiff_tag_read Region-based and block processing bestblk blockproc col2im colfilt im2col impixel nlfilter poly2mask roicolor Spatial transformations affine2d affine3d cp2tform imcrop imperspectivewarp impyramid imremap imresize imrotate imshear imtranslate maketform rotate_scale tformfwd tforminv Types and Type conversions grayslice graythresh im2bw im2int16 im2single im2uint16 im2uint8 imcast imquantize isbw isgray isind isrgb lab2double lab2rgb lab2single lab2uint16 lab2uint8 lab2xyz label2rgb mat2gray ntsc2rgb rgb2lab rgb2ntsc rgb2xyz xyz2lab xyz2rgb Utilities checkerboard edgetaper getrangefromclass imattributes imfuse imgetfile imshowpair iptcheckconn iptcheckmap iptnum2ordinal otf2psf padarray phantom psf2otf Image Registration @imref2d/contains.m @imref2d/disp.m @imref2d/imref2d.m @imref2d/intrinsicToWorld.m @imref2d/sizesMatch.m @imref2d/subsasgn.m @imref2d/subsref.m @imref2d/worldToIntrinsic.m @imref2d/worldToSubscript.m @imref3d/contains.m @imref3d/disp.m @imref3d/imref3d.m @imref3d/intrinsicToWorld.m @imref3d/subsasgn.m @imref3d/subsref.m @imref3d/worldToIntrinsic.m @imref3d/worldToSubscript.m image-2.16.1/PaxHeaders.61586/src0000644000000000000000000000013215005111723013136 xustar0030 mtime=1746179027.274726131 30 atime=1746179027.274726131 30 ctime=1746179027.274726131 image-2.16.1/src/0000755000175000017500000000000015005111723014613 5ustar00avinoamavinoam00000000000000image-2.16.1/src/PaxHeaders.61586/watershed.cc0000644000000000000000000000006215005110255015511 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/watershed.cc0000644000175000017500000004537015005110255017120 0ustar00avinoamavinoam00000000000000// Copyright (C) 2015 Carnë Draug // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include #include #include #include #include #include "connectivity.h" using namespace octave_image_pkg; template static boolNDArray imregionalmin (const T& im, const connectivity& conn) { octave_value_list args (2); args(0) = im; args(1) = conn.mask; const octave_value regional_min = octave::feval ("imregionalmin", args)(0); return regional_min.bool_array_value (); } static NDArray bwlabeln (const boolNDArray& bw, const connectivity& conn) { octave_value_list args (2); args(0) = bw; args(1) = conn.mask; const octave_value label = octave::feval ("bwlabeln", args)(0); return label.array_value (); } // Implements watershed in a quite naïve way. From the wikipedia, named // "Meyer's flooding algorithm" (but I could not find the actual paper // that reports it). There are faster (and also nicer results) algorithms, // but this is the only one I found that matches Matlab results. // // 1. A set of markers, pixels where the flooding shall start, are chosen. // Each is given a different label. // 2. The neighboring pixels of each marked area are inserted into a // priority queue with a priority level corresponding to the gray level // of the pixel. // 3. The pixel with the lowest priority level is extracted from the // priority queue. If the neighbors of the extracted pixel that have // already been labeled all have the same label, then the pixel is // labeled with their label. All non-marked neighbors that are not yet // in the priority queue are put into the priority queue. // 4. Redo step 3 until the priority queue is empty. // // There is a detail missing on the description above. On step 3, if the // labeled neighbours do *not* have the same label, should the non-labeled // neighbours be added to the queue? Apparently not. template class Voxel { public: P val; octave_idx_type idx; // We need this to sort elements with the same priority. We need them // to come out in the same order they went in. octave_idx_type pos; Voxel (const P val, const octave_idx_type idx, const octave_idx_type pos) : val (val), idx (idx), pos (pos) { } inline bool operator>(const Voxel& rhs) const { if (val == rhs.val) return pos > rhs.pos; else return val > rhs.val; } }; // As part of this algorithm, we will check the neighbourhood for existing // labels. We don't know in advance the number of labeled neighbours, or // where the first label will be. But we do know the length of the // neighbourhood. template class Collection { public: explicit Collection (const octave_idx_type n) : data (new T[n]) { } ~Collection (void) { delete [] data; } inline octave_idx_type numel (void) const { return count; } inline void push_back (const T val) { data[count++] = val; } inline void reset (void) { count = 0; } protected: T* data = NULL; octave_idx_type count = 0; private: // Disable default and copy constructor and assignment Collection (void); Collection (Collection const& other); Collection& operator = (Collection const& other); }; class LabelsCollection : public Collection { public: using Collection::Collection; inline double label (void) const { return *data; } inline bool all_equal (void) const { for (octave_idx_type i = 0; i < count; i++) if (data[0] != data[i]) return false; return true; } }; class IdxCollection : public Collection { public: using Collection::Collection; inline octave_idx_type operator [] (octave_idx_type i) const { return data[i]; } }; template NDArray watershed (const T& im, const connectivity& conn) { typedef typename T::element_type P; // 1. A set of markers, pixels where the flooding shall start, are chosen. // Each is given a different label. const boolNDArray markers = imregionalmin (im, conn); boolNDArray padded_markers = conn.create_padded (markers, false); NDArray label_array = bwlabeln (padded_markers, conn); double* label = label_array.fortran_vec (); const T padded_im_array = conn.create_padded (im, 0); const P* padded_im = padded_im_array.data (); const Array neighbours_array = conn.deleted_neighbourhood (padded_im_array.dims ()); const octave_idx_type* neighbours = neighbours_array.data (); const octave_idx_type n_neighbours = neighbours_array.numel (); // We need two flags per voxel for this implementation: // 1. Whether a voxel has been labelled or not. (TODO profile this later, // maybe it's enough to do label > 0) // 2. Whether a voxel can go into the queue. Reasons to not go into // the queue are: it's a padding voxel, it's already in the queue, // it's already been labelled. bool* label_flag = padded_markers.fortran_vec (); boolNDArray queue_flag_array (padded_markers); connectivity::set_padding (markers.dims (), padded_markers.dims (), queue_flag_array, true); bool* queue_flag = queue_flag_array.fortran_vec (); const octave_idx_type n = padded_im_array.numel (); octave_idx_type pos = 0; // 2. The neighboring pixels of each marked area are inserted into a // priority queue with a priority level corresponding to the gray level // of the pixel. std::priority_queue, std::vector>, std::greater>> q; for (octave_idx_type i = 0; i < n; i++) if (label_flag[i]) for (octave_idx_type j = 0; j < n_neighbours; j++) { const octave_idx_type ij = i + neighbours[j]; if (! queue_flag[ij]) { queue_flag[ij] = true; q.push (Voxel

(padded_im[ij], ij, pos++)); } } // 3. The pixel with the lowest priority level is extracted from the // priority queue. If the neighbors of the extracted pixel that have // already been labeled all have the same label, then the pixel is // labeled with their label. All non-marked neighbors that are not yet // in the priority queue are put into the priority queue. // 4. Redo step 3 until the priority queue is empty. // // There is a detail missing on the description above. On step 3, if the // labeled neighbours do *not* have the same label, should the non-labeled // neighbours be added to the queue? Apparently not. LabelsCollection lc (n_neighbours); IdxCollection ic (n_neighbours); while (! q.empty ()) { Voxel

v = q.top (); q.pop (); lc.reset (); ic.reset (); for (octave_idx_type j = 0; j < n_neighbours; j++) { const octave_idx_type ij = v.idx + neighbours[j]; if (label_flag[ij]) lc.push_back(label[ij]); else if (! queue_flag[ij]) ic.push_back(ij); } if (lc.numel () > 0 && lc.all_equal ()) { label[v.idx] = lc.label (); label_flag[v.idx] = true; for (octave_idx_type i = 0; i < ic.numel (); i++) { const octave_idx_type ij = ic[i]; queue_flag[ij] = true; q.push (Voxel

(padded_im[ij], ij, pos++)); } } } conn.unpad (label_array); return label_array; } DEFUN_DLD(watershed, args, , "\ -*- texinfo -*-\n\ @deftypefn {Function File} {} watershed (@var{im})\n\ @deftypefnx {Function File} {} watershed (@var{im}, @var{conn})\n\ Compute watershed transform.\n\ \n\ Computes by immersion\n\ \n\ Element connectivity @var{conn}, to define the size of objects, can be\n\ specified with a numeric scalar (number of elements in the neighborhood):\n\ \n\ @table @samp\n\ @item 4 or 8\n\ for 2 dimensional matrices;\n\ @item 6, 18 or 26\n\ for 3 dimensional matrices;\n\ @end table\n\ \n\ or with a binary matrix representing a connectivity array. Defaults to\n\ @code{conndef (ndims (@var{bw}), \"maximal\")} which is equivalent to\n\ @var{conn} of 8 and 26 for 2 and 3 dimensional matrices respectively.\n\ \n\ @seealso{bwdist, bwlabeln, regionprops}\n\ @end deftypefn") { const octave_idx_type nargin = args.length (); if (nargin < 1 || nargin > 2) print_usage (); connectivity conn; if (nargin > 1) conn = octave_image_pkg::conndef (args(1)); else { try { conn = connectivity (args(0).ndims (), "maximal"); } catch (invalid_connectivity& e) { error ("bwconncomp: failed to create MASK (%s)", e.what ()); } } const octave_value im (args(0)); #define IF_TYPE(IS_TYPE, VALUE_TYPE) \ if (im.is ## IS_TYPE ()) \ return octave_value (watershed (im. VALUE_TYPE ## array_value (), \ conn)); \ // My guess is that uint8, uint16, and double will be the most common types. IF_TYPE(_uint8_type, uint8_) else IF_TYPE(_uint16_type, uint16_) else if (im.isfloat ()) { if (im.iscomplex ()) { IF_TYPE(_double_type, complex_) else IF_TYPE(_single_type, float_complex_) } else { IF_TYPE(_double_type, ) else IF_TYPE(_single_type, float_) } } else IF_TYPE(_uint32_type, uint32_) else IF_TYPE(_uint64_type, uint64_) else IF_TYPE(_int8_type, int8_) else IF_TYPE(_int16_type, int16_) else IF_TYPE(_int32_type, int32_) else IF_TYPE(_int64_type, int64_) else IF_TYPE(_uint8_type, uint8_) else IF_TYPE(logical, bool_) // default case if all other above fail. error ("watershed: IM of unsupported class `%s'", im.class_name ().c_str ()); #undef IF_TYPE } /* ## Some simple tests that will check the multiple ways to measure ## distances (comes to light on plateus) %!test %! ex = tril (ones (50), -1) + triu (repmat (2, [50 50]), 2); %! ex(1, 1) = 1; %! ex(end, end) = 1; %! %! in = ones (50); %! in(end,1) = 0; %! in(1,end) = 0; %! assert (watershed (in), ex) %!test %! ex = tril (ones (49), -1) + triu (repmat (2, [49 49]), 2); %! ex(1, 1) = 1; %! ex(end, end) = 1; %! %! in = ones (49); %! in(end,1) = 0; %! in(1,end) = 0; %! assert (watershed (in), ex) %! %! c = (fspecial ('disk', 5) > 0) + 1; %! in(20:30,20:30) = c; %! c = (fspecial ('disk', 4) > 0) + 2; %! in(21:29,21:29) = c; %! assert (watershed (in), ex) %!test %! ex = tril (ones (49), -1) + triu (repmat (2, [49 49]), 2); %! ex(1:28,1:28) = (tril (ones (28) ,7) + triu (repmat (2, [28 28]), 10)); %! ex(1,9) = 1; %! ex(end,end) = 1; %! ex(20:29, 29) = 0; %! %! in = ones (49); %! in(end,1) = 0; %! in(1,end) = 0; %! c = (fspecial ("disk", 5) > 0) + 1; %! in(1:11,38:48) = c; %! %! assert (watershed (in), ex) ## See http://perso.esiee.fr/~info/tw/index.html for a page on topological ## watershed. The following test cases were taken from a powerpoint ## presentation there http://perso.esiee.fr/~info/tw/isis03b.ppt ## "A topological approach to watersheds". Presentation made by Gilles Bertrand ## at the ISIS Workshop on Mathematical Morphology in Paris, France, 2003. ## ## From that presentation, the algorithm we must implement for Matlab ## compatibility is named "Meyer". %!test %! im = [ %! 3 4 5 6 0 %! 2 3 4 5 6 %! 1 2 3 4 5 %! 0 1 2 3 4 %! 1 0 1 2 3]; %! %! labeled8 = [ %! 1 1 1 0 2 %! 1 1 1 0 0 %! 1 1 1 1 1 %! 1 1 1 1 1 %! 1 1 1 1 1]; %! labeled4 = [ %! 1 1 1 0 3 %! 1 1 1 0 0 %! 1 1 0 2 2 %! 1 0 2 2 2 %! 0 2 2 2 2]; %! labeled_weird = [ %! 1 1 1 0 2 %! 1 1 1 1 0 %! 1 1 1 1 1 %! 1 1 1 1 1 %! 1 1 1 1 1]; %! %! assert (watershed (im), labeled8); %! assert (watershed (im, 8), labeled8); %! assert (watershed (im, 4), labeled4); %! assert (watershed (im, [1 1 0; 1 1 1; 0 1 1]), labeled_weird); %!test %! im = [ %! 2 3 30 2 %! 3 30 3 30 %! 255 31 30 4 %! 2 255 31 30 %! 1 2 255 5]; %! %! labeled4 = [ %! 1 1 0 4 %! 1 0 3 0 %! 0 2 0 5 %! 2 2 2 0 %! 2 2 0 6]; %! labeled_weird = [ %! 1 1 0 3 %! 1 1 1 0 %! 0 1 1 1 %! 2 0 0 0 %! 2 2 0 4]; %! %! assert (watershed (im, 4), labeled4); %! assert (watershed (im, [1 1 0; 1 1 1; 0 1 1]), labeled_weird); %!xtest %! ## The following test is required for Matlab compatibility. There must be %! ## something specific about their implementation that causes it to return %! ## this value. Even when solving it on paper, we get different results. %! im = [ %! 2 3 30 2 %! 3 30 3 30 %! 255 31 30 4 %! 2 255 31 30 %! 1 2 255 5]; %! %! labeled8 = [ %! 1 1 0 3 %! 1 1 0 3 %! 0 0 0 0 %! 2 2 0 4 %! 2 2 0 4]; %! assert (watershed (im), labeled8); %! assert (watershed (im, 8), labeled8); %!test %! im = [ %! 2 2 2 2 2 2 2 %! 2 2 30 30 30 2 2 %! 2 30 20 20 20 30 2 %! 40 40 20 20 20 40 40 %! 1 40 20 20 20 40 0 %! 1 1 40 20 40 0 0 %! 1 1 1 20 0 0 0]; %! %! labeled8 = [ %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 0 0 0 0 0 0 0 %! 2 2 2 0 3 3 3 %! 2 2 2 0 3 3 3 %! 2 2 2 0 3 3 3]; %! labeled4 = [ %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 0 1 1 1 1 1 0 %! 2 0 1 1 1 0 3 %! 2 2 0 1 0 3 3 %! 2 2 2 0 3 3 3]; %! labeled_weird = [ %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 0 1 1 0 0 0 0 %! 2 0 0 0 3 3 3 %! 2 2 0 3 3 3 3 %! 2 2 2 0 3 3 3]; %! %! assert (watershed (im), labeled8); %! assert (watershed (im, 8), labeled8); %! assert (watershed (im, 4), labeled4); %! assert (watershed (im, [1 1 0; 1 1 1; 0 1 1]), labeled_weird); %!test %! im = [ %! 40 40 40 40 40 40 40 40 40 40 40 40 40 %! 40 3 3 5 5 5 10 10 10 10 15 20 40 %! 40 3 3 5 5 30 30 30 10 15 15 20 40 %! 40 3 3 5 30 20 20 20 30 15 15 20 40 %! 40 40 40 40 40 20 20 20 40 40 40 40 40 %! 40 10 10 10 40 20 20 20 40 10 10 10 40 %! 40 5 5 5 10 40 20 40 10 10 5 5 40 %! 40 1 3 5 10 15 20 15 10 5 1 0 40 %! 40 1 3 5 10 15 20 15 10 5 1 0 40 %! 40 40 40 40 40 40 40 40 40 40 40 40 40]; %! %! labeled8 = [ %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 0 0 0 0 0 0 0 0 0 0 0 0 0 %! 2 2 2 2 2 2 0 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3]; %! labeled4 = [ %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 0 0 0 0 1 1 1 1 1 0 0 0 0 %! 2 2 2 2 0 1 1 1 0 3 3 3 3 %! 2 2 2 2 2 0 1 0 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3]; %! labeled_weird = [ %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 1 1 1 1 1 %! 0 0 0 0 1 1 0 0 0 0 0 0 0 %! 2 2 2 2 0 0 0 3 3 3 3 3 3 %! 2 2 2 2 2 0 3 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3 %! 2 2 2 2 2 2 0 3 3 3 3 3 3]; %! %! assert (watershed (im), labeled8); %! assert (watershed (im, 8), labeled8); %! assert (watershed (im, 4), labeled4); %! assert (watershed (im, [1 1 0; 1 1 1; 0 1 1]), labeled_weird); %!xtest %! ## This test is failing for Matlab compatibility %! im_full = [ %! 1 2 10 3 8 7 5 %! 3 2 5 10 8 1 4 %! 1 8 2 3 8 3 6]; %! %! matlab_result_full = [ %! 1 1 0 3 0 4 4 %! 0 0 0 0 0 4 4 %! 2 2 2 0 4 4 4]; %! %! assert (watershed (im_full), matlab_result_full); %! %! im_crop = [ %! 2 10 3 8 7 5 %! 2 5 10 8 1 4 %! 8 2 3 8 3 6]; %! %! matlab_result_crop = [ %! 1 0 2 0 3 3 %! 1 0 0 0 3 3 %! 1 1 1 0 3 3]; %! %! assert (watershed (im_crop), matlab_result_crop); */ image-2.16.1/src/PaxHeaders.61586/__eps__.cc0000644000000000000000000000006215005110255015106 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/__eps__.cc0000644000175000017500000000460715005110255016513 0ustar00avinoamavinoam00000000000000/* Copyright (C) 1994-2017 John W. Eaton Copyright (C) 2009 Jaroslav Hajek Copyright (C) 2009-2010 VZLU Prague Copyright (C) 2012 Carlo de Falco 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; see the file COPYING. If not, see . */ #include #include #include #include // Work around bug #50561 - remove this when the Image package is // dependent on Octave 4.4 or later only. // // When computing the eps values for an array, the eps function in // core is unnaceptably slow in versions 4.2 and older. Just try // something like `eps (rand (1024, 1024))`. This issue was fixed // sometime during the 4.3 development versions. // // This is the fixed implementation and to be used by the image // package. It does no input check and only works with an array // input. template T eps (const T& x) { T epsval = x.abs (); typedef typename T::value_type P; for (octave_idx_type i = 0; i < x.numel (); i++) { P val = epsval.xelem (i); if (octave::math::isnan (val) || octave::math::isinf (val)) epsval(i) = octave::numeric_limits

::NaN (); else if (val < std::numeric_limits

::min ()) epsval(i) = std::numeric_limits

::denorm_min (); else { int exponent; octave::math::frexp (val, &exponent); const P digits = std::numeric_limits

::digits; epsval(i) = std::pow (static_cast

(2.0), static_cast

(exponent - digits)); } } return epsval; } DEFUN_DLD (__eps__, args, , "") { octave_value retval; octave_value arg0 = args(0); if (arg0.is_single_type ()) { FloatNDArray epsval = eps (arg0.float_array_value ()); retval = epsval; } else { NDArray epsval = eps (arg0.array_value ()); retval = epsval; } return retval; } image-2.16.1/src/PaxHeaders.61586/imerode.cc0000644000000000000000000000006215005110255015147 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/imerode.cc0000644000175000017500000010304015005110255016543 0ustar00avinoamavinoam00000000000000// Copyright (C) 2013 Carnë Draug // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . #include #include // for an optimization using logical matrices #include #include // to get ind2sub #include #include #include // octave_Inf #include #include #include #include #include #include "strel.h" using namespace octave_image_pkg; // How this works: // // Erosion and dilation are simply minimum and maximum filters (in case of // dilation, the filter needs to be reflected). We have a binary matrix as // Structuring Element (SE) which slides through the image, and using the // maximum or minimum value of those elements for the output matrix. The // border of the matrix is considered to be +Inf or -Inf for the minimum // (erosion) and maximum (dilation) respectively. // // We start by padding the input matrix accordingly to the requested shape // (maybe this step could be avoided by doing the filtering in some different // method around the borders of the matrix0. // // For performance (so we can use a pointer to access the data) while // supporting ND matrices, we calculate the offset for all the points in the // input matrix that affects a single point in the output. Note that as we // slide through this offset values will always be the same. // // We could implement something more close to convn which is quite efficient // but that requires to go through every element of the SE which would be a // waste because not all elements in the SE will be true. Anyway, at least for // binary images (and convn can only be used to do erosion and dilation of // binary images), we already perform faster. // Pads the matrix MT with PADVAL, so it has the correct size to perform a // spatial filtering with SE, for the requested SHAPE. The SHAPE argument // is the same as in convn(). // FIXME: apparently it is not the same as convn(). Matlab seems to have // changed how this is done and will trim the SE, effectively changing // what its origin is. For example, requesting full erosion with the // following SE's will return the same // // 0 0 0 // 0 0 1 0 1 // 0 1 1 1 1 // // because in the first case, the first column and row are ignored. This // means that the size of output for full erosion will differ depending // on the SE. template static T pad_matrix (const T& mt, const strel& se, const double& padval, const std::string& shape) { // If the shape is valid, we can return the input matrix. if (shape == "valid") return mt; const octave_idx_type ndims = mt.ndims (); const Array pre_pad = se.pre_pad (ndims, shape); const Array post_pad = se.post_pad (ndims, shape); dim_vector padded_size (mt.dims ()); for (octave_idx_type dim = 0; dim < ndims; dim++) padded_size(dim) += pre_pad(dim) + post_pad(dim); T padded (padded_size, padval); // Ammount of pre_pad is also how much the original must be shifted // when inserting into the new padded matrix. padded.insert (mt, pre_pad); return padded; } // The general idea about the following is to look at each point for the // output, one at a time, and evaluate all the points from the input. This // at least allows us to skip many points in the case of binary images. For // each output point we consider the one with same index in the input as // "under" the SE element with index 0, and shift from that point to all the // others. Then we move to the next point of output. // // SE: // 0 1 1 // // Input in: // 0 1 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 1 0 // // Input out: // 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 // // Output: // 0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 0 0 0 0 0 // // Note that the output is shorter in size since we have already padded the // input as appropriate for the requested shape. When we slide the SE over // the input, its center (origin) shows what value will be on the output. But // we won't actually use the center of the SE, only the first element and the // distance from it. This means that in this example, the NNZ elements of the // SE will have an offset of 1 and 2. // We match the first element of output with the first from output, and shift // their offset values to move to all other points in the input and assign // them to the output. // // To deal with N dimensional images we have the cumulative dimensions of the // input matrix, e.g. for 10x20x4x5 matrix, this would be the array // [10 200 800 4000]. This is how much we must shift the pointer in the input // matrix to get to the next value in a specific dimension. For example, to get // to the second column we would add 10*(2-1) to the input matrix. To get to // element 4 of the 3rd dimension we would add 200*(4-3). So we use this with // recursion, and adding to the pointer of the input matrix until we only // have a column to erode). // The values of erosion and isflat come as template since they are checked // at the deepest of the loop. By using a template instead of function // argument, it's all done at compile time so we get better performance. // If erosion is false, we perform dilation instead template inline static void erode_line (const P* in, P* out, const octave_idx_type* offsets, const P* height, const octave_idx_type& nnz, const octave_idx_type& line_length) { for (octave_idx_type line_idx = 0; line_idx < line_length; line_idx++) { for (octave_idx_type nnz_idx = 0; nnz_idx < nnz; nnz_idx++) { if (flat) { if (erosion) { if (in[offsets[nnz_idx]] < out[line_idx]) out[line_idx] = in[offsets[nnz_idx]]; } else { if (in[offsets[nnz_idx]] > out[line_idx]) out[line_idx] = in[offsets[nnz_idx]]; } } else { // If non-flat, there is no need to check if typeid is boolean // since non-flat makes no sense for binary images. if (erosion) { P val = in[offsets[nnz_idx]] - height[nnz_idx]; if (val < out[line_idx]) out[line_idx] = val; } else { P val = in[offsets[nnz_idx]] + height[nnz_idx]; if (val > out[line_idx]) out[line_idx] = val; } } } in++; } } // For the specific case of boolean dilation/erosion, we may be able to // break from the loop sooner. Also, there is non-flat binary erosion // and dilation. template inline static void erode_line (const bool* in, bool* out, const octave_idx_type* offsets, const bool* height, const octave_idx_type& nnz, const octave_idx_type& line_length) { for (octave_idx_type line_idx = 0; line_idx < line_length; line_idx++) { for (octave_idx_type nnz_idx = 0; nnz_idx < nnz; nnz_idx++) { if (erosion) { if (! in[offsets[nnz_idx]]) { out[line_idx] = false; break; } } else { if (in[offsets[nnz_idx]]) { out[line_idx] = true; break; } } } in++; } } template static void erode_nd (const P* in, const dim_vector& in_cd, P* out, const dim_vector& out_cd, const dim_vector& out_d, const octave_idx_type* offsets, const P* height, const octave_idx_type& nnz, const octave_idx_type& dim) { if (dim == 0) erode_line (in, out, offsets, height, nnz, out_d(0)); else for (octave_idx_type elem = 0; elem < out_d(dim); elem++) erode_nd (in + in_cd(dim-1) * elem, in_cd, out + out_cd(dim-1)* elem, out_cd, out_d, offsets, height, nnz, dim -1); OCTAVE_QUIT; } template static octave_value erode (const T& im, const strel& se, const std::string& shape, const bool& erosion) { typedef typename T::element_type P; // If image is empty, return empty of the same class. if (octave_value (im).isempty ()) return octave_value (im); // In the case of floating point, complex and integers numbers, both // octave_Inf and -octave_Inf actually become that type max and min value. // However, for boolean, both of them are converted to "true" so for // dilation, where we want false, we check the type. T padded; if (erosion) padded = pad_matrix (im, se, octave_Inf, shape); else { if (typeid (P) == typeid (bool)) padded = pad_matrix (im, se, false, shape); else padded = pad_matrix (im, se, -octave_Inf, shape); } const boolNDArray nhood = se.get_nhood (); const octave_idx_type ndims = padded.ndims (); const dim_vector nhood_size = nhood.dims ().redim (ndims); const dim_vector padded_size = padded.dims (); const dim_vector cum_size = padded_size.cumulative (); const Array offsets = se.offsets (cum_size); const Array

heights = se.true_heights

(); const bool flat = se.flat (); if (typeid (P) == typeid (bool) && ! flat) error ("only non flat structuring elements for binary images"); dim_vector out_size (padded_size); for (octave_idx_type i = 0; i < ndims; i++) out_size(i) -= nhood_size(i) - 1; T out; // When there's only a single neighbor on the SE, then we will only shift // the matrix by its distance to the origin of the SE. if (se.get_nnz () == 1) { octave_idx_type ind = nhood.find (1)(0); Array sub = ind2sub (nhood_size, idx_vector (ind)); Array ranges (dim_vector (ndims, 1)); for (octave_idx_type dim = 0; dim < ndims; dim++) { octave_idx_type start (sub(dim)(0)); octave_idx_type limit (start + out_size(dim)); ranges(dim) = idx_vector (start, limit); } out = padded.index (ranges); } else { if (erosion) out = T (out_size, octave_Inf); else if (typeid (P) == typeid (bool)) out = T (out_size, false); else out = T (out_size, -octave_Inf); if (flat) if (erosion) erode_nd (padded.data (), cum_size, out.fortran_vec (), out_size.cumulative (), out_size, offsets.data (), heights.data (), offsets.numel (), ndims -1); else erode_nd (padded.data (), cum_size, out.fortran_vec (), out_size.cumulative (), out_size, offsets.data (), heights.data (), offsets.numel (), ndims -1); else if (erosion) erode_nd (padded.data (), cum_size, out.fortran_vec (), out_size.cumulative (), out_size, offsets.data (), heights.data (), offsets.numel (), ndims -1); else erode_nd (padded.data (), cum_size, out.fortran_vec (), out_size.cumulative (), out_size, offsets.data (), heights.data (), offsets.numel (), ndims -1); } return octave_value (out); } static octave_value base_action (const std::string& func, const bool& erosion, const octave_value_list& args) { octave_value retval; const octave_idx_type nargin = args.length (); if (nargin < 2 || nargin > 4) print_usage (func); // Default shape is "same" const std::string shape = nargin > 2? args(2).string_value () : "same"; strel se (args(1)); if (! erosion) // must be dilation, then get the se reflection se = se.reflect (); octave_value im (args(0)); for (octave_idx_type idx = 0; idx < se.numel (); idx++) { const strel se_elem = se(idx); if (im.islogical ()) im = erode (im.bool_array_value (), se_elem, shape, erosion); else if (im.is_int8_type ()) im = erode (im.int8_array_value (), se_elem, shape, erosion); else if (im.is_int16_type ()) im = erode (im.int16_array_value (), se_elem, shape, erosion); else if (im.is_int32_type ()) im = erode (im.int32_array_value (), se_elem, shape, erosion); else if (im.is_int64_type ()) im = erode (im.int64_array_value (), se_elem, shape, erosion); else if (im.is_uint8_type ()) im = erode (im.uint8_array_value (), se_elem, shape, erosion); else if (im.is_uint16_type ()) im = erode (im.uint16_array_value (), se_elem, shape, erosion); else if (im.is_uint32_type ()) im = erode (im.uint32_array_value (), se_elem, shape, erosion); else if (im.is_uint64_type ()) im = erode (im.uint64_array_value (), se_elem, shape, erosion); else if (im.isreal ()) if (im.is_single_type ()) im = erode (im.float_array_value (), se_elem, shape, erosion); else // must be double im = erode (im.array_value (), se_elem, shape, erosion); else if (im.iscomplex ()) if (im.is_single_type ()) im = erode (im.float_complex_array_value (), se_elem, shape, erosion); else // must be double im = erode (im.complex_array_value (), se_elem, shape, erosion); else im = octave_value (); } return im; } DEFUN_DLD(imerode, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {} imerode (@var{im}, @var{SE})\n\ @deftypefnx {Loadable Function} {} imerode (@var{im}, @var{SE}, @var{shape})\n\ Perform morphological erosion.\n\ \n\ The image @var{im} must be a numeric matrix with any number of dimensions.\n\ The erosion is performed with the structuring element @var{se} which can\n\ be a:\n\ \n\ @itemize @bullet\n\ @item strel object;\n\ @item array of strel objects as returned by @code{@@strel/getsequence};\n\ @item matrix of 0's and 1's.\n\ @end itemize\n\ \n\ To perform a non-flat erosion, @var{SE} must be a strel object.\n\ \n\ The size of the result is determined by the optional @var{shape} argument\n\ which takes the following values:\n\ \n\ @table @asis\n\ @item @qcode{\"same\"} (default)\n\ Return image of the same size as input @var{im}.\n\ \n\ @item @qcode{\"full\"}\n\ Return the full erosion (image is padded to accommodate @var{se} near the\n\ borders).\n\ \n\ @item @qcode{\"valid\"}\n\ Return only the parts which do not include the padded edges.\n\ @end table\n\ \n\ In case of a @var{SE} with a size of even length, the center is considered\n\ at indices @code{floor ([size(@var{SE})/2] + 1)}.\n\ \n\ @seealso{imdilate, imopen, imclose, strel}\n\ @end deftypefn") { return base_action ("imerode", true, args); } /* ## using [1] as mask returns the same value %!assert (imerode (eye (3), [1]), eye (3)); ## and an empty SE returns all Inf %!assert (imerode (eye (3), []), Inf (3, 3)); ## test normal usage with non-symmetric SE %!test %! im = [0 1 0 %! 1 1 1 %! 0 1 0]; %! se = [1 0 0 %! 0 1 0 %! 0 1 1]; %! assert (imerode (im, se), [0 1 0; 0 0 0; 0 1 0]); %! assert (imerode (logical(im), se), logical ([0 1 0; 0 0 0; 0 1 0])); %! assert (imerode (im, se, "full"), %! [ 0 0 0 0 Inf %! 1 0 1 0 Inf %! 0 0 0 0 0 %! Inf 0 1 0 1 %! Inf Inf 0 1 0]); %! assert (imerode (logical(im), se, "full"), %! logical([0 0 0 0 1 %! 1 0 1 0 1 %! 0 0 0 0 0 %! 1 0 1 0 1 %! 1 1 0 1 0])); %!test %! a = rand ([10 22 11 6 8 5]) > 0.2; %! se = ones ([5 3 7]); %! %! ## the image is not really indexed but this way it is padded with 1s %! assert (imerode (a, se), colfilt (a, "indexed", size (se), "sliding", @all)) %! %! assert (imerode (a, se, "valid"), convn (a, se, "valid") == nnz (se)) %! ## again, we need to pad it ourselves because convn pads with zeros %! b = true (size (a) + [4 2 6 0 0 0]); %! b(3:12, 2:23, 4:14,:,:,:) = a; %! assert (imdilate (b, se, "same"), convn (b, se, "same") > 0) %! b = true (size (a) + [8 4 12 0 0 0]); %! b(5:14, 3:24, 7:17,:,:,:) = a; %! assert (imdilate (b, se, "full"), convn (b, se, "full") > 0) %!test %! im = [0 0 0 0 0 0 0 %! 0 0 1 0 1 0 0 %! 0 0 1 1 0 1 0 %! 0 0 1 1 1 0 0 %! 0 0 0 0 0 0 0]; %! se = [0 0 0 %! 0 1 0 %! 0 1 1]; %! out = [0 0 0 0 0 0 0 %! 0 0 1 0 0 0 0 %! 0 0 1 1 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0]; %! assert (imerode (im, se), out); %! assert (imerode (logical (im), se), logical (out)); %! assert (imerode (im, logical (se)), out); %! assert (imerode (logical (im), logical (se)), logical (out)); %! %! # with an even-size SE %! se = [0 0 0 1 %! 0 1 0 0 %! 0 1 1 1]; %! out = [0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 1 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0]; %! assert (imerode (im, se), out); %! out = [ 0 0 0 0 1 0 1 %! 0 0 1 0 1 1 0 %! 0 0 1 1 1 1 1 %! 0 0 1 1 1 1 1 %! 0 0 1 1 1 1 1]; %! assert (imdilate (im, se), out); ## normal usage for grayscale images %!test %! a = [ 82 2 97 43 79 43 41 65 51 11 %! 60 65 21 56 94 77 36 38 75 39 %! 32 68 78 1 16 75 76 90 81 56 %! 43 90 82 41 36 1 87 19 18 63 %! 63 64 2 48 18 43 38 25 22 99 %! 12 46 90 79 3 92 39 79 10 22 %! 38 98 11 10 40 90 88 38 4 76 %! 54 37 9 4 33 98 36 47 53 57 %! 38 76 82 50 14 74 64 99 7 33 %! 88 96 41 62 84 89 97 23 41 3]; %! %! domain = ones (3); %! out = [ 2 1 1 1 16 36 36 11 %! 21 1 1 1 1 1 18 18 %! 2 1 1 1 1 1 18 18 %! 2 2 2 1 1 1 10 10 %! 2 2 2 3 3 25 4 4 %! 9 4 3 3 3 36 4 4 %! 9 4 4 4 14 36 4 4 %! 9 4 4 4 14 23 7 3]; %! assert (imerode (a, domain, "valid"), out); %! assert (imerode (uint8 (a), domain, "valid"), uint8 (out)); %! assert (imerode (uint8 (a), strel ("arbitrary", domain), "valid"), uint8 (out)); %! assert (imerode (uint8 (a), strel ("square", 3), "valid"), uint8 (out)); %! %!## Test for non-flat strel %! assert (imerode (a, strel ("arbitrary", domain, ones (3)), "valid"), out -1); %! %! out = [ 97 97 97 94 94 90 90 90 %! 90 90 94 94 94 90 90 90 %! 90 90 82 75 87 90 90 99 %! 90 90 90 92 92 92 87 99 %! 98 98 90 92 92 92 88 99 %! 98 98 90 98 98 98 88 79 %! 98 98 82 98 98 99 99 99 %! 96 96 84 98 98 99 99 99]; %! assert (imdilate (a, domain, "valid"), out); %! assert (imdilate (uint8 (a), domain, "valid"), uint8 (out)); %! %!## Test for non-flat strel %! assert (imdilate (a, strel ("arbitrary", domain, ones (3)), "valid"), out +1); %! %! ## test while using SE that can be decomposed and an actual sequence %! domain = ones (5); %! out = [ 2 1 1 1 1 1 16 11 11 11 %! 2 1 1 1 1 1 1 1 11 11 %! 2 1 1 1 1 1 1 1 11 11 %! 2 1 1 1 1 1 1 1 10 10 %! 2 1 1 1 1 1 1 1 4 4 %! 2 2 2 1 1 1 1 1 4 4 %! 2 2 2 2 2 3 3 4 4 4 %! 9 4 3 3 3 3 3 3 3 3 %! 9 4 4 4 4 4 4 3 3 3 %! 9 4 4 4 4 4 7 3 3 3]; %! assert (imerode (a, domain), out); %! assert (imerode (a, strel ("square", 5)), out); %! assert (imerode (a, getsequence (strel ("square", 5))), out); %! %! ## using a non-symmetric SE %! domain = [ 1 1 0 %! 0 1 1 %! 0 1 0]; %! %! out = [ 2 2 1 16 36 36 38 39 %! 60 1 1 16 1 36 19 18 %! 32 2 1 1 1 19 18 18 %! 2 2 18 3 1 1 19 10 %! 46 2 2 3 18 38 10 4 %! 11 9 4 3 3 36 4 4 %! 9 4 4 10 36 36 38 4 %! 37 9 4 4 33 36 7 7]; %! assert (imerode (a, domain, "valid"), out); %! assert (imerode (a, strel ("arbitrary", domain, ones (3)), "valid"), out -1); %! %! out = [ 78 97 56 94 94 90 90 81 %! 90 82 78 94 87 87 90 90 %! 90 90 82 43 75 87 90 99 %! 90 90 79 92 92 87 79 25 %! 98 90 90 90 92 92 79 79 %! 98 98 79 98 98 90 88 57 %! 98 82 50 74 98 99 99 53 %! 96 82 84 89 98 97 99 99]; %! assert (imdilate (a, domain, "valid"), out); %! assert (imdilate (a, strel ("arbitrary", domain, ones (3)), "valid"), out +1); // Tests for N-dimensions %!test %! im = reshape (magic(16), [4 8 4 2]); %! se = true (3, 3, 3); %! out = zeros (4, 8, 4, 2); %! out(:,:,1,1) = [ %! 3 3 46 2 2 2 47 47 %! 3 3 30 2 2 2 31 31 %! 17 17 16 16 16 20 13 13 %! 33 33 16 16 16 36 13 13]; %! out(:,:,2,1) = [ %! 3 3 46 2 2 2 43 43 %! 3 3 30 2 2 2 27 27 %! 17 17 12 12 12 20 13 13 %! 33 33 12 12 12 36 13 13]; %! out(:,:,3,1) = [ %! 3 3 42 6 6 6 43 43 %! 3 3 26 6 6 6 27 27 %! 21 21 12 12 12 20 9 9 %! 37 37 12 12 12 36 9 9]; %! out(:,:,4,1) = [ %! 7 7 42 6 6 6 43 43 %! 7 7 26 6 6 6 27 27 %! 21 21 12 12 12 24 9 9 %! 37 37 12 12 12 40 9 9]; %! out(:,:,1,2) = [ %! 11 11 38 10 10 10 39 39 %! 11 11 22 10 10 10 23 23 %! 25 25 8 8 8 28 5 5 %! 41 41 8 8 8 44 5 5]; %! out(:,:,2,2) = [ %! 11 11 38 10 10 10 35 35 %! 11 11 22 10 10 10 19 19 %! 25 25 4 4 4 28 5 5 %! 41 41 4 4 4 44 5 5]; %! out(:,:,3,2) = [ %! 11 11 34 14 14 14 35 35 %! 11 11 18 14 14 14 19 19 %! 29 29 4 4 4 28 1 1 %! 45 45 4 4 4 44 1 1]; %! out(:,:,4,2) = [ %! 15 15 34 14 14 14 35 35 %! 15 15 18 14 14 14 19 19 %! 29 29 4 4 4 32 1 1 %! 45 45 4 4 4 48 1 1]; %! assert (imerode (im, se), out); %! assert (imerode (uint16 (im), se), uint16 (out)); %! %! ## trying a more weird SE %! se(:,:,1) = [1 0 1; 0 1 1; 0 0 0]; %! se(:,:,3) = [1 0 1; 0 1 1; 0 0 1]; %! out(:,:,1,1) = [ %! 3 17 46 2 2 2 47 47 %! 17 3 30 2 2 2 31 31 %! 17 17 16 16 16 20 13 31 %! 33 33 16 16 16 36 13 13]; %! out(:,:,2,1) = [ %! 3 3 46 2 2 20 43 61 %! 3 3 30 2 20 2 27 43 %! 33 17 12 20 20 20 13 13 %! 51 33 12 12 30 36 13 13]; %! out(:,:,3,1) = [ %! 3 21 42 6 6 6 43 43 %! 21 3 26 6 6 6 27 27 %! 21 21 12 12 12 20 9 27 %! 37 37 12 12 12 36 9 9]; %! out(:,:,4,1) = [ %! 7 7 42 6 6 24 57 57 %! 7 7 26 6 24 6 43 43 %! 37 21 26 24 24 24 9 9 %! 55 37 12 12 26 40 9 9]; %! out(:,:,1,2) = [ %! 11 25 38 10 10 10 39 39 %! 25 11 22 10 10 10 23 23 %! 25 25 8 8 8 28 5 23 %! 41 41 8 8 8 44 5 5]; %! out(:,:,2,2) = [ %! 11 11 38 10 10 28 35 53 %! 11 11 22 10 22 10 19 35 %! 41 25 4 22 22 28 5 5 %! 59 41 4 4 22 44 5 5]; %! out(:,:,3,2) = [ %! 11 29 34 14 14 14 35 35 %! 29 11 18 14 14 14 19 19 %! 29 29 4 4 4 28 1 19 %! 45 45 4 4 4 44 1 1]; %! out(:,:,4,2) = [ %! 15 15 34 14 14 32 49 49 %! 15 15 18 14 18 14 35 35 %! 45 29 18 18 18 32 1 1 %! 63 45 4 4 18 48 1 1]; %! assert (imerode (im, se), out); %! assert (imerode (uint16 (im), se), uint16 (out)); ## Test input check %!error imerode (ones (10), 45) %!error imerode (ones (10), "some text") %!error imerode (ones (10), {23, 45}) ## No binary erosion for non-flat strel %!error imerode (rand (10) > 10 , strel ("arbitrary", true (3), ones (3))) */ // PKG_ADD: autoload ("imdilate", which ("imerode")); // PKG_DEL: autoload ("imdilate", which ("imerode"), "remove"); DEFUN_DLD(imdilate, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {} imdilate (@var{im}, @var{SE})\n\ @deftypefnx {Loadable Function} {} imdilate (@var{im}, @var{SE}, @var{shape})\n\ Perform morphological dilation.\n\ \n\ The image @var{im} must be a numeric matrix with any number of dimensions.\n\ The dilation is performed with the structuring element @var{se} which can\n\ be a:\n\ \n\ @itemize @bullet\n\ @item strel object;\n\ @item array of strel objects as returned by @code{@@strel/getsequence};\n\ @item matrix of 0's and 1's.\n\ @end itemize\n\ \n\ To perform a non-flat dilation, @var{SE} must be a strel object.\n\ \n\ The size of the result is determined by the optional @var{shape} argument\n\ which takes the following values:\n\ \n\ @table @asis\n\ @item @qcode{\"same\"} (default)\n\ Return image of the same size as input @var{im}.\n\ \n\ @item @qcode{\"full\"}\n\ Return the full dilation (matrix is padded to accommodate @var{se} near the\n\ borders).\n\ \n\ @item @qcode{\"valid\"}\n\ Return only the parts which do not include the padded edges.\n\ @end table\n\ \n\ In case of a @var{SE} with a size of even length, the center is considered\n\ at indices @code{floor ([size(@var{SE})/2] + 1)}.\n\ \n\ @seealso{imerode, imopen, imclose}\n\ @end deftypefn") { return base_action ("imdilate", false, args); } /* // Tests for N-dimensions %!test %! a = rand ([10 22 11 6 8 5]) > 0.8; %! se = ones ([5 3 7]); %! assert (imdilate (a, se), convn (a, se, "same") > 0) %! assert (imdilate (a, se, "full"), convn (a, se, "full") > 0) %! assert (imdilate (a, se, "valid"), convn (a, se, "valid") > 0) %! assert (imdilate (a, se), colfilt (a, size (se), "sliding", @any)) %!test %! im = reshape (magic(16), [4 8 4 2]); %! se = true (3, 3, 3); %! out = zeros (4, 8, 4, 2); %! %! out(:,:,1,1) = [ %! 256 256 209 253 253 253 212 212 %! 256 256 225 253 253 253 228 228 %! 238 238 243 243 243 239 242 242 %! 222 222 243 243 243 223 242 242]; %! out(:,:,2,1) = [ %! 256 256 213 253 253 253 212 212 %! 256 256 229 253 253 253 228 228 %! 238 238 243 243 243 239 246 246 %! 222 222 243 243 243 223 246 246]; %! out(:,:,3,1) = [ %! 252 252 213 253 253 253 216 216 %! 252 252 229 253 253 253 232 232 %! 238 238 247 247 247 235 246 246 %! 222 222 247 247 247 219 246 246]; %! out(:,:,4,1) = [ %! 252 252 213 249 249 249 216 216 %! 252 252 229 249 249 249 232 232 %! 234 234 247 247 247 235 246 246 %! 218 218 247 247 247 219 246 246]; %! out(:,:,1,2) = [ %! 248 248 217 245 245 245 220 220 %! 248 248 233 245 245 245 236 236 %! 230 230 251 251 251 231 250 250 %! 214 214 251 251 251 215 250 250]; %! out(:,:,2,2) = [ %! 248 248 221 245 245 245 220 220 %! 248 248 237 245 245 245 236 236 %! 230 230 251 251 251 231 254 254 %! 214 214 251 251 251 215 254 254]; %! out(:,:,3,2) = [ %! 244 244 221 245 245 245 224 224 %! 244 244 237 245 245 245 240 240 %! 230 230 255 255 255 227 254 254 %! 214 214 255 255 255 211 254 254]; %! out(:,:,4,2) = [ %! 244 244 221 241 241 241 224 224 %! 244 244 237 241 241 241 240 240 %! 226 226 255 255 255 227 254 254 %! 210 210 255 255 255 211 254 254]; %! assert (imdilate (im, se), out); %! assert (imdilate (uint16 (im), se), uint16 (out)); %! %! ## trying a more weird SE %! se(:,:,1) = [1 0 1; 0 1 1; 0 0 0]; %! se(:,:,3) = [1 0 1; 0 1 1; 0 0 1]; %! out(:,:,1,1) = [ %! 256 256 209 239 253 253 212 194 %! 256 256 225 239 239 239 228 212 %! 222 222 243 239 243 239 242 242 %! 208 208 225 243 243 223 242 242]; %! out(:,:,2,1) = [ %! 256 256 213 253 253 253 212 212 %! 238 256 229 253 253 253 228 228 %! 238 238 243 243 243 239 246 228 %! 222 222 243 243 243 223 228 246]; %! out(:,:,3,1) = [ %! 252 252 213 235 253 253 216 198 %! 252 252 229 235 235 253 232 216 %! 222 238 247 235 247 235 246 246 %! 204 222 229 247 247 219 246 246]; %! out(:,:,4,1) = [ %! 252 252 213 249 249 249 216 216 %! 234 252 229 249 249 249 232 232 %! 234 234 247 247 247 235 246 232 %! 218 218 247 247 247 219 232 246]; %! out(:,:,1,2) = [ %! 248 248 217 231 245 245 220 202 %! 248 248 233 233 233 231 236 220 %! 214 214 251 233 251 231 250 250 %! 200 200 233 251 251 215 250 250]; %! out(:,:,2,2) = [ %! 248 248 221 245 245 245 220 220 %! 230 248 237 245 245 245 236 236 %! 230 230 251 251 251 231 254 236 %! 214 214 251 251 251 215 236 254]; %! out(:,:,3,2) = [ %! 244 244 221 227 245 245 224 206 %! 244 244 237 237 237 245 240 224 %! 214 230 255 237 255 227 254 254 %! 196 214 237 255 255 211 254 254]; %! out(:,:,4,2) = [ %! 244 244 221 241 241 241 224 224 %! 226 244 237 241 241 241 240 240 %! 226 226 255 255 255 227 254 240 %! 210 210 255 255 255 211 240 254]; %! assert (imdilate (im, se), out); %! assert (imdilate (uint16 (im), se), uint16 (out)); */ /* ## bug #47879 (invalid but mathematically interesting corner-case) ## This is all about empty sets, either by using a blank/empty/zeros SE ## or by picking a SE that "picks" elements only from the borders. These ## are two completely different issues that may look the same. See a more ## detailed explanation at http://stackoverflow.com/a/37117842/1609556 %!test # scalar blank SE %! se = 0; %! assert (imerode (5, se), Inf) %! assert (imerode (true, se), true) %! assert (imerode (false, se), true) %! assert (imerode (uint8 (3), se), uint8 (255)) %! %! assert (imdilate (5, se), -Inf) %! assert (imdilate (true, se), false) %! assert (imdilate (false, se), false) %! assert (imdilate (uint8 (3), se), uint8 (0)) %!test # empty SE %! se = []; %! assert (imerode (5, se), Inf) %! assert (imerode (true, se), true) %! assert (imerode (false, se), true) %! assert (imerode (uint8 (3), se), uint8 (255)) %! %! assert (imdilate (5, se), -Inf) %! assert (imdilate (true, se), false) %! assert (imdilate (false, se), false) %! assert (imdilate (uint8 (3), se), uint8 (0)) %!test # non-scalar blank SE %! se = zeros (3, 3); %! assert (imerode (5, se), Inf) %! assert (imerode (true, se), true) %! assert (imerode (false, se), true) %! assert (imerode (uint8 (3), se), uint8 (255)) %! %! assert (imdilate (5, se), -Inf) %! assert (imdilate(true, se), false) %! assert (imdilate (false, se), false) %! assert (imdilate (uint8 (3), se), uint8 (0)) %!test # erode only with out-of-border elements %! se = [1 1 1; 1 0 1; 1 1 1]; %! assert (imerode (5, se), Inf) %! assert (imerode (true, se), true) %! %! assert (imdilate (5, se), -Inf) %! assert (imdilate (true, se), false) %!test # only true elements of SE are out-of-border %! se = [0 0 0; 1 0 0; 1 1 0]; %! assert (imerode (zeros (3), se), [0 0 0; 0 0 0; Inf 0 0]) %! assert (imerode (false (3), se), logical ([0 0 0; 0 0 0; 1 0 0])) %! assert (imdilate (zeros (3), se), [0 0 -Inf; 0 0 0; 0 0 0]) %! assert (imdilate (false (3), se), false (3, 3)) %! %! se = [0 0 0; 0 0 0; 1 1 1]; %! assert (imerode (zeros (3, 3), se), [0 0 0; 0 0 0; Inf Inf Inf]) %! assert (imerode (false (3, 3), se), logical ([0 0 0; 0 0 0; 1 1 1])) %! assert (imdilate (zeros (3, 3), se), [-Inf -Inf -Inf; 0 0 0; 0 0 0]) %! assert (imdilate (false (3, 3), se), false (3, 3)) %!test # only true elements of even-sized SE are out-of-border %! se = logical ([0 1; 1 1]); %! assert (imerode (false (3, 3), se), logical ([0 0 0; 0 0 0; 0 0 1])) %! assert (imerode (zeros (3, 3), se), [0 0 0; 0 0 0; 0 0 Inf]) %! %! assert (imdilate (false (3, 3), se), false (3, 3)) %! assert (imdilate (zeros (3, 3), se), [-Inf 0 0; 0 0 0; 0 0 0]) */ image-2.16.1/src/PaxHeaders.61586/union-find.h0000644000000000000000000000006215005110255015433 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/union-find.h0000644000175000017500000000457215005110255017041 0ustar00avinoamavinoam00000000000000// Copyright (C) 2011 Jordi Gutiérrez Hermoso // Copyright (C) 2014 Carnë Draug // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . #include struct voxel { octave_idx_type rank; octave_idx_type parent; voxel () = default; }; class union_find { // Union-find data structure, see e.g. // http://en.wikipedia.org/wiki/Union-find private: std::vector voxels; public: explicit union_find (octave_idx_type s) : voxels (s) {}; // Use only when adding new elements for the first time void add (const octave_idx_type idx) { voxels[idx].parent = idx; voxels[idx].rank = 0; return; } // Give the root representative id for this object octave_idx_type find (const octave_idx_type idx) { voxel* elt = &voxels[idx]; if (elt->parent != idx) elt->parent = find (elt->parent); return elt->parent; } //Given two objects, unite the sets to which they belong void unite (const octave_idx_type idx1, const octave_idx_type idx2) { octave_idx_type root1 = find (idx1); octave_idx_type root2 = find (idx2); //Check if any union needs to be done, maybe they already are //in the same set. if (root1 != root2) { voxel* v1 = &voxels[root1]; voxel* v2 = &voxels[root2]; if (v1->rank > v2->rank) v1->parent = root2; else if (v1->rank < v2->rank) v2->parent = root1; else { v2->parent = root1; v1->rank++; } } } std::vector get_ids (const NDArray& L) const { std::vector ids; const double* v = L.data (); for (size_t i = 0; i < voxels.size (); i++) if (v[i]) ids.push_back (i); return ids; }; }; image-2.16.1/src/PaxHeaders.61586/Makefile.in0000644000000000000000000000006215005110255015261 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/Makefile.in0000644000175000017500000000207715005110255016665 0ustar00avinoamavinoam00000000000000MKOCTFILE ?= mkoctfile ## We can't link oct files, and Octave's package system does not handle ## shared libraries. Because of this, we need to create object files for ## our "shared" libraries and statically link to selected oct files. conn_dependent = conndef.oct bwlabeln.oct imreconstruct.oct bwconncomp.oct \ watershed.oct strel_dependent = imerode.oct libs = connectivity.o strel.o OCT_FILES = __spatial_filtering__.oct __bilateral__.oct __eps__.oct \ __custom_gaussian_smoothing__.oct __boundary__.oct bwfill.oct \ rotate_scale.oct hough_line.oct graycomatrix.oct bwdist.oct \ intlut.oct nonmax_suppress.oct $(strel_dependent) $(conn_dependent) CC_FILES = $(patsubst %.oct, %.cc, ${OCT_FILES}) all: ${OCT_FILES} %.o: %.cc %.h $(MKOCTFILE) -c $< $(conn_dependent): %.oct: %.cc connectivity.o $(MKOCTFILE) $^ $(strel_dependent): %.oct: %.cc strel.o $(MKOCTFILE) $^ %.oct: %.cc $(MKOCTFILE) $< clean: $(RM) *.o octave-core octave-workspace *~ ${OCT_FILES} distclean: clean $(RM) Makefile config.log config.status image-2.16.1/src/PaxHeaders.61586/connectivity.cc0000644000000000000000000000006215005110255016241 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/connectivity.cc0000644000175000017500000002217115005110255017642 0ustar00avinoamavinoam00000000000000// Copyright (C) 2014 Carnë Draug // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, see // . #include "connectivity.h" #include #include #include #include #include using namespace octave_image_pkg; connectivity::connectivity (const boolNDArray& mask_arg) { mask = mask_arg; // Must be 1x1, 3x1, or 3x3x3x...x3 const octave_idx_type numel = mask.numel (); const dim_vector dims = mask.dims (); const octave_idx_type ndims = connectivity::ndims (dims); for (octave_idx_type i = 0; i < ndims; i++) if (dims(i) != 3) throw invalid_connectivity ("is not 1x1, 3x1, 3x3, or 3x3x...x3"); // Center must be true const octave_idx_type center = floor (numel /2); if (! mask(center)) throw invalid_connectivity ("center is not true"); // Must be symmetric relative to its center const bool* start = mask.data (); const bool* end = mask.data () + (numel -1); for (octave_idx_type i = 0; i < center; i++) if (start[i] != end[-i]) throw invalid_connectivity ("is not symmetric relative to its center"); return; } connectivity::connectivity (const unsigned int conn) { if (conn == 4) { mask = boolNDArray (dim_vector (3, 3), true); bool* md = mask.fortran_vec (); md[ 0] = false; md[ 2] = false; md[ 6] = false; md[ 8] = false; } else if (conn == 6) { mask = boolNDArray (dim_vector (3, 3, 3), false); bool* md = mask.fortran_vec (); md[ 4] = true; md[10] = true; md[12] = true; md[13] = true; md[14] = true; md[16] = true; md[22] = true; } else if (conn == 8) mask = boolNDArray (dim_vector (3, 3), true); else if (conn == 18) { mask = boolNDArray (dim_vector (3, 3, 3), true); bool* md = mask.fortran_vec (); md[ 0] = false; md[ 2] = false; md[ 6] = false; md[ 8] = false; md[18] = false; md[20] = false; md[24] = false; md[26] = false; } else if (conn == 26) mask = boolNDArray (dim_vector (3, 3, 3), true); else throw invalid_connectivity ("must be in the set [4 6 8 18 26]" " (was " + std::to_string (conn) + ")"); return; } connectivity::connectivity (const octave_idx_type& ndims, const std::string& type) { dim_vector size; if (ndims == 1) size = dim_vector (3, 1); else { size = dim_vector (3, 3); size.resize (ndims, 3); } if (type == "maximal") { mask = boolNDArray (size, true); } else if (type == "minimal") { mask = boolNDArray (size, false); bool* md = mask.fortran_vec (); md += int (floor (pow (3, ndims) /2)); // move to center md[0] = true; for (octave_idx_type dim = 0; dim < ndims; dim++) { const octave_idx_type stride = pow (3, dim); md[ stride] = true; md[-stride] = true; } } else throw invalid_connectivity ("must be \"maximal\" or \"minimal\""); return; } // A couple of things: // * it is handy that the offsets come sorted since they will be used to // access the elements and we want to jump around as little as possible. // * the number of dimensions used may be different than the mask. Array connectivity::neighbourhood (const dim_vector& size) const { const octave_idx_type ndims = connectivity::ndims (mask); const octave_idx_type numel = mask.numel (); // If ndims is 0 or numel is 0, return an empty array if (ndims <= 0 || numel <= 0) return Array (dim_vector (0, 1)); // offset to adjacent element on correspoding dimension Array strides (dim_vector (ndims, 1)); strides(0) = 1; for (octave_idx_type dim = 1; dim < ndims; dim++) strides(dim) = strides(dim-1) * size(dim-1); Array pow3 (dim_vector (ndims, 1)); pow3(0) = 1; for (octave_idx_type dim = 1; dim < ndims; dim++) pow3(dim) = pow3(dim-1) * 3; // We calculate this for all elements. We could do it only for the "true" // elements but that's slightly more complex and in most cases we will // already want most, if not all, elements anyway. Array all_offsets (dim_vector (numel, 1), 0); for (octave_idx_type dim = 0; dim < ndims; dim++) { octave_idx_type i (0); for (int x = 0; x < pow3(ndims -1 -dim); x++) { for (octave_idx_type k = 0; k < pow3(dim); k++) all_offsets(i++) -= strides(dim); i += pow3(dim); for (octave_idx_type k = 0; k < pow3(dim); k++) all_offsets(i++) += strides(dim); } } octave_idx_type start_idx = 0; for (octave_idx_type dim = ndims; dim > connectivity::ndims (size); dim--) start_idx += pow3(dim -1); const bool* m = mask.data (); const octave_idx_type* ao = all_offsets.data (); octave_idx_type nnz = 0; for (octave_idx_type i = start_idx; i < (numel - start_idx); i++) if (m[i]) nnz++; Array offsets (dim_vector (nnz, 1)); octave_idx_type* o = offsets.fortran_vec (); for (octave_idx_type i = start_idx, j = 0; i < (numel - start_idx); i++) if (m[i]) o[j++] = ao[i]; return offsets; } Array connectivity::deleted_neighbourhood (const dim_vector& size) const { Array offsets = neighbourhood (size); for (octave_idx_type i = 0; i < offsets.numel (); i++) if (offsets(i) == 0) offsets.delete_elements (idx_vector (i)); return offsets; } Array connectivity::positive_neighbourhood (const dim_vector& size) const { Array offsets = neighbourhood (size); std::vector to_keep; for (octave_idx_type i = 0; i < offsets.numel (); i++) if (offsets(i) > 0) to_keep.push_back (offsets(i)); octave_idx_type numel = to_keep.size (); Array neg (dim_vector (numel, 1)); for (octave_idx_type i = 0; i < numel; i++) neg(i) = to_keep[i]; return neg; } Array connectivity::negative_neighbourhood (const dim_vector& size) const { Array offsets = neighbourhood (size); std::vector to_keep; for (octave_idx_type i = 0; i < offsets.numel (); i++) if (offsets(i) < 0) to_keep.push_back (offsets(i)); octave_idx_type numel = to_keep.size (); Array neg (dim_vector (numel, 1)); for (octave_idx_type i = 0; i < numel; i++) neg(i) = to_keep[i]; return neg; } octave_idx_type connectivity::ndims (const dim_vector& dims) { // We do not bother with 1x3 arrays since those are not valid // connectivity masks anyway. if (dims(1) == 1) { if (dims(0) == 1) return 0; else return 1; } else return dims.length (); } template octave_idx_type connectivity::ndims (const Array& a) { return connectivity::ndims (a.dims ()); } Array connectivity::padding_lengths (const dim_vector& size, const dim_vector& padded_size) { const octave_idx_type ndims = size.length (); Array lengths (dim_vector (ndims, 1), 0); lengths(0) = 1; for (octave_idx_type i = 1; i < ndims; i++) if (size(i) < padded_size(i)) lengths(i) = lengths(i -1) * padded_size(i-1); return lengths; } boolNDArray connectivity::padding_mask (const dim_vector& size, const dim_vector& padded_size) { boolNDArray mask (padded_size, false); set_padding (size, padded_size, mask, true); return mask; } connectivity octave_image_pkg::conndef (const octave_value& val) { // A mask may not not be of type logical/bool, it can be of any // numeric type as long all values are zeros and ones (usually // manually typed masks which by default are of type double. if (val.islogical () || (val.isnumeric() && ! val.array_value ().any_element_not_one_or_zero ())) { try { return connectivity (val.bool_array_value ()); } catch (invalid_connectivity& e) { error ("conndef: CONN %s", e.what ()); } } else if (val.isnumeric () && val.is_scalar_type ()) { if (val.double_value () < 1) error ("conndef: if CONN is a scalar it must be a positive number"); try { return connectivity (val.uint_value ()); } catch (invalid_connectivity& e) { error ("conndef: CONN %s", e.what ()); } } else error ("conndef: CONN must either be a logical array or a numeric scalar"); } image-2.16.1/src/PaxHeaders.61586/connectivity.h0000644000000000000000000000006215005110255016103 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/connectivity.h0000644000175000017500000001246015005110255017504 0ustar00avinoamavinoam00000000000000// Copyright (C) 2014 Carnë Draug // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, see // . #ifndef OCTAVE_IMAGE_CONNDEF #define OCTAVE_IMAGE_CONNDEF #include #include #include #include #include #include #include #include // octave_Inf #include namespace octave_image_pkg { class connectivity { public: connectivity () = default; //! Will throw if val is bad explicit connectivity (const boolNDArray& mask_arg); explicit connectivity (const unsigned int conn); connectivity (const octave_idx_type& ndims, const std::string& type); boolNDArray mask; // For a matrix of size `size', what are the offsets for all of its // connected elements (will have negative and positive values). Array neighbourhood (const dim_vector& size) const; Array deleted_neighbourhood (const dim_vector& size) const; Array positive_neighbourhood (const dim_vector& size) const; Array negative_neighbourhood (const dim_vector& size) const; template T create_padded (const T& image, const P& val) const; template void unpad (T& image) const; //! Return a logical mask of elements that are part of the padding. static boolNDArray padding_mask (const dim_vector& size, const dim_vector& padded_size); //! Set the padding elements to a specific value. template static void set_padding (const dim_vector& size, const dim_vector& padded_size, T& im, const P& val); template static P min_value (void); static Array padding_lengths (const dim_vector& size, const dim_vector& padded_size); private: //! Like Array::ndims() but will return 1 dimension for ColumnVector static octave_idx_type ndims (const dim_vector& d); template static octave_idx_type ndims (const Array& a); }; class invalid_connectivity : public std::invalid_argument { public: invalid_connectivity (const std::string& what_arg) : std::invalid_argument (what_arg) { } }; connectivity conndef (const octave_value& val); } // Templated methods template T octave_image_pkg::connectivity::create_padded (const T& image, const P& val) const { const octave_idx_type pad_ndims = std::min (mask.ndims (), image.ndims ()); Array idx (dim_vector (image.ndims (), 1), 0); dim_vector padded_size = image.dims (); for (octave_idx_type i = 0; i < pad_ndims; i++) { padded_size(i) += 2; idx(i) = 1; } T padded (padded_size, val); // padded(2:end-1, 2:end-1, ..., 2:end-1) = BW padded.insert (image, idx); return padded; } template void octave_image_pkg::connectivity::unpad (T& image) const { const octave_idx_type pad_ndims = std::min (mask.ndims (), image.ndims ()); const dim_vector padded_size = image.dims (); Array inner_slice (dim_vector (image.ndims (), 1)); for (octave_idx_type i = 0; i < pad_ndims ; i++) inner_slice(i) = idx_vector (1, padded_size(i) - 1); for (octave_idx_type i = pad_ndims; i < image.ndims (); i++) inner_slice(i) = idx_vector (0, padded_size(i)); image = image.index (inner_slice); return; } template P octave_image_pkg::connectivity::min_value (void) { if (typeid (P) == typeid (bool)) return false; else return P(-octave_Inf); } template void octave_image_pkg::connectivity::set_padding (const dim_vector& size, const dim_vector& padded_size, T& im, const P& val) { P* im_v = im.fortran_vec (); const Array lengths = padding_lengths (size, padded_size); const octave_idx_type* lengths_v = lengths.data (); const octave_idx_type row_stride = size.xelem (0); std::function fill; fill = [&] (const octave_idx_type dim) -> void { for (octave_idx_type i = 0; i < lengths_v[dim]; i++, im_v++) *im_v = val; if (dim == 0) im_v += row_stride; else for (octave_idx_type i = 0; i < size.xelem (dim); i++) fill (dim -1); for (octave_idx_type i = 0; i < lengths_v[dim]; i++, im_v++) *im_v = val; }; fill (im.ndims () -1); } #endif image-2.16.1/src/PaxHeaders.61586/bwfill.cc0000644000000000000000000000006215005110255015002 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/bwfill.cc0000644000175000017500000002036115005110255016402 0ustar00avinoamavinoam00000000000000// Copyright (C) 1999 Andy Adler // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . #include #include #define ptUP (-1) #define ptDN (+1) #define ptRT (+ioM) #define ptLF (-ioM) /* * check if the point needs to be filled, if so * fill it and change the appropriate variables */ void checkpoint (int pt, unsigned char *imo, int *ptstack, int *npoints) { // printf("filling %d np=%d fill=%d\n",pt,*npoints, *(imo+pt)==0 ); if (*(imo+pt) != 0) return; *(imo+pt) = 2; *(ptstack + (*npoints))= pt; (*npoints)++; } DEFUN_DLD (bwfill, args, ,"\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {[@var{bw2}, @var{idx}] =} bwfill(@var{bw1}, @var{c}, @var{r}, @var{n})\n\ Perform a flood-fill operation on the binary image @var{bw1}.\n\ \n\ The flood-filling starts in the pixel (@var{r}, @var{c}). If @var{r} and @var{c}\n\ are vectors of the same length, each pixel pair (@var{r}(i), @var{c}(i)) will\n\ be a starting point for a flood-fill operation.\n\ The argument @var{n} changes the neighborhood connectivity (of the holes) for the flood-fill\n\ operation. @var{n} can be either 4 or 8, and has a default value of 8.\n\ \n\ Note that @var{n} is the connectivity of the foreground, and not of the background,\n\ even though the function acts on the background.\n\ \n\ The output is the processed image @var{bw2} and the indexes of the filled\n\ pixels @var{idx}\n\ \n\ @end deftypefn\n\ @deftypefn {Loadable Function} {[@var{bw2}, @var{idx}] =} bwfill(@var{bw1}, \"holes\", @var{n})\n\ If the string \"holes\" is given instead of starting points for the flood-fill\n\ operation, the function finds interior holes in @var{bw1} and fills them.\n\ \n\ Note: bwfill is not recommended. Please use \"imfill\" instead.\n\ @seealso{imfill}\n\ @end deftypefn\n\ ") { octave_value_list retval; octave_value tmp; ColumnVector xseed, yseed ; const int nargin = args.length (); if (nargin < 2 || nargin > 4) print_usage (); const Matrix im = args (0).matrix_value (); const int imM = im.rows (); const int imN = im.columns (); if (imM == 1 || imN == 1) // check for vector inputs. { retval (0) = im; retval (1) = ColumnVector (0); return retval; } int nb = 8; int npoints = 0; bool fillmode = false; if (args (1).is_string () && args (1).string_value () == "holes") { // usage: bwfill (A, "holes", [N]) if (nargin > 3) print_usage (); fillmode = true; npoints = 2 * (imM + imN - 4); // don't start fill from corners xseed = ColumnVector (npoints); yseed = ColumnVector (npoints); int idx = 0; for (int j = 2; j <= imN-1; j++) { xseed (idx) = j; yseed (idx++) = 1; xseed (idx) = j; yseed (idx++) = imM; } for (int i = 2; i <= imM-1; i++) { yseed (idx) = i; xseed (idx++) = 1; yseed (idx) = i; xseed (idx++) = imN; } if (nargin >= 3) nb = (int)args (2).double_value (); } else { // usage: bwfill (A, C, R, [N]) if (nargin < 3) print_usage (); { ColumnVector tmp (args (1).vector_value ()); xseed = tmp; } { ColumnVector tmp (args (2).vector_value ()); yseed = tmp; } npoints= xseed.numel (); if (nargin >= 4) nb = (int)args (3).double_value (); } if (nb != 4 && nb != 8) error ("bwfill: connectivity must be 4 or 8"); /* * put a one pixel thick boundary around the image * so that we can be more efficient in the main loop */ int ioM = imM + 2; std::vector imo ((imM+2) * (imN+2)); for (int i = 0; i < imM; i++) for (int j = 0; j < imN; j++) imo[(i+1) + ioM*(j+1)] = (im (i, j) > 0); for (int i = 0; i < ioM; i++) imo[i]= imo[i + ioM*(imN+1)] = 3; for (int j = 1; j < imN+1; j++) imo[ioM*j]= imo[imM+1 + ioM*j] = 3; // This is obviously big enough for the point stack, but I'm // sure it can be smaller. std::vector ptstack (ioM*imN); int seedidx = npoints; npoints = 0; while ((--seedidx) >= 0) { // no need to add 1 to convert indexing style because we're adding a boundary const int x = xseed (seedidx); const int y = yseed (seedidx); if (x < 1 || y < 1 || x > imN || y > imM) { warning ("bwfill: (%d, %d) out of bounds", x, y); continue; } const int pt = x * ioM + y; checkpoint (pt , imo.data (), ptstack.data (), &npoints); } while (npoints > 0) { npoints--; int pt = ptstack[npoints]; checkpoint (pt + ptLF, imo.data (), ptstack.data (), &npoints); checkpoint (pt + ptRT, imo.data (), ptstack.data (), &npoints); checkpoint (pt + ptUP, imo.data (), ptstack.data (), &npoints); checkpoint (pt + ptDN, imo.data (), ptstack.data (), &npoints); if (nb==4) { checkpoint (pt + ptLF + ptUP, imo.data (), ptstack.data (), &npoints); checkpoint (pt + ptRT + ptUP, imo.data (), ptstack.data (), &npoints); checkpoint (pt + ptLF + ptDN, imo.data (), ptstack.data (), &npoints); checkpoint (pt + ptRT + ptDN, imo.data (), ptstack.data (), &npoints); } } // while ( npoints > 0) boolNDArray imout (dim_vector (imM, imN)); ColumnVector idxout (imM*imN); int idx = 0; int notvalidpt = 0; int idxpoint = 2; if (fillmode) { notvalidpt = 2; idxpoint = 0; } for (int i = 0; i < imM; i++) for (int j = 0; j < imN; j++) { imout (i, j) = imo[(i+1) + ioM*(j+1)] != notvalidpt; if (imo[(i+1) + ioM*(j+1)] == idxpoint) idxout (idx++) = (double) (i + j*imM + 1); } /* Matrix imout( imM+2, imN+2 ); for (int i=0; i 0) retval (1) = idxout.extract (0, idx-1); else retval (1) = ColumnVector (0); return retval; } /* %!test %! A = [0 1 0 0 1; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 1 0]; %! R4 = logical(ones(5)); %! R8 = logical([1 1 0 0 1; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 1 0]); %! assert (bwfill (A,1,1,4), R4) %! assert (bwfill (A,1,1,8), R8) %! assert (bwfill (A,1,1), R8) %! B = logical([0 1 0 0 1; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 1 0]); %! assert (bwfill (A,3,3,4), B) %! assert (bwfill (A,3,3,8), B) %! assert (bwfill (A,3,3), B) %! C = logical ([0 1 1 1 1; 1 0 1 1 1; 1 0 1 1 1; 1 1 1 1 1; 1 0 0 1 1]); %! assert (bwfill (A,3,1,8), C) %! assert (bwfill (A,3,1,4), R4) %! assert (bwfill (A, [3 1], [1 3], 4), R4); %! D = logical([0 1 1 1 1; 1 0 1 1 1; 1 0 1 1 1; 1 1 1 1 1; 1 0 0 1 1]); %! assert (bwfill (A, [3 1], [1 3], 8), D); %! assert (bwfill (A, [3 1], [1 3]), D); %! E = logical ([0 1 0 0 1; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 1 0]); %! assert (bwfill (A, "holes", 4), E); %! F = logical ([1 1 0 0 1; 1 1 1 0 0; 1 1 1 1 0; 1 1 1 0 0; 1 0 0 1 0]); %! assert (bwfill (A, "holes", 8), F); %! assert (bwfill (A, "holes"), F); %!error id=Octave:invalid-fun-call bwfill () %!error id=Octave:invalid-fun-call bwfill ("aaa") %!error id=Octave:invalid-fun-call bwfill (rand (5) > 0.5) %!error id=Octave:invalid-fun-call bwfill (rand (5) > 0.5, 2) %!error bwfill (rand (5) > 0.5, "holes", 1) %!error bwfill (rand (5) > 0.5, 2, 2, 5) %!error id=Octave:invalid-fun-call bwfill (rand (5) > 0.5, "xxx") %!error id=Octave:invalid-fun-call bwfill (rand (5) > 0.5, 2, 2, 4, 5) %!error id=Octave:invalid-fun-call bwfill (rand (5) > 0.5, "holes", 4, 2) */ image-2.16.1/src/PaxHeaders.61586/bwlabeln.cc0000644000000000000000000000006215005110255015311 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/bwlabeln.cc0000644000175000017500000006672115005110255016723 0ustar00avinoamavinoam00000000000000// Copyright (C) 2002 Jeffrey E. Boyd // Copyright (C) 2011-2012 Jordi Gutiérrez Hermoso // Copyright (C) 2013 Carnë Draug // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, see // . // Copyright // Jeffrey E. Boyd and Carnë Draug for bwlabel_2d // Jordi Gutiérrez Hermoso for bwlabel_nd #include #include #include #include #include #include "union-find.h" #include "connectivity.h" using namespace octave_image_pkg; static union_find pre_label (NDArray& L, const connectivity& conn) { double* L_vec = L.fortran_vec (); const octave_idx_type numel = L.numel (); const Array neighbours = conn.negative_neighbourhood (L.dims ()); const octave_idx_type* nbr = neighbours.data (); const octave_idx_type nbr_numel = neighbours.numel (); union_find u_f (numel); for (octave_idx_type Lidx = 0; Lidx < numel; Lidx++) { // The boundary is always zero, so we'll always skip it, so // we're never considering the neighbours of the boundary. Thus, // there is no possibility of out-of-bounds error below. if (L_vec[Lidx]) { //Insert this one into its group u_f.add (Lidx); for (octave_idx_type i = 0; i < nbr_numel; i++) { octave_idx_type n = *nbr++ + Lidx; if (L_vec[n]) u_f.unite (n, Lidx); } nbr -= nbr_numel; } } return u_f; } static octave_idx_type paint_labels (NDArray& L, union_find& u_f) { double* L_vec = L.fortran_vec (); std::unordered_map ids_to_label; octave_idx_type next_label = 1; std::vector idxs = u_f.get_ids (L); for (auto idx = idxs.begin (); idx != idxs.end (); idx++) { octave_idx_type label; octave_idx_type id = u_f.find (*idx); auto try_label = ids_to_label.find (id); if (try_label == ids_to_label.end ()) { label = next_label++; ids_to_label[id] = label; } else label = try_label->second; L_vec[*idx] = label; } return ids_to_label.size (); } static octave_value_list bwlabel_nd (const boolNDArray& BW, const connectivity& conn) { boolNDArray conn_mask = conn.mask; const dim_vector size_vec = BW.dims (); // Use temporary array with borders padded with zeros. Labels will // also go in here eventually. NDArray L = conn.create_padded (BW, 0); union_find u_f = pre_label (L, conn); octave_idx_type n_labels = paint_labels (L, u_f); // Remove the zero padding... conn.unpad (L); octave_value_list rval; rval(0) = L; rval(1) = n_labels; return rval; } static octave_idx_type find (std::vector& lset, octave_idx_type x) { // Follow lset until we find a value that points to itself while (lset[x] != x) x = lset[x]; return x; } static octave_value_list bwlabel_2d (const boolMatrix& BW, const octave_idx_type& n) { // This algorithm was derived from BKP Horn, Robot Vision, MIT Press, // 1986, p 65 - 89 by Jeffrey E. Boyd in 2002. Some smaller changes // were then introduced by Carnë Draug in 2013 to speed up by iterating // down a column, and what values to use when connecting two labels // to increase chance of getting them in the right order in the end. const octave_idx_type nr = BW.rows (); const octave_idx_type nc = BW.columns (); // The labelled image Matrix L (nr, nc); std::vector lset (nc*nr); // label table/tree octave_idx_type ntable = 0; // number of elements in the component table/tree octave_idx_type ind = 0; // linear index bool n4, n6, n8; n4 = n6 = n8 = false; if (n == 4) n4 = true; else if (n == 6) n6 = true; else if (n == 8) n8 = true; const bool* BW_vec = BW.data (); double* L_vec = L.fortran_vec (); for (octave_idx_type c = 0; c < nc; c++) { for (octave_idx_type r = 0; r < nr; r++, ind++) { if (BW_vec[ind]) // if A is an object { octave_idx_type stride = ind - nr; // Get the neighboring pixels B, C, D, and E // // D B // C A <-- ind is linear index to A // E // // C and B will always be needed so we get them here, but // D is only needed when n is 6 or 8, and E when n is 8. octave_idx_type B, C; if (c == 0) C = 0; else C = find (lset, L_vec[stride]); if (r == 0) B = 0; else B = find (lset, L_vec[ind -1]); if (n4) { // apply 4 connectedness if (B && C) // B and C are labeled { if (B != C) lset[B] = C; L_vec[ind] = C; } else if (B) // B is object but C is not L_vec[ind] = B; else if (C) // C is object but B is not L_vec[ind] = C; else // B, C not object - new object { // label and put into table ntable++; L_vec[ind] = lset[ntable] = ntable; } } else if (n6) { // Apply 6 connectedness. Seem there's more than one // possible way to do this for 2D images but for some // reason, the most common seems to be the top left pixel // and the bottom right // See http://en.wikipedia.org/wiki/Pixel_connectivity octave_idx_type D; // D is only required for n6 and n8 if (r == 0 || c == 0) D = 0; else D = find (lset, L_vec[stride -1]); if (D) // D object, copy label and move on L_vec[ind] = D; else if (B && C) // B and C are labeled { if (B == C) L_vec[ind] = B; else { octave_idx_type tlabel = std::min (B, C); lset[B] = tlabel; lset[C] = tlabel; L_vec[ind] = tlabel; } } else if (B) // B is object but C is not L_vec[ind] = B; else if (C) // C is object but B is not L_vec[ind] = C; else // B, C, D not object - new object { // label and put into table ntable++; L_vec[ind] = lset[ntable] = ntable; } } else if (n8) { octave_idx_type D, E; // D is only required for n6 and n8 if (r == 0 || c == 0) D = 0; else D = find (lset, L_vec[stride -1]); // E is only required for n8 if (c == 0 || r == nr -1) E = 0; else E = find (lset, L_vec[stride +1]); // apply 8 connectedness if (B || C || D || E) { octave_idx_type tlabel = D; if (D) ; // do nothing (tlabel is already D) else if (C) tlabel = C; else if (E) tlabel = E; else if (B) tlabel = B; L_vec[ind] = tlabel; if (B && B != tlabel) lset[B] = tlabel; if (C && C != tlabel) lset[C] = tlabel; if (D) // we don't check if B != tlabel since if B // is true, tlabel == B lset[D] = tlabel; if (E && E != tlabel) lset[E] = tlabel; } else { // label and put into table ntable++; // run image through the look-up table L_vec[ind] = lset[ntable] = ntable; } } } else L_vec[ind] = 0; // A is not an object so leave it } } const octave_idx_type numel = BW.numel (); // consolidate component table for (octave_idx_type i = 0; i <= ntable; i++) lset[i] = find (lset, i); // run image through the look-up table for (octave_idx_type ind = 0; ind < numel; ind++) L_vec[ind] = lset[L_vec[ind]]; // count up the objects in the image for (octave_idx_type i = 0; i <= ntable; i++) lset[i] = 0; for (octave_idx_type ind = 0; ind < numel; ind++) lset[L_vec[ind]]++; // number the objects from 1 through n objects octave_idx_type nobj = 0; lset[0] = 0; for (octave_idx_type i = 1; i <= ntable; i++) if (lset[i] > 0) lset[i] = ++nobj; // Run through the look-up table again, so that their numbers // match the number of labels for (octave_idx_type ind = 0; ind < numel; ind++) L_vec[ind] = lset[L_vec[ind]]; octave_value_list rval; rval(0) = L; rval(1) = double (nobj); return rval; } DEFUN_DLD(bwlabeln, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {[@var{l}, @var{num}] =} bwlabeln (@var{bw})\n\ @deftypefnx {Loadable Function} {[@var{l}, @var{num}] =} bwlabeln (@var{bw}, @var{n})\n\ Label foreground objects in the n-dimensional binary image @var{bw}.\n\ \n\ The optional argument @var{n} sets the connectivity and defaults 26,\n\ for 26-connectivity in 3-D images. Other possible values are 18 and 6\n\ for 3-D images, 4 and 8 for 2-D images, or an arbitrary N-dimensional\n\ binary connectivity mask where each dimension is of size 3.\n\ \n\ The output @var{l} is an Nd-array where 0 indicates a background\n\ pixel, 1 indicates that the pixel belongs to object number 1, 2 that\n\ the pixel belongs to object number 2, etc. The total number of objects\n\ is @var{num}.\n\ \n\ The algorithm used is a disjoint-set data structure, a.k.a. union-find.\n\ See, for example, http://en.wikipedia.org/wiki/Union-find\n\ \n\ @seealso{bwconncomp, bwlabel, regionprops}\n\ @end deftypefn\n\ ") { octave_value_list rval; const octave_idx_type nargin = args.length (); if (nargin < 1 || nargin > 2) print_usage (); octave_value bw_value (args(0)); if (! bw_value.isnumeric () && ! bw_value.islogical ()) error ("bwlabeln: BW must be a numeric or logical matrix"); boolNDArray BW = bw_value.bool_array_value (); dim_vector size_vec = BW.dims (); connectivity conn; if (nargin == 2) conn = conndef (args(1)); else { try { conn = connectivity (BW.ndims (), "maximal"); } catch (invalid_connectivity& e) { error ("bwlabeln: failed to create MASK (%s)", e.what ()); } } // The implementation in bwlabel_2d is faster so use it if we can const octave_idx_type ndims = BW.ndims (); if (ndims == 2 && boolMatrix (conn.mask) == connectivity (4).mask) rval = bwlabel_2d (BW, 4); else if (ndims == 2 && boolMatrix (conn.mask) == connectivity (8).mask) rval = bwlabel_2d (BW, 8); else rval = bwlabel_nd (BW, conn); return rval; } /* %!shared a2d, a3d %! a2d = [1 0 0 0 0 0 1 0 0 1 %! 1 0 0 1 0 1 0 1 0 1 %! 1 0 1 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 1 1 1 0 0 0 0 %! 1 1 0 1 0 0 0 1 0 0 %! 1 1 0 0 0 0 1 0 1 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 1 1 0 0 1]; %! %! a3d = a2d; %! a3d(:,:,2) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 1 1 0 0 1 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 1 1 0 0 0 0 %! 1 1 0 1 0 0 0 0 0 0 %! 1 0 0 0 0 0 1 0 0 0 %! 0 1 0 0 0 0 0 0 0 1 %! 1 1 0 0 0 0 1 0 0 0]; %! %! a3d(:,:,3) = [ %! 1 0 0 0 0 0 0 0 0 0 %! 0 1 0 1 1 0 0 1 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 1 %! 1 1 0 0 0 0 0 0 0 0]; %!test %! label2dc4 = [ %! 1 0 0 0 0 0 8 0 0 13 %! 1 0 0 4 0 6 0 10 0 13 %! 1 0 3 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 2 0 0 0 0 0 0 0 0 %! 2 2 0 5 5 5 0 0 0 0 %! 2 2 0 5 0 0 0 11 0 0 %! 2 2 0 0 0 0 9 0 12 0 %! 2 2 0 0 0 0 0 0 0 0 %! 2 2 0 0 0 7 7 0 0 14]; %! assert (bwlabeln (a2d, 4), label2dc4) %! assert (bwlabeln (a2d, [0 1 0; 1 1 1; 0 1 0]), label2dc4) %! assert (bwlabeln (a2d, conndef (2, "minimal")), label2dc4) %! assert (bwlabeln (a2d, conndef (3, "minimal")), label2dc4) %!test %! label2dc8 = [ %! 1 0 0 0 0 0 5 0 0 8 %! 1 0 0 3 0 5 0 5 0 8 %! 1 0 3 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 2 0 0 0 0 0 0 0 0 %! 2 2 0 4 4 4 0 0 0 0 %! 2 2 0 4 0 0 0 7 0 0 %! 2 2 0 0 0 0 7 0 7 0 %! 2 2 0 0 0 0 0 0 0 0 %! 2 2 0 0 0 6 6 0 0 9]; %! assert (bwlabeln (a2d, 8), label2dc8) %! assert (bwlabeln (a2d, ones (3)), label2dc8) %! assert (bwlabeln (a2d, conndef (2, "maximal")), label2dc8) %! assert (bwlabeln (a2d, conndef (3, "maximal")), label2dc8) %!test %! label3dc8 = [ %! 1 0 0 0 0 0 5 0 0 8 %! 1 0 0 3 0 5 0 5 0 8 %! 1 0 3 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 2 0 0 0 0 0 0 0 0 %! 2 2 0 4 4 4 0 0 0 0 %! 2 2 0 4 0 0 0 7 0 0 %! 2 2 0 0 0 0 7 0 7 0 %! 2 2 0 0 0 0 0 0 0 0 %! 2 2 0 0 0 6 6 0 0 9]; %! label3dc8(:,:,2) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 10 0 0 12 12 0 0 16 0 0 %! 0 0 0 12 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 11 0 0 0 0 0 0 0 0 %! 11 11 0 0 13 13 0 0 0 0 %! 11 11 0 13 0 0 0 0 0 0 %! 11 0 0 0 0 0 14 0 0 0 %! 0 11 0 0 0 0 0 0 0 17 %! 11 11 0 0 0 0 15 0 0 0]; %! label3dc8(:,:,3) = [ %! 18 0 0 0 0 0 0 0 0 0 %! 0 18 0 20 20 0 0 22 0 0 %! 0 0 0 20 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 21 21 21 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 19 0 0 0 0 0 0 0 0 0 %! 19 19 0 0 0 0 0 0 0 23 %! 19 19 0 0 0 0 0 0 0 0]; %! assert (bwlabeln (a3d, 8), label3dc8) %! assert (bwlabeln (a3d, ones (3, 3)), label3dc8) %! assert (bwlabeln (a3d, conndef (2, "maximal")), label3dc8) %!test %! label3dc26 = [ %! 1 0 0 0 0 0 3 0 0 7 %! 1 0 0 3 0 3 0 3 0 7 %! 1 0 3 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 2 0 0 0 0 0 0 0 0 %! 2 2 0 4 4 4 0 0 0 0 %! 2 2 0 4 0 0 0 6 0 0 %! 2 2 0 0 0 0 6 0 6 0 %! 2 2 0 0 0 0 0 0 0 0 %! 2 2 0 0 0 5 5 0 0 6]; %! label3dc26(:,:,2) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 3 3 0 0 3 0 0 %! 0 0 0 3 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 2 0 0 0 0 0 0 0 0 %! 2 2 0 0 4 4 0 0 0 0 %! 2 2 0 4 0 0 0 0 0 0 %! 2 0 0 0 0 0 6 0 0 0 %! 0 2 0 0 0 0 0 0 0 6 %! 2 2 0 0 0 0 5 0 0 0]; %! label3dc26(:,:,3) = [ %! 1 0 0 0 0 0 0 0 0 0 %! 0 1 0 3 3 0 0 3 0 0 %! 0 0 0 3 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 4 4 4 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 2 0 0 0 0 0 0 0 0 0 %! 2 2 0 0 0 0 0 0 0 6 %! 2 2 0 0 0 0 0 0 0 0]; %! assert (bwlabeln (a3d, 26), label3dc26) %! assert (bwlabeln (a3d, ones (3, 3, 3)), label3dc26) %! assert (bwlabeln (a3d, conndef (3, "maximal")), label3dc26) %!test %! label3dc18 = [ %! 1 0 0 0 0 0 3 0 0 7 %! 1 0 0 3 0 3 0 3 0 7 %! 1 0 3 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 2 0 0 0 0 0 0 0 0 %! 2 2 0 4 4 4 0 0 0 0 %! 2 2 0 4 0 0 0 6 0 0 %! 2 2 0 0 0 0 6 0 6 0 %! 2 2 0 0 0 0 0 0 0 0 %! 2 2 0 0 0 5 5 0 0 8]; %! label3dc18(:,:,2) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 3 3 0 0 3 0 0 %! 0 0 0 3 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 2 0 0 0 0 0 0 0 0 %! 2 2 0 0 4 4 0 0 0 0 %! 2 2 0 4 0 0 0 0 0 0 %! 2 0 0 0 0 0 6 0 0 0 %! 0 2 0 0 0 0 0 0 0 8 %! 2 2 0 0 0 0 5 0 0 0]; %! label3dc18(:,:,3) = [ %! 1 0 0 0 0 0 0 0 0 0 %! 0 1 0 3 3 0 0 3 0 0 %! 0 0 0 3 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 4 4 4 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 2 0 0 0 0 0 0 0 0 0 %! 2 2 0 0 0 0 0 0 0 8 %! 2 2 0 0 0 0 0 0 0 0]; %! assert (bwlabeln (a3d, 18), label3dc18) %!test %! label2dc3 = [ %! 1 0 0 0 0 0 11 0 0 17 %! 1 0 0 5 0 8 0 14 0 17 %! 1 0 4 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 3 0 0 0 0 0 0 0 0 %! 2 3 0 6 7 9 0 0 0 0 %! 2 3 0 6 0 0 0 15 0 0 %! 2 3 0 0 0 0 12 0 16 0 %! 2 3 0 0 0 0 0 0 0 0 %! 2 3 0 0 0 10 13 0 0 18]; %! assert (bwlabeln (a2d, [1 1 1]'), label2dc3) %! %! label3dc3 = label2dc3; %! label3dc3(:,:,2) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 19 0 0 24 26 0 0 31 0 0 %! 0 0 0 24 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 22 0 0 0 0 0 0 0 0 %! 20 22 0 0 27 28 0 0 0 0 %! 20 22 0 25 0 0 0 0 0 0 %! 20 0 0 0 0 0 29 0 0 0 %! 0 23 0 0 0 0 0 0 0 32 %! 21 23 0 0 0 0 30 0 0 0]; %! label3dc3(:,:,3) = [ %! 33 0 0 0 0 0 0 0 0 0 %! 0 35 0 37 39 0 0 42 0 0 %! 0 0 0 37 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 38 40 41 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 34 0 0 0 0 0 0 0 0 0 %! 34 36 0 0 0 0 0 0 0 43 %! 34 36 0 0 0 0 0 0 0 0]; %! assert (bwlabeln (a3d, [1 1 1]'), label3dc3) %!test %! label2dc1 = zeros (size (a2d)); %! label2dc1(a2d != 0) = 1:nnz (a2d); %! assert (bwlabeln (a2d, [1]), label2dc1); %! assert (bwlabeln (a2d, [0 1 0]'), label2dc1); %! %! label3dc1 = zeros (size (a3d)); %! label3dc1(a3d != 0) = 1:nnz (a3d); %! assert (bwlabeln (a3d, [1]), label3dc1); %! assert (bwlabeln (a3d, [0 1 0]'), label3dc1); */ // PKG_ADD: autoload ("bwlabel", which ("bwlabeln")); // PKG_DEL: autoload ("bwlabel", which ("bwlabeln"), "remove"); DEFUN_DLD(bwlabel, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {[@var{l}, @var{num}] =} bwlabel(@var{BW})\n\ @deftypefnx {Loadable Function} {[@var{l}, @var{num}] =} bwlabel(@var{BW}, @var{n})\n\ Label binary 2 dimensional image.\n\ \n\ Labels foreground objects in the binary image @var{bw}.\n\ The output @var{l} is a matrix where 0 indicates a background pixel,\n\ 1 indicates that the pixel belongs to object number 1, 2 that the pixel\n\ belongs to object number 2, etc.\n\ The total number of objects is @var{num}.\n\ \n\ Two pixels belong to the same object if they are neighbors. By default\n\ the algorithm uses 8-connectivity to define a neighborhood, but this\n\ can be changed through the argument @var{n}, which can be either 4, 6, or 8.\n\ \n\ @seealso{bwconncomp, bwlabeln, regionprops}\n\ @end deftypefn\n\ ") { octave_value_list rval; const octave_idx_type nargin = args.length (); if (nargin < 1 || nargin > 2) print_usage (); // We do not check error state after conversion to boolMatrix // because what we want is to actually get a boolean matrix // with all non-zero elements as true (Matlab compatibility). octave_value bw_value (args(0)); if ((! bw_value.isnumeric () && ! bw_value.islogical ()) || bw_value.ndims () != 2) error ("bwlabel: BW must be a 2D matrix"); // For some reason, we can't use bool_matrix_value() to get a // a boolMatrix since it will error if there's values other // than 0 and 1 (whatever bool_array_value() does, bool_matrix_value() // does not). const boolMatrix BW = bw_value.bool_array_value (); // N-hood connectivity const octave_idx_type n = nargin < 2 ? 8 : args(1).idx_type_value (); if (n != 4 && n!= 6 && n != 8) error ("bwlabel: BW must be a 2 dimensional matrix"); return bwlabel_2d (BW, n); } /* %!shared in %! in = rand (10) > 0.8; %!assert (bwlabel (in, 4), bwlabeln (in, 4)); %!assert (bwlabel (in, 4), bwlabeln (in, [0 1 0; 1 1 1; 0 1 0])); %!assert (bwlabel (in, 8), bwlabeln (in, 8)); %!assert (bwlabel (in, 8), bwlabeln (in, [1 1 1; 1 1 1; 1 1 1])); %!assert (bwlabel (logical ([0 1 0; 0 0 0; 1 0 1])), [0 2 0; 0 0 0; 1 0 3]); %!assert (bwlabel ([0 1 0; 0 0 0; 1 0 1]), [0 2 0; 0 0 0; 1 0 3]); ## Support any type of real non-zero value %!assert (bwlabel ([0 -1 0; 0 0 0; 5 0 0.2]), [0 2 0; 0 0 0; 1 0 3]); %!shared in, out %! %! in = [ 0 1 1 0 0 1 0 0 0 0 %! 0 0 0 1 0 0 0 0 0 1 %! 0 1 1 0 0 0 0 0 1 1 %! 1 0 0 0 0 0 0 1 0 0 %! 0 0 0 0 0 1 1 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 1 1 0 1 0 0 %! 0 0 0 1 0 1 0 1 0 1 %! 1 1 0 0 0 0 0 1 1 0]; %! %! out = [ 0 3 3 0 0 9 0 0 0 0 %! 0 0 0 5 0 0 0 0 0 13 %! 0 4 4 0 0 0 0 0 13 13 %! 1 0 0 0 0 0 0 11 0 0 %! 0 0 0 0 0 10 10 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 6 0 0 0 0 0 0 %! 0 0 0 0 8 8 0 12 0 0 %! 0 0 0 7 0 8 0 12 0 14 %! 2 2 0 0 0 0 0 12 12 0]; %!assert (nthargout ([1 2], @bwlabel, in, 4), {out, 14}); %!assert (nthargout ([1 2], @bwlabel, logical (in), 4), {out, 14}); %! %! out = [ 0 3 3 0 0 7 0 0 0 0 %! 0 0 0 3 0 0 0 0 0 11 %! 0 4 4 0 0 0 0 0 11 11 %! 1 0 0 0 0 0 0 9 0 0 %! 0 0 0 0 0 8 8 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 5 0 0 0 0 0 0 %! 0 0 0 0 5 5 0 10 0 0 %! 0 0 0 6 0 5 0 10 0 12 %! 2 2 0 0 0 0 0 10 10 0]; %!assert (nthargout ([1 2], @bwlabel, in, 6), {out, 12}); %!assert (nthargout ([1 2], @bwlabel, logical (in), 6), {out, 12}); %! %! ## The labeled image is not the same as Matlab, but they are %! ## labeled correctly. Do we really need to get them properly %! ## ordered? (the algorithm in bwlabeln does it) %! mout = [0 1 1 0 0 4 0 0 0 0 %! 0 0 0 1 0 0 0 0 0 5 %! 0 1 1 0 0 0 0 0 5 5 %! 1 0 0 0 0 0 0 5 0 0 %! 0 0 0 0 0 5 5 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 3 0 0 0 0 0 0 %! 0 0 0 0 3 3 0 6 0 0 %! 0 0 0 3 0 3 0 6 0 6 %! 2 2 0 0 0 0 0 6 6 0]; %! %! out = [ 0 2 2 0 0 4 0 0 0 0 %! 0 0 0 2 0 0 0 0 0 5 %! 0 2 2 0 0 0 0 0 5 5 %! 2 0 0 0 0 0 0 5 0 0 %! 0 0 0 0 0 5 5 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 3 0 0 0 0 0 0 %! 0 0 0 0 3 3 0 6 0 0 %! 0 0 0 3 0 3 0 6 0 6 %! 1 1 0 0 0 0 0 6 6 0]; %!assert (nthargout ([1 2], @bwlabel, in, 8), {out, 6}); %!assert (nthargout ([1 2], @bwlabel, logical (in), 8), {out, 6}); %! %!error bwlabel (rand (10, 10, 10) > 0.8, 4) %!error bwlabel (rand (10) > 0.8, "text") %!error bwlabel ("text", 6) */ image-2.16.1/src/PaxHeaders.61586/__bilateral__.cc0000644000000000000000000000006215005110255016256 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/__bilateral__.cc0000644000175000017500000001301615005110255017655 0ustar00avinoamavinoam00000000000000// Copyright (C) 2008 Søren Hauberg // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . #include #include inline double gauss (const std::vector x, const std::vector mu, const double sigma) { double s = 0; for (size_t i = 0; i < x.size (); i++) { const double d = x[i] - mu[i]; s += d*d; } return exp (-0.5*s/(sigma*sigma)); } template octave_value bilateral (const MatrixType &im, const double sigma_d, const double sigma_r) { // Get sizes const octave_idx_type ndims = im.ndims (); const dim_vector size = im.dims (); const octave_idx_type num_planes = (ndims == 2) ? 1 : size (2); // Build spatial kernel const int s = std::max ((int)std::round (3*sigma_d), 1); Matrix kernel (2*s+1, 2*s+1); for (octave_idx_type r = 0; r < 2*s+1; r++) { for (octave_idx_type c = 0; c < 2*s+1; c++) { const int dr = r-s; const int dc = c-s; kernel (r,c) = exp (-0.5 * (dr*dr + dc*dc)/(sigma_d*sigma_d)); } } // Allocate output dim_vector out_size (size); out_size (0) = std::max (size (0) - 2*s, (octave_idx_type)0); out_size (1) = std::max (size (1) - 2*s, (octave_idx_type)0); MatrixType out = MatrixType (out_size); // Iterate over every element of 'out'. for (octave_idx_type r = 0; r < out_size (0); r++) { for (octave_idx_type c = 0; c < out_size (1); c++) { OCTAVE_QUIT; // For each neighbour std::vector val (num_planes); std::vector sum (num_planes); double k = 0; for (octave_idx_type i = 0; i < num_planes; i++) { val[i] = im (r+s,c+s,i); sum[i] = 0; } for (octave_idx_type kr = 0; kr < 2*s+1; kr++) { for (octave_idx_type kc = 0; kc < 2*s+1; kc++) { std::vector lval (num_planes); for (octave_idx_type i = 0; i < num_planes; i++) lval[i] = im (r+kr, c+kc, i); const double w = kernel (kr, kc) * gauss (val, lval, sigma_r); for (octave_idx_type i = 0; i < num_planes; i++) sum[i] += w * lval[i]; k += w; } } for (octave_idx_type i = 0; i < num_planes; i++) out (r, c, i) = sum[i]/k; } } return octave_value (out); } DEFUN_DLD (__bilateral__, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} __bilateral__(@var{im}, @var{sigma_d}, @var{sigma_r})\n\ Performs Gaussian bilateral filtering in the image @var{im}. @var{sigma_d} is the\n\ spread of the Gaussian used as closenes function, and @var{sigma_r} is the spread\n\ of Gaussian used as similarity function. This function is internal and should NOT\n\ be called directly. Instead use @code{imsmooth}.\n\ @end deftypefn\n\ ") { octave_value_list retval; if (args.length () != 3) print_usage (); const octave_idx_type ndims = args (0).ndims (); if (ndims != 2 && ndims != 3) error ("__bilateral__: only 2 and 3 dimensional is supported"); const double sigma_d = args (1).scalar_value (); const double sigma_r = args (2).scalar_value (); // Take action depending on input type if (args (0).is_real_matrix ()) { const NDArray im = args(0).array_value (); retval = bilateral (im, sigma_d, sigma_r); } else if (args (0).is_int8_type ()) { const int8NDArray im = args (0).int8_array_value (); retval = bilateral (im, sigma_d, sigma_r); } else if (args (0).is_int16_type ()) { const int16NDArray im = args (0).int16_array_value (); retval = bilateral (im, sigma_d, sigma_r); } else if (args (0).is_int32_type ()) { const int32NDArray im = args (0).int32_array_value (); retval = bilateral (im, sigma_d, sigma_r); } else if (args (0).is_int64_type ()) { const int64NDArray im = args (0).int64_array_value (); retval = bilateral (im, sigma_d, sigma_r); } else if (args (0).is_uint8_type ()) { const uint8NDArray im = args (0).uint8_array_value (); retval = bilateral (im, sigma_d, sigma_r); } else if (args(0).is_uint16_type()) { const uint16NDArray im = args (0).uint16_array_value (); retval = bilateral (im, sigma_d, sigma_r); } else if (args (0).is_uint32_type ()) { const uint32NDArray im = args (0).uint32_array_value (); retval = bilateral (im, sigma_d, sigma_r); } else if (args (0).is_uint64_type ()) { const uint64NDArray im = args (0).uint64_array_value (); retval = bilateral (im, sigma_d, sigma_r); } else error ("__bilateral__: first input should be a real or integer array"); return retval; } image-2.16.1/src/PaxHeaders.61586/strel.h0000644000000000000000000000006215005110255014516 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/strel.h0000644000175000017500000001042115005110255016112 0ustar00avinoamavinoam00000000000000// Copyright (C) 2013 Carnë Draug // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . // This is wrapper class for the @strel class so that it can be used by // the rest of the image package using SE's. It's not a perfect wrapper // on purpose. For example, the reflect method behaves kinda weird for // matlab compatibility. In here we try to make a bit more sense. // An important thing about this class is how the origin (defaults to // center coordinates which can have different interpretations when // sides are of even length) moves when it's reflected. Consider the // following (x is the origin) // // o o o o o o // o x o -- reflect --> o x o // o o o o o o // // o o o o o o o o // o o x o -- reflect --> o x o o // o o o o o o o o #ifndef OCTAVE_IMAGE_STREL #define OCTAVE_IMAGE_STREL #include #include #include #include #include #include #include namespace octave_image_pkg { class strel { public: explicit strel (const octave_value& arg); strel (const boolNDArray& nhood, const NDArray& height); strel (const boolNDArray& nhood, const NDArray& height, const Array& origin); boolNDArray get_nhood (void) const; octave_idx_type get_nnz (void) const; Array get_origin (void) const; strel operator () (const octave_idx_type& i) const; // Number of strel objects after decomposition, NOT numel of nhood octave_idx_type numel (void) const; // flat SE? bool flat (void) const; // set origin of the SE to specific coordinates void set_origin (const Array& sub); // reflect the SE (rotates is 180 degrees in all dimensions). // Note that when rotating it, the origin coordinates move with it // (this is by design see ratinoal on top of this file). strel reflect (void) const; // given a matrix with a specific cumulative size, what's the offset // for each true element of the nhood from the first element (true or // false) of the nhood. Array offsets (const dim_vector& cum_size) const; // array with height of each true element in the nhood (same order // as the one returned by offsets). template Array

true_heights (void) const; Array pre_pad (const octave_idx_type& mt_ndims, const std::string& shape) const; Array post_pad (const octave_idx_type& mt_ndims, const std::string& shape) const; private: boolNDArray nhood; NDArray height; octave_idx_type nnz; Array origin; dim_vector size; octave_idx_type ndims; std::vector decomposition; void ini_ctor (void); Array default_origin (void); void end_ctor (void); void validate_origin (void); }; } // Define it on header or we we need to instantiate it for all // possible classes in strel.cc template Array

octave_image_pkg::strel::true_heights (void) const { Array

true_heights (dim_vector (nnz, 1)); for (octave_idx_type ind = 0, found = 0; found < nnz; ind++) if (nhood(ind)) true_heights(found++) = height(ind); return true_heights; } #endif image-2.16.1/src/PaxHeaders.61586/configure.ac0000644000000000000000000000007415005110255015505 xustar0030 atime=1746179027.222725859 30 ctime=1746179027.274726131 image-2.16.1/src/configure.ac0000644000175000017500000000331615005110255017103 0ustar00avinoamavinoam00000000000000## Copyright (C) 2020 David Miguel Susano Pinto ## ## Copying and distribution of this file, with or without modification, ## are permitted in any medium without royalty provided the copyright ## notice and this notice are preserved. This file is offered as-is, ## without any warranty. AC_PREREQ([2.67]) AC_INIT([Octave-Forge image package], [2.16.1]) AC_PATH_PROG([MKOCTFILE], [mkoctfile]) if test -z "$MKOCTFILE"; then AC_MSG_ERROR([*** 'mkoctfile' not found.]) fi AC_LANG(C++) image_save_CXX="$CXX" image_save_CXXFLAGS="$CXXFLAGS" image_save_CPPFLAGS="$CPPFLAGS" CXX=`${MKOCTFILE} -p CXX` CXXFLAGS=`${MKOCTFILE} -p CXXFLAGS` CPPFLAGS=`${MKOCTFILE} -p CPPFLAGS` ## Test for gcc bug #65843 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65843 ## which shows up as Octave bug #45096 https://savannah.gnu.org/bugs/?45096 AC_CACHE_CHECK([whether templated lambda functions accept '&const int'], [_cv_template_lambda_accepts_ref_const_inst], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ template void test (T b) { const int a = b; [&] () { return a, a; }(); } ]], [[ test (1); ]])], [_cv_template_lambda_accepts_ref_const_inst=yes], [_cv_template_lambda_accepts_ref_const_inst=no]) ]) if test $_cv_template_lambda_accepts_ref_const_inst = no; then AC_MSG_WARN([ Your C++ compiler (are you using GCC 5.0 or 5.1?) has a bug that prevents it from building the Octave Forge image package. But you can fix it very easily. See https://savannah.gnu.org/bugs/?45096 for details on working around it. ]) fi CXX="$image_save_CXX" CXXFLAGS="$image_save_CXXFLAGS" CPPFLAGS="$image_save_CPPFLAGS" AC_CONFIG_FILES([Makefile]) AC_OUTPUT image-2.16.1/src/PaxHeaders.61586/graycomatrix.cc0000644000000000000000000000006215005110255016234 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/graycomatrix.cc0000644000175000017500000001162015005110255017632 0ustar00avinoamavinoam00000000000000// Copyright (C) 2004 Stefan van der Walt // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1 Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // 2 Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include DEFUN_DLD(graycomatrix, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {@var{P} =} graycomatrix(@var{im}, @var{levels}, @var{distances}, @var{angles})\n\ Calculates the gray-level co-occurrence matrix @var{P} of a gray-level image @var{im}.\n\ \n\ @var{P} is a 4-dimensional matrix (histogram). The value @var{P}(@var{i},@var{j},@var{d},@var{theta})\n\ is the number of times that gray-level @var{j} occurs at a distance @var{d} and\n\ at an angle @var{theta} from gray-level @var{i}.\n\ \n\ @var{im} is the input image which should contain integers in [0, @var{levels}-1],\n\ where @var{levels} indicate the number of gray-levels counted (typically\n\ 256 for an 8-bit image). @var{distances} and @var{angles} are vectors of\n\ the different distances and angles to use.\n\ @end deftypefn\n\ " ) { // 4-dimensional histogram // P = f(i, j, d, theta) where i and j are gray levels // See Pattern Recognition Engineering (Morton Nadler & Eric P. Smith) octave_value_list retval; if (args.length() != 4) print_usage (); // Input arguments Matrix I = args(0).matrix_value(); int L = args(1).int_value(); ColumnVector d = ColumnVector(args(2).vector_value()); ColumnVector th = ColumnVector(args(3).vector_value()); // Create output NDArray, P dim_vector dim = dim_vector(); dim.resize(4); dim(0) = L; dim(1) = L; dim(2) = d.numel(); dim(3) = th.numel(); NDArray P = NDArray(dim, 0); // Run through image //int d_max = (int)ceil(d.max()); //unused //int cnt = 0; //unused for (int r = 0; r < I.rows(); r++) { for (int c = 0; c < I.columns(); c++) { OCTAVE_QUIT; int i = (int)I(r,c); for (int d_idx = 0; d_idx < d.numel(); d_idx++) { int d_val = (int)d(d_idx); for (int th_idx = 0; th_idx < th.numel(); th_idx++) { double angle = th(th_idx); int row = r + (int)floor(cos(angle) * d_val + 0.5); int col = c - (int)floor(sin(angle) * d_val + 0.5); if ( ( row >= 0 ) && ( row < I.rows() ) && ( col >= 0 ) && ( col < I.cols() ) ) { int j = (int)I(row, col); if (i >= 0 && i < L && j >= 0 && j < L) { Array coord (dim_vector (4, 1), 0); coord (0, 0) = i; coord (1, 0) = j; coord (2, 0) = d_idx; coord (3, 0) = th_idx; P(coord)++; } else { warning("Image contains invalid gray-level! (%d, %d)", i, j); } } } } } } return octave_value(P); } /* %!shared a %!test %! a = [0 0 0 1 2; %! 1 1 0 1 1; %! 2 2 1 0 0; %! 1 1 0 2 0; %! 0 0 1 0 1]; %! squeeze(graycomatrix(a, 3, 1, -pi/4)) == [4 2 0; %! 2 3 2; %! 1 2 0]; %! %!assert(size(graycomatrix(a, 3, 1:5, [0:3]*-pi/4)), [3, 3, 5, 4]) %!demo %! %! # Pattern Recognition Engineering (Nadler & Smith) %! # Digital Image Processing (Gonzales & Woods), p. 668 %! %! a = [0 0 0 1 2; %! 1 1 0 1 1; %! 2 2 1 0 0; %! 1 1 0 2 0; %! 0 0 1 0 1]; %! %! graycomatrix(a, 3, 1, [0 1]*-pi/4) %! */ image-2.16.1/src/PaxHeaders.61586/configure0000644000000000000000000000013115005111723015116 xustar0029 mtime=1746179027.27072611 30 atime=1746179027.254726027 30 ctime=1746179027.274726131 image-2.16.1/src/configure0000755000175000017500000031301615005111723016526 0ustar00avinoamavinoam00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for Octave-Forge image package 2.16.1. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Octave-Forge image package' PACKAGE_TARNAME='octave-forge-image-package' PACKAGE_VERSION='2.16.1' PACKAGE_STRING='Octave-Forge image package 2.16.1' PACKAGE_BUGREPORT='' PACKAGE_URL='' ac_subst_vars='LTLIBOBJS LIBOBJS OBJEXT EXEEXT ac_ct_CXX CPPFLAGS LDFLAGS CXXFLAGS CXX MKOCTFILE target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking ' ac_precious_vars='build_alias host_alias target_alias CXX CXXFLAGS LDFLAGS LIBS CPPFLAGS CCC' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures Octave-Forge image package 2.16.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/octave-forge-image-package] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of Octave-Forge image package 2.16.1:";; esac cat <<\_ACEOF Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF Octave-Forge image package configure 2.16.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by Octave-Forge image package $as_me 2.16.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Extract the first word of "mkoctfile", so it can be a program name with args. set dummy mkoctfile; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_MKOCTFILE+:} false; then : $as_echo_n "(cached) " >&6 else case $MKOCTFILE in [\\/]* | ?:[\\/]*) ac_cv_path_MKOCTFILE="$MKOCTFILE" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_MKOCTFILE="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi MKOCTFILE=$ac_cv_path_MKOCTFILE if test -n "$MKOCTFILE"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKOCTFILE" >&5 $as_echo "$MKOCTFILE" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$MKOCTFILE"; then as_fn_error $? "*** 'mkoctfile' not found." "$LINENO" 5 fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu image_save_CXX="$CXX" image_save_CXXFLAGS="$CXXFLAGS" image_save_CPPFLAGS="$CPPFLAGS" CXX=`${MKOCTFILE} -p CXX` CXXFLAGS=`${MKOCTFILE} -p CXXFLAGS` CPPFLAGS=`${MKOCTFILE} -p CPPFLAGS` ## Test for gcc bug #65843 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65843 ## which shows up as Octave bug #45096 https://savannah.gnu.org/bugs/?45096 ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 $as_echo_n "checking whether the C++ compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C++ compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 $as_echo_n "checking for C++ compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C++ compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether templated lambda functions accept '&const int'" >&5 $as_echo_n "checking whether templated lambda functions accept '&const int'... " >&6; } if ${_cv_template_lambda_accepts_ref_const_inst+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ template void test (T b) { const int a = b; [&] () { return a, a; }(); } int main () { test (1); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : _cv_template_lambda_accepts_ref_const_inst=yes else _cv_template_lambda_accepts_ref_const_inst=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_cv_template_lambda_accepts_ref_const_inst" >&5 $as_echo "$_cv_template_lambda_accepts_ref_const_inst" >&6; } if test $_cv_template_lambda_accepts_ref_const_inst = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Your C++ compiler (are you using GCC 5.0 or 5.1?) has a bug that prevents it from building the Octave Forge image package. But you can fix it very easily. See https://savannah.gnu.org/bugs/?45096 for details on working around it. " >&5 $as_echo "$as_me: WARNING: Your C++ compiler (are you using GCC 5.0 or 5.1?) has a bug that prevents it from building the Octave Forge image package. But you can fix it very easily. See https://savannah.gnu.org/bugs/?45096 for details on working around it. " >&2;} fi CXX="$image_save_CXX" CXXFLAGS="$image_save_CXXFLAGS" CPPFLAGS="$image_save_CPPFLAGS" ac_config_files="$ac_config_files Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by Octave-Forge image package $as_me 2.16.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ Octave-Forge image package config.status 2.16.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi image-2.16.1/src/PaxHeaders.61586/bwdist.cc0000644000000000000000000000006215005110255015017 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/bwdist.cc0000644000175000017500000006521415005110255016425 0ustar00avinoamavinoam00000000000000// Copyright (C) 2009 Stefan Gustavson // Copyright (C) 2013 Carnë Draug // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . #include #include /* edtfunc - Euclidean distance transform of a binary image This is a sweep-and-update Euclidean distance transform of a binary image. All positive pixels are considered object pixels, zero or negative pixels are treated as background. By Stefan Gustavson (stefan.gustavson@gmail.com). Originally written in 1994, based on paper-only descriptions of the SSED8 algorithm, invented by Per-Erik Danielsson and improved by Ingemar Ragnemalm. This is a classic algorithm with roots in the 1980s, still very good for the 2D case. Updated in 2004 to treat pixels at image edges correctly, and to improve code readability. Edited in 2009 to form the foundation for Octave BWDIST: added #define-configurable distance measure and function name Edited in 2013 for C++, removed the #define stuff, and other fixes for matlab compatibility. As discussed in 2022 in bug https://savannah.gnu.org/bugs/?62192 the results of the currently used algorithm can differ slightly from the theoretically correct values. A more precise algorithm can be found in the paper of Maurer, Rensheng Qi and V. Raghavan https://doi.org/10.1109/TPAMI.2003.1177156 from 2003. */ void edtfunc (float (*func)(short int, short int), const boolNDArray &img, std::vector& distx, std::vector& disty) { const int w = img.cols (); const int h = img.rows (); const int numel = img.numel (); // Initialize the distance images to be all large values const bool* elem = img.data (); for (octave_idx_type i = 0; i < numel; i++) if(! elem[i]) { // Large but still representable in a short, and 32000^2 + // 32000^2 does not overflow an int distx[i] = 32000; disty[i] = 32000; } if (h == 1 || w == 1) { // special treatment for 1D input for (octave_idx_type i = 0; i < numel; i++) disty[i] = 0; for (octave_idx_type i = 1; i < numel; i++) if (distx[i] != 0) distx[i] = distx[i-1] +1; for (octave_idx_type i = numel-2; i >= 0; i--) { if (distx[i] != 0) { int tmp = distx[i+1] -1; // signed distance, to allow index calculation if (std::abs (tmp) < distx[i]) distx[i] = tmp; } } return; } double olddist2, newdist2, newdistx, newdisty; bool changed; // Initialize index offsets for the current image width const int offset_u = -h; const int offset_ur = -h+1; const int offset_r = 1; const int offset_rd = h+1; const int offset_d = h; const int offset_dl = h-1; const int offset_l = -1; const int offset_lu = -h-1; // Perform the transformation int x, y, i; do { changed = false; // Scan rows, except first row for (y = 1; y < w; y++) { OCTAVE_QUIT; // move index to leftmost pixel of current row octave_idx_type i = y*h; /* scan right, propagate distances from above & left */ /* Leftmost pixel is special, has no left neighbors */ olddist2 = (*func)(distx[i], disty[i]); if(olddist2 > 0) // If not already zero distance { newdistx = distx[i+offset_u]; newdisty = disty[i+offset_u]+1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_ur]-1; newdisty = disty[i+offset_ur]+1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; changed = true; } } i++; /* Middle pixels have all neighbors */ for(x=1; x 0) // If not already zero distance { newdistx = distx[i+offset_l]+1; newdisty = disty[i+offset_l]; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_lu]+1; newdisty = disty[i+offset_lu]+1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_u]; newdisty = disty[i+offset_u]+1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } } /* Move index to second rightmost pixel of current row. */ /* Rightmost pixel is skipped, it has no right neighbor. */ i = y*h + h-2; /* scan left, propagate distance from right */ for(x=h-2; x>=0; x--, i--) { olddist2 = (*func)(distx[i], disty[i]); if(olddist2 == 0) continue; // Already zero distance newdistx = distx[i+offset_r]-1; newdisty = disty[i+offset_r]; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; changed = true; } } } /* Scan rows in reverse order, except last row */ for(y=w-2; y>=0; y--) { OCTAVE_QUIT; /* move index to rightmost pixel of current row */ i = y*h + h-1; /* Scan left, propagate distances from below & right */ /* Rightmost pixel is special, has no right neighbors */ olddist2 = (*func)(distx[i], disty[i]); if(olddist2 > 0) // If not already zero distance { newdistx = distx[i+offset_d]; newdisty = disty[i+offset_d]-1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_dl]+1; newdisty = disty[i+offset_dl]-1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; changed = true; } } i--; /* Middle pixels have all neighbors */ for(x=h-2; x>0; x--, i--) { olddist2 = (*func)(distx[i], disty[i]); if(olddist2 == 0) continue; // Already zero distance newdistx = distx[i+offset_r]-1; newdisty = disty[i+offset_r]; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_rd]-1; newdisty = disty[i+offset_rd]-1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_d]; newdisty = disty[i+offset_d]-1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_dl]+1; newdisty = disty[i+offset_dl]-1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; changed = true; } } /* Leftmost pixel is special, has no left neighbors */ olddist2 = (*func)(distx[i], disty[i]); if(olddist2 > 0) // If not already zero distance { newdistx = distx[i+offset_r]-1; newdisty = disty[i+offset_r]; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_rd]-1; newdisty = disty[i+offset_rd]-1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } newdistx = distx[i+offset_d]; newdisty = disty[i+offset_d]-1; newdist2 = (*func)(newdistx, newdisty); if(newdist2 < olddist2) { distx[i]=newdistx; disty[i]=newdisty; olddist2=newdist2; changed = true; } } /* Move index to second leftmost pixel of current row. */ /* Leftmost pixel is skipped, it has no left neighbor. */ i = y*h + 1; for(x=1; xstd::abs(y) ? (std::abs(x) + sqrt2_1 * std::abs(y)) : (sqrt2_1 * std::abs(x) + std::abs(y)) ; } static FloatMatrix calc_distances (float (*func)(short, short), const boolNDArray& bw, std::vector& xdist, std::vector& ydist) { FloatMatrix dist (bw.dims ()); edtfunc (func, bw, xdist, ydist); const int numel = dist.numel (); float* dist_vec = dist.fortran_vec (); for (int i = 0; i < numel; i++) dist_vec[i] = (*func)(xdist[i], ydist[i]); return dist; } template T calc_index (const boolNDArray& bw, const std::vector& xdist, const std::vector& ydist) { typedef typename T::element_type P; T idx (bw.dims ()); const int numel = bw.numel (); const int rows = bw.rows (); P* idx_vec = idx.fortran_vec (); for(int i = 0; i < numel; i++) idx_vec[i] = i+1 - xdist[i] - ydist[i]*rows; return idx; } DEFUN_DLD (bwdist, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Loadable Function} {@var{dist} =} bwdist (@var{bw})\n\ @deftypefnx {Loadable Function} {@var{dist} =} bwdist (@var{bw}, @var{method})\n\ @deftypefnx {Loadable Function} {[@var{dist}, @var{idx}] =} bwdist (@dots{})\n\ Compute distance transform in binary image.\n\ \n\ The image @var{bw} must be a binary matrix For @sc{matlab} compatibility, no\n\ check is performed, all non-zero values are considered object pixels.\n\ The return value @var{dist}, is the distance of each background pixel to the\n\ closest object pixel in a matrix of class @code{single}.\n\ \n\ @var{idx} is the linear index for the closest object, used to calculate the\n\ distance for each of the pixels. Its class is dependent on the number of\n\ elements in @var{bw}, @code{uint64} if less than 2^32 elements, @code{uint32}\n\ otherwise.\n\ \n\ The distance can be measured through different @var{method}s:\n\ \n\ @table @asis\n\ @item euclidean (default)\n\ \n\ @item chessboard\n\ \n\ @item cityblock\n\ \n\ @item quasi-euclidean\n\ \n\ @end table\n\ \n\ Currently, only 2D images are supported.\n\ \n\ Note: This implementation of bwdist follows the algorithm of Per-Erik Danielsson\n\ from 1980 (see source code for details). It gives fast and good results,\n\ but they can slightly differ from the theoretically correct result and from\n\ the Matlab result. Distance deviations are much smaller than a fraction\n\ of a single pixel.\n\ \n\ @end deftypefn") { octave_value_list retval; const int nargin = args.length (); if (nargin < 1 || nargin > 2) print_usage (); // for matlab compatibility, we do not actually check if the values are all // 0 and 1, any non-zero value is considered true const boolNDArray bw = args (0).bool_array_value (); std::string method = (nargin > 1) ? args (1).string_value () : "euclidean"; for (octave_idx_type q = 0; q < octave_idx_type (method.length ()); q++) method[q] = tolower (method[q]); // Special case of there being no foreground element (bug #50874). // Because of the way the function was originally structured, // handling this case as part of the rest gets a bit hairy. if (! (static_cast(bw.as_column())).any()(0)) { FloatMatrix dist (bw.dims (), std::numeric_limits::infinity ()); retval(0) = dist; // Compute optional 'index to closest object pixel', only if // requested. if (nargout > 1) { if (bw.numel () >= pow (2, 32)) retval(1) = uint64NDArray (bw.dims (), 0); else retval(1) = uint32NDArray (bw.dims (), 0); } } else { // Allocate two arrays for temporary output values const int numel = bw.numel (); std::vector xdist (numel, 0); std::vector ydist (numel, 0); FloatMatrix dist; if (method == "euclidean") { dist = calc_distances (euclidean, bw, xdist, ydist); const Array positions = (!bw).find (); const int zpos = positions.numel(); const octave_idx_type* pos_vec = positions.data (); float* dist_vec = dist.fortran_vec (); for (int i = 0; i < zpos; i++) dist_vec[pos_vec[i]] = sqrt (dist_vec[pos_vec[i]]); } else if (method == "chessboard") dist = calc_distances (chessboard, bw, xdist, ydist); else if (method == "cityblock") dist = calc_distances (cityblock, bw, xdist, ydist); else if (method == "quasi-euclidean") dist = calc_distances (quasi_euclidean, bw, xdist, ydist); else error ("bwdist: unknown METHOD '%s'", method.c_str ()); retval(0) = dist; // Compute optional 'index to closest object pixel', only if // requested if (nargout > 1) { if (numel >= pow (2, 32)) retval(1) = calc_index (bw, xdist, ydist); else retval(1) = calc_index (bw, xdist, ydist); } } return retval; } /* %!shared bw %! %! bw = [0 1 0 1 0 1 1 0 %! 0 0 0 1 1 0 0 0 %! 0 0 0 1 1 0 0 0 %! 0 0 0 1 1 0 0 0 %! 0 0 1 1 1 1 1 1 %! 1 1 1 1 0 0 0 1 %! 1 1 1 0 0 0 1 0 %! 0 0 1 0 0 0 1 1]; %!test %! out = [ 1.00000 0.00000 1.00000 0.00000 1.00000 0.00000 0.00000 1.00000 %! 1.41421 1.00000 1.00000 0.00000 0.00000 1.00000 1.00000 1.41421 %! 2.23607 2.00000 1.00000 0.00000 0.00000 1.00000 2.00000 2.00000 %! 2.00000 1.41421 1.00000 0.00000 0.00000 1.00000 1.00000 1.00000 %! 1.00000 1.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 %! 0.00000 0.00000 0.00000 0.00000 1.00000 1.00000 1.00000 0.00000 %! 0.00000 0.00000 0.00000 1.00000 1.41421 1.00000 0.00000 1.00000 %! 1.00000 1.00000 0.00000 1.00000 2.00000 1.00000 0.00000 0.00000]; %! out = single (out); %! %! assert (bwdist (bw), out, 0.0001); # default is euclidean %! assert (bwdist (bw, "euclidean"), out, 0.0001); %! assert (bwdist (logical (bw), "euclidean"), out, 0.0001); %!test %! out = [ 1 0 1 0 1 0 0 1 %! 1 1 1 0 0 1 1 1 %! 2 2 1 0 0 1 2 2 %! 2 1 1 0 0 1 1 1 %! 1 1 0 0 0 0 0 0 %! 0 0 0 0 1 1 1 0 %! 0 0 0 1 1 1 0 1 %! 1 1 0 1 2 1 0 0]; %! out = single (out); %! %! assert (bwdist (bw, "chessboard"), out); %!test %! out = [ 1 0 1 0 1 0 0 1 %! 2 1 1 0 0 1 1 2 %! 3 2 1 0 0 1 2 2 %! 2 2 1 0 0 1 1 1 %! 1 1 0 0 0 0 0 0 %! 0 0 0 0 1 1 1 0 %! 0 0 0 1 2 1 0 1 %! 1 1 0 1 2 1 0 0]; %! out = single (out); %! %! assert (bwdist (bw, "cityblock"), out); %!test %! out = [ 1.00000 0.00000 1.00000 0.00000 1.00000 0.00000 0.00000 1.00000 %! 1.41421 1.00000 1.00000 0.00000 0.00000 1.00000 1.00000 1.41421 %! 2.41421 2.00000 1.00000 0.00000 0.00000 1.00000 2.00000 2.00000 %! 2.00000 1.41421 1.00000 0.00000 0.00000 1.00000 1.00000 1.00000 %! 1.00000 1.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 %! 0.00000 0.00000 0.00000 0.00000 1.00000 1.00000 1.00000 0.00000 %! 0.00000 0.00000 0.00000 1.00000 1.41421 1.00000 0.00000 1.00000 %! 1.00000 1.00000 0.00000 1.00000 2.00000 1.00000 0.00000 0.00000]; %! out = single (out); %! %! assert (bwdist (bw, "quasi-euclidean"), out, 0.0001); %! %! bw(logical (bw)) = 3; # there is no actual check if matrix is binary or 0 and 1 %! assert (bwdist (bw, "quasi-euclidean"), out, 0.0001); %! %! bw(logical (bw)) = -2; # anything non-zero is considered object %! assert (bwdist (bw, "quasi-euclidean"), out, 0.0001); %!test %! bw = [ 1 1 1 1 0 1 1 1 1 %! 1 1 1 1 0 1 1 1 1 %! 1 1 0 1 1 1 1 1 1 %! 0 1 1 1 1 1 1 1 1]; %! %! dist = [ 0 0 0 0 1 0 0 0 0 %! 0 0 0 0 1 0 0 0 0 %! 0 0 1 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0]; %! dist = single (dist); %! %! c = [ 1 5 9 13 13 21 25 29 33 %! 2 6 10 14 14 22 26 30 34 %! 3 7 10 15 19 23 27 31 35 %! 8 8 12 16 20 24 28 32 36]; %! c = uint32 (c); %! %! [dout, cout] = bwdist (bw, "euclidean"); %! assert (dout, dist) %! assert (cout, c) ## The quasi-euclidean method is apparently sensitive to a machine precision ## error that happens in x86 systems only. This test will cause an endless ## loop in case of a regression. %!test %! bw = [ 0 1 1 0 0 0 1 0 %! 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 1 0 %! 0 0 0 0 1 0 0 1 %! 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 %! 0 0 1 0 0 1 1 0]; %! out = single ([ %! 1.00000 0.00000 0.00000 1.00000 2.00000 1.00000 0.00000 1.00000 %! 1.00000 1.00000 1.00000 sqrt(2) sqrt(2)+1 sqrt(2) 1.00000 sqrt(2) %! 0.00000 0.00000 1.00000 2.00000 2.00000 sqrt(2) 1.00000 sqrt(2) %! 1.00000 1.00000 sqrt(2) sqrt(2) 1.00000 1.00000 0.00000 1.00000 %! 2.00000 2.00000 2.00000 1.00000 0.00000 1.00000 1.00000 0.00000 %! 1.00000 sqrt(2) 2.00000 sqrt(2) 1.00000 sqrt(2) sqrt(2) 1.00000 %! 0.00000 1.00000 1.00000 sqrt(2) sqrt(2) 1.00000 1.00000 sqrt(2) %! 1.00000 1.00000 0.00000 1.00000 1.00000 0.00000 0.00000 1.00000 %! ]); %! assert (bwdist (bw, "quasi-euclidean"), out); %!error bwdist (bw, "not a valid method"); %!test %! ## Special case of there being no foreground element (bug #50874) %! expected_dist = single (Inf (2, 2)); %! expected_idx = uint32 ([0 0; 0 0]); %! %! [dist, idx] = bwdist (false (2, 2)); %! assert (dist, expected_dist) %! assert (idx, expected_idx) %! %! [dist, idx] = bwdist (zeros (2, 2)); %! assert (dist, expected_dist) %! assert (idx, expected_idx) %!test %! ## Special case of 1D input (bug #50874) %! assert (bwdist ([1 0]), single ([0 1])) %! assert (bwdist ([1 0]'), single ([0 1]')) %! assert (bwdist ([0 1 0 0 0 0 1 1]), single ([1 0 1 2 2 1 0 0])) %! assert (bwdist ([1 1 0 0 0 0 1 1]'), single ([0 0 1 2 2 1 0 0])') %! assert (bwdist ([1 0], "euclidean"), single ([0 1])) %! assert (bwdist ([1 0], "chessboard"), single ([0 1])) %! assert (bwdist ([1 0], "cityblock"), single ([0 1])) %! assert (bwdist ([1 0], "quasi-euclidean"), single ([0 1])) %!test %! ## test 1D input with 2nd output argument (indices) (bug #50874) %! expected_dist = single ([1 0 1]); %! expected_idx = uint32 ([2 2 2]); %! %! [dist, idx] = bwdist ([0 1 0]); %! assert (dist, expected_dist) %! assert (idx, expected_idx) %! %! [dist, idx] = bwdist ([0 1 0]'); %! assert (dist, expected_dist') %! assert (idx, expected_idx') %! %! expected_dist = single ([0 0 1 0 0]); %! expected_idx = uint32 ([1 2 2 4 5]); %! [dist, idx] = bwdist ([1 1 0 1 1]); %! assert (dist, expected_dist) %! assert (idx, expected_idx) %! %! expected_dist = single ([1 0 1 2 1 0 0 0 1 1 0 0 0 0 1 2 3 4]); %! expected_idx = uint32 ([2 2 2 2 6 6 7 8 8 11 11 12 13 14 14 14 14 14]); %! [dist, idx] = bwdist ([0 1 0 0 0 1 1 1 0 0 1 1 1 1 0 0 0 0]); %! assert (dist, expected_dist) %! assert (idx, expected_idx) %! %! expected_dist = single ([0 0 1 2 1 0 0 0 1 1 0 0 0 0 1 2 1 0]); %! expected_idx = uint32 ([1 2 2 2 6 6 7 8 8 11 11 12 13 14 14 14 18 18]); %! [dist, idx] = bwdist ([1 1 0 0 0 1 1 1 0 0 1 1 1 1 0 0 0 1]); %! assert (dist, expected_dist) %! assert (idx, expected_idx) %!test %! assert (bwdist ([0 0]), single ([Inf, Inf])) %! assert (bwdist ([0 0]'), single ([Inf, Inf]')) %!xtest %! ## This is Matlab incompatible because the bottom right corners is %! ## equally distant to the top right and bottom left corners. However, %! ## both are correct answers, and the returned value is just %! ## implementation dependent. %! bw = logical ([ %! 0 0 1 %! 0 0 0 %! 1 0 0 %! ]); %! expected_dist = single ([ %! 2.0 1.0 0.0 %! 1.0 sqrt(2) 1.0 %! 0.0 1.0 2.0 %! ]); %! expected_idx = uint32 ([ %! 3 7 7 %! 3 3 7 %! 3 3 3 %! ]); %! [dist, idx] = bwdist (bw); %! assert (dist, expected_dist) %! assert (idx, expected_idx) %!xtest %! # bug #62192 %! a = zeros (200, 200); %! a (158, 100) = 1; %! a (141, 141) = 1; %! a (156, 115) = 1; %! d = bwdist (a); %! expected_result = single (57.9741); %! assert (d (100, 100), expected_result, 1e-4) */ image-2.16.1/src/PaxHeaders.61586/intlut.cc0000644000000000000000000000006215005110255015042 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/intlut.cc0000644000175000017500000001071115005110255016440 0ustar00avinoamavinoam00000000000000// Copyright (C) 2013-2015 Carnë Draug // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, see // . // This function is implemented in C++ because it basically does // indexing with base 0. If implemented in a m file, it would // require conversion of the image to a float just to add 1. #include #include #include #include #include #include #include template static inline P intlut_index (const typename P::val_type A, const P lut_vec[]) { return lut_vec[A]; } template<> inline octave_int16 intlut_index (const typename octave_int16::val_type A, const octave_int16 lut_vec[]) { return lut_vec[32768 + A]; } template static T intlut (const T& A, const T& lut) { const auto* A_vec = A.data (); const auto* lut_vec = lut.data (); T B (A.dims ()); auto* B_vec = B.fortran_vec (); const octave_idx_type n = A.numel (); typedef typename T::element_type::val_type P_val_type; for (octave_idx_type i = 0; i < n; i++, B_vec++, A_vec++) *B_vec = intlut_index (static_cast (*A_vec), lut_vec); return B; } DEFUN_DLD (intlut, args, , "\ -*- texinfo -*-\n\ @deftypefn {Function File} {} intlut (@var{A}, @var{LUT})\n\ Convert integer values with lookup table (LUT).\n\ \n\ Replace the values from the array @var{A} with the corresponding\n\ value from the lookup table @var{LUT}. This is equivalent as indexing\n\ @var{LUT} with @var{A}, with a base equal to @var{A} minimum possible\n\ value, i.e., @code{intmin (@var{A})}.\n\ \n\ For the simplest case of uint8 and uint16 class, it corresponds to:\n\ \n\ @example\n\ @var{LUT}(double (@var{A}) +1)\n\ @end example\n\ \n\ but without the temporary conversion of @var{A} to floating point\n\ thus reducing memory usage.\n\ \n\ @var{A} and @var{LUT} must be of the same class, and uint8, uint16,\n\ or int16. @var{LUT} must have exactly 256 elements for class uint8,\n\ and 65536 for classes uint16 and int16. Output is of same class\n\ as @var{LUT}.\n\ \n\ @seealso{ind2gray, ind2rgb, rgb2ind}\n\ @end deftypefn") { octave_value_list rv (1); if (args.length () != 2) print_usage (); const std::string cls = args(0).class_name (); if (cls != args(1).class_name ()) error ("intlut: A and LUT must be of same class"); const dim_vector lut_dims = args(1).dims (); if (lut_dims.length () != 2 || (lut_dims(0) > 1 && lut_dims(1) > 1)) error ("intlut: LUT must be a vector"); #define IF_TYPE(TYPE, TYPE_RANGE) \ if (args(0).is_ ## TYPE ## _type ()) \ { \ if (args(1).numel () != TYPE_RANGE) \ error ("intlut: LUT must have " #TYPE_RANGE " elements for class %s", \ cls.c_str ()); \ rv(0) = intlut (args(0).TYPE ## _array_value (), \ args(1).TYPE ## _array_value ()); \ } IF_TYPE(uint8, 256) else IF_TYPE(uint16, 65536) else IF_TYPE(int16, 65536) else error ("intlut: A must be of class uint8, uint16, or int16"); #undef IF_TYPE return rv; } /* %!assert (intlut (uint8 (1:4), uint8 ( 255:-1:0)), uint8 (254:-1:251)); %!assert (intlut (uint16 (1:4), uint16 (65535:-1:0)), uint16 (65534:-1:65531)); %!assert (intlut (int16 (1:4), int16 (32767:-1:-32768)), int16 (-2:-1:-5)); %!assert (intlut (uint8 (255), uint8 (0:255)), uint8 (255)); %!assert (intlut (uint16 (65535), uint16 (0:65535)), uint16 (65535)); %!assert (intlut (int16 (32767), int16 (-32768:32767)), int16 (32767)); %!error intlut () %!error intlut ("text") %!error intlut (1:20, uint8 (0:255)); %!error intlut (uint16 (1:20), uint8 (0:255)); %!error intlut (uint8 (1:20), uint8 (0:200)); %!error intlut (uint16 (1:20), uint16 (0:500)); %!error intlut (uint8 (56), uint8 (magic (16) -1)) */ image-2.16.1/src/PaxHeaders.61586/rotate_scale.cc0000644000000000000000000000006215005110255016170 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/rotate_scale.cc0000644000175000017500000001344415005110255017574 0ustar00avinoamavinoam00000000000000// Copyright (C) 2003 Andy Adler // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . /* * ROTATE_SCALE: rotate and scale a matrix using bilinear interpolation * imo= block(im, xregs, yregs); */ #include void calc_rotation_params( double x0l,double y0l,double x0r,double y0r, double x1l,double y1l,double x1r,double y1r, double* Tx_x, double* Ty_x, double* Tx_y, double* Ty_y, double* Tx_1, double* Ty_1 ); void do_interpolation ( double Tx_x, double Ty_x, double Tx_y, double Ty_y, double Tx_1, double Ty_1, int x0max, int y0max,// initial size int x1max, int y1max,// output size const double * img0, double * img1 ); DEFUN_DLD (rotate_scale, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {@var{im1} =} rotate_scale(@var{im0}, @var{lm0}, @var{lm1}, @var{out_size})\n\ Arbitrary rotation and scaling of a gray-scale image using fast bilinear interpolation.\n\ \n\ The image @var{im0} will be rotated and scaled such that the landmark points in\n\ @var{lm0} in the image will be placed in the landmark points in @var{lm1} in\n\ the output image @var{im1}. The landmark points are given as a 2 by 2 matrix\n\ where the first row contains the x-coordinates of the landmarks, and the second\n\ row contains the y-coordinates.\n\ \n\ The size of the output image is given in the vector @var{out_size}.\n\ \n\ The following example shows basic usage of the function\n\ @example\n\ im0 = zeros(100); im0(25:75, 25:75)=1;\n\ im1 = rotate_scale( im0, [40,60; 50,50], [60,90; 60,90], [120,120]);\n\ @end example\n\ @seealso{imrotate, imresize}\n\ @end deftypefn\n\ ") { octave_value_list retval; if (args.length() < 4 || !args(0).is_matrix_type() || !args(1).is_matrix_type() || !args(2).is_matrix_type() || !args(3).is_matrix_type()) print_usage (); Matrix im0( args(0).matrix_value() ); const double * im0p = im0.data(); Matrix lm0( args(1).matrix_value() ); Matrix lm1( args(2).matrix_value() ); ColumnVector out_size( args(3).vector_value() ); int inp_hig= im0.rows(); int inp_wid= im0.cols(); int out_hig= (int) out_size(0); int out_wid= (int) out_size(1); Matrix im1( out_hig, out_wid); double * im1p = im1.fortran_vec(); double Tx_x; double Ty_x; double Tx_y; double Ty_y; double Tx_1; double Ty_1; calc_rotation_params( lm0(0,0), lm0(1,0), lm0(0,1), lm0(1,1), lm1(0,0), lm1(1,0), lm1(0,1), lm1(1,1), & Tx_x, & Ty_x, & Tx_y, & Ty_y, & Tx_1, & Ty_1 ); do_interpolation( Tx_x, Ty_x, Tx_y, Ty_y, Tx_1, Ty_1, inp_wid, inp_hig, out_wid, out_hig, im0p, im1p ); retval(0) = im1; return retval; } inline double sqr(double a) { return (a)*(a); } void calc_rotation_params( double x1l,double y1l,double x1r,double y1r, double x0l,double y0l,double x0r,double y0r, double* Tx_x, double* Ty_x, double* Tx_y, double* Ty_y, double* Tx_1, double* Ty_1 ) { double d0= sqrt( sqr(x0l-x0r) + sqr(y0l-y0r) ); double d1= sqrt( sqr(x1l-x1r) + sqr(y1l-y1r) ); double dr= d1/d0; double a0= atan2( y0l-y0r , x0l-x0r ); double a1= atan2( y1l-y1r , x1l-x1r ); double ad= a1-a0; double dr_cos_ad= dr*cos(ad); double dr_sin_ad= dr*sin(ad); double x0m= (x0l+x0r)/2; double y0m= (y0l+y0r)/2; double x1m= (x1l+x1r)/2; double y1m= (y1l+y1r)/2; *Tx_x= dr_cos_ad; *Ty_x= dr_sin_ad; *Tx_y= -dr_sin_ad; *Ty_y= dr_cos_ad; *Tx_1= x1m - dr_cos_ad*x0m + dr_sin_ad*y0m; *Ty_1= y1m - dr_sin_ad*x0m - dr_cos_ad*y0m; } void do_interpolation ( double Tx_x, double Ty_x, double Tx_y, double Ty_y, double Tx_1, double Ty_1, int x0max, int y0max,// initial size int x1max, int y1max,// output size const double * img0, double * img1 ) { for (int i=0; i< x1max; i++) { for (int j=0; j< y1max; j++) { double x0i= Tx_x * i + Tx_y * j + Tx_1; double y0i= Ty_x * i + Ty_y * j + Ty_1; if ( x0i < 0 ) x0i= 0; else if (x0i >= x0max-1 ) x0i= x0max - 1.00001; if ( y0i < 0 ) y0i= 0; else if (y0i >= y0max-1 ) y0i= y0max - 1.00001; int x0idx= (int) x0i; int y0idx= (int) y0i; double frac_r= x0i- x0idx; double frac_l= 1 - frac_r; double frac_d= y0i- y0idx; double frac_u= 1 - frac_d; int pix_lu= (y0idx+0) + (x0idx+0) * y0max ; int pix_ru= (y0idx+0) + (x0idx+1) * y0max ; int pix_ld= (y0idx+1) + (x0idx+0) * y0max ; int pix_rd= (y0idx+1) + (x0idx+1) * y0max ; img1[ i*y1max + j ]= frac_l*frac_u* img0[ pix_lu ] + frac_r*frac_u* img0[ pix_ru ] + frac_l*frac_d* img0[ pix_ld ] + frac_r*frac_d* img0[ pix_rd ]; } } } image-2.16.1/src/PaxHeaders.61586/conndef.cc0000644000000000000000000000006215005110255015137 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/conndef.cc0000644000175000017500000002223515005110255016541 0ustar00avinoamavinoam00000000000000// Copyright (C) 2014 Carnë Draug // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, see // . #include #include "connectivity.h" using namespace octave_image_pkg; // The conndef() function is really really simple and could have easily // been a m file (actually it once was, check the hg log if it ever needs // to be recovered) but then it would be awkward to call it from oct // functions so we made a C++ class for it. DEFUN_DLD(conndef, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {} conndef (@var{conn})\n\ @deftypefnx {Loadable Function} {} conndef (@var{mask})\n\ @deftypefnx {Loadable Function} {} conndef (@var{ndims}, @var{type})\n\ Create connectivity array.\n\ \n\ Creates a matrix of for morphological operations, where elements with\n\ a value of 1 are considered connected to the center element (a\n\ connectivity array).\n\ \n\ It can be specified by the number of dimensions, @var{ndims}, and\n\ @var{type} which must be one of the following strings:\n\ \n\ @table @asis\n\ @item @qcode{\"minimal\"}\n\ Neighbours touch the central element on a (@var{ndims}-1)-dimensional\n\ surface.\n\ \n\ @item @qcode{\"maximal\"}\n\ Neighbours touch the central element in any way. Equivalent to\n\ @code{ones (repmat (3, 1, @var{ndims}))}.\n\ \n\ @end table\n\ \n\ the number of connected elements to the center element, @var{conn},\n\ in which case the following are valid:\n\ \n\ @table @asis\n\ @item 4\n\ Two-dimensional 4-connected neighborhood.\n\ \n\ @item 8\n\ Two-dimensional 8-connected neighborhood.\n\ \n\ @item 6\n\ Three-dimensional 6-connected neighborhood.\n\ \n\ @item 18\n\ Three-dimensional 18-connected neighborhood.\n\ \n\ @item 26\n\ Three-dimensional 26-connected neighborhood.\n\ \n\ @end table\n\ \n\ or a connectivity array itself, in which case it checks for its validity\n\ and returns itself. In such case, it is equivalent to @code{iptcheckconn}.\n\ \n\ @seealso{iptcheckconn, strel}\n\ @end deftypefn") { const octave_idx_type nargin = args.length (); if (nargin < 1 || nargin > 2) print_usage (); connectivity conn; if (nargin == 1) conn = conndef (args(0)); else { const octave_idx_type ndims = args(0).uint_value (true); if (ndims < 1) error ("conndef: NDIMS must be a positive integer"); const std::string type = args(1).string_value (); try { conn = connectivity (ndims, type); } catch (invalid_connectivity& e) { error ("conndef: TYPE %s", e.what ()); } } // we must return an array of class double return octave_value (NDArray (conn.mask)); } /* %!assert (conndef (1, "minimal"), [1; 1; 1]); %!assert (conndef (2, "minimal"), [0 1 0; 1 1 1; 0 1 0]); %!test %! C = zeros (3, 3, 3); %! C(:,2,2) = 1; %! C(2,:,2) = 1; %! C(2,2,:) = 1; %! assert (conndef (3, "minimal"), C); %!test %! C = zeros (3, 3, 3, 3); %! C(:,:,2,1) = [0 0 0 %! 0 1 0 %! 0 0 0]; %! C(:,:,1,2) = [0 0 0 %! 0 1 0 %! 0 0 0]; %! C(:,:,2,2) = [0 1 0 %! 1 1 1 %! 0 1 0]; %! C(:,:,3,2) = [0 0 0 %! 0 1 0 %! 0 0 0]; %! C(:,:,2,3) = [0 0 0 %! 0 1 0 %! 0 0 0]; %! assert (conndef (4, "minimal"), C); %!assert (conndef (1, "maximal"), ones (3, 1)); %!assert (conndef (2, "maximal"), ones (3, 3)); %!assert (conndef (3, "maximal"), ones (3, 3, 3)); %!assert (conndef (4, "maximal"), ones (3, 3, 3, 3)); %!assert (nnz (conndef (3, "minimal")), 7) %!assert (nnz (conndef (4, "minimal")), 9) %!assert (nnz (conndef (5, "minimal")), 11) %!assert (nnz (conndef (6, "minimal")), 13) %!assert (find (conndef (3, "minimal")), [5 11 13 14 15 17 23](:)) %!assert (find (conndef (4, "minimal")), [14 32 38 40 41 42 44 50 68](:)) %!assert (find (conndef (5, "minimal")), %! [ 41 95 113 119 121 122 123 125 131 149 203](:)) %!assert (find (conndef (6, "minimal")), %! [ 122 284 338 356 362 364 365 366 368 374 392 446 608](:)) %!error conndef () %!error conndef (-2, "minimal") %!error conndef (char (2), "minimal") %!error conndef ("minimal", 3) %!error conndef (3, "invalid") %!error conndef (10) %!assert (conndef (2, "minimal"), conndef (4)) %!assert (conndef (2, "maximal"), conndef (8)) %!assert (conndef (3, "minimal"), conndef (6)) %!assert (conndef (3, "maximal"), conndef (26)) %!assert (conndef (18), reshape ([0 1 0 1 1 1 0 1 0 %! 1 1 1 1 1 1 1 1 1 %! 0 1 0 1 1 1 0 1 0], [3 3 3])) */ // PKG_ADD: autoload ("iptcheckconn", which ("conndef")); // PKG_DEL: autoload ("iptcheckconn", which ("conndef"), "remove"); DEFUN_DLD(iptcheckconn, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {} iptcheckconn (@var{conn}, @var{func}, @var{var})\n\ @deftypefnx {Loadable Function} {} iptcheckconn (@var{conn}, @var{func}, @var{var}, @var{pos})\n\ Check if argument is valid connectivity.\n\ \n\ If @var{conn} is not a valid connectivity argument, gives a properly\n\ formatted error message. @var{func} is the name of the function to be\n\ used on the error message, @var{var} the name of the argument being\n\ checked (for the error message), and @var{pos} the position of the\n\ argument in the input.\n\ \n\ A valid connectivity argument must be either double or logical. It must\n\ also be either a scalar from set [4 6 8 18 26], or a symmetric matrix\n\ with all dimensions of size 3, with only 0 or 1 as values, and 1 at its\n\ center.\n\ \n\ @seealso{conndef}\n\ @end deftypefn") { const octave_idx_type nargin = args.length (); if (nargin < 3 || nargin > 4) print_usage (); const std::string func = args(1).string_value (); const std::string var = args(2).string_value (); int pos = 0; if (nargin > 3) { pos = args(3).int_value (); if (pos < 1) error ("iptcheckconn: POS must be a positive integer"); } std::string err_msg; try { const connectivity conn = conndef (args(0)); } catch (invalid_connectivity& e) { err_msg = e.what (); } catch (octave::execution_exception& e) { err_msg = e.message (); } if (! err_msg.empty ()) { // We get the error message from conndef and then parse it to // get the issue with the connectivity so we can throw it again // formatted appropriately for iptcheckconn. This parsing of // the error message is not nice but:1) we don't want to // duplicate the logic of conndef in iptcheckconn; 2) we prefer // to use conndef and only have this function for Matlab // compatibility; 3) this code is only used when conn is invalid // so won't be happening many times (meaning performance here is // not important); 4) we have plenty of tests to ensure that the // commit message "surgery" will continue to work as expected. const std::string token = "CONN "; std::string::size_type n = err_msg.find(token); if (n == std::string::npos) error ("iptcheckconn: CONN is invalid but failed to parse error"); err_msg = err_msg.substr (n + token.size ()); if (pos == 0) error ("%s: %s %s", func.c_str (), var.c_str (), err_msg.c_str ()); else error ("%s: %s, at pos %i, %s", func.c_str (), var.c_str (), pos, err_msg.c_str ()); } return octave_value (); } /* // the complete error message should be "expected error <.> but got none", // but how to escape <> within the error message? %!test iptcheckconn ( 4, "func", "var") %!test iptcheckconn ( 6, "func", "var") %!test iptcheckconn ( 8, "func", "var") %!test iptcheckconn (18, "func", "var") %!test iptcheckconn (26, "func", "var") %!test iptcheckconn (1, "func", "var") %!test iptcheckconn (ones (3, 1), "func", "var") %!test iptcheckconn (ones (3, 3), "func", "var") %!test iptcheckconn (ones (3, 3, 3), "func", "var") %!test iptcheckconn (ones (3, 3, 3, 3), "func", "var") %!error %! iptcheckconn (3, "func", "VAR"); %!error %! iptcheckconn ([1 1 1; 1 0 1; 1 1 1], "func", "VAR"); %!error %! iptcheckconn ([1 2 1; 1 1 1; 1 1 1], "func", "VAR"); %!error %! iptcheckconn ([0 1 1; 1 1 1; 1 1 1], "func", "VAR"); %!error %! iptcheckconn (ones (3, 3, 3, 4), "func", "VAR"); */ image-2.16.1/src/PaxHeaders.61586/__custom_gaussian_smoothing__.cc0000644000000000000000000000006215005110255021612 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/__custom_gaussian_smoothing__.cc0000644000175000017500000002000115005110255023201 0ustar00avinoamavinoam00000000000000// Copyright (C) 2008 Søren Hauberg // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . #include template MT custom_gaussian_smoothing (const MT &I, const Matrix &lambda1, const Matrix &lambda2, const Matrix &theta) { const octave_idx_type rows = I.rows (); const octave_idx_type cols = I.columns (); // Allocate output MT J (I.dims ()); // Iterate over every element of 'I' for (octave_idx_type row = 0; row < rows; row++) { for (octave_idx_type col = 0; col < cols; col++) { // Extract parameters const double v1 = lambda1 (row, col); const double v2 = lambda2 (row, col); const double t = theta (row, col); // Should we perform any filtering? if (std::min (v1, v2) > 0) { // Compute inverse covariance matrix, C^-1 = [a, b; b, c] const double iv1 = 1.0/v1; const double iv2 = 1.0/v2; const double ct = cos (t); const double st = sin (t); const double ct2 = ct*ct; const double st2 = st*st; const double ctst = ct*st; const double a = ct2*iv2 + st2*iv1; const double b = (iv2-iv1)*ctst; const double c = st2*iv2 + ct2*iv1; // Compute bounding box of the filter const double k = 3.0; // The maximally allowed Mahalanobis' distance const double sqrtv1 = sqrt (v1); const double sqrtv2 = sqrt (v2); const octave_idx_type rur = (octave_idx_type)std::fabs (k*(ct*sqrtv2 - st*sqrtv1)); // 'rur' means 'row-upper-right' const octave_idx_type cur = (octave_idx_type)std::fabs (k*(st*sqrtv2 + ct*sqrtv1)); const octave_idx_type rlr = (octave_idx_type)std::fabs (k*(ct*sqrtv2 + st*sqrtv1)); const octave_idx_type clr = (octave_idx_type)std::fabs (k*(st*sqrtv2 - ct*sqrtv1)); const octave_idx_type rul = (octave_idx_type)std::fabs (k*(-ct*sqrtv2 - st*sqrtv1)); const octave_idx_type cul = (octave_idx_type)std::fabs (k*(-st*sqrtv2 + ct*sqrtv1)); const octave_idx_type rll = (octave_idx_type)std::fabs (k*(-ct*sqrtv2 + st*sqrtv1)); const octave_idx_type cll = (octave_idx_type)std::fabs (k*(-st*sqrtv2 - ct*sqrtv1)); const octave_idx_type r_delta = std::max (std::max (rur, rlr), std::max (rul, rll)); const octave_idx_type c_delta = std::max (std::max (cur, clr), std::max (cul, cll));; // The bounding box is now (row-r_delta):(row+r_delta)x(col-c_delta):(col+c_delta). // We, however, represent the bounding box in a local coordinate system around (row, col). const octave_idx_type r1 = std::max (row-r_delta, octave_idx_type (0)) - row; const octave_idx_type r2 = std::min (row+r_delta, rows-1) - row; const octave_idx_type c1 = std::max (col-c_delta, octave_idx_type (0)) - col; const octave_idx_type c2 = std::min (col+c_delta, cols-1) - col; // Perform the actual filtering double sum = 0; double wsum = 0; // for normalisation for (octave_idx_type rl = r1; rl <= r2; rl++) { for (octave_idx_type cl = c1; cl <= c2; cl++) { // Compute Mahalanobis' distance const double dsquare = rl*(a*rl + b*cl) + cl*(b*rl + c*cl); // We only do the filtering in an elliptical window if (dsquare > k*k) continue; // Update filter values const double w = exp (-0.5*dsquare); wsum += w; sum += w*(double)I.elem (row + rl, col + cl); } // End: cl } // End: rl // Compute final result J (row, col) = sum/wsum; } else // No filtering is performed { J.elem (row, col) = I.elem (row, col); } } // End: column iteration } // End: row iteration // Return return J; } DEFUN_DLD (__custom_gaussian_smoothing__, args, ,"\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {@var{J} =} __custom_gaussian_smooting__ (@var{I}, @var{lambda1}, @var{lambda2}, @var{theta})\n\ Performs Gaussian smoothing on the image @var{I}. In pixel @math{(r,c)} the \n\ Eigenvalues of the Gaussian is @var{lambda1}@math{(r,c)} and @var{lambda2}@math{(r,c)}.\n\ The Gaussian is rotated with the angle given in @var{theta}@math{(r,c)}.\n\ \n\ @strong{Warning:} this function should @i{never} be called directly! The user\n\ interface to this function is available in @code{imsmooth}.\n\ @seealso{imsmooth}\n\ @end deftypefn\n\ ") { // Handle Input octave_value_list retval; const int nargin = args.length (); if (nargin != 4) print_usage (); const Matrix lambda1 = args (1).matrix_value (); const Matrix lambda2 = args (2).matrix_value (); const Matrix theta = args (3).matrix_value (); const octave_idx_type rows = args (0).rows(); const octave_idx_type cols = args (0).columns(); if (lambda1.rows () != rows || lambda1.columns () != cols || lambda2.rows () != rows || lambda2.columns () != cols || theta.rows () != rows || theta.columns () != cols) error ("__custom_gaussian_smoothing__: size mismatch"); // Take action depending on input type //octave_value J; if (args(0).is_real_matrix()) { const Matrix I = args(0).matrix_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else if (args(0).is_int8_type()) { const int8NDArray I = args(0).int8_array_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else if (args(0).is_int16_type()) { const int16NDArray I = args(0).int16_array_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else if (args(0).is_int32_type()) { const int32NDArray I = args(0).int32_array_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else if (args(0).is_int64_type()) { const int64NDArray I = args(0).int64_array_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else if (args(0).is_uint8_type()) { const uint8NDArray I = args(0).uint8_array_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else if (args(0).is_uint16_type()) { const uint16NDArray I = args(0).uint16_array_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else if (args(0).is_uint32_type()) { const uint32NDArray I = args(0).uint32_array_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else if (args(0).is_uint64_type()) { const uint64NDArray I = args(0).uint64_array_value(); retval.append (custom_gaussian_smoothing(I, lambda1, lambda2, theta)); } else error("__custom_gaussian_smoothing__: first input should be a real or integer array"); return retval; } image-2.16.1/src/PaxHeaders.61586/hough_line.cc0000644000000000000000000000006215005110255015644 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/hough_line.cc0000644000175000017500000000742315005110255017250 0ustar00avinoamavinoam00000000000000// Copyright (C) 2004 Stefan van der Walt // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1 Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // 2 Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include DEFUN_DLD(hough_line, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {[@var{H}, @var{R}] =} hough_line(@var{I}, @var{angles})\n\ Calculate the straight line Hough transform of a binary image @var{I}.\n\ \n\ The angles are given in radians and default to -pi/2:pi/2.\n\ \n\ @var{H} is the resulting Hough transform, and @var{R} is the radial distances.\n\ \n\ The algorithm is described in\n\ Digital Image Processing by Gonzales & Woods (2nd ed., p. 587)\n\ \n\ For a Matlab compatible Hough transform see hough.m\n\ @end deftypefn\n\ ") { octave_value_list retval; const int nargin = args.length (); const bool DEF_THETA = (nargin == 1); if (nargin < 1 || nargin > 2) print_usage (); const Matrix I = args (0).matrix_value (); const ColumnVector thetas = (DEF_THETA) ? ColumnVector (octave::range (-M_PI/2.0, M_PI/180.0, M_PI/2.0).array_value ()) : ColumnVector (args (1).vector_value ()); const int r = I.rows (); const int c = I.columns (); const int thetas_length = thetas.numel (); const double diag_length = sqrt ((r-1)*(r-1) + (c-1)*(c-1)); const int nr_bins = 2 * (int)ceil (diag_length) + 1; RowVector bins = RowVector (octave::range (1, nr_bins).array_value ()) - ceil (nr_bins/2.0); const int bins_length = bins.numel (); Matrix J (bins_length, thetas_length, 0.0); for (int i = 0; i < thetas_length; i++) { const double theta = thetas (i); const double cT = cos (theta); const double sT = sin (theta); for (int x = 0; x < r; x++) { for (int y = 0; y < c; y++) { if (I(x, y) == 1) { const int rho = (int)floor (cT*x + sT*y + 0.5); const int bin = (int)(rho - bins (0)); if ((bin > 0) && (bin < bins_length)) J (bin, i)++; } } } } retval.append (J); retval.append (bins); return retval; } /* %!test %! I = zeros(100, 100); %! I(1,1) = 1; I(100,100) = 1; I(1,100) = 1; I(100, 1) = 1; I(50,50) = 1; %! [J, R] = houghtf(I); J = J / max(J(:)); %! assert(size(J) == [length(R) 181]); %! %!demo %! I = zeros(100, 150); %! I(30,:) = 1; I(:, 65) = 1; I(35:45, 35:50) = 1; %! for i = 1:90, I(i,i) = 1;endfor %! I = imnoise(I, 'salt & pepper'); %! imshow(I); %! J = houghtf(I); J = J / max(J(:)); %! imshow(J); */ image-2.16.1/src/PaxHeaders.61586/__boundary__.cc0000644000000000000000000000006215005110255016142 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/__boundary__.cc0000644000175000017500000001264515005110255017550 0ustar00avinoamavinoam00000000000000// Copyright (C) 2010 Andrew Kelly, IPS Radio & Space Services // // 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 . #include /** * Oct-file to trace the boundary of an object in a binary image. * * b = boundary(region, conn=8) */ #include using namespace std; DEFUN_DLD(__boundary__, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Loadable Function} {} boundary(@var{region})\n\ @deftypefnx {Loadable Function} {} boundary(@var{region}, @var{conn})\n\ Trace the boundary of an object in a binary image.\n\ \n\ @code{boundary} computes the exterior clockwise boundary of the single \ @var{conn}-connected object represented by the non-zero pixels \ of @var{region}. It uses an algorithm based on Moore-neighbour tracing.\n\ \n\ @var{conn} can be either 8 (the default) or 4.\n\ \n\ @var{b} is an N-by-2 matrix containing the row/column coordinates of points \ on the boundary. The first boundary point is the first non-zero \ pixel of @var{region}, as determined by @code{find}. The last boundary \ point is the same as the first.\n\ @seealso{boundaries, bwlabel, find}\n\ @end deftypefn") { octave_value_list retval; enum { ROW, COL }; // check number of arguments const int nargin = args.length (); if (nargin > 2 || nargout != 1) error ("__boundary__: wrong number of input arguments"); // extract arguments const boolMatrix unpadded = args (0).bool_matrix_value (); const int conn = (nargin > 1) ? (int) args (1).scalar_value () : 8; // pad to avoid boundary issues int rows = unpadded.rows (); int cols = unpadded.columns (); boolMatrix region (rows + 2, cols + 2, false); for (int r = 0; r < rows; r++) for (int c = 0; c < cols; c++) region.elem (r+1, c+1) = unpadded (r, c); // the padded size rows += 2; cols += 2; // find the (first two) true pixels, if any std::vector pixels; for (int i = 0; pixels.size () < 2 && i < region.numel (); ++i) if (region.elem (i)) pixels.push_back (i); if (pixels.empty ()) return retval; // the starting boundary point const int start = pixels [0]; std::vector bound; bound.push_back (start); // is this the only point? if (pixels.size () == 1) bound.push_back (start); // otherwise, find the boundary by tracing the Moore neighbourhood of its pixels // // 8-connected: 7 0 1 4-connected: 0 // 6 . 2 3 . 1 // 5 4 3 2 else { // relative row/column positions static const int row8 [] = {-1, -1, 0, 1, 1, 1, 0, -1}; static const int col8 [] = { 0, 1, 1, 1, 0, -1, -1, -1}; static const int row4 [] = {-1, 0, 1, 0 }; static const int col4 [] = { 0, 1, 0, -1 }; const int* mr = (conn == 4) ? row4 : row8; const int* mc = (conn == 4) ? col4 : col8; // next after backing-up static const int back8 [] = {7, 7, 1, 1, 3, 3, 5, 5}; static const int back4 [] = {3, 0, 1, 2}; const int* mBack = (conn == 4) ? back4 : back8; // relative indexes into the region for the Moore neighbourhood pixels std::vector mi (conn); for (int i = 0; i < conn; ++i) mi[i] = mr[i] + (rows * mc [i]); // next neighbourhood pixel static const int next8 [] = {1, 2, 3, 4, 5, 6, 7, 0}; static const int next4 [] = {1, 2, 3, 0}; const int* mNext = (conn == 4) ? next4 : next8; // the final boundary point to be visited int finish = 0; for (int i = 0; i < conn; ++i) if (region.elem(start + mi[i])) finish = start + mi[i]; // look for the next boundary point, starting at the next neighbour int bp = start; int mCurrent = mNext [0]; bool done = false; while (!done) { // next neighbour int cp = bp + mi[mCurrent]; // if this pixel is false, try the next one if (!region.elem (cp)) { mCurrent = mNext [mCurrent]; } // otherwise, we have another boundary point else { bound.push_back (cp); // either we're back at the start for the last time if (bp == finish && cp == start) { done = true; } // or we step back to where we came in from, and continue else { bp = cp; mCurrent = mBack [mCurrent]; } } } } // convert boundary points to row/column coordinates Matrix b (bound.size (), 2); for (unsigned int i = 0; i < bound.size (); i++) { const int point = bound [i]; b (i, ROW) = point % rows; b (i, COL) = point / rows; } retval.append (b); return retval; } image-2.16.1/src/PaxHeaders.61586/__spatial_filtering__.cc0000644000000000000000000000006215005110255020017 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/__spatial_filtering__.cc0000644000175000017500000006517315005110255021431 0ustar00avinoamavinoam00000000000000// Copyright (C) 2008 Søren Hauberg // Copyright (C) 2013 Carnë Draug // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . // The 'compare' and 'selnth' functions are copied from the 'cordflt2' function // developed by Teemu Ikonen. This work was released under GPLv2 or later. // -- Søren Hauberg, March 21st, 2008 #include #include /** * Filter functions for ordered filtering. */ template inline bool compare (const ET a, const ET b) { if (a > b) return 1; else return 0; } template <> inline bool compare (const Complex a, const Complex b) { const double anorm2 = a.real () * a.real () + a.imag () * a.imag (); const double bnorm2 = b.real () * b.real () + b.imag () * b.imag (); if (anorm2 > bnorm2) return 1; else return 0; } // select nth largest member from the array values // Partitioning algorithm, see Numerical recipes chap. 8.5 template ET_OUT selnth (MT &vals, octave_idx_type len, int nth) { ET hinge; int l, r, mid, i, j; l = 0; r = len - 1; for (;;) { // if partition size is 1 or two, then sort and return if (r <= l+1) { if (r == l+1 && compare (vals (l), vals (r))) std::swap (vals (l), vals (r)); return vals (nth); } else { mid = (l+r) >> 1; std::swap (vals (mid), vals (l+1)); // choose median of l, mid, r to be the hinge element // and set up sentinels in the borders (order l, l+1 and r) if (compare (vals (l), vals (r))) std::swap (vals (l), vals (r)); if (compare (vals (l+1), vals (r))) std::swap (vals (l+1), vals (r)); if (compare (vals (l), vals (l+1))) std::swap (vals (l), vals (l+1)); i = l + 1; j = r; hinge = vals (l+1); for (;;) { do i++; while (compare (hinge, vals (i))); do j--; while (compare (vals (j), hinge)); if (i > j) break; std::swap (vals (i), vals (j)); } vals (l+1) = vals (j); vals (j) = hinge; if (j >= nth) r = j - 1; if (j <= nth) l = i; } } } template ET_OUT min_filt (MT &vals, octave_idx_type len, int not_used) { ET_OUT min_val = vals (0); for (octave_idx_type i = 1; i < len; i++) min_val = compare (min_val, vals (i)) ? vals (i) : min_val; return min_val; } template ET_OUT max_filt (MT &vals, octave_idx_type len, int not_used) { ET_OUT max_val = vals (0); for (octave_idx_type i = 1; i < len; i++) max_val = compare (max_val, vals (i)) ? max_val : vals (i); return max_val; } /** * Filter functions for standard deviation filters */ template inline ET square (const ET a) { return a * a; } template ET_OUT std_filt (MT &vals, octave_idx_type len, int norm) { // Compute mean ET_OUT mean = 0; for (octave_idx_type i = 0; i < len; i++) mean += (ET_OUT)vals (i); mean /= (ET_OUT)len; // Compute sum of square differences from the mean ET_OUT var = 0; for (octave_idx_type i = 0; i < len; i++) var += square ((ET_OUT)vals (i) - mean); // Normalise to produce variance var /= (ET_OUT)norm; // Compute std. deviation return sqrt (var); } /** * Functions for the entropy filter. */ /* We only need the explicit typed versions */ template void get_entropy_info (ET &add, int &nbins) { } #define ENTROPY_INFO(TYPE, ADD, NBINS) \ template <> \ void get_entropy_info (TYPE &add, int &nbins) \ { \ add = ADD; \ if (nbins <= 0) \ nbins = NBINS; \ } ENTROPY_INFO (bool, 0, 2) ENTROPY_INFO (octave_int8, 128, 256) //ENTROPY_INFO (octave_int16, 32768, 65536) ENTROPY_INFO (octave_uint8, 0, 256) //ENTROPY_INFO (octave_uint16, 0, 65536) #undef ENTROPY_INFO template ET_OUT entropy_filt (MT &vals, octave_idx_type len, int nbins) { ET add; get_entropy_info (add, nbins); // Compute histogram from values ColumnVector hist (nbins, 0); for (octave_idx_type i = 0; i < len; i++) hist(vals(i) + add)++; for (octave_idx_type i = 0; i < nbins; i++) hist(i) /= (double)len; // Compute entropy double entropy = 0; for (octave_idx_type i = 0; i < nbins; i++) { const double p = hist (i); if (p > 0) entropy -= p * std::log2 (p); } return entropy; } /** * The function for the range filter */ template ET_OUT range_filt (MT &vals, octave_idx_type len, int not_used) { const ET_OUT min_val = min_filt (vals, len, not_used); const ET_OUT max_val = max_filt (vals, len, not_used); return max_val - min_val; } // The general function for doing the filtering // // We differentiate between MT and MTout for cases such as std and // entropy, where the output will have a different class than the input. template octave_value do_filtering (const MT &in, const boolNDArray &se, ETout (*filter_function) (MT&, octave_idx_type, int), const MT &S, int arg4) { typedef typename MT::element_type Pin; const octave_idx_type ndims = in.ndims (); const octave_idx_type se_nnz = se.nnz (); const dim_vector se_size = se.dims ().redim (ndims); const dim_vector in_size = in.dims (); // Create output matrix dim_vector out_size (in_size); for (octave_idx_type i = 0; i < ndims; i++) { out_size (i) = in_size (i) - se_size (i) + 1; } MTout out (out_size); // We will loop for all elements of the output matrix. On each iteration // we collect the values from the input matrix as marked by the // structuring element (SE), and pass them to the filtering function. // The value returned is then assigned assigned to the output matrix. // Using dim_vector's and increment_index() allows us to support matrices // with any number of dimensions. // Create a 2D array with the subscript indices for each of the // true elements on the SE. Each column has the subscripts for // each true elements, and the rows are the dimensions. // We also don't need the heights in a matrix. We can extract the // ones that matter for us and place them in a vector to access // them easily during the loop. Array se_sub (dim_vector (ndims, 1), 0); Array nnz_sub (dim_vector (ndims, se_nnz)); Array heights (dim_vector (se_nnz, 1)); for (octave_idx_type found = 0; found < se_nnz; ) { if (se(se_sub)) { // insert the coordinate vectors on the next column nnz_sub.insert (se_sub, 0, found); // store new height value heights(found) = S(se_sub); found++; } boolNDArray::increment_index (se_sub, se_size); } // Create array with subscript indexes for the elements being // evaluated at any given time. We will be using linear indexes // later but need the subscripts to add them. Array in_sub (dim_vector (ndims, 1)); Array out_sub (dim_vector (ndims, 1), 0); // For each iteration of the output matrix, will store the values from // the input matrix that will be passed to the filtering function. MT values (dim_vector (1, se_nnz)); // Get pointers to the fortran vector for good performance. ETout* out_fvec = out.fortran_vec (); Pin* values_fvec = values.fortran_vec (); Pin* heights_fvec = heights.fortran_vec (); octave_idx_type* in_sub_fvec = in_sub.fortran_vec (); octave_idx_type* out_sub_fvec = out_sub.fortran_vec (); octave_idx_type* nnz_sub_fvec = nnz_sub.fortran_vec (); // We iterate for all elements of the output matrix const octave_idx_type out_numel = out.numel (); for (octave_idx_type out_ind = 0; out_ind < out_numel; out_ind++) { // On each iteration we get the subscript indexes for the output // matrix (obtained with increment_index), and add to it the // subscript indexes of each of the nnz elements in the SE. These // are the subscript indexes for the elements in input matrix that // need to be evaluated for that element in the output matrix octave_idx_type nnz_sub_ind = 0; for (octave_idx_type se_ind = 0; se_ind < se_nnz; se_ind++) { nnz_sub_ind = se_ind * ndims; // move to the next column for (octave_idx_type n = 0; n < ndims; n++, nnz_sub_ind++) { // get subcript indexes for the input matrix in_sub_fvec[n] = out_sub_fvec[n] + nnz_sub_fvec[nnz_sub_ind]; } values_fvec[se_ind] = in(in_sub) + heights_fvec[se_ind]; } // Compute filter result out_fvec[out_ind] = filter_function (values, se_nnz, arg4); // Prepare for next iteration boolNDArray::increment_index (out_sub, out_size); OCTAVE_QUIT; } return octave_value (out); } DEFUN_DLD(__spatial_filtering__, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} __spatial_filtering__(@var{A}, @var{domain},\ @var{method}, @var{S}, @var{arg})\n\ Implementation of two-dimensional spatial filtering. In general this function\n\ should NOT be used -- user interfaces are available in other functions.\n\ The function computes local characteristics of the image @var{A} in the domain\n\ @var{domain}. The following values of @var{method} are supported.\n\ \n\ @table @asis\n\ @item @qcode{\"ordered\"}\n\ Perform ordered filtering. The output in a pixel is the @math{n}th value of a\n\ sorted list containing the elements of the neighbourhood. The value of @math{n}\n\ is given in the @var{arg} argument. The corresponding user interface is available\n\ in @code{ordfilt2} and @code{ordfiltn}.\n\ \n\ @item @qcode{\"std\"}\n\ Compute the local standard deviation. The corresponding user interface is available\n\ in @code{stdfilt}.\n\ \n\ @item @qcode{\"entropy\"}\n\ Compute the local entropy. The corresponding user interface is available\n\ in @code{entropyfilt}.\n\ \n\ @item @qcode{\"range\"}\n\ Compute the local range of the data. The corresponding user interface is\n\ available in @code{rangefilt}.\n\ \n\ @end table\n\ @seealso{ordfilt2}\n\ @end deftypefn\n\ ") { octave_value_list retval; const octave_idx_type nargin = args.length (); if (nargin < 4) print_usage (); const octave_value A (args(0)); const octave_value S (args(3)); const boolNDArray domain = args(1).bool_array_value (); octave_idx_type len = 0; for (octave_idx_type i = 0; i < domain.numel (); i++) len += domain (i); const int ndims = domain.ndims (); if (A.ndims () != ndims) error ("__spatial_filtering__: A and DOMAIN must have same dimensions"); if (S.ndims () != ndims) error ("__spatial_filtering__: DOMAIN and S must have same size"); for (octave_idx_type i = 0; i < ndims; i++) if (domain.size (i) != S.dims ()(i)) error ("__spatial_filtering__: DOMAIN and S must have same size"); int arg4 = (nargin == 4) ? 0 : args(4).int_value (); const std::string method = args(2).string_value (); #define GENERAL_ACTION(MT, FUN, ET, MT_OUT, ET_OUT, FILTER_FUN) \ retval = do_filtering (A.FUN (), domain, \ FILTER_FUN, \ S.FUN (), arg4) \ if (method == "ordered") { // Handle input arg4 -= 1; // convert arg to zero-based index if (arg4 > len - 1) { warning ("__spatial_filtering__: nth should be less than number of non-zero " "values in domain setting nth to largest possible value"); arg4 = len - 1; } if (arg4 < 0) { warning ("__spatial_filtering__: nth should be non-negative, setting to 1"); arg4 = 0; } #define ACTION(MT, FUN, ET) \ GENERAL_ACTION(MT, FUN, ET, MT, ET, selnth) if (A.is_int8_type ()) ACTION (int8NDArray, int8_array_value, octave_int8); else if (A.is_int16_type ()) ACTION (int16NDArray, int16_array_value, octave_int16); else if (A.is_int32_type ()) ACTION (int32NDArray, int32_array_value, octave_int32); else if (A.is_int64_type ()) ACTION (int64NDArray, int64_array_value, octave_int64); else if (A.is_uint8_type ()) ACTION (uint8NDArray, uint8_array_value, octave_uint8); else if (A.is_uint16_type ()) ACTION (uint16NDArray, uint16_array_value, octave_uint16); else if (A.is_uint32_type ()) ACTION (uint32NDArray, uint32_array_value, octave_uint32); else if (A.is_uint64_type ()) ACTION (uint64NDArray, uint64_array_value, octave_uint64); else if (A.islogical ()) ACTION (boolNDArray, bool_array_value, bool); else if (A.isreal ()) { if (A.is_single_type ()) ACTION (FloatNDArray, float_array_value, float); else ACTION (NDArray, array_value, double); } else if (A.iscomplex ()) { if (A.is_single_type ()) ACTION (FloatComplexNDArray, float_complex_array_value, FloatComplex); else ACTION (ComplexNDArray, complex_array_value, Complex); } else error ("__spatial_filtering__: A should be real, complex, or integer"); #undef ACTION } else if (method == "range") { #define ACTION(MT, FUN, ET) \ GENERAL_ACTION(MT, FUN, ET, MT, ET, range_filt) if (A.is_int8_type ()) ACTION (int8NDArray, int8_array_value, octave_int8); else if (A.is_int16_type ()) ACTION (int16NDArray, int16_array_value, octave_int16); else if (A.is_int32_type ()) ACTION (int32NDArray, int32_array_value, octave_int32); else if (A.is_int64_type ()) ACTION (int64NDArray, int64_array_value, octave_int64); else if (A.is_uint8_type ()) ACTION (uint8NDArray, uint8_array_value, octave_uint8); else if (A.is_uint16_type ()) ACTION (uint16NDArray, uint16_array_value, octave_uint16); else if (A.is_uint32_type ()) ACTION (uint32NDArray, uint32_array_value, octave_uint32); else if (A.is_uint64_type ()) ACTION (uint64NDArray, uint64_array_value, octave_uint64); else if (A.islogical ()) ACTION (boolNDArray, bool_array_value, bool); else if (A.isreal ()) { if (A.is_single_type ()) ACTION (FloatNDArray, float_array_value, float); else ACTION (NDArray, array_value, double); } else if (A.iscomplex ()) { if (A.is_single_type ()) ACTION (FloatComplexNDArray, float_complex_array_value, FloatComplex); else ACTION (ComplexNDArray, complex_array_value, Complex); } else error ("__spatial_filtering__: A should be real, complex, or integer"); #undef ACTION } else if (method == "std") { // Compute normalisation factor if (arg4 == 0) arg4 = len - 1; // unbiased else arg4 = len; // max. likelihood #define ACTION(MT, FUN, ET) \ GENERAL_ACTION(MT, FUN, ET, NDArray, double, std_filt) if (A.is_int8_type ()) ACTION (int8NDArray, int8_array_value, octave_int8); else if (A.is_int16_type ()) ACTION (int16NDArray, int16_array_value, octave_int16); else if (A.is_int32_type ()) ACTION (int32NDArray, int32_array_value, octave_int32); else if (A.is_int64_type ()) ACTION (int64NDArray, int64_array_value, octave_int64); else if (A.is_uint8_type ()) ACTION (uint8NDArray, uint8_array_value, octave_uint8); else if (A.is_uint16_type ()) ACTION (uint16NDArray, uint16_array_value, octave_uint16); else if (A.is_uint32_type ()) ACTION (uint32NDArray, uint32_array_value, octave_uint32); else if (A.is_uint64_type ()) ACTION (uint64NDArray, uint64_array_value, octave_uint64); else if (A.islogical ()) ACTION (boolNDArray, bool_array_value, bool); else if (A.is_real_matrix ()) { if (A.is_single_type ()) ACTION (FloatNDArray, float_array_value, float); else ACTION (NDArray, array_value, double); } else error ("__spatial_filtering__: A should be real or logical"); #undef ACTION } else if (method == "entropy") { #define ACTION(MT, FUN, ET) \ GENERAL_ACTION(MT, FUN, ET, NDArray, double, entropy_filt) if (A.islogical ()) ACTION (boolNDArray, bool_array_value, bool); else if (A.is_uint8_type ()) ACTION (uint8NDArray, uint8_array_value, octave_uint8); else error ("__spatial_filtering__: A should be logical or uint8"); #undef ACTION } else error ("__spatial_filtering__: unknown method '%s'.", method.c_str ()); return retval; } /* %!error %! __spatial_filtering__ (ones (10), ones (3), "std", ones (10), 0) %!error %! __spatial_filtering__ (ones (10), ones (3), "std", ones (3, 3, 3), 0) %!error %! __spatial_filtering__ (ones (10), ones (3), "std", ones (1, 9), 0) %!shared a, domain, s, out %! a = [ 82 2 97 43 79 43 41 65 51 11 %! 60 65 21 56 94 77 36 38 75 39 %! 32 68 78 1 16 75 76 90 81 56 %! 43 90 82 41 36 1 87 19 18 63 %! 63 64 2 48 18 43 38 25 22 99 %! 12 46 90 79 3 92 39 79 10 22 %! 38 98 11 10 40 90 88 38 4 76 %! 54 37 9 4 33 98 36 47 53 57 %! 38 76 82 50 14 74 64 99 7 33 %! 88 96 41 62 84 89 97 23 41 3]; %! %! domain = ones (3); %! s = zeros (3); %! %! out = [ 2 1 1 1 16 36 36 11 %! 21 1 1 1 1 1 18 18 %! 2 1 1 1 1 1 18 18 %! 2 2 2 1 1 1 10 10 %! 2 2 2 3 3 25 4 4 %! 9 4 3 3 3 36 4 4 %! 9 4 4 4 14 36 4 4 %! 9 4 4 4 14 23 7 3]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, 1), out); %! %! out = [ 97 97 97 94 94 90 90 90 %! 90 90 94 94 94 90 90 90 %! 90 90 82 75 87 90 90 99 %! 90 90 90 92 92 92 87 99 %! 98 98 90 92 92 92 88 99 %! 98 98 90 98 98 98 88 79 %! 98 98 82 98 98 99 99 99 %! 96 96 84 98 98 99 99 99]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, nnz (domain)), out); %! %! out = [ 60 43 43 43 43 43 51 51 %! 60 56 36 36 36 38 38 39 %! 63 48 18 18 36 38 25 25 %! 46 48 36 36 36 38 22 22 %! 38 46 11 40 39 39 25 22 %! 37 11 10 33 39 47 38 38 %! 38 11 11 33 40 64 38 38 %! 41 41 33 50 64 64 41 33]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, 4), out); %! %! out = [ 31.223 33.788 35.561 31.011 26.096 20.630 20.403 24.712 %! 23.428 29.613 32.376 34.002 33.593 32.470 29.605 26.333 %! 27.834 32.890 29.903 24.207 30.083 32.497 31.898 32.600 %! 32.027 28.995 33.530 31.002 32.241 32.004 27.501 32.070 %! 34.682 36.030 33.046 33.745 32.509 27.352 28.607 34.180 %! 32.709 37.690 32.992 40.036 34.456 26.656 27.685 26.863 %! 30.971 36.227 25.775 34.873 29.917 25.269 32.292 30.410 %! 29.135 31.626 30.056 33.594 30.814 28.853 30.917 29.120]; %!assert (__spatial_filtering__ (a, domain, "std", s), out, 0.001); %! %! out = [ 95 96 96 93 78 54 54 79 %! 69 89 93 93 93 89 72 72 %! 88 89 81 74 86 89 72 81 %! 88 88 88 91 91 91 77 89 %! 96 96 88 89 89 67 84 95 %! 89 94 87 95 95 62 84 75 %! 89 94 78 94 84 63 95 95 %! 87 92 80 94 84 76 92 96]; %!assert (__spatial_filtering__ (a, domain, "range", s), out); %! %! domain = [ 1 1 0 %! 0 1 1 %! 0 1 0]; %! %! out = [ 2 2 1 16 36 36 38 39 %! 60 1 1 16 1 36 19 18 %! 32 2 1 1 1 19 18 18 %! 2 2 18 3 1 1 19 10 %! 46 2 2 3 18 38 10 4 %! 11 9 4 3 3 36 4 4 %! 9 4 4 10 36 36 38 4 %! 37 9 4 4 33 36 7 7]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, 1), out); %! %! out = [ 82 97 97 94 79 76 90 81 %! 90 82 56 94 94 90 90 81 %! 90 82 78 36 87 87 90 90 %! 90 90 82 43 92 87 87 99 %! 98 90 79 92 92 88 79 25 %! 98 90 90 90 98 92 79 79 %! 98 98 50 98 98 90 99 57 %! 96 82 62 84 98 99 99 53]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, nnz (domain)), out); %! %! out = [ 68 78 94 79 77 43 75 75 %! 78 78 41 75 77 87 81 75 %! 82 78 48 18 75 76 76 81 %! 64 90 79 41 43 39 79 22 %! 90 79 48 48 90 79 38 22 %! 46 46 79 79 92 88 47 76 %! 76 82 33 40 90 88 88 53 %! 82 50 50 74 89 98 47 47]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, 4), out); %! %! out = [ 34.2389 39.2772 39.6699 31.6812 20.7364 16.5439 22.2419 17.2395 %! 11.9248 36.3084 21.6217 30.8350 36.4047 21.6726 30.9144 26.1017 %! 22.2980 33.2746 27.5808 14.5017 36.8890 29.0259 34.6020 33.2521 %! 32.2490 37.9579 26.9685 17.1959 32.5346 31.3847 33.5976 36.8280 %! 21.3354 40.1833 34.0044 33.9882 32.9894 24.1102 25.6613 9.0995 %! 35.4641 35.3794 39.0871 35.4753 39.9775 28.7193 26.7451 35.6553 %! 35.2179 45.3398 19.3210 35.2987 28.4042 24.0832 26.8421 25.0539 %! 23.4307 26.2812 26.3287 35.6959 25.2646 28.1016 34.9829 17.9221]; %!assert (__spatial_filtering__ (a, domain, "std", s), out, 0.001); %! %! out = [ 80 95 96 78 43 40 52 42 %! 30 81 55 78 93 54 71 63 %! 58 80 77 35 86 68 72 72 %! 88 88 64 40 91 86 68 89 %! 52 88 77 89 74 50 69 21 %! 87 81 86 87 95 56 75 75 %! 89 94 46 88 62 54 61 53 %! 59 73 58 80 65 63 92 46]; %!assert (__spatial_filtering__ (a, domain, "range", s), out); %! %! s = [ 1 -3 4 %! 6 -7 2 %! -1 3 -5]; %! %! out = [ -1 3 4 19 38 29 31 41 %! 61 3 -6 9 4 33 22 21 %! 33 5 -2 2 -6 21 12 11 %! 4 -5 20 6 -2 2 16 13 %! 39 -1 3 -4 19 32 12 3 %! 13 4 3 0 4 36 6 -3 %! 11 2 -3 11 38 29 35 1 %! 34 6 1 5 34 33 9 0]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, 1), out); %! %! out = [ 83 94 98 87 80 79 93 84 %! 93 85 53 91 95 92 83 74 %! 84 75 79 29 89 80 87 91 %! 87 93 83 45 95 84 88 101 %! 101 83 72 94 93 91 72 26 %! 91 87 91 92 101 93 76 80 %! 95 99 53 100 91 91 102 59 %! 99 75 65 87 95 101 92 50]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, nnz (domain)), out); %! %! out = [ 71 81 96 79 78 44 77 68 %! 80 71 44 77 78 90 83 72 %! 83 75 51 21 72 76 77 78 %! 57 91 82 42 40 42 82 20 %! 92 81 45 49 85 81 41 24 %! 43 47 76 80 90 81 50 78 %! 79 85 35 37 87 85 89 46 %! 84 52 43 76 92 100 44 48]; %!assert (__spatial_filtering__ (a, domain, "ordered", s, 4), out); %! %! out = [ 34.903 40.206 39.885 28.627 20.620 19.248 25.209 17.111 %! 14.536 35.865 23.221 32.230 34.903 23.923 28.879 22.621 %! 20.635 30.113 29.351 11.610 38.863 25.936 34.608 34.482 %! 29.811 40.998 28.279 17.897 34.666 29.978 36.150 38.213 %! 25.066 39.240 30.013 37.300 31.856 27.428 22.884 10.281 %! 31.890 34.761 39.645 37.526 39.336 27.031 25.648 39.285 %! 35.017 47.776 22.764 35.912 25.460 25.636 29.861 24.566 %! 25.213 25.000 26.391 38.451 24.631 31.305 31.118 20.611]; %!assert (__spatial_filtering__ (a, domain, "std", s), out, 0.001); %! %! out = [ 84 91 94 68 42 50 62 43 %! 32 82 59 82 91 59 61 53 %! 51 70 81 27 95 59 75 80 %! 83 98 63 39 97 82 72 88 %! 62 84 69 98 74 59 60 23 %! 78 83 88 92 97 57 70 83 %! 84 97 56 89 53 62 67 58 %! 65 69 64 82 61 68 83 50]; %!assert (__spatial_filtering__ (a, domain, "range", s), out); */ image-2.16.1/src/PaxHeaders.61586/imreconstruct.cc0000644000000000000000000000006215005110255016424 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/imreconstruct.cc0000644000175000017500000004051415005110255020026 0ustar00avinoamavinoam00000000000000// Copyright (C) 2014 Carnë Draug // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, see // . // This file implements imreconstruct as described on "Vincent, L. (1993). // Morphological grayscale reconstruction in image analysis: applications // and efficient algorithms. Image Processing, IEEE Transactions on, 2(2), // 176-201." // // Our strategy to handle elements in the border is to simply pad it with // the lowest value for the type, which will be ignored on the comparisons. // This should still be more efficient than using subscript indices to find // when we are on the border. #include #include #include #include #include #include #include #include #include "connectivity.h" using namespace octave_image_pkg; /* ## A dirty implementation of the fast hybrid reconstruction as m file ## for testing purposes. function marker = fast_hybrid_reconstruction (marker, mask) ## works for 10x10 matrices, padded to 12x12 with zeros, when ## connectivity is ones (3, 3) offsets = [-13 -12 -11 -1 1 11 12 13]; pos_offsets = [0 1 11 12 13]; # don't forget the zero neg_offsets = - [pos_offsets]; ## the raster scan for c = 2:(columns(marker) -1) for r = 2:(rows(marker) -1) i = sub2ind (size (marker), r, c); marker(r,c) = min (mask(r,c), max (marker(i + [neg_offsets]))); endfor endfor ## the antiraster scan fifo = []; for c = (columns(marker) -1):-1:2 for r = (rows(marker) -1):-1:2 i = sub2ind (size (marker), r, c); offs = marker(i + [pos_offsets]); marker(r,c) = min (mask(r,c), max (offs)); offs(1) = []; #remove itself picks = offs < marker(i) & offs < mask(i + [pos_offsets(2:end)]); if (any (picks)) fifo(end+1) = i; endif endfor endfor ## the propagation step while (numel (fifo) != 0) p = fifo(1); fifo(1) = []; for i = offsets; if (marker(p +i) < marker(p) && mask(p+i) != marker(p+i)) marker(p +i) = min (marker(p), mask(p+i)); fifo(end+1) = p+i; endif endfor endwhile endfunction */ template static void scan_raster_order (T& padded_marker, const T& padded_mask, const dim_vector& original_size, const Array& padding_lengths, const Array& raster_neighbours) { typedef typename T::element_type P; P* J = padded_marker.fortran_vec (); const P* I = padded_mask.data (); const octave_idx_type* pads = padding_lengths.data (); const octave_idx_type* neighbours = raster_neighbours.data (); const octave_idx_type n_neighbours = raster_neighbours.numel (); std::function scan; scan = [&] (const octave_idx_type dim) -> void { J += pads[dim]; I += pads[dim]; if (dim == 0) { for (octave_idx_type k = 0; k < original_size.xelem (0); k++, J++, I++) { for (octave_idx_type i = 0; i < n_neighbours; i++) if (*J < J[neighbours[i]]) *J = J[neighbours[i]]; if (*J > *I) *J = *I; } } else for (octave_idx_type i = 0; i < original_size.xelem (dim); i++) scan (dim-1); J += pads[dim]; I += pads[dim]; return; }; scan (original_size.length () -1); return; } template static std::queue scan_antiraster_order (T& padded_marker, const T& padded_mask, const dim_vector& original_size, const Array& padding_lengths, const Array& antiraster_neighbours) { typedef typename T::element_type P; std::queue unfinished; P* J = padded_marker.fortran_vec (); const P* I = padded_mask.data (); const octave_idx_type* pads = padding_lengths.data (); const octave_idx_type* neighbours = antiraster_neighbours.data (); const octave_idx_type n_neighbours = antiraster_neighbours.numel (); J += padded_marker.numel () -1; I += padded_marker.numel () -1; octave_idx_type ind = padded_marker.numel () -1; std::function scan; scan = [&] (const octave_idx_type dim) -> void { J -= pads[dim]; I -= pads[dim]; ind -= pads[dim]; if (dim == 0) { for (octave_idx_type k = 0; k < original_size.xelem (0); k++, J--, I--, ind--) { for (octave_idx_type i = 0; i < n_neighbours; i++) if (*J < J[neighbours[i]]) *J = J[neighbours[i]]; if (*J > *I) *J = *I; for (octave_idx_type i = 0; i < n_neighbours; i++) if (J[neighbours[i]] < *J && J[neighbours[i]] < I[neighbours[i]]) unfinished.push (ind); } } else for (octave_idx_type i = 0; i < original_size.xelem (dim); i++) scan (dim-1); J -= pads[dim]; I -= pads[dim]; ind -= pads[dim]; return; }; scan (original_size.length () -1); return unfinished; } template static void propagation_step (T& padded_marker, const T& padded_mask, std::queue& unfinished, const Array& deleted_neighbours) { typedef typename T::element_type P; P* J = padded_marker.fortran_vec (); const P* I = padded_mask.data (); const octave_idx_type* neighbours = deleted_neighbours.data (); const octave_idx_type n_neighbours = deleted_neighbours.numel (); while (! unfinished.empty ()) { octave_idx_type p = unfinished.front (); unfinished.pop (); for (octave_idx_type k = 0; k < n_neighbours; k++) { octave_idx_type q = p + neighbours[k]; if (J[q] < J[p] && I[q] != J[q]) { J[q] = octave::math::min (J[p], I[q]); unfinished.push (q); } } OCTAVE_QUIT; } return; } template static T fast_hybrid_reconstruction (const T& marker, const T& mask, const connectivity& conn) { typedef typename T::element_type P; const dim_vector original_size = marker.dims (); T padded_marker = conn.create_padded (marker, connectivity::min_value

()); const T padded_mask = conn.create_padded (mask, connectivity::min_value

()); const dim_vector padded_size = padded_marker.dims (); const Array padding_lengths = connectivity::padding_lengths (original_size, padded_size); scan_raster_order (padded_marker, padded_mask, original_size, padding_lengths, conn.negative_neighbourhood (padded_size)); OCTAVE_QUIT; std::queue unfinished = scan_antiraster_order (padded_marker, padded_mask, original_size, padding_lengths, conn.positive_neighbourhood (padded_size)); OCTAVE_QUIT; propagation_step (padded_marker, padded_mask, unfinished, conn.deleted_neighbourhood (padded_size)); conn.unpad (padded_marker); return padded_marker; } template static T reconstruct (const T& marker, const T& mask, const connectivity& conn) { return fast_hybrid_reconstruction (marker, mask, conn); } // TODO implement the following by reusing the code in bwlabeln //static boolNDArray //reconstruct (const boolNDArray& marker, const boolNDArray& mask, // const connectivity& conn) //{ // /* // 1. Label the connected components of the mask image, i.e., each of these // components is assigned a unique number. Note that this step can itself // be implemented very efficiently by using algorithms based on chain an // loops [16] or queues of pixels [23, 26]. // 2. Determine the labels of the connected components which contain at // least a pixel of the marker image. // 3. Remove all the connected components whose label is not one of the // previous ones. // */ // return boolNDArray (); //} DEFUN_DLD(imreconstruct, args, , "\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {} imreconstruct (@var{marker}, @var{mask})\n\ @deftypefnx {Loadable Function} {} imreconstruct (@var{marker}, @var{mask}, @var{conn})\n\ \n\ @seealso{imclearborder, imdilate, imerode}\n\ @end deftypefn") { const octave_idx_type nargin = args.length (); if (nargin < 2 || nargin > 3) print_usage (); if (args(0).class_name () != args(1).class_name ()) error ("imreconstruct: MARKER and MASK must be of same class"); connectivity conn; if (nargin > 2) conn = conndef (args(2)); else { try { conn = connectivity (args(0).ndims (), "maximal"); } catch (invalid_connectivity& e) { error ("imreconstruct: unable to create connectivity (%s)", e.what ()); } } octave_value marker (args(0)); #define RECONSTRUCT(TYPE) \ ret = reconstruct (marker.TYPE ## _array_value (), \ args(1).TYPE ## _array_value (), conn); #define IF_TYPE(TYPE) \ if (marker.is_ ## TYPE ## _type ()) \ RECONSTRUCT (TYPE) #define INT_BRANCH(TYPE) \ IF_TYPE(u ## TYPE) \ else IF_TYPE(TYPE) #define FLOAT_BRANCH(CR) \ if (marker.is_single_type ()) \ ret = reconstruct (marker.float_ ## CR ## array_value (), \ args(1).float_ ## CR ## array_value (), conn); \ else \ ret = reconstruct (marker.CR ## array_value (), \ args(1).CR ## array_value (), conn); octave_value ret; if (marker.islogical ()) RECONSTRUCT(bool) else INT_BRANCH (int8) else INT_BRANCH (int16) else INT_BRANCH (int32) else INT_BRANCH (int64) else if (marker.isreal ()) { FLOAT_BRANCH() } else if (marker.iscomplex ()) { FLOAT_BRANCH(complex_) } else error ("imreconstruct: unsupported class %s for MARKER", marker.class_name ().c_str ()); #undef IF_TYPE #undef INT_BRANCH #undef FLOAT_BRANCH return ret; } /* ## When using the fast hybrid reconstruction (and specially with random ## images), and if the images are small, it is often finished after the ## antiraster scan and before the propagation step. Using larger images ## makes sure we get in the propagation step and that we catch bugs in there. ## This function does exactly what imreconstruct is meant to but is, in ## the words of Luc Vicent 1993, and I can attest to it, "[...] not suited ## to conventional computers, where its execution time is often of several ## minutes." %!function recon = parallel_reconstruction (marker, mask, %! conn = conndef (ndims (marker), "maximal")) %! do %! previous = marker; %! marker = imdilate (marker, conn); %! ## FIXME https://savannah.gnu.org/bugs/index.php?43712 %! if (strcmp (class (marker), "logical")) %! marker = marker & mask; %! else %! marker = min (marker, mask); %! endif %! until (all ((marker == previous)(:))) %! recon = marker; %!endfunction %!test %! for cl = {"int8", "uint8", "int16", "uint16", "int32", "uint32"} %! cl = cl{1}; %! a = randi ([intmin(cl) intmax(cl)-30], 100, 100, cl); %! b = a + randi (20, 100, 100, cl); %! assert (imreconstruct (a, b), parallel_reconstruction (a, b)) %! endfor %! for cl = {"double", "single"} %! cl = cl{1}; %! a = (rand (100, 100, cl) - 0.5) .* 1000; %! b = a + rand (100, 100, cl) * 100; %! assert (imreconstruct (a, b), parallel_reconstruction (a, b)) %! endfor %!test %! for cl = {"int8", "uint8", "int16", "uint16", "int32", "uint32"} %! cl = cl{1}; %! a = randi ([intmin(cl) intmax(cl)-30], 100, 100, cl); %! b = a + randi (20, 100, 100, cl); %! c = [0 1 0; 1 1 1; 0 1 0]; %! assert (imreconstruct (a, b, c), parallel_reconstruction (a, b, c)) %! endfor %!test %! a = randi (210, 100, 100); %! b = a + randi (20, 100, 100); %! c = ones (3, 1); %! assert (imreconstruct (a, b, c), parallel_reconstruction (a, b, c)) %!test %! a = randi (210, 500, 500, 10, 4); %! b = a + randi (20, 500, 500, 10, 4); %! c = ones (3, 3, 3); %! assert (imreconstruct (a, b, c), parallel_reconstruction (a, b, c)) %!test %! a = randi (210, 500, 500, 10, 4); %! b = a + randi (20, 500, 500, 10, 4); %! c = conndef (4, "minimal"); %! assert (imreconstruct (a, b, c), parallel_reconstruction (a, b, c)) %!test %! a = [ 0 0 0 0 0 0 0 1 0 0 %! 0 0 0 0 0 0 0 1 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 1 0 0 %! 0 0 0 0 0 0 1 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 1 0 0 0 0 0 %! 0 0 0 0 0 0 0 1 0 0]; %! %! b = [ 0 1 0 0 0 0 0 1 1 0 %! 1 1 0 0 0 1 0 1 1 0 %! 1 1 0 0 1 0 0 0 0 0 %! 1 1 0 0 0 1 1 0 0 0 %! 1 0 0 0 0 0 1 1 0 0 %! 0 1 0 0 0 0 1 1 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 1 1 0 0 0 0 %! 0 0 0 1 1 0 0 0 0 0 %! 1 0 0 0 1 0 0 1 0 1]; %! %! c = [ 0 1 0 0 0 0 0 1 1 0 %! 1 1 0 0 0 1 0 1 1 0 %! 1 1 0 0 1 0 0 0 0 0 %! 1 1 0 0 0 1 1 0 0 0 %! 1 0 0 0 0 0 1 1 0 0 %! 0 1 0 0 0 0 1 1 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 1 1 0 0 0 0 %! 0 0 0 1 1 0 0 0 0 0 %! 0 0 0 0 1 0 0 1 0 0]; %! assert (imreconstruct (logical (a), logical (b)), logical (c)); %! %! c = [ 0 1 0 0 0 0 0 1 1 0 %! 1 1 0 0 0 0 0 1 1 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 1 1 0 0 0 %! 1 0 0 0 0 0 1 1 0 0 %! 0 0 0 0 0 0 1 1 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 1 1 0 0 0 0 %! 0 0 0 1 1 0 0 0 0 0 %! 0 0 0 0 1 0 0 1 0 0]; %! assert (imreconstruct (logical (a), logical (b), [0 1 0; 1 1 1; 0 1 0]), %! logical (c)); %!test %! do %! b = rand (100, 100, 100) > 0.98; %! until (nnz (b) > 4) %! b = imdilate (b, ones (5, 5, 5)); %! a = false (size (b)); %! f = find (b); %! a(f(randi (numel (f), 6, 1))) = true; %! assert (imreconstruct (a, b), parallel_reconstruction (a, b)) ## we try to be smart about the padding so make sure this works. There ## was a nasty bug during development which this test brings up. %!test %! a = randi (200, 100,100, 10, 10); %! b = a + randi (20, 100,100, 10, 10); %! c1 = ones (3, 3, 3); %! c2 = zeros (3, 3, 3, 3); %! c2(:,:,:,2) = c1; %! assert (imreconstruct (a, b, c1), imreconstruct (a, b, c2)) %!test %! ## Values in MARKER above MASK should be clipped (bug #48794) %! ## (well, treated internally as if they were clipped) %! mask = logical ([1 1 1; 1 0 1; 1 1 1]); %! assert (imreconstruct (true (3, 3), mask), mask) %! %! mask = ones (5, 5); %! mask(2:4,2:4) = 0; %! assert (imreconstruct (ones (5, 5), mask), mask) %! %! mask = ones (5, 5); %! mask(2:4,2:4) = 0; %! assert (imreconstruct (repmat (2, [5, 5]), mask), mask) %! %! mask = ones (5, 5); %! mask(2:4,2:4) = 0; %! assert (imreconstruct (repmat (2, [5, 5]), mask), mask) %! %! marker = ones (3, 3, 3, 3); %! mask = marker; %! mask(2, 2, 2, 2) = 0; %! assert (imreconstruct (marker, mask), mask) %! %! marker = randi (210, 100, 100); %! assert (imreconstruct (marker +1, marker), marker) %! assert (imreconstruct (marker +1, marker), imreconstruct (marker, marker)) */ image-2.16.1/src/PaxHeaders.61586/nonmax_suppress.cc0000644000000000000000000000006215005110255016767 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/nonmax_suppress.cc0000644000175000017500000001254215005110255020371 0ustar00avinoamavinoam00000000000000// Copyright (C) 2005 Søren Hauberg // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . #include #include #include #include DEFUN_DLD(nonmax_suppress,args,nargout,"\ -*- texinfo -*-\n\ @deftypefn {Loadable Function} {} nonmax_suppress (@var{Es}, @var{Eo})\n\ Performs non-maximum suppression on the given edge data. \ @var{Es} is a matrix containing the edge strength (the length of \ the gradient), and @var{Eo} is the edge normal orientation (the \ direction of the gradient).\n\ \n\ @end deftypefn\n\ @deftypefn {Loadable Function} {} nonmax_suppress (@var{Es}, @var{Eo},\ @var{low}, @var{high} )\n\ Performs non-maximum suppression and hysteresis thresholdong, using \ @var{low} and @var{high} as thresholds.\n\ \n\ This function is designed to be used as part of the Canny edge \ detection, and not to be used in general. So if you use this function: \ Beware...\n\ \n\ @seealso{edge}\n\ @end deftypefn\n\ ") { if (args.length () != 2 && args.length () != 4) print_usage (); octave_value_list retval; std::stack< std::pair > S; /* Neighbourhood directions in radians */ const double d[4] = { 0.0, M_PI * 45.0 / 180.0, M_PI * 90.0 / 180.0, M_PI * 135.0 / 180.0 }; const Matrix Es = args(0).matrix_value(); Matrix Eo = args(1).matrix_value(); double low, high; bool hysteresis = (args.length()==4); if (hysteresis) { low = args(2).scalar_value(); high = args(3).scalar_value(); } else { low = high = 0; } const int rows = Es.rows(); const int cols = Es.columns(); /**************************** ** Non-maximum suppression ** ****************************/ Matrix In = Matrix( rows, cols, 0.0 ); for (int r = 1; r < rows-1; r++) { for (int c = 1; c < cols-1; c++) { const double orientation = Eo(r,c); const double strength = Es(r,c); int best_d = 0; double testdist = M_PI; double dist = M_PI; for (int i = 0; i < 4; i++) { testdist = orientation-d[i]; if (testdist > 0.5 * M_PI) testdist = testdist - M_PI; testdist = std::fabs(testdist); if (testdist < dist) { dist = testdist; best_d = i; } } Eo(r,c) = best_d; switch (best_d) { case 0: // 0 degrees if ( (strength > Es(r,c-1)) && (strength > Es(r,c+1)) ) { In(r,c) = strength; } break; case 1: // 45 degrees if ( (strength > Es(r-1,c+1)) && (strength > Es(r+1,c-1)) ) { In(r,c) = strength; } break; case 2: // 90 degrees if ( (strength > Es(r-1,c)) && (strength > Es(r+1,c)) ) { In(r,c) = strength; } break; case 3: // 135 degrees if ( (strength > Es(r-1,c-1)) && (strength > Es(r+1,c+1)) ) { In(r,c) = strength; } break; } if (hysteresis && In(r,c) > high) { S.push( std::pair(r,c) ); } } } if (hysteresis == false) { retval.append(In); return retval; } /************************** ** Hysteresis threshold ** **************************/ boolMatrix out = boolMatrix( rows, cols, false ); while (S.empty() == false) { std::pair p = S.top(); S.pop(); const int r = p.first; const int c = p.second; if (r < 0 || r >= rows || c < 0 || c >= cols || out(r,c) == true) { continue; } out(r,c) = true; const int dir = (int)Eo(r,c); switch (dir) { case 0: // 0 degrees if ( In(r-1,c) > low ) { S.push(std::pair(r-1,c)); } if ( In(r+1,c) > low ) { S.push(std::pair(r+1,c)); } break; case 1: // 45 degrees if ( In(r-1,c-1) > low ) { S.push(std::pair(r-1,c-1)); } if ( In(r+1,c+1) > low ) { S.push(std::pair(r+1,c+1)); } break; case 2: // 90 degrees if ( In(r,c-1) > low ) { S.push(std::pair(r,c-1)); } if ( In(r,c+1) > low ) { S.push(std::pair(r,c+1)); } break; case 3: // 135 degrees if ( In(r-1,c+1) > low ) { S.push(std::pair(r-1,c+1)); } if ( In(r+1,c-1) > low ) { S.push(std::pair(r+1,c-1)); } break; } } retval.append(out); return retval; } image-2.16.1/src/PaxHeaders.61586/strel.cc0000644000000000000000000000006215005110255014654 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/strel.cc0000644000175000017500000002341715005110255016261 0ustar00avinoamavinoam00000000000000// Copyright (C) 2013 Carnë Draug // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 3 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, see . // This is wrapper class for the @strel class so that it can be used by // the rest of the image package using SE's. It's not a perfect wrapper // on purpose. For example, the reflect method behaves kinda weird for // matlab compatibility. In here we try to make a bit more sense. #include "strel.h" #include #include #include #include // gives us feval so we can use @strel #include // Constructors // Expects a @strel object, or a boolean matrix (or something // that can be converted into one with bool_matrix_value() octave_image_pkg::strel::strel (const octave_value& arg) { octave_value se = arg; // We are only creating a strel object here so that we can use // getsequence which tries to guess a shape to decompose. In // the cases where we don't get a @strel object we could also: // // 1) don't do any automatic decomposition, use the matrix as // it is. // 2) implement the guessing in C++ and make a oct file for it. // If we don't have a strel object, then make one. if (se.class_name () != "strel") { octave_value_list strel_args (2); strel_args(0) = "arbitrary"; strel_args(1) = se; // We are leaving the input check up to @strel se = octave::feval ("strel", strel_args)(0); } nhood = octave::feval ("getnhood", se)(0).bool_array_value (); height = octave::feval ("getheight", se)(0).array_value (); // Maybe we should handle this better but how? This makes imerode // and imdilate work with a [] strel if (octave_value (nhood).isempty ()) { nhood = boolNDArray (dim_vector (1, 1), false); height = NDArray (dim_vector (1, 1), 0); } ini_ctor (); origin = default_origin (); const octave_value se_seq = octave::feval ("getsequence", se)(0); const octave_idx_type seq_numel = octave::feval ("numel", se_seq)(0).idx_type_value (); // This is to emulate the strel_obj(idx) syntax in function form static const char *fields[] = {"type", "subs", 0}; octave_scalar_map ref = octave_scalar_map (string_vector (fields)); ref.setfield ("type", octave_value ("()")); octave_value_list subsref_args (2); subsref_args(0) = se_seq; if (seq_numel > 1) { for (octave_idx_type subs = 0; subs < seq_numel; subs++) { // subs+1 because octave is index base 1 ref.setfield ("subs", Cell (octave_value (subs+1))); subsref_args(1) = ref; // Equivalent to "selem = strel_obj(subs)" const octave_value_list elem = octave::feval ("subsref", subsref_args)(0); const boolNDArray elem_nhood = octave::feval ("getnhood", elem)(0).bool_array_value (); const NDArray elem_height = octave::feval ("getheight", elem)(0).array_value (); decomposition.push_back (strel (elem_nhood, elem_height)); } } end_ctor (); return; } octave_image_pkg::strel::strel (const boolNDArray& nhood, const NDArray& height) : nhood (nhood), height (height) { ini_ctor (); origin = default_origin (); end_ctor (); return; } octave_image_pkg::strel::strel (const boolNDArray& nhood, const NDArray& height, const Array& origin) : nhood (nhood), height (height), origin (origin) { ini_ctor (); validate_origin (); end_ctor (); return; } boolNDArray octave_image_pkg::strel::get_nhood (void) const { return nhood; } octave_idx_type octave_image_pkg::strel::get_nnz (void) const { return nnz; } Array octave_image_pkg::strel::get_origin (void) const { return origin; } octave_image_pkg::strel octave_image_pkg::strel::operator () (const octave_idx_type& i) const { assert (i >= 0 && i < octave_idx_type (decomposition.size ())); return decomposition[i]; } // Number of strel elements after decomposition octave_idx_type octave_image_pkg::strel::numel (void) const { return octave_idx_type (decomposition.size ()); } // Pretty much rotates the matrix by 180 degrees in all dimensions. The // only tricky thing that we doo it purpose is that the origin is also // rotated. For example, if the origin is set to the bottom right point, // after refleection it will be in the top left. If the origin is at the // center of the matrix, there will be no change. // The reason for this is so that we can keep the origin in matrices with // sides with an even length. octave_image_pkg::strel octave_image_pkg::strel::reflect (void) const { boolNDArray ref_nhood (size); NDArray ref_height (size); const octave_idx_type numel = nhood.numel (); for (octave_idx_type ind = 0; ind < numel; ind++) { ref_nhood(ind) = nhood(numel - ind -1); ref_height(ind) = height(numel - ind -1); } Array ref_origin (origin); for (octave_idx_type dim = 0; dim < ndims; dim++) ref_origin(dim) = size(dim) - origin(dim) -1; return octave_image_pkg::strel (ref_nhood, ref_height, ref_origin); } void octave_image_pkg::strel::set_origin (const Array& sub) { origin = sub; validate_origin (); return; } bool octave_image_pkg::strel::flat (void) const { bool flat = true; if (! height.all_elements_are_zero ()) { const octave_idx_type numel = height.numel (); for (octave_idx_type ind = 0; ind < numel; ind++) { if (height(ind)) { flat = false; break; } } } return flat; } // For any given point in the input matrix, calculates the memory offset for // all the others that will have an effect on the erosion and dilation with it. // How much we need to shift the input matrix to cover the nnz of the SE. // That is, how many elements away is each nnz of the SE, for any element // of the input matrix. Given a 10x10 input matrix (cumulative dimensions // of [10 100]), and a SE with: // [1 0 0 // 1 1 1 // 0 0 1] // linear shift is [0 1 11 21 22] // The second element is the matching height for each. Array octave_image_pkg::strel::offsets (const dim_vector& cum_size) const { Array sub (dim_vector (ndims, 1), 0); Array offsets (dim_vector (nnz, 1)); for (octave_idx_type found = 0; found < nnz; boolNDArray::increment_index (sub, size)) { if (nhood(sub)) { offsets(found) = sub(0); for (octave_idx_type dim = 1; dim < ndims; dim++) offsets(found) += cum_size(dim-1) * sub(dim); found++; } } return offsets; } // The final size of the output matrix will be the size of the input // matrix, plus the size of the SE, less its center. Consider a square SE // at the corner of the input matrix. The origin (center) of the SE will be // at coordinates (0,0) of the input matrix and we need enough padding for // it. If the shape is "full", then we add the double. Array octave_image_pkg::strel::pre_pad (const octave_idx_type& mt_ndims, const std::string& shape) const { Array pad (dim_vector (mt_ndims, 1), 0); octave_idx_type pad_times; if (shape == "valid") return pad; else if (shape == "same") pad_times = 1; else if (shape == "full") pad_times = 2; else error ("invalid SHAPE"); Array resized_origin (origin); dim_vector resized_size (size); if (ndims < mt_ndims) { resized_origin.resize (dim_vector (mt_ndims, 1), 0); resized_size.resize (mt_ndims, 1); } for (octave_idx_type dim = 0; dim < mt_ndims; dim++) pad(dim) = resized_origin(dim) * pad_times; return pad; } Array octave_image_pkg::strel::post_pad (const octave_idx_type& mt_ndims, const std::string& shape) const { Array pad (dim_vector (mt_ndims, 1), 0); octave_idx_type pad_times; if (shape == "valid") return pad; else if (shape == "same") pad_times = 1; else if (shape == "full") pad_times = 2; else error ("invalid SHAPE"); Array resized_origin (origin); dim_vector resized_size (size); if (ndims < mt_ndims) { resized_origin.resize (dim_vector (mt_ndims, 1), 0); resized_size.resize (mt_ndims, 1); } for (octave_idx_type dim = 0; dim < mt_ndims; dim++) pad(dim) = (resized_size(dim) - resized_origin(dim) -1) * pad_times; return pad; } void octave_image_pkg::strel::ini_ctor () { size = nhood.dims (); ndims = nhood.ndims (); nnz = nhood.nnz (); return; } Array octave_image_pkg::strel::default_origin () { Array origin (dim_vector (ndims, 1)); for (octave_idx_type dim = 0; dim < ndims; dim++) origin(dim) = floor ((size(dim) +1) /2) -1; // -1 for zero based indexing return origin; } void octave_image_pkg::strel::end_ctor (void) { if (decomposition.empty ()) decomposition.push_back (*this); } void octave_image_pkg::strel::validate_origin (void) { assert (ndims == origin.numel ()); for (octave_idx_type dim = 0; dim < ndims; dim++) assert (origin(dim) >= 0 && origin(dim) < size(dim)); return; } image-2.16.1/src/PaxHeaders.61586/bwconncomp.cc0000644000000000000000000000006215005110255015670 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/src/bwconncomp.cc0000644000175000017500000003505115005110255017272 0ustar00avinoamavinoam00000000000000// Copyright (C) 2014 Carnë Draug // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, see // . // Implements connected components but using flood-fill algorithm instead // of union find (like bwlabeln) so it uses a lot less memory. // TODO: functions that could be here // * bwareafilt // * imfill / bwfill // * bwselect // * labelmatrix #include #include #include #include #include "connectivity.h" using namespace octave_image_pkg; static std::vector> connected_components (const boolNDArray& BW, const connectivity& conn) { boolNDArray BW_pad = conn.create_padded (BW, false); bool* BW_vec = BW_pad.fortran_vec (); const Array offsets = conn.deleted_neighbourhood (BW_pad.dims ()); const octave_idx_type n_offsets = offsets.numel (); const octave_idx_type* off_v = offsets.data (); std::vector> all_components; const octave_idx_type numel = BW_pad.numel (); for (octave_idx_type i = 0; i < numel; BW_vec++, i++) { if (! *BW_vec) continue; // We want a queue but we will mimic one with a vector because in the // end all elements that go in the queue go in the vector anyway. std::vector conn_comp {0}; *BW_vec = false; std::vector::size_type front = 0; while (front < conn_comp.size ()) { octave_idx_type base_offset = conn_comp[front++]; for (octave_idx_type j = 0; j < n_offsets; j++) { const octave_idx_type this_offset = base_offset + off_v[j]; if (BW_vec[this_offset]) { BW_vec[this_offset] = false; conn_comp.push_back (this_offset); } } } for (octave_idx_type& offset : conn_comp) offset += i; all_components.push_back (conn_comp); } // The collected indices are for the padded image so they need fixing const dim_vector original_size = BW.dims (); const dim_vector padded_size = BW_pad.dims (); const octave_idx_type ndims_m1 = BW_pad.ndims () -1; std::vector dim_padded (BW_pad.ndims (), true); for (octave_idx_type i = 0; i < BW_pad.ndims (); i++) if (padded_size.xelem (i) == original_size.xelem (i)) dim_padded[i] = false; for (std::vector& conn_comp : all_components) { for (octave_idx_type& offset : conn_comp) { octave_idx_type mult = 1; octave_idx_type ind = 0; for (octave_idx_type d = 0; d < ndims_m1; d++) { if (dim_padded[d]) { ind += mult * (offset % padded_size.xelem (d) - 1); mult *= padded_size.xelem (d) - 2; offset /= padded_size.xelem (d); } else { ind += mult * (offset % padded_size.xelem (d)); mult *= padded_size.xelem (d); offset /= padded_size.xelem (d); } } if (dim_padded[ndims_m1]) ind += mult * (offset % padded_size.xelem (ndims_m1) - 1); else ind += mult * (offset % padded_size.xelem (ndims_m1)); offset = ind; } } return all_components; } static Array dim_vector_2_array (const dim_vector& dims) { RowVector size (dim_vector (1, dims.length ())); for (octave_idx_type i = 0; i < dims.length (); i++) size(i) = dims(i); return size; } // We should just return the connectivity used as input, args(1), but for // Matlab compatibility, we must return 4, 8, etc if it matches static octave_value conn_to_octave_value (const connectivity& conn) { const octave_idx_type n = conn.mask.numel (); const octave_idx_type nnz = conn.mask.nnz (); const bool* b_v = conn.mask.data (); octave_idx_type nr; if ((n == 9 || n == 27) && n == nnz) nr = nnz -1; else if (nnz == 5 && b_v[1] && b_v[3] && b_v[4] && b_v[5] && b_v[7]) nr = 4; else if (nnz == 7 && b_v[4] && b_v[10] && b_v[12] && b_v[13] && b_v[14] && b_v[16] && b_v[22]) nr = 6; else if (nnz == 19 && ! b_v[0] && ! b_v[2] && ! b_v[6] && ! b_v[8] && ! b_v[18] && ! b_v[20] && ! b_v[24] && ! b_v[26]) nr = 18; else return octave_value (conn.mask); return octave_value (nr); } DEFUN_DLD(bwconncomp, args, , "\ -*- texinfo -*-\n\ @deftypefn {Function File} {@var{cc} =} bwconncomp (@var{bw})\n\ @deftypefnx {Function File} {@var{cc} =} bwconncomp (@var{bw}, @var{conn})\n\ Find connected objects.\n\ \n\ Elements from the matrix @var{bw}, belong to an object if they have a\n\ non-zero value. The output @var{cc} is a structure with information about\n\ each object;\n\ \n\ @table @asis\n\ @item @qcode{\"Connectivity\"}\n\ The connectivity used in the boundary tracing. This may be different from\n\ the input argument, e.g., if @var{conn} is defined as a matrix of 1s and\n\ size 3x3, the @qcode{\"Connectivity\"} value will still be 8.\n\ \n\ @item @qcode{\"ImageSize\"}\n\ The size of the matrix @var{bw}.\n\ \n\ @item @qcode{\"NumObjects\"}\n\ The number of objects in the image @var{bw}.\n\ \n\ @item @qcode{\"PixelIdxList\"}\n\ A cell array with linear indices for each element of each object in @var{bw}\n\ A cell array containing where each element corresponds to an object in @var{BW}.\n\ Each element is represented as a vector of linear indices of the boundary of\n\ the given object.\n\ \n\ @end table\n\ \n\ Element connectivity @var{conn}, to define the size of objects, can be\n\ specified with a numeric scalar (number of elements in the neighborhood):\n\ \n\ @table @samp\n\ @item 4 or 8\n\ for 2 dimensional matrices;\n\ @item 6, 18 or 26\n\ for 3 dimensional matrices;\n\ @end table\n\ \n\ or with a binary matrix representing a connectivity array. Defaults to\n\ @code{conndef (ndims (@var{bw}), \"maximal\")} which is equivalent to\n\ @var{conn} of 8 and 26 for 2 and 3 dimensional matrices respectively.\n\ \n\ @seealso{bwlabel, bwlabeln, bwboundaries, ind2sub, regionprops}\n\ @end deftypefn") { const octave_idx_type nargin = args.length (); if (nargin < 1 || nargin > 2) print_usage (); const boolNDArray BW = args(0).bool_array_value (); connectivity conn; if (nargin > 1) conn = conndef (args(1)); else { try { conn = connectivity (BW.ndims (), "maximal"); } catch (invalid_connectivity& e) { error ("bwconncomp: failed to create MASK (%s)", e.what ()); } } const std::vector> all_cc = connected_components (BW, conn); static const char *fields[] = { "Connectivity", "ImageSize", "NumObjects", "PixelIdxList", 0 }; octave_scalar_map cc = octave_scalar_map (string_vector (fields)); cc.assign ("Connectivity", conn_to_octave_value (conn)); cc.assign ("NumObjects", octave_value (all_cc.size ())); cc.assign ("ImageSize", octave_value (dim_vector_2_array (BW.dims ()))); Cell idx_cell (dim_vector (1, all_cc.size ()), ColumnVector ()); octave_idx_type i_out = 0; for (auto it = all_cc.begin (); it != all_cc.end (); it++, i_out++) { ColumnVector idx (dim_vector (it->size (), 1)); double* idx_v = idx.fortran_vec (); octave_idx_type i_in = 0; for (auto it_it = it->begin (); it_it != it->end (); it_it++, i_in++) idx_v[i_in] = *it_it +1; // +1 (we fix it for Octave indexing) idx_cell(i_out) = idx; } cc.setfield ("PixelIdxList", idx_cell); return octave_value (cc); } /* %!test %! a = rand (10) > 0.5; %! cc = bwconncomp (a, 4); %! assert (cc.Connectivity, 4) %! assert (cc.ImageSize, [10 10]) %! %! b = false (10); %! for i = 1:numel (cc.PixelIdxList) %! b(cc.PixelIdxList{i}) = true; %! endfor %! assert (a, b) %!test %! a = rand (10, 13) > 0.5; %! cc = bwconncomp (a, 4); %! assert (cc.ImageSize, [10 13]) %! %! b = false (10, 13); %! for i = 1:numel (cc.PixelIdxList) %! b(cc.PixelIdxList{i}) = true; %! endfor %! assert (a, b) %!test %! a = rand (15) > 0.5; %! conn_8 = bwconncomp (a, 8); %! assert (conn_8, bwconncomp (a)) %! assert (conn_8, bwconncomp (a, ones (3))) %! assert (conn_8.Connectivity, 8) %! assert (bwconncomp (a, ones (3)).Connectivity, 8) %! assert (bwconncomp (a, [0 1 0; 1 1 1; 0 1 0]).Connectivity, 4) %!test %! bw = logical ([ %! 1 0 0 1 0 1 0 %! 1 0 0 1 0 1 0 %! 0 0 0 0 0 1 0 %! 0 0 0 0 1 0 0 %! 1 1 0 1 1 0 0 %! 0 1 0 0 0 0 0 %! 1 1 0 0 0 0 0 %! ]); %! cc = bwconncomp (bw); %! cc = struct (); %! cc.Connectivity = 8; %! cc.ImageSize = [7 7]; %! cc.NumObjects = 4; %! ## The commented line has the results from Matlab. We return the %! ## same result but in a slightly different order. Since the order %! ## is not defined, it is not required for compatibility. %! #cc.PixelIdxList = {[1;2], [5;7;12;13;14], [22;23], [26;32;33;36;37;38]}; %! cc.PixelIdxList = {[1;2], [5;12;13;7;14], [22;23], [26;32;33;38;37;36]}; %! assert (bwconncomp (bw), cc) %!test %! ## test that PixelIdxList is a row vector %! a = rand (40, 40) > 0.2; %! cc = bwconncomp (a, 4); %! assert (rows (cc.PixelIdxList), 1) %! assert (columns (cc.PixelIdxList), cc.NumObjects) ## PixelIdxList is a row vector, even when there's zero objects %!assert (bwconncomp (false (5)), struct ("ImageSize", [5 5], "NumObjects", 0, %! "PixelIdxList", {cell(1, 0)}, %! "Connectivity", 8)) */ // PKG_ADD: autoload ("bwareaopen", which ("bwconncomp")); // PKG_DEL: autoload ("bwareaopen", which ("bwconncomp"), "remove"); DEFUN_DLD(bwareaopen, args, , "\ -*- texinfo -*-\n\ @deftypefn {Function File} {} bwareaopen (@var{bw}, @var{lim})\n\ @deftypefnx {Function File} {} bwareaopen (@var{bw}, @var{lim}, @var{conn})\n\ Perform area opening.\n\ \n\ Remove objects with less than @var{lim} elements from a binary image\n\ @var{bw}.\n\ \n\ Element connectivity @var{conn}, to define the size of objects, can be\n\ specified with a numeric scalar (number of elements in the neighborhood):\n\ \n\ @table @samp\n\ @item 4 or 8\n\ for 2 dimensional matrices;\n\ @item 6, 18 or 26\n\ for 3 dimensional matrices;\n\ @end table\n\ \n\ or with a binary matrix representing a connectivity array. Defaults to\n\ @code{conndef (ndims (@var{bw}), \"maximal\")} which is equivalent to\n\ @var{conn} of 8 and 26 for 2 and 3 dimensional matrices respectively.\n\ \n\ @seealso{bwconncomp, conndef, bwboundaries}\n\ @end deftypefn") { const octave_idx_type nargin = args.length (); if (nargin < 2 || nargin > 3) print_usage (); boolNDArray BW = args(0).bool_array_value (); const std::vector::size_type lim = args(1).idx_type_value (); if (lim < 0) error ("bwareaopen: LIM must be a non-negative scalar integer"); connectivity conn; if (nargin > 2) conn = conndef (args(2)); else { try { conn = connectivity (BW.ndims (), "maximal"); } catch (invalid_connectivity& e) { error ("bwareaopen: failed to create MASK (%s)", e.what ()); } } const std::vector> all_cc = connected_components (BW, conn); bool* BW_v = BW.fortran_vec (); for (std::vector cc : all_cc) { if (cc.size () < lim) for (octave_idx_type ind : cc) BW_v[ind] = false; } return octave_value (BW); } /* %!test %! in = [ 0 0 1 0 0 1 0 1 0 0 %! 0 0 1 0 0 0 0 0 1 1 %! 1 0 0 0 0 1 1 0 0 0 %! 1 0 0 0 1 0 0 0 0 0 %! 1 1 1 1 0 0 0 0 0 1 %! 0 1 0 1 1 0 0 1 0 0 %! 1 0 0 0 1 0 0 0 0 0 %! 0 0 0 1 1 0 0 1 0 0 %! 0 1 0 1 1 0 0 1 1 0 %! 0 1 0 1 1 1 0 0 1 0]; %! assert (bwareaopen (in, 1, 4), logical (in)) %! %! out = [0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 1 1 1 0 0 0 0 0 0 %! 0 1 0 1 1 0 0 0 0 0 %! 0 0 0 0 1 0 0 0 0 0 %! 0 0 0 1 1 0 0 0 0 0 %! 0 0 0 1 1 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0]; %! assert (bwareaopen (logical (in), 10, 4), logical (out)) %! assert (bwareaopen (in, 10, 4), logical (out)) %! assert (bwareaopen (in, 10, [0 1 0; 1 1 1; 0 1 0]), logical (out)) %! %! out = [0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 1 1 0 0 0 %! 1 0 0 0 1 0 0 0 0 0 %! 1 1 1 1 0 0 0 0 0 0 %! 0 1 0 1 1 0 0 0 0 0 %! 1 0 0 0 1 0 0 0 0 0 %! 0 0 0 1 1 0 0 0 0 0 %! 0 0 0 1 1 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0]; %! assert (bwareaopen (in, 10, 8), logical (out)) %! assert (bwareaopen (in, 10, ones (3)), logical (out)) %! assert (bwareaopen (in, 10), logical (out)) %! %! out = [0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 1 1 1 0 0 0 0 0 0 %! 0 1 0 1 1 0 0 0 0 0 %! 0 0 0 0 1 0 0 0 0 0 %! 0 0 0 1 1 0 0 1 0 0 %! 0 0 0 1 1 0 0 1 1 0 %! 0 0 0 1 1 1 0 0 1 0]; %! assert (bwareaopen (in, 4, [1 1 0; 1 1 1; 0 1 1]), logical (out)) %!error bwareaopen ("not an image", 78, 8) %!error bwareaopen (rand (10) > 0.5, 10, 100) %!error bwareaopen (rand (10) > 0.5, 10, "maximal") %!error bwareaopen (rand (10) > 0.5, 10, [1 1 1; 0 1 1; 0 1 0]) */ image-2.16.1/PaxHeaders.61586/inst0000644000000000000000000000013215005111723013324 xustar0030 mtime=1746179027.110725273 30 atime=1746179027.274726131 30 ctime=1746179027.274726131 image-2.16.1/inst/0000755000175000017500000000000015005111723015001 5ustar00avinoamavinoam00000000000000image-2.16.1/inst/PaxHeaders.61586/stdfilt.m0000644000000000000000000000006215005110255015231 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/inst/stdfilt.m0000644000175000017500000001134015005110255016626 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{S} =} stdfilt (@var{im}) ## @deftypefnx{Function File} {@var{S} =} stdfilt (@var{im}, @var{domain}) ## @deftypefnx{Function File} {@var{S} =} stdfilt (@var{im}, @var{domain}, @var{padding}, @dots{}) ## Computes the local standard deviation in a neighbourhood around each pixel in ## an image. ## ## The standard deviation of the pixels of a neighbourhood is computed as ## ## @example ## @var{S} = sqrt ((sum (@var{x} - @var{mu}).^2)/(@var{N}-1)) ## @end example ## ## where @var{mu} is the mean value of the pixels in the neighbourhood, ## @var{N} is the number of pixels in the neighbourhood. So, an unbiased estimator ## is used. ## ## The neighbourhood is defined by the @var{domain} binary mask. Elements of the ## mask with a non-zero value are considered part of the neighbourhood. By default ## a 3 by 3 matrix containing only non-zero values is used. ## ## At the border of the image, extrapolation is used. By default symmetric ## extrapolation is used, but any method supported by the @code{padarray} function ## can be used. Since extrapolation is used, one can expect a lower deviation near ## the image border. ## ## @seealso{std2, paddarray, entropyfilt} ## @end deftypefn function retval = stdfilt (I, domain = true (3), padding = "symmetric", varargin) ## Check input if (nargin == 0) error ("stdfilt: not enough input arguments"); endif if (! isimage (I)) error ("stdfilt: first input must be a matrix"); endif if (! isnumeric (domain) && ! islogical (domain)) error ("stdfilt: second input argument must be a logical matrix"); endif domain = logical (domain); ## Pad image pad = floor (size (domain)/2); I = padarray (I, pad, padding, varargin {:}); even = (round (size (domain)/2) == size (domain)/2); idx = cell (1, ndims (I)); for k = 1:ndims (I) idx {k} = (even (k)+1):size (I, k); endfor I = I (idx {:}); retval = __spatial_filtering__ (I, domain, "std", zeros (size (domain)), 0); endfunction %!test %! im = stdfilt (ones (5)); %! assert (im, zeros (5)) ## some (Matlab compatible) tests on simple 2D-images: %!test %! A = zeros (3,3); %! B = ones (3,3); %! C = [1 1 1; 2 2 2; 3 3 3]; %! D = C'; %! E = ones (3,3); %! E(2,2) = 2; %! F = 3 .* ones (3,3); %! F(2,2) = 1; %! G = [-1 2 7; -5 2 8; -7 pi 9]; %! H = [5 2 8; 1 -3 1; 5 1 0]; %! A_out = [0 0 0; 0 0 0; 0 0 0]; %! B_out = [0 0 0; 0 0 0; 0 0 0]; %! C_out = repmat ([std([1 1 1 1 1 1 2 2 2]) %! std([1 1 1 2 2 2 3 3 3]) %! std([2 2 2 3 3 3 3 3 3])], [1 3]); %! D_out = C_out'; %! E_out = (1/3) .* ones (3,3); %! F_out = (2/3) .* ones (3,3); %! G_out = [std([-1 -1 2 -1 -1 2 -5 -5 2]), std([-1 2 7 -1 2 7 -5 2 8]), std([2 7 7 2 7 7 2 8 8]); %! std([-1 -1 2 -5 -5 2 -7 -7 pi]), std([-1 2 7 -5 2 8 -7 pi 9]), std([2 7 7 2 8 8 pi 9 9]); %! std([-5 -5 2 -7 -7 pi -7 -7 pi]), std([-5 2 8 -7 pi 9 -7 pi 9]), std([2 8 8 pi 9 9 pi 9 9])]; %! H_out = [std([5 5 2 5 5 2 1 1 -3]), std([5 2 8 5 2 8 1 -3 1]), std([2 8 8 2 8 8 -3 1 1]); %! std([5 5 2 1 1 -3 5 5 1]), std([5 2 8 1 -3 1 5 1 0]), std([2 8 8 -3 1 1 1 0 0]); %! std([1 1 -3 5 5 1 5 5 1]), std([1 -3 1 5 1 0 5 1 0]), std([-3 1 1 1 0 0 1 0 0])]; %! assert (stdfilt (A), A_out) %! assert (stdfilt (B), B_out) %! assert (stdfilt (C), C_out, 4*eps) %! assert (stdfilt (D), D_out, 4*eps) %! assert (stdfilt (E), E_out, 4*eps) %! assert (stdfilt (F), F_out, 4*eps) %! assert (stdfilt (G), G_out, 4*eps) %! assert (stdfilt (H), H_out, 4*eps) ## testing all input types %! im = stdfilt (ones (5, 'logical')); %! assert (im, zeros (5)) %! im = stdfilt (ones (5, 'uint8')); %! assert (im, zeros (5)) %! assert (stdfilt (int8(H), H_out, 4*eps)) %! assert (stdfilt (uint8(H), H_out, 4*eps)) %! assert (stdfilt (int16(H), H_out, 4*eps)) %! assert (stdfilt (uint16(H), H_out, 4*eps)) %! assert (stdfilt (int32(H), H_out, 4*eps)) %! assert (stdfilt (uint32(H), H_out, 4*eps)) %! assert (stdfilt (int64(H), H_out, 4*eps)) %! assert (stdfilt (uint64(H), H_out, 4*eps)) %! assert (stdfilt (single(H), H_out, 4*eps)) image-2.16.1/inst/PaxHeaders.61586/imimposemin.m0000644000000000000000000000006215005110255016106 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/inst/imimposemin.m0000644000175000017500000001760715005110255017517 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} @ imimposemin (@var{im}, @var{bw}) ## @deftypefnx {Function File} {} @ imimposemin (@var{im}, @var{bw}, @var{conn}) ## Modify the input impage @var{im} to only have regional minima at ## the marker positions given by the nonzero pixels of @var{bw}. ## ## This function returns a grayscale image that is similar to @var{im} but ## only has regional minima at pixel positions where the marker image @var{bw} is nonzero. ## ## The input image @var{im} needs to be a real and nonsparse numeric array ## (of any dimension). And the marker image @var{bw} needs to be a real or ## logical nonsparse array of identical size. ## (The values in @var{bw} will first be converted to logical values.) ## ## The definition of "neighborhood" for this morphological operation can be set ## with the connectivity parameter @var{conn}, ## which defaults to 8 for 2D images, to 26 for 3D images and to ## @code{conn(ndims(n), "maximal")} in general. @var{conn} can be given as scalar value ## or as a boolean matrix (see @code{conndef} for details). ## ## @seealso{imextendedmin, imhmin, imregionalmin, imreconstruct} ## @end deftypefn ## Algorithm: ## * The 'classical' reference for this morphological "minima imposition" function ## is the book "Morphological Image Analysis" by P. Soille ## (Springer, 2nd edition, 2004), chapter 6.4.6 "Minima imposition". ## It says (own translation from the german book, two typos corrected): ## "The marker image f_m is defined for every pixel as follows: ## f_m(x) = 0 if x is part of a marker ## = t_max else. ## The minima imposition of the input image f is performed ## in two steps. First, the pointwise minimum between the input ## image and the marker image is calculated: f /\ f_m. [...] ## it is necessary to rather consider (f+1) /\ f_m instead of f /\ f_m. ## The second step consists of a morphological reconstruction ## by erosion of (f+1) /\ f_m from the marker image fm." ## (We will call the grayscale image im instead of f.) ## * A more easily accessible reference is for example the following ## web page by Régis Clouard: ## https://clouard.users.greyc.fr/Pantheon/experiments/morphology/index-en.html#ch4-D ## It says: "IV.D.2. Maxima/Minima Imposition ## [..., it follows an example script with Pandore functions, the input image is in.pan, ## the generated marker image is i1.pan and the output is swamping.pan] ## padcst 1 in.pan i2.pan [i2 = in + 1] ## pmin i1.pan i2.pan i3.pan [i3 = min(i1, i2)] ## perosionreconstruction 8 i1.pan i3.pan swamping.pan [out = erosionreconstruction(i1, i3, 8)] function im2 = imimposemin (im, bw, varargin) ## retrieve input parameters, set default value: if (nargin == 3) conn = varargin{1}; iptcheckconn (conn, "imimposemin", "CONN"); elseif (nargin == 2) ## Buggy Matlab doc claims "minimum" connectivity instead, ## but defaults of 8 and 26, which are "maximal" connectivities. conn = conndef (ndims (im), "maximal"); else print_usage (); endif ## check input parameters: if (! isnumeric (im) || ! isreal (im) || issparse (im) ) error ("imimposemin: IM must be a real and nonsparse numeric array"); endif if (((! isnumeric (bw) || ! isreal (bw)) && (! islogical (bw))) || issparse (bw) ) error ("imimposemin: BW must be a logical or numeric nonsparse array"); endif if (! (ndims (im) == ndims (bw)) || ! all (size (im) == size (bw))) error ("imimposemin: BW must have the same size as IM"); endif ## do the actual calculation ## convert bw to class logical if necessary: if (! islogical (bw)) bw = logical (bw); endif ## define the marker image fm (see algorithm above): fm = zeros (size (im), class (im)); fm(bw) = -Inf; # min value of class(im) fm(!bw) = Inf; # max value of class(im) ## define the difference delta: ## for integer images this value is 1 (see algorithm above) ## for float images this is done in analogy ## (A possible value for float images would be delta = eps (im), ## see bug #51724, but Matlab seems to do the following instead.) if isfloat (im) delta = ( max (im(:)) - min (im(:)) ) / 1000; else # integer images delta = 1; endif ## calculate pointwise minimum (see algorithm above): min_im = min (im + delta, fm); ## do the morphological reconstruction by erosion (see algorithm above): ## (Calculate dilations of the inverse images, instead of erosions of the ## original images, because this is what imreconstruct can do.) im2 = imreconstruct (imcomplement (fm), imcomplement (min_im), conn); im2 = imcomplement (im2); endfunction %!shared im0, bw0, out0, out0_4 %! im0 = uint8 ([5 5 5 5 5; %! 5 4 3 4 5; %! 5 3 0 3 5; %! 5 4 3 4 5; %! 5 5 5 5 5]); %! bw0 = false (5); %! bw0(4, 4) = true; %! out0 = im0 + 1; %! out0(4, 4) = 0; %! out0_4 = out0; %! out0_4(3, 3) = 4; ## test input syntax: %!error imimposemin () %!error imimposemin (im0) %!error imimposemin ("hello", bw0) %!error imimposemin (i.*im0, bw0) %!error imimposemin (sparse (im0), bw0) %!error imimposemin (im0, ones (2)) %!error imimposemin (im0, 'hello') %!error imimposemin (im0, i .* double (bw0)) %!error imimposemin (im0, sparse (bw0)) %!error imimposemin (im0, bw0, 'hello') %!error imimposemin (im0, bw0, 3) %!assert (imimposemin (im0, bw0), out0) %!assert (imimposemin (im0, bw0, 8), out0) %!assert (imimposemin (im0, bw0, 4), out0_4) %!assert (imimposemin (im0, bw0, true (3)), out0) ## test output class and shape: %!test %! out = imimposemin (im0, bw0); %! assert (size (out), size (im0)) %! assert (class (out), "uint8") %!test %! out = imimposemin (double (im0), bw0); %! assert (size (out), size (im0)) %! assert (class (out), "double") %!test %! out = imimposemin (single (im0), bw0); %! assert (size (out), size (im0)) %! assert (class (out), "single") %!test %! out = imimposemin (uint16 (im0), bw0); %! assert (size (out), size (im0)) %! assert (class (out), "uint16") %!test %! im = cat (3, im0, im0, im0, im0); %! bw = cat (3, bw0, bw0, bw0, bw0); %! out = imimposemin (im, bw); %! assert (size (out), size (im)) ## test calculation result: %!test %! expected_double = double (im0); %! expected_double += 0.005; %! expected_double(4, 4) = -inf; %! out = imimposemin (double (im0), bw0); %! assert (out, expected_double, eps) %!test %! im = uint8 (10 .* ones (10)); %! im(6:8, 6:8) = 2; %! im(2:4, 2:4) = 7; %! im(3, 3) = 5; %! im(2, 9) = 9; %! im(3, 8) = 9; %! im(9, 2) = 9; %! im(8, 3) = 9; %! bw = false (10); %! bw(3, 3) = true; %! bw(6:8, 6:8) = true; %! expected = uint8 (11 .* ones(10)); %! expected(2:4, 2:4) = 8; %! expected(3, 3) = 0; %! expected(6:8, 6:8) = 0; %! expected_double = double (expected); %! expected_double -= 0.992; %! expected_double (expected_double < 0) = -inf; %! out = imimposemin (im, bw); %! assert (out, expected, eps) %! out = imimposemin (double (im), bw); %! assert (out, expected_double, eps) image-2.16.1/inst/PaxHeaders.61586/corr2.m0000644000000000000000000000006215005110255014607 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/inst/corr2.m0000644000175000017500000000235515005110255016212 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{r} = corr2 (@var{I},@var{J}) ## Compute correlation coefficients of images. ## ## The two images @var{I} and @var{J} must be real type matrices or vectors of ## same size. ## @seealso{corr, cov, std2} ## @end deftypefn function r = corr2 (I, J) if (nargin != 2) print_usage (); elseif (! isimage (I) || ! isimage (J)) error ("corr2: I and J must be real matrices"); elseif (! size_equal (I, J)) error ("corr2: I and J must be of same size"); endif r = corr (I(:), J(:)); endfunction image-2.16.1/inst/PaxHeaders.61586/maketform.m0000644000000000000000000000006215005110255015545 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/inst/maketform.m0000644000175000017500000001571415005110255017153 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Pantxo Diribarne ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{T} =} maketform (@var{ttype}, @var{tmat}) ## @deftypefnx {Function File} {@var{T} =} maketform (@var{ttype}, @var{inc}, @var{outc}) ## @deftypefnx {Function File} {@var{T} =} maketform ("custom", @var{ndims_in}, @var{ndims_out}, @var{forward_fcn}, @var{inverse_fcn}, @var{tdata}) ## Create a transform structure @var{T} to be used for spatial ## transformations between an input space and an output space. ## ## The fields of the transform structure are: ## ## @table @asis ## @item @qcode{"ndims_in"}, @qcode{"ndims_out"}: the number of ## dimensions of the input and output space. ## ## @item @qcode{"forward_fcn"}, @qcode{"inverse_fcn"}: the callback ## functions that are called for forward (input to output) and inverse ## transform. ## ## @item @qcode{"tdata"}: an inverse transform matrix or a structure ## containing forward and inverse transform matrices. ## @end table ## ## The content of each field depends on the requested transform type ## @var{ttype}: ## ## @table @asis ## @item "projective" ## A ndims_in = ndims_out = N projective transform structure ## is returned. ## If a second input argument @var{tmat} is provided, it must be a ## (N+1)-by-(N+1) inverse transformation matrix. The (N+1)th column must ## contain projection coefficients. As an example a two ## dimensional inverse transform from [x y] coordinates to [u v] coordinates ## is represented by an inverse transformation matrix defined so that: ## ## @example ## [xx yy zz] = [u v 1] * [a d g; ## b e h; ## c f i] ## [x y] = [xx./zz yy./zz]; ## @end example ## ## Alternatively the transform can be specified using the coordinates ## of a quadrilateral (typically the 4 corners of the ## image) in the input space (@var{inc}, 4-by-ndims_in matrix) and in ## the output space (@var{outc}, 4-by-ndims_out matrix). This is ## equivalent to building the transform using ## @code{T = cp2tform (@var{inc}, @var{outc}, "projective")}. ## ## @item "affine" ## Affine is a subset of projective transform (see above). A ## ndims_in = ndims_out = N affine transformation structure is ## returned. ## If a second input argument @var{tmat} is provided, it must be a ## (N+1)-by-(N+1) or (N+1)-by-(N) transformation matrix. If present, the ## (N+1)th column must contain [zeros(N,1); 1] so that projection is ## suppressed. ## ## Alternatively the transform can be specified using the coordinates ## of a triangle (typically 3 corners of the ## image) in the input space (@var{inc}, 3-by-ndims_in matrix) and in ## the output space (@var{outc}, 3-by-ndims_out matrix). This is ## equivalent to building the transform using ## @code{T = cp2tform (@var{inc}, @var{outc}, "affine")}. ## ## @item "custom" ## For user defined transforms every field of the transform structure ## must be supplied. The prototype of the transform functions, ## @var{forward_fcn} and @var{inverse_fcn}, should be X' = ## transform_fcn (X, T). X and X' are respectively p-by-ndims_in and ## p-by-ndims_out arrays for forward_fcn and reversed for inverse_fcn. ## The argument T is the transformation structure which will contain ## the user supplied transformation matrix @var{tdata}. ## @end table ## ## @seealso{tformfwd, tforminv, cp2tform} ## @end deftypefn ## Author: Pantxo Diribarne function T = maketform (ttype, varargin) if (nargin < 2 || ! any (strcmpi (ttype, {"affine", "projective", "custom"}))) print_usage (); endif if (numel (varargin) == 1) tmat = varargin {1}; ndin = rows (tmat) - 1; ndout = columns (tmat) - 1; if (ndin < 2); error ("maketform: expect at least 3-by-2 transform matrix") elseif ((ndin-ndout) > 1 || (ndout > ndin)) print_usage (); endif switch (tolower (ttype)) case "affine" if ((ndin - ndout) == 1) tmat = [tmat [zeros(ndin, 1); 1]]; ndout += 1; elseif (!all (tmat(:,end) == [zeros(ndin, 1); 1])) error ("maketform: \"%s\" expect [zeros(N,1); 1] as (N+1)th column", ttype); endif forward_fcn = @fwd_affine; inverse_fcn = @inv_affine; case "projective" if ((ndin - ndout) == 1) print_usage (); endif forward_fcn = @fwd_projective; inverse_fcn = @inv_projective; endswitch T.ndims_in = ndin; T.ndims_out = ndout; T.forward_fcn = forward_fcn; T.inverse_fcn = inverse_fcn; T.tdata.T = tmat; T.tdata.Tinv = inv (tmat); elseif (numel (varargin) == 2) inc = varargin{1}; outc = varargin{2}; if (strcmp (ttype, "affine")) if (all (size (inc) == size (outc)) && all (size (inc) == [3 2])) T = cp2tform (inc, outc, ttype); else error ("maketform: expect INC and OUTC to be 3-by-2 vectors."); endif elseif (strcmp (ttype, "projective")) if (all (size (inc) == size (outc)) && all (size (inc) == [4 2])) T = cp2tform (inc, outc, ttype); else error ("maketform: expect INC and OUTC to be 4-by-2 vectors."); endif endif elseif (numel (varargin) == 5 && strcmpi (ttype, "custom")) if (isscalar (varargin{1}) && isscalar (varargin{2}) && varargin{1} > 0 && varargin{2} > 0) T.ndims_in = varargin{1}; T.ndims_out = varargin{2}; else error ("maketform: expect positive scalars as ndims.") endif if (is_function_handle (varargin{3}) || isempty (varargin{3})) T.forward_fcn = varargin{3}; else error ("maketform: expect function handle as forward_fcn.") endif if (is_function_handle (varargin{4}) || isempty (varargin{4})) T.inverse_fcn = varargin{4}; else error ("maketform: expect function handle as inverse_fcn.") endif T.tdata = varargin{5}; else print_usage (); endif endfunction function X = fwd_affine (U, T) U = [U, ones(rows(U), 1)]; X = U * T.tdata.T(:,1:end-1); endfunction function U = inv_affine (X, T) X = [X, ones(rows(X), 1)]; U = X * T.tdata.Tinv(:,1:end-1); endfunction function X = fwd_projective (U, T) U = [U, ones(rows(U), 1)]; XX = U * T.tdata.T; X = [XX(:,1)./XX(:,3) XX(:,2)./XX(:,3)]; endfunction function U = inv_projective (X, T) X = [X, ones(rows(X), 1)]; UU = X * T.tdata.Tinv; U = [UU(:,1)./UU(:,3) UU(:,2)./UU(:,3)]; endfunction image-2.16.1/inst/PaxHeaders.61586/bweuler.m0000644000000000000000000000006215005110255015225 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/inst/bweuler.m0000644000175000017500000000651415005110255016631 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## Copyright (C) 2011 Adrián del Pino ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{eul} = } bweuler (@var{BW}, @var{n}) ## Calculate the Euler number of a binary image. ## ## This function calculates the Euler number @var{eul} of a binary ## image @var{BW}. This number is a scalar whose value represents the total ## number of objects in @var{BW} minus the number of holes. ## ## @var{n} is an optional argument that specifies the neighbourhood ## connectivity. Must either be 4 or 8. If omitted, defaults to 8. ## ## This function uses Bit Quads as described in "Digital Image ## Processing" to calculate euler number. ## ## References: ## W. K. Pratt, "Digital Image Processing", 3rd Edition, pp 593-595 ## ## @seealso{bwmorph, bwperim, qtgetblk} ## @end deftypefn function eul = bweuler (BW, n = 8) if (nargin < 1 || nargin > 2) print_usage; elseif (!isbw (BW, "non-logical")) error("first argument must be a Black and White image"); elseif (ndims (BW) != 2) error ("bweuler: BW must have 2 dimensions"); endif ## lut_4=(q1lut-q3lut+2*qdlut)/4; # everything in one lut will be quicker ## lut_8=(q1lut-q3lut-2*qdlut)/4; # but only the final result is divided by four ## we precalculate this... # to save more time if (!isnumeric (n) || !isscalar (n) || (n != 8 && n != 4)) error("second argument must either be 4 or 8"); elseif (n == 8) lut = [0; 1; 1; 0; 1; 0; -2; -1; 1; -2; 0; -1; 0; -1; -1; 0]; elseif (n == 4) lut = [0; 1; 1; 0; 1; 0; 2; -1; 1; 2; 0; -1; 0; -1; -1; 0]; endif ## Adding zeros to the top and left bordes to avoid errors when figures touch these borders. ## Notice that 1 0 is equivalent to 1 0 0 because there are implicit zeros in the bottom and right ## 0 1 0 1 0 ## 0 0 0 ## borders. Therefore, there are three one-pixel and one diagonal pixels. So, we get 3 * 1 - 2 = 1 ## (error) instead of 6 * 1 - 2 = 4 (correct). BWaux = false (size (BW) + 1); BWaux(2:end,2:end) = BW; eul = sum (applylut (BWaux, lut) (:)) / 4; endfunction %!demo %! A = zeros (9,10); %! A([2,5,8],2:9) = 1; %! A(2:8,[2,9]) = 1 %! bweuler (A) %! # Euler number (objects minus holes) is 1-2=-1 in an 8-like object %!test %! A = zeros (10,10); %! A(2:9,3:8) = 1; %! A(4,4) = 0; %! A(8,8) = 0; # not a hole %! A(6,6) = 0; %! assert (bweuler (A),-1); ## This will test if n=4 and n=8 behave differently %!test %! A = zeros(10,10); %! A(2:4,2:4) = 1; %! A(5:8,5:8) = 1; %! assert (bweuler (A,4),2); %! assert (bweuler (A,8),1); %! assert (bweuler (A),1); %!error <2 dimensions> bweuler (true (5, 5, 1, 5)) image-2.16.1/inst/PaxHeaders.61586/mat2gray.m0000644000000000000000000000006215005110255015306 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/inst/mat2gray.m0000644000175000017500000001147715005110255016716 0ustar00avinoamavinoam00000000000000## Copyright (C) 1999, 2000 Kai Habel ## Copyright (C) 2011, 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{I} =} mat2gray (@var{M}) ## @deftypefnx {Function File} {@var{I} =} mat2gray (@var{M}, [@var{min} @var{max}]) ## Convert a matrix to an intensity image. ## ## The returned matrix @var{I} is a grayscale image, of double class and in the ## range of values [0, 1]. The optional arguments @var{min} and @var{max} will ## set the limits of the conversion; values in @var{M} below @var{min} and ## above @var{max} will be set to 0 and 1 on @var{I} respectively. ## ## @var{max} and @var{min} default to the maximum and minimum values of @var{M}. ## ## If @var{min} is larger than @var{max}, the `inverse' will be returned. Values ## in @var{M} above @var{max} will be set to 0 while the ones below @var{min} ## will be set to 1. ## ## @strong{Caution:} For compatibility with @sc{matlab}, if @var{min} and @var{max} ## are equal (either from being actually being set manually or automatically ## calculated from the @var{M} min and max values, Octave's mat2gray will truncate ## all values between [0 1]. For example ## ## @example ## @group ## mat2gray ([-2 0 0.5 0.9 5], [2 2]) ## @result{} [0 0 0.5 0.9 1] ## mat2gray ([0.5 0.5 0.5]) ## @result{} [0.5 0.5 0.5] ## mat2gray ([4 4 4]) ## @result{} [1 1 1] ## @end group ## @end example ## ## @seealso{gray2ind, ind2gray, rgb2gray, im2double, im2uin16, im2uint8, im2int16} ## @end deftypefn function in = mat2gray (in, scale) if (nargin < 1 || nargin > 2) print_usage; elseif (! isnumeric (in) && ! islogical (in)) error ("mat2gray: IN must be a matrix"); elseif (nargin == 2 && (!isvector (scale) || numel (scale) != 2)) error ("mat2gray: second argument must be a vector with 2 elements"); endif if (nargin == 1) out_min = min (in(:)); out_max = max (in(:)); else ## see more at the end for the cases where max and min are swapped out_min = min (scale (1), scale (2)); out_max = max (scale (1), scale (2)); endif ## since max() and min() return a value of same class as input, ## need to make this values double or the calculations later may fail out_min = double (out_min); out_max = double (out_max); ## if max and min are the same, matlab seems to simple truncate the input ## between 0 and 1, and ignores the min/max values set. Don't get the logic ## but hey! Matlab compatibility if (out_min == out_max) in(in>1) = 1; in(in<0) = 0; return endif ## we are editing the input matrix rather than creating a new one to save ## memory. We need to make sure it's double though in = double(in); ## it's faster to get the index of values between max and min only once ## than to have it calculated on both sides of the assignment later on. We ## need to get the index before starting editing idx = (in > out_min & in < out_max); idx_max = (in >= out_max); in(in <= out_min) = 0; in(idx_max) = 1; in(idx) = (1/(out_max - out_min)) * (double(in(idx)) - out_min); ## if the given min and max are in the inverse order... if (nargin > 1 && scale(1) > scale (2)) ## matlab seems to allow setting the min higher than the max but not by ## checking which one is actually correct. Seems to just invert it in = abs (in - 1); endif endfunction %!assert(mat2gray([1 2 3]), [0 0.5 1]); # standard use %!assert(mat2gray(repmat ([1 2; 3 3], [1 1 3])), repmat ([0 0.5; 1 1], [1 1 3])); # setting min and max %!assert(mat2gray([1 2 3], [2 2]), [1 1 1]); # equal min and max %!assert(mat2gray([-1 0 0.5 3], [2 2]), [0 0 0.5 1]); # equal min and max %!test %! ## SCALE is unset and all values in the input IMAGE are the same: %! ## case 1: all values are in the [0 1] range] %! assert (mat2gray ([.5 .5; .5 .5]), [.5 .5; .5 .5]) %! ## case 2: all values are above the [0 1] range %! assert (mat2gray ([3 3; 3 3]), [1 1; 1 1]) %! ## case 2: all values are below the [0 1] range %! assert (mat2gray ([-3 -3; -3 -3]), [0 0; 0 0]) %!assert(mat2gray([1 2 3], [3 1]), [1 0.5 0]); # max and min inverted ## bug #47516 (when MIN is greater than input max value) %!assert (mat2gray ([-3 -2 -1]), [0 0.5 1]) image-2.16.1/inst/PaxHeaders.61586/imdivide.m0000644000000000000000000000006215005110255015352 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/inst/imdivide.m0000644000175000017500000000535015005110255016753 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{out} =} imdivide (@var{a}, @var{b}) ## @deftypefnx {Function File} {@var{out} =} imdivide (@var{a}, @var{b}, @var{class}) ## Divide image by another image or constant. ## ## If @var{a} and @var{b} are two images of same size and class, @var{a} is divided ## by @var{b}. Alternatively, if @var{b} is a floating-point scalar, @var{a} is divided ## by it. ## ## The class of @var{out} will be the same as @var{a} unless @var{a} is logical ## in which case @var{out} will be double. Alternatively, it can be ## specified with @var{class}. ## ## @emph{Note}: the values are truncated to the mininum value of the output ## class. ## @seealso{imabsdiff, imadd, imcomplement, immultiply, imlincomb, imsubtract} ## @end deftypefn function img = imdivide (img, val, out_class = class (img)) if (nargin < 2 || nargin > 3) print_usage; endif [img, val] = imarithmetics ("imdivide", img, val, out_class); ## matlab doesn't even gives a warning in this situation, it simply returns ## a double precision float if (nargin > 2 && strcmpi (out_class, "logical")) warning ("Ignoring request to return logical as output of division."); endif warning ("off", "Octave:divide-by-zero", "local"); img = img ./ val; endfunction %!assert (imdivide (uint8 ([23 250]), uint8 ([ 2 50])), uint8 ([ 12 5])); # default to first class %!assert (imdivide (uint8 ([56 255]), uint8 ([ 0 0])), uint8 ([255 255])); # dividing by zero works (tested in matlab) %!assert (imdivide (uint8 ([23 250]), 2), uint8 ([ 12 125])); # works subtracting a scalar %!assert (imdivide (uint8 ([23 250]), uint8 ([ 2 50]), "uint16"), uint16 ([ 12 5])); # defining output class works (not in matlab) %!assert (imdivide (logical ([1 1 0 0]), logical ([1 0 1 0])), double ([1 Inf 0 NaN])); # dividing logical matrix (tested in matlab) %!fail ("imdivide (uint8 ([23 250]), uint16 ([23 250]))"); # input needs to have same class image-2.16.1/inst/PaxHeaders.61586/rgb2xyz.m0000644000000000000000000000006215005110255015167 xustar0020 atime=1746178221 30 ctime=1746179027.274726131 image-2.16.1/inst/rgb2xyz.m0000644000175000017500000001210515005110255016564 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{xyz} =} rgb2xyz (@var{rgb}) ## @deftypefnx {Function File} {@var{xyz_map} =} rgb2xyz (@var{rgb_map}) ## Transform a colormap or image from sRGB to CIE XYZ color space. ## ## A color in the RGB space consists of red, green, and blue intensities. ## The input RGB values are interpreted as nonlinear sRGB values ## with the white point D65. This means the input values are assumed to ## be in the colorimetric (sRGB) colorspace. ## ## A color in the CIE XYZ color space consists of three values X, Y and Z. ## Those values are designed to be colorimetric, meaning that their values ## do not depend on the display device hardware. ## ## Input values of class double, single, uint8 or uint16 are accepted. ## Output class is generally of type double, only input type single will ## result in an output type of single. The shape of the input is ## conserved. ## ## note: This function returns slightly different values than the Matlab ## version. But it has a better "round trip accuracy" (<2e-5) ## for RGB -> XYZ -> RGB. ## ## @seealso{xyz2rgb, rgb2lab, rgb2hsv, rgb2ind, rgb2ntsc} ## @end deftypefn ## Author: Hartmut Gimpel ## algorithm taken from the following book: ## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015) function xyz = rgb2xyz (rgb) if (nargin != 1) print_usage (); endif [rgb, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check ("rgb2xyz", "RGB", rgb, 0); ## transform from non-linear sRGB values to linear sRGB values ## (inverse modified gamma transform) rgb_lin = rgb; mask = rgb <= 0.04045; rgb_lin(mask) = rgb(mask) ./ 12.92; rgb_lin(! mask) = ((rgb(! mask) + 0.055) ./ 1.055) .^2.4; ## transform from linear sRGB values to CIE XYZ with whitepoint D65 ## (source of this matrix: book of Burger) matrix_rgb2xyz_D65 = ... [0.412453, 0.357580, 0.180423; 0.212671, 0.715160, 0.072169; 0.019334, 0.119193, 0.950227]; # Matlab uses the following slightly different conversion matrix. # matrix_rgb2xyz_D65 = ... # [0.4124, 0.3576, 0.1805; # 0.2126, 0.7152, 0.0722; # 0.0193, 0.1192, 0.9505]; # open source of this transformation matrix: # https://de.wikipedia.org/wiki/CIE-Normvalenzsystem#Umrechnung_der_Farbr.C3.A4ume # on July 30, 2015 xyz = rgb_lin * matrix_rgb2xyz_D65'; # always return values of type double for Matlab compatibility (exception: type single) xyz = colorspace_conversion_revert (xyz, cls, sz, is_im, is_nd, is_int, 1); endfunction ## Test pure colors, gray and some other colors ## (This set of test values is taken from the book by Burger.) %!assert (rgb2xyz ([0 0 0]), [0, 0, 0], 1e-3) %!assert (rgb2xyz ([1 0 0]), [0.4125, 0.2127, 0.0193], 1e-3) %!assert (rgb2xyz ([1 1 0]), [0.7700, 0.9278, 0.1385], 1e-3) %!assert (rgb2xyz ([0 1 0]), [0.3576, 0.7152, 0.1192], 1e-3) %!assert (rgb2xyz ([0 1 1]), [0.5380, 0.7873, 1.0694], 1e-3) %!assert (rgb2xyz ([0 0 1]), [0.1804, 0.0722, 0.9502], 1e-3) %!assert (rgb2xyz ([1 0 1]), [0.5929, 0.2848, 0.9696], 1e-3) %!assert (rgb2xyz ([1 1 1]), [0.9505, 1.0000, 1.0888], 1e-3) %!assert (rgb2xyz ([0.5 0.5 0.5]), [0.2034, 0.2140, 0.2330], 1e-3) %!assert (rgb2xyz ([0.75 0 0]), [0.2155, 0.1111, 0.0101], 1e-3) %!assert (rgb2xyz ([0.5 0 0]), [0.0883, 0.0455, 0.0041], 1e-3) %!assert (rgb2xyz ([0.25 0 0]), [0.0210, 0.0108, 0.0010], 1e-3) %!assert (rgb2xyz ([1 0.5 0.5]), [0.5276, 0.3812, 0.2482], 1e-3) ## Test tolarant input checking on floats %!assert (rgb2xyz ([1.5 1 1]), [1.5845, 1.3269, 1.1185], 1e-3) %!test %! rgb_map = rand (64, 3); %! assert (xyz2rgb (rgb2xyz (rgb_map)), rgb_map, 2e-5); %!test %! rgb_img = rand (64, 64, 3); %! assert (xyz2rgb (rgb2xyz (rgb_img)), rgb_img, 2e-5); ## support sparse input %!assert (rgb2xyz (sparse ([0 0 0])), [0 0 0], 1e-3) %!assert (rgb2xyz (sparse ([0 0 1])), [0.1804, 0.0722, 0.9502], 1e-3) ## support integer input (and double output) %!assert (rgb2xyz (uint8([255 255 255])), [0.9505, 1.0000, 1.0888], 1e-3) ## conserve class of single input %!assert (class (rgb2xyz (single([1 1 1]))), 'single') ## Test input validation %!error rgb2xyz () %!error rgb2xyz (1,2) %!error rgb2xyz ({1}) %!error rgb2xyz (ones (2,2)) ## Test ND input %!test %! rgb = rand (16, 16, 3, 5); %! xyz = zeros (size (rgb)); %! for i = 1:5 %! xyz(:,:,:,i) = rgb2xyz (rgb(:,:,:,i)); %! endfor %! assert (rgb2xyz (rgb), xyz) image-2.16.1/inst/PaxHeaders.61586/private0000644000000000000000000000013215005111723014776 xustar0030 mtime=1746179027.102725232 30 atime=1746179027.278726152 30 ctime=1746179027.274726131 image-2.16.1/inst/private/0000755000175000017500000000000015005111723016453 5ustar00avinoamavinoam00000000000000image-2.16.1/inst/private/PaxHeaders.61586/colorspace_conversion_revert.m0000644000000000000000000000006215005110255023220 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/colorspace_conversion_revert.m0000644000175000017500000000275115005110255024623 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## Private function for functions that convert between color spaces, i.e., ## rgb2xyz, xyz2rgb, xyz2lab, lab2xyz, rgb2lab and lab2rgb. This reverts ## a colormap type into the same shape and class as it was in the input. ## (But setting the keep_class flag to 1 tells this function to not change ## the class back.) The other flags are meant to come from complementary ## private function colorspace_conversion_input_check() ## ## adapted by: Hartmut Gimpel, 2015 function rv = colorspace_conversion_revert (rv, cls, sz, is_im, is_nd, is_int, keep_class) if (is_im) if (is_nd) rv = reshape (rv, [sz(1:2) sz(4) sz(3)]); rv = permute (rv, [1 2 4 3]); else rv = reshape (rv, sz); endif endif if (is_int && ~keep_class) rv *= intmax (cls); endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/handle_colorspec.m0000644000000000000000000000006215005110255020536 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/handle_colorspec.m0000644000175000017500000000274415005110255022143 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## There's 3 ways to specify colors: ## RGB triplet values ## short names ## long names ## ## And probably more undocumented ways to do it. function rgb = handle_colorspec (func, spec) if (iscolormap (spec)) rgb = spec; elseif (ischar (spec)) switch (tolower (spec)) case {"b", "blue" }, rgb = [0 0 1]; case {"c", "cyan" }, rgb = [0 1 1]; case {"g", "green" }, rgb = [0 1 0]; case {"k", "black" }, rgb = [0 0 0]; case {"m", "magenta"}, rgb = [1 0 1]; case {"r", "red" }, rgb = [1 0 0]; case {"w", "white" }, rgb = [1 1 1]; case {"y", "yellow" }, rgb = [1 1 0]; otherwise error("%s: unknown color '%s'", func, spec); endswitch else error ("%s: invalid color specification"); endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/interp_method.m0000644000000000000000000000006215005110255020073 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/interp_method.m0000644000175000017500000000262115005110255021472 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## This private function is to be common to all functions that have an option ## for the interpolation method to use. For matlab compatibility, bicubic and ## bilinear are accepted which are the same as cubic and linear. This does not ## actually check if the method is valid, we leave that to interp2. The reason ## is that if interp2 implements a new method, all this functions will ## automatically work with it. function method = interp_method (method) method = tolower (method); switch method case "bicubic", method = "cubic"; case "bilinear", method = "linear"; case "triangle", method = "linear"; # interpolation kernel case "box", method = "nearest"; endswitch endfunction image-2.16.1/inst/private/PaxHeaders.61586/ispart.m0000644000000000000000000000006215005110255016534 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/ispart.m0000644000175000017500000000211615005110255020132 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## This a private function for the is... type of functions for the image package ## Rather than checking the whol image, there can be a speed up by checking only ## a corner of the image first and then the rest if that part is true. function bool = ispart (foo, in) bool = foo (in(1:ceil (rows (in) /100), 1:ceil (columns (in) /100))); if (bool) bool = foo (in); endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/isimage.m0000644000000000000000000000006215005110255016650 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/isimage.m0000644000175000017500000000175715005110255020260 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## This a private function for the is... type of functions for the image package ## It simply checks if the input really is an image. function retval = isimage (img) retval = ((isnumeric (img) || islogical (img)) && ! issparse (img) && ! isempty (img) && isreal (img)); endfunction image-2.16.1/inst/private/PaxHeaders.61586/prepare_strel.m0000644000000000000000000000006215005110255020101 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/prepare_strel.m0000644000175000017500000000272315005110255021503 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## This function will be common to imopen, imclose, imbothat, imtophat, ## and any other function that can take a strel object or a matrix of 0 ## and 1. It checks the input and gets a strel object in the later case. function se = prepare_strel (func, se) ## We could do a lot more of input checking but it'd be repeated again ## in imerode and imdilate. We just do the minimum for strel as well. ## Since imerode and imdilate will create a strel object out of the SE ## we pass them, we can create it now ourselves. if (! strcmpi (class (se), "strel")) if (! islogical (se) && (isnumeric (se) && any (se(:) != 1 & se(:) != 0))) error ("%s: SE must be a strel object or matrix of 0 and 1", func); endif se = strel ("arbitrary", se); endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/ycbcrfunc.m0000644000000000000000000000006215005110255017210 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/ycbcrfunc.m0000644000175000017500000000766715005110255020626 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## Private function for ycbcr2rgb and rgb2ycbcr functions which are ## very similar function out = ycbcrfunc (func, in, standard) img = false; # was input an image? if (iscolormap (in)) ## do nothing, it's a colormap elseif (isrgb (in)) img = true; ## we shape it as a colormap (2D matrix) so we can use matrix multiplcation nRows = rows (in); nCols = columns (in); in = reshape (in, [nRows*nCols 3]); else error ("%s: input must be a colormap (Nx3) or RGB image (NxMx3)", func); endif if (ischar (standard)) if (strcmpi (standard, "601")) # for ITU-R BT.601 Kb = 0.114; Kr = 0.299; elseif (strcmpi (standard, "709")) # for ITU-R BT.709 Kb = 0.0722; Kr = 0.2126; elseif (strcmpi (standard, "2020")) # for ITU-R BT.2020 Kb = 0.0593; Kr = 0.2627; else error ("%s: unknown standard `%s'", func, standard); endif elseif (isnumeric (standard) && numel (standard) == 2) Kb = standard(1); Kr = standard(2); else error ("%s: must specify a standard (string), or Kb and Kr values", func); endif ## the color matrix for the conversion. Derived from: ## Y = Kr*R + (1-Kr-Kb)*G + kb*B ## Cb = (1/2) * ((B-Y)/(1-Kb)) ## Cr = (1/2) * ((R-Y)/(1-Kr)) ## It expects RGB values in the range [0 1], and returns Y in the ## range [0 1], and Cb and Cr in the range [-0.5 0.5] cmat = [ Kr (1-Kr-Kb) Kb -(Kr/(2-2*Kb)) -(1-Kr-Kb)/(2-2*Kb) 0.5 0.5 -(1-Kr-Kb)/(2-2*Kr) -(Kb/(2-2*Kr)) ]; cls = class (in); if (! isfloat (in)) in = im2double (in); endif ## note that these blocks are the inverse of one another. Changes ## in one will most likely require a change on the other if (strcmp (func, "rgb2ycbcr")) ## convert to YCbCr colorspace out = in * cmat'; ## rescale Cb and Cr to range [0 1] out(:, [2 3]) += 0.5; ## the actual range of Cb, Cr and Y will be smaller. The values at the ## extremes are named footroom and headroom. Cb, Cr, and Y have different ## ranges for footroom and headroom ## ## Cb and Cr: footroom -> [0 16/255[ ## Y : footroom -> [0 16/255[ ## Cb and Cr: headroom -> ]240/255 1 ] ## Y : headroom -> ]235/255 1 ] ## ## So we first compress the values to the actual available range (the whole ## [0 1] interval minus headroom and footroom), and then shift forward out(:,1) = (out(:,1) * 219/255) + 16/255; out(:,[2 3]) = (out(:,[2 3]) * 224/255) + 16/255; elseif (strcmp (func, "ycbcr2rgb")) ## just the inverse of the rgb2ycbcr conversion in(:,[2 3]) = (in(:,[2 3]) - 16/255) / (224/255); in(:,1) = (in(:,1) - 16/255) / (219/255); in(:,[2 3]) -= 0.5; out = in * inv (cmat'); else error ("internal error for YCbCr conversion. Unknown function %s", func); endif switch (cls) case {"single", "double"} ## do nothing. All is good case "uint8" out = im2uint8 (out); case "uint16" out = im2uint16 (out); otherwise error ("%s: unsupported image class %s", func, cls); endswitch if (img) ## put the image back together out = reshape (out, [nRows nCols 3]); endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/is_float_image.m0000644000000000000000000000006215005110255020174 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/is_float_image.m0000644000175000017500000000164415005110255021577 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## simple check used by some functions. Images of the double class must have ## all their values between 0 and 1 or be NaN function bool = is_float_image (img) bool = min (img(:)) >= 0 && max (img(:)) <= 1; endfunction image-2.16.1/inst/private/PaxHeaders.61586/pad_for_sliding_filter.m0000644000000000000000000000006215005110255021722 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/pad_for_sliding_filter.m0000644000175000017500000000316315005110255023323 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## This private function is to be common to all functions that call ## __spatial_filtering__. It may be better to have __spatial_filtering__ do this ## part as well then function im = pad_for_sliding_filter (im, window_size, padval) if (ndims (im) != numel (window_size)) error ("image and domain must have the same number of dimensions") endif pad = floor (window_size / 2); im = padarray (im, pad, padval); even = ! mod (window_size, 2); ## if one of the domain dimensions is even, its origin is must be ## floor ([size(domain)/2] + 1) ## so after padarray, we need to remove the 1st element (1st row or column for ## dimension 1 and 2) from the dimensions where the domain is even if (any (even)) idx = cell (1, ndims (im)); for k = 1:ndims(im) idx{k} = (even(k)+1):size(im, k); endfor im = im(idx{:}); endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/im2col_check.m0000644000000000000000000000006215005110255017554 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/im2col_check.m0000644000175000017500000000373115005110255021156 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## A private function originally written for im2col but that can also ## be used by colfilt and nlfilt, since the syntax is very similar. function [p, block_size, padval] = im2col_check (func, nargin, A, varargin) if (nargin < 2) print_usage (func); elseif (! isnumeric (A) && ! islogical (A)) error ("%s: A must be a numeric of logical matrix", func); endif p = 1; # varargin param being processsed padval = 0; ## Check for 'indexed' presence if (ischar (varargin{p}) && strcmpi (varargin{p}, "indexed")) if (nargin < 3) print_usage (func); endif ## We pad with value of 0 for indexed images of uint8 or uint16 class. ## Indexed images of signed integer, or above uint16, are treated the ## same as floating point (a value of 1 is index 1 on the colormap) if (any (isa (A, {"uint8", "uint16"}))) padval = 0; else padval = 1; endif p++; endif ## check [m,n] block_size = varargin{p++}; if (! isnumeric (block_size) || ! isvector (block_size) || any (block_size(:) < 1)) error ("%s: BLOCK_SIZE must be a vector of positive elements.", func); endif block_size(end+1:ndims(A)) = 1; # expand singleton dimensions if required block_size = block_size(:).'; # make sure it's a row vector endfunction image-2.16.1/inst/private/PaxHeaders.61586/imarithmetics.m0000644000000000000000000000006215005110255020074 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/imarithmetics.m0000644000175000017500000000716715005110255021505 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imarithmetics () ## This is a private function common to the likes of imadd, imsubtract, etc. ## ## First argument is the function name for the error message, while the others are ## the same order as the original function. It returns the two first input of the ## original function ## @end deftypefn function [img, val] = imarithmetics (func, img, val, out_class, in_args) is_valid = @(x) ((!isnumeric (x) && !islogical (x)) || isempty (x) || issparse (x) || !isreal (x)); if (is_valid (img) || is_valid (val)) error ("%s: input must be a numeric or logical, non-empty, non-sparse real matrix", func) elseif (!ischar (out_class)) error ("%s: third argument must be a string that specifies the output class", func) endif same_size = all (size (img) == size (val)); if (same_size && strcmpi (class (img), class (val))) [img, val] = convert (out_class, img, 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? img = convert (out_class, img); else error ("%s: second argument must either be of same class and size of the first or a floating point scalar", func) end endfunction function [a, b] = convert (out_class, a, b = 0) ## 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 ((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); case {"uint16"} a = uint16 (a); b = uint16 (b); case {"uint32"} a = uint32 (a); b = uint32 (b); case {"uint64"} a = uint64 (a); b = uint64 (b); case {"int8"} a = int8 (a); b = int8 (b); case {"int16"} a = int16 (a); b = int16 (b); case {"int32"} a = int32 (a); b = int32 (b); case {"int64"} a = int64 (a); b = int64 (b); case {"double"} a = double (a); b = double (b); case {"single"} a = single (a); b = single (b); otherwise error ("%s: requested class '%s' for output is not supported", func, out_class) endswitch endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/colorspace_conversion_input_check.m0000644000000000000000000000006215005110255024205 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/colorspace_conversion_input_check.m0000644000175000017500000000611415005110255025605 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## Private function for functions that convert between color spaces, i.e., ## rgb2xyz, xyz2rgb, xyz2lab, lab2xyz, rgb2lab and lab2rgb. All of these ## functions need to handle input in the same way. The returned flags ## are meant to be handled by the complementary private function ## colorspace_conversion_revert() ## Setting the only_foats flag to 1 tells this function to only accept single ## and double input types, otherwise it will also accept uint8 and uint16 ## input values. ## ## adapted by: Hartmut Gimpel, 2015 function [in_arg, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check (func, arg_name, in_arg, only_floats) cls = class (in_arg); sz = size (in_arg); ## If we have an image convert it into a color map. if (! iscolormap (in_arg)) if (! any (strcmp (cls, {"uint8", "int8", "uint16", "single", "double"}))) error ("%s: %s of invalid data type '%s'", func, arg_name, cls); elseif (only_floats && ! any (strcmp (cls, {"single", "double"}))) error ("%s: %s of invalid data type '%s'", func, arg_name, cls); elseif (size (in_arg, 3) != 3 && !(size (in_arg) == [1, 3] || size (in_arg) == [3, 1])) error ("%s: %s must be a colormap or %s image", func, arg_name, arg_name); elseif (! isreal (in_arg) || ! isnumeric (in_arg)) error ("%s: %s must be numeric and real", func, arg_name); endif is_im = true; ## Some floating point values (like R, G, B values) should be in the [0 1] range, ## otherwise they don't make any sense. We accept those values ## anyways because we must return something for Matlab compatibility. ## User case is when a function returns an RGB image just slightly outside ## the range due to floating point rounding errors. ## Allow for ND images, i.e., multiple images on the 4th dimension. nd = ndims (in_arg); if (nd == 2 || nd == 3) is_nd = false; elseif (nd == 4) is_nd = true; in_arg = permute (in_arg, [1 2 4 3]); elseif (nd > 4) error ("%s: invalid %s with more than 4 dimensions", func, arg_name); endif in_arg = reshape (in_arg, [numel(in_arg)/3 3]); else is_im = false; is_nd = false; endif ## Convert to floating point (remember to leave class single alone) if (isinteger (in_arg)) in_arg = double (in_arg) / double (intmax (cls)); is_int = true; else is_int = false; endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/lab2cls.m0000644000000000000000000000006215005110255016554 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/lab2cls.m0000644000175000017500000000635015005110255020156 0ustar00avinoamavinoam00000000000000## Copyright (C) 2016 Carnë Draug ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## Internal function that does all the work of lab2uint8, lab2uin16, ## and lab2double functions. function [lab] = lab2cls (lab, out_cls) nd = ndims (lab); sz = size (lab); ## We want to minimize operations on the input but we also want to ## have the same code for colormap and ND images. So we reshape into ## a colormap with other images on the 3rd dimension. The actual ## conversion is broadcasted, and then we reshape back at the end. was_image = false; if (nd == 2 && sz(2) == 3) # colormap shape ## Do nothing, we already have it shaped like we want. elseif (nd == 3 && sz(3) == 3) # MxNx3 image was_image = true; lab = reshape (lab, [sz(1)*sz(2) 3]); elseif (nd == 4 && sz(3) == 3) # MxNx3xK image was_image = true; lab = reshape (lab, [sz(1)*sz(2) 3 sz(4)]); else error ("lab2%s: LAB must be Mx3, MxNx3, or MxNx3xK size", out_cls); endif in_cls = class (lab); switch (out_cls) case {"double", "single"} lab = cast (lab, out_cls); switch (in_cls) case "uint8" lab(:, 1, :) *= (100 / 255); lab(:, [2 3], :) -= 128; case "uint16" lab(:, 1, :) *= (100 / 65280); lab(:, [2 3], :) *= (255 / 65280); lab(:, [2 3], :) -= 128; case {"double", "single"} ## Do nothing, we already casted to the other type. otherwise error ("lab2%s: invalid class '%s' for LAB", out_cls, in_cls); endswitch case "uint8" switch (in_cls) case {"double", "single"} lab(:, 1, :) *= (255 / 100); lab(:, [2 3], :) += 128; lab(isnan (lab)) = 255; # for Matlab compatibility lab = uint8 (lab); case "uint16" lab /= 256; lab = uint8 (lab); case "uint8" ## Do nothing. otherwise error ("lab2uint8: invalid class '%s' for LAB", in_cls); endswitch case "uint16" switch (in_cls) case {"double", "single"} lab(:, 1, :) *= (65280 / 100); lab(:, [2 3], :) += 128; lab(:, [2 3], :) *= (65280 / 255); lab(isnan (lab)) = 65535; # for Matlab compatibility lab = uint16 (lab); case "uint8" lab = uint16 (lab) * 256; case "uint16" ## Do nothing. otherwise error ("lab2uint16: invalid class '%s' for LAB", in_cls); endswitch otherwise error ("lab2%s: non-supported conversion (internal error)", out_cls); endswitch if (was_image) lab = reshape (lab, sz); endif endfunction image-2.16.1/inst/private/PaxHeaders.61586/analyze75filename.m0000644000000000000000000000006215005110255020552 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/analyze75filename.m0000644000175000017500000000325015005110255022150 0ustar00avinoamavinoam00000000000000%% Copyright (C) 2012 Adam H Aitkenhead %% Copyright (C) 2012 Carnë Draug %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; either version 3 of the License, or %% (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; If not, see . %% private function with common code for the analyze75info and read functions function filename = analyze75filename (filename) %% Check filename if (exist (filename, 'dir')) filelist = dir ([filename, filesep, '*.hdr']); if (numel (filelist) == 1) filename = [filename, filesep, filelist.name]; elseif (numel (filelist) > 1) error ('analyze75: `filename'' is a directory with multiple hdr files.') else error ('analyze75: `filename'' is a directory with no hdr files.') end elseif (~exist (filename, 'file')) error ('analyze75: no file `%s''', filename) end %% Strip the filename of the extension fileextH = strfind (filename, '.hdr'); fileextI = strfind (filename, '.img'); if (~isempty (fileextH)) filename = filename(1:fileextH(end)-1); elseif (~isempty (fileextI)) filename = filename(1:fileextI(end)-1); else filename = filename; end end image-2.16.1/inst/private/PaxHeaders.61586/istform.m0000644000000000000000000000006215005110255016715 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/private/istform.m0000644000175000017500000000241215005110255020312 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Pantxo Diribarne ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## Private internal function to check if a argument is a transformation ## structure (as created by maketform and to be use by findbounds, ## imtransform, and the like) ## Author: Pantxo Diribarne function out = istform (T) out = true; if (!isstruct (T)) out = false; else required = {"ndims_in";"ndims_out"; ... "forward_fcn"; "inverse_fcn"; ... "tdata"}; fields = fieldnames (T); tst = cellfun (@(x) any (strcmp (fields, x)), required); if (! all (tst)) out = false endif endif endfunction image-2.16.1/inst/PaxHeaders.61586/bwpack.m0000644000000000000000000000006215005110255015027 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwpack.m0000644000175000017500000000634415005110255016434 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {} bwpack (@var{bw}) ## Pack binary image. ## ## Packs binary image @var{bw} into an array of 32 bit unsigned ## integers. Each 32 elements of @var{bw} are packed into a uint32 ## integer, the first row corresponding to the least significant bit ## and the 32th row corresponding to the most significant bit. ## ## Packing is performed along the first dimension (rows), so that each ## 32 rows on each column correspond to one element in the packed ## image. @var{bw} is zero-padded if its height isn't an exact ## multiple of 32. It is thus necessary to remember the height of the ## original image in order to retrieve it from the packed version, ## e.g. by calling @code{bwunpack}. ## ## @var{bw} is converted to logical before packing, non-zero elements ## being converted to @code{true}. ## ## @seealso{bwunpack, bitpack, bitunpack} ## @end deftypefn function bw = bwpack (bw) if (nargin != 1) print_usage (); endif try bw = logical (bw); catch error ("Octave:invalid-input-arg", "bwpack: BW must be logical or conversible to logical") end_try_catch class_size = 32; # number of pixels packed into a single unsigned int dims = size (bw); out_nrows = ceil (dims(1) / class_size); in_nrows = out_nrows * class_size; if (in_nrows != dims(1)) bw = resize (bw, [in_nrows dims(2:end)]); endif bw = reshape (bitpack (bw(:), "uint32"), [out_nrows dims(2:end)]); endfunction %!error id=Octave:invalid-fun-call bwpack () %!error id=Octave:invalid-input-arg bwpack ("text") %!xtest %! ## bug #55521 %! assert (bwpack (eye (5)), uint32 ([1 2 4 8 16])) %!xtest %! ## bug #55521 %! assert (bwpack (repmat (eye (4), 15, 1)), %! uint32 ([286331153 572662306 1145324612 2290649224 %! 17895697 35791394 71582788 143165576])) %!xtest %! ## bug #55521 %! assert (bwpack (ones (3, 3, 3, 3)), repmat (uint32 (7), 1, 3, 3, 3)) %!assert (bwpack (false (0, 10)), uint32 (zeros (0, 10))) %!assert (bwpack (false (0, 0)), uint32 (zeros (0, 0))) %!assert (bwpack (false (32, 0)), uint32 (zeros (1, 0))) %!assert (bwpack (false (33, 0)), uint32 (zeros (2, 0))) %!assert (bwpack (false (0, 10, 3)), uint32 (zeros (0, 10, 3))) %!assert (bwpack (false (33, 0, 3)), uint32 (zeros (2, 0, 3))) ## This would error in Matlab but works in Octave. Reason is that ## `logical (i)` fails in Matlab so `bwpack (i)` makes no sense and ## fails too. However, `logical (i)` works fine in Octave so `bwpack ## (i)` must also work. %!assert (bwpack (i), bwpack (logical (i))) image-2.16.1/inst/PaxHeaders.61586/im2col.m0000644000000000000000000000006215005110255014745 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/im2col.m0000644000175000017500000002137015005110255016346 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} im2col (@var{A}, @var{block_size}) ## @deftypefnx {Function File} {} im2col (@var{A}, @var{block_size}, @var{block_type}) ## @deftypefnx {Function File} {} im2col (@var{A}, "indexed", @dots{}) ## Rearrange blocks from matrix into columns. ## ## Rearranges blocks of size @var{block_size}, sampled from the matrix @var{A}, ## into a serie of columns. This effectively transforms any image into a ## 2 dimensional matrix, a block per column, which can then be passed to ## other functions that perform calculations along columns. ## ## Both blocks and matrix @var{A} can have any number of dimensions (though ## for sliding blocks, a block can't be larger than @var{A} in any dimension). ## Blocks are always accessed in column-major order (like Octave arrays are ## stored) so that a matrix can be easily reconstructed with @code{reshape} ## and @code{col2im}. For a 2 dimensional matrix, blocks are taken first from ## the top to the bottom, and then from the left to the right of the matrix. ## ## The sampling can be performed in two different ways as defined by ## @var{block_type} (defaults to @qcode{"sliding"}): ## ## @table @asis ## @item @qcode{"distinct"} ## Each block is completely distinct from the other, with no overlapping ## elements. The matrix @var{A} is padded as required with a value of 0 ## (or 1 for non-integer indexed images). ## ## @item @qcode{"sliding"} ## A single block slides across @var{A} without any padding. ## ## While this can be used to perform sliding window operations such as maximum ## and median filters, specialized functions such as @code{imdilate} and ## @code{medfilt2} will be more efficient. ## ## Note that large images being arranged in large blocks can easily exceed the ## maximum matrix size (see @code{sizemax}). For example, a matrix @var{A} of ## size 500x500, with sliding block of size [100 100], would require a matrix ## with 2.4108e+09 elements, i.e., the number of elements in a block, ## @code{100*100}, times the number of blocks, @code{(500-10+1) * (500-10+1)}. ## ## @end table ## ## If @var{A} is an indexed image, the second argument should be the ## string @qcode{"indexed"} so that any required padding is done correctly. ## The padding value will be 0 except for indexed images of class uint8 ## and uint16. ## ## @seealso{blockproc, bestblk, col2im, colfilt, nlfilter, reshape} ## @end deftypefn ## Matlab behaves weird with N-dimensional images. It ignores block_size ## elements after the first 2, and treat N-dimensional as if the extra ## dimensions were concatenated horizontally. We are performing real ## N-dimensional conversion of image blocks into colums. function B = im2col (A, varargin) ## Input check if (nargin > 4) print_usage (); endif [p, block_size, padval] = im2col_check ("im2col", nargin, A, varargin{:}); if (nargin > p) ## we have block_type param if (! ischar (varargin{p})) error("im2col: BLOCK_TYPE must be a string"); endif block_type = varargin{p++}; else block_type = "sliding"; endif if (nargin > p) print_usage (); endif ## After all the input check, start the actual im2col. The idea is to ## calculate the linear indices for each of the blocks (using broadcasting ## for each dimension), and then reshape it with one block per column. switch (tolower (block_type)) case "distinct" ## We may need to expand the size vector to include singletons size_singletons = @(x, ndim) postpad (size (x), ndim, 1); ## Calculate needed padding A_size = size_singletons (A, numel (block_size)); sp = mod (-A_size, block_size); if (any (sp)) A = padarray (A, sp, padval, "post"); endif A_size = size_singletons (A, numel (block_size)); ## Get linear indixes for the first block [ind, stride] = get_1st_ind (A_size, block_size); ## Get linear indices for all of the blocks blocks = A_size ./ block_size; step = stride .* block_size; limit = step .* (blocks -1); for dim = 1:numel (A_size) ind = ind(:) + (0:step(dim):limit(dim)); endfor n_blocks = prod (blocks); case "sliding" if (numel (block_size) > ndims (A)) error ("im2col: BLOCK_SIZE can't have more elements than the dimensions of A"); endif ## Get linear indixes for the first block [ind, stride] = get_1st_ind (size (A), block_size); ## Get linear indices for all of the blocks slides = size (A) - block_size; limit = stride .* slides; for dim = 1:ndims (A) ## We need to use bsxfun here because of ## https://savannah.gnu.org/bugs/?47085 ind = bsxfun (@plus, ind(:), (0:stride(dim):limit(dim))); endfor n_blocks = prod (slides +1); otherwise error ("im2col: invalid BLOCK_TYPE `%s'.", block_type); endswitch B = reshape (A(ind(:)), prod (block_size), n_blocks); endfunction ## Get linear indices and for the first block, and stride size per dimension function [ind, stride] = get_1st_ind (A_size, block_size) stride = [1 cumprod(A_size(1:end-1))]; limit = (block_size -1) .* stride; ind = 1; for dim = 1:numel (A_size) ind = ind(:) + (0:stride(dim):limit(dim)); endfor endfunction %!demo %! ## Divide A using distinct blocks and then reverse the operation %! A = [ 1:10 %! 11:20 %! 21:30 %! 31:40]; %! B = im2col (A, [2 5], "distinct") %! C = col2im (B, [2 5], [4 10], "distinct") ## test default block type %!test %! a = rand (10); %! assert (im2col (a, [5 5]), im2col (a, [5 5], "sliding")) ## indexed makes no difference when sliding %!test %! a = rand (10); %! assert (im2col (a, [5 5]), im2col (a, "indexed", [5 5])) %!error im2col (rand (20), [2 5], 10) %!error im2col (rand (20), [2 5], "wrong_block_type") %!error im2col (rand (10), [5 5], "sliding", 5) %!error im2col (rand (10), "indexed", [5 5], "sliding", 5) %!shared B, A, Bs, As, Ap, Bp0, Bp1, Bp0_3s %! v = [1:10]'; %! r = reshape (v, 2, 5); %! B = [v v+20 v+40 v+10 v+30 v+50]; %! A = [r r+10; r+20 r+30; r+40 r+50]; %! As = [ 1 2 3 4 5 %! 6 7 8 9 10 %! 11 12 13 14 15]; %! b1 = As(1:2, 1:4)(:); %! b2 = As(2:3, 1:4)(:); %! b3 = As(1:2, 2:5)(:); %! b4 = As(2:3, 2:5)(:); %! Bs = [b1, b2, b3, b4]; %! Ap = A(:, 1:9); %! Bp1 = Bp0 = B; %! Bp0(9:10, 4:6) = 0; %! Bp1(9:10, 4:6) = 1; %! Bp0_3s = Bp0; %! Bp0_3s(11:30, :) = 0; ## test distinct block type %!assert (im2col (A, [2 5], "distinct"), B); ## padding for distinct %!assert (im2col (Ap, [2 5], "distinct"), Bp0); %!assert (im2col (Ap, [2 5 3], "distinct"), Bp0_3s); %!assert (im2col (Ap, "indexed", [2 5], "distinct"), Bp1); %!assert (im2col (uint8 (Ap), "indexed", [2 5], "distinct"), uint8 (Bp0)); %!assert (im2col (uint16 (Ap), "indexed", [2 5], "distinct"), uint16 (Bp0)); %!assert (im2col (int16 (Ap), "indexed", [2 5], "distinct"), int16 (Bp1)); %!assert (im2col (uint32 (Ap), "indexed", [2 5], "distinct"), uint32 (Bp1)); ## Always return correct class %!assert (im2col (uint8 (A), [2 5], "distinct"), uint8 (B)); %!assert (im2col (single (A), [2 5], "distinct"), single (B)); %!assert (im2col (logical (A), [2 5], "distinct"), logical (B)); %!assert (im2col (uint8 (As), [2 4], "sliding"), uint8 (Bs)); %!assert (im2col (single (As), [2 4], "sliding"), single (Bs)); %!assert (im2col (logical (As), [2 4], "sliding"), logical (Bs)); ## test sliding block type %!assert (im2col (As, [2 4], "sliding"), Bs); %!assert (im2col (As, [3 5], "sliding"), As(:)); ## Test N-dimensional %!test %! A = randi (9, 10, 9, 5); %!assert (convn (A, ones (3, 3, 3), "valid"), %! reshape (sum (im2col (A, [3 3 3])), [8 7 3])); %! %! A = randi (9, 10, 9, 5, 7); %!assert (convn (A, ones (3, 3, 3), "valid"), %! reshape (sum (im2col (A, [3 3 3])), [8 7 3 7])); %!assert (convn (A, ones (3, 4, 3), "valid"), %! reshape (sum (im2col (A, [3 4 3])), [8 6 3 7])); %!assert (convn (A, ones (3, 5, 3, 2), "valid"), %! reshape (sum (im2col (A, [3 5 3 2])), [8 5 3 6])); ## Corner case for Matlab compatibility -- bug #46774 %!assert (im2col (1:8, [2 1]), zeros (2, 0)) image-2.16.1/inst/PaxHeaders.61586/imextendedmin.m0000644000000000000000000000006215005110255016412 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imextendedmin.m0000644000175000017500000001355315005110255020017 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} @ imextendedmin (@var{im}, @var{h}) ## @deftypefnx {Function File} {} @ imextendedmin (@var{im}, @var{h}, @var{conn}) ## Caculate the (morphological) extended minima of an image @var{im}. ## ## This function returns a binary image that marks the extended minima ## of the input image @var{im}. Those extended minima are definded as the ## regional minima of the h-minima transform of the input image (which removed ## all regional minima of a depth less then h beforehand). ## ## The input image @var{im} needs to be a real and nonsparse numeric array (of any dimension), ## and the height parameter @var{h} a non-negative scalar number. ## ## The definition of "neighborhood" for this morphological operation can be set ## with the connectivity parameter @var{conn}, ## which defaults to 8 for 2D images, to 26 for 3D images and to ## @code{conn(ndims(n), "maximal")} in general. @var{conn} can be given as scalar value ## or as a boolean matrix (see @code{conndef} for details). ## ## The output is a binary image of same shape as the input image @var{im}. ## ## @seealso{imextendedmax, imhmin, imregionalmin, imreconstruct} ## @end deftypefn ## Algorithm: ## * The 'classical' reference for this morphological "extended maximum" function ## is the book "Morphological Image Analysis" by P. Soille ## (Springer, 2nd edition, 2004), chapter 6.3.4 "Extended and h-extrema". ## It says: "The extended minima EMIN are definded as the regional minima ## of the corresponding h-minima transformation, the extended maxima ## EMAX being definded by duality: ## EMAX_h(f) = RMAX[HMAX_h(f)], ## EMIN_h(f) = RMIN[HMIN_h(f)].". ## * A more easily accessible reference is for example the following ## web page by Régis Clouard: ## https://clouard.users.greyc.fr/Pantheon/experiments/morphology/index-en.html#extremum ## It says: "The extended minima EMIN are definded as the regional minima of ## the corresponding h-minima transformation: ## EMIN_h(f) = RMIN( HMIN_h(f) )" ## (We will call the grayscale image im instead of f.) function bw = imextendedmin (im, h, varargin) ## retrieve input parameters, set default value: if (nargin == 3) conn = varargin{1}; iptcheckconn (conn, "imextendedmin", "CONN"); elseif (nargin == 2) conn = conndef (ndims (im), "maximal"); else print_usage (); endif ## check input parameters: if (! isnumeric (im) || ! isreal (im) || issparse (im) ) error ("imextendedmin: IM must be a real and nonsparse numeric array"); endif if (! isnumeric (h) || ! isscalar (h) || ! isreal (h) || (h<0) ) error ("imextendedmin: H must be a non-negative scalar number"); endif ## do the actual calculation: bw = imregionalmin (imhmin (im, h, conn), conn); endfunction %!shared im0, bw0_h2_out %! im0 = uint8 ([5 5 5 5 5; %! 5 4 3 4 5; %! 5 3 0 3 5; %! 5 4 3 4 5; %! 5 5 5 5 5]); %! bw0_h2_out = false (5); %! bw0_h2_out(3,3) = true; ## test input syntax: %!error imextendedmin () %!error imextendedmin (im0) %!error imextendedmin ("hello", 2) %!error imextendedmin (i.*im0, 2) %!error imextendedmin (sparse (im0), 2) %!error imextendedmin (im0, -2) %!error imextendedmin (im0, 'a') %!error imextendedmin (im0, ones (2)) %!error imextendedmin (im0, 2*i) %!assert (imextendedmin (im0, 2), bw0_h2_out) %!assert (imextendedmin (double (im0), 2), bw0_h2_out) %!assert (imextendedmin (im0, 2, 8), bw0_h2_out) %!assert (imextendedmin (im0, 2, 4), bw0_h2_out) %!assert (imextendedmin (im0, 2, true (3)), bw0_h2_out) ## test output class and shape: %!test %! out = imextendedmin (im0, 2); %! assert (size (out), size (im0)) %! assert (class (out), "logical") %!test %! out = imextendedmin (single (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "logical") %!test %! out = imextendedmin (uint8 (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "logical") %!test %! out = imextendedmin (uint16 (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "logical") %!test %! im = cat (3, im0, im0, im0, im0); %! out = imextendedmin (im, 2); %! assert (size (out), size (im)) ## test calculation result: %!test %! im = 10 .* ones (10); %! im(2:4, 2:4) = 7; %! im(6:8, 6:8) = 2; %! expected_4 = false (10); %! expected_4(6:8, 6:8) = true; %! expected_2 = expected_4; %! expected_2(2:4, 2:4) = true; %! out = imextendedmin (im, 4); %! assert (out, expected_4, eps) %! out = imextendedmin (0.1.*im, 0.4); %! assert (out, expected_4, eps) %! out = imextendedmin (im, 2); %! assert (out, expected_2, eps) %!test %! im2 = 10 .* ones (10); %! im2(2:4, 2:4) = 7; %! im2(6:9, 6:9)=2; %! im2(5, 5)=2; %! im2(6, 7)=10; %! im2(7, 8)=10; %! expected_8 = false (10); %! expected_8(6:9, 6:9) = true; %! expected_8(5, 5) = true; %! expected_8(6, 7) = false; %! expected_8(7, 8) = false; %! expected_4 = expected_8; %! expected_4(2:4, 2:4) = true; %! out2 = imextendedmin (im2, 2); %! assert (out2, expected_8, eps) %! out2 = imextendedmin (im2, 2, 4); %! assert (out2, expected_4, eps) %! out2 = imextendedmin (im2, 2, 8); %! assert (out2, expected_8, eps) image-2.16.1/inst/PaxHeaders.61586/otf2psf.m0000644000000000000000000000006215005110255015143 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/otf2psf.m0000644000175000017500000000764315005110255016553 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} otf2psf (@var{otf}) ## @deftypefnx {Function File} {} otf2psf (@var{otf}, @var{outsize}) ## Compute PSF from OTF. ## ## Returns the Point Spread Function (OTF) of the Optical Transfer ## Function @var{otf}. ## ## The optional argument @var{outsize} defines the size of the returned ## @var{psf}. ## ## @seealso{circshift, ifft2, ifftn, psf2otf} ## @end deftypefn function psf = otf2psf (otf, outsize) if (nargin < 1 || nargin > 2) print_usage (); elseif (! isnumeric (otf)) error ("otf2psf: OTF must be numeric") endif insize = size (otf); psf = ifftn (otf); psf = circshift (psf, floor (insize / 2)); if (nargin > 1) if (! isnumeric (outsize) || ! isvector (outsize)) error ("otf2psf: OUTSIZE must be a numeric vector"); endif insize = size (otf); n = max (numel (outsize), numel (insize)); outsize = postpad (outsize(:), n, 1); insize = postpad (insize(:) , n, 1); pad = (insize - outsize) / 2; if (any (pad < 0)) error ("otf2psf: OUTSIZE must be smaller than or equal than OTF size"); endif prepad = floor (pad); postpad = ceil (pad); idx = arrayfun (@colon, prepad + 1, insize - postpad, "UniformOutput", false); psf = psf(idx{:}); endif endfunction ## We are assuming psf2otf is working correctly %!function otf = rand_otf (varargin) %! otf = complex (rand (varargin{:}), rand (varargin{:})); %!endfunction ## Basic usage, 1, 2, and 3 dimensional %!test %! otf = rand_otf (6, 1); %! assert (otf2psf (otf), circshift (ifft (otf), 3), 1e-16); %!test %! otf = rand_otf (6, 6); %! assert (otf2psf (otf), circshift (ifft2 (otf), [3 3])); %!test %! otf = rand_otf (6, 6, 6); %! assert (otf2psf (otf), circshift (ifftn (otf), [3 3 3])); ## Test when length of some sides are odd %!test %! otf = rand_otf (7, 1); %! assert (otf2psf (otf), circshift (ifft (otf), 3), 2.5e-16); %!test %! otf = rand_otf (7, 7); %! assert (otf2psf (otf), circshift (ifft2 (otf), [3 3]), 2.5e-16); %!test %! otf = rand_otf (6, 7, 8); %! assert (otf2psf (otf), circshift (ifftn (otf), [3 3 4]), 2.5e-16); ## Test the outsize/unpadding option %!test %! otf = rand_otf (7, 1); %! ppsf = circshift (ifft (otf), 3); %! assert (otf2psf (otf, 6), ppsf(1:6), 2.5e-16); %! assert (otf2psf (otf, [6 1]), ppsf(1:6), 2.5e-16); %!test %! otf = rand_otf (7, 7); %! ppsf = circshift (ifft2 (otf), [3 3]); %! assert (otf2psf (otf, [6 1]), ppsf(1:6,4), 2.5e-16); %!test %! otf = rand_otf (6, 7); %! ppsf = circshift (ifft2 (otf), [3 3]); %! assert (otf2psf (otf, [6 6]), ppsf(:,1:6), 2.5e-16); %!error otf2psf ("not a otf") %!error otf2psf (rand_otf (16), 18) %!error otf2psf (rand_otf (16), [14 18]) %!error otf2psf (rand_otf (16), [18 18]) %!error otf2psf (rand_otf (16, 1), 18) ## Some less random tests %!test %! psf = fspecial ("gaussian", 16); %! otf = psf2otf (psf); %! assert (otf2psf (otf), psf, eps); %!test %! psf = rand (16); %! otf = psf2otf (psf); %! assert (otf2psf (otf), psf, 4*eps); %!test %! psf = rand (8); %! otf = psf2otf (psf, [16 16]); %! assert (otf2psf (otf, [8 8]), psf, 2*eps); image-2.16.1/inst/PaxHeaders.61586/imquantize.m0000644000000000000000000000006215005110255015746 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imquantize.m0000644000175000017500000001526015005110255017350 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{quant}, @var{idx}] =} imquantize (@var{img}, @var{levels}) ## @deftypefnx {Function File} {[@dots{}] =} imquantize (@var{img}, @var{levels}, @var{values}) ## Quantize image with multiple threshold levels and values. ## ## This function reduces the number of unique values in @var{img} by ## performing multiple thresholds, one for each element in @var{levels}, ## and assigning it values from @var{values}. ## ## The output @var{quant} is the quantized image, of same size as @var{img} ## and same class as @var{values}. @var{idx} are the indices into ## @var{values} such that @code{@var{quant} == @var{values}(@var{idx})}. ## ## For the simpler case where @var{values} is not defined, it defaults ## to @code{1:N+1} and this function becomes equivalent to: ## ## @example ## out = ones (size (@var{img})); ## for i = 1:numel(@var{levels}) ## out(@var{img} > @var{levels}(i)) += 1; ## endfor ## @end example ## ## So that for example: ## ## @example ## >> img = [1 2 3; 4 5 6; 7 8 9]; ## >> imquantize (img, [3 6]) ## @result{} ans = ## ## 1 1 1 ## 2 2 2 ## 3 3 3 ## @end example ## ## For the more general case, when @var{values} is defined, this function ## is equivalent to: ## ## @example ## @var{levels} = [-Inf @var{levels} Inf]; ## out = zeros (size (@var{img}), class (@var{values})); ## for i = 1:numel(@var{values}) ## out(@var{img} > @var{levels}(i) & @var{img} <= @var{levels}(i+1)) = @var{values}(i); ## endfor ## @end example ## ## So that for example: ## ## @example ## >> img = [1 2 3; 4 5 6; 7 8 9]; ## >> imquantize (img, [3 6], [0 5 8]) ## @result{} ans = ## ## 0 0 0 ## 5 5 5 ## 8 8 8 ## @end example ## ## @seealso{gray2ind, ind2gray, ind2rgb, intlut, label2rgb, lookup, rgb2ind} ## @end deftypefn function [quant, idx] = imquantize (img, levels, values) if (nargin < 2 || nargin > 3) print_usage (); elseif (! isimage (img)) error ("imquantize: IMG must be an image - numeric array with real values"); endif validateattributes (levels, {"numeric"}, {"nondecreasing", "vector"}, "imquantize", "LEVELS"); ## Add eps to levels because lookup does ## "table(idx(i)) <= y(i) < table(idx(i+1))" ## But we want: ## "table(idx(i)) < y(i) <= table(idx(i+1))" levels = double (levels); if (isfloat (img)) ## Beware when img is of class single. The comparison in lookup() ## will be done in the class of the image if it is float. levels += eps (cast (levels, class (img))); else levels += eps (levels); endif idx = lookup ([-Inf; levels(:); Inf], img); if (nargin < 3) quant = idx; else if (! isvector (values) || ! isnumeric (values)) error ("imquantize: VALUES must be a numeric vector") elseif (numel (values) != numel (levels) +1) error ("imquantize: VALUES must have 1 element more than LEVELS"); endif quant = values(idx); endif endfunction %!error %! imquantize (rand (5), [3 4 2 5]) %!error %! imquantize (rand (5), [1 2 3], "foo") %!error %! imquantize (rand (5), [1 2 3 4], 1:6) %!error %! imquantize (rand (5), [1 2 3 4], 1:2) %!test %! img = [-inf 0 10000000; -100000 -3 1/1000000; 5 5 10]; %! [q, q_idx] = imquantize (img, 5); %! assert (q, [1 1 2; 1 1 1; 1 1 2]) %! assert (q_idx, q) %!test %! img = [1:10; 11:20; 21:30; 31:40; 41:50; 51:60; 61:70]; %! %! expected_q = [ %! 0 0 0 0 0 1 1 1 1 1 %! 1 1 1 1 1 5 5 5 5 5 %! 5 5 5 5 5 10 10 10 10 10 %! 20 20 20 20 20 20 20 20 20 20 %! 30 30 30 30 30 30 30 30 30 30 %! 30 30 30 30 30 30 30 30 30 30 %! 15 15 15 15 15 15 15 15 15 15]; %! %! expected_q_idx = [ %! 1 1 1 1 1 2 2 2 2 2 %! 2 2 2 2 2 3 3 3 3 3 %! 3 3 3 3 3 4 4 4 4 4 %! 5 5 5 5 5 5 5 5 5 5 %! 6 6 6 6 6 6 6 6 6 6 %! 6 6 6 6 6 6 6 6 6 6 %! 7 7 7 7 7 7 7 7 7 7]; %! %! [q, q_idx] = imquantize (img, [5 15 25 30 40 60], [0 1 5 10 20 30 15]); %! assert (q, expected_q) %! assert (q_idx, expected_q_idx) %! %! [q, q_idx] = imquantize (single (img), [5 15 25 30 40 60], %! [0 1 5 10 20 30 15]); %! assert (q, expected_q) %! assert (q_idx, expected_q_idx) %! %! [q, q_idx] = imquantize (uint8 (img), [5 15 25 30 40 60], %! [0 1 5 10 20 30 15]); %! assert (q, expected_q) %! assert (q_idx, expected_q_idx) %! %! [q, q_idx] = imquantize (uint8 (img), uint8 ([5 15 25 30 40 60]), %! [0 1 5 10 20 30 15]); %! assert (q, expected_q) %! assert (q_idx, expected_q_idx) %! %! [q, q_idx] = imquantize (uint8 (img), uint8 ([5 15 25 30 40 60]), %! uint8 ([0 1 5 10 20 30 15])); %! assert (q, uint8 (expected_q)) %! assert (q_idx, expected_q_idx) ## Test output class %!test %! img = randi ([0 255], 10, "uint8"); %! [q, q_idx] = imquantize (img, [50 100 150 200]); %! assert (class (q), "double") %! assert (class (q_idx), "double") %! %! [q, q_idx] = imquantize (img, [50 100 150 200], uint16 ([5 7 8 9 2])); %! assert (class (q), "uint16") %! assert (class (q_idx), "double") %! %! [q, q_idx] = imquantize (img, [50 100 150 200], uint8 ([5 7 8 9 2])); %! assert (class (q), "uint8") %! assert (class (q_idx), "double") ## A test with non-ordered pixel values, tested against ordered %!test %! img = [1:10; 11:20; 21:30; 31:40; 41:50; 51:60; 61:70].'; %! r_idx = reshape (randperm (numel (img)), size (img)); %! %! [quant, quant_idx] = imquantize (img, [5 15 25 30 40 60]); %! [quant_r, quant_r_idx] = imquantize (img(r_idx), [5 15 25 30 40 60]); %! %! assert (imquantize (img(r_idx), [5 15 25 30 40 60]), quant(r_idx)) %! assert (quant_r, quant_r_idx) image-2.16.1/inst/PaxHeaders.61586/axes2pix.m0000644000000000000000000000006215005110255015323 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/axes2pix.m0000644000175000017500000001134115005110255016721 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} axes2pix {@var{pixelCoord} =} axes2pix (@var{n}, @var{extent}, @var{axesCoord}) ## Convert axes coordinates to pixel coordinates. ## ## Converts coordinates @var{axesCoord} on a single axis in world units to ## intrinsic pixel coordinates of a 2-D image with @var{n} rows or columns, ## given a two-element real vector @var{extent} representing coordinates ## of centers of the first and last pixel in world units. Intrinsic pixel ## coordinates are always x = 1.0, y = 1.0 in the center of a top left pixel ## and x = number of columns, y = number of rows in the center of a bottom ## right pixel and are continuous. The actual coordinate range spanned by ## the image is larger than @var{exntent} by half a pixel on each side. ## For example if @var{extent} is [1, 2] and @var{n} is 2, the coordinate range ## spanned by the image on the axis is [0.5, 2.5]. ## ## Elements of the real vector @var{axesCoord} are trated as individual points ## on the axis. @var{extent} can also be given in reverse order. ## ## MATLAB compatibility note: in order to produce results similar to MATLAB, ## very few argument validity checks are done. Octave will accept any scalar ## @var{n} (only positive integers make any sense), more-than-two-element vector ## @var{extent} (in which case only the first and last element are used), ## coordinates outside the the coordinate range spanned by the image ## (possibly resulting in negative pixel coordinates) or any matrix ## @var{axesCoord}. It's up to the caller to provide sensible inputs. ## ## @example ## @group ## ## 800x600 pixel image with its top left pixel centered at (100, 0) ## xData = [100, 140]; ## yData = [0, 30]; ## axes2pix(800, xData, [100, 120, 140]) ## @result{} 1.0 400.5 800.0 ## axes2pix(600, yData, [0, 15, 30]) ## @result{} 1.0 300.50 600.0 ## @end group ## @end example ## ## @example ## @group ## ## x-axis reversed ## xData = [140, 100]; ## axes2pix(800, xData, [100, 120, 140]) ## @result{} 800.0 400.5 1.0 ## @end group ## @end example ## ## @end deftypefn function pixelCoord = axes2pix (n, extent, axesCoord) if (nargin != 3) print_usage (); endif if (! isscalar (n)) error ("Octave:invalid-input-arg", "axes2pix: N must be a scalar"); endif if (! isvector (extent)) error ("Octave:invalid-input-arg", "axes2pix: EXTENT must be a vector"); endif if ((n == 1) || extent(1) == extent(end)) pixelCoord = axesCoord - extent(1) + 1; else pixelWidth = (extent(end) - extent(1)) ./ (n - 1); pixelCoord = (axesCoord - extent(1)) ./ pixelWidth + 1; endif endfunction ## test argument checking %!error id=Octave:invalid-fun-call axes2pix () %!error id=Octave:invalid-fun-call axes2pix (42) %!error id=Octave:invalid-fun-call axes2pix (42, [1, 2]) %!error id=Octave:invalid-input-arg axes2pix ([42, 43], [1, 2], [1, 2, 3]) %!error id=Octave:invalid-input-arg axes2pix (42, [1, 2; 3, 4], [1, 2, 3]) ## empty input %!assert (axes2pix (42, [1 42], []), []) ## some numbers from MATLAB axes2pix documentation %!assert (axes2pix (240, [1, 240], 30), 30) %!assert (axes2pix (291, [1, 291], 30), 30) %!assert (axes2pix (240, [400.5, 520], 450), 100) %!assert (axes2pix (291, [-19, 271], 90), 110) ## world width is zero %!assert (axes2pix (1, [1 1], [0, 1, 2, 3, 4]), [0, 1, 2, 3, 4]) %!assert (axes2pix (5, [1 1], [0, 1, 2, 3, 4]), [0, 1, 2, 3, 4]) %!assert (axes2pix (0, [1 1], [0, 1, 2, 3, 4]), [0, 1, 2, 3, 4]) ## world axes are in reverse order %!assert (axes2pix (5, [5 1], [1, 2, 3, 4, 5]), [5, 4, 3, 2, 1]) %!assert (axes2pix (5, [3 -1], [1, 2, 3, 4, 5]), [3, 2, 1, 0, -1]) %!assert (axes2pix (25, [5 1], [1, 2, 3, 4, 5]), [25, 19, 13, 7, 1]) ## single row/column %!assert (axes2pix (1, [1 5], [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5]) %!assert (axes2pix (1, [5 1], [-1, 0, 1, 2.5]), [-5, -4, -3, -1.5]) %!assert (axes2pix (1, [-10 -15], [-1, 0, 1.5]), [10, 11, 12.5]) ## extent as a column vector %!assert (axes2pix (5, [5; 1], [1, 2, 3, 4, 5]), [5, 4, 3, 2, 1]) ## axesCoord as a column vector %!assert (axes2pix (5, [5; 1], [1; 2; 3; 4; 5]), [5; 4; 3; 2; 1]) image-2.16.1/inst/PaxHeaders.61586/imclose.m0000644000000000000000000000006215005110255015213 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imclose.m0000644000175000017500000001114715005110255016615 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imclose (@var{img}, @var{SE}) ## Perform morphological closing. ## ## The matrix @var{img} must be numeric while @var{SE} can be a: ## @itemize @bullet ## @item ## strel object; ## @item ## array of strel objects as returned by `@@strel/getsequence'; ## @item ## matrix of 0's and 1's. ## @end itemize ## ## The closing corresponds to a dilation followed by an erosion of @var{img}, ## using the same @var{SE}, i.e., it is equivalent to: ## @example ## imerode (imdilate (img, se), se); ## @end example ## ## @seealso{imdilate, imerode, imopen} ## @end deftypefn function closed = imclose (img, se) if (nargin != 2) print_usage (); elseif (! isimage (img)) error("imclose: IMG must be a numeric matrix"); endif se = prepare_strel ("imclose", se); ## Perform filtering closed = imerode (imdilate (img, se), se); endfunction %!shared in, out %! in = [ 0 0 0 1 1 1 0 0 1 1 %! 0 1 0 1 1 1 0 0 0 1 %! 1 1 1 1 1 0 0 0 0 0 %! 0 1 1 1 1 0 0 0 0 0 %! 0 0 0 1 0 0 0 0 1 0 %! 0 0 0 0 0 0 0 1 1 1 %! 0 0 0 0 1 0 1 0 1 0 %! 0 0 0 1 1 1 1 1 0 0 %! 0 0 0 0 1 1 1 0 0 0 %! 0 0 0 1 1 1 0 0 0 0]; %! %! out = [ 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 0 0 0 1 %! 1 1 1 1 1 0 0 0 0 1 %! 1 1 1 1 1 0 0 0 0 1 %! 0 0 0 1 1 0 0 0 1 1 %! 0 0 0 1 1 1 1 1 1 1 %! 0 0 0 1 1 1 1 1 1 1 %! 0 0 0 1 1 1 1 1 0 0 %! 0 0 0 1 1 1 1 0 0 0 %! 0 0 0 1 1 1 1 0 0 0]; %!assert (imclose (logical (in), ones (3)), logical (out)); %! %! out = [99 99 16 16 16 73 74 64 64 64 %! 98 88 16 16 16 73 71 64 64 64 %! 93 88 88 61 61 61 68 70 70 70 %! 93 88 88 61 61 61 68 71 71 71 %! 93 93 88 61 61 61 68 75 66 66 %! 79 79 82 90 90 49 49 49 49 66 %! 79 79 82 91 91 48 46 46 46 66 %! 79 79 82 95 97 48 46 46 46 72 %! 18 18 94 96 84 48 46 46 46 59 %! 18 18 100 96 84 50 50 50 50 59]; %!assert (imclose (magic (10), ones (3)), out); %!assert (imclose (uint8 (magic (10)), strel ("square", 3)), uint8 (out)); %! %! ## using a se that will be decomposed in 2 pieces %! out =[ 99 99 88 74 74 74 74 70 70 70 %! 98 93 88 74 74 74 74 70 70 70 %! 93 93 88 74 74 74 74 70 70 70 %! 93 93 88 74 74 74 74 71 71 71 %! 93 93 88 75 75 75 75 75 75 75 %! 93 93 90 90 90 72 72 72 72 72 %! 93 93 91 91 91 72 72 72 72 72 %! 93 93 93 95 97 72 72 72 72 72 %! 94 94 94 96 97 72 72 72 72 72 %! 100 100 100 97 97 72 72 72 72 72]; %!assert (imclose (magic (10), ones(5)), out); %! %! ## using a weird non-symmetric and even-size se %! out =[ 92 99 16 16 16 70 74 58 58 58 %! 98 88 60 73 16 73 69 70 64 58 %! 88 81 88 60 60 60 69 69 70 70 %! 87 87 61 68 61 60 68 69 71 69 %! 86 93 87 61 61 61 68 75 68 69 %! 23 82 89 89 90 45 68 45 68 66 %! 23 23 82 89 91 48 45 45 45 66 %! 79 23 82 95 97 46 48 46 45 72 %! 18 79 94 96 78 50 46 46 46 59 %! 18 18 100 94 94 78 50 50 46 59]; %!assert (imclose (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out); image-2.16.1/inst/PaxHeaders.61586/deconvwnr.m0000644000000000000000000000006215005110255015565 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/deconvwnr.m0000644000175000017500000004543715005110255017200 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {} {} deconvwnr (@var{I}, @var{PSF}) ## @deftypefnx {} {} deconvwnr (@var{I}, @var{PSF}, @var{NSR}) ## Apply Wiener deconvolution filter. ## ## The Wiener deconvolution algorithm estimates the original image ## from a deteriorated image @var{I}. It approximately undoes the ## filtering (e.g. blurring) that deteriorated the original image with ## a linear filter @var{PSF} ("point spread function") and additional ## additive noise. The amount of noise is specified by the parameter ## @var{NSR} ("noise to signal ratio"), also known as "regularisation ## parameter" K. This @var{NSR} parameter is meant as the ratio ## between the noise variance and the, unknown, original image ## variance. The resulting image is optimal in the sense that it ## minimises the mean square error to the original image. ## ## The input image @var{I} can be of class uint8, uint16, int16, ## single, or double. The output image has the same class and size as ## @var{I}. ## ## The filter @var{PSF} should be a float array. It can ## have any size that is smaller or equal to the size of @var{I}. ## ## The noise parameter @var{NSR} must be non-negative and can either ## be given as a single float number, or as a float array ## (in Fourier domain) of the same size as @var{I}. It defaults to 0 ## (zero) which produces the, generally bad quality, direct inverse ## filtering. ## ## @seealso{wiener2} ## @end deftypefn ## The algorithm is taken from the book "Digital Image Processing" ## by R. C. Gonzalez and R. E. Woods, Prentice Hall, 3rd edition 2007. ## Equation (5.8-6) in chapter 5.8 "Minimum Mean Square Error (Wiener) Filtering". function deconvolved = deconvwnr (img, psf, varargin) ## Defaults nsr = 0; if (nargin < 2 || nargin > 3) print_usage (); elseif (nargin == 3) # deconvwnr (I, PSF, NSR) nsr = varargin{1}; ## TODO: The additional Matlab syntax deconvwnr (I, PSF, NCORR, ICORR) ## is not yet implemented. endif if (! isimage (img) || islogical (img)) error ("deconvwnr: I must be an non-logical image"); endif if (! isreal (psf) || ! isfloat (psf)) error("deconvwnr: PSF must be real and float"); elseif (ndims (psf) > ndims (img)) error ("deconvwnr: PSF must have less dimensions than I"); endif ## Check length of each dimension but must allow PSF to have less ## dimensions than IMG. if (any (size (psf) > (size (img)(1:ndims (psf))))) error ("deconvwnr: PSF dimensions length must not be longer than I"); endif if (! isfloat (nsr) || any (nsr < 0)) error ("deconvwnr: NSR must be non-negative and float"); elseif (numel (nsr) != 1 && ! size_equal (nsr, img)) error ("deconvwnr: NSR must be a scalar or array of same size as I"); endif cls = class (img); if (! isa (img, "double")) img = im2double (img); endif ## Allow psf and nsr inputs to be of class single too, but cast them ## to double for calculations. This behavior is Octave-only, Matlab ## requires everything to be double and the user needs to explictely ## cast them to double. if (isa (psf, "single")) psf = double (psf); endif if (isa (nsr, "single")) nsr = double (nsr); endif deconvolved = wiener_deconvolution (img, psf, nsr); deconvolved = imcast (deconvolved, cls); endfunction ## Actually perform the deconvolution. All input must be real and of ## class double. Variables names in capital letters are meant in ## Fourier space. function deconvolved = wiener_deconvolution (im, psf, K) ## This is the equation from the book cited above: ## ## DECONV = 1 / PSF * [ |PSF|^ 2 / ( |PSF|^ 2 + K ) ] * IM ## ## using |PSF|^ 2 = PSF * conj(PSF) this can be transformed to ## ## DECONV = [ conj(PSF) / ( |PSF|^ 2 + K ) ] * IM ## ## (This is the Wiener deconvolution filter given in ## https://en.wikipedia.org/wiki/Wiener_deconvolution#Definition , ## it avoids divisions by maybe-zero-valued PSF). PSF = psf2otf (psf, size (im)); PSF_abs_sq = PSF .* conj (PSF); ## Make sure the denominator is non-zero (PSF_abs_sq is ## non-negative) K(K==0) = eps; FILTER = conj (PSF) ./ (PSF_abs_sq + K); deconvolved = ifftn (FILTER .* fftn (im)); ## Both im and psf are real so the output should also be. deconvolved = real (deconvolved); endfunction %!shared im0, psf0, im0_out, psf1, im2, out2_0, out2_1, im3 %! im0 = ones (5, 5); %! psf0 = ones (3, 3); %! im0_out = 0.11111 .* ones (5, 5); %! psf1 = [1 0 0; 0 1 0; 0 0 1]; %! im2 = checkerboard (2, 2, 2); %! out2_0 = [ %! -0.4713 -0.2786 0.4229 0.5161 -0.2759 -0.4685 0.5131 0.4199; %! -0.4713 -0.2786 0.4229 0.5161 -0.2759 -0.4685 0.5131 0.4199; %! 0.5161 0.4229 -0.2786 -0.4713 0.4199 0.5131 -0.4685 -0.2759; %! 0.5161 0.4229 -0.2786 -0.4713 0.4199 0.5131 -0.4685 -0.2759; %! -0.4713 -0.2786 0.4229 0.5161 -0.2759 -0.4685 0.5131 0.4199; %! -0.4713 -0.2786 0.4229 0.5161 -0.2759 -0.4685 0.5131 0.4199; %! 0.5161 0.4229 -0.2786 -0.4713 0.4199 0.5131 -0.4685 -0.2759; %! 0.5161 0.4229 -0.2786 -0.4713 0.4199 0.5131 -0.4685 -0.2759]; %! out2_1 = [ %! -0.0000 0.8481 0.4288 -0.4194 0.0000 0.2765 0.1373 -0.1392; %! 0.5623 -0.0000 -0.4194 0.1429 0.5623 0.0000 -0.1392 0.4231; %! 0.1429 -0.4194 0 0.5623 0.4231 -0.1392 0 0.5623; %! -0.4194 0.4288 0.8481 0 -0.1392 0.1373 0.2765 0; %! -0.0000 0.8481 0.4288 -0.4194 0.0000 0.2765 0.1373 -0.1392; %! 0.5623 -0.0000 -0.4194 0.1429 0.5623 0.0000 -0.1392 0.4231; %! 0.1429 -0.4194 0 0.5623 0.4231 -0.1392 0 0.5623; %! -0.4194 0.4288 0.8481 0 -0.1392 0.1373 0.2765 0]; %! im3 = rot90 (diag (0.5.*ones (1,8)) + diag (ones(1,7), 1)); ## test input syntax: %!error deconvwnr () %!error deconvwnr (ones (5)) %!assert (deconvwnr (ones (5), ones (3))) %!assert (deconvwnr (ones (5), ones (3), 0.7)) %!assert (deconvwnr (ones (5), ones (3), 0.5 .* ones (5))) %!assert (deconvwnr (ones (5, 5, 5), ones (3))) %!error deconvwnr (ones (5), ones (3), -0.7) %!error %! deconvwnr (ones (5), ones (7)) %!error %! deconvwnr (ones (5, 8, 2), ones (6, 5)) ## test dimensions and classes: %!assert (deconvwnr (im0, psf0), im0_out, 1e-5) %!assert (deconvwnr (im0, single (psf0)), im0_out, 1e-5) %!assert (class (deconvwnr (im0, psf0)), "double") %!assert (deconvwnr (single (im0), psf0), single (im0_out), 1e-5) %!assert (class (deconvwnr (single (im0), psf0)), "single") %!assert (deconvwnr (im2uint8 (im0), psf0), im2uint8 (im0_out)) %!assert (class (deconvwnr (im2uint8 (im0), psf0)), "uint8") %!assert (deconvwnr (im2uint16 (im0), psf0), im2uint16 (im0_out)) %!assert (class (deconvwnr (im2uint16 (im0), psf0)), "uint16") %!assert (deconvwnr (im2int16 (im0), psf0), im2int16 (im0_out)) %!assert (class (deconvwnr (im2int16 (im0), psf0)), "int16") %!error deconvwnr (true (5), ones (3)) ## test calculation results: %!test %! assert (deconvwnr (im0, psf0, 0.01), im0_out, 1e-4) %! assert (deconvwnr (im0, psf1, 0.01), 0.333.*ones (5), 1e-4) %!test %! im1 = magic (5)./25; %! out1_0 = [ %! -0.0820 0.5845 -0.4293 0.2372 -0.0214; %! 0.6241 -0.5877 0.2768 0.0182 -0.0424; %! -0.5481 0.3164 0.0578 -0.2009 0.6637; %! 0.1580 0.0974 -0.1613 0.7033 -0.5085; %! 0.1370 -0.1217 0.5449 -0.4689 0.1976]; %! out1_1 = [ %! -0.2959 -0.1363 0.4038 0.7595 0.1347; %! -0.0191 0.3269 0.8768 0.0559 -0.3748; %! 0.2481 0.7979 0.1731 -0.4517 0.0982; %! 0.7210 0.2904 -0.5305 0.0194 0.3654; %! 0.2116 -0.4132 -0.0575 0.4826 0.6422]; %! assert (deconvwnr (im1, psf0, 0.01), out1_0, 1e-4) %! assert (deconvwnr (im1, psf1, 0.01), out1_1, 1e-4) %!test %! assert (deconvwnr (im2, psf0, 0.01), out2_0, 1e-4) %! assert (deconvwnr (im2, psf1, 0.01), out2_1, 1e-4) %!test %! out3_0_x = [ %! -1.1111 1.0556 -0.4444 -0.1111 0.5556 -0.9444 0.8889 0.0556; %! 1.0556 -0.7778 0.2222 0.5556 -1.2778 1.2222 0.0556 -0.7778; %! -0.4444 0.2222 0.2222 -0.9444 1.2222 -0.2778 -0.4444 0.7222; %! -0.1111 0.5556 -0.9444 0.8889 0.0556 -0.4444 0.3889 -0.4444; %! 0.5556 -1.2778 1.2222 0.0556 -0.7778 0.7222 -0.4444 0.2222; %! -0.9444 1.2222 -0.2778 -0.4444 0.7222 -0.7778 0.5556 0.2222; %! 0.8889 0.0556 -0.4444 0.3889 -0.4444 0.5556 -0.1111 -0.9444; %! 0.0556 -0.7778 0.7222 -0.4444 0.2222 0.2222 -0.9444 1.2222]; %! out3_0_01 = [ %! -0.5064 0.2140 0.1101 -0.0993 0.0297 -0.1942 0.3223 0.0772; %! 0.2140 -0.0659 0.0375 0.0891 -0.4109 0.4783 0.2202 -0.2860; %! 0.1101 0.0375 -0.0525 -0.3208 0.5721 0.0034 -0.1743 0.0939; %! -0.0993 0.0891 -0.3208 0.4624 0.0936 -0.1150 -0.1395 -0.0135; %! 0.0297 -0.4109 0.5721 0.0936 -0.2566 -0.0027 0.1101 0.1341; %! -0.1942 0.4783 0.0034 -0.1150 -0.0027 -0.0659 0.2542 -0.0819; %! 0.3223 0.2202 -0.1743 -0.1395 0.1101 0.2542 -0.3023 -0.3371; %! 0.0772 -0.2860 0.0939 -0.0135 0.1341 -0.0819 -0.3371 0.6794]; %! out3_0_00001 = [ %! -1.1087 1.0520 -0.4419 -0.1112 0.5532 -0.9410 0.8864 0.0557; %! 1.0520 -0.7746 0.2213 0.5537 -1.2742 1.2190 0.0565 -0.7759; %! -0.4419 0.2213 0.2211 -0.9418 1.2196 -0.2767 -0.4433 0.7195; %! -0.1112 0.5537 -0.9418 0.8870 0.0557 -0.4428 0.3864 -0.4425; %! 0.5532 -1.2742 1.2196 0.0557 -0.7755 0.7188 -0.4419 0.2220; %! -0.9410 1.2190 -0.2767 -0.4428 0.7188 -0.7746 0.5544 0.2206; %! 0.8864 0.0565 -0.4433 0.3864 -0.4419 0.5544 -0.1121 -0.9418; %! 0.0557 -0.7759 0.7195 -0.4425 0.2220 0.2206 -0.9418 1.2201]; %! out3_0_3 = [ %! -0.0893 -0.0089 0.0446 -0.0357 -0.0268 0.0268 0.0893 0.0446; %! -0.0089 0.0223 -0.0089 -0.0357 -0.0089 0.1473 0.1161 0.0179; %! 0.0446 -0.0089 -0.0357 -0.0089 0.1607 0.0804 -0.0089 -0.0357; %! -0.0357 -0.0357 -0.0089 0.1652 0.0804 -0.0179 -0.0714 0.0045; %! -0.0268 -0.0089 0.1607 0.0804 -0.0179 -0.0446 0.0446 -0.0000; %! 0.0268 0.1473 0.0804 -0.0179 -0.0446 0.0223 0.0268 -0.0000; %! 0.0893 0.1161 -0.0089 -0.0714 0.0446 0.0268 -0.1071 -0.0446; %! 0.0446 0.0179 -0.0357 0.0045 0.0000 -0.0000 -0.0446 0.1652]; %! out3_1_x = [ %! -0.3333 0.1667 -0.6667 -0.3333 0.3333 0.1667 0.3333 0.1667; %! 0.1667 -0.3333 -0.3333 0.3333 0.1667 0.3333 0.1667 0.3333; %! -0.6667 -0.3333 0.6667 0.1667 0.3333 0.1667 0.3333 0.1667; %! -0.3333 0.3333 0.1667 -0.3333 0.1667 0.3333 0.1667 -0.6667; %! 0.3333 0.1667 0.3333 0.1667 0.6667 0.1667 -0.6667 -0.3333; %! 0.1667 0.3333 0.1667 0.3333 0.1667 -0.3333 -0.3333 0.3333; %! 0.3333 0.1667 0.3333 0.1667 -0.6667 -0.3333 -0.3333 0.1667; %! 0.1667 0.3333 0.1667 -0.6667 -0.3333 0.3333 0.1667 0.6667]; %! out3_1_01 = [ %! -0.1868 0.1548 -0.5994 -0.2997 0.3097 0.1548 0.3097 0.1548; %! 0.1548 -0.2997 -0.2997 0.3097 0.1548 0.3097 0.1548 0.3097; %! -0.5994 -0.2997 0.4965 0.1548 0.3097 0.1548 0.3097 0.1548; %! -0.2997 0.3097 0.1548 -0.1247 0.1548 0.3097 0.1548 -0.5994; %! 0.3097 0.1548 0.3097 0.1548 0.4965 0.1548 -0.5994 -0.2997; %! 0.1548 0.3097 0.1548 0.3097 0.1548 -0.2997 -0.2997 0.3097; %! 0.3097 0.1548 0.3097 0.1548 -0.5994 -0.2997 -0.1868 0.1548; %! 0.1548 0.3097 0.1548 -0.5994 -0.2997 0.3097 0.1548 0.4343]; %! out3_1_00001 = [ %! -0.3331 0.1667 -0.6666 -0.3333 0.3333 0.1667 0.3333 0.1667; %! 0.1667 -0.3333 -0.3333 0.3333 0.1667 0.3333 0.1667 0.3333; %! -0.6666 -0.3333 0.6664 0.1667 0.3333 0.1667 0.3333 0.1667; %! -0.3333 0.3333 0.1667 -0.3330 0.1667 0.3333 0.1667 -0.6666; %! 0.3333 0.1667 0.3333 0.1667 0.6664 0.1667 -0.6666 -0.3333; %! 0.1667 0.3333 0.1667 0.3333 0.1667 -0.3333 -0.3333 0.3333; %! 0.3333 0.1667 0.3333 0.1667 -0.6666 -0.3333 -0.3331 0.1667; %! 0.1667 0.3333 0.1667 -0.6666 -0.3333 0.3333 0.1667 0.6663]; %! out3_1_3 = [ %! -0.0089 0.0625 -0.1250 -0.0625 0.1250 0.0625 0.1250 0.0625; %! 0.0625 -0.0625 -0.0625 0.1250 0.0625 0.1250 0.0625 0.1250; %! -0.1250 -0.0625 0.1339 0.0625 0.1250 0.0625 0.1250 0.0625; %! -0.0625 0.1250 0.0625 0.0982 0.0625 0.1250 0.0625 -0.1250; %! 0.1250 0.0625 0.1250 0.0625 0.1339 0.0625 -0.1250 -0.0625; %! 0.0625 0.1250 0.0625 0.1250 0.0625 -0.0625 -0.0625 0.1250; %! 0.1250 0.0625 0.1250 0.0625 -0.1250 -0.0625 -0.0089 0.0625; %! 0.0625 0.1250 0.0625 -0.1250 -0.0625 0.1250 0.0625 0.0268]; %! assert (deconvwnr (im3, psf0), out3_0_x, 1e-4) %! assert (deconvwnr (im3, psf0, 0.1), out3_0_01, 1e-4) %! assert (deconvwnr (im3, psf0, 0.0001), out3_0_00001, 1e-4) %! assert (deconvwnr (im3, psf0, 3), out3_0_3, 1e-4) %! assert (deconvwnr (im3, psf1), out3_1_x, 1e-4) %! assert (deconvwnr (im3, psf1, 0.1), out3_1_01, 1e-4) %! assert (deconvwnr (im3, psf1, 0.0001), out3_1_00001, 1e-4) %! assert (deconvwnr (im3, psf1, 3), out3_1_3, 1e-4) %!test %! im_rgb = cat (3, im2, im3, magic (8)./64); %! out_rgb_0(:, :, 1) = out2_0; %! out_rgb_0(:, :, 2) = [ %! -0.9255 0.7869 -0.2553 -0.1154 0.3801 -0.6906 0.7000 0.0651; %! 0.7869 -0.5407 0.1534 0.4141 -1.0064 0.9816 0.1222 -0.6335; %! -0.2553 0.1534 0.1343 -0.7453 1.0211 -0.1936 -0.3586 0.5209; %! -0.1154 0.4141 -0.7453 0.7468 0.0675 -0.3247 0.2023 -0.2996; %! 0.3801 -1.0064 1.0211 0.0675 -0.6045 0.4711 -0.2553 0.2032; %! -0.6906 0.9816 -0.1936 -0.3247 0.4711 -0.5407 0.4692 0.1052; %! 0.7000 0.1222 -0.3586 0.2023 -0.2553 0.4692 -0.1868 -0.7477; %! 0.0651 -0.6335 0.5209 -0.2996 0.2032 0.1052 -0.7477 1.0630]; %! out_rgb_0(:, :, 3) = [ %! -0.8118 0.8805 0.8341 -0.7963 -0.6343 0.8222 0.7757 -0.6188; %! 0.5720 -0.4151 -0.3687 0.5565 0.3945 -0.3567 -0.3103 0.3791; %! 0.2007 -0.0438 0.0026 0.1852 0.0232 0.0146 0.0610 0.0078; %! -0.6880 0.7568 0.7104 -0.6725 -0.5105 0.6984 0.6520 -0.4951; %! 0.6079 -0.5392 -0.5856 0.6234 0.7854 -0.5975 -0.6439 0.8008; %! 0.1051 0.0519 0.0983 0.0896 -0.0724 0.1102 0.1566 -0.0879; %! -0.2662 0.4231 0.4696 -0.2817 -0.4437 0.4815 0.5279 -0.4592; %! 0.7317 -0.6629 -0.7093 0.7471 0.9091 -0.7213 -0.7677 0.9246]; %! out_rgb_1(:, :, 1) = out2_1; %! out_rgb_1(:, :, 2) = [ %! -0.3110 0.1654 -0.6593 -0.3297 0.3308 0.1654 0.3308 0.1654; %! 0.1654 -0.3297 -0.3297 0.3308 0.1654 0.3308 0.1654 0.3308; %! -0.6593 -0.3297 0.6418 0.1654 0.3308 0.1654 0.3308 0.1654; %! -0.3297 0.3308 0.1654 -0.3016 0.1654 0.3308 0.1654 -0.6593; %! 0.3308 0.1654 0.3308 0.1654 0.6418 0.1654 -0.6593 -0.3297; %! 0.1654 0.3308 0.1654 0.3308 0.1654 -0.3297 -0.3297 0.3308; %! 0.3308 0.1654 0.3308 0.1654 -0.6593 -0.3297 -0.3110 0.1654; %! 0.1654 0.3308 0.1654 -0.6593 -0.3297 0.3308 0.1654 0.6323]; %! out_rgb_1(:, :, 3) = [ %! -0.0240 0.3338 0.3335 0.0329 0.0344 0.1564 0.3942 0.0913; %! 0.7871 0.6512 -0.5394 -0.2225 0.7287 0.5905 -0.3619 -0.2809; %! 0.1333 -0.7196 0.2335 1.0291 0.0749 -0.5421 0.1728 0.9708; %! -0.2201 0.4109 0.6487 -0.1632 -0.1617 0.4716 0.4713 -0.1048; %! 0.4430 -0.1331 -0.1334 0.4999 0.5014 -0.3106 -0.0727 0.5582; %! -0.6326 0.1654 0.8803 0.2633 -0.6910 0.1047 1.0577 0.2049; %! 0.6191 0.7001 -0.2523 -0.3905 0.5607 0.8776 -0.3130 -0.4489; %! 0.2469 -0.0561 0.1818 0.3038 0.3052 0.0047 0.0043 0.3621]; %! assert (deconvwnr (im_rgb, psf0, 0.01), out_rgb_0, 1e-4) %! assert (deconvwnr (im_rgb, psf1, 0.01), out_rgb_1, 1e-4) %!test %! ## Test that psf and nsr can be of class single, but are usually %! ## internally as doubles. Matlab requires everything all to be %! ## double so this is Matlab incompatible behaviour by design. %! nsr = 0.1; %! psf1_recast = double (single (psf1)); %! nsr_recast = double (single (0.1)); %! deconvolved = deconvwnr (im2, psf1_recast, nsr_recast); %! assert (deconvwnr (im2, single (psf1), single (nsr)), deconvolved, 2e-16) %! assert (deconvwnr (im2, single (psf1), nsr_recast), deconvolved, 2e-16) %! assert (deconvwnr (im2, psf1_recast, single (nsr)), deconvolved, 2e-16) ## show instructive demo: %!demo %! I = phantom (); %! figure, imshow (I); %! title ("Original image"); %! psf = fspecial ("motion", 30, 15); %! blurred = imfilter (I, psf, "conv"); %! figure, imshow (blurred); %! title ("Image with added motion blur"); %! var_noise = 0.00005; %! blurred_noisy = imnoise (blurred, "gaussian", 0, var_noise); %! figure, imshow (blurred_noisy); %! title ("Image with motion blur and added Gaussian noise"); %! estimated_nsr = var_noise / (var(blurred_noisy(:)) - var_noise); %! J = deconvwnr (blurred_noisy, psf, estimated_nsr); %! figure, imshow (J) %! title ({"restored image after Wiener deconvolution", %! "with known PSF and estimated NSR"}); image-2.16.1/inst/PaxHeaders.61586/grayslice.m0000644000000000000000000000006215005110255015542 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/grayslice.m0000644000175000017500000002666415005110255017156 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014-2018 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {} {} grayslice (@var{I}) ## @deftypefnx {} {} grayslice (@var{I}, @var{v}) ## Create indexed image from intensity image using multilevel thresholding. ## ## The intensity image @var{I} is split into multiple threshold levels. ## For regularly spaced intervals, the number of levels can be specified as the ## numeric scalar @var{n} (defaults to 10), which will use the intervals: ## ## @tex ## \def\frac#1#2{{\begingroup#1\endgroup\over#2}} ## $$ \frac{1}{n}, \frac{2}{n}, \dots{}, \frac{n - 1}{n} $$ ## @end tex ## @ifnottex ## @verbatim ## 1 2 n-1 ## -, -, ..., --- ## n n n ## @end verbatim ## @end ifnottex ## ## For irregularly spaced intervals, a numeric vector @var{v} of ## threshold values can be used instead. ## ## The output image will be of class uint8 if the number of levels is ## less than 256, otherwise it will be double. ## ## @seealso{im2bw, gray2ind} ## @end deftypefn function sliced = grayslice (I, n = 10) if (nargin < 1 || nargin > 2) print_usage (); elseif (! isnumeric (n)) error ("Octave:invalid-invalid-input-arg", "grayslice: N and V must be numeric"); endif if isa (I, "int16") ## Convert int16 images to uint16, because that is what Matlab does. I = im2uint16 (I); endif if (isscalar (n) && n >= 1) ## For Matlab compatibility, don't check if N is an integer but ## don't allow n < 1 either. n = double (n); v = (1:(n-1)) ./ n; v = imcast (v, class (I)); elseif ((isvector (n) && ! isscalar (n)) || (isscalar (n) && n > 0 && n <1)) ## For Matlab compatibility, a 0>N>1 is handled like V. v = sort (n(:)); n = numel (v) + 1; ## The range is [0 1] but if the image is floating point we may ## need to increase the range (but never decrease it). if (isfloat (I)) imax = max (I(:)); imin = min (I(:)); v(v < imin) = imin; v(v > imax) = imax; endif else if (isscalar (n) && n <= 0) error ("Octave:invalid-invalid-input-arg", "grayslice: N must be a positive number"); endif error ("Octave:invalid-invalid-input-arg", "grayslice: N and V must be a numeric scalar an vector"); endif sliced_tmp = lookup (v, I); if (n < 256) sliced_tmp = uint8 (sliced_tmp); else ## Indexed images of class double have indices base 1 sliced_tmp++; endif if (nargout < 1) imshow (sliced_tmp, jet (n)); else sliced = sliced_tmp; endif endfunction %!test %! expected = uint8 ([0 4 5 5 9]); %! im = [0 0.45 0.5 0.55 1]; %! assert (grayslice (im), expected) %! assert (grayslice (im, 10), expected) %! assert (grayslice (im, uint8 (10)), expected) %! assert (grayslice (im, [.1 .2 .3 .4 .5 .6 .7 .8 .9]), expected) %!test %! im = [0 0.45 0.5 0.55 1]; %! assert (grayslice (im, 2), uint8 ([0 0 1 1 1])) %! assert (grayslice (im, 3), uint8 ([0 1 1 1 2])) %! assert (grayslice (im, 4), uint8 ([0 1 2 2 3])) %! assert (grayslice (im, [0 0.5 1]), uint8 ([1 1 2 2 3])) %! assert (grayslice (im, [0.5 1]), uint8 ([0 0 1 1 2])) %! assert (grayslice (im, [0.6 1]), uint8 ([0 0 0 0 2])) %!test %% ## non-integer values of N when N>1 are used anyway %! im = [0 .55 1]; %! assert (grayslice (im, 9), uint8 ([0 4 8])) %! assert (grayslice (im, 9.1), uint8 ([0 5 8])) %! assert (grayslice (im, 10), uint8 ([0 5 9])) ## handle unsorted V %!assert (grayslice ([0 .5 1], [0 1 .5]), uint8 ([1 2 3])) %!test %! ## 0 > N > 1 values are treated as if they are V and N=2 %! im = [0 .5 .55 .7 1]; %! assert (grayslice (im, .5), uint8 ([0 1 1 1 1])) %! assert (grayslice (im, .51), uint8 ([0 0 1 1 1])) %! assert (grayslice (im, .7), uint8 ([0 0 0 1 1])) %! assert (grayslice (im, 1), uint8 ([0 0 0 0 0])) %! assert (grayslice (im, 1.2), uint8 ([0 0 0 0 0])) ## V is outside the [0 1] and image range %!assert (grayslice ([0 .5 .7 1], [0 .5 1 2]), uint8 ([1 2 2 4])) ## repeated values in V %!assert (grayslice ([0 .45 .5 .65 .7 1], [.4 .5 .5 .7 .7 1]), %! uint8 ([0 1 3 3 5 6])) ## Image an V with values outside [0 1] range %!assert (grayslice ([-.5 .1 .8 1.2], [-1 -.4 .05 .6 .9 1.1 2]), %! uint8 ([1 3 4 7])) %!assert (grayslice ([0 .5 1], [-1 .5 1 2]), uint8 ([1 2 4])) %!assert (grayslice ([-2 -1 .5 1], [-1 .5 1]), uint8 ([0 1 2 3])) %!test %! sliced = [ %! repmat(0, [26 1]) %! repmat(1, [25 1]) %! repmat(2, [26 1]) %! repmat(3, [25 1]) %! repmat(4, [26 1]) %! repmat(5, [25 1]) %! repmat(6, [26 1]) %! repmat(7, [25 1]) %! repmat(8, [26 1]) %! repmat(9, [26 1]) %! ]; %! sliced = uint8 (sliced(:).'); %! assert (grayslice (uint8 (0:255)), sliced) %!assert (grayslice (uint8 (0:255), 255), uint8 ([0:254 254])) ## Returns class double if n >= 256 and not n > 256 %!assert (class (grayslice (uint8 (0:255), 256)), "double") %!xtest %! assert (grayslice (uint8 (0:255), 256), [1:256]) %! %! ## While the above fails, this passes and should continue to do so %! ## since it's the actual formula in the documentation. %! assert (grayslice (uint8 (0:255), 256), %! grayslice (uint8 (0:255), (1:255)./256)) %!test %! ## Use of threshold in the [0 1] range for images of integer type does %! ## not really work despite the Matlab documentation. It's Matlab %! ## documentation that is wrong, see bug #55059 %! %! assert (grayslice (uint8([0 100 200 255]), [.1 .4 .5]), %! uint8 ([0 3 3 3])) %! assert (grayslice (uint8([0 100 200 255]), [100 199 200 210]), %! uint8 ([0 1 3 4])) %! %! ## P (penny) is a 2d image of class double in [1 255] range %! q = warning ("query", "Octave:data-file-in-path"); %! warning ("off", "Octave:data-file-in-path"); %! load ("penny.mat"); %! warning (q.state, "Octave:data-file-in-path"); %! assert (grayslice (P), repmat (uint8 (9), size (P))) %!function gs = test_grayslice_v (I, v) %! ## This is effectively what grayslice does but slower with a for %! ## loop internally. %! gs = zeros (size (I)); %! for idx = 1:numel (v) %! gs(I >= v(idx)) = idx; %! endfor %! if (numel (v) >= 256) %! gs = gs +1; %! else %! gs = uint8 (gs); %! endif %!endfunction %!test %! q = warning ("query", "Octave:data-file-in-path"); %! warning ("off", "Octave:data-file-in-path"); %! load ("penny.mat"); %! warning (q.state, "Octave:data-file-in-path"); %! %! ## The loaded P in penny.mat is of size 128x128, class double, and %! ## with values in the [1 255] range %! penny_uint8 = uint8 (P); %! penny_double = im2double (penny_uint8); # rescales to [0 1] range] %! %! ## default of N = 10 %! expected = test_grayslice_v (penny_uint8, %! [26 51 77 102 128 153 179 204 230]); %! assert (grayslice (penny_uint8, 10), expected) %! assert (grayslice (penny_uint8), expected) %! %! expected = test_grayslice_v (penny_double, %! [.1 .2 .3 .4 .5 .6 .7 .8 .9]); %! assert (grayslice (penny_double, 10), expected) %! assert (grayslice (penny_double), expected) %!test %! ## For images with more than 2d %! q = warning ("query", "Octave:data-file-in-path"); %! warning ("off", "Octave:data-file-in-path"); %! load ("penny.mat"); %! warning (q.state, "Octave:data-file-in-path"); %! penny_double = im2double (uint8 (P)); %! P_3d = repmat (penny_double, [1 1 3]); %! P_5d = repmat (penny_double, [1 1 3 2 3]); %! %! v = [.3 .5 .7]; %! expected_2d = test_grayslice_v (penny_double, v); %! assert (grayslice (P_3d, v), repmat (expected_2d, [1 1 3])) %! assert (grayslice (P_5d, v), repmat (expected_2d, [1 1 3 2 3])) %!test %! q = warning ("query", "Octave:data-file-in-path"); %! warning ("off", "Octave:data-file-in-path"); %! load ("penny.mat"); %! warning (q.state, "Octave:data-file-in-path"); %! penny_double = uint8 (P); %! %! ## Test that change from uint8 to double happens at 256 exactly %! assert (class (grayslice (penny_double, 255)), "uint8") %! assert (class (grayslice (penny_double, 256)), "double") %! %! ## If returns in class double, it's +1. %! v = [10 150 200]; %! v_long = [v 256:600]; %! assert (double (grayslice (penny_double, v)) +1, %! grayslice (penny_double, v_long)) %!test %! ## If there's a vector for floating point and goes outside the %! ## range, it uses the last index of the vector. %! q = warning ("query", "Octave:data-file-in-path"); %! warning ("off", "Octave:data-file-in-path"); %! load ("penny.mat"); %! warning (q.state, "Octave:data-file-in-path"); %! penny_double = im2double (uint8 (P)); %! v = [.3 .5 .7 2:10]; %! idx_1 = find (penny_double == 1); %! assert (grayslice (penny_double, v)(idx_1), uint8 ([12; 12])) %!error x = grayslice ([1 2; 3 4], 0) %!error x = grayslice ([1 2; 3 4], -1) %!error x = grayslice ([1 2; 3 4], "foo") %!test %! ## test output values for all input classes %! %! klasse = "uint8"; %! im = cast ([intmin(klasse): intmax(klasse)], klasse); %! erg05 = grayslice (im, 0.5); %! first1_erg05 = im(find (erg05)(1)); %! assert (first1_erg05, cast (1, klasse)); %! erg5 = grayslice (im, 5); %! first1_erg5 = im(find (erg5)(1)); %! assert (first1_erg5, cast (51, klasse)); %! ergint5 = grayslice (im, uint8 (5)); %! first1_ergint5 = im(find (ergint5)(1)); %! assert (first1_ergint5, cast (51, klasse)); %! %! klasse = "uint16"; %! im = cast ([intmin(klasse): intmax(klasse)], klasse); %! erg05 = grayslice (im, 0.5); %! first1_erg05 = im(find (erg05)(1)); %! assert (first1_erg05, cast (1, klasse)); %! erg5 = grayslice (im, 5); %! first1_erg5 = im(find (erg5)(1)); %! assert (first1_erg5, cast (13107, klasse)); %! ergint5 = grayslice (im, uint8 (5)); %! first1_ergint5 = im(find (ergint5)(1)); %! assert (first1_ergint5, cast (13107, klasse)); %! %! klasse = "int16"; %! im = cast ([intmin(klasse): intmax(klasse)], klasse); %! erg05 = grayslice (im, 0.5); %! first1_erg05 = im(find (erg05)(1)); %! assert (first1_erg05, cast (-32767, klasse)); %! erg5 = grayslice (im, 5); %! first1_erg5 = im(find (erg5)(1)); %! assert (first1_erg5, cast (-19661, klasse)); %! ergint5 = grayslice (im, uint8 (5)); %! first1_ergint5 = im(find (ergint5)(1)); %! assert (first1_ergint5, cast (-19661, klasse)); %! %! klasse = "single"; %! im = cast ([0:0.001:1], klasse); %! erg05 = grayslice (im, 0.5); %! first1_erg05 = im(find (erg05)(1)); %! assert (first1_erg05, cast (0.5, klasse)); %! erg5 = grayslice (im, 5); %! first1_erg5 = im(find (erg5)(1)); %! assert (first1_erg5, cast (0.2, klasse)); %! ergint5 = grayslice (im, uint8 (5)); %! first1_ergint5 = im(find (ergint5)(1)); %! assert (first1_ergint5, cast (0.2, klasse)); %! %! klasse = "double"; %! im = cast ([0:0.001:1], klasse); %! erg05 = grayslice (im, 0.5); %! first1_erg05 = im(find (erg05)(1)); %! assert (first1_erg05, cast (0.5, klasse)); %! erg5 = grayslice (im, 5); %! first1_erg5 = im(find (erg5)(1)); %! assert (first1_erg5, cast (0.2, klasse)); %! ergint5 = grayslice (im, uint8 (5)); %! first1_ergint5 = im(find (ergint5)(1)); %! assert (first1_ergint5, cast (0.2, klasse)); image-2.16.1/inst/PaxHeaders.61586/xyz2lab.m0000644000000000000000000000006215005110255015153 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/xyz2lab.m0000644000175000017500000001152615005110255016556 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{lab} =} xyz2lab (@var{xyz}) ## @deftypefnx {Function File} {@var{lab_map} =} xyz2lab (@var{xyz_map}) ## Transform a colormap or image from CIE XYZ to CIE L*a*b* color space. ## ## A color in the CIE XYZ color space consists of three values X, Y and Z. ## Those values are designed to be colorimetric, meaning that their values ## do not depend on the display device hardware. ## ## A color in the CIE L*a*b* (or CIE Lab) space consists of lightness L* and ## two color-opponent dimensions a* and b*. The whitepoint is taken as D65. ## The CIE L*a*b* colorspace is also a colorimetric colorspace. It is designed ## to incorporate the human perception of color differences. ## ## Input values of class single and double are accepted. ## The shape and the class of the input are conserved. ## ## The return values of L* are normally in the inteval [0, 100] ## and the values of a* and b* in the interval [-127, 127] ## ## @seealso{lab2xyz, rgb2lab, rgb2hsv, rgb2ind, rgb2ntsc} ## @end deftypefn ## Author: Hartmut Gimpel ## algorithm taken from the following book: ## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015) function lab = xyz2lab (xyz) if (nargin != 1) print_usage (); endif [xyz, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check ("xyz2lab", "XYZ", xyz, 1); # only accept single and double inputs because valid xyz values can be >1 ## normalize with the whitepoint D65 ## (reference: en.wikipedia.org/wiki/Illuminant_D65) D65 = [0.95047, 1, 1.08883]; xyz_D65 = xyz ./ D65; # Matlab truncates to D65_Matlab = [0.9504, 1.0000, 1.0888]; ## transformation xyz -> xyz' epsilon = (6/29)^3; kappa = 1/116 * (29/3)^3; xyz_prime = xyz_D65; mask = xyz_D65 <= epsilon; xyz_prime(mask) = kappa .* xyz_D65(mask) + 16/116; xyz_prime(! mask) = xyz_D65(! mask) .^(1/3); x_prime = xyz_prime(:,1); y_prime = xyz_prime(:,2); z_prime = xyz_prime(:,3); ## transformation xyz' -> lab L = 116 .* y_prime - 16; a = 500 .* (x_prime - y_prime); b = 200 .* (y_prime - z_prime); lab = [L, a, b]; # always return values of type double for Matlab compatibility (exception: type single) lab = colorspace_conversion_revert (lab, cls, sz, is_im, is_nd, is_int, 1); endfunction ## Test pure colors, gray and some other colors ## (This set of test values is taken from the book by Burger.) %!assert (xyz2lab ([0, 0, 0]), [0 0 0], 5e-2) %!assert (xyz2lab ([0.4125, 0.2127, 0.0193]), [53.24, 80.09, 67.20], 5e-2) %!assert (xyz2lab ([0.7700, 0.9278, 0.1385]), [97.14, -21.55, 94.48], 5e-2) %!assert (xyz2lab ([0.3576, 0.7152, 0.1192]), [87.74, -86.18, 83.18], 5e-2) %!assert (xyz2lab ([0.5380, 0.7873, 1.0694]), [91.11, -48.09, -14.13], 5e-2) %!assert (xyz2lab ([0.1804, 0.07217, 0.9502]), [32.30, 79.19, -107.86], 5e-2) %!assert (xyz2lab ([0.5929, 0.28484, 0.9696]), [60.32, 98.24, -60.83], 5e-2) %!assert (xyz2lab ([0.9505, 1.0000, 1.0888]), [100, 0.00, 0.00], 5e-2) %!assert (xyz2lab ([0.2034, 0.2140, 0.2330]), [53.39, 0.00, 0.00], 5e-2) %!assert (xyz2lab ([0.2155, 0.1111, 0.0101]), [39.77, 64.51, 54.13], 5e-2) %!assert (xyz2lab ([0.0883, 0.0455, 0.0041]), [25.42, 47.91, 37.91], 5e-2) %!assert (xyz2lab ([0.02094, 0.0108, 0.00098]), [9.66, 29.68, 15.24], 5e-2) %!assert (xyz2lab ([0.5276, 0.3812, 0.2482]), [68.11, 48.39, 22.83], 5e-2) ## Test tolarant input checking on floats %!assert (xyz2lab ([1.5 1 1]), [100, 82.15, 5.60], 5e-2) %! xyz_map = rand (64, 3); %! assert (lab2xyz (xyz2lab (xyz_map)), xyz_map, 1e-5); %!test %! xyz_img = rand (64, 64, 3); %! assert (lab2xyz (xyz2lab (xyz_img)), xyz_img, 1e-5); ## support sparse input (the only useful xyz value with zeros is black) %!assert (xyz2lab (sparse ([0 0 0])), [0 0 0], 5e-2) ## conserve class of single input %!assert (class (xyz2lab (single([0.5 0.5 0.5]))), 'single') ## Test input validation %!error xyz2lab () %!error xyz2lab (1,2) %!error xyz2lab ({1}) %!error xyz2lab (ones (2,2)) ## Test ND input %!test %! xyz = rand (16, 16, 3, 5); %! lab = zeros (size (xyz)); %! for i = 1:5 %! lab(:,:,:,i) = xyz2lab (xyz(:,:,:,i)); %! endfor %! assert (xyz2lab (xyz), lab) image-2.16.1/inst/PaxHeaders.61586/iradon.m0000644000000000000000000000006215005110255015034 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/iradon.m0000644000175000017500000002413215005110255016434 0ustar00avinoamavinoam00000000000000## Copyright (C) 2010 Alex Opie ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{recon} =} iradon (@var{proj}, @var{theta}, @var{interp}, @var{filter}, @var{scaling}, @var{output_size}) ## ## Performs filtered back-projection on the projections in @var{proj} ## to reconstruct an approximation of the original image. ## ## @var{proj} should be a matrix whose columns are projections of an ## image (or slice). Each element of @var{theta} is used as the angle ## (in degrees) that the corresponding column of @var{proj} was ## projected at. If @var{theta} is omitted, it is assumed that ## projections were taken at evenly spaced angles between 0 and 180 degrees. ## @var{theta} can also be a scalar, in which case it is taken as the ## angle between projections if more than one projection is provided. ## ## @var{interp} determines the type of interpolation that is used ## in the back-projection. It must be one of the types accepted by ## @command{interp1}, and defaults to 'Linear' if it is omitted. ## ## @var{filter} and @var{scaling} determine the type of rho filter ## to apply. See the help for @command{rho_filter} for their use. ## ## @var{output_size} sets the edge length of the output image (it ## is always square). This argument does not scale the image. If it ## is omitted, the length is taken to be ## @group ## 2 * floor (size (proj, 1) / (2 * sqrt (2))). ## @end group ## ## If @var{proj} was obtained using @command{radon}, there is no ## guarantee that the reconstructed image will be exactly the same ## size as the original. ## ## @end deftypefn ## @deftypefn {Function File} {[@var{recon}, @var{filt}] =} iradon (@dots{}) ## ## This form also returns the filter frequency response in the vector ## @var{filt}. ## ## Performs filtered back-projection in order to reconstruct an ## image based on its projections. ## ## Filtered back-projection is the most common means of reconstructing ## images from CT scans. It is a two step process: First, each of ## the projections is filtered with a `rho filter', so named due ## to its frequency domain definition, which is simply |rho|, where ## rho is the radial axis in a polar coordinate system. Second, ## the filtered projections are each `smeared' across the image ## space. This is the back-projection part. ## ## Usage example: ## ## @example ## @group ## P = phantom (); ## projections = radon (P, 1:179); ## reconstruction = iradon (filtered_projections, 1:179, 'Spline', 'Hann'); ## figure, imshow (reconstruction, []) ## @end group ## @end example ## ## @end deftypefn function [recon, filt] = iradon (proj, theta, interp, filter, scaling, output_size) if (nargin == 0) error ("No projections provided to iradon"); endif if (nargin < 6) output_size = 2 * floor (size (proj, 1) / (2 * sqrt (2))); endif if (nargin < 5) || (length (scaling) == 0) scaling = 1; endif if (nargin < 4) || (length (filter) == 0) filter = "Ram-Lak"; endif if (nargin < 3) || (length (interp) == 0) interp = "linear"; endif if (nargin < 2) || (length (theta) == 0) theta = 180 * (0:1:size (proj, 2) - 1) / size (proj, 2); endif if (isscalar (theta)) && (size (proj, 2) != 1) theta = (0:size (proj, 2) - 1) * theta; endif if (length (theta) != size (proj, 2)) error ("iradon: Number of projections does not match number of angles") endif if (!isscalar (scaling)) error ("iradon: Frequency scaling value must be a scalar"); endif if (!length (find (strcmpi (interp, {'nearest', 'linear', 'spline', ... 'pchip', 'cubic'})))) error ("iradon: Invalid interpolation method specified"); endif ## Convert angles to radians theta *= pi / 180; ## First, filter the projections [filtered, filt] = rho_filter (proj, filter, scaling); ## Next, back-project recon = back_project (filtered, theta, interp, output_size); endfunction function recon = back_project (proj, theta, interpolation, dim) ## Make an empty image recon = zeros (dim, dim); ## Zero pad the projections if the requested image ## has a diagonal longer than the projections diagonal = ceil (dim * sqrt (2)) + 1; if (size (proj, 1) < diagonal) diff = 2 * ceil ((diagonal - size (proj, 1)) / 2); proj = padarray (proj, diff / 2); endif ## Create the x & y values for each pixel centre = floor ((dim + 1) / 2); x = (0:dim - 1) - centre + 1; x = repmat (x, dim, 1); y = (dim - 1: -1 : 0)' - centre; y = repmat (y, 1, dim); ## s axis for projections, needed by interp1 s = (0:size (proj, 1) - 1) - floor (size (proj, 1) / 2); ## Sum each projection's contribution for i = 1:length (theta) s_dash = (x * cos (theta (i)) + y * sin (theta (i))); interpolated = interp1 (s, proj (:, i), s_dash (:), ["*", interpolation]); recon += reshape (interpolated, dim, dim); endfor ## Scale the reconstructed values to their original size recon *= pi / (2 * length (theta)); endfunction ## test all input types: %!assert (iradon (single ([0; 1; 1; 0]), 90)); %!assert (iradon (double ([0; 1; 1; 0]), 90)); %!assert (iradon (int8 ([0; 1; 1; 0]), 90)); %!assert (iradon (int16 ([0; 1; 1; 0]), 90)); %!assert (iradon (int32 ([0; 1; 1; 0]), 90)); %!assert (iradon (int64 ([0; 1; 1; 0]), 90)); %!assert (iradon (uint8 ([0; 1; 1; 0]), 90)); %!assert (iradon (uint16 ([0; 1; 1; 0]), 90)); %!assert (iradon (uint32 ([0; 1; 1; 0]), 90)); %!assert (iradon (uint64 ([0; 1; 1; 0]), 90)); %!assert (iradon (logical ([0; 1; 1; 0]), 90)); ## test some valid input syntax: %!assert (iradon (ones (5), 1:5)); %!assert (iradon (ones (5), 1:5, 'nearest')); %!assert (iradon (ones (5), 1:5, 'linear')); %!assert (iradon (ones (5), 1:5, 'spline')); %!assert (iradon (ones (5), 1:5, 'pchip')); %!assert (iradon (ones (5), 1:5, 'linear', 'None')); %!assert (iradon (ones (5), 1:5, 'linear', 'Ram-Lak')); %!assert (iradon (ones (5), 1:5, 'linear', 'Shepp-Logan')); %!assert (iradon (ones (5), 1:5, 'linear', 'Cosine')); %!assert (iradon (ones (5), 1:5, 'linear', 'Hamming')); %!assert (iradon (ones (5), 1:5, 'linear', 'Hann')); %!assert (iradon (ones (5), 1:5, 'linear', 'None', 0.45)); %!assert (iradon (ones (5), 1:5, 'linear', 'None', 0.45, 5)); %!test %! [R, F] = iradon (ones (5), 1:5); %! assert(isvector(F)); %! assert(ismatrix(R)); ## test some invalid input syntax: %!error iradon (); %!error iradon ('xxx'); %!error iradon (ones (2), 'xxx'); %!error iradon (ones (5), 1:5, 'foo'); %!error iradon (ones (5), 1:5, 'linear', 'foo'); %!error iradon (ones (5), 1:5, 'linear', 'none', 'foo'); %!error iradon (ones (5), 1:5, 'linear', 'none', 0.65, 'foo'); ## test numeric values of output: %!test %! A = iradon([0; 1; 1; 0], 90); %! A_matlab = 0.4671 .* ones (2); %! assert (A, A_matlab, 0.02); # as Matlab compatible as iradon outputs currently get ## test numeric values of output for "none" filter: %!test %! A = iradon (radon (ones (2, 2), 0:5), 0:5, "nearest", "none"); %! A_matlab = [1, 1, 1, 1]' * [0.4264, 2.7859, 2.7152, 0.3557]; %! assert (A, A_matlab, 0.0001); ## test numeric values of output for all filter types: %!test %! P = phantom (128); %! R = radon (P, 0:179); %! %! IR = iradon (R, 0:179, [], [], [], 128); # (errors in Matlab because of []s) %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_matlab = 0.3601; %! assert (maxdiff, maxdiff_matlab, 0.002); %! meandiff = mean (abs (D(:))); %! meandiff_matlab = 0.0218; %! assert (meandiff, meandiff_matlab, 0.001); %! %! filtername = "None"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_matlab = 36.5671; %! assert (maxdiff, maxdiff_matlab, 0.0001); %! meandiff = mean (abs (D(:))); %! meandiff_matlab = 24.6302; %! assert (meandiff, meandiff_matlab, 0.0001); %! %! filtername = "Ram-Lak"; # is same as default %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_matlab = 0.3601; %! assert (maxdiff, maxdiff_matlab, 0.002); %! meandiff = mean (abs (D(:))); %! meandiff_matlab = 0.0218; %! assert (meandiff, meandiff_matlab, 0.001); %! %! filtername = "Hamming"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_matlab = 0.5171; %! assert (maxdiff, maxdiff_matlab, 0.005); %! meandiff = mean (abs (D(:))); %! meandiff_matlab = 0.0278; %! assert (meandiff, meandiff_matlab, 0.003); %! %! filtername = "Shepp-Logan"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_matlab = 0.3941; %! assert (maxdiff, maxdiff_matlab, 0.005); %! meandiff = mean (abs (D(:))); %! meandiff_matlab = 0.0226; %! assert (meandiff, meandiff_matlab, 0.0015); %! %! filtername = "Cosine"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_matlab = 0.4681; %! assert (maxdiff, maxdiff_matlab, 0.005); %! meandiff = mean (abs (D(:))); %! meandiff_matlab = 0.0249; %! assert (meandiff, meandiff_matlab, 0.002); %! %! filtername = "Hann"; %! IR = iradon (R, 0:179, [], filtername, [], 128); %! D = P - IR; %! maxdiff = max (abs (D(:))); %! maxdiff_matlab = 0.5334; %! assert (maxdiff, maxdiff_matlab, 0.005); %! meandiff = mean (abs (D(:))); %! meandiff_matlab = 0.0285; %! assert (meandiff, meandiff_matlab, 0.0025); %!demo %! P = phantom (); %! figure, imshow (P, []), title ("Original image") %! projections = radon (P, 0:179); %! reconstruction = iradon (projections, 0:179, 'Spline', 'Hann'); %! figure, imshow (reconstruction, []), title ("Reconstructed image") image-2.16.1/inst/PaxHeaders.61586/imgradientxy.m0000644000000000000000000000006215005110255016264 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imgradientxy.m0000644000175000017500000001076615005110255017674 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Brandon Miles ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{gradX}, @var{gradY}] =} imgradientxy (@var{img}) ## @deftypefnx {Function File} {[@var{gradX}, @var{gradY}] =} imgradientxy (@var{img}, @var{method}) ## Compute the x and y gradients of an image using various methods. ## ## The first input @var{img} is the gray scale image to compute the edges ## on. The second input @var{method} controls the method used to calculate ## the gradients. ## ## The first output @var{gradX} returns the gradient in the x direction. ## The second output @var{gradY} returns the gradient in the y direction. ## ## The @var{method} input argument can be any of the following strings: ## ## @table @asis ## @item @qcode{"sobel"} (default) ## Calculates the gradient using the Sobel approximation to the ## derivatives. ## ## @item @qcode{"prewitt"} ## Calculates the gradient using the Prewitt approximation to the ## derivatives. This method works just like Sobel except a different ## approximation of the gradient is used. ## ## @item @qcode{"central"} or @qcode{"centraldifference"} ## Calculates the gradient using the central difference approximation to the ## derivatives: @code{(x(i-1) - x(i+1))/2}. ## ## @item @qcode{"intermediate"} or @qcode{"intermediatedifference"} ## Calculates the gradient in using the intermediate difference approximation ## to the derivatives: @code{x(i) - x(i+1)}. ## ## @end table ## ## @seealso{edge, imgradient} ## @end deftypefn function [gradX, gradY] = imgradientxy (img, method = "sobel") if (nargin < 1 || nargin > 2) print_usage (); elseif (! isgray (img)) error ("imgradientxy: IMG must be a gray-scale image"); elseif (! ischar (method)) error ("imgradientxy: METHOD must be a string"); endif switch (tolower (method)) case {"sobel", "prewitt"} ker = fspecial (method); # horizontal case {"central", "centraldifference"} ker = [0.5; 0; -0.5]; case {"intermediate", "intermediatedifference"} ker = [1; -1]; otherwise error("imgradientxy: unrecognized METHOD %s", method); endswitch gradX = conv2 (img, ker', "same"); if (nargout > 1) gradY = conv2 (img, ker, "same"); endif endfunction %!test %! A = [0 1 0 %! 1 1 1 %! 0 1 0]; %! %! [gxSobel, gySobel] = imgradientxy (A); %! [gxSobel2, gySobel2] = imgradientxy (A, "Sobel"); %! assert (gxSobel, %! [ 3 0 -3 %! 4 0 -4 %! 3 0 -3]); %! assert (gySobel, %! [ 3 4 3 %! 0 0 0 %! -3 -4 -3]); %! %! ## test default method %! assert(gxSobel, gxSobel2); %! assert(gySobel, gySobel2); %! %! [gxPrewitt, gyPrewitt] = imgradientxy (A, "Prewitt"); %! assert (gxPrewitt, %! [ 2 0 -2 %! 3 0 -3 %! 2 0 -2]); %! assert (gyPrewitt, %! [ 2 3 2 %! 0 0 0 %! -2 -3 -2]); %! %! [gxCd, gyCd] = imgradientxy (A, "CentralDifference"); %! assert (gxCd, %! [ 0.5 0.0 -0.5 %! 0.5 0.0 -0.5 %! 0.5 0.0 -0.5]); %! assert (gyCd, %! [ 0.5 0.5 0.5 %! 0 0 0 %! -0.5 -0.5 -0.5]); %! %! [gxCd, gyCd] = imgradientxy (A, "Central"); %! assert (gxCd, %! [ 0.5 0.0 -0.5 %! 0.5 0.0 -0.5 %! 0.5 0.0 -0.5]); %! assert (gyCd, %! [ 0.5 0.5 0.5 %! 0 0 0 %! -0.5 -0.5 -0.5]); %! %! [gxId, gyId] = imgradientxy(A, "IntermediateDifference"); %! assert (gxId, %! [ 1 -1 0 %! 0 0 -1 %! 1 -1 0]); %! assert (gyId, %! [ 1 0 1 %! -1 0 -1 %! 0 -1 0]); %! %! [gxId, gyId] = imgradientxy(A, "Intermediate"); %! assert (gxId, %! [ 1 -1 0 %! 0 0 -1 %! 1 -1 0]); %! assert (gyId, %! [ 1 0 1 %! -1 0 -1 %! 0 -1 0]); image-2.16.1/inst/PaxHeaders.61586/imresize.m0000644000000000000000000000006215005110255015407 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imresize.m0000644000175000017500000010432615005110255017013 0ustar00avinoamavinoam00000000000000## Copyright (C) 2005 Søren Hauberg ## Copyright (C) 2013 Carnë Draug ## Copyright (C) 2022 Christof Kaufmann ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imresize (@var{im}, @var{scale}) ## @deftypefnx {Function File} {} imresize (@var{im}, [@var{M} @var{N}]) ## @deftypefnx {Function File} {} imresize (@dots{}, @var{method}) ## @deftypefnx {Function File} {} imresize (@dots{}, @dots{}, @var{property}, @var{value}, @dots{}) ## Resize image with interpolation ## ## Scales the image @var{im} by a factor @var{scale} or into the size @var{M} ## rows by @var{N} columns. For example: ## ## @example ## @group ## imresize (im, 1); # return the same image as input ## imresize (im, 1.5); # return image 1.5 times larger ## imresize (im, 0.5); # return image with half the size ## imresize (im, 2); # return image with the double size ## imresize (im, [512 610]); # return image of size 512x610 ## @end group ## @end example ## ## If @var{M} or @var{N} is @code{NaN}, it will be determined automatically so ## as to preserve aspect ratio. ## ## The optional argument @var{method} defines the interpolation method to be ## used. The following methods are available, see below for custom methods. ## @table @code ## @item "nearest", "box" ## Nearest neighbor method. Only the nearest pixel is used. This gives hard ## edges. ## ## @item "linear", "bilinear", "triangle" ## Bilinear interpolation method using the four neighbor pixels. ## ## @item "cubic", "bicubic" (default) ## Bicubic interpolation method using the 16 neighbor pixels. At the borders ## symmetric padding is used. This is the default method. ## @end table ## By default, the @code{cubic} method is used. ## ## For custom interpolation kernels, specify a two-element cell array for ## @var{method}: @{@var{kernel}, @var{size}@}. @var{kernel} must be an ## interpolation kernel function that can handle vector input. It must be zero ## outside -@var{size}/2 <= x <= @var{size}/2. The following example does a ## bilinear interpolation, just as using "bilinear": ## @example ## @group ## im = magic(6); ## lin = @@(x) (1 - abs(x)) .* (abs(x) < 1); ## size = 2; ## imresize(im, 0.5, @{lin, size@}); ## @end group ## @end example ## Note that also for custom kernels anti-aliasing is applied by default. ## ## Additionally the following optional property-value-pairs can be used: ## @table @code ## @item "Antialiasing" ## If this is set to @code{true} and the scale factor in horizontal or vertical ## direction is less than 1, anti-aliasing will used for that direction. This ## means the interpolation kernel is broadened by 1/scale to reduce the ## frequency components that cause aliasing effects. Hence more neighbors than ## described above are used, e. g. "bilinear" with a scale of 0.5 in both ## directions uses 16 neighbors. The default value is @code{true}, except for ## the method "nearest" / "box". ## ## @item "Method" ## The interpolation method as string or a custom interpolation kernel, see ## above. ## ## @item "OutputSize" ## Specify the output size @var{M} rows by @var{N} columns as vector ## [@var{M} @var{N}], see above. ## ## @item "Scale" ## Either a scalar to use the same scale factor for both direction or a vector ## [@var{scale_rows} @var{scale_columns}] to use different scaling factors. ## @end table ## ## Note: Currently there is no special support for categorical images or images ## with indexed colors. ## ## @seealso{imremap, imrotate, interp2} ## @end deftypefn function im = imresize (im, varargin) if (nargin < 2 || nargin > 7) print_usage (); endif antialiasing = []; scale_or_M_N = []; scale = []; # for the property, that can specify [scale_rows, scale_cols] method = []; if (nargin/2 == round (nargin/2)) # even number of inputs (2, 4 or 6) ## imresize (im, scale_or_M_N) ## imresize (im, scale_or_M_N, property1, value1) ## imresize (im, scale_or_M_N, property1, value1, property2, value2) scale_or_M_N = varargin{1}; n_start = 2; else # odd number of inputs (3, 5 or 7) ## imresize (im, property1, value1) # property1 must be Scale or OutputSize ## imresize (im, scale_or_M_N, method) ## imresize (im, scale_or_M_N, method, property1, value1) ## imresize (im, scale_or_M_N, method, property1, value1, property2, value2) if isnumeric(varargin{1}) scale_or_M_N = varargin{1}; method = varargin{2}; n_start = 3; else n_start = 1; endif endif for n = n_start:2:(nargin-1) # process parameter-values pairs if (strcmpi (varargin{n}, "Antialiasing")) antialiasing = varargin{n+1}; elseif (strcmpi (varargin{n}, "Method")) method = varargin{n+1}; elseif (strcmpi (varargin{n}, "OutputSize")) if (! isempty (scale_or_M_N)) error ("imresize: OutputSize must not be specified, when SCALE or [M N] is also specified.") endif scale_or_M_N = varargin{n+1}; elseif (strcmpi (varargin{n}, "Scale")) scale = varargin{n+1}; else error ("imresize: invalid PROPERTY given") endif endfor ## defaults if (isempty (method)) method = "cubic"; elseif (ischar (method)) ## convert to lower case. Replace "box" by "nearest", replace "bicubic" by "cubic" and replace "bilinear" and "triangle" by "linear". method = interp_method (method); endif if (isempty (antialiasing)) if (strcmpi (method, "nearest")) antialiasing = false; else antialiasing = true; endif endif ## check input arguments if (isempty (scale_or_M_N) && isempty (scale)) error ("imresize: Scale or output size must be specified."); elseif (! isempty(scale_or_M_N) && ! isempty (scale)) error ("imresize: Scale and OutputSize must not be specified both.") elseif (! (islogical (antialiasing) || isnumeric (antialiasing)) || ! isscalar (antialiasing)) ## accept also numbers as logical values (even complex, which Matlab does not accept) error ("imresize: Antialiasing must be true, false or a number (0 interpreted as false, otherwise as true).") elseif (! ((isnumeric (im) || islogical (im)) && ! issparse (im) && ! isempty (im))) error ("imresize: IM must be an image") elseif (! isempty (scale_or_M_N) && (! isnumeric (scale_or_M_N) || any (scale_or_M_N <= 0))) error ("imresize: SCALE or [M N] must be numeric positive values") elseif (! isempty (scale_or_M_N) && numel (scale_or_M_N) > 2) error ("imresize: SCALE or [M N] argument must be a scalar or a 2 element vector"); elseif (! isempty (scale_or_M_N) && all (isnan (scale_or_M_N))) error ("imresize: In [M N] only one value may be NaN to maintain aspect ratio.") elseif (! isempty (scale) && (! isnumeric (scale) || any (scale <= 0) || any (isnan (scale)) || numel (scale) > 2)) error ("imresize: Scale must be one or two numeric positive values") elseif (! ischar (method) && ! (iscell (method) && length (method) == 2)) error ("imresize: METHOD must be a string with the interpolation method or a two-element cell array with a custom kernel and size.") endif in_rows = rows (im); in_cols = columns (im); if (isscalar (scale_or_M_N)) scale = scale_or_M_N; scale_or_M_N = []; endif if (isempty (scale_or_M_N)) if (isscalar (scale)) scale_rows = scale; scale_cols = scale; else scale_rows = scale(1); scale_cols = scale(2); endif out_rows = ceil (in_rows * scale_rows); out_cols = ceil (in_cols * scale_cols); else # scale_or_M_N contains output size out_rows = scale_or_M_N(1); out_cols = scale_or_M_N(2); ## maintain aspect ratio if requested if (isnan (out_rows)) out_rows = in_rows * (out_cols / in_cols); elseif (isnan (out_cols)) out_cols = in_cols * (out_rows / in_rows); endif scale_rows = out_rows / in_rows; scale_cols = out_cols / in_cols; out_rows = ceil (out_rows); out_cols = ceil (out_cols); endif ## calculate the new pixel indices in terms of the old pixel indices off_rows = 1 / scale_rows / 2; off_cols = 1 / scale_cols / 2; idx_rows = 0.5 + off_rows + (0:out_rows-1) / scale_rows; idx_cols = 0.5 + off_cols + (0:out_cols-1) / scale_cols; ## trivial cases if (scale_rows == 1 && scale_cols == 1) ## no resizing to do return elseif (ischar (method) && strcmp (method, "nearest") ... && (! antialiasing || (scale_rows >= 1 && scale_cols >= 1))) idx_rows = max (min (idx_rows, in_rows), 1); idx_cols = max (min (idx_cols, in_cols), 1); im = im(round (idx_rows), round (idx_cols), :); return endif ## cast to floating point for accuracy orig_class = class (im); switch orig_class case {"int8", "uint8", "int16", "uint16"} im = single (im); idx_cols = single (idx_cols); idx_rows = single (idx_rows); case {"int32", "uint32", "int64", "uint64"} im = double (im); endswitch ## actual interpolation for scale_and_idx = {scale_cols, scale_rows; idx_cols, idx_rows'; 2, 1} [scale, idx, axis] = scale_and_idx{:}; if scale == 1 continue; endif if (iscell (method)) kernel_size = method{2}; if (scale < 1 && antialiasing) scale_int = 1 / ceil (1 / scale); kernel = @(h) scale_int * method{1} (scale_int * h); kernel_size /= scale_int; else kernel = method{1}; endif elseif (strcmp (method, "nearest")) if (scale < 1) scale_int = 1 / ceil (1 / scale); kernel = @(h) scale_int * box (scale_int * h); kernel_size = 1 / scale_int; else if axis == 1 im = im(round (idx), :, :); else im = im(:, round (idx), :); endif continue; endif elseif (strcmp (method, "linear")) kernel_size = 2; if (scale < 1 && antialiasing) scale_int = 1 / ceil (1 / scale); kernel = @(h) scale_int * triangle (scale_int * h); kernel_size /= scale_int; else kernel = @triangle; endif elseif (strcmp (method, "cubic")) kernel_size = 4; if (scale < 1 && antialiasing) scale_int = 1 / ceil (1 / scale); kernel = @(h) scale_int * cubic (scale_int * h); kernel_size /= scale_int; else kernel = @cubic; endif else error ("imresize: Interpolation method not supported"); endif ## When rounding the output size up, it can happen that some interpolation ## points lie out at the right or the bottom of the input image. For these ## points symmetric padding is very important and also used in Matlab. ## For an example, see tests below with scale 1/3. im = conv_interp_vec (im, idx, kernel, kernel_size, axis, "symmetric"); endfor ## we return image on same class as input im = cast (im, orig_class); endfunction ## box / nearest neighbor interpolation kernel. function w = box (d) w = -0.5 < d & d <= 0.5; endfunction ## linear interpolation kernel. function w = triangle (d) absd = abs (d); absd01 = absd <= 1; w = (1 - absd) .* absd01; ## for |d| <= 1 endfunction ## cubic interpolation kernel with a = -0.5 for MATLAB compatibility. function w = cubic (h) absh = abs (h); absh01 = absh <= 1; absh12 = absh <= 2 & ~absh01; absh_sqr = absh .* absh; absh_cube = absh_sqr .* absh; w = (1.5 * absh_cube - 2.5 * absh_sqr + 1) .* absh01 ... ## for |h| <= 1 + (-0.5 * absh_cube + 2.5 * absh_sqr - 4 * absh + 2) .* absh12; ## for 1 < |h| <= 2 end ## padding by changing indices. Cannot mimic constant value padding, like zero padding function idx = pad_indices (i, sz, method = "symmetric") if strcmp (method, "replicate") idx = max (min (i, sz), 1); elseif strcmp (method, "symmetric") idx = i - 1; m = mod (idx, sz); odd = mod (floor (idx / sz), 2) == 1; idx(odd) = sz - m(odd); idx(!odd) = m(!odd) + 1; elseif strcmp (method, "reflect") idx = i - 1; while (any (idx(:) < 0 | idx(:) >= sz)) idx(idx < 0) = -idx(idx < 0); idx(idx >= sz) = 2*sz - 2 - idx(idx >= sz); endwhile idx += 1; elseif strcmp (method, "circular") idx = mod (i - 1, sz) + 1; else error (['imresize: Invalid argument for PADDING. Valid are "replicate", "symmetric", "reflect", "circular". You gave "', method, '"']) endif endfunction ## interpolation using convolution kernel function out = conv_interp_vec (img, ZI, kernel, kernel_size, axis, padding = "symmetric") ## get indexes and distances idx = floor (ZI); DZ = ZI - idx; pad_size = ceil (kernel_size / 2); pad_border = size (img, axis); ## allocate output out_shape = size (img); out_shape(axis) = length (ZI); out = zeros (out_shape); ## interpolate for shift = 1-pad_size : pad_size h = shift - DZ; idx_padded = pad_indices (idx + shift, pad_border, padding); if axis == 1 out += img(idx_padded, :, :) .* kernel (h); else out += img(:, idx_padded, :) .* kernel (h); endif endfor endfunction ## Test basic features. %!test %! ## Test scaling with 1: %! in = [116 227 153 69 146 194 59 130 139 106 %! 2 47 137 249 90 75 16 24 158 44 %! 155 68 46 84 166 156 69 204 32 152 %! 71 221 137 230 210 153 192 115 30 118 %! 107 143 108 52 51 73 101 21 175 90 %! 54 158 143 77 26 168 113 229 165 225 %! 9 47 133 135 130 207 236 43 19 73]; %! assert (imresize (uint8 (in), 1, "nearest"), uint8 (in)) %! assert (imresize (uint8 (in), 1, "bicubic"), uint8 (in)) %! ## Test nearest neighbour with a scale factor of 2, also by aspect ratio preservation: %! out = [116 116 227 227 153 153 69 69 146 146 194 194 59 59 130 130 139 139 106 106 %! 116 116 227 227 153 153 69 69 146 146 194 194 59 59 130 130 139 139 106 106 %! 2 2 47 47 137 137 249 249 90 90 75 75 16 16 24 24 158 158 44 44 %! 2 2 47 47 137 137 249 249 90 90 75 75 16 16 24 24 158 158 44 44 %! 155 155 68 68 46 46 84 84 166 166 156 156 69 69 204 204 32 32 152 152 %! 155 155 68 68 46 46 84 84 166 166 156 156 69 69 204 204 32 32 152 152 %! 71 71 221 221 137 137 230 230 210 210 153 153 192 192 115 115 30 30 118 118 %! 71 71 221 221 137 137 230 230 210 210 153 153 192 192 115 115 30 30 118 118 %! 107 107 143 143 108 108 52 52 51 51 73 73 101 101 21 21 175 175 90 90 %! 107 107 143 143 108 108 52 52 51 51 73 73 101 101 21 21 175 175 90 90 %! 54 54 158 158 143 143 77 77 26 26 168 168 113 113 229 229 165 165 225 225 %! 54 54 158 158 143 143 77 77 26 26 168 168 113 113 229 229 165 165 225 225 %! 9 9 47 47 133 133 135 135 130 130 207 207 236 236 43 43 19 19 73 73 %! 9 9 47 47 133 133 135 135 130 130 207 207 236 236 43 43 19 19 73 73]; %! assert (imresize (uint8 (in), 2, "nearest"), uint8 (out)) %! assert (imresize (uint8 (in), 2, "neAreST"), uint8 (out)) %! assert (imresize (uint8 (in), [14 NaN], "nearest"), uint8 (out)) %! assert (imresize (uint8 (in), [NaN 20], "nearest"), uint8 (out)) %! ## Test nearest neighbour with a scaling of 2 for x and 1 for y: %! out = [116 116 227 227 153 153 69 69 146 146 194 194 59 59 130 130 139 139 106 106 %! 2 2 47 47 137 137 249 249 90 90 75 75 16 16 24 24 158 158 44 44 %! 155 155 68 68 46 46 84 84 166 166 156 156 69 69 204 204 32 32 152 152 %! 71 71 221 221 137 137 230 230 210 210 153 153 192 192 115 115 30 30 118 118 %! 107 107 143 143 108 108 52 52 51 51 73 73 101 101 21 21 175 175 90 90 %! 54 54 158 158 143 143 77 77 26 26 168 168 113 113 229 229 165 165 225 225 %! 9 9 47 47 133 133 135 135 130 130 207 207 236 236 43 43 19 19 73 73]; %! assert (imresize (uint8 (in), [7 20], "nearest"), uint8 (out)) %! ## Test nearest neighbour with a scaling of 1 for x and 2 for y: %! out = [116 227 153 69 146 194 59 130 139 106 %! 116 227 153 69 146 194 59 130 139 106 %! 2 47 137 249 90 75 16 24 158 44 %! 2 47 137 249 90 75 16 24 158 44 %! 155 68 46 84 166 156 69 204 32 152 %! 155 68 46 84 166 156 69 204 32 152 %! 71 221 137 230 210 153 192 115 30 118 %! 71 221 137 230 210 153 192 115 30 118 %! 107 143 108 52 51 73 101 21 175 90 %! 107 143 108 52 51 73 101 21 175 90 %! 54 158 143 77 26 168 113 229 165 225 %! 54 158 143 77 26 168 113 229 165 225 %! 9 47 133 135 130 207 236 43 19 73 %! 9 47 133 135 130 207 236 43 19 73]; %! assert (imresize (uint8 (in), [14 10], "nearest"), uint8 (out)) %! ## Test equivalence of different input writing styles: %! assert (imresize (uint8 (in), 1.5, "box"), imresize (uint8 (in), 1.5, "MeTHoD", "nearest")) %! assert (imresize (uint8 (in), "Scale", 1.5, "Method", "box"), imresize (uint8 (in), 1.5, {@(h) -0.5 < h & h <= 0.5, 1})) %! assert (imresize (uint8 (in), 1.5, "bicubic"), imresize (uint8 (in), 1.5, "cubic")) %! assert (imresize (uint8 (in), [NaN, size(in,2)*1.5], "bicubic"), imresize (uint8 (in), 1.5, "cubic")) %! assert (imresize (uint8 (in), [size(in,1)*1.5, NaN], "bicubic"), imresize (uint8 (in), 1.5, "cubic")) %! assert (imresize (uint8 (in), "outputsize", [size(in,1)*1.5, NaN], "method", "bicubic"), imresize (uint8 (in), 1.5, "cubic")) %! assert (imresize (uint8 (in), 1.5, "linear"), imresize (uint8 (in), 1.5, "LIneAR")) %! assert (imresize (uint8 (in), 1.5, "linear"), imresize (uint8 (in), 1.5, "triangle")) ## nearest neighbour test. The distance is the same for all neighbours here. %!test %! in = [116 227 153 69 146 194 59 130 139 106 %! 2 47 137 249 90 75 16 24 158 44 %! 155 68 46 84 166 156 69 204 32 152 %! 71 221 137 230 210 153 192 115 30 118 %! 107 143 108 52 51 73 101 21 175 90 %! 54 158 143 77 26 168 113 229 165 225 %! 9 47 133 135 130 207 236 43 19 73 %! 129 60 59 243 64 181 249 56 32 86]; %! ## Check that a pixel from the neighbour locations gets picked. %! out = imresize (in, 0.5, "nearest", "Antialiasing", false); %! for x = 1:columns (out) %! for y = 1:rows (out) %! x_in = 2 * (x-1) + 1; %! y_in = 2 * (y-1) + 1; %! sub = in(y_in:y_in+1, x_in:x_in+1); %! assert (any (any (sub == out(y, x)))) %! endfor %! endfor %! ## Check that with anti-aliasing the mean of the neighbour pixels is used. %! out = imresize (in, 0.5, "nearest", "Antialiasing", true); %! for x = 1:columns (out) %! for y = 1:rows (out) %! x_in = 2 * (x-1) + 1; %! y_in = 2 * (y-1) + 1; %! val = mean (mean (in(y_in:y_in+1, x_in:x_in+1))); %! assert (val, out(y, x)) %! endfor %! endfor %! ## Check that anti-aliasing also works in only y direction. %! out = imresize (in, "Scale", [0.5, 2], "Method", "nearest", "Antialiasing", true); %! for x = 1:columns (out) %! for y = 1:rows (out) %! x_in = floor (0.5 * (x-1) + 1); %! y_in = 2 * (y-1) + 1; %! val = mean (in(y_in:y_in+1, x_in)); %! assert (val, out(y, x)) %! endfor %! endfor %! ## Check that anti-aliasing also works in only x direction. %! out = imresize (in, "Scale", [2, 0.5], "Method", "nearest", "Antialiasing", true); %! for x = 1:columns (out) %! for y = 1:rows (out) %! x_in = 2 * (x-1) + 1; %! y_in = floor (0.5 * (y-1) + 1); %! val = mean (in(y_in, x_in:x_in+1)); %! assert (val, out(y, x)) %! endfor %! endfor ## Test floating point range and and scaling of multi-channel images. %!test %! ## Do not enforce floating point images to be in the [0 1] range (bug #43846): %! assert (imresize (repmat (5, [3 3]), 2), repmat (5, [6 6]), eps*100) %! ## Similarly, do not enforce images to have specific dimensions and only ## expand on the first 2 dimensions: %! assert (imresize (repmat (5, [3 3 2]), 2), repmat (5, [6 6 2]), eps*100) ## Test that scaling a multi-channel image is equivalent to scaling its channels. %!test %! %! for channels = 1:3 %! in = rand (5, 4, channels); %! for method = {"nearest", "bilinear", "bicubic"} %! out = imresize (in, 2, method{1}); %! for i = 1:size (in, 3) %! assert (out(:, :, i), imresize (in(:, :, i), 2, method{1})) %! endfor %! endfor %! endfor ## Test scaling down to a single row %!test %! %! for channels = 1:3 %! in = rand (5, 4, channels); %! out = imresize (in, [1, columns(in)], "nearest", "Antialiasing", true); %! for i = 1:columns (in) %! assert (out(1, i, :), mean (in(:, i, :), 1), 10*eps) %! endfor %! endfor ## Test scaling down to a single column %!test %! %! for channels = 1:3 %! in = rand (5, 4, channels); %! out = imresize (in, [rows(in), 1], "nearest", "Antialiasing", true); %! for i = 1:rows (in) %! assert (out(i, 1, :), mean (in(i, :, :), 2), 10*eps) %! endfor %! endfor ## Test scaling down to a single pixel %!test %! %! for channels = 1:3 %! in = rand (5, 4, channels); %! out = imresize (in, [1, 1], "nearest", "Antialiasing", true); %! assert (out(1, 1, :), mean (mean (in(:, :, :))), 10*eps) %! endfor ## Test linear interpolations against some reference results from matlab. ## The floating point error is less than 1e-13, but for int matlab uses an ## optimized algorithm. So a difference of 1 is acceptable. %!test %! %! in = [116 227 153 69 146 194 59 130 139 106 %! 2 47 137 249 90 75 16 24 158 44 %! 155 68 46 84 166 156 69 204 32 152 %! 71 221 137 230 210 153 192 115 30 118 %! 107 143 108 52 51 73 101 21 175 90 %! 54 158 143 77 26 168 113 229 165 225 %! 9 47 133 135 130 207 236 43 19 73 %! 129 60 59 243 64 181 249 56 32 86]; %! ## Factor 0.91 yields same output size, but interpolation must not be skipped %! out = [115 208 134 100 163 117 101 136 109 103 %! 26 61 149 182 95 53 41 116 73 60 %! 133 101 82 140 167 125 152 71 126 144 %! 88 184 137 164 142 145 110 81 104 108 %! 86 146 109 55 73 110 111 156 153 150 %! 33 104 131 100 130 184 147 97 133 142 %! 84 59 114 164 133 219 120 33 72 81 %! 126 60 104 181 116 218 125 38 77 86]; %! assert (imresize (uint8 (in), 0.91, "bilinear", "Antialiasing", false), uint8 (out), 1) %! ## Factor 1.5, gives an output of size 12 x 15 (without requiring to round the size) %! out = [116 172 215 165 111 82 133 170 171 81 95 132 138 123 106 %! 59 98 138 144 152 152 125 127 119 54 58 89 137 112 75 %! 27 39 62 110 172 202 123 96 78 36 40 68 123 100 62 %! 129 97 64 62 87 119 146 148 128 74 117 154 73 94 134 %! 113 129 136 101 125 162 183 172 151 135 146 139 53 83 135 %! 77 143 195 145 166 197 186 162 146 171 138 92 62 84 113 %! 101 129 149 120 98 81 78 82 91 111 77 56 132 123 95 %! 81 116 147 130 96 61 43 80 119 109 116 132 162 164 158 %! 46 93 139 141 114 80 50 109 168 141 166 189 151 171 200 %! 16 41 77 123 130 123 115 157 204 214 145 69 48 71 98 %! 69 62 61 89 143 174 112 146 202 235 147 46 30 53 80 %! 129 95 60 59 151 213 94 123 192 238 153 52 36 59 86]; %! assert (imresize (uint8 (in), 1.5, "bilinear"), uint8 (out), 1) %! ## Factor 0.5, gives an output of size 4 x 5 (without requiring to round the size) %! out = [ 98 152 126 58 112 %! 129 125 172 146 83 %! 116 96 80 116 164 %! 62 143 146 147 53]; %! assert (imresize (uint8 (in), 0.5, "bilinear", "Antialiasing", false), uint8 (out), 1) %! %! out = [108 136 125 89 107 %! 111 132 143 114 99 %! 106 110 106 127 136 %! 75 124 154 142 75]; %! assert (imresize (uint8 (in), 0.5, "bilinear", "Antialiasing", true), uint8 (out), 1) %! ## Factor 4/3, gives an output of size 10.6667 x 13.3333 rounded up to 11 x 14 %! out = [116 185 199 143 80 117 164 177 76 103 133 135 110 106 %! 45 89 126 148 177 138 114 109 43 52 97 141 78 67 %! 59 57 73 114 177 145 114 96 45 71 99 108 88 85 %! 145 109 76 63 96 146 166 147 93 152 133 47 134 148 %! 82 157 174 137 201 208 186 156 174 145 90 42 111 122 %! 94 143 152 119 119 114 108 107 131 86 80 119 104 101 %! 87 126 139 114 69 49 67 109 106 102 126 167 145 141 %! 48 108 143 135 91 56 89 167 134 177 184 154 199 206 %! 15 44 88 133 129 121 149 204 219 124 55 44 85 92 %! 84 66 67 102 189 132 127 198 237 123 42 34 74 81 %! 129 86 60 82 220 131 108 190 241 128 47 39 79 86]; %! assert (imresize (uint8 (in), 4/3, "bilinear"), uint8 (out), 1) %! ## Define custom bilinear interpolation kernel %! lin = @(x) (1 - abs(x)) .* (abs(x) < 1); %! ## Factor 1/3, gives an output of size 2.6667 x 3.3333 rounded up to 3 x 4 %! out = [ 47 90 24 44 %! 143 51 21 90 %! 60 64 56 86]; %! assert (imresize (uint8 (in), 1/3, "bilinear", "Antialiasing", false), uint8 (out), 1) %! assert (imresize (uint8 (in), 1/3, {lin, 2}, "Antialiasing", false), uint8 (out), 1) %! %! out = [115 131 101 102 %! 114 117 120 121 %! 91 147 116 76]; %! assert (imresize (uint8 (in), 1/3, "bilinear", "Antialiasing", true), uint8 (out), 1) %! assert (imresize (uint8 (in), 1/3, {lin, 2}, "Antialiasing", true), uint8 (out), 1) ## Test bicubic interpolations against some reference results from matlab. ## The floating point error is less than 4e-13, but with integer Matlab rounds ## wrong. So a difference of 1 is acceptable. ## Factor 1.5, gives an output of size 12 x 15 (without requiring to round the size) %!test %! in = [116 227 153 69 146 194 59 130 139 106 %! 2 47 137 249 90 75 16 24 158 44 %! 155 68 46 84 166 156 69 204 32 152 %! 71 221 137 230 210 153 192 115 30 118 %! 107 143 108 52 51 73 101 21 175 90 %! 54 158 143 77 26 168 113 229 165 225 %! 9 47 133 135 130 207 236 43 19 73 %! 129 60 59 243 64 181 249 56 32 86]; %! %! out = [116 187 237 171 94 61 135 191 187 75 91 142 140 124 108 %! 43 92 143 149 164 163 119 123 118 44 38 80 151 118 62 %! 13 21 47 107 195 228 115 81 70 24 19 56 137 105 48 %! 146 98 49 49 71 107 148 159 132 58 124 176 61 85 146 %! 118 139 144 92 116 168 201 188 159 140 167 158 27 69 153 %! 61 151 218 145 174 219 201 164 146 187 148 84 48 76 115 %! 102 132 151 119 90 72 72 72 83 114 60 31 144 130 80 %! 81 121 154 133 87 41 19 67 116 95 108 140 183 180 163 %! 37 95 152 150 117 73 35 108 179 130 174 214 153 176 219 %! 3 29 73 131 136 120 116 162 214 229 147 54 35 62 96 %! 67 54 51 83 153 187 111 141 210 255 149 22 13 42 74 %! 142 99 53 43 164 237 77 103 197 254 159 42 31 59 91]; %! assert (imresize (uint8 (in), 1.5, "bicubic"), uint8 (out), 1) %! ## Factor 0.5, gives an output of size 4 x 5 (without requiring to round the size) %! out = [ 92 164 123 38 118 %! 139 116 188 167 69 %! 121 87 67 108 180 %! 54 153 141 149 42]; %! assert (imresize (uint8 (in), 0.5, "bicubic", "Antialiasing", false), uint8 (out), 1) %! %! out = [105 140 126 81 109 %! 110 134 153 114 93 %! 108 108 94 127 146 %! 67 126 162 149 62]; %! assert (imresize (uint8 (in), 0.5, "bicubic", "Antialiasing", true), uint8 (out), 1) %! ## Factor 4/3, gives an output of size 10.6667 x 13.3333 rounded up to 11 x 14 %! out = [116 203 221 141 62 110 180 191 70 104 143 136 111 106 %! 26 78 126 156 200 139 103 103 33 28 92 158 67 46 %! 51 35 51 112 195 146 101 87 29 57 100 114 81 74 %! 159 110 63 50 82 148 179 152 83 173 147 27 143 170 %! 70 171 189 134 217 226 193 158 186 157 83 25 114 135 %! 91 152 162 116 118 114 102 98 138 65 60 127 92 84 %! 90 130 144 111 52 24 50 101 94 86 129 190 146 135 %! 41 114 157 139 89 37 82 178 125 192 203 154 213 227 %! 4 33 89 141 127 118 151 213 232 119 35 34 81 92 %! 88 61 54 97 203 129 115 203 255 119 18 24 70 81 %! 147 91 43 68 247 125 80 191 255 130 33 37 83 94]; %! assert (imresize (uint8 (in), 4/3, "bicubic"), uint8 (out), 1) %! ## Factor 1/3, gives an output of size 2.6667 x 3.3333 rounded up to 3 x 4 %! out = [ 47 90 24 44 %! 143 51 21 90 %! 60 64 56 86]; %! assert (imresize (uint8 (in), 1/3, "bicubic", "Antialiasing", false), uint8 (out), 1) %! %! out = [115 135 97 101 %! 113 119 124 125 %! 81 157 118 64]; %! assert (imresize (uint8 (in), 1/3, "bicubic", "Antialiasing", true), uint8 (out), 1) ## Reduce the size of a checkerboard, such that it turns into gray %!test %! %! in = checkerboard (1, [2 2]); %! out = [0.5 0.35 %! 0.5 0.35]; %! assert ( imresize (in, 0.5, "bilinear", "Antialiasing", false), out) ## check complex inputs (Matlab allows them) %!test %! %! in = ones (2) + 1i; %! out_nearest = imresize (in, 1.5, "nearest"); %! assert (out_nearest, ones (3) + 1i); %! out_linear = imresize (in, 1.5, "linear"); %! assert (out_linear, ones (3) + 1i); %! out_cubic = imresize (in, 1.5, "cubic"); %! assert (out_cubic, ones (3) + 1i, 1e-14); ## check resizing of 1 pixel rgb images %!test %! %! in = cat (3, 10, 10, 10); %! expected = 10 * ones (2, 2, 3); # consistent with MATLAB behaviour %! %! out_nearest = imresize (in, [2, 2], "nearest"); %! assert (out_nearest, expected); %! out_linear = imresize (in, [2, 2], "linear"); %! assert (out_linear, expected); %! out_cubic = imresize (in, [2, 2], "cubic"); %! assert (out_cubic, expected); ## check resizing of row and col rgb images %!test %! %! in_row = cat(3, [10,6], [10,6], [10,6]); %! %! plane_expected_nearest = [1; 1; 1] * [10, 10, 6, 6]; %! out_expected = cat (3, plane_expected_nearest, plane_expected_nearest, plane_expected_nearest); %! out_nearest = imresize (in_row, [3, 4], "nearest"); %! assert (out_nearest, out_expected); %! %! plane_expected_linear = [1; 1; 1] * [10, 8, 6]; %! out_expected = cat (3, plane_expected_linear, plane_expected_linear, plane_expected_linear); %! out_linear = imresize (in_row, [3, 3], "linear"); %! assert (out_linear, out_expected); %! %! plane_expected_cubic = [1; 1; 1] * [10.27777777777777, 8, 5.72222222222222]; # values from MATLAB %! out_expected = cat (3, plane_expected_cubic, plane_expected_cubic, plane_expected_cubic); %! out_cubic = imresize (in_row, [3, 3], "cubic"); %! assert (out_cubic, out_expected, 1e-13); %! %! in_col = cat(3, [10;6], [10;6], [10;6]); %! %! plane_expected_nearest = [10; 10; 6; 6] * [1, 1, 1]; %! out_expected = cat (3, plane_expected_nearest, plane_expected_nearest, plane_expected_nearest); %! out_nearest = imresize (in_col, [4, 3], "nearest"); %! assert (out_nearest, out_expected); %! %! plane_expected_linear = [10; 8; 6] * [1, 1, 1]; %! out_expected = cat (3, plane_expected_linear, plane_expected_linear, plane_expected_linear); %! out_linear = imresize (in_col, [3, 3], "linear"); %! assert (out_linear, out_expected); %! %! plane_expected_cubic = [10.27777777777777; 8; 5.72222222222222] * [1, 1, 1]; # values from MATLAB %! out_expected = cat (3, plane_expected_cubic, plane_expected_cubic, plane_expected_cubic); %! out_cubic = imresize (in_col, [3, 3], "cubic"); %! assert (out_cubic, out_expected, 1e-13); ## performance tests for scale down and scale up (commented out by default) %!#test %! r_big_int16 = randi(16000, 8000, 8000, "int16"); %! r_big_double = randi(16000, 8000, 8000); %! tic; imresize (r_big_int16, 100/8000); toc; %! tic; imresize (r_big_double, 100/8000); toc; %! %! r_small_int16 = randi(16000, 100, 100, "int16"); %! r_small_double = randi(16000, 100, 100); %! tic; imresize (r_small_int16, 8000/100); toc; %! tic; imresize (r_small_double, 8000/100); toc; ## test for stripes when downscaling with antialiasing (bug #63560) %!test %! im = 0.5 .* ones (30); %! out1 = imresize (im, 0.55, "bilinear", "antialiasing", true); %! out2 = imresize (im, 0.55, "bicubic", "antialiasing", true); %! out3 = imresize (im, 0.55, "nearest", "antialiasing", true); %! assert (std (out1(:)), 0, 10*eps); %! assert (std (out2(:)), 0, 10*eps); %! assert (std (out3(:)), 0, 10*eps); image-2.16.1/inst/PaxHeaders.61586/imrotate.m0000644000000000000000000000006215005110255015404 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imrotate.m0000644000175000017500000003463315005110255017013 0ustar00avinoamavinoam00000000000000## Copyright (C) 2002 Jeff Orchard ## Copyright (C) 2004-2005 Justus H. Piater ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imrotate (@var{imgPre}, @var{theta}, @var{method}, @var{bbox}, @var{extrapval}) ## Rotate image about its center. ## ## Input parameters: ## ## @var{imgPre} a gray-level image matrix ## ## @var{theta} the rotation angle in degrees counterclockwise ## ## The optional argument @var{method} defines the interpolation method ## to be used. All methods supported by @code{interp2} can be used, ## except @qcode{"spline"}, which is not supported by imrotate in ## current versions of core Octave. ## ## In addition, Fourier interpolation by decomposing the rotation ## matrix into 3 shears can be used with the @code{fourier} method. ## By default, the @code{nearest} method is used. ## ## For @sc{matlab} compatibility, the methods @code{bicubic} (same as ## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as ## @code{linear}) are also supported. ## ## @var{bbox} ## @itemize @w ## @item "loose" grows the image to accommodate the rotated image (default). ## @item "crop" rotates the image about its center, clipping any part of the image that is moved outside its boundaries. ## @end itemize ## ## @var{extrapval} sets the value used for extrapolation. The default value ## is 0. This argument is ignored of Fourier interpolation is used. ## ## Output parameters: ## ## @var{imgPost} the rotated image matrix ## ## @var{H} the homography mapping original to rotated pixel ## coordinates. To map a coordinate vector c = [x;y] to its ## rotated location, compute round((@var{H} * [c; 1])(1:2)). ## ## @end deftypefn function [imgPost, H] = imrotate (imgPre, thetaDeg, interp = "nearest", bbox = "loose", extrapval = 0) if (nargin < 2 || nargin > 5) print_usage (); elseif (! isimage (imgPre)) error ("imrotate: IMGPRE must be a grayscale or RGB image.") elseif (! isscalar (thetaDeg) || ! isfinite (thetaDeg)) error("imrotate: THETA must be a finite scalar.") elseif (! ischar (interp)) error("imrotate: interpolation METHOD must be a character array"); elseif (! isscalar (extrapval)) error("imrotate: EXTRAPVAL must be a scalar"); elseif (! ischar (bbox) || ! any (strcmpi (bbox, {"loose", "crop"}))) error("imrotate: BBOX must be 'loose' or 'crop'"); endif interp = interp_method (interp); if (strcmp (interp, "spline")) error("imrotate: spline interpolation is not supported"); endif ## Input checking done. Start working thetaDeg = mod(thetaDeg, 360); # some code below relies on positive angles theta = thetaDeg * pi/180; sizePre = size(imgPre); ## We think in x,y coordinates here (rather than row,column), except ## for size... variables that follow the usual size() convention. The ## coordinate system is aligned with the pixel centers. R = [cos(theta) sin(theta); -sin(theta) cos(theta)]; if (nargin >= 4 && strcmpi(bbox, "crop")) sizePost = sizePre; else ## Compute new size by projecting zero-base image corner pixel ## coordinates through the rotation: corners = [0, 0; (R * [sizePre(2) - 1; 0 ])'; (R * [sizePre(2) - 1; sizePre(1) - 1])'; (R * [0 ; sizePre(1) - 1])' ]; sizePost(2) = round(max(corners(:,1)) - min(corners(:,1))) + 1; sizePost(1) = round(max(corners(:,2)) - min(corners(:,2))) + 1; ## This size computation yields perfect results for 0-degree (mod ## 90) rotations and, together with the computation of the center of ## rotation below, yields an image whose corresponding region is ## identical to "crop". However, we may lose a boundary of a ## fractional pixel for general angles. endif ## Compute the center of rotation and the translational part of the ## homography: oPre = ([ sizePre(2); sizePre(1)] + 1) / 2; oPost = ([sizePost(2); sizePost(1)] + 1) / 2; T = oPost - R * oPre; # translation part of the homography ## And here is the homography mapping old to new coordinates: H = [[R; 0 0] [T; 1]]; ## Treat trivial rotations specially (multiples of 90 degrees): if (mod(thetaDeg, 90) == 0) nRot90 = mod(thetaDeg, 360) / 90; if (mod(thetaDeg, 180) == 0 || sizePre(1) == sizePre(2) || strcmpi(bbox, "loose")) imgPost = rotdim (imgPre, nRot90, [1 2]); return; elseif (mod(sizePre(1), 2) == mod(sizePre(2), 2)) ## Here, bbox is "crop" and the rotation angle is +/- 90 degrees. ## This works only if the image dimensions are of equal parity. imgRot = rotdim (imgPre, nRot90, [1 2]); imgPost = zeros (sizePre, class (imgPre)); hw = min (sizePre(1:2)) / 2 - 0.5; imgPost (round(oPost(2) - hw) : round(oPost(2) + hw), round(oPost(1) - hw) : round(oPost(1) + hw),:) = ... imgRot(round(oPost(1) - hw) : round(oPost(1) + hw), round(oPost(2) - hw) : round(oPost(2) + hw),:); return; else ## Here, bbox is "crop", the rotation angle is +/- 90 degrees, and ## the image dimensions are of unequal parity. This case cannot ## correctly be handled by rot90() because the image square to be ## cropped does not align with the pixels - we must interpolate. A ## caller who wants to avoid this should ensure that the image ## dimensions are of equal parity. endif endif ## Now the actual rotations happen if (strcmpi (interp, "fourier")) in_class = class (imgPre); imgPre = im2double (imgPre); if (isgray(imgPre)) imgPost = imrotate_Fourier(imgPre, thetaDeg, interp, bbox); else # rgb image for i = 3:-1:1 imgPost(:,:,i) = imrotate_Fourier(imgPre(:,:,i), thetaDeg, interp, bbox); endfor endif imgPost = imcast (imgPost, in_class); else imgPost = imperspectivewarp(imgPre, H, interp, bbox, extrapval); endif endfunction function fs = imrotate_Fourier (f, theta, method, bbox) # Get original dimensions. [ydim_orig, xdim_orig] = size(f); # This finds the index coords of the centre of the image (indices are base-1) # eg. if xdim_orig=8, then xcentre_orig=4.5 (half-way between 1 and 8) xcentre_orig = (xdim_orig+1) / 2; ycentre_orig = (ydim_orig+1) / 2; # Pre-process the angle =========================================================== # Whichever 90 degree multiple theta is closest to, that multiple of 90 will # be implemented by rot90. The remainder will be done by shears. # This ensures that 0 <= theta < 360. theta = rem( rem(theta,360) + 360, 360 ); # This is a flag to keep track of 90-degree rotations. perp = 0; if ( theta>=0 && theta<=45 ) phi = theta; elseif ( theta>45 && theta<=135 ) phi = theta - 90; f = rotdim(f,1, [1 2]); perp = 1; elseif ( theta>135 && theta<=225 ) phi = theta - 180; f = rotdim(f,2, [1 2]); elseif ( theta>225 && theta<=315 ) phi = theta - 270; f = rotdim(f,3, [1 2]); perp = 1; else phi = theta; endif if ( phi == 0 ) fs = f; if ( strcmp(bbox,"loose") == 1 ) return; else xmax = xcentre_orig; ymax = ycentre_orig; if ( perp == 1 ) xmax = max([xmax ycentre_orig]); ymax = max([ymax xcentre_orig]); [ydim xdim] = size(fs); xpad = ceil( xmax - (xdim+1)/2 ); ypad = ceil( ymax - (ydim+1)/2 ); fs = padarray (fs, [ypad xpad]); endif xcentre_new = (size(fs,2)+1) / 2; ycentre_new = (size(fs,1)+1) / 2; endif else # At this point, we can assume -451) = 1; fs(fs<0) = 0; endif endfunction %!test %! ## Verify minimal loss across six rotations that add up to 360 +/- 1 deg.: %! methods = { "nearest", "bilinear", "bicubic", "Fourier" }; %! angles = [ 59 60 61 ]; %! tolerances = [ 7.4 8.5 8.6 # nearest %! 3.5 3.1 3.5 # bilinear %! 2.7 2.0 2.7 # bicubic %! 2.7 1.6 2.8 ]/8; # Fourier %! %! # This is peaks(50) without the dependency on the plot package %! x = y = linspace(-3,3,50); %! [X,Y] = meshgrid(x,y); %! x = 3*(1-X).^2.*exp(-X.^2 - (Y+1).^2) ... %! - 10*(X/5 - X.^3 - Y.^5).*exp(-X.^2-Y.^2) ... %! - 1/3*exp(-(X+1).^2 - Y.^2); %! %! x -= min(x(:)); # Fourier does not handle neg. values well %! x = x./max(x(:)); %! for m = 1:(length(methods)) %! y = x; %! for i = 1:5 %! y = imrotate(y, 60, methods{m}, "crop", 0); %! end %! for a = 1:(length(angles)) %! assert(norm((x - imrotate(y, angles(a), methods{m}, "crop", 0)) %! (10:40, 10:40)) < tolerances(m,a)); %! endfor %! endfor %!xtest %! ## Verify exactness of near-90 and 90-degree rotations: %! X = rand(99); %! for angle = [90 180 270] %! for da = [-0.1 0.1] %! Y = imrotate(X, angle + da , "nearest", :, 0); %! Z = imrotate(Y, -(angle + da), "nearest", :, 0); %! assert(norm(X - Z) == 0); # exact zero-sum rotation %! assert(norm(Y - imrotate(X, angle, "nearest", :, 0)) == 0); # near zero-sum %! endfor %! endfor %!test %! ## Verify preserved pixel density: %! methods = { "nearest", "bilinear", "bicubic", "Fourier" }; %! ## This test does not seem to do justice to the Fourier method...: %! tolerances = [ 4 2.2 2.0 209 ]; %! range = 3:9:100; %! for m = 1:(length(methods)) %! t = []; %! for n = range %! t(end + 1) = sum(imrotate(eye(n), 20, methods{m}, :, 0)(:)); %! endfor %! assert(t, range, tolerances(m)); %! endfor %!test %! a = reshape (1:18, [2 3 3]); %! %! a90(:,:,1) = [5 6; 3 4; 1 2]; %! a90(:,:,2) = a90(:,:,1) + 6; %! a90(:,:,3) = a90(:,:,2) + 6; %! %! a180(:,:,1) = [6 4 2; 5 3 1]; %! a180(:,:,2) = a180(:,:,1) + 6; %! a180(:,:,3) = a180(:,:,2) + 6; %! %! am90(:,:,1) = [2 1; 4 3; 6 5]; %! am90(:,:,2) = am90(:,:,1) + 6; %! am90(:,:,3) = am90(:,:,2) + 6; %! %! assert (imrotate (a, 0), a); %! assert (imrotate (a, 90), a90); %! assert (imrotate (a, -90), am90); %! assert (imrotate (a, 180), a180); %! assert (imrotate (a, -180), a180); %! assert (imrotate (a, 270), am90); %! assert (imrotate (a, -270), a90); %! assert (imrotate (a, 360), a); ## Test special case of 90 degrees rotation, when image is not square ## but sides are of even length. Test both grayscale and RGB. %!test %! # bug #53309 %! in = ones (2, 4); %! out = [0 1 1 0; 0 1 1 0]; %! assert (imrotate (in, 90, "nearest", "crop"), out) %! assert (imrotate (repmat (in, [1 1 3]), 90, "nearest", "crop"), %! repmat (out, [1 1 3])) ## When rotating RGB images, rotate all 3 channels. Also test all the ## special cases we have internally. %!test %! ## bug #53309 %! rgbs = { %! rand(5, 4, 3), # normal path, does interpolation %! rand(4, 4, 3), # rows and columns, same number, simple rotdim %! rand(4, 6, 3), # rows and columns differents, but of length even %! }; %! for rgb_i = 1:numel(rgbs) %! rgb = rgbs{rgb_i}; %! rot = imrotate (rgb, 90, "nearest", "crop"); %! for i = 1:3 %! assert (rot(:,:,i), imrotate (rgb(:,:,i), 90, "nearest", "crop")) %! endfor %! ## same check but with an integer class %! rgb = im2uint8 (rgb); %! rot = imrotate (rgb, 90, "nearest", "crop"); %! assert (class (rgb), class (rot)) %! for i = 1:3 %! assert (rot(:,:,i), imrotate (rgb(:,:,i), 90, "nearest", "crop")) %! endfor %! endfor image-2.16.1/inst/PaxHeaders.61586/imtransform.m0000644000000000000000000000006215005110255016121 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imtransform.m0000644000175000017500000004140015005110255017516 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Pantxo Diribarne ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{IM_OUT} =} imtransform (@var{IM_IN}, @var{T}) ## @deftypefnx {Function File} {@var{IM_OUT} =} imtransform (@var{IM_IN}, @var{T}, @var{interp}) ## @deftypefnx {Function File} {@var{IM_OUT} =} imtransform (@dots{}, @var{prop}, @var{val}) ## @deftypefnx {Function File} {[@var{IM_OUT}, @var{xdata}, @var{ydata}] =} imtransform (@dots{}) ## Transform image. ## ## Given an image @var{IM_IN}, return an image @var{IM_OUT} ## resulting from the forward transform defined in the transformation ## structure @var{T}. An additional input argument @var{interp}, ## 'bicubic', 'bilinear' (default) or 'nearest', ## specifies the interpolation method to be used. Finally, the ## transform can be tuned using @var{prop}/@var{val} pairs. The ## following properties @var{prop} are supported: ## ## @table @asis ## @item "udata" ## Specifies the input space horizontal limits. @var{val} must be a two ## elements vector [minval maxval] in ascending order. ## Default: [1 columns(@var{IM_IN})] ## ## @item "vdata" ## Specifies the input space vertical limits. @var{val} must be a two ## elements vector [minval maxval] in ascending order. ## Default: [1 rows(@var{IM_IN})] ## ## @item "xdata" ## Specifies the required output space horizontal limits. @var{val} must ## be a two elements vector [first_col last_col] represents the coordinates ## of the first column and the last column of output image in the world ## coordinate system. ## Note: xdata can be in descending order, which causes a horizontal flip of ## the output image. ## Default: estimated using udata, vdata and findbounds function. ## ## @item "ydata" ## Specifies the required output space vertical limits. @var{val} must ## be a two elements vector [first_row last_row] represents the coordinates ## of the first row and the last row of output image in the world ## coordinate system. ## Note: ydata can be in descending order, which cause a vertical flip of ## the output image. ## Default: estimated using udata, vdata and findbounds function. ## ## @item "xyscale" ## Specifies the size of pixels in the output space. If a scalar ## is provided, both vertical and horizontal dimensions are scaled the ## same way. If @var{val} is a two element vector, it must indicate ## consecutively width and height of the output pixels. The default is ## to use the width and height of input pixels provided ## that it does not lead to a too large output image. ## ## @item "size" ## Size of the output image (1-by-2 vector). Overrides the effect of ## "xyscale" property. ## ## @item "fillvalues" ## Color of the areas where no interpolation is possible, e.g. when ## coordinates of points in the output space are out of the limits of ## the input space. @var{val} must be coherent with the input image ## format: for grayscale and indexed images (2D) @var{val} must be ## scalar, for RGB (n-by-m-by-3) @var{val} must be a 3 element vector. ## ## @end table ## ## The actual output limits, @var{xdata} and @var{ydata} vectors, ## are returned respectively as second and third output variables. ## @seealso{maketform, cp2tform, tforminv, tformfwd, findbounds} ## @end deftypefn ## Author: Pantxo Diribarne function [varargout] = imtransform (im, T, varargin) if (nargin < 2) print_usage (); elseif (! istform (T)) error ("imtransform: T must be a transformation structure (see `maketform')"); endif ## Parameters interp = "linear"; imdepth = size (im, 3); maximsize = [20000 20000]; udata = [1; columns(im)]; vdata = [1; rows(im)]; xdata = ydata = []; xyscale = []; imsize = []; fillvalues = zeros (1, imdepth); xdir = 1; ydir = 1; if (isempty (varargin)) xydata = findbounds (T, [udata vdata]); xdata = xydata(:,1); ydata = xydata(:,2); else ## interp if (floor (numel (varargin)/2) != numel (varargin)/2) allowed = {"bicubic", "bilinear", "nearest"}; tst = strcmp (varargin{1}, allowed); if (!any (tst)) error ("imtransform: expect one of %s as interp method", disp (allowed)); else interp = {"pchip", "linear", "nearest"}{find (tst)}; endif varargin = varargin(2:end); endif ## options allowed = {"udata", "vdata", "xdata", "ydata", ... "xyscale", "size", "fillvalues"}; props = varargin(1:2:end); vals = varargin(2:2:end); np = numel (props); if (!all (cellfun (@ischar, props))) error ("imtransform: expect property/value pairs."); endif props = tolower (props); tst = cellfun (@(x) any (strcmp (x, allowed)), props); if (!all (tst)) error ("imtransform: unknown property %s", disp (props{!tst})); endif ## u(vxy)data iolims = allowed(1:4); for ii = 1:numel (iolims) tst = cellfun (@(x) any (strcmp (x, iolims{ii})), props); if (any (tst)) prop = props{find (tst)(1)}; val = vals{find (tst)(1)}; if (isnumeric (val) && numel (val) == 2) if (isrow (val)) val = val'; endif eval (sprintf ("%s = val;", prop), "error (\"imtransform: %s\n\", lasterr ());"); else error ("imtransform: expect 2 elements real vector for %s", prop) endif endif endfor # checking that udata and vdata are in ascending order if (udata(1) > udata(2) || vdata(1) > vdata(2)) error ("imtransform: udata and vdata should be in ascending order") endif if (isempty (xdata) && isempty (ydata)) xydata = findbounds (T, [udata vdata]); xdata = xydata(:,1); ydata = xydata(:,2); elseif (isempty (xdata)) xydata = findbounds (T, [udata vdata]); xdata = xydata(:,1); elseif (isempty (ydata)) xydata = findbounds (T, [udata vdata]); ydata = xydata(:,2); endif # handle descending order of xdata, ydata(bug #59953) if (diff (xdata) < 0) xdata = [xdata(2); xdata(1)]; xdir = -1; endif if (diff (ydata) < 0) ydata = [ydata(2); ydata(1)]; ydir = -1; endif ## size and xyscale tst = strcmp ("size", props); if (any (tst)) val = vals{find (tst)(1)}; if (isnumeric (val) && numel (val) == 2 && all (val > 0)) imsize = val; else error ("imtransform: expect 2 elements real vector for size"); endif elseif (any (tst = strcmp ("xyscale", props))) val = vals{find (tst)(1)}; if (isnumeric (val) && all (val > 0)) if (numel (val) == 1) xyscale(1:2) = val; elseif (numel (val) == 2) xyscale = val; else error ("imtransform: expect 1 or 2 element(s) real vector for xyscale"); endif else error ("imtransform: expect 1 or 2 elements real vector for xyscale"); endif endif ## Fillvalues tst = strcmp ("fillvalues", props); if (any (tst)) val = vals{find (tst)(1)}; if (isnumeric (val) && numel (val) == 1) fillvalues(1:end) = val; elseif (isnumeric (val) && numel (val) == 3) fillvalues = val; else error ("imtransform: expect 1 or 3 elements real vector for `fillvalues'"); endif endif endif ## Output/Input pixels if (isempty (imsize)) if (isempty (xyscale)) nx = columns (im); ny = rows (im); xyscale = [(diff (udata) / (nx - 1)), ... (diff (vdata) / (ny - 1))]; endif xscale = xyscale(1); yscale = xyscale(2); xsize = ceil (diff (xdata) / xscale) + 1; ysize = ceil (diff (ydata) / yscale) + 1; ## xyscale takes precedence: recompute x/ydata considering roundoff errors xdata(2) = xdata(1) + (xsize - 1) * xscale; ydata(2) = ydata(1) + (ysize - 1) * yscale; if (xsize > maximsize(2) || ysize > maximsize(1)) if (xsize >= ysize) scalefactor = (diff (xdata) / maximsize(2)) / xscale; else scalefactor = (diff (ydata) / maximsize(1)) / yscale; endif xscale *= scalefactor; yscale *= scalefactor; xsize = floor (diff (xdata) / xscale); ysize = floor (diff (ydata) / yscale); warning ("imtransform: output image two large, adjusting the largest dimension to %d", maximsize); endif imsize = [ysize xsize]; endif [xx yy] = meshgrid (linspace (xdata(1), xdata(2), imsize(2)), linspace (ydata(1), ydata(2), imsize(1))); [uu vv] = meshgrid (linspace (udata(1), udata(2), size(im)(2)), linspace (vdata(1), vdata(2), size(im)(1))); ## Input coordinates [uui, vvi] = tforminv (T, reshape (xx, numel (xx), 1), reshape (yy, numel (yy), 1)); uui = reshape (uui, size (xx)); vvi = reshape (vvi, size (yy)); ## Interpolation for layer = 1:imdepth imout(:,:,layer) = interp2 (uu, vv, im(:,:,layer), ... uui, vvi, interp, fillvalues(layer)); if (xdir == -1) imout(:,:,layer) = fliplr (imout(:,:,layer)); endif if (ydir == -1) imout(:,:,layer) = flipud (imout(:,:,layer)); endif endfor if (nargout != 0) # revert xdata and ydata to original values if (xdir == -1) xdata = [xdata(2); xdata(1)]; endif if (ydir == -1) ydata = [ydata(2); ydata(1)]; endif varargout = {imout, xdata(:).', ydata(:).'}; endif endfunction %!demo %! ## Various linear transforms %! figure (); %! im = [checkerboard(20, 2, 4); checkerboard(40, 1, 2)]; %! %input space corners %! incp = [1 1; 160 1; 160 160; 1 160]; %! udata = [min(incp(:,1)) max(incp(:,1))]; %! vdata = [min(incp(:,2)) max(incp(:,2))]; %! subplot (2,3,1); %! imshow (im) %! hold on %! plot (incp(:,1), incp(:,2), 'ob') %! axis on %! xlabel ('Original') %! %! % Translation and scaling %! outcp = incp * 2; %! outcp(:,1) += 200; %! outcp(:,2) += 500; %! T = maketform ('affine', incp(1:3,:), outcp(1:3,:)); %! subplot (2,3,2); %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, %! 'vdata', vdata, 'fillvalues', 1); %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) %! set (gca, 'xlim', xdata, 'ylim', ydata) %! axis on, hold on, xlabel ('Translation / Scaling'); %! plot (outcp(:,1), outcp(:,2), 'or') %! %! % Shear %! outcp = [1 1; 160 1; 140 160; -19 160]; % affine only needs 3 control points %! T = maketform ('affine', incp(1:3,:), outcp(1:3,:)); %! subplot (2,3,3); %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, %! 'vdata', vdata, 'fillvalues', 1); %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) %! set (gca, 'xlim', xdata, 'ylim', ydata) %! axis on, hold on, xlabel ('Shear'); %! plot (outcp(:,1), outcp(:,2), 'or') %! %! % Rotation %! theta = pi/4; %! T = maketform ('affine', [cos(theta) -sin(theta); ... %! sin(theta) cos(theta); 0 0]); %! outcp = tformfwd (T, incp); %! subplot (2,3,4); %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, %! 'vdata', vdata, 'fillvalues', 1 ); %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) %! set (gca, 'xlim', xdata, 'ylim', ydata) %! axis on, hold on, xlabel ('Rotation'); %! plot (outcp(:,1), outcp(:,2), 'or') %! %! % Reflection around x axis %! outcp = incp; %! outcp(:,2) *= -1; %! T = cp2tform (incp, outcp, 'similarity'); %! subplot (2,3,5); %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, %! 'vdata', vdata, 'fillvalues', 1 ); %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) %! set (gca, 'xlim', xdata, 'ylim', ydata) %! axis on, hold on, xlabel ('Reflection'); %! plot (outcp(:,1), outcp(:,2), 'or') %! %! % Projection %! outcp = [1 1; 160 -40; 220 220; 12 140]; %! T = maketform ('projective', incp, outcp); %! subplot (2,3,6); %! [im2 xdata ydata] = imtransform (im, T, 'udata', udata, %! 'vdata', vdata, 'fillvalues', 1 ); %! imh = imshow (im2); set (imh, 'xdata', xdata, 'ydata', ydata) %! set (gca, 'xlim', xdata, 'ylim', ydata) %! axis on, hold on, xlabel ('Projection'); %! plot (outcp(:,1), outcp(:,2), 'or') %!demo %! ## Streched image %! rad = 2; % minimum value: 4/pi %! [uu vv] = meshgrid ((-2:2)/rad, (-2:2)/rad); %! rescfactor = sin ((uu.^2 + vv.^2).^.5); %! inpts = [(reshape (uu, numel (uu), 1)), (reshape (vv, numel (uu), 1))]; %! xx = rescfactor .* sign(uu); %! yy = rescfactor .* sign(vv); %! outpts = [reshape(xx, numel (xx), 1) reshape(yy, numel (yy), 1)]; %! %! T = cp2tform (inpts, outpts, "polynomial", 4); %! figure; %! subplot (1,2,1) %! im = zeros (800, 800, 3); %! im(:,:,1) = checkerboard (100) > 0.2; %! im(:,:,3) = checkerboard (100) < 0.2; %! [im2 xdata ydata] = imtransform (im, T, 'udata', [-2 2], %! 'vdata', [-2 2], 'fillvalues', %! [0 1 0]); %! imh = imshow (im2); %! set (imh, 'xdata', xdata, 'ydata', ydata) %! set (gca, 'xlim', xdata, 'ylim', ydata) %! [im cmap] = imread ('default.img'); %! subplot (1,2,2) %! [im2 xdata ydata] = imtransform (im, T, 'udata', [-1 1], %! 'vdata', [-1 1], 'fillvalues', %! round (length (cmap) / 2)); %! imh = imshow (im2, cmap); %!test %! im = checkerboard (); %! incp = [0 0; 0 1; 1 1]; %! scl = 10; %! outcp = scl * incp; %! T = maketform ('affine', incp, outcp); %! [im2 xdata ydata] = imtransform (im, T, 'udata', [0 1], %! 'vdata', [0 1], 'size', [500 500]); %! assert (xdata, scl * ([0 1])) %! assert (ydata, scl * ([0 1])) %! assert (size (im2), [500 500]) %!test %! im = checkerboard (); %! incp = [0 0; 0 1; 1 1]; %! scl = 10; %! outcp = scl * incp; %! xyscale = scl; %! T = maketform ('affine', incp, outcp); %! [im2 xdata ydata] = imtransform (im, T, 'xyscale', xyscale); %! assert (size (im2), size (im), 1) %!test %! im = checkerboard (100, 10, 4); %! theta = 2 * pi; %! T = maketform ("affine", [cos(theta) -sin(theta); ... %! sin(theta) cos(theta); 0 0]); %! im2 = imtransform (im, T, "nearest", "xdata", [1 800], "ydata", [1 2000]); %! im = im(2:end-1, 2:end-1); %avoid boundaries %! im2 = im2(2:end-1, 2:end-1); %! assert (im, im2) %!test %! im = checkerboard (20, 10, 4); %! theta = pi/6; %! T = maketform ('affine', [cos(theta) -sin(theta); ... %! sin(theta) cos(theta); 0 0]); %! [im2, xdata] = imtransform (im, T); %! nu = columns(im); %! nv = rows(im); %! nx = xdata(2); %! diag = sqrt (nu^2 + nv^2); %! ang = atan (nv / nu); %! assert (nx, diag * abs (cos (theta - ang)), %! diag * 1 / size (im2, 2)) ## Test default fillvalues %!test %! im = rand (2); %! tmat = [eye(2); 0 0]; %! T = maketform ("affine", tmat); %! im2 = imtransform (im, T, "xdata", [1 3]); %! assert (im2(:,3), zeros (2,1)) ## Test image size when forcing x/ydata %!test %! im = rand (2); %! tmat = [eye(2); 0 0]; %! T = maketform ('affine', tmat); %! im2 = imtransform (im, T, "xdata", [1 3]); %! assert (size (im2), [2 3]) ## Test image size when forcing xyscale %!test %! im = rand (2); %! tmat = [eye(2); 0 0]; %! T = maketform ('affine', tmat); %! im2 = imtransform (im, T, "xyscale", [0.5 0.5]); %! assert (size (im2), [3 3]) ## Test descending order of xdata/ydata (bug #59953) %!test %! im = [1,2,3,4; 5,6,7,8; 9,10,11,12; 13,14,15,16]; %! tmat = [eye(2); 0 0]; %! T = maketform ('affine', tmat); %! im2 = imtransform (im, T, 'xdata', [1 4], 'ydata', [4 1]); %! assert (im2, [13,14,15,16; 9,10,11,12; 5,6,7,8; 1,2,3,4]) %! %! im2 = imtransform (im, T, 'xdata', [4 1], 'ydata', [1 4]); %! assert (im2, [4,3,2,1; 8,7,6,5; 12,11,10,9; 16,15,14,13]) %! %! [im2, xd, yd] = imtransform (im, T, 'xdata', [4 1], 'ydata', [4 1]); %! assert (im2, [16,15,14,13; 12,11,10,9; 8,7,6,5; 4,3,2,1]) %! assert (xd, [4 1]) %! assert (yd, [4 1]) %! %!error imtransform () %!error imtransform (rand(2)) %!error ... %! imtransform (rand(2), maketform ('affine', [eye(2); 0 0]), ... %! 'udata', [1 2], 'vdata', [2 1]); %!error ... %! imtransform (rand(2), maketform ('affine', [eye(2); 0 0]), ... %! 'udata', [2 1], 'vdata', [1 2]); image-2.16.1/inst/PaxHeaders.61586/iptnum2ordinal.m0000644000000000000000000000006215005110255016527 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/iptnum2ordinal.m0000644000175000017500000000667515005110255020143 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{ord} =} iptnum2ordinal (@var{num}) ## Convert number to ordinal string. ## ## @var{num} must be a real positive integer which will be converted to a string ## with its ordinal form @var{ord}. ## ## @example ## @group ## iptnum2ordinal (1) ## @result{} first ## iptnum2ordinal (12) ## @result{} twelfth ## iptnum2ordinal (21) ## @result{} 21st ## @end group ## @end example ## ## @seealso{num2str, sprintf, int2str, mat2str} ## @end deftypefn function ord = iptnum2ordinal (num) ## thanks to Skei in ##matlab for checking the grammar of ordinals and that it ## is after number 20 that it starts using the suffixes only ## thanks to porten in ##matlab for help checking these corner-cases ## the following were test and failed: Inf, 0, -1, 3.4, 1e-7 ## using a string kind of succeeded as the character position in the ascii ## table as used for the conversion if (nargin != 1) print_usage; elseif (!isnumeric (num) || !isscalar (num) || !isreal (num) || num <= 0 || rem (num, 1) != 0) error ("num must be a real positive integer"); endif switch num case {1} ord = "first"; case {2} ord = "second"; case {3} ord = "third"; case {4} ord = "fourth"; case {5} ord = "fifth"; case {6} ord = "sixth"; case {7} ord = "seventh"; case {8} ord = "eighth"; case {9} ord = "ninth"; case {10} ord = "tenth"; case {11} ord = "eleventh"; case {12} ord = "twelfth"; case {13} ord = "thirteenth"; case {14} ord = "fourteenth"; case {15} ord = "fifteenth"; case {16} ord = "sixteenth"; case {17} ord = "seventeenth"; case {18} ord = "eighteenth"; case {19} ord = "nineteenth"; case {20} ord = "twentieth"; otherwise ## if we ever want to mimic matlab's defective behaviour of accepting a ## string and return the ordinal of position on the ascii table, we must ## check here if it's a string, and if so, use: ## ord = sprintf ("%dth", num); num = num2str (num); switch num(end) case {"1"} ord = strcat (num, "st"); case {"2"} ord = strcat (num, "nd"); case {"3"} ord = strcat (num, "rd"); otherwise ord = strcat (num, "th"); endswitch endswitch endfunction %!assert (strcmp (iptnum2ordinal (1), 'first')); # simple works %!assert (strcmp (iptnum2ordinal (21), '21st')); # after 20, goes stupid %!assert (strcmp (iptnum2ordinal (100), '100th')); # use th correctly %!fail ("iptnum2ordinal (inf)"); # must be real %!fail ("iptnum2ordinal (0)"); # must be positive %!fail ("iptnum2ordinal (-1)"); # must be positive %!fail ("iptnum2ordinal (3.4)"); # must be integer image-2.16.1/inst/PaxHeaders.61586/regionprops.m0000644000000000000000000000006215005110255016127 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/regionprops.m0000644000175000017500000021132715005110255017533 0ustar00avinoamavinoam00000000000000## Copyright (C) 2010 Søren Hauberg ## Copyright (C) 2012 Jordi Gutiérrez Hermoso ## Copyright (C) 2015, 2017 Hartmut Gimpel ## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} regionprops (@var{BW}) ## @deftypefnx {Function File} {} regionprops (@var{L}) ## @deftypefnx {Function File} {} regionprops (@var{CC}) ## @deftypefnx {Function File} {} regionprops (@dots{}, @var{properties}) ## @deftypefnx {Function File} {} regionprops (@dots{}, @var{I}, @var{properties}) ## Compute properties of image regions. ## ## Measures several properties for each region within an image. Returns ## a struct array, one element per region, whose field names are the ## measured properties. ## ## Individual regions can be defined in three different ways, a binary ## image, a labelled image, or a bwconncomp struct, each providing ## different advantages. ## ## @table @asis ## @item @var{BW} ## A binary image. Must be of class logical. Individual regions will be ## the connected component as computed by @code{bwconnmp} using the ## maximal connectivity for the number of dimensions of @var{bw} (see ## @code{conndef} for details). For alternative connectivities, call ## @code{bwconncomp} directly and use its output instead. ## ## @var{bw} must really be of class logical. If not, even if it is a ## numeric array of 0's and 1's, it will be treated as a labelled image ## with a single discontinuous region. For example: ## ## @example ## ## Handled as binary image with 3 regions ## bw = logical ([ ## 1 0 1 0 1 ## 1 0 1 0 1 ## ]); ## ## ## Handled as labelled image with 1 region ## bw = [ ## 1 0 1 0 1 ## 1 0 1 0 1 ## ]; ## @end example ## ## @item @var{L} ## A labelled image. Each region is the collection of all positive ## elements with the same value. This allows computing properties of ## regions that would otherwise be considered separate or connected. ## For example: ## ## @example ## ## Recognizes 4 regions ## l = [ ## 1 2 3 4 ## 1 2 3 4 ## ]; ## ## ## Recognizes 2 (discontinuous) regions ## l = [ ## 1 2 1 2 ## 1 2 1 2 ## ]; ## @end example ## ## @item @var{CC} ## A @code{bwconnmp()} structure. This is a struct with the following ## 4 fields: Connectivity, ImageSize, NumObjects, and PixelIdxList. See ## @code{bwconncomp} for details. ## ## @end table ## ## The properties to be measured can be defined via a cell array or a ## comma separated list or strings. Some of the properties are only ## supported if the matching grayscale image @var{I} is also supplied. ## Others are only supported for 2 dimensional images. See the list ## below for details on each property limitation. If none is specified, ## it defaults to the @qcode{"basic"} set of properties. ## ## @table @asis ## @item @qcode{"Area"} ## The number of pixels in the region. Note that this differs from ## @code{bwarea} where each pixel has different weights. ## ## @item @qcode{"BoundingBox"} ## The smallest rectangle that encloses the region. This is represented ## as a row vector such as ## @code{[x y z @dots{} x_length y_length z_length @dots{}]}. ## ## The first half corresponds to the lower coordinates of each dimension ## while the second half, to the length in that dimension. For the two ## dimensional case, the first 2 elements correspond to the coordinates ## of the upper left corner of the bounding box, while the two last entries ## are the width and the height of the box. ## ## @item @qcode{"Centroid"} ## The coordinates for the region centre of mass. This is a row vector ## with one element per dimension, such as @code{[x y z @dots{}]}. ## ## @item @qcode{"ConvexArea"} ## Number of pixels in the ConvexImage. ## Only supported for 2D images. ## ## @item @qcode{"ConvexHull"} ## The coordinates of the smallest convex polygon that fully encloses ## the region. Returns a m*2 matrix with each row containing the ## x- and y-coordinate of one corner point of the polygon. ## Only supported for 2D images. (see also: convhull) ## ## @item @qcode{"ConvexImage"} ## A binary image containing all pixels inside the convex hull. The ## size of this image is the bounding box. Only supported for ## 2D images. ## (see also: poly2mask) ## ## @item @qcode{"Eccentricity"} ## The eccentricity of the ellipse that has the same normalized ## second central moments as the region (value between 0 and 1). ## ## @item @qcode{"EquivDiameter"} ## The diameter of a circle with the same area as the object. ## ## @item @qcode{"EulerNumber"} ## The Euler number of the region using connectivity 8. Only supported ## for 2D images. See @code{bweuler} for details. ## ## @item @qcode{"Extent"} ## The area of the object divided by the area of the bounding box. ## ## @item @qcode{"Extrema"} ## Returns an 8-by-2 matrix with the extrema points of the object. ## The first column holds the returned x- and the second column the y-values. ## The order of the 8 points is: top-left, top-right, right-top, right-bottom, ## bottom-right, bottom-left, left-bottom, left-top. ## ## @item @qcode{"FilledArea"} ## The area of the object including possible holes. ## ## @item @qcode{"FilledImage"} ## A binary image with the same size as the object's bounding box that contains ## the object with all holes removed. ## ## @item @qcode{"Image"} ## An image with the same size as the bounding box that contains the original ## pixels. ## ## @item @qcode{"MajorAxisLength"} ## The length of the major axis of the ellipse that has the same ## normalized second central moments as the object. ## ## @item @qcode{"MaxIntensity"} ## The maximum intensity value inside each region. ## Requires a grayscale image @var{I}. ## ## @item @qcode{"MeanIntensity"} ## The mean intensity value inside each region. ## Requires a grayscale image @var{I}. ## ## @item @qcode{"MinIntensity"} ## The minimum intensity value inside each region. ## Requires a grayscale image @var{I}. ## ## @item @qcode{"MinorAxisLength"} ## The length of the minor axis of the ellipse that has the same ## normalized second central moments as the object. ## ## @item @qcode{"Orientation"} ## The angle between the x-axis and the major axis of the ellipse that ## has the same normalized second central moments as the object ## (value in degrees between -90 and 90). ## ## @item @qcode{"Perimeter"} ## The length of the boundary of the object. ## ## @item @qcode{"PixelIdxList"} ## The linear indices for the elements of each region in a column vector. ## ## @item @qcode{"PixelList"} ## The subscript indices for the elements of each region. This is a p-by-Q ## matrix where p is the number of elements and Q is the number of ## dimensions. Each row is of the form @code{[x y z @dots{}]}. ## ## @item @qcode{"PixelValues"} ## The actual pixel values inside each region in a column vector. ## Requires a grayscale image @var{I}. ## ## @item @qcode{"Solidity"} ## Ratio of Area / ConvexArea. ## Only supported for 2D images. ## ## @item @qcode{"SubarrayIdx"} ## A cell array with subscript indices for the bounding box. This can ## be used as @code{@var{I}(@var{props}(@var{p}).SubarrayIdx@{:@})}, where ## @var{p} is one of the regions, to extract the image in its bounding box. ## ## @item @qcode{"WeightedCentroid"} ## The coordinates for the region centre of mass when using the intensity ## of each element as weight. This is a row vector with one element per ## dimension, such as @code{[x y z @dots{}]}. ## Requires a grayscale image @var{I}. ## ## @end table ## ## In addition, the strings @qcode{"basic"} and @qcode{"all"} can be ## used to select a subset of the properties: ## ## @table @asis ## @item @qcode{"basic"} (default) ## Compute @qcode{"Area"}, @qcode{"Centroid"}, and @qcode{"BoundingBox"}. ## ## @item @qcode{"all"} ## Computes all possible properties for the image, i.e., it will not ## compute properties that require grayscale unless the grayscale image ## is available, and it will not compute properties that are limited to ## 2 dimensions, unless the image is 2 dimensions. ## ## @end table ## ## @seealso{bwlabel, bwperim, bweuler, convhull, poly2mask} ## @end deftypefn function props = regionprops (bw, varargin) if (nargin < 1) print_usage (); endif if (isstruct (bw)) if (! isempty (setxor (fieldnames (bw), {"Connectivity", "ImageSize", ... "NumObjects", "PixelIdxList"}))) error ("regionprops: CC is an invalid bwconnmp() struct"); endif cc = bw; elseif (islogical (bw)) cc = bwconncomp (bw); elseif (isnumeric (bw)) if (isinteger (bw)) if (intmin (class (bw)) < 0 && any (bw(:) < 0)) error ("regionprops: L must be non-negative integers only"); endif else if (any (bw(:) < 0) || any (fix (bw(:)) != bw(:))) error ("regionprops: L must be non-negative integers only"); endif endif n_obj = max (bw(:)); if (! n_obj) ## workaround for https://savannah.gnu.org/bugs/index.php?47287 cc = struct ("ImageSize", size (bw), "NumObjects", double (n_obj), "PixelIdxList", {cell(1, 0)}); else l_idx = find (bw); cc = struct ("ImageSize", size (bw), "NumObjects", double (n_obj), "PixelIdxList", {accumarray(bw(l_idx)(:), l_idx, [1 n_obj], @(x) {x})}); endif else error ("regionprops: no valid BW, CC, or L input"); endif is_2d = numel (cc.ImageSize) == 2; next_idx = 1; has_gray = false; if (numel (varargin) && isnumeric (varargin{1})) next_idx++; has_gray = true; img = varargin{1}; sz = size (img); if (! size_equal (sz, cc.ImageSize) || any (sz != cc.ImageSize)) error ("regionprops: BW and I sizes must be equal"); endif endif if (numel (varargin) >= next_idx) if (iscell (varargin{next_idx})) properties = varargin{next_idx++}; if (numel (varargin) >= next_idx) print_usage (); endif else properties = varargin(next_idx++:end); endif if (! iscellstr (properties)) error ("regionprops: PROPERTIES must be a string or a cell array of strings"); endif properties = tolower (strrep (properties, "_", "")); else properties = {"basic"}; endif properties = select_properties (properties, is_2d, has_gray); ## Some properties require the value of others. In addition, most ## properties have common code. Ideally, to avoid repeating ## computations, we would make use not only of the already measured ## properties. but also of their intermediary steps. We handle this ## with a stack of properties that need to be measured and we push ## dependencies into it as we find them. A scalar struct keeps all ## values whose fields are the properties and intermediary steps names. ## ## Note that we do not want to fill the return value just yet. The ## reason is that props is a struct array. Since the computation of ## the properties is vectorized, it would require a constant back and ## forth conversion between cell arrays and numeric arrays. So we ## keep everything in a numeric array and everything is much faster. ## At the end, we put everything in place in a struct array. dependencies = struct ( "area", {{}}, "accum_subs", {{"area"}}, # private "accum_subs_nd", {{"accum_subs"}}, # private "boundingbox", {{"pixellist", "accum_subs_nd"}}, "centroid", {{"accum_subs_nd", "pixellist", "area"}}, "filledarea", {{"filledimage"}}, "filledimage", {{"image"}}, "image", {{"subarrayidx", "accum_subs", "pixelidxlist"}}, "pixelidxlist", {{}}, "pixellist", {{"pixelidxlist"}}, "subarrayidx", {{"boundingbox"}}, "convexarea", {{"conveximage"}}, "convexhull", {{"boundingbox", "image"}}, "conveximage", {{"boundingbox", "convexhull"}}, "eccentricity", {{"minoraxislength", "majoraxislength"}}, "equivdiameter", {{"area"}}, "eulernumber", {{"image"}}, "extent", {{"area", "boundingbox"}}, "extrema", {{"area", "accum_subs_nd", "pixellist"}}, "local_ellipse", {{"area", "pixellist"}}, # private "majoraxislength", {{"local_ellipse"}}, "minoraxislength", {{"local_ellipse"}}, "orientation", {{"local_ellipse"}}, "perimeter", {{}}, "perimeterold", {{}}, "solidity", {{"area", "convexarea"}}, "maxintensity", {{"accum_subs", "pixelidxlist"}}, "meanintensity", {{"total_intensity", "area"}}, "minintensity", {{"accum_subs", "pixelidxlist"}}, "pixelvalues", {{"pixelidxlist"}}, "total_intensity", {{"accum_subs", "pixelidxlist"}}, "weightedcentroid", {{"accum_subs_nd", "total_intensity", "pixellist", "pixelidxlist", "area"}} ); to_measure = properties; values = struct (); ## There's too many indirectly dependent on "area", and even if not ## required, it will be required later to create the struct array. values.area = rp_area (cc); while (! isempty (to_measure)) pname = to_measure{end}; ## Already computed. Pop it and move on. if (isfield (values, pname)) to_measure(end) = []; continue endif ## There's missing dependencies. Push them and start again. deps = dependencies.(pname); missing = deps(! isfield (values, deps)); if (! isempty (missing)) to_measure(end+1:end+numel(missing)) = missing; continue endif to_measure(end) = []; switch (pname) case "area" values.area = rp_area (cc); case "accum_subs" values.accum_subs = rp_accum_subs (cc, values.area); case "accum_subs_nd" values.accum_subs_nd = rp_accum_subs_nd (cc, values.accum_subs); case "boundingbox" values.boundingbox = rp_bounding_box (cc, values.pixellist, values.accum_subs_nd); case "centroid" values.centroid = rp_centroid (cc, values.pixellist, values.area, values.accum_subs_nd); case "filledarea" values.filledarea = rp_filled_area (values.filledimage); case "filledimage" values.filledimage = rp_filled_image (values.image); case "image" values.image = rp_image (cc, bw, values.pixelidxlist, values.accum_subs, values.subarrayidx); case "pixelidxlist" values.pixelidxlist = rp_pixel_idx_list (cc); case "pixellist" values.pixellist = rp_pixel_list (cc, values.pixelidxlist); case "subarrayidx" values.subarrayidx = rp_subarray_idx (cc, values.boundingbox); case "convexarea" values.convexarea = rp_convex_area (values.conveximage); case "convexhull" values.convexhull = rp_convex_hull (values.boundingbox, values.image); case "conveximage" values.conveximage = rp_convex_image (values.boundingbox, values.convexhull); case "eccentricity" values.eccentricity = rep_eccentricity (values.minoraxislength, values.majoraxislength); case "equivdiameter" values.equivdiameter = rp_equivdiameter (values.area); case "eulernumber" values.eulernumber = rp_euler_number (values.image); case "extent" values.extent = rp_extent (values.area, values.boundingbox); case "extrema" values.extrema = rp_extrema (cc, values.pixellist, values.area, values.accum_subs_nd); case "local_ellipse" values.local_ellipse = true; [values.minoraxislength, values.majoraxislength, ... values.orientation] = rp_local_ellipse (values.area, values.pixellist); case {"majoraxislength", "minoraxislength", "orientation"} ## Do nothing. These are "virtual" targets which are computed ## in local_ellipse. case "perimeter" values.perimeter = rp_perimeter (cc); case "perimeterold" values.perimeterold = rp_perimeter_old (cc); case "solidity" values.solidity = rp_solidity (values.area, values.convexarea); case "maxintensity" values.maxintensity = rp_max_intensity (cc, img, values.pixelidxlist, values.accum_subs); case "meanintensity" values.meanintensity = rp_mean_intensity (cc, values.total_intensity, values.area); case "minintensity" values.minintensity = rp_min_intensity (cc, img, values.pixelidxlist, values.accum_subs); case "pixelvalues" values.pixelvalues = rp_pixel_values (cc, img, values.pixelidxlist); case "total_intensity" values.total_intensity = rp_total_intensity (cc, img, values.pixelidxlist, values.accum_subs); case "weightedcentroid" values.weightedcentroid = rp_weighted_centroid (cc, img, values.pixellist, values.pixelidxlist, values.total_intensity, values.accum_subs_nd, values.area); otherwise error ("regionprops: unknown property `%s'", pname); endswitch endwhile ## After we have made all the measurements, we need to pack everything ## into struct arrays. Area = values.area; props = repmat (struct (), cc.NumObjects, 1); for ip = 1:numel (properties) switch (properties{ip}) case "area" [props.Area] = num2cell (Area){:}; case "boundingbox" [props.BoundingBox] = mat2cell (values.boundingbox, ones (cc.NumObjects, 1)){:}; case "centroid" [props.Centroid] = mat2cell (values.centroid, ones (cc.NumObjects, 1)){:}; case "filledarea" [props.FilledArea] = num2cell (values.filledarea){:}; case "filledimage" [props.FilledImage] = values.filledimage{:}; case "image" [props.Image] = values.image{:}; case "pixelidxlist" [props.PixelIdxList] = mat2cell (values.pixelidxlist, Area){:}; case "pixellist" [props.PixelList] = mat2cell (values.pixellist, Area){:}; case "subarrayidx" [props.SubarrayIdx] = values.subarrayidx{:}; case "convexarea" [props.ConvexArea] = num2cell (values.convexarea){:}; case "convexhull" [props.ConvexHull] = values.convexhull{:}; case "conveximage" [props.ConvexImage] = values.conveximage{:}; case "eccentricity" [props.Eccentricity] = num2cell (values.eccentricity){:}; case "equivdiameter" [props.EquivDiameter] = num2cell (values.equivdiameter){:}; case "eulernumber" [props.EulerNumber] = num2cell (values.eulernumber){:}; case "extent" [props.Extent] = num2cell (values.extent){:}; case "extrema" [props.Extrema] = mat2cell (values.extrema, repmat (8, 1, cc.NumObjects)){:}; case "majoraxislength" [props.MajorAxisLength] = num2cell (values.majoraxislength){:}; case "minoraxislength" [props.MinorAxisLength] = num2cell (values.minoraxislength){:}; case "orientation" [props.Orientation] = num2cell (values.orientation){:}; case "perimeter" [props.Perimeter] = num2cell (values.perimeter){:}; case "perimeterold" [props.PerimeterOld] = num2cell (values.perimeterold){:}; case "solidity" [props.Solidity] = num2cell (values.solidity){:}; case "maxintensity" [props.MaxIntensity] = num2cell (values.maxintensity){:}; case "meanintensity" [props.MeanIntensity] = num2cell (values.meanintensity){:}; case "minintensity" [props.MinIntensity] = num2cell (values.minintensity){:}; case "pixelvalues" [props.PixelValues] = mat2cell (values.pixelvalues, Area){:}; case "weightedcentroid" [props.WeightedCentroid] = mat2cell (values.weightedcentroid, ones (cc.NumObjects, 1)){:}; otherwise error ("regionprops: unknown property `%s'", pname); endswitch endfor endfunction function props = select_properties (props, is_2d, has_gray) persistent props_basic = { "area", "boundingbox", "centroid", }; persistent props_2d = { "convexarea", "convexhull", "conveximage", "eccentricity", "equivdiameter", "extrema", "majoraxislength", "minoraxislength", "orientation", "perimeter", "solidity", }; persistent props_gray = { "maxintensity", "meanintensity", "minintensity", "pixelvalues", "weightedcentroid", }; persistent props_others = { "eulernumber", "extent", # Matlab limits Extent to 2D. Octave does not. "filledarea", "filledimage", "image", "pixelidxlist", "pixellist", "subarrayidx", }; props = props(:); p_basic = strcmp ("basic", props); p_all = strcmp ("all", props); props(p_basic | p_all) = []; if (any (p_all)) props = vertcat (props, props_basic, props_others); if (is_2d) props = vertcat (props, props_2d); endif if (has_gray) props = vertcat (props, props_gray); endif elseif (any (p_basic)) props = vertcat (props, props_basic); endif if (! is_2d) non_2d = ismember (props, props_2d); if (any (non_2d)) warning ("regionprops: ignoring %s properties for non 2 dimensional image", strjoin (props(non_2d), ", ")); props(non_2d) = []; endif endif if (! has_gray) non_val = ismember (props, props_gray); if (any (non_val)) warning ("regionprops: ignoring %s properties due to missing grayscale image", strjoin (props(non_val), ", ")); props(non_val) = []; endif endif endfunction function area = rp_area (cc) area = cellfun (@numel, cc.PixelIdxList(:)); endfunction function centroid = rp_centroid (cc, pixel_list, area, subs_nd) nd = numel (cc.ImageSize); no = cc.NumObjects; weighted_sub = pixel_list ./ vec (repelems (area, [1:no; vec(area, 2)])); centroid = accumarray (subs_nd, weighted_sub(:), [no nd]); endfunction function bounding_box = rp_bounding_box (cc, pixel_list, subs_nd) nd = numel (cc.ImageSize); no = cc.NumObjects; init_corner = accumarray (subs_nd, pixel_list(:), [no nd], @min) - 0.5; end_corner = accumarray (subs_nd, pixel_list(:), [no nd], @max) + 0.5; bounding_box = [(init_corner) (end_corner - init_corner)]; endfunction function eccentricity = rep_eccentricity (minoraxislength, majoraxislength) eccentricity = sqrt (1 - (minoraxislength ./ majoraxislength).^2); endfunction function equivdiameter = rp_equivdiameter (area) equivdiameter = sqrt (4 * area / pi); endfunction function euler = rp_euler_number (bb_images) ## TODO there should be a way to vectorize this, right? euler = cellfun (@bweuler, bb_images); endfunction function extent = rp_extent (area, bounding_box) bb_area = prod (bounding_box(:,(end/2)+1:end), 2); extent = area ./ bb_area; endfunction function extrema = rp_extrema (cc, pixel_list, area, subs_nd) ## Note that this property is limited to 2d regions no = cc.NumObjects; ## Algorithm: ## 1. Find the max and min values for row and column values on ## each object. That is, max and min of each column in ## pixel_list, for each object. ## ## 2. Get a mask for pixel_list, for those rows and columns indices. ## ## 3. Use that mask on the other dimension to find the max and min ## values for each object. ## ## 4. Assign those values to a (8*no)x2 array. ## ## This gets a bit convoluted because we do the two dimensions and ## all objects at the same time. ## In the following, "head" and "base" are the top and bottom index for ## each dimension. We use the words "head" and "base" to avoid confusion ## with the rest where top and bottom only refer to the row dimension. ## So "head" has the lowest index values (rows for top left/right, and ## columns for left top/bottom), while "base" has the highest index ## values (rows for bottom left/right, and columns for right top/bottom). ## 1. Find the max and min values for row and column values on ## each object. That is, max and min of each column in ## pixel_list, for each object. head = accumarray (subs_nd, pixel_list(:), [no 2], @min); base = accumarray (subs_nd, pixel_list(:), [no 2], @max); ## 2. Get a mask for pixel_list, for those rows and columns indices. ## ## 3. Use that mask on the other dimension to find the max and min ## values for each object. ## ## head_head and head_base, have the lowest index (head) and the ## highest index (base) values, for the "head" indices. ## Same logic for base_head and base_base. px_l_sz = size (pixel_list); rep_extrema = @(x) reshape (repelems (x, [1:(no*2); area(:)' area(:)']), px_l_sz); head_mask = (pixel_list == rep_extrema (head))(:, [2 1]); head_head = accumarray (subs_nd(head_mask), pixel_list(head_mask), [no 2], @min); head_base = accumarray (subs_nd(head_mask), pixel_list(head_mask), [no 2], @max); base_mask = (pixel_list == rep_extrema (base))(:, [2 1]); base_head = accumarray (subs_nd(base_mask), pixel_list(base_mask), [no 2], @min); base_base = accumarray (subs_nd(base_mask), pixel_list(base_mask), [no 2], @max); ## Adjust from idx integer to pixel border coordinates head -= 0.5; head_head -= 0.5; head_base += 0.5; base += 0.5; base_head -= 0.5; base_base += 0.5; ## 4. Assign those values to a (8*no)x2 array. nr = 8 * no; extrema = zeros (nr, 2); extrema(1:8:nr, 2) = head(:,2); # y values for top left extrema(2:8:nr, 2) = head(:,2); # y values for top right extrema(7:8:nr, 1) = head(:,1); # x values for left bottom extrema(8:8:nr, 1) = head(:,1); # x values for left top extrema(5:8:nr, 2) = base(:,2); # y values for bottom right extrema(6:8:nr, 2) = base(:,2); # y values for bottom left extrema(3:8:nr, 1) = base(:,1); # x values for right top extrema(4:8:nr, 1) = base(:,1); # x values for right bottom extrema(1:8:nr, 1) = head_head(:,1); # x value for top left extrema(8:8:nr, 2) = head_head(:,2); # y value for left top extrema(2:8:nr, 1) = head_base(:,1); # x value for top right extrema(7:8:nr, 2) = head_base(:,2); # y value for left bottom extrema(6:8:nr, 1) = base_head(:,1); # x value for bottom left extrema(3:8:nr, 2) = base_head(:,2); # y value for right top extrema(5:8:nr, 1) = base_base(:,1); # x value for bottom right extrema(4:8:nr, 2) = base_base(:,2); # y value for right bottom endfunction function filled_area = rp_filled_area (bb_filled_images) filled_area = cellfun ('nnz', bb_filled_images); endfunction function bb_filled_images = rp_filled_image (bb_images) ## Beware if attempting to vectorize this. The bounding boxes of ## different regions may overlap, and a "hole" may be a hole for ## several regions (e.g., concentric circles). There should be tests ## this weird cases. bb_filled_images = cellfun (@(x) imfill (x, "holes"), bb_images, "UniformOutput", false); endfunction function bb_images = rp_image (cc, bw, idx, subs, subarray_idx) ## For this property, we must remember to remove elements from other ## regions (remember that bounding boxes may overlap). We do that by ## creating a labelled image, extracting the bounding boxes, and then ## comparing elements. no = cc.NumObjects; ## If BW is numeric then it already is a labeled image. if (isnumeric (bw)) L = bw; else if (no < 255) cls = "uint8"; elseif (no < 65535) cls = "uint16"; elseif (no < 4294967295) cls = "uint32"; else cls = "double"; endif L = zeros (cc.ImageSize, cls); L(idx) = subs; endif sub_structs = num2cell (struct ("type", "()", "subs", subarray_idx)); bb_images = cellfun (@subsref, {L}, sub_structs, "UniformOutput", false); bb_images = cellfun (@eq, bb_images, num2cell (1:no)(:), "UniformOutput", false); endfunction function perim = rp_perimeter (cc) ## FIXME: this should be vectorized. We were previously using ## bwboundaries (incorrectly, see bug #52926) but ## bwboundaries is doing a similar loop internally. ## regular CHAIN_CODE = [3, 2, 1, 4, -1, 0, 5, 6, 7]; CHAIN_CODE = [1, 2, 1, 4, 1, 0, 1, 6, 1]; no = cc.NumObjects; boundaries = cell (no, 1); bw = false (cc.ImageSize); perim = zeros(no, 1); for k = 1:no idx = cc.PixelIdxList{k}; bw(idx) = true; boundaries{k} = __boundary__ (bw, 8); if (k != no) bw(idx) = false; endif np = size (boundaries{k}, 1); if (np == 2) ## single pixel - perimeter is 0 perim(k) = 0; else ## calculating perimeter according to Vossepoel and Smeulders, ## Computer Graphics and Image Processing 20(4):347-364, 1982. ## see: Cris Luengo, "Measuring boundary length" ## http://www.crisluengo.net/index.php/archives/310 ## Corners are counted using Hartmut Gimpel observations in ## http://savannah.gnu.org/bugs/?52933#comment12: ## 1. corners of 90 deg counted, but only when they are aligned with the coordinate system. ## 2. corners of 135 deg are counted ## 3. corners of 45 deg are counted ## 4. NO other types of corners are counted, especially not "spikes" of 360 deg (aligned with axes or not), ## and 90 deg cornes that are tilted (by 45 deg) with regard to the axes. ## regular CHAIN_CODE is = [3, 2, 1, 4, -1, 0, 5, 6, 7]; ## we use: CHAIN_CODE = [1, 2, 1, 4, 1, 0, 1, 6, 1]; ## we preserve even value, while changing odd values to 1 ## corners of 90 deg which aligned with the coordinate system are change of 2 or 4 ## in the chain code, but only when the two values are even, ## and that's the reason for the modified chain code. ## corners of 45 deg and 135 deg are changes from even to odd or from odd to even in the chain code # boundary of component k boundary = boundaries {k}; # distance between consecutive pixels in the boundary dists = boundary (2:end,:) - boundary (1:end-1,:) + 2; # converting x_y distances to vector dists_vec = dists(:,2) + (dists(:,1)-1)*3; # converting distances to chain code chain_code = CHAIN_CODE (dists_vec); # odd numbers in the chain code - diagonal movement odd = sum(mod (chain_code, 2)); # even entries in the chain code - vetical or horizontal movement even = np - 1 - odd; # counting corners chain_code_change = abs(chain_code - [chain_code(end), chain_code(1:end-1)]); # 45 deg & 145 deg corners are places where chain code changes from # odd to even or from even to odd corners = numel (find (mod(chain_code_change, 2) == 1)); # 90 deg corners aligned with axes are places where both codes are even, # and there is a jump of 2 or 6 in the chain code corners += numel (find (chain_code_change == 2)); corners += numel (find (chain_code_change == 6)); # using Vossepoel and Smeulders formula perim(k) = even*0.980 + odd*1.406 - corners*0.091; endif endfor endfunction function perim = rp_perimeter_old (cc) ## FIXME: this should be vectorized. We were previously using ## bwboundaries (incorrectly, see bug #52926) but ## bwboundaries is doing a similar loop internally. no = cc.NumObjects; boundaries = cell (no, 1); bw = false (cc.ImageSize); perim = zeros(no, 1); for k = 1:no idx = cc.PixelIdxList{k}; bw(idx) = true; boundaries{k} = __boundary__ (bw, 8); if (k != no) bw(idx) = false; endif endfor npx = cellfun ("size", boundaries, 1); dists = diff (cell2mat (boundaries)); dists(cumsum (npx)(1:end-1),:) = []; dists = sqrt (sumsq (dists, 2)); subs = repelems (1:no, [1:no; (npx-1)(:)']); perim = accumarray (subs(:), dists(:), [no 1]); endfunction function idx = rp_pixel_idx_list (cc) idx = cell2mat (cc.PixelIdxList(:)); endfunction function pixel_list = rp_pixel_list (cc, idx) nd = numel (cc.ImageSize); pixel_list = cell2mat (nthargout (1:nd, @ind2sub, cc.ImageSize, idx)); ## If idx is empty, pixel_list will have size (0x0) so we need to expand ## it to (0xnd). Unfortunately, in2sub() returns (0x0) and not (0x1) pixel_list = postpad (pixel_list, nd, 0, 2); pixel_list(:,[1 2]) = pixel_list(:,[2 1]); endfunction function pixel_values = rp_pixel_values (cc, img, idx) pixel_values = img(idx); endfunction function max_intensity = rp_max_intensity (cc, img, idx, subs) max_intensity = accumarray (subs, img(idx), [cc.NumObjects 1], @max); endfunction function mean_intensity = rp_mean_intensity (cc, totals, area) mean_intensity = totals ./ area; endfunction function min_intensity = rp_min_intensity (cc, img, idx, subs) min_intensity = accumarray (subs, img(idx), [cc.NumObjects 1], @min); endfunction function subarray_idx = rp_subarray_idx (cc, bounding_box) nd = columns (bounding_box) / 2; bb_limits = bounding_box; ## Swap x y coordinates back to row and column bb_limits(:,[1 2 [1 2]+nd]) = bounding_box(:,[2 1 [2 1]+nd]); ## Set initial coordinates (it is faster to add 0.5 than to call ceil()) bb_limits(:,1:nd) += 0.5; ## Set the end coordinates bb_limits(:,(nd+1):end) += bb_limits(:,1:nd); bb_limits(:,(nd+1):end) -= 1; subarray_idx = arrayfun (@colon, bb_limits(:,1:nd), bb_limits(:,(nd+1):end), "UniformOutput", false); subarray_idx = mat2cell (subarray_idx, ones (cc.NumObjects, 1)); endfunction function weighted_centroid = rp_weighted_centroid (cc, img, pixel_list, pixel_idx_list, totals, subs_nd, area) no = cc.NumObjects; nd = numel (cc.ImageSize); rep_totals = vec (repelems (totals, [1:no; vec(area, 2)])); ## Note that we need 1 column, even if pixel_idx_list is [], hence (:) ## so that we get (0x1) instead of (0x0) vals = img(pixel_idx_list)(:); weighted_pixel_list = pixel_list .* (double (vals) ./ rep_totals); weighted_centroid = accumarray (subs_nd, weighted_pixel_list(:), [no nd]); endfunction function convexhull = rp_convex_hull (boundingbox, image) convexhull = {}; for idx = 1:numel (image) ## 1. calculate perimeter image perim_image = bwperim (image{idx}, 8); if numel (image{idx}) == 1 # work around bug 50153 perim_image = [true]; endif ## 2. calculate global indices of perimeter pixels [r, c] = find (perim_image); r0 = boundingbox(idx, 2) - 0.5; c0 = boundingbox(idx, 1) - 0.5; R = r(:) + r0; C = c(:) + c0; RR = [R-0.5; R; R+0.5; R]; # use 4 corners of each pixel CC = [C; C+0.5; C; C-0.5]; ## 3. calculate convex hull around those perimeter pixels if (isempty (RR)) convexhull{idx} = zeros (0,2); else hull_idx = convhull (RR, CC); # Matlab also gives points along the lines, we don't do that. # (This is also closer to the definition of a 'convex hull'.) convexhull{idx} = [CC, RR](hull_idx, :); endif endfor endfunction function conveximage = rp_convex_image (boundingbox, convexhull) conveximage = {}; for idx = 1:numel (convexhull) ## to work around a Matlab incompatible poly2mask ## (bug #50188, currently does rounding of vertex ## coodinates to nearest integer) ## we do the following (see Matlab help for poly2mask): ## * subdivide each pixel into 5*5 pieces ## * make the pixel part of the convex image if ## more than half of its pieces are inside the hull ## (This uses 25 times more memory as the region itself. ## There might be a more memory saving way to do this.) M = boundingbox(idx, 4) * 5; N = boundingbox(idx, 3)* 5; hull = convexhull{idx} * 5; if (isempty (hull)) conveximage{idx} = false (M/5,N/5); else y0 = boundingbox(idx, 2) * 5; x0 = boundingbox(idx, 1) * 5; Y = hull(:,2) - y0 + 0; X = hull(:,1) - x0 + 0; X = round (X); # reason: independence of bug #50188 Y = round (Y); cimage5 = poly2mask (X,Y,M,N); collect = zeros (5, 5, M/5, N/5); for m = 1:5 for n = 1:5 collect(m, n, :, :) = cimage5(m:5:end, n:5:end); endfor endfor summed = sum (sum (collect,1), 2); conveximage{idx} = reshape (summed, M/5, N/5) > 25*0.5; endif endfor endfunction function convexarea = rp_convex_area (conveximage) n = numel (conveximage); convexarea = zeros (n); for idx = 1:n convexarea(idx) = sum (conveximage{idx}(:)); endfor; endfunction function solidity = rp_solidity (area, convexarea) solidity = area ./ convexarea; solidity(area == 0) = NaN; endfunction ## ## Intermediary steps -- no match to specific property ## ## Creates subscripts for use with accumarray, when computing a column vector. function subs = rp_accum_subs (cc, area) rn = 1:cc.NumObjects; R = [rn; vec(area, 2)]; subs = vec (repelems (rn, R)); endfunction ## Creates subscripts for use with accumarray, when computing something ## with a column per number of dimensions function subs_nd = rp_accum_subs_nd (cc, subs) nd = numel (cc.ImageSize); no = cc.NumObjects; ## FIXME workaround bug #47085 subs_nd = vec (bsxfun (@plus, subs, [0:no:(no*nd-1)])); endfunction ## Total/Integrated density of each region. function totals = rp_total_intensity (cc, img, idx, subs) totals = accumarray (subs, img(idx), [cc.NumObjects 1]); endfunction function [minor, major, orientation] = rp_local_ellipse (area, pixellist) ## FIXME: this should be vectorized. See R.M. Haralick and Linda G. ## Shapiro, "Computer and Robot Vision: Volume 1", Appendix A no = numel (area); minor = zeros (no, 1); major = minor; orientation = minor; c_idx = 1; for idx = 1:no sel = c_idx:(c_idx + area(idx) -1); c_idx += area(idx); X = pixellist(sel, 2); Y = pixellist(sel, 1); ## calculate (centralised) second moment of region with pixels [X, Y] ## This is equivalent to "cov ([X(:) Y(:)], 1)" but will work as ## expected even if X and Y have only one row each. C = center ([X(:) Y(:)], 1); C = C' * C / (rows (C)); C = C + 1/12 .* eye (rows (C)); # centralised second moment of 1 pixel is 1/12 [V, lambda] = eig (C); lambda_d = 4 .* sqrt (diag (lambda)); minor(idx) = min (lambda_d); [major(idx), major_idx] = max (lambda_d); major_vec = V(:, major_idx); if (major(idx) == minor(idx)) orientation(idx) = 0; elseif (major_vec(2) == 0) orientation(idx) = 90; else orientation(idx) = -(180/pi) .* atan (major_vec(1) ./ major_vec(2)); endif endfor endfunction %!shared bw2d, gray2d, bw2d_over_bb, bw2d_insides %! bw2d = logical ([ %! 0 1 0 1 1 0 %! 0 1 1 0 1 1 %! 0 1 0 0 0 0 %! 0 0 0 1 1 1 %! 0 0 1 1 0 1]); %! %! gray2d = [ %! 2 4 0 7 5 2 %! 3 0 4 9 3 7 %! 0 5 3 4 8 1 %! 9 2 0 5 8 6 %! 8 9 7 2 2 5]; %! %! ## For testing overlapping bounding boxes %! bw2d_over_bb = logical ([ %! 0 1 1 1 0 1 1 %! 1 1 0 0 0 0 1 %! 1 0 0 1 1 0 1 %! 1 0 0 1 1 0 0 %! 0 0 0 1 1 1 1]); %! %! ## For testing when there's regions inside regions %! bw2d_insides = logical ([ %! 0 0 0 0 0 0 0 0 %! 0 1 1 1 1 1 1 0 %! 0 1 0 0 0 0 1 0 %! 0 1 0 1 1 0 1 0 %! 0 1 0 1 1 0 1 0 %! 0 1 0 0 0 0 1 0 %! 0 1 1 1 1 1 1 0 %! 0 0 0 0 0 0 0 0]); %!function c = get_2d_centroid_for (idx) %! subs = ind2sub ([5 6], idx); %! m = false ([5 6]); %! m(idx) = true; %! y = sum ((1:5)' .* sum (m, 2) /sum (m(:))); %! x = sum ((1:6) .* sum (m, 1) /sum (m(:))); %! c = [x y]; %!endfunction %!assert (regionprops (bw2d, "Area"), struct ("Area", {8; 6})) %!assert (regionprops (double (bw2d), "Area"), struct ("Area", {14})) %!assert (regionprops (bwlabel (bw2d, 4), "Area"), struct ("Area", {4; 6; 4})) ## These are different from Matlab because the indices in PixelIdxList ## do not appear sorted. This is because we get them from bwconncomp() ## which does not sort them (it seems bwconncomp in Matlab returns them ## sorted but that's undocumented, just like the order here is undocumented) %!assert (regionprops (bw2d, "PixelIdxList"), %! struct ("PixelIdxList", {[6; 7; 12; 8; 16; 21; 22; 27] %! [15; 19; 20; 24; 29; 30]})) %!assert (regionprops (bwlabel (bw2d, 4), "PixelIdxList"), %! struct ("PixelIdxList", {[6; 7; 8; 12] %! [15; 19; 20; 24; 29; 30] %! [16; 21; 22; 27]})) %!assert (regionprops (bw2d, "PixelList"), %! struct ("PixelList", {[2 1; 2 2; 3 2; 2 3; 4 1; 5 1; 5 2; 6 2] %! [3 5; 4 4; 4 5; 5 4; 6 4; 6 5]})) %!assert (regionprops (bwlabel (bw2d, 4), "PixelList"), %! struct ("PixelList", {[2 1; 2 2; 2 3; 3 2] %! [3 5; 4 4; 4 5; 5 4; 6 4; 6 5] %! [4 1; 5 1; 5 2; 6 2]})) ## Also different from Matlab because we do not sort the values by index %!assert (regionprops (bw2d, gray2d, "PixelValues"), %! struct ("PixelValues", {[4; 0; 4; 5; 7; 5; 3; 7] %! [7; 5; 2; 8; 6; 5]})) %!assert (regionprops (bw2d, gray2d, "MaxIntensity"), %! struct ("MaxIntensity", {7; 8})) %!assert (regionprops (bw2d, gray2d, "MinIntensity"), %! struct ("MinIntensity", {0; 2})) %!assert (regionprops (bw2d, "BoundingBox"), %! struct ("BoundingBox", {[1.5 0.5 5 3]; [2.5 3.5 4 2]})) %!assert (regionprops (bw2d, "Centroid"), %! struct ("Centroid", {get_2d_centroid_for([6 7 8 12 16 21 22 27]) %! get_2d_centroid_for([15 19 20 24 29 30])}), %! 5 * eps) %!test %! props = struct ("Area", {8; 6}, %! "Centroid", {get_2d_centroid_for([6 7 8 12 16 21 22 27]) %! get_2d_centroid_for([15 19 20 24 29 30])}, %! "BoundingBox", {[1.5 0.5 5 3]; [2.5 3.5 4 2]}); %! assert (regionprops (bw2d, "basic"), props, 5 * eps) %! assert (regionprops (bwconncomp (bw2d, 8), "basic"), props, 5 * eps) %! assert (regionprops (bwlabeln (bw2d, 8), "basic"), props, 5 * eps) %!test %! props = struct ("Area", {4; 6; 4}, %! "Centroid", {get_2d_centroid_for([6 7 8 12]) %! get_2d_centroid_for([15 19 20 24 29 30]) %! get_2d_centroid_for([16 21 22 27])}, %! "BoundingBox", {[1.5 0.5 2 3]; [2.5 3.5 4 2]; [3.5 0.5 3 2]}); %! assert (regionprops (bwconncomp (bw2d, 4), "basic"), props, 5 * eps) %! assert (regionprops (bwlabeln (bw2d, 4), "basic"), props, 5 * eps) ## This it is treated as labeled image with a single discontiguous region. %!assert (regionprops (double (bw2d), "basic"), %! struct ("Area", 14, %! "Centroid", get_2d_centroid_for (find (bw2d)), %! "BoundingBox", [1.5 0.5 5 5]), eps*1000) %!assert (regionprops ([0 0 1], "Centroid").Centroid, [3 1]) %!assert (regionprops ([0 0 1; 0 0 0], "Centroid").Centroid, [3 1]) ## bug #39701 %!assert (regionprops ([0 1 1], "Centroid").Centroid, [2.5 1]) %!assert (regionprops ([0 1 1; 0 0 0], "Centroid").Centroid, [2.5 1]) %!test %! a = zeros (2, 3, 3); %! a(:, :, 1) = [0 1 0; 0 0 0]; %! a(:, :, 3) = a(:, :, 1); %! c = regionprops (a, "centroid"); %! assert (c.Centroid, [2 1 2]) %!test %! d1=2; d2=4; d3=6; %! a = ones (d1, d2, d3); %! c = regionprops (a, "centroid"); %! assert (c.Centroid, [mean(1:d2), mean(1:d1), mean(1:d3)], eps*1000) %!test %! a = [0 0 2 2; 3 3 0 0; 0 1 0 1]; %! c = regionprops (a, "centroid"); %! assert (c(1).Centroid, [3 3]) %! assert (c(2).Centroid, [3.5 1]) %! assert (c(3).Centroid, [1.5 2]) %!test %!assert (regionprops (bw2d, gray2d, "WeightedCentroid"), %! struct ("WeightedCentroid", %! {sum([2 1; 2 2; 3 2; 2 3; 4 1; 5 1; 5 2; 6 2] %! .* ([4; 0; 4; 5; 7; 5; 3; 7] / 35)) %! sum([3 5; 4 4; 4 5; 5 4; 6 4; 6 5] %! .* ([7; 5; 2; 8; 6; 5] / 33))}), 5 * eps) %!test %! img = zeros (3, 9); %! img(2, 1:9) = 0:0.1:0.8; %! bw = im2bw (img, 0.5); %! props = regionprops (bw, img, "WeightedCentroid"); %! ix = 7:9; %! x = sum (img(2,ix) .* (ix)) / sum (img(2,ix)); %! assert (props(1).WeightedCentroid(1), x, 10*eps) %! assert (props(1).WeightedCentroid(2), 2, 10*eps) %!assert (regionprops (bw2d, gray2d, "MeanIntensity"), %! struct ("MeanIntensity", {mean([4 0 5 4 7 5 3 7]) %! mean([7 5 2 8 6 5])})) %!assert (regionprops (bwlabel (bw2d, 4), gray2d, "MeanIntensity"), %! struct ("MeanIntensity", {mean([4 0 5 4]) %! mean([7 5 2 8 6 5]) %! mean([7 5 3 7])})) %!assert (regionprops (bw2d, "SubarrayIdx"), %! struct ("SubarrayIdx", {{[1 2 3], [2 3 4 5 6]} %! {[4 5], [3 4 5 6]}})) %!assert (regionprops (bwlabel (bw2d, 4), "SubarrayIdx"), %! struct ("SubarrayIdx", {{[1 2 3], [2 3]} %! {[4 5], [3 4 5 6]} %! {[1 2], [4 5 6]}})) %!test %! out = struct ("Image", {logical([1 0 1 1 0; 1 1 0 1 1; 1 0 0 0 0]) %! logical([0 1 1 1; 1 1 0 1])}); %! assert (regionprops (bw2d, "Image"), out) %! assert (regionprops (bw2d, gray2d, "Image"), out) %! assert (regionprops (bwlabel (bw2d), "Image"), out) %!assert (regionprops (bwlabel (bw2d, 4), "Image"), %! struct ("Image", {logical([1 0; 1 1; 1 0]) %! logical([0 1 1 1; 1 1 0 1]) %! logical([1 1 0; 0 1 1])})) ## Test overlapping bounding boxes %!test %! out = struct ("Image", {logical([0 1 1 1; 1 1 0 0; 1 0 0 0; 1 0 0 0]) %! logical([1 1 0 0; 1 1 0 0; 1 1 1 1]) %! logical([1 1; 0 1; 0 1])}); %! assert (regionprops (bw2d_over_bb, "Image"), out) %! assert (regionprops (bwlabel (bw2d_over_bb), "Image"), out) %!test %! out = struct ("Image", {logical([1 1 1 1 1 1 %! 1 0 0 0 0 1 %! 1 0 0 0 0 1 %! 1 0 0 0 0 1 %! 1 0 0 0 0 1 %! 1 1 1 1 1 1]) %! logical([1 1; 1 1])}); %! assert (regionprops (bw2d_insides, "Image"), out) %! assert (regionprops (bwlabel (bw2d_insides), "Image"), out) %!test %! l = uint8 ([ %! 0 0 0 0 0 0 %! 0 1 1 1 1 0 %! 0 1 2 2 1 0 %! 0 1 2 2 1 0 %! 0 1 1 1 1 0 %! 0 0 0 0 0 0 %! ]); %! assert (regionprops (l, "EulerNumber"), %! struct ("EulerNumber", {0; 1})) %! %! l = uint8 ([ %! 0 0 0 0 0 0 0 %! 0 1 1 1 1 1 0 %! 0 1 2 2 2 1 0 %! 0 1 2 3 2 1 0 %! 0 1 2 2 2 1 0 %! 0 1 1 1 1 1 0 %! 0 0 0 0 0 0 0 %! ]); %! assert (regionprops (l, "EulerNumber"), %! struct ("EulerNumber", {0; 0; 1})) %!test %! l = uint8 ([ %! 0 0 0 0 0 0 0 %! 0 1 1 1 1 1 0 %! 0 1 0 0 0 1 0 %! 0 1 0 1 0 1 0 %! 0 1 0 0 0 1 0 %! 0 1 1 1 1 1 0 %! 0 0 0 0 0 0 0 %! ]); %! assert (regionprops (l, "EulerNumber"), %! struct ("EulerNumber", 1)) %!test %! l = uint8 ([ %! 1 1 1 1 1 1 1 %! 1 1 2 1 2 2 1 %! 1 2 1 2 1 2 1 %! 1 1 2 1 2 1 1 %! 1 2 1 2 1 2 1 %! 1 2 2 1 2 1 1 %! 1 1 1 1 1 1 1 %! ]); %! assert (regionprops (l, "EulerNumber"), %! struct ("EulerNumber", {-9; -4})) %!test %! l = uint8 ([ %! 1 1 1 1 1 1 1 %! 1 1 4 1 5 5 1 %! 1 3 1 4 1 5 1 %! 1 1 3 1 4 1 1 %! 1 2 1 3 1 4 1 %! 1 2 2 1 3 1 1 %! 1 1 1 1 1 1 1 %! ]); %! assert (regionprops (l, "EulerNumber"), %! struct ("EulerNumber", {-9; 1; 1; 1; 1})) ## Test connectivity for hole filling. %!test %! l = uint8 ([ %! 1 1 1 1 1 1 1 %! 0 1 2 1 2 2 1 %! 1 2 1 2 1 2 1 %! 1 1 2 1 2 1 1 %! 1 2 1 2 1 2 1 %! 1 2 2 1 2 1 1 %! 1 1 1 1 1 1 1 %! ]); %! filled = { %! logical([ %! 1 1 1 1 1 1 1 %! 0 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! ]); %! logical([ %! 0 1 0 1 1 %! 1 1 1 1 1 %! 0 1 1 1 0 %! 1 1 1 1 1 %! 1 1 0 1 0 %! ]); %! }; %! assert (regionprops (l, {"FilledImage", "FilledArea"}), %! struct ("FilledImage", filled, "FilledArea", {48; 19})) ## Disconnected regions without holes. %!test %! l = uint8 ([ %! 0 0 0 0 0 0 0 %! 0 1 0 1 0 1 0 %! 0 1 0 1 0 1 0 %! 0 0 0 0 0 0 0 %! ]); %! filled = logical ([ %! 1 0 1 0 1 %! 1 0 1 0 1 %! ]); %! assert (regionprops (l, {"FilledImage", "FilledArea"}), %! struct ("FilledImage", filled, "FilledArea", 6)) %! %! l = uint8 ([ %! 2 2 2 2 2 2 2 %! 2 1 2 1 2 1 2 %! 2 1 2 1 2 1 2 %! 2 2 2 2 2 2 2 %! ]); %! filled = { %! logical([ %! 1 0 1 0 1 %! 1 0 1 0 1 %! ]); %! true(4, 7) %! }; %! assert (regionprops (l, {"FilledImage", "FilledArea"}), %! struct ("FilledImage", filled, "FilledArea", {6; 28})) ## Concentric regions to fill holes. %!test %! l = uint8 ([ %! 0 0 0 0 0 0 0 %! 0 1 1 1 1 1 0 %! 0 1 2 2 2 1 0 %! 0 1 2 3 2 1 0 %! 0 1 2 2 2 1 0 %! 0 1 1 1 1 1 0 %! 0 0 0 0 0 0 0 %! ]); %! filled = {true(5, 5); true(3, 3); true}; %! assert (regionprops (l, {"FilledImage", "FilledArea"}), %! struct ("FilledImage", filled, "FilledArea", {25; 9; 1})) ## Regions with overlapping holes. %!test %! l = uint8 ([ %! 1 1 1 2 0 0 %! 1 0 2 1 2 0 %! 1 2 0 1 0 2 %! 1 2 1 1 0 2 %! 0 1 2 2 2 2 %! ]); %! filled = { %! logical([ %! 1 1 1 0 %! 1 1 1 1 %! 1 1 1 1 %! 1 1 1 1 %! 0 1 0 0 %! ]); %! logical([ %! 0 0 1 0 0 %! 0 1 1 1 0 %! 1 1 1 1 1 %! 1 1 1 1 1 %! 0 1 1 1 1 %! ]) %! }; %! assert (regionprops (l, {"FilledImage", "FilledArea"}), %! struct ("FilledImage", filled, "FilledArea", {16; 18})) ## 3D region to fill which requires connectivity 6 (fails with 18 or 26). %!test %! bw = false (5, 5, 5); %! bw(2:4, 2:4, [1 5]) = true; %! bw(2:4, [1 5], 2:4) = true; %! bw([1 5], 2:4, 2:4) = true; %! filled = bw; %! filled(2:4, 2:4, 2:4) = true; %! assert (regionprops (bw, {"FilledImage", "FilledArea"}), %! struct ("FilledImage", filled, "FilledArea", 81)) %!test %! l = uint8 ([ %! 1 1 1 2 0 0 %! 1 0 2 1 2 0 %! 1 2 0 1 0 2 %! 1 2 1 1 0 2 %! 0 1 2 2 2 2 %! ]); %! assert (regionprops (l, {"Extent"}), struct ("Extent", {0.55; 0.44})) %!test %! bw = logical ([0 0 0; 0 1 0; 0 0 0]); %! assert (regionprops (bw, {"MinorAxisLength", "MajorAxisLength", ... %! "Eccentricity", "Orientation"}), %! struct ("MajorAxisLength", 4 .* sqrt (1/12), %! "MinorAxisLength", 4 .* sqrt (1/12), %! "Eccentricity", 0, %! "Orientation", 0)) %!test %! a = eye (4); %! t = regionprops (a, "majoraxislength"); %! assert (t.MajorAxisLength, 6.4291, 1e-3); %! t = regionprops (a, "minoraxislength"); %! assert(t.MinorAxisLength, 1.1547 , 1e-3); %! t = regionprops (a, "eccentricity"); %! assert (t.Eccentricity, 0.98374 , 1e-3); %! t = regionprops (a, "orientation"); %! assert (t.Orientation, -45); %! t = regionprops (a, "equivdiameter"); %! assert (t.EquivDiameter, 2.2568, 1e-3); %!test %! b = ones (5); %! t = regionprops (b, "majoraxislength"); %! assert (t.MajorAxisLength, 5.7735 , 1e-3); %! t = regionprops (b, "minoraxislength"); %! assert (t.MinorAxisLength, 5.7735 , 1e-3); %! t = regionprops (b, "eccentricity"); %! assert (t.Eccentricity, 0); %! t = regionprops (b, "orientation"); %! assert (t.Orientation, 0); %! t = regionprops (b, "equivdiameter"); %! assert (t.EquivDiameter, 5.6419, 1e-3); %!test %! c = [0 0 1; 0 1 1; 1 1 0]; %! t = regionprops (c, "minoraxislength"); %! assert (t.MinorAxisLength, 1.8037 , 1e-3); %! t = regionprops (c, "majoraxislength"); %! assert (t.MajorAxisLength, 4.1633 , 1e-3); %! t = regionprops (c, "eccentricity"); %! assert (t.Eccentricity, 0.90128 , 1e-3); %! t = regionprops (c, "orientation"); %! assert (t.Orientation, 45); %! t = regionprops (c, "equivdiameter"); %! assert (t.EquivDiameter, 2.5231, 1e-3); ## Tests for multiple 'Orientation' thin cases (bug #49613) %!test %! bw = logical ([0 0 0 0; 0 1 1 0; 0 0 0 0]); %! props = regionprops (bw, "Orientation"); %! assert ([props.Orientation], 0, 0) %! %! props = regionprops (bw', "Orientation"); %! assert ([props.Orientation], 90, 0) %! %! bw = logical ([0 0 0 0; 0 1 1 0; 0 1 1 0; 0 0 0 0]); %! props = regionprops (bw, "Orientation"); %! assert ([props.Orientation], 0, 0) %! %! bw = logical ([1 1 0 0 0 ; 0 0 1 1 0 ; 0 0 0 0 0; 0 0 0 0 0]); %! props = regionprops (bw, "Orientation"); %! assert ([props.Orientation], -22.5, eps (22.5)) %! %! bw = logical ([ %! 1 1 0 0 1 %! 0 0 0 0 1 %! 0 0 0 0 0 %! 0 0 1 1 0 %! 1 0 1 1 0 %! 1 0 0 0 0 %! 0 1 0 0 0 %! 0 1 0 0 0]); %! props = regionprops (bw, "Orientation"); %! assert ([props.Orientation], [0 -67.5 0 90]) %!test %! f = [0 0 0 0; 1 1 1 1; 0 1 1 1; 0 0 0 0]; %! t = regionprops (f, "Extrema"); %! shouldbe = [0.5 1.5; 4.5 1.5; 4.5 1.5; 4.5 3.5; 4.5 3.5; 1.5 3.5; 0.5 2.5; 0.5 1.5]; %! assert (t.Extrema, shouldbe, eps); %!test %! bw = false (5); %! bw([8 12 13 14 18]) = true; %! extrema = [2 1; 3 1; 4 2; 4 3; 3 4; 2 4; 1 3; 1 2] + 0.5; %! assert (regionprops (bw, "extrema"), struct ("Extrema", extrema)) %!test %! ext1 = [1 0; 5 0; 6 1; 6 2; 2 3; 1 3; 1 3; 1 0] + 0.5; %! ext2 = [3 3; 6 3; 6 3; 6 5; 6 5; 2 5; 2 5; 2 4] + 0.5; %! assert (regionprops (bw2d, "extrema"), struct ("Extrema", {ext1; ext2})) %!assert (regionprops (bw2d, "equivDiameter"), %! struct ("EquivDiameter", {sqrt(4*8/pi); sqrt(4*6/pi)})) %!assert (regionprops (bw2d_over_bb, "equivDiameter"), %! struct ("EquivDiameter", {sqrt(4*7/pi); sqrt(4*8/pi); sqrt(4*4/pi)})) %!assert (regionprops (bw2d_insides, "equivDiameter"), %! struct ("EquivDiameter", {sqrt(4*20/pi); sqrt(4*4/pi)})) ## Test the diameter of a circle of diameter 21. %!test %! I = zeros (40); %! disk = fspecial ("disk",10); %! disk = disk ./ max (disk(:)); %! I(10:30, 10:30) = disk; %! bw = im2bw (I, 0.5); %! props = regionprops (bw, "PerimeterOld"); %! assert (props.PerimeterOld, 10*4 + (sqrt (2) * 4)*4, eps*100) %! props = regionprops (bw, "Perimeter"); %! assert (props.Perimeter, 59.876) %! %! props = regionprops (bwconncomp (bw), "PerimeterOld"); %! assert (props.PerimeterOld, 10*4 + (sqrt (2) * 4)*4, eps*100) %! props = regionprops (bwconncomp (bw), "Perimeter"); %! assert (props.Perimeter, 59.876) %!assert (regionprops (bw2d, "PerimeterOld"), %! struct ("PerimeterOld", {(sqrt (2)*6 + 4); (sqrt (2)*3 + 4)}), eps*10) %!assert (regionprops (bw2d, "Perimeter"), %! struct ("Perimeter", {11.81; 7.683})) ## Test Perimeter with nested objects %!assert (regionprops (bw2d_insides, "PerimeterOld"), %! struct ("PerimeterOld", {20; 4})) %!assert (regionprops (bw2d_insides, "Perimeter"), %! struct ("Perimeter", {19.236; 3.556})) %!assert (regionprops (bwconncomp (bw2d_insides), "PerimeterOld"), %! struct ("PerimeterOld", {20; 4})) %!assert (regionprops (bwconncomp (bw2d_insides), "Perimeter"), %! struct ("Perimeter", {19.236; 3.556})) ## Test ConvexHull, ConvexImage, ConvexArea and Solidity %!test %! BW = false (5); %! BW(2:4, 2:4) = true; # region with simple shape %! hull_test = [4.5 4; 4.5 2; 4 1.5; 2 1.5; 1.5 2; 1.5 4; 2 4.5; 4 4.5]; %! cimage_test = true(3); %! carea_test = 9; %! csolid_test = 1; %! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull = props.ConvexHull; %! # test only for existence of the correct corner points %! # because Matlab returns more points (than necessary) %! # (The correct shape of the ConvexHull results will only %! # be tested indirectly via the tests of ConvexArea.) %! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test)) %! assert (all (hull(1,:) == hull(end,:))) %! cimage = props.ConvexImage; %! assert (cimage, cimage_test); %! carea = props.ConvexArea; %! assert (carea, carea_test); %! csolid = props.Solidity; %! assert (csolid, csolid_test); %!test %! BW = logical ([... # region with non-trivial shape %! 0 0 0 0 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 1 1 0 0 0 0 0 0 0 %! 0 0 1 1 1 1 1 0 0 0 0 0 0 0 %! 0 1 1 1 1 1 1 0 0 0 0 0 0 0 %! 0 0 1 1 1 1 1 1 1 1 1 0 0 0 %! 0 0 0 1 1 1 1 1 1 1 1 1 0 0 %! 0 0 0 0 1 1 1 1 1 1 1 1 1 0 %! 0 0 0 0 0 1 1 1 0 1 1 1 1 0 %! 0 0 0 0 0 0 1 0 0 0 1 1 1 0 %! 0 0 0 0 0 0 0 0 0 0 0 0 0 0]); %! hull_test = [4 1.5; 1.5 4; 7 9.5; 13 9.5; 13.5 9; 13.5 7; 11 4.5; 7 1.5]; %! cimage_test = logical ([... %! 0 0 1 1 1 1 0 0 0 0 0 0 %! 0 1 1 1 1 1 1 1 0 0 0 0 %! 1 1 1 1 1 1 1 1 1 0 0 0 %! 0 1 1 1 1 1 1 1 1 1 0 0 %! 0 0 1 1 1 1 1 1 1 1 1 0 %! 0 0 0 1 1 1 1 1 1 1 1 1 %! 0 0 0 0 1 1 1 1 1 1 1 1 %! 0 0 0 0 0 1 1 1 1 1 1 1]); %! carea_test = 62; %! csolid_test = 0.8548; %! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull = props.ConvexHull; %! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test)) %! assert (all (hull(1,:) == hull(end,:))) %! cimage = props.ConvexImage; %! assert (cimage, cimage_test); %! carea = props.ConvexArea; %! assert (carea, carea_test); %! csolid = props.Solidity; %! assert (csolid, csolid_test, 1e-4); %!test %! BW = false (7); %! BW(2:6, 2:6) = true; %! BW(4,4) = false; # region with hole %! hull_test = [6.5 6; 6.5 2; 6 1.5; 2 1.5; 1.5 2; 1.5 6; 2 6.5; 6 6.5]; %! cimage_test = true(5); %! carea_test = 25; %! csolid_test = 0.96; %! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull = props.ConvexHull; %! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test)) %! assert (all (hull(1,:) == hull(end,:))) %! cimage = props.ConvexImage; %! assert (cimage, cimage_test); %! carea = props.ConvexArea; %! assert (carea, carea_test); %! csolid = props.Solidity; %! assert (csolid, csolid_test, 1e-4); %!test %! BW = false (5); %! BW(3, 3) = true; # region with single pixel %! hull_test = [3.5 3; 3 2.5; 2.5 3]; %! cimage_test = true; %! carea_test = 1; %! csolid_test = 1; %! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull = props.ConvexHull; %! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test)) %! assert (all (hull(1,:) == hull(end,:))) %! cimage = props.ConvexImage; %! assert (cimage, cimage_test); %! carea = props.ConvexArea; %! assert (carea, carea_test); %! csolid = props.Solidity; %! assert (csolid, csolid_test); %!test %! BW = false (5); %! BW(3, 2:4) = true; # regions with pixel line %! BW2 = BW'; %! hull_test = [2 2.5; 1.5 3; 2 3.5; 4 3.5; 4.5 3; 4 2.5]; %! hull_test2 = fliplr (hull_test); %! cimage_test = true(1,3); %! cimage_test2 = cimage_test'; %! carea_test = 3; %! csolid_test = 1; %! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull = props.ConvexHull; %! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test)) %! assert (all (hull(1,:) == hull(end,:))) %! cimage = props.ConvexImage; %! assert (cimage, cimage_test); %! carea = props.ConvexArea; %! assert (carea, carea_test); %! csolid = props.Solidity; %! assert (csolid, csolid_test); %! props2 = regionprops (BW2, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull2 = props2.ConvexHull; %! assert (sum (ismember (hull_test2, hull2, "rows")), rows (hull_test2)) %! assert (all (hull2(1,:) == hull2(end,:))) %! cimage2 = props2.ConvexImage; %! assert (cimage2, cimage_test2); %! carea2 = props2.ConvexArea; %! assert (carea2, carea_test); %! csolid2 = props2.Solidity; %! assert (csolid2, csolid_test); %!test %! BW = logical ([ ... %! 1 0 1 0 %! 1 0 1 0 %! 1 0 1 0 %! 1 0 1 0]); # two seperate regions %! hull_test_1 = [1.5 1; 1 0.5; 0.5 1; 0.5 4; 1 4.5; 1.5 4]; %! hull_test_2 = [3.5 1; 3 0.5; 2.5 1; 2.5 4; 3 4.5; 3.5 4]; %! cimage_test_1 = true(4,1); %! cimage_test_2 = true(4,1); %! carea_test1 = 4; %! carea_test2 = 4; %! csolid_test1 = 1; %! csolid_test2 = 1; %! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull1 = {props.ConvexHull}{1}; %! assert (sum (ismember (hull_test_1, hull1, "rows")), rows (hull_test_1)) %! assert (all (hull1(1,:) == hull1(end,:))) %! hull2 = {props.ConvexHull}{2}; %! assert (sum (ismember (hull_test_2, hull2, "rows")), rows (hull_test_2)) %! assert (all (hull2(1,:) == hull2(end,:))) %! cimage1 = {props.ConvexImage}{1}; %! assert (cimage1, cimage_test_1); %! cimage2 = {props.ConvexImage}{2}; %! assert (cimage2, cimage_test_2); %! carea1 = {props.ConvexArea}{1}; %! assert (carea1, carea_test1); %! carea2 = {props.ConvexArea}{2}; %! assert (carea2, carea_test2); %! csolid1 = {props.Solidity}{1}; %! assert (csolid1, csolid_test1); %! csolid2 = {props.Solidity}{2}; %! assert (csolid2, csolid_test2); %!test %! L = zeros (5); %! L(1:2:5, :) = 1; # labelled region with 3 disconnected parts %! hull_test = [5.5 5; 5.5 1; 5 0.5; 1 0.5; 0.5 1; 0.5 5; 1 5.5; 5 5.5]; %! cimage_test = true(5); %! carea_test = 25; %! csolid_test = 0.6; %! props = regionprops (L, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull = props.ConvexHull; %! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test)) %! assert (all (hull(1,:) == hull(end,:))) %! cimage = props.ConvexImage; %! assert (cimage, cimage_test); %! carea = props.ConvexArea; %! assert (carea, carea_test); %! csolid = props.Solidity; %! assert (csolid, csolid_test); %!xtest %! ## Matlab compatible, currently fails because of bug #50188 %! BW = false(4,16); %! BW(2,2) = true; %! BW(3,2:end-1) = true; # L-shaped region (small angle) %! hull_test = [2 1.5; 1.5 2; 1.5 3; 2 3.5; 15 3.5; 15.5 3; 15 2.5]; %! cimage_test = true (2,14); %! cimage_test(1, 8:end) = false; # this is the Matlab result %! carea_test = 21; %! csolid_test = 0.7143; %! props = regionprops (BW, {'ConvexHull', 'ConvexImage', 'ConvexArea', 'Solidity'}); %! hull = props.ConvexHull; %! assert (sum (ismember (hull_test, hull, "rows")), rows (hull_test)) %! assert (all (hull(1,:) == hull(end,:))) %! cimage = props.ConvexImage; %! assert (cimage, cimage_test); %! carea = props.ConvexArea; %! assert (carea, carea_test); %! csolid = props.Solidity; %! assert (csolid, csolid_test, 1e-4); ## Test guessing between labelled and binary image %!assert (regionprops ([1 0 1; 1 0 1], "Area"), struct ("Area", 4)) %!assert (regionprops ([1 0 2; 1 1 2], "Area"), struct ("Area", {3; 2})) ## Test missing labels %!assert (regionprops ([1 0 3; 1 1 3], "Area"), struct ("Area", {3; 0; 2})) ## Test dimensionality of struct array %!assert (size (regionprops ([1 0 0; 0 0 2], "Area")), [2, 1]) %!error regionprops ([1 -2 0 3]) %!error regionprops ([1 1.5 0 3]) ## Test for BW images with zero objects %!test %! im = rand (5); %! %! ## First do this so we get a list of all supported properties and don't %! ## have to update the list each time. %! bw = false (5); %! bw(13) = true; %! props = regionprops (bw, im, "all"); %! all_props = fieldnames (props); %! %! bw = false (5); %! props = regionprops (bw, im, "all"); %! assert (size (props), [0 1]) %! assert (sort (all_props), sort (fieldnames (props))) ## Test for labeled images with zeros objects %!test %! im = rand (5); %! %! ## First do this so we get a list of all supported properties and don't %! ## have to update the list each time. %! labeled = zeros (5); %! labeled(13) = 1; %! props = regionprops (labeled, im, "all"); %! all_props = fieldnames (props); %! %! labeled = zeros (5); %! props = regionprops (labeled, im, "all"); %! assert (size (props), [0 1]) %! assert (sort (all_props), sort (fieldnames (props))) ## Test for bwconncomp struct with zeros objects %!test %! im = rand (5); %! %! ## First do this so we get a list of all supported properties and don't %! ## have to update the list each time. %! bw = false (5); %! bw(13) = true; %! props = regionprops (bwconncomp (bw), im, "all"); %! all_props = fieldnames (props); %! %! bw = false (5); %! props = regionprops (bwconncomp (bw), im, "all"); %! assert (size (props), [0 1]) %! assert (sort (all_props), sort (fieldnames (props))) ## Test MajorAxisLength, MinorAxisLength, and Orientation with ## multiple regions (bug #49613) %!test %! bw = logical ([ %! 0 1 1 1 1 %! 0 1 1 0 0 %! 0 0 0 0 0 %! 0 0 0 1 0 %! 0 1 1 1 0]); %! props = regionprops (bw, "MajorAxisLength", "MinorAxisLength", %! "Orientation"); %! assert ([props.MajorAxisLength] ,[4.51354115 3.65148372], 1.e-8) %! assert ([props.MinorAxisLength], [2.01801654 1.82574186], 1.e-8) %! assert ([props.Orientation], [12.93317840 18.43494882], 1.e-8) ## Test warnings about invalid props for nd images and missing grayscale %!warning %! regionprops (rand (5, 5, 5) > 0.5, {"perimeter", "extrema"}); %!warning %! regionprops (rand (5, 5) > 0.5, {"minintensity", "weightedcentroid"}); ## Input check for labeled images %!error %! regionprops ([0 -1 3 4; 0 -1 3 4]) %!error %! regionprops ([0 1.5 3 4; 0 1.5 3 4]) %!error %! regionprops (int8 ([0 -1 3 4; 0 -1 3 4])) %!test # bug #52926 %! ## Perimeter of objects that would be connected with connectivity 8 %! ## but have been labeled with connectivity 4. %! BW = logical ([1 1 1 0 0 0 0 0 %! 1 1 1 0 1 1 0 0 %! 1 1 1 0 1 1 0 0 %! 1 1 1 0 0 0 1 0 %! 1 1 1 0 0 0 1 0 %! 1 1 1 0 0 0 1 0 %! 1 1 1 0 0 1 1 0 %! 1 1 1 0 0 0 0 0]); %! %! L = bwlabel (BW, 4); %! props = regionprops(L, "PerimeterOld"); %! assert ([props.PerimeterOld], [18 4 6+sqrt(2)]) %! props = regionprops(L, "Perimeter"); %! assert ([props.Perimeter], [17.276 3.556 7.013]) %! L = bwlabel (BW, 8); %! props = regionprops(L, "PerimeterOld"); %! assert ([props.PerimeterOld], [18 10+3*sqrt(2)]) %! props = regionprops(L, "Perimeter"); %! assert ([props.Perimeter], [17.276 13.108]) %!test %! I = zeros(5); %! I(3,3) = 1; %! props = regionprops(I, "Perimeter"); %! assert ([props.Perimeter], [0]) %! I = zeros(5); %! I(3,3:4) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [1.96]) %! I = zeros(5); %! I(3:4,3) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [1.96]) %! I = zeros(5); %! I(3,3) = 1; %! I(4,4) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [2.812]) %! I = zeros(5); %! I(3,4) = 1; %! I(4,3) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [2.812]) %! I = zeros(5); %! I(3:4,3:4) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [3.556]) %! I = zeros(5); %! I(3:4,3:4) = 1; %! I(4,5) = 1; %! props=regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [4.962]) %! I = zeros(5); %! I(3:4,3:4) = 1; %! I(5,5) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [6.277], 4*eps) %! I = zeros(5); %! I(2,3) = 1; %! I(3,2:4) = 1; %! I(4,3) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [5.624]) %! I = zeros(5); %! I(2,3) = 1; %! I(3,2:4) = 1; %! I(4,3) = 1; %! I(5,3) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [7.402], 4*eps) %! I = zeros(5); %! I(2,3) = 1; %! I(3,2:4) = 1; %! I(4,3) = 1; %! I(5,4) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [8.436]) %! I = zeros(5); %! I(2,1:4) = 1; %! I(3,4) = 1; %! props = regionprops (I, "Perimeter"); %! assert ([props.Perimeter], [7.013]) %!test # bug #61827 %! L = uint8 (checkerboard); %! props = regionprops (L, "Centroid"); %! assert ([props.Centroid], [40.5, 40.5], 1e-10) image-2.16.1/inst/PaxHeaders.61586/isrgb.m0000644000000000000000000000006215005110255014666 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/isrgb.m0000644000175000017500000000476615005110255016301 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## Copyright (C) 2004 Josep Monés i Teixidor ## Copyright (C) 2011, 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} isrgb (@var{img}) ## Return true if @var{img} is a RGB image. ## ## A variable can be considered a RGB image if it is a non-sparse, ## real array of size @nospell{MxNx3xK}, and: ## ## @itemize @bullet ## @item of floating point class with values in the [0 1] range or NaN; ## @item of class uint8, uint16, or int16. ## @end itemize ## ## @emph{Note}: despite their suggestive names, the functions isbw, ## isgray, isind, and isrgb, are ambiguous since it is not always possible ## to distinguish between those image types. For example: an uint8 matrix ## can be both a grayscale and indexed image; a grayscale image may have ## values outside the range [0 1]. They are good to dismiss input as an ## invalid image type, but not for identification. ## ## @seealso{rgb2gray, rgb2ind, isbw, isgray, isind} ## @end deftypefn function bool = isrgb (img) if (nargin != 1) print_usage; endif bool = false; if (isimage (img) && ndims (img) < 5 && size (img, 3) == 3) if (isfloat (img)) bool = ispart (@is_float_image, img); elseif (any (isa (img, {"uint8", "uint16", "int16"}))) bool = true; endif endif endfunction ## Non-matrix %!assert (isrgb ("this is not a RGB image"), false); ## Double matrix tests %!assert (isrgb (rand (5, 5)), false); %!assert (isrgb (rand (5, 5, 1, 5)), false); %!assert (isrgb (rand (5, 5, 3, 5)), true); %!assert (isrgb (rand (5, 5, 3)), true); %!assert (isrgb (ones (5, 5, 3)), true); %!assert (isrgb (ones (5, 5, 3) + eps), false); %!assert (isrgb (zeros (5, 5, 3) - eps), false); %!assert (isrgb (rand (5, 5, 3) > 0.5), false); %!assert (isrgb (randi ([-100 100], 5, 5, 3, "int16")), true) image-2.16.1/inst/PaxHeaders.61586/@imref3d0000644000000000000000000000013215005111723014755 xustar0030 mtime=1746179027.058725002 30 atime=1746179027.278726152 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/0000755000175000017500000000000015005111723016432 5ustar00avinoamavinoam00000000000000image-2.16.1/inst/@imref3d/PaxHeaders.61586/imref3d.m0000644000000000000000000000006215005110255016542 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/imref3d.m0000644000175000017500000003271015005110255020143 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{r} =} imref3d ## @deftypefnx {} {@var{r} =} imref3d (@var{imageSize}) ## @deftypefnx {} {@var{r} =} imref3d (@var{imageSize}, @var{pixelExtentInWorldX}, @var{pixelExtentInWorldY}, @var{pixelExtentInWorldZ}) ## @deftypefnx {} {@var{r} =} imref3d (@var{imageSize}, @var{xWorldLimits}, @var{yWorldLimits}, @var{zWorldLimits}) ## Reference 3-D image to world coordinates. ## ## Creates an imref3d object referencing a 3-D m-by-n-by-p image with the size ## @var{imageSize} to world coordinates. The world extent is either given ## by @var{xWorldLimits}, @var{yWorldLimits} and @var{xWorldLimits} or computed ## from @var{pixelExtentInWorldX}, @var{pixelExtentInWorldY} and ## @var{pixelExtentInWorldZ}. @var{imageSize} is [2, 2, 2] by default. ## ## Intrinsic coordinates are x = 1.0, y = 1.0, z = 1.0 in the center of the ## top left pixel in the first plane and x = n, y = m, z = p in the center ## of the bottom right pixel in the last plane. Spatial resolution in each ## dimension can be different. ## ## imref3d object has the following properties: ## ## ImageSize - two element integer vector with image height and width ## in pixels. ## ## XWorldLimits - limits of the image along the x-axis in world units ## specified as a two element real vector @code{[xMin, xMax]}. ## ## YWorldLimits - limits of the image along the y-axis in world units ## specified as a two element real vector @code{[yMin, yMax]}. ## ## ZWorldLimits - limits of the image along the z-axis in world units ## specified as a two element real vector @code{[zMin, zMax]}. ## ## PixelExtentInWorldX - pixel extent along the x-axis in world units ## specified as a real scalar. ## ## PixelExtentInWorldY - pixel extent along the y-axis in world units ## specified as a real scalar. ## ## PixelExtentInWorldZ - pixel extent along the z-axis in world units ## specified as a real scalar. ## ## ImageExtentInWorldX - image extent along the x-axis in world units ## specified as a real scalar. ## ## ImageExtentInWorldY - image extent along the y-axis in world units ## specified as a real scalar. ## ## ImageExtentInWorldZ - image extent along the z-axis in world units ## specified as a real scalar. ## ## XIntrinsicLimits - limits of the image along the x-axis in intrinsic ## units, equals to @code{[n - 0.5, n + 0.5]}. ## ## YIntrinsicLimits - limits of the image along the y-axis in intrinsic ## units, equals to @code{[m - 0.5, m + 0.5]}. ## ## ZIntrinsicLimits - limits of the image along the z-axis in intrinsic ## units, equals to @code{[p - 0.5, p + 0.5]}. ## ## @seealso{imref2d} ## @end deftypefn function r = imref3d (imageSize, varargin) if (nargin > 4) print_usage (); endif if (nargin == 0) imageSize = [2, 2, 2]; elseif (nargin > 0) validateattributes (imageSize, {"numeric"}, ... {"positive", "integer", "vector", "size", [1, 3]}, "imref3d", "imageSize"); imageSize = imageSize(1:3); endif m = imageSize(1); n = imageSize(2); p = imageSize(3); if (numel (varargin) == 0) xWorldLimits = [0.5, n + 0.5]; yWorldLimits = [0.5, m + 0.5]; zWorldLimits = [0.5, p + 0.5]; r2 = @imref2d (imageSize); elseif (numel (varargin) == 3) if (isscalar (varargin{1})) validateattributes (varargin{1}, {"numeric"}, ... {"real", "positive", "scalar"}, "imref3d", "pixelExtentInWorldX"); validateattributes (varargin{2}, {"numeric"}, ... {"real", "positive", "scalar"}, "imref3d", "pixelExtentInWorldY"); validateattributes (varargin{3}, {"numeric"}, ... {"real", "positive", "scalar"}, "imref3d", "pixelExtentInWorldZ"); pixelExtentInWorldX = varargin{1}; pixelExtentInWorldY = varargin{2}; pixelExtentInWorldZ = varargin{3}; else validateattributes (varargin{1}, {"numeric"}, ... {"real", "increasing", "vector", "size", [1, 2]}, "imref3d", ... "xWorldLimits"); validateattributes (varargin{2}, {"numeric"}, ... {"real", "increasing", "vector", "size", [1, 2]}, "imref3d", ... "yWorldLimits"); validateattributes (varargin{3}, {"numeric"}, ... {"real", "increasing", "vector", "size", [1, 2]}, "imref3d", ... "zWorldLimits"); xWorldLimits = varargin{1}; yWorldLimits = varargin{2}; zWorldLimits = varargin{3}; endif endif if (exist ("pixelExtentInWorldX") && exist ("pixelExtentInWorldY") ... && exist ("pixelExtentInWorldZ")) imageExtentInWorldX = pixelExtentInWorldX * m; imageExtentInWorldY = pixelExtentInWorldY * n; imageExtentInWorldZ = pixelExtentInWorldZ * p; xWorldLimits = [pixelExtentInWorldX / 2, imageExtentInWorldX + ... pixelExtentInWorldX / 2]; yWorldLimits = [pixelExtentInWorldY / 2, imageExtentInWorldY + ... pixelExtentInWorldY / 2]; zWorldLimits = [pixelExtentInWorldZ / 2, imageExtentInWorldZ + ... pixelExtentInWorldZ / 2]; elseif (exist ("xWorldLimits") && exist ("yWorldLimits") ... && exist ("zWorldLimits")) imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1); imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1); imageExtentInWorldZ = zWorldLimits(2) - zWorldLimits(1); pixelExtentInWorldX = imageExtentInWorldX / n; pixelExtentInWorldY = imageExtentInWorldY / m; pixelExtentInWorldZ = imageExtentInWorldZ / p; endif xIntrinsicLimits = [0.5, n + 0.5]; yIntrinsicLimits = [0.5, m + 0.5]; zIntrinsicLimits = [0.5, p + 0.5]; r.ImageSize = imageSize; r.XWorldLimits = xWorldLimits; r.YWorldLimits = yWorldLimits; r.ZWorldLimits = zWorldLimits; r.PixelExtentInWorldX = pixelExtentInWorldX; r.PixelExtentInWorldY = pixelExtentInWorldY; r.PixelExtentInWorldZ = pixelExtentInWorldZ; r.ImageExtentInWorldX = imageExtentInWorldX; r.ImageExtentInWorldY = imageExtentInWorldY; r.ImageExtentInWorldZ = imageExtentInWorldZ; r.XIntrinsicLimits = xIntrinsicLimits; r.YIntrinsicLimits = yIntrinsicLimits; r.ZIntrinsicLimits = zIntrinsicLimits; ## in MATLAB imref3d isa imref2d r = class (r, "imref3d", @imref2d()); endfunction %!error id=Octave:invalid-fun-call imref3d (1, 2, 3, 4, 5) %!error id=Octave:incorrect-size imref3d (42) %!error id=Octave:incorrect-size imref3d ([42]) %!error id=Octave:incorrect-size imref3d ([4, 2]) %!error id=Octave:incorrect-size imref3d ([4, 2, 3, 3]) %!error id=Octave:expected-integer imref3d ([4.2, 42]) %!error id=Octave:expected-positive imref3d ([0, 0]) %!error id=Octave:expected-positive imref3d ([-4, 2]) %!error id=Octave:expected-positive imref3d ([4, 2, 3], 0, 1, 2) %!error id=Octave:expected-positive imref3d ([4, 2, 3], 1, 0, 2) %!error id=Octave:expected-positive imref3d ([4, 2, 3], 1, 2, 0) %!error id=Octave:expected-real imref3d ([4, 2, 3], j, 1, 2) %!error id=Octave:expected-real imref3d ([4, 2, 3], 1, j, 2) %!error id=Octave:expected-real imref3d ([4, 2, 3], 1, 2, j) %!error id=Octave:expected-real imref3d ([4, 2, 3], [j, 2], [3, 4], [5, 6]) %!error id=Octave:expected-real imref3d ([4, 2, 3], [1, 2], [j, 4], [5, 6]) %!error id=Octave:expected-real imref3d ([4, 2, 3], [1, 2], [3, 4], [5, j]) %!error id=Octave:expected-vector imref3d ([4, 2, 3], [], [], []) %!error id=Octave:expected-vector imref3d ([4, 2, 3], [], [1], [2]) %!error id=Octave:expected-scalar imref3d ([4, 2, 3], [1], [], []) %!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3, 4], [0]) %!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3, 4, 5], [6, 7]) %!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3, 4], [5, 6, 7]) %!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1; 2], [3, 4], [5, 6]) %!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3; 4], [5, 6]) %!error id=Octave:incorrect-size imref3d ([4, 2, 3], [1, 2], [3, 4], [5; 6]) %!error id=Octave:invalid-indexing imref3d().InvalidProperty %!error id=Octave:expected-increasing imref3d ([100, 200, 3], [1.5 0.5], [2.5, 3.5], [0.5, 1.5]) %!error id=Octave:expected-increasing imref3d ([100, 200, 3], [1.5 2.5], [2.5, 1.5], [0.5, 1.5]) %!error id=Octave:expected-increasing imref3d ([100, 200, 3], [1.5 2.5], [2.5, 3.5], [1.5, 0.5]) %!assert (imref3d ([4, 2, 3]).ImageSize, [4, 2, 3]) %!test %! r = imref3d; %! assert (r.XWorldLimits, [0.5, 2.5]) %! assert (r.YWorldLimits, [0.5, 2.5]) %! assert (r.ZWorldLimits, [0.5, 2.5]) %! assert (r.ImageSize, [2, 2, 2]) %! assert (r.PixelExtentInWorldX, 1) %! assert (r.PixelExtentInWorldY, 1) %! assert (r.PixelExtentInWorldZ, 1) %! assert (r.ImageExtentInWorldX, 2) %! assert (r.ImageExtentInWorldY, 2) %! assert (r.ImageExtentInWorldZ, 2) %! assert (r.XIntrinsicLimits, [0.5, 2.5]) %! assert (r.YIntrinsicLimits, [0.5, 2.5]) %! assert (r.ZIntrinsicLimits, [0.5, 2.5]) %!test %! r = imref3d ([128, 128, 27]); %! assert (r.XWorldLimits, [0.5, 128.5]) %! assert (r.YWorldLimits, [0.5, 128.5]) %! assert (r.ZWorldLimits, [0.5, 27.5]) %! assert (r.ImageSize, [128, 128, 27]) %! assert (r.PixelExtentInWorldX, 1) %! assert (r.PixelExtentInWorldY, 1) %! assert (r.PixelExtentInWorldZ, 1) %! assert (r.ImageExtentInWorldX, 128) %! assert (r.ImageExtentInWorldY, 128) %! assert (r.ImageExtentInWorldZ, 27) %! assert (r.XIntrinsicLimits, [0.5, 128.5]) %! assert (r.YIntrinsicLimits, [0.5, 128.5]) %! assert (r.ZIntrinsicLimits, [0.5, 27.5]) %!test %! r = imref3d ([128, 128, 27], 2, 2, 4); %! assert (r.XWorldLimits, [1, 257]) %! assert (r.YWorldLimits, [1, 257]) %! assert (r.ZWorldLimits, [2, 110]) %! assert (r.ImageSize, [128, 128, 27]) %! assert (r.PixelExtentInWorldX, 2) %! assert (r.PixelExtentInWorldY, 2) %! assert (r.PixelExtentInWorldZ, 4) %! assert (r.ImageExtentInWorldX, 256) %! assert (r.ImageExtentInWorldY, 256) %! assert (r.ImageExtentInWorldZ, 108) %! assert (r.XIntrinsicLimits, [0.5, 128.5]) %! assert (r.YIntrinsicLimits, [0.5, 128.5]) %! assert (r.ZIntrinsicLimits, [0.5, 27.5]) ## changing ImageSize %!test %! r = imref3d; %! assert (r.XWorldLimits, [0.5, 2.5]) %! assert (r.YWorldLimits, [0.5, 2.5]) %! assert (r.ZWorldLimits, [0.5, 2.5]) %! assert (r.ImageSize, [2, 2, 2]) %! assert (r.PixelExtentInWorldX, 1) %! assert (r.PixelExtentInWorldY, 1) %! assert (r.PixelExtentInWorldZ, 1) %! assert (r.ImageExtentInWorldX, 2) %! assert (r.ImageExtentInWorldY, 2) %! assert (r.ImageExtentInWorldZ, 2) %! assert (r.XIntrinsicLimits, [0.5, 2.5]) %! assert (r.YIntrinsicLimits, [0.5, 2.5]) %! assert (r.ZIntrinsicLimits, [0.5, 2.5]) %! r.ImageSize = [128, 128, 27]; %! assert (r.XWorldLimits, [0.5, 2.5]) %! assert (r.YWorldLimits, [0.5, 2.5]) %! assert (r.ZWorldLimits, [0.5, 2.5]) %! assert (r.ImageSize, [128, 128, 27]) %! assert (r.PixelExtentInWorldX, 0.015625, 1e-6) %! assert (r.PixelExtentInWorldY, 0.015625, 1e-6) %! assert (r.PixelExtentInWorldZ, 0.074074, 1e-6) %! assert (r.ImageExtentInWorldX, 2) %! assert (r.ImageExtentInWorldY, 2) %! assert (r.ImageExtentInWorldZ, 2) %! assert (r.XIntrinsicLimits, [0.5, 128.5]) %! assert (r.YIntrinsicLimits, [0.5, 128.5]) %! assert (r.ZIntrinsicLimits, [0.5, 27.5]) ## changing XWorldLimits, YWorldLimits and ZWorldLimits %!test %! r = imref3d; %! assert (r.XWorldLimits, [0.5, 2.5]) %! assert (r.YWorldLimits, [0.5, 2.5]) %! assert (r.ZWorldLimits, [0.5, 2.5]) %! assert (r.ImageSize, [2, 2, 2]) %! assert (r.PixelExtentInWorldX, 1) %! assert (r.PixelExtentInWorldY, 1) %! assert (r.PixelExtentInWorldZ, 1) %! assert (r.ImageExtentInWorldX, 2) %! assert (r.ImageExtentInWorldY, 2) %! assert (r.ImageExtentInWorldZ, 2) %! assert (r.XIntrinsicLimits, [0.5, 2.5]) %! assert (r.YIntrinsicLimits, [0.5, 2.5]) %! assert (r.ZIntrinsicLimits, [0.5, 2.5]) %! r.XWorldLimits = [-60, 13.33]; %! r.YWorldLimits = [-900.8, -560.26]; %! r.ZWorldLimits = [-302.48, 1500.333]; %! assert (r.XWorldLimits, [-60, 13.33]) %! assert (r.YWorldLimits, [-900.8, -560.26]) %! assert (r.ZWorldLimits, [-302.48, 1500.333]) %! assert (r.ImageSize, [2, 2, 2]) %! assert (r.PixelExtentInWorldX, 36.6650) %! assert (r.PixelExtentInWorldY, 170.27, 1e-5) %! assert (r.PixelExtentInWorldZ, 901.4065) %! assert (r.ImageExtentInWorldX, 73.33, 1e-5) %! assert (r.ImageExtentInWorldY, 340.54, 1e-5) %! assert (r.ImageExtentInWorldZ, 1802.813, 1e-5) %! assert (r.XIntrinsicLimits, [0.5, 2.5]) %! assert (r.YIntrinsicLimits, [0.5, 2.5]) %! assert (r.ZIntrinsicLimits, [0.5, 2.5]) %!test %! r = imref3d; %! fail ("r.XWorldLimits = []", "") %! fail ("r.XWorldLimits = [1]", "") %! fail ("r.XWorldLimits = [j]", "") %! fail ("r.XWorldLimits = [1; 2]", "") %! fail ("r.YWorldLimits = []", "") %! fail ("r.YWorldLimits = [1]", "") %! fail ("r.YWorldLimits = [j]", "") %! fail ("r.YWorldLimits = [1; 2]", "") %! fail ("r.ZWorldLimits = []", "") %! fail ("r.ZWorldLimits = [1]", "") %! fail ("r.ZWorldLimits = [j]", "") %! fail ("r.ZWorldLimits = [1; 2]", "")image-2.16.1/inst/@imref3d/PaxHeaders.61586/contains.m0000644000000000000000000000006215005110255017027 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/contains.m0000644000175000017500000000557615005110255020442 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{tf} =} contains (@var{r}, @var{xWorld}, @var{yWorld}, @var{zWorld}) ## Determine if image contains points in world coordinate system. ## ## Outputs a logical array @var{tf}, where the i-th nonzero value means the ## point (@var{xWorld}(i), @var{yWorld}(i), @var{zWorld}(i)) lies within the ## bounds of an image associated with a spatial referencing object @var{r}. ## ## @seealso{imref2d, imref3d} ## @end deftypefn function tf = contains (r, xWorld, yWorld, zWorld) if (nargin != 4) print_usage(); endif validateattributes (xWorld, {"numeric"}, ... {"real"}, "imref3d", "xWorld"); validateattributes (yWorld, {"numeric"}, ... {"real"}, "imref3d", "yWorld"); validateattributes (zWorld, {"numeric"}, ... {"real"}, "imref3d", "zWorld"); if (! all (size (xWorld) == size (yWorld)) ... || ! all (size (xWorld) == size (zWorld))) error ("Octave:invalid-input-arg", ... "imref3d/contains: xWorld, yWorld and zWorld must be of the same size"); endif xWorldLimits = r.XWorldLimits; yWorldLimits = r.YWorldLimits; zWorldLimits = r.ZWorldLimits; containsX = xWorld >= xWorldLimits(1) & xWorld <= xWorldLimits(2); containsY = yWorld >= yWorldLimits(1) & yWorld <= yWorldLimits(2); containsZ = zWorld >= zWorldLimits(1) & zWorld <= zWorldLimits(2); tf = containsX & containsY & containsZ; endfunction %!error id=Octave:invalid-fun-call contains (imref3d) %!error id=Octave:invalid-fun-call contains (imref3d, 1) %!error id=Octave:invalid-fun-call contains (imref3d, 1, 2) %!error id=Octave:invalid-fun-call contains (imref3d, 1, 2, 3, 4) %!error id=Octave:invalid-input-arg contains (imref3d, [1, 2], 3, 4) %!error id=Octave:invalid-input-arg contains (imref3d, 1, [2, 3], 4) %!error id=Octave:invalid-input-arg contains (imref3d, 1, 2, [3, 4]) %!error id=Octave:expected-real contains (imref3d, 1j, 2, 3) %!error id=Octave:expected-real contains (imref3d, 1, 2j, 3) %!error id=Octave:expected-real contains (imref3d, 1, 2, 3j) %!test %! r = imref3d ([128, 128, 27]); %! assert (contains (r, [5, 6, 6, 8], [5, 10, 10, 257], [1, 27.5, 28, 1]), logical ([1, 1, 0, 0]))image-2.16.1/inst/@imref3d/PaxHeaders.61586/intrinsicToWorld.m0000644000000000000000000000006215005110255020526 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/intrinsicToWorld.m0000644000175000017500000000775515005110255022142 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {[@var{xWorld}, @var{yWorld}, @var{zWorld}] =} intrinsicToWorld (@var{r}, @var{xIntrinsic}, @var{yIntrinsic}, @var{zIntrinsic}) ## Convert from intrinsic to world coordinates. ## ## Converts intrinsic coordinates of the image associated with the spatial ## referencing object @var{r} to world coordinates @var{xWorld}, @var{yWorld} ## and @var{zWorld}. If a point ## (@var{xIntrinsic}(i), @var{yIntrinsic}(i), @var{zIntrinsic}(i)) ## falls outside the intrinsic bounds of the image, the world coordinates are ## extrapolated, possibly resulting in negative values. ## ## @seealso{imref2d, imref3d, worldToIntrinsic} ## @end deftypefn function [xWorld, yWorld, zWorld] = intrinsicToWorld (r, xIntrinsic, yIntrinsic, zIntrinsic) if (nargin != 4) print_usage (); endif validateattributes (xIntrinsic, {"numeric"}, ... {"real"}, "imref3d", "xIntrinsic"); validateattributes (yIntrinsic, {"numeric"}, ... {"real"}, "imref3d", "yIntrinsic"); validateattributes (zIntrinsic, {"numeric"}, ... {"real"}, "imref3d", "zIntrinsic"); if (! all (size (xIntrinsic) == size (yIntrinsic)) ... || ! all (size (xIntrinsic) == size (zIntrinsic))) error ("Octave:invalid-input-arg", ... "xIntrinsic, yIntrinsic and zIntrinsic must be of the same size"); endif xIntrinsicLimits = r.XIntrinsicLimits; yIntrinsicLimits = r.YIntrinsicLimits; zIntrinsicLimits = r.ZIntrinsicLimits; xWorldLimits = r.XWorldLimits; yWorldLimits = r.YWorldLimits; zWorldLimits = r.ZWorldLimits; xWorld = xWorldLimits(1) + r.PixelExtentInWorldX * ... (xIntrinsic - xIntrinsicLimits(1)); yWorld = yWorldLimits(1) + r.PixelExtentInWorldY * ... (yIntrinsic - yIntrinsicLimits(1)); zWorld = zWorldLimits(1) + r.PixelExtentInWorldZ * ... (zIntrinsic - zIntrinsicLimits(1)); endfunction %!error id=Octave:invalid-fun-call intrinsicToWorld (imref3d) %!error id=Octave:invalid-fun-call intrinsicToWorld (imref3d, 1) %!error id=Octave:invalid-fun-call intrinsicToWorld (imref3d, 1, 2) %!error id=Octave:invalid-fun-call intrinsicToWorld (imref3d, 1, 2, 3, 4) %!error id=Octave:expected-real intrinsicToWorld (imref3d, 1j, 2, 3) %!error id=Octave:expected-real intrinsicToWorld (imref3d, 1, 2j, 3) %!error id=Octave:expected-real intrinsicToWorld (imref3d, 1, j, 3j) %!error id=Octave:invalid-input-arg intrinsicToWorld (imref3d, [1, 2], 3, 4) %!error id=Octave:invalid-input-arg intrinsicToWorld (imref3d, 1, [2, 3], 4) %!error id=Octave:invalid-input-arg intrinsicToWorld (imref3d, 1, 2, [3, 4]) %!test %! r = imref3d ([128, 128, 27], 2, 2, 4); %! xI = [54, 71, 57, 70]; %! yI = [46, 48, 79, 80]; %! zI = [13, 13, 13, 13]; %! [xW, yW, zW] = intrinsicToWorld (r, xI, yI, zI); %! assert (xW, [108, 142, 114, 140]) %! assert (yW, [92, 96, 158, 160]) %! assert (zW, [52, 52, 52, 52]) %!test %! [xW, yW, zW] = intrinsicToWorld (imref3d, -5.3, -2.8, -15.88); %! assert (xW, -5.3) %! assert (yW, -2.8) %! assert (zW, -15.88, 1e-6) %!test %! [xW, yW, zW] = intrinsicToWorld (imref3d, [1, 2; 3, 4], %! [2, 3; 5, 9], %! [-5, 8; 19, 42.8]); %! assert (xW, [1, 2; 3, 4]) %! assert (yW, [2, 3; 5, 9]) %! assert (zW, [-5, 8; 19, 42.8]) image-2.16.1/inst/@imref3d/PaxHeaders.61586/subsref.m0000644000000000000000000000006215005110255016662 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/subsref.m0000644000175000017500000000434715005110255020270 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{r} =} subref (@var{val}, @var{idx}) ## ## @seealso{} ## @end deftypefn function r = subsref (val, idx) if (strcmp (idx(1).type, ".")) switch (idx(1).subs) case "ImageSize" r = val.ImageSize; case "XWorldLimits" r = val.XWorldLimits; case "YWorldLimits" r = val.YWorldLimits; case "ZWorldLimits" r = val.ZWorldLimits; case "PixelExtentInWorldX" r = val.PixelExtentInWorldX; case "PixelExtentInWorldY" r = val.PixelExtentInWorldY; case "PixelExtentInWorldZ" r = val.PixelExtentInWorldZ; case "ImageExtentInWorldX" r = val.ImageExtentInWorldX; case "ImageExtentInWorldY" r = val.ImageExtentInWorldY; case "ImageExtentInWorldZ" r = val.ImageExtentInWorldZ; case "XIntrinsicLimits" r = val.XIntrinsicLimits; case "YIntrinsicLimits" r = val.YIntrinsicLimits; case "ZIntrinsicLimits" r = val.ZIntrinsicLimits; otherwise error ("Octave:invalid-indexing", ... strcat ("unknown property '", idx(1).subs, "' for class imref3d")); endswitch if (length (idx) > 1) switch (idx(2).type) case "()" i = idx(2).subs; r = r(i{1}); otherwise error ("Octave:invalid-indexing", ... strcat ("can't index '", idx(1).subs, "' with ", idx(2).type)); endswitch endif endif endfunction image-2.16.1/inst/@imref3d/PaxHeaders.61586/disp.m0000644000000000000000000000006215005110255016150 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/disp.m0000644000175000017500000000334715005110255017555 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} disp (@var{r}) ## ## @seealso{} ## @end deftypefn function disp (r) printf("%s with properties:\n", class (r)); printf("\n"); printf(" XWorldLimits: [%d %d]\n", r.XWorldLimits); printf(" YWorldLimits: [%d %d]\n", r.YWorldLimits); printf(" ZWorldLimits: [%d %d]\n", r.ZWorldLimits); printf(" ImageSize: [%d %d %d]\n", r.ImageSize); printf(" PixelExtentInWorldX: %d\n", r.PixelExtentInWorldX); printf(" PixelExtentInWorldY: %d\n", r.PixelExtentInWorldY); printf(" PixelExtentInWorldZ: %d\n", r.PixelExtentInWorldZ); printf(" ImageExtentInWorldX: %d\n", r.ImageExtentInWorldX); printf(" ImageExtentInWorldY: %d\n", r.ImageExtentInWorldY); printf(" ImageExtentInWorldZ: %d\n", r.ImageExtentInWorldZ); printf(" XIntrinsicLimits: [%d %d]\n", r.XIntrinsicLimits); printf(" YIntrinsicLimits: [%d %d]\n", r.YIntrinsicLimits); printf(" ZIntrinsicLimits: [%d %d]\n", r.ZIntrinsicLimits); endfunction image-2.16.1/inst/@imref3d/PaxHeaders.61586/subsasgn.m0000644000000000000000000000006215005110255017036 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/subsasgn.m0000644000175000017500000000732715005110255020445 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{rout} =} subsasgn (@var{r}, @var{index}, @var{val}) ## ## @seealso{} ## @end deftypefn function rout = subsasgn (r, index, val) switch (index.type) case "." fld = index.subs; switch (fld) case "ImageSize" imageSize = val; if (length (imageSize) < 3) error ("Octave:invalid-input-arg", ... "ImageSize must have three elements"); endif validateattributes (imageSize, {"numeric"}, ... {"positive", "integer", "vector"}, "imref3d", "imageSize"); m = imageSize(1); n = imageSize(2); p = imageSize(3); rout = r; rout.ImageSize = imageSize; rout.PixelExtentInWorldX = r.ImageExtentInWorldX / n; rout.PixelExtentInWorldY = r.ImageExtentInWorldY / m; rout.PixelExtentInWorldZ = r.ImageExtentInWorldZ / p; rout.XIntrinsicLimits = [0.5, n + 0.5]; rout.YIntrinsicLimits = [0.5, m + 0.5]; rout.ZIntrinsicLimits = [0.5, p + 0.5]; case "XWorldLimits" xWorldLimits = val; validateattributes (xWorldLimits, {"numeric"}, ... {"increasing", "real", "vector", "size", [1, 2]}, ... "imref3d", "xWorldLimits"); imageSize = r.ImageSize; imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1); rout = r; rout.XWorldLimits = val; rout.ImageExtentInWorldX = imageExtentInWorldX; rout.PixelExtentInWorldX = imageExtentInWorldX / imageSize(2); case "YWorldLimits" yWorldLimits = val; validateattributes (yWorldLimits, {"numeric"}, ... {"increasing", "real", "vector", "size", [1, 2]}, ... "imref3d", "yWorldLimits"); imageSize = r.ImageSize; imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1); rout = r; rout.YWorldLimits = val; rout.ImageExtentInWorldY = imageExtentInWorldY; rout.PixelExtentInWorldY = imageExtentInWorldY / imageSize(1); case "ZWorldLimits" zWorldLimits = val; validateattributes (zWorldLimits, {"numeric"}, ... {"increasing", "real", "vector", "size", [1, 2]}, ... "imref3d", "zWorldLimits"); imageSize = r.ImageSize; imageExtentInWorldZ = zWorldLimits(2) - zWorldLimits(1); rout = r; rout.ZWorldLimits = val; rout.ImageExtentInWorldZ = imageExtentInWorldZ; rout.PixelExtentInWorldZ = imageExtentInWorldZ / imageSize(3); otherwise error ("Octave:invalid-indexing", ... "@imref3d/subsasgn: invalid property '%s'", fld); endswitch otherwise error ("Octave:invalid-indexing", "@imref3d/subsasgn: invalid index type") endswitch endfunctionimage-2.16.1/inst/@imref3d/PaxHeaders.61586/worldToIntrinsic.m0000644000000000000000000000006215005110255020526 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/worldToIntrinsic.m0000644000175000017500000000677715005110255022145 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {[@var{xIntrinsic}, @var{yIntrinsic}, @var{zIntrinsic}] =} worldToIntrinsic (@var{r}, @var{xWorld}, @var{yWorld}, @var{zWorld}) ## Convert from world to intrinsic coordinates. ## ## Converts world coordinates @var{xWorld}, @var{yWorld} and @var{zWorld} to ## intrinsic coordinates @var{xIntrinsic}, @var{yIntrinsic} and @var{zIntrinsic} ## of an image associated with the spatial referencing object @var{r}. If a ## point (@var{xWorld}(i), @var{yWorld}(i), @var{zWorld}(i)) falls outside ## the bounds of the image, its intrinsic coordinates are extrapolated, ## possibly resulting in negative values. ## ## @seealso{imref2d, imref3d, intrinsicToWorld} ## @end deftypefn function [xIntrinsic, yIntrinsic, zIntrinsic] = worldToIntrinsic (r, xWorld, yWorld, zWorld) if (nargin != 4) print_usage (); endif validateattributes (xWorld, {"numeric"}, ... {"real"}, "imref3d", "xWorld"); validateattributes (yWorld, {"numeric"}, ... {"real"}, "imref3d", "yWorld"); validateattributes (zWorld, {"numeric"}, ... {"real"}, "imref3d", "zWorld"); if (! all (size (xWorld) == size (yWorld)) ... || ! all (size (xWorld) == size (zWorld))) error ("Octave:invalid-input-arg", ... "xWorld, yWorld and zWorld must be of the same size"); endif xIntrinsicLimits = r.XIntrinsicLimits; yIntrinsicLimits = r.YIntrinsicLimits; zIntrinsicLimits = r.ZIntrinsicLimits; xWorldLimits = r.XWorldLimits; yWorldLimits = r.YWorldLimits; zWorldLimits = r.ZWorldLimits; xIntrinsic = xIntrinsicLimits(1) + (xWorld - xWorldLimits(1)) ... / r.PixelExtentInWorldX; yIntrinsic = yIntrinsicLimits(1) + (yWorld - yWorldLimits(1)) ... / r.PixelExtentInWorldY; zIntrinsic = zIntrinsicLimits(1) + (zWorld - zWorldLimits(1)) ... / r.PixelExtentInWorldZ; endfunction %!error id=Octave:invalid-fun-call worldToIntrinsic (imref3d) %!error id=Octave:invalid-fun-call worldToIntrinsic (imref3d, 1, 2) %!error id=Octave:invalid-fun-call worldToIntrinsic (imref3d, 1, 2, 3, 4) %!error id=Octave:expected-real worldToIntrinsic (imref3d, 1j, 2, 3) %!error id=Octave:expected-real worldToIntrinsic (imref3d, 1, 2j, 3) %!error id=Octave:expected-real worldToIntrinsic (imref3d, 1, 2, 3j) %!error id=Octave:invalid-input-arg worldToIntrinsic (imref3d, [1, 2], 3, 4) %!error id=Octave:invalid-input-arg worldToIntrinsic (imref3d, 1, [2, 3], 4) %!error id=Octave:invalid-input-arg worldToIntrinsic (imref3d, 1, 2, [3, 4]) %!test %! r = imref3d ([128, 128, 27], 2, 2, 4); %! xW = [108, 108, 108.2, 2]; %! yW = [92, 92, 92, -1]; %! zW = [52, 55, 52, 0.33]; %! [xI, yI, zI] = worldToIntrinsic (r, xW, yW, zW); %! assert (xI, [54, 54, 54.1, 1], 1e-6) %! assert (yI, [46, 46, 46, -0.5], 1e-6) %! assert (zI, [13, 13.75, 13, 0.0825], 1e-6)image-2.16.1/inst/@imref3d/PaxHeaders.61586/worldToSubscript.m0000644000000000000000000000006215005110255020542 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref3d/worldToSubscript.m0000644000175000017500000000674215005110255022151 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {[@var{i}, @var{j}, @var{k}] =} worldToSubscript (@var{r}, @var{xWorld}, @var{yWorld}, @var{zWorld}) ## Convert world coordinates to row, column and plane subscripts. ## ## Converts world coordinates to row, column and plane subscripts of an image ## associated with the spatial referencing object @var{r}. A point located at ## (@var{xWorld}(i), @var{yWorld}(i), @var{zWorld}(i)) world coordinates maps ## to row, column and plane subscripts @var{i}(i), @var{j}(i), @var{k}(i) ## respectively. Note the reversed order of the first two dimensions. If the ## point falls outside the bounds of the image, all of its subscripts are NaN. ## ## @seealso{imref2d, imref3d, worldToIntrinsic} ## @end deftypefn function [i, j, k] = worldToSubscript (r, xWorld, yWorld, zWorld) if (nargin != 4) print_usage (); endif validateattributes (xWorld, {"numeric"}, ... {"real"}, "imref3d", "xWorld"); validateattributes (yWorld, {"numeric"}, ... {"real"}, "imref3d", "yWorld"); validateattributes (zWorld, {"numeric"}, ... {"real"}, "imref3d", "zWorld"); if (! all (size (xWorld) == size (yWorld)) ... || ! all (size (xWorld) == size (zWorld))) error ("Octave:invalid-input-arg", ... "xWorld, yWorld and zWorld must be of the same size"); endif [xIntrinsic, yIntrinsic, zIntrinsic] ... = worldToIntrinsic (r, xWorld, yWorld, zWorld); xIntrinsicLimits = r.XIntrinsicLimits; yIntrinsicLimits = r.YIntrinsicLimits; zIntrinsicLimits = r.ZIntrinsicLimits; inImage = contains (r, xWorld, yWorld, zWorld); xIntrinsic(! inImage) = NaN; yIntrinsic(! inImage) = NaN; zIntrinsic(! inImage) = NaN; i = round (yIntrinsic); j = round (xIntrinsic); k = round (zIntrinsic); endfunction %!error id=Octave:invalid-fun-call worldToSubscript (imref3d) %!error id=Octave:invalid-fun-call worldToSubscript (imref3d, 1) %!error id=Octave:invalid-fun-call worldToSubscript (imref3d, 1, 2) %!error id=Octave:invalid-fun-call worldToSubscript (imref3d, 1, 2, 3, 4) %!error id=Octave:expected-real worldToSubscript (imref3d, 1j, 2, 3) %!error id=Octave:expected-real worldToSubscript (imref3d, 1, 2j, 3) %!error id=Octave:expected-real worldToSubscript (imref3d, 1, 2, 3j) %!error id=Octave:invalid-input-arg worldToSubscript (imref3d, [1, 2], 3, 4) %!error id=Octave:invalid-input-arg worldToSubscript (imref3d, 1, [2, 3], 4) %!error id=Octave:invalid-input-arg worldToSubscript (imref3d, 1, 2, [3, 4]) %!test %! r = imref3d ([128, 128, 27], 2, 2, 4); %! xW = [108, 108, 113.2, 2]; %! yW = [92, 92, 92, -1]; %! zW = [52, 55, 52, 0.33]; %! [rS, cS, pS] = worldToSubscript (r, xW, yW, zW); %! assert (rS, [46, 46, 46, NaN]) %! assert (cS, [54, 54, 57, NaN]) %! assert (pS, [13, 14, 13, NaN])image-2.16.1/inst/PaxHeaders.61586/lab2uint16.m0000644000000000000000000000006215005110255015447 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/lab2uint16.m0000644000175000017500000000555015005110255017052 0ustar00avinoamavinoam00000000000000## Copyright (C) 2016 Carnë Draug ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} lab2double (@var{lab}) ## Convert L*a*b* data to uint16 precision. ## ## @var{lab} must be a L*a*b* image or colormap, i.e., its dimensions ## must be MxNx3xK or Mx3. Its type must be double, single, uint16, ## or uint8. ## ## When converted from double or single, L* values usually range from 0 to ## 100, while a* and b* range from -128 to 127. The upper limit values ## will be converted to 65280. ## ## @seealso{lab2double, lab2rgb, lab2single, lab2uint8, lab2uin16, lab2xyz} ## @end deftypefn function [lab] = lab2uint16 (lab) if (nargin () != 1) print_usage (); endif lab = lab2cls (lab, "uint16"); endfunction ## Instead of testing the lab2uint16 function here, we test the ## conversion from uint16 type. The actual tests for lab2uint16, ## are spread all other lab2* functions. This makes the tests ## simpler. %!test %! cm_uint16 = uint16 ([0 127 128 383 384 65151 65152 65279 65280 65281 65534 65535]); %! cm_uint16 = repmat (cm_uint16(:), [1 3]); %! im2d_uint16 = reshape (cm_uint16, [4 3 3]); %! imnd_uint16 = permute (im2d_uint16, [1 4 3 2]); %! %! cm_uint8 = uint8 ([0 0 1 1 2 254 255 255 255 255 255 255]); %! cm_uint8 = repmat (cm_uint8(:), [1 3]); %! assert (lab2uint8 (cm_uint16), cm_uint8) %! im2d_uint8 = reshape (cm_uint8, [4 3 3]); %! assert (lab2uint8 (im2d_uint16), im2d_uint8) %! assert (lab2uint8 (imnd_uint16), permute (im2d_uint8, [1 4 3 2])) %! %! l1 = 100/65280; %! ab1 = 255/65280; %! cm = [ %! 0 -128 %! 127*l1 -128+(ab1*127) %! 128*l1 -128+(ab1*128) %! 383*l1 -128+(ab1*383) %! 384*l1 -128+(ab1*384) %! 65151*l1 -128+(ab1*65151) %! 65152*l1 -128+(ab1*65152) %! 65279*l1 -128+(ab1*65279) %! 100 127 %! 65281*l1 -128+(ab1*65281) %! 65534*l1 -128+(ab1*65534) %! 65535*l1 -128+(ab1*65535)]; %! cm(:,3) = cm(:,2); %! im2d = reshape (cm, [4 3 3]); %! imnd = permute (im2d, [1 4 3 2]); %! %! assert (lab2double (cm_uint16), cm) %! assert (lab2double (im2d_uint16), im2d) %! assert (lab2double (imnd_uint16), imnd) %! %! assert (lab2single (cm_uint16), single (cm)) %! assert (lab2single (im2d_uint16), single (im2d)) %! assert (lab2single (imnd_uint16), single (imnd)) image-2.16.1/inst/PaxHeaders.61586/affine.m0000644000000000000000000000006215005110255015010 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/affine.m0000644000175000017500000001311515005110255016407 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Carnë Draug ## Copyright (C) 2015 Motherboard ## ## 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 . classdef affine ## -*- texinfo -*- ## @deftypefn {Function File} {@var{tform} =} affine (@var{T}) ## tform is a representation of an affine 2D or 3D transform. ## ## affine is a master class for affine2d/affine3d. ## For detailed description of all methods see the detailed ## documentation in affine2d or affine3d ## ## @seealso{affine2d, affine3d} ## @end deftypefn properties (SetAccess = protected) Dimensionality; T; endproperties methods (Access = protected) function [varargout] = transformPoints (this, T, varargin) nargs = numel (varargin); if (nargs == 1) U = varargin{1}; N = rows (U); if (ndims (U) != 2 || columns (U) != this.Dimensionality) error ("transformPointsForward: U must be NxDimensionality"); endif elseif (nargs == this.Dimensionality) N = numel (varargin{1}); if (any (cellfun ("numel", varargin) != N)) error ("transformPointsForward: U, V, W, ... must have same numel"); endif U = cellfun (@vec, varargin, "UniformOutput", false); U = cell2mat (U); else error ("affine: transformPoints: wrong number of arguments"); endif U = [U ones(N, 1)]; varargout{1} = (U * T)(:,1:this.Dimensionality); if (nargout > 1) varargout = num2cell (varargout{1}, 1); endif endfunction endmethods methods function this = affine (T) if (nargin != 1) error ("To activate affine withot arguments use affine2d or affine3d"); elseif (isscalar (T)) if (fix (T) != T || T < 0) error ("affine: DIMENSIONALITY must be a non-negative integer"); endif this.Dimensionality = T; this.T = eye (T+1); elseif (! issquare (T) || T(end) != 1 || any (T(1:end-1,end) != 0)) error ("affine: T must be an affine transform matrix"); elseif (det (T(1:end-1,1:end-1)) == 0) error ("affine: transform must not be a singular matrix"); else this.Dimensionality = rows (T) -1; this.T = T; endif endfunction function this = invert (this) this.T = inv (this.T); this.T(1:end-1, end) = 0; this.T(end) = 1; endfunction ## Check if transform is only rotation or translation. function check = isRigid (this) submatrix = this.T(1:end-1,1:end-1); check = abs (det (submatrix) - 1) < eps; endfunction ## Check if transform is only homogeneous scaling, rotation, reflection or ## translation. function check = isSimilarity (this) submatrix = this.T(1:end-1,1:end-1); s2 = submatrix*submatrix'; check = all ((abs (s2 - s2(1,1) * eye (this.Dimensionality)) ... < (eps * ones (this.Dimensionality)))(:)); endfunction ## Check if transform is only a translation. function check = isTranslation (this) submatrix = this.T(1:end-1,1:end-1); check = !any ((abs (submatrix - eye (this.Dimensionality)) > 0)(:)); endfunction ## given a bounding cube corner coordinates in xlims, ylims and ## zlims (top left front, right bottom back) - return the new ## bounding cube after transformation. function [varargout] = outputLimits (this, varargin) ## if (any (cellfun ('numel', varargin) != 2)) ## error ("outputLimits: LimitsIn must all be 2 elements vector"); ## endif ## limits_in = cellfun (@vec, varargin); ## r = 2 * (this.dimensionality -1); ## for d = 1:this.dimensionality ## limits_in{d} xlims2 = [xlims(:); xlims(:); xlims(:); xlims(:)]; ylims2 = [ylims(:); ylims(end:-1:1)(:); ylims(:); ylims(end:-1:1)(:)]; zlims2 = [zlims(:); zlims(:); zlims(end:-1:1)(:); zlims(end:-1:1)(:)]; temp = [xlims2 ylims2 zlims2 ones(8,1)]; temp = temp*this.T; limitsX = [min(temp(1:8,1)), max(temp(1:8,1))]; limitsY = [min(temp(1:8,2)), max(temp(1:8,2))]; limitsZ = [min(temp(1:8,3)), max(temp(1:8,3))]; endfunction ## Apply transformation on the set of u, v, w points (1xn vectors) ## or on U (3xn matrix) function [varargout] = transformPointsForward (this, varargin) [varargout{1:nargout}] = transformPoints (this, this.T, varargin{:}); endfunction ## Apply inverse transformation on the set of u, v, w points (1xn ## vectors) or on U (3xn matrix). function [varargout] = transformPointsInverse (this, varargin) [varargout{1:nargout}] = transformPoints (this, inv (this.T), varargin{:}); endfunction function disp (this) printf (" %s with properties:\n\n", class (this)); printf (" T: [%dx%d %s]\n", size (this.T), class (this.T)); printf (" Dimensionality: %d\n", this.Dimensionality); endfunction endmethods endclassdef image-2.16.1/inst/PaxHeaders.61586/rgb2ntsc.m0000644000000000000000000000006215005110255015304 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/rgb2ntsc.m0000644000175000017500000001140415005110255016702 0ustar00avinoamavinoam00000000000000## Copyright (C) 1994-2017 John W. Eaton ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {} {@var{yiq_map} =} rgb2ntsc (@var{rgb_map}) ## @deftypefnx {} {@var{yiq_img} =} rgb2ntsc (@var{rgb_img}) ## Transform a colormap or image from red-green-blue (RGB) color space to ## luminance-chrominance (NTSC) space. The input may be of class uint8, ## uint16, single, or double. The output is of class double. ## ## Implementation Note: ## The reference matrix for the transformation is ## ## @example ## @group ## /Y\ 0.299 0.587 0.114 /R\ ## |I| = 0.596 -0.274 -0.322 |G| ## \Q/ 0.211 -0.523 0.312 \B/ ## @end group ## @end example ## ## @noindent ## as documented in @url{http://en.wikipedia.org/wiki/YIQ} and truncated to 3 ## significant figures. Note: The FCC version of NTSC uses only 2 significant ## digits and is slightly different. ## @seealso{ntsc2rgb, rgb2hsv, rgb2ind} ## @end deftypefn function yiq = rgb2ntsc (rgb) if (nargin != 1) print_usage (); endif [rgb, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check ("rgb2ntsc", "RGB", rgb, false); ## Reference matrix for transformation from http://en.wikipedia.org/wiki/YIQ ## and truncated to 3 significant figures. Matlab uses this matrix for their ## conversion. trans = [ 0.299, 0.596, 0.211; 0.587, -0.274, -0.523; 0.114, -0.322, 0.312 ]; yiq = rgb * trans; ## Note that if the input is of class single, we also return an image ## of class single. This is Matlab incompatible by design, since ## Matlab always returning class double, is a Matlab bug (see patch #8709) yiq = colorspace_conversion_revert (yiq, cls, sz, is_im, is_nd, false); endfunction ## Test pure RED, GREEN, BLUE colors %!assert (rgb2ntsc ([1 0 0]), [.299 .596 .211]) %!assert (rgb2ntsc ([0 1 0]), [.587 -.274 -.523]) %!assert (rgb2ntsc ([0 0 1]), [.114 -.322 .312]) %!test %! rgb_map = rand (64, 3); %! assert (ntsc2rgb (rgb2ntsc (rgb_map)), rgb_map, 1e-3); %!test %! rgb_img = rand (64, 64, 3); %! assert (ntsc2rgb (rgb2ntsc (rgb_img)), rgb_img, 1e-3); ## test tolerance input checking on floats %! assert (rgb2ntsc ([1.5 1 1]), [1.149 0.298 0.105], 1e-3); ## Test input validation %!error rgb2ntsc () %!error rgb2ntsc (1,2) %!error rgb2ntsc ({1}) %!error rgb2ntsc (ones (2,2)) ## Test ND input %!test %! rgb = rand (16, 16, 3, 5); %! yiq = zeros (size (rgb)); %! for i = 1:5 %! yiq(:,:,:,i) = rgb2ntsc (rgb(:,:,:,i)); %! endfor %! assert (rgb2ntsc (rgb), yiq); ## Test output class and size for input images. ## Most of the tests only test for colormap input. %!test %! ntsc = rgb2ntsc (rand (10, 10, 3)); %! assert (class (ntsc), "double"); %! assert (size (ntsc), [10 10 3]); %!test %! ntsc = rgb2ntsc (rand (10, 10, 3, "single")); %! assert (class (ntsc), "single"); %! assert (size (ntsc), [10 10 3]); %!test %! rgb = (rand (10, 10, 3) * 3 ) - 0.5; # values outside range [0 1] %! ntsc = rgb2ntsc (rgb); %! assert (class (ntsc), "double"); %! assert (size (ntsc), [10 10 3]); %!test %! rgb = (rand (10, 10, 3, "single") * 3 ) - 0.5; # values outside range [0 1] %! ntsc = rgb2ntsc (rgb); %! assert (class (ntsc), "single"); %! assert (size (ntsc), [10 10 3]); %!test %! ntsc = rgb2ntsc (randi ([0 255], 10, 10, 3, "uint8")); %! assert (class (ntsc), "double"); %! assert (size (ntsc), [10 10 3]); %!test %! ntsc = rgb2ntsc (randi ([0 65535], 10, 10, 3, "uint16")); %! assert (class (ntsc), "double"); %! assert (size (ntsc), [10 10 3]); %!test %! ntsc = rgb2ntsc (randi ([-128 127], 10, 10, 3, "int8")); %! assert (class (ntsc), "double"); %! assert (size (ntsc), [10 10 3]); %!test %! rgb_double = reshape ([1 0 0 0 0 1 0 0 0 0 1 0], [2 2 3]); %! rgb_uint8 = reshape (uint8 ([255 0 0 0 0 255 0 0 0 0 255 0]), %! [2 2 3]); %! rgb_int16 = int16 (double (rgb_double * uint16 (65535)) -32768); %! expected = reshape ([.299 .587 .114 0 .596 -.274 -.322 0 .211 -.523 .312 0], %! [2 2 3]); %! %! assert (rgb2ntsc (rgb_double), expected); %! assert (rgb2ntsc (rgb_uint8), expected); %! assert (rgb2ntsc (single (rgb_double)), single (expected)); image-2.16.1/inst/PaxHeaders.61586/im2int16.m0000644000000000000000000000006215005110255015131 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/im2int16.m0000644000175000017500000000426515005110255016536 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012-2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} im2int16 (@var{img}) ## Convert image to int16. ## ## The conversion of @var{img} to a 16-bit signed integer, is dependent ## on the type of input image. The following input classes are supported: ## ## @table @samp ## @item uint8 or uint16 ## Values are rescaled to the range of the int16 class [-32768 32767]. ## ## @item logical ## True and false values are assigned a value of 32767 and -32768 respectively. ## ## @item double or single ## Values are truncated to the interval [0 1] and then rescaled to the range ## of values of the int16 class [-32768 32767]. ## ## @item int16 ## Returns the same image. ## ## @end table ## ## @seealso{im2bw, imcast, im2uint8, im2double, im2single, im2uint16} ## @end deftypefn function imout = im2int16 (img) if (nargin != 1) print_usage (); endif imout = imcast (img, "int16"); endfunction %!assert (im2int16 (int16 ([-2 2 3])), int16 ([-2 2 3])); %!assert (im2int16 (uint16 ([0 65535])), int16 ([-32768 32767])); %!assert (im2int16 ([false true]), int16 ([-32768 32767])); %!assert (im2int16 ([true false]), int16 ([32767 -32768])); %!assert (im2int16 (uint8 ([0 127 128 255])), int16 ([-32768 -129 128 32767])); %!assert (im2int16 ([0 1.4/65535 1.5/65535 2/65535 1]), int16 ([-32768 -32767 -32766 -32766 32767])); %!assert (im2int16 ([0 0.5 1]), int16 ([-32768 0 32767])); %!assert (im2int16 ([-1 0 1 2]), int16 ([-32768 -32768 32767 32767])); %!error im2int16 ([1 2], "indexed"); image-2.16.1/inst/PaxHeaders.61586/imhist.m0000644000000000000000000000006215005110255015055 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imhist.m0000644000175000017500000001561615005110255016464 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011, 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imhist (@var{I}) ## @deftypefnx {Function File} {} imhist (@var{I}, @var{n}) ## @deftypefnx {Function File} {} imhist (@var{X}, @var{cmap}) ## @deftypefnx {Function File} {[@var{counts}, @var{x}] =} imhist (@dots{}) ## Produce histogram counts of image @var{I}. ## ## The second argument can either be @var{n}, a scalar that specifies the number ## of bins; or @var{cmap}, a colormap in which case @var{X} is expected to be ## an indexed image. If not specified, @var{n} defaults to 2 for binary images, ## and 256 for grayscale images. ## ## If output is requested, @var{counts} is the number of counts for each bin and ## @var{x} is a range for the bins so that @code{stem (@var{x}, @var{counts})} will ## show the histogram. ## ## @emph{Note:} specially high peaks that may prevent an overview of the histogram ## may not be displayed. To avoid this, use @code{axis "auto y"} after the call ## to @code{imhist}. ## ## @seealso{hist, histc, histeq} ## @end deftypefn function [varargout] = imhist (img, b) ## "img" can be a normal or indexed image. We need to check "b" to find out indexed = false; if (nargin < 1 || nargin > 2) print_usage; elseif (nargin == 1) if (islogical (img)) b = 2; else b = 256; endif elseif (nargin == 2) if (iscolormap (b)) if (!isind(img)) error ("imhist: second argument is a colormap but first argument is not an indexed image."); endif indexed = true; ## an indexed image reads differently wether it's uint8/16 or double ## If uint8/16, index+1 is the colormap row number (0 on the image ## corresponds to the 1st column on the colormap). ## If double, index is the colormap row number (no offset). ## isind above already checks for double/uint8/uint16 so we can use isinteger ## and isfloat safely if ( (isfloat (img) && max (img(:)) > rows(b) ) || (isinteger (img) && max (img(:)) > rows(b)-1) ) warning ("imhist: largest index in image exceeds length of colormap."); endif elseif (isnumeric (b) && isscalar (b) && fix(b) == b && b > 0) if (islogical (img) && b != 2) error ("imhist: there can only be 2 bins when input image is binary") endif else error ("imhist: second argument must be a positive integer scalar or a colormap"); endif endif ## prepare bins and image if (indexed) if (isinteger (img)) bins = 0:rows(b)-1; else bins = 1:rows(b); endif else if (isinteger (img)) bins = linspace (intmin (class (img)), intmax (class (img)), b); elseif (islogical (img)) bins = 0:1; else ## image must be single or double bins = linspace (0, 1, b); endif ## we will use this bins with histc() where their values will be edges for ## each bin. However, what we actually want is for their values to be the ## center of each bin. To do this, we decrease their values by half of bin ## width and will increase it back at the end of the function. We could do ## it on the image and it would be a single step but it would be an heavier ## operation since images are likely to be much longer than the bins. ## The use of hist() is also not simple for this since values right in the ## middle of two bins will go to the bottom bin (4.5 will be placed on the ## bin 4 instead of 5 and we must keep matlab compatibility). ## Of course, none of this needed for binary images. if (!islogical (img)) bins_adjustment = ((bins(2) - bins(1))/2); bins -= bins_adjustment; endif ## matlab returns bins as one column instead of a row but only for non ## indexed images bins = bins'; ## histc does not counts values outside the edges of the bins so we need to ## truncate their values. ## truncate the minimum... integers could in no way have a value below the ## minimum of their class so truncation on this side is only required for if (isfloat (img) && min (img(:)) < 0) img(img < 0) = 0; endif ## truncating the maximum... also adjusts floats above 1. We might need if (max (img(:)) > bins(end)) ## bins (end) is probably a decimal number. If an image is an int, we ## can't assign the new value since it will be fix(). So we need to change ## the image class to double but that will take more memory so let's ## avoid it if we can if (fix (bins(end)) != bins(end)) img = double (img); endif img(img > bins(end)) = bins(end); endif endif [nn] = histc (img(:), bins); if (!indexed && !islogical(img)) bins += bins_adjustment; endif if (nargout != 0) varargout{1} = nn; varargout{2} = bins; else stem (bins, nn, "marker", "none"); xlim ([bins(1) bins(end)]); box off; # remove the box to see bar for the last bin ## If we have a few very high peaks, it prevents the overview of the ## histogram since the axis are set automatically. So we consider the ## automatic y axis bad if it's 10 times above the median of the ## histogram. ## The (ylimit != 0) is for cases when most of the bins is zero. In ## such cases, the median is zero and we'd get an error trying to set ## "ylim ([0 0])". We could adjust it to [0 1] but in such cases, it's ## probably important to show how high those few peaks are. ylimit = round (median (nn) * 10); if (ylim()(2) > ylimit && ylimit != 0) ylim ([0 ylimit]); endif if (indexed) colormap (b); else colormap (gray (b)); endif colorbar ("xticklabel", [], "SouthOutside"); endif endfunction %!shared nn, bb, enn, ebb %! [nn, bb] = imhist(logical([0 1 0 0 1])); %!assert({nn, bb}, {[3 2]', [0 1]'}) %! [nn, bb] = imhist([0 0.2 0.4 0.9 1], 5); %!assert({nn, bb}, {[1 1 1 0 2]', [0 0.25 0.5 0.75 1]'}) %! [nn, bb] = imhist([-2 0 0.2 0.4 0.9 1 5], 5); %!assert({nn, bb}, {[2 1 1 0 3]', [0 0.25 0.5 0.75 1]'}) %! [nn, bb] = imhist(uint8([0 32 255]), 256); %! enn = zeros(256, 1); enn([1, 33, 256]) = 1; %! ebb = 0:255; %!assert({nn, bb}, {enn, ebb'}) %! [nn, bb] = imhist(int8([-50 0 100]), 31); %! enn = zeros(31, 1); enn([10, 16, 28]) = 1; %! ebb = -128:8.5:127; %!assert({nn, bb}, {enn, ebb'}) image-2.16.1/inst/PaxHeaders.61586/bwarea.m0000644000000000000000000000006215005110255015021 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwarea.m0000644000175000017500000000351615005110255016424 0ustar00avinoamavinoam00000000000000## Copyright (C) 2005 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{total} =} bwarea (@var{bw}) ## Estimate total area of objects on the image @var{bw}. ## ## The image @var{bw} can be of any class, even non-logical, in which case non ## zero valued pixels are considered to be an object. ## ## This algorithm is not the same as counting the number of pixels belonging to ## an object as it tries to estimate the area of the original object. The value ## of each pixel to the total area is weighted in relation to its neighbour ## pixels. ## ## @seealso{im2bw, bweuler, bwperim, regionprops} ## @end deftypefn function total = bwarea (bw) if (nargin != 1) print_usage; elseif (!isimage (bw) || ndims (bw) != 2) error("bwarea: input image must be a 2D image"); elseif (!islogical (bw)) bw = (bw != 0) endif four = ones (2); two = diag ([1 1]); fours = conv2 (bw, four); twos = conv2 (bw, two); nQ1 = sum (fours(:) == 1); nQ3 = sum (fours(:) == 3); nQ4 = sum (fours(:) == 4); nQD = sum (fours(:) == 2 & twos(:) != 1); nQ2 = sum (fours(:) == 2 & twos(:) == 1); total = 0.25*nQ1 + 0.5*nQ2 + 0.875*nQ3 + nQ4 + 0.75*nQD; endfunction image-2.16.1/inst/PaxHeaders.61586/imfilter.m0000644000000000000000000000006215005110255015373 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imfilter.m0000644000175000017500000002107115005110255016772 0ustar00avinoamavinoam00000000000000## Copyright (C) 2007 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{J} = imfilter(@var{I}, @var{f}) ## @deftypefnx{Function File} @var{J} = imfilter(@var{I}, @var{f}, @var{options}, @dots{}) ## Computes the linear filtering of the image @var{I} and the filter @var{f}. ## The computation is performed using double precision floating point numbers, ## but the class of the input image is preserved as the following example shows. ## @example ## I = 255*ones(100, 100, "uint8"); ## f = fspecial("average", 3); ## J = imfilter(I, f); ## class(J) ## @result{} ans = uint8 ## @end example ## ## The function also accepts a number of optional arguments that control the ## details of the filtering. The following options is currently accepted ## @table @samp ## @item S ## If a scalar input argument is given, the image is padded with this scalar ## as part of the filtering. The default value is 0. ## @item "symmetric" ## The image is padded symmetrically. ## @item "reflect" ## Same as "symmetric", but the borders are not repeated. ## @item "replicate" ## The image is padded using the border of the image. ## @item "circular" ## The image is padded by circular repeating of the image elements. ## @item "same" ## The size of the output image is the same as the input image. This is the default ## behaviour. ## @item "full" ## Returns the full filtering result. ## @item "corr" ## The filtering is performed using correlation. This is the default behaviour. ## @item "conv" ## The filtering is performed using convolution. ## @end table ## @seealso{conv2, filter2, fspecial, padarray} ## @end deftypefn function retval = imfilter(im, f, varargin) if (nargin < 2) print_usage (); endif ## Check image if (! isimage (im)) error("imfilter: IM must be an image"); endif [imrows, imcols, imchannels, tmp] = size(im); if (tmp != 1 || (imchannels != 1 && imchannels != 3)) error("imfilter: first input argument must be an image"); endif C = class(im); ## Check filter (XXX: matlab support 3D filter, but I have no idea what they do with them) if (! isnumeric (f)) error("imfilter: F must be a numeric array"); endif [frows, fcols, tmp] = size(f); if (tmp != 1) error("imfilter: second argument must be a 2-dimensional matrix"); endif ## Parse options res_size = "same"; res_size_options = {"same", "full"}; pad = 0; pad_options = {"symmetric", "reflect", "replicate", "circular"}; ftype = "corr"; ftype_options = {"corr", "conv"}; for i = 1:length(varargin) v = varargin{i}; if (any(strcmpi(v, pad_options)) || isscalar(v)) pad = v; elseif (any(strcmpi(v, res_size_options))) res_size = v; elseif (any(strcmpi(v, ftype_options))) ftype = v; else warning("imfilter: cannot handle input argument number %d", i+2); endif endfor ## Pad the image im = padarray(im, floor([frows/2, fcols/2]), pad); if (mod(frows,2) == 0) im = im(2:end, :, :); endif if (mod(fcols,2) == 0) im = im(:, 2:end, :); endif ## Do the filtering if (strcmpi(res_size, "same")) res_size = "valid"; else # res_size == "full" res_size = "same"; endif if (strcmpi(ftype, "corr")) for i = imchannels:-1:1 retval(:,:,i) = filter2(f, im(:,:,i), res_size); endfor else for i = imchannels:-1:1 retval(:,:,i) = conv2(im(:,:,i), f, res_size); endfor endif ## Change the class of the output to the class of the input ## (the filtering functions returns doubles) retval = cast(retval, C); endfunction %!test %! img = [ %! 8 2 6 7 4 3 7 8 4 1 %! 9 9 1 1 4 7 3 3 8 1 %! 2 9 8 3 7 6 5 8 6 5 %! 9 5 9 1 8 2 7 3 5 8 %! 6 8 7 1 2 2 9 9 9 9 %! 1 2 7 8 5 5 9 4 3 2 %! 3 4 7 7 5 9 5 2 7 6 %! 5 9 4 3 6 4 2 3 7 5 %! 9 8 6 9 7 6 2 6 4 1 %! 9 9 2 1 7 3 3 5 6 4]; %! %! expected_corr = [ %! 46 53 30 34 44 42 40 51 42 19 %! 48 66 57 42 46 50 59 58 49 34 %! 48 67 55 54 44 58 50 50 64 39 %! 44 77 52 43 28 55 57 75 70 50 %! 29 51 65 51 42 50 60 62 55 42 %! 23 44 58 59 63 59 55 57 50 36 %! 36 50 52 56 56 47 48 45 47 39 %! 51 64 70 62 56 50 40 38 41 31 %! 58 72 50 49 58 45 41 42 49 28 %! 27 37 27 21 19 26 16 23 24 17]; %! assert (imfilter (img, [0 1 0; 2 1 1; 1 2 2]), expected_corr) %! %! ## test order of options (and matching with defaults) %! assert (imfilter (img, [0 1 0; 2 1 1; 1 2 2], 0), expected_corr) %! assert (imfilter (img, [0 1 0; 2 1 1; 1 2 2], "corr"), expected_corr) %! assert (imfilter (img, [0 1 0; 2 1 1; 1 2 2], "corr", 0), expected_corr) %! assert (imfilter (img, [0 1 0; 2 1 1; 1 2 2], 0, "corr"), expected_corr) %! %! expected_conv = [ %! 21 31 23 22 21 28 29 26 22 6 %! 47 55 43 43 51 44 49 64 44 24 %! 56 69 53 34 47 50 57 48 52 37 %! 38 70 60 56 41 57 54 61 66 44 %! 46 67 53 48 32 54 59 65 63 46 %! 28 56 63 50 36 54 58 66 63 47 %! 20 43 55 62 67 57 52 53 44 28 %! 42 51 54 61 57 53 44 46 48 39 %! 53 70 63 50 57 42 38 38 43 33 %! 53 62 50 54 52 44 38 40 40 20]; %! assert (imfilter (img, [0 1 0; 2 1 1; 1 2 2], "conv"), expected_conv) %! %! ## alternative class %! assert (imfilter (single (img), [0 1 0; 2 1 1; 1 2 2]), %! single (expected_corr)) %! assert (imfilter (int8 (img), [0 1 0; 2 1 1; 1 2 2]), %! int8 (expected_corr)) %! assert (imfilter (uint8 (img), [0 1 0; 2 1 1; 1 2 2]), %! uint8 (expected_corr)) %! %! assert (imfilter (single (img), [0 1 0; 2 1 1; 1 2 2], "conv"), %! single (expected_conv)) %! assert (imfilter (int8 (img), [0 1 0; 2 1 1; 1 2 2], "conv"), %! int8 (expected_conv)) %! assert (imfilter (uint8 (img), [0 1 0; 2 1 1; 1 2 2], "conv"), %! uint8 (expected_conv)) %! ## test padding with even sized filters (bug #45568) %!test %! I = zeros (6); %! I(2:3,2:3) = 1; %! F = zeros (4); %! F(2,2:3) = 1; %! result = [0 0 0 0 0 0 %! 1 2 1 0 0 0 %! 1 2 1 0 0 0 %! 0 0 0 0 0 0 %! 0 0 0 0 0 0 %! 0 0 0 0 0 0]; %! assert (imfilter (I, F), result) ## test boundary padding %!test %! I = magic(6); %! I = uint8(I); %! filter = [ 1 2 3 4 5 %! 6 7 8 9 10 %! 11 12 13 14 15 %! 16 17 18 19 20 %! 21 22 23 24 25]; %! filter = filter / sum(filter(:)); %! result = [ 7 11 15 15 12 11 %! 10 14 17 18 14 10 %! 13 16 19 18 14 10 %! 14 16 19 19 14 9 %! 10 11 12 12 9 5 %! 6 6 7 7 5 3]; %! result = uint8(result); %! assert (imfilter (I, filter), result) % %! result = [15 17 18 18 20 23 %! 17 18 18 19 19 20 %! 19 19 19 18 19 17 %! 20 19 19 19 18 16 %! 21 19 20 20 17 15 %! 22 20 19 18 17 14]; %! result = uint8(result); %! assert (imfilter (I, filter, "symmetric"), result) % %! result = [17 18 18 18 20 23 %! 17 18 18 19 19 20 %! 20 19 19 18 19 18 %! 19 19 19 19 18 15 %! 18 19 20 20 17 14 %! 18 18 20 20 16 13]; %! result = uint8(result); %! assert (imfilter (I, filter, "replicate"), result) %! %! result = [18 18 18 18 19 20 %! 18 18 18 19 18 19 %! 19 19 19 18 19 18 %! 19 19 19 19 18 17 %! 19 19 19 18 19 18 %! 18 18 18 19 18 19]; %! result = uint8(result); %! assert (imfilter (I, filter, "circular"), result) %! %! result = [13 16 18 19 20 23 %! 17 18 18 19 19 20 %! 19 18 19 18 18 17 %! 23 20 19 19 18 16 %! 23 20 19 18 18 14 %! 24 21 19 19 17 14]; %! result = uint8(result); %! assert (imfilter (I, filter, "reflect"), result) image-2.16.1/inst/PaxHeaders.61586/applylut.m0000644000000000000000000000006215005110255015432 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/applylut.m0000644000175000017500000000417015005110255017032 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{A} =} applylut (@var{BW}, @var{LUT}) ## Uses lookup tables to perform a neighbour operation on binary images. ## ## A = applylut(BW,LUT) returns the result of a neighbour operation ## using the lookup table @var{LUT} which can be created by makelut. ## ## It first computes a matrix with the index of each element in the ## lookup table. To do this, it convolves the original matrix with a ## matrix which assigns each of the neighbours a bit in the resulting ## index. Then @var{LUT} is accessed to compute the result. ## ## @seealso{makelut} ## @end deftypefn function A = applylut (BW, LUT) if (nargin != 2) print_usage; endif nq=log2(length(LUT)); n=sqrt(nq); if (floor(n)!=n) error ("applylut: LUT length is not as expected. Use makelut to create it."); endif w=reshape(2.^[nq-1:-1:0],n,n); A=LUT(filter2(w,BW)+1); endfunction %!demo %! lut = makelut (@(x) sum (x (:)) >= 3, 3); %! S = applylut (eye (5), lut); %! disp (S) %! ## Everything should be 0 despite a diagonal which doesn't reach borders. ## 2-by-2 test %!assert (prod (applylut (eye (3), makelut (@(x) x(1) == 1, 2)) == eye (3)), [1 1 1]); ## 3-by-3 test %!assert (prod (applylut (eye (3), makelut (@(x) x(2,2) == 1, 3)) == eye (3)), [1 1 1]); %!assert (prod (applylut (eye (3), makelut (@(x) x(3,3) == 1, 3)) == %! applylut (eye (3), makelut (@(x) x(2,2) == 1, 2))), %! [1 1 1]); image-2.16.1/inst/PaxHeaders.61586/xyz2rgb.m0000644000000000000000000000006215005110255015167 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/xyz2rgb.m0000644000175000017500000001117415005110255016571 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{rgb} =} xyz2rgb (@var{xyz}) ## @deftypefnx {Function File} {@var{rgb_map} =} xyz2rgb (@var{xyz_map}) ## Transform a colormap or image from CIE XYZ to sRGB color space. ## ## A color in the CIE XYZ color space consists of three values X, Y and Z. ## Those values are designed to be colorimetric, meaning that their values ## do not depend on the display device hardware. ## ## A color in the RGB space consists of red, green, and blue intensities. ## The output RGB values are calculated to be nonlinear sRGB values ## with the white point D65. This means the output values are in the ## colorimetric (sRGB) colorspace. ## ## Input values of class single and double are acceptecd. ## The shape and the class of the input are conserved. ## ## note: outside the definition range (0<=R, G, B<=1) this function might ## return different (but also nonsense) values than Matlab. ## ## @seealso{rgb2xyz, rgb2lab, rgb2hsv, rgb2ind, rgb2ntsc} ## @end deftypefn ## Author: Hartmut Gimpel ## algorithm taken from the following book: ## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015) function rgb = xyz2rgb (xyz) if (nargin != 1) print_usage (); endif [xyz, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check ("xyz2rgb", "XYZ", xyz, 1); # only accept single and double inputs because valid xyz values can be >1 ## transform from CIE XYZ to linear sRGB values with whitepoint D65 ## (source of matrix: book of Burger) matrix_xyz2rgb_D65 = ... [3.240479, -1.537150, -0.498535; -0.969256, 1.875992, 0.041556; 0.055648, -0.204043, 1.057311]; # Matlab uses the following slightly different conversion matrix # matrix_xyz2rgb_D65 = ... # [3.2406, -1.5372, -0.4986; # -0.9689, 1.8758, 0.0415; # 0.0557, -0.2040, 1.0570]; rgb_lin = xyz * matrix_xyz2rgb_D65'; ## transform from linear sRGB values to non-linear sRGB values ## (modified gamma transform) rgb = rgb_lin; mask = rgb_lin <= 0.0031308; rgb(mask) = 12.92 .* rgb_lin(mask); rgb(! mask) = 1.055 .* (rgb_lin(! mask) .^ (1/2.4)) -0.055; rgb = colorspace_conversion_revert (rgb, cls, sz, is_im, is_nd, is_int, 0); endfunction ## Test pure colors, gray and some other colors ## (This set of test values is taken from the book by Burger.) %!assert (xyz2rgb ([0, 0, 0]), [0 0 0], 1e-3) %!assert (xyz2rgb ([0.4125, 0.2127, 0.0193]), [1 0 0], 1e-3) %!assert (xyz2rgb ([0.7700, 0.9278, 0.1385]), [1 1 0], 1e-3) %!assert (xyz2rgb ([0.3576, 0.7152, 0.1192]), [0 1 0], 1e-3) %!assert (xyz2rgb ([0.5380, 0.7873, 1.0694]), [0 1 1], 1e-3) %!assert (xyz2rgb ([0.1804, 0.07217, 0.9502]), [0 0 1], 1e-3) %!assert (xyz2rgb ([0.5929, 0.28484, 0.9696]), [1 0 1], 1e-3) %!assert (xyz2rgb ([0.9505, 1.0000, 1.0888]), [1 1 1], 1e-3) %!assert (xyz2rgb ([0.2034, 0.2140, 0.2330]), [0.5 0.5 0.5], 1e-3) %!assert (xyz2rgb ([0.2155, 0.1111, 0.0101]), [0.75 0 0], 1e-3) %!assert (xyz2rgb ([0.0883, 0.0455, 0.0041]), [0.5 0 0], 1e-3) %!assert (xyz2rgb ([0.0210, 0.0108, 0.0010]), [0.25 0 0], 1e-3) %!assert (xyz2rgb ([0.5276, 0.3812, 0.2482]), [1 0.5 0.5], 1e-3) ## Test tolarant input checking on floats %!assert (xyz2rgb ([1.5 1 1]), [1.5712, 0.7109 0.9717], 1e-3) %!test %! xyz_map = rand (64, 3); %! assert (rgb2xyz (xyz2rgb (xyz_map)), xyz_map, 3e-4); %!test %! xyz_img = rand (64, 64, 3); %! assert (rgb2xyz (xyz2rgb (xyz_img)), xyz_img, 3e-4); ## support sparse input (the only useful xyz value with zeros is black) %!assert (xyz2rgb (sparse ([0 0 0])), [0 0 0], 1e-3) ## conserve class of single input %!assert (class (xyz2rgb (single([0.5 0.5 0.5]))), 'single') ## Test input validation %!error xyz2rgb () %!error xyz2rgb (1,2) %!error xyz2rgb ({1}) %!error xyz2rgb (ones (2,2)) ## Test ND input %!test %! xyz = rand (16, 16, 3, 5); %! rgb = zeros (size (xyz)); %! for i = 1:5 %! rgb(:,:,:,i) = xyz2rgb (xyz(:,:,:,i)); %! endfor %! assert (xyz2rgb (xyz), rgb) image-2.16.1/inst/PaxHeaders.61586/imapplymatrix.m0000644000000000000000000000006215005110255016460 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imapplymatrix.m0000644000175000017500000001644015005110255020063 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{Y} =} imapplymatrix (@var{M}, @var{X}) ## @deftypefnx {} {@var{Y} =} imapplymatrix (@var{M}, @var{X}, @var{C}) ## @deftypefnx {} {@var{Y} =} imapplymatrix (@dots{}, @var{output_type}) ## Linear combination of color channels. ## ## Computes a linear combination of channels of image @var{X} given a matrix ## of coefficients @var{M} and optionally adding a constant value to ## each output channel. @var{X} is an image of size @code{[m, n, p, @dots{}]}, ## @var{M} is a multiplication matrix of size @code{[q, p]} and @var{C} is a ## vector of constants of length q. @var{Y} is a matrix of size ## @code{[m, n, q, @dots{}]} with q output channels. q is in the range [1, p]. ## ## @var{Y} is of the same type as @var{X}, unless @var{output_type} is ## specified. ## ## @end deftypefn function Y = imapplymatrix (M, X, varargin) if (nargin < 2 || nargin > 4) print_usage (); endif if (length (size (M)) > 2) error ("Octave:invalid-input-arg", "imapplymatrix: M must be a 2-D matrix"); endif if (! isempty(X) && isnumeric (M)) if (size(M, 1) > size (M, 2)) error ("Octave:invalid-input-arg", ... "imapplymatrix: The number of rows of M must be less than or equal to \ the number of its columns"); endif if (size (M, 2) != size (X, 3)) error ("Octave:invalid-input-arg", ... "imapplymatrix: The number of columns in M must match the number of \ image channels"); endif elseif (! isnumeric (M)) M = nan (1, size (X, 3)); elseif (isempty (M)) M = []; # make sure q is zero endif [C, output_type] = parseVarargin (M, X, varargin{:}); m = size (X, 1); n = size (X, 2); p = size (X, 3); q = size (M, 1); xdims = size (X); npix = m * n; # number of pixels resh_dims_1 = [npix, max(prod(xdims(3:end)), 1)]; resh_dims_2 = [sign(npix) * p, prod(xdims) ./ max(p, 1)]; X = double (X); M = double (M); ## reshape X for vectorized multiplication with M X_reshaped = reshape (reshape (X, resh_dims_1)', resh_dims_2); if (! isempty (C)) C = double (C); M = cat(2, C(:), M); X_reshaped = cat(1, ones (1, size (X_reshaped, 2)), X_reshaped); endif ydims = [m, n, q, xdims(4:end)]; ydims(length (xdims) + 1:end) = []; Y = cast (reshape (reshape (M * X_reshaped, ... [max(prod(ydims(3:end)), 1), npix])', ydims), output_type); endfunction function [C, output_type] = parseVarargin (M, X, varargin) if (numel (varargin) == 0) C = []; output_type = class (X); elseif (numel (varargin) == 1) if (ischar (varargin{1})) C = []; output_type = varargin{1}; elseif (! isValidC (M, varargin{1})) error ("Octave:invalid-input-arg", ... "imapplymatrix: third argument must be a vector C or output_type"); else C = varargin{1}; output_type = class (X); endif elseif (numel (varargin) == 2) if (! isValidC (M, varargin{1})) error ("Octave:invalid-input-arg", ... "imapplymatrix: The length of C must equal the number of rows of M"); elseif (! ischar (varargin{2})) error ("Octave:invalid-input-arg", ... "imapplymatrix: output_type must be a valid numeric data type"); else C = varargin{1}; output_type = varargin{2}; endif endif endfunction function TF = isValidC (M, arg) TF = isempty (M) || isempty (arg) || (isnumeric (arg) && isvector (arg) ... && length (arg) == size (M, 1)); endfunction ## test argument checking %!error id=Octave:invalid-fun-call imapplymatrix () %!error id=Octave:invalid-fun-call imapplymatrix (42) %!error id=Octave:invalid-input-arg imapplymatrix (ones (2, 2, 2), 42) %!error id=Octave:invalid-input-arg imapplymatrix ([], ones (2, 2)) %!error id=Octave:invalid-input-arg imapplymatrix (ones (0, 2), ones (2, 2)) %!error id=Octave:invalid-input-arg imapplymatrix (ones (2, 0), ones (2, 2)) %!error id=Octave:invalid-input-arg imapplymatrix (4, 2, [2, 2]) %!error id=Octave:invalid-input-arg imapplymatrix (4, 2, [2, 2], "uint8") %!error id=Octave:invalid-input-arg imapplymatrix (4, 2, 0, 666) %!assert (imapplymatrix ([], []), []) %!assert (imapplymatrix ([], [], "uint16"), uint16 ([])) %!assert (imapplymatrix (1, 10, []), 10) %!assert (imapplymatrix (1, 10, ones (0, 5)), 10) %!assert (imapplymatrix (1, 10, ones (5, 0)), 10) %!assert (imapplymatrix (ones (0), ones (0), 3), []) %!assert (imapplymatrix (ones (0), ones (4, 0), 3), zeros (4, 0)) %!assert (imapplymatrix (ones (0), ones (0, 4), 3), zeros (0, 4)) %!assert (imapplymatrix (ones (2, 0), ones (0, 4), 3), zeros (0, 4)) %!assert (imapplymatrix (ones (0, 2), ones (0, 4), 3), zeros (0, 4)) %!assert (imapplymatrix (ones (0, 2), ones (0, 4, 0), 3), zeros (0, 4, 0)) %!assert (imapplymatrix("a", ones(2, 2)), nan (2, 2)) %!assert (imapplymatrix("abc", ones(2, 2)), nan (2, 2)) %!assert (imapplymatrix (1, 10), 10) %!assert (imapplymatrix (1, 10, 3), 13) %!assert (imapplymatrix (ones (1), uint8 (10), 3), uint8 (13)) %!assert (imapplymatrix (uint8 (ones (1)), 10, 3), double (13)) %!assert (imapplymatrix (uint8 (ones (1)), uint8 (10), 3), uint8 (13)) %!assert (imapplymatrix (2.6 * ones (1), uint8 (10), 4.7), uint8 (31)) %!assert (imapplymatrix (42, ones (1, 2)), 42 * ones (1, 2)) %!assert (imapplymatrix (42, ones (2, 1)), 42 * ones (2, 1)) %!assert (imapplymatrix (42, ones (2, 2)), 42 * ones (2, 2)) %!assert (imapplymatrix (42, ones (2, 2), 0.5), 42.5 * ones (2, 2)) %!assert (imapplymatrix ([4, 2], ones (2, 2, 2), 0.5), 6.5 * ones (2, 2)) %!assert (imapplymatrix ([4, 2; %! 4, 2], ones (2, 2, 2), [0.5, 0.5]), 6.5 * ones (2, 2, 2)) %!assert (imapplymatrix ([4, 2; %! 4, 2], ones (2, 2, 2), [0.5; 0.5]), 6.5 * ones (2, 2, 2)) %!assert (imapplymatrix ([1, 2, 3], ones (2, 2, 3)), 6 * ones (2, 2, 1)) %!assert (imapplymatrix ([1, 2, 3], ones (2, 2, 3), 1), 7 * ones (2, 2, 1)) %!test %! expected = zeros (2, 2, 2, "uint8"); %! expected(:, :, 1) = 7 * ones (2, 2); %! expected(:, :, 2) = 16 * ones (2, 2); %! I = uint8 (ones (2, 2, 3)); %! assert (imapplymatrix ([1, 2, 3 %! 4, 5, 6], I, [1, 1]), expected) %!test %! expected = zeros (2, 2, 2, 2, "uint16"); %! expected(:, :, 1, 1) = 7 * ones (2, 2); %! expected(:, :, 2, 1) = 16 * ones (2, 2); %! expected(:, :, 1, 2) = 13 * ones (2, 2); %! expected(:, :, 2, 2) = 31 * ones (2, 2); %! I(:, :, :, 1) = uint16 (ones (2, 2, 3)); %! I(:, :, :, 2) = 2 * uint16 (ones (2, 2, 3)); %! assert (imapplymatrix ([1, 2, 3; %! 4, 5, 6], I, [1, 1]), expected) image-2.16.1/inst/PaxHeaders.61586/bwpropfilt.m0000644000000000000000000000006215005110255015750 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwpropfilt.m0000644000175000017500000001034715005110255017353 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} bwpropfilt (@var{bw}, @var{attrib}) ## @deftypefnx {Function File} {} bwpropfilt (@var{bw}, @var{I}, @var{attrib}) ## @deftypefnx {Function File} {} bwpropfilt (@dots{}, @var{range}) ## @deftypefnx {Function File} {} bwpropfilt (@dots{}, @var{n}) ## @deftypefnx {Function File} {} bwpropfilt (@dots{}, @var{n}, @var{keep}) ## @deftypefnx {Function File} {} bwpropfilt (@dots{}, @dots{}, @var{conn}) ## Filter objects from image based on their properties. ## ## Returns a logical matrix with the objects of @var{bw} filtered based ## on the specific property @var{attrib}. The possible values for @var{attrib} ## are all the properties from @command{regionprops} that return a scalar ## value, e.g., Area, Extent, and MaxIntensity, but not PixelValues, basic, and ## BoundingBox. For certain attributes, such as MaxIntensity and ## WeightedCentroid, the grayscale image @var{I} must also be specified. ## ## To filter objects with a value on a specific interval, @var{range} must be ## a two-element vector with the interval @code{[@var{low} @var{high}]} ## (values are inclusive). ## ## Alternatively, a scalar @var{n} will select the objects with the N highest ## values. The @var{keep} option defaults to @qcode{"largest"} but can also ## be set to @qcode{"smallest"} to select the N objects with lower values. ## ## The last optional argument, @var{conn}, can be a connectivity matrix, or ## the number of elements connected to the center (see @command{conndef}). ## ## @seealso{bwareaopen, bwareafilt, bwlabel, bwlabeln, bwconncomp, regionprops} ## @end deftypefn function bwfiltered = bwpropfilt (bw, varargin) if (nargin < 3 || nargin > 6) print_usage (); endif if (ischar (varargin{1})) no_gray = true; attrib = varargin{1}; next_idx = 2; else no_gray = false; img = varargin{1}; attrib = varargin{2}; next_idx = 3; endif valid_nargin = @(x) numel (varargin) >= x; if (isscalar (varargin{next_idx})) ## Get the N largest or smallest in_range = false; n_keep = varargin{next_idx}; next_idx++; if (valid_nargin (next_idx) && ischar (varargin{next_idx})) keep = tolower (varargin{next_idx}); if (! any (strcmpi (keep, {"largest", "smallest"}))) error ("bwpropfilt: KEEP must be `largest' or `smallest'"); endif next_idx++; else keep = "largest"; endif elseif (numel (varargin{next_idx}) == 2) in_range = true; range = varargin{next_idx}; next_idx++; else error ("bwpropfilt: N and RANGE must have 1 or 2 elements respectively"); endif if (valid_nargin (next_idx)) conn = conndef (varargin{next_idx++}); if (valid_nargin (next_idx)) print_usage (); endif else ## Non-documented default conn = conndef (ndims (bw), "maximal"); endif cc = bwconncomp (bw, conn); if (no_gray) stats = regionprops (cc, {"PixelIdxList", attrib}); else stats = regionprops (cc, img, {"PixelIdxList", attrib}); endif n_objs = numel (stats); idxs = {stats.PixelIdxList}; attribs = [stats.(attrib)]; if (in_range) filtered_idxs = idxs(attribs >= range(1) & attribs <= range(2)); else [~, sorted_idxs] = sort (attribs, "descend"); switch (keep) case "largest", filtered_idxs = idxs(sorted_idxs(1:min (n_keep, n_objs))); case "smallest", filtered_idxs = idxs(sorted_idxs(max (1, n_objs - n_keep +1):end)); endswitch endif bwfiltered = false (size (bw)); bwfiltered(cat (1, filtered_idxs{:})(:)) = true; endfunction image-2.16.1/inst/PaxHeaders.61586/mean2.m0000644000000000000000000000006215005110255014562 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/mean2.m0000644000175000017500000000333715005110255016166 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} mean2 (@var{I}) ## Compute mean value of array. ## ## While the function name suggests that it computes the mean value of ## a 2D array, it will actually computes the mean value of an entire ## array. It is equivalent to @code{mean (I(:))}. ## ## The return value will be of class double independently of the input ## class. ## ## @seealso{mean, std2} ## @end deftypefn function m = mean2 (I) if (nargin != 1) print_usage(); endif m = mean (I(:)); endfunction ## Corner cases for Matlab compatibility (bug #51144) %!test %! ## This throws a division by zero warning which Matlab does not, but %! ## that's because Matlab does not throw such warnings in the first %! ## place. Octave does, so we do not turn the warning off. %! warning ("off", "Octave:divide-by-zero", "local"); %! assert (mean2 ([]), NaN) %!assert (mean2 (logical ([1 1; 0 0])), 0.5) %!assert (mean2 (ones (3, 3, 3)), 1) %!assert (mean2 (i), i) %!assert (mean2 ([1 i]), [0.5+0.5i]) %!assert (mean2 (speye (3)), sparse (1/3)) image-2.16.1/inst/PaxHeaders.61586/im2bw.m0000644000000000000000000000006215005110255014600 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/im2bw.m0000644000175000017500000001347415005110255016207 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## Copyright (C) 2012-2016 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} im2bw (@var{img}) ## @deftypefnx {Function File} {} im2bw (@var{X}, @var{cmap}) ## @deftypefnx {Function File} {} im2bw (@dots{}, @var{threshold}) ## @deftypefnx {Function File} {} im2bw (@dots{}, @var{method}) ## Convert image to binary, black and white, by threshold. ## ## The input image @var{img} can either be a grayscale or RGB image. In the later ## case, @var{img} is first converted to grayscale with @code{rgb2gray}. Input ## can also be an indexed image @var{X} in which case the colormap @var{cmap} ## needs to be specified. ## ## The value of @var{threshold} should be in the range [0,1] independently of the ## class of @var{img}. Values from other classes can be converted to the correct ## value with @code{im2double}: ## ## @example ## bw = im2bw (img_of_class_uint8, im2double (thresh_of_uint8_class)); ## @end example ## ## For an automatic threshold value, consider using @code{graythresh}. ## The argument @var{method} is a string that specifies a valid algorithm ## available in @code{graythresh}. The following are equivalent: ## ## @example ## bw = im2bw (img, "moments"); ## bw = im2bw (img, graythresh (img, "moments")); ## @end example ## ## @seealso{graythresh, ind2gray, otsuthresh, rgb2gray} ## @end deftypefn function BW = im2bw (img, cmap, thresh = 0.5) if (nargin < 1 || nargin > 3) print_usage (); elseif (nargin == 3 && ! isind (img)) error ("im2bw: IMG must be an indexed image when are 3 input arguments"); elseif (nargin == 3 && ! iscolormap (cmap)) error ("im2bw: CMAP must be a colormap"); elseif (nargin == 2) thresh = cmap; endif if (! isimage (img)) error ("im2bw: IMG must be an image"); elseif (! ischar (thresh) && ! (isnumeric (thresh) && isscalar (thresh) && thresh >= 0 && thresh <= 1)) error ("im2bw: THRESHOLD must be a string or a scalar in the interval [0 1]"); endif if (islogical (img)) warning ("im2bw: IMG is already binary so nothing is done"); tmp = img; else ## Convert img to gray scale if (nargin == 3) ## indexed image (we already checked that is indeed indexed earlier) img = ind2gray (img, cmap); elseif (isrgb (img)) img = rgb2gray (img); else ## Everything else, we do nothing, no matter how many dimensions endif if (ischar (thresh)) thresh = graythresh (img, thresh); endif ## Convert the threshold value to same class as the image which ## is faster and saves more memory than the opposite. if (isinteger (img)) ## We do the conversion from double to int ourselves (instead ## of using im2uint* functions), because those functions round ## during the conversion but we need thresh to be the limit. ## See bug #46390. cls = class(img); I_min = double (intmin (cls)); I_range = double (intmax (cls)) - I_min; thresh = cast (floor ((thresh * I_range) + I_min), cls); elseif (isfloat (img)) ## do nothing else ## we should have never got here in the first place anyway error ("im2bw: unsupported image of class '%s'", class (img)); endif tmp = (img > thresh); endif if (nargout > 0) BW = tmp; else imshow (tmp); endif endfunction %!assert(im2bw ([0 0.4 0.5 0.6 1], 0.5), logical([0 0 0 1 1])); # basic usage %!assert(im2bw (uint8 ([0 100 255]), 0.5), logical([0 0 1])); # with a uint8 input ## We use "bw = im2bw (...)" because otherwise it would display a figure %!warning bw = im2bw (logical ([0 1 0])); %!warning bw = im2bw (logical ([0 1 0]), 1); %!test %! warning ("off", "all", "local"); %! assert (im2bw (logical ([0 1 0])), logical ([0 1 0])) %! assert (im2bw (logical ([0 1 0]), 0), logical ([0 1 0])) %! assert (im2bw (logical ([0 1 0]), 1), logical ([0 1 0])) ## bug #46390 (on the rounding/casting of the threshold value) %!assert (nnz (im2bw (uint8 ([0:255]), 0.9)), 26) %!test %! img = uint8 ([0:255]); %! s = 0; %! for i=0:.1:1 %! s += nnz (im2bw (img, i)); %! endfor %! assert (s, 1405) ## threshold may be a negative value in the image class so care must ## taken when casting and rounding it. %!assert (nnz (im2bw (int16 ([-128:127]), 0.499)), 194) %!assert (nnz (im2bw (int16 ([-128:127]), 0.500)), 128) %!assert (nnz (im2bw (int16 ([-128:127]), 0.501)), 62) %!test %! img = uint16 ([0:intmax("uint16")]); %! s = 0; %! for i=0:.1:1 %! s += nnz (im2bw (img, i)); %! endfor %! assert (s, 360445) %!test %! img = int16 ([intmin("int16"):intmax("int16")]); %! s = 0; %! for i=0:.1:1 %! s += nnz (im2bw (img, i)); %! endfor %! assert (s, 360445) %!test %! im = [((randn(10)/10)+.3) ((randn(10)/10)+.7)]; %! assert (im2bw (im, "Otsu"), im2bw (im, graythresh (im, "Otsu"))) %! assert (im2bw (im, "moments"), im2bw (im, graythresh (im, "moments"))) %!test %! im = [((randn(10)/10)+.3) ((randn(10)/10)+.7)]; %! im = reshape (im, [10 10 1 2]); %! assert (im2bw (im, "Otsu"), im2bw (im, graythresh (im, "Otsu"))) %! assert (im2bw (im, "moments"), im2bw (im, graythresh (im, "moments"))) image-2.16.1/inst/PaxHeaders.61586/@strel0000644000000000000000000000013215005111723014555 xustar0030 mtime=1746179027.062725023 30 atime=1746179027.278726152 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/0000755000175000017500000000000015005111723016232 5ustar00avinoamavinoam00000000000000image-2.16.1/inst/@strel/PaxHeaders.61586/isscalar.m0000644000000000000000000000006215005110255016612 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/isscalar.m0000644000175000017500000000243315005110255020212 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{TF} =} isflat (@var{SE}) ## Say if a structuring element object is flat or not. ## ## Although in accordance with me TF should be a logical value, it is typed ## double due to compatibility with MATLAB ## (Perhaps there is a good reason which I can't figure out now) ## ## @seealso{strel} ## @end deftypefn ## TODO: if SE is an array of strel, this function return results for any ## strel included. function retval = isscalar (se) if (isempty (se.seq)) retval = true; else retval = isscalar (se.seq); endif endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/getsequence.m0000644000000000000000000000006215005110255017321 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/getsequence.m0000644000175000017500000000521115005110255020716 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Roberto Metere ## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{seq} =} getsequence (@var{se}) ## Decompose structuring element. ## ## Returns a strel object @var{se} that can be indexed with @code{()} to obtain ## the decomposed structuring elements that can be used to "rebuild" @var{se}. ## ## Decomposing a structuring element may lead to faster operations by ## replacing a single operation with a large @var{se} (large nhood), with ## multiple operations with smaller @var{se}. ## ## Most functions will automatically perform SE decomposition provided a ## strel object is used (instead of a logical array). This also requires ## that specific shapes are specified instead of @qcode{"arbitrary"}, since ## it may prevent SE decomposition. ## ## @seealso{imdilate, imerode, strel} ## @end deftypefn function se = getsequence (se) if (isempty (se.seq)) ## guess a square/cube/rectangle/hyperrectangle shape even if shape ## is arbitrary. There's no need to decompose when it's very small ## hence the no bother if numel > 15 if (se.flat && all (se.nhood(:)) && ! isvector (se.nhood) && numel (se.nhood) > 15) nd = ndims (se.nhood); se.seq = cell (nd, 1); for idx = 1:nd vec_size = ones (1, nd); vec_size(idx) = size (se.nhood, idx); se.seq{idx} = strel ("arbitrary", true (vec_size)); endfor elseif (strcmp (se.shape, "octagon") && se.opt.apothem > 0) persistent octagon_template = get_octagon_template (); se.seq = repmat (octagon_template, se.opt.apothem /3, 1); else se.seq{1,1} = se; endif endif endfunction function template = get_octagon_template () template = repmat ({false(3)}, 4, 1); template{1}(2,:) = true; template{2}(:,2) = true; template{3}([1 5 9]) = true; template{4}([3 5 7]) = true; template = cellfun (@(x) strel ("arbitrary", x), template, "UniformOutput", false); endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/getneighbors.m0000644000000000000000000000006215005110255017471 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/getneighbors.m0000644000175000017500000000375615005110255021102 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{offsets}, @var{heights}] =} getneighbors (@var{se}) ## Get neighbors relative position and height. ## ## For each of the neighbors in the strel object @var{se}, @var{offsets} are ## their positions relative to the origin (center). It is a @var{P}x@var{N} ## matrix, with @var{P} as the number of neighbors, and @var{N} as the number ## of dimensions. ## ## @var{heights} is a vector with the height of each neighbor in @var{se}. ## ## @seealso{getnhood, getheight, strel} ## @end deftypefn function [offsets, heights] = getneighbors (se) se_ndims = ndims (se.nhood); se_size = size (se.nhood); ## To calculate the offsets we get the subscript indexes of all elements ## in a cell array, one cell for each dimension and the that dimension ## indexes in a column. We then just subtract the center position of each ## dimension to the respective column. sub = cell (1, se_ndims); ## The (:) on find(...)(:) is because find() will return a row (instead ## of a column), if the input is just a row vector. We need to make sure ## that we always get a column for ind2sub(). [sub{1:se_ndims}] = ind2sub (se_size, find (se.nhood)(:)); offsets = cell2mat (sub) - floor ((se_size + 1) / 2); heights = getheight (se)(se.nhood); endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/subsref.m0000644000000000000000000000006215005110255016462 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/subsref.m0000644000175000017500000000330315005110255020057 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . function req = subsref (se, idx) if (! isa (se, "strel")) error ("object must be of the strel class but '%s' was used", class (se)); elseif (! strcmp (idx(1).type, "()") || numel (idx) > 1) error ("incorrect syntax to reference strel object. Use obj(idx) to access elements."); endif if (isempty (se.seq)) ## for matlab compatibility, if we don't have the sequence, then this is ## still an array of strel objects, it just happens we have we give ## back the object as longsame as 1 element sequence ## so we give the object back. This means the following: ## se(1) <-- works fine ## se(2) <-- fails ## se(1,1,1,1) <-- works fine ## se(1:3) <-- fails ## Yes, this is ugly but we don't have classdef yet... if (any (cellfun (@(x) any (x != 1), idx(1).subs))) error ("A(I): index out of bounds. This is not a sequence of strel object"); endif req = se; else req = se.seq{idx(1).subs{:}}; endif endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/numel.m0000644000000000000000000000006215005110255016131 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/numel.m0000644000175000017500000000202615005110255017527 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} numel (@var{se}) ## Return number of structuring elements in a strel object. ## @seealso{strel, @@strel/getsequence} ## @end deftypefn function number = numel (se, varargin) if (isempty (se.seq)) number = 1; else number = numel (se.seq, varargin{:}); endif endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/isflat.m0000644000000000000000000000006215005110255016273 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/isflat.m0000644000175000017500000000232215005110255017670 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Roberto Metere ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{TF} =} isflat (@var{SE}) ## Say if a structuring element object is flat or not. ## ## Although in accordance with me TF should be a logical value, it is typed ## double due to compatibility with MATLAB ## (Perhaps there is a good reason which I can't figure out now) ## ## @seealso{strel} ## @end deftypefn ## TODO: if SE is an array of strel, this function return results for any ## strel included. function TF = isflat (SE) TF = double (SE.flat); endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/display.m0000644000000000000000000000006215005110255016456 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/display.m0000644000175000017500000000275415005110255020064 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Roberto Metere ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} display (@var{SE}) ## Display the contents of object SE. ## ## @seealso{strel} ## @end deftypefn function display (SE) P = nnz (SE.nhood); if (SE.flat) flatstr = "Flat"; else flatstr = "Nonflat"; endif if (P != 1) plural_p = "s"; else plural_p = ""; endif ## FIXME using inputname won't work when SE is a field in a struct or in a ## cell array. Not only won't get the correct name sometimes, it may ## also mess up the display of nested structures. printf ("%s = \n", inputname (1)); printf (" %s STREL object with %d neighbor%s\n\n", flatstr, P, plural_p); printf (" Neighborhood:\n"); display (SE.nhood); if (!SE.flat) printf (" Height:\n"); display (SE.height); endif endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/strel.m0000644000000000000000000000006215005110255016142 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/strel.m0000644000175000017500000006153615005110255017553 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012, 2013 Roberto Metere ## Copyright (C) 2012, 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} strel (@var{shape}, @var{parameters}) ## Create a strel (structuring element) object for morphology operations. ## ## The structuring element can have any type of shape as specified by ## @var{shape}, each one with its @var{parameters}. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("arbitrary", @var{nhood}) ## @deftypefnx {Function File} {} strel ("arbitrary", @var{nhood}, @var{height}) ## Create arbitrary shaped structuring elements. ## ## @var{nhood} must be a matrix of 0's and 1's. Any number with of dimensions ## are possible. To create a non-flat SE, the @var{height} can be specified. ## See individual functions that use the strel object for an interpretation of ## non-flat SEs. ## ## Note that if an arbitrary shape is used, it may not be possible to guess ## it which may prevent shape-based optimizations (structuring element ## decomposition, see @@strel/getsequence for details). ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("ball", @var{radius}, @var{height}) ## Create ball shaped @var{nonflat} structuring element. @var{radius} must be a ## nonnegative integer that specifies the ray of a circle in X-Y plane. @var{height} ## is a real number that specifies the height of the center of the circle. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("cube", @var{edge}) ## Create cube shaped @var{flat} structuring element. @var{edge} must be a ## positive integer that specifies the length of its edges. This shape meant to ## perform morphology operations in volumes, see the square shape for 2 ## dimensional images. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("diamond", @var{radius}) ## Create diamond shaped flat structuring element. @var{radius} must be a ## positive integer. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("disk", @var{radius}) ## @deftypefnx {Function File} {} strel ("disk", @var{radius}, @var{n}) ## Create disk shaped flat structuring element. @var{radius} must be a positive ## integer. ## ## The optional @var{n} argument *must* have a value of zero but the default ## value is 4. This is to prevent future backwards incompatibilty since ## @sc{Matlab} default is also 4 but at the moment only 0 has been ## implemented. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("hypercube", @var{n}, @var{edge}) ## Create @var{n} dimensional cube (n-cube) shaped @var{flat} structuring ## element. @var{edge} must be a positive integer that specifies the length ## of its edges. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("hyperrectangle", @var{dimensions}) ## Create @var{n} dimensional hyperrectangle (or orthotope) shaped flat ## structuring element. @var{dimensions} must be a vector of positive ## integers with its lengtht at each of the dimensions. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("line", @var{len}, @var{deg}) ## Create line shaped flat structuring element. @var{len} must be a positive ## real number. @var{deg} must be a 1 or 2 elements real number, for a line in ## in 2D or 3D space. The first element of @var{deg} is the angle from X-axis ## to X-Y projection of the line while the second is the angle from Z-axis to ## the line. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("octagon", @var{apothem}) ## Create octagon shaped flat structuring element. @var{apothem} must be a ## non-negative integer, multiple of 3, that specifies the distance from the ## origin to the sides of the octagon. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("pair", @var{offset}) ## Create flat structuring element with two members. One member is placed ## at the origin while the other is placed with @var{offset} in relation to the ## origin. @var{offset} must then be a 2 element vector for the coordinates. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("periodicline", @var{p}, @var{v}) ## Create periodic line shaped flat structuring element. A periodic line will ## be built with 2*@var{p}+1 points around the origin included. These points will ## be displaced in accordance with the offset @var{v} at distances: 1*@var{v}, ## -1*@var{v}, 2*@var{v}, -2*@var{v}, ..., @var{p}*@var{v}, -@var{p}*@var{v}. ## Therefore @var{v} must be a 2 element vector for the coordinates. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("rectangle", @var{dimensions}) ## Create rectangular shaped flat structuring element. @var{dimensions} must ## be a two element vector of positive integers with the number of rows and ## columns of the rectangle. ## ## @end deftypefn ## @deftypefn {Function File} {} strel ("square", @var{edge}) ## Create square shaped flat structuring element. @var{edge} must be a positive ## integer that specifies the length of its edges. For use in volumes, see the ## cube shape. ## ## The actual structuring element neighborhood, the logical matrix used for the ## operations, can be accessed with the @code{getnhood} method. However, most ## morphology functions in the image package will have an improved performance ## if the actual strel object is used, and not its element neighborhood. ## ## @example ## @group ## se = strel ("square", 5); ## getnhood (se) ## @result{} ## 1 1 1 1 1 ## 1 1 1 1 1 ## 1 1 1 1 1 ## 1 1 1 1 1 ## 1 1 1 1 1 ## @end group ## @end example ## ## @seealso{imdilate, imerode} ## @end deftypefn function SE = strel (shape, varargin) if (nargin < 1 || nargin > 4 || (ischar (shape) && nargin < 2)) print_usage (); elseif (! ischar (shape)) varargin = horzcat ({shape}, varargin); shape = "arbitrary"; endif nvar = numel (varargin); ## because the order that these are created matters, we make them all here SE = struct; SE.shape = tolower (shape); SE.nhood = false; SE.flat = true; SE.height = []; SE.seq = cell; SE.opt = struct; switch (SE.shape) case "arbitrary" if (numel (varargin) == 1) nhood = varargin{1}; SE.flat = true; elseif (numel (varargin) == 2) nhood = varargin{1}; SE.height = varargin{2}; SE.flat = false; else error ("strel: an arbitrary shape takes 1 or 2 arguments"); endif ## don't use isbw because we also want to allow empty nhood if (any ((nhood(:) != 1) & (nhood(:) != 0))) error ("strel: NHOOD must be a matrix with only 0 and 1 values") endif SE.nhood = logical (nhood); # we need this as logical for the height tests if (! SE.flat && ! (isnumeric (SE.height) && isreal (SE.height) && ndims (SE.height) == ndims (nhood) && all (size (SE.height) == size (nhood)) && all (isfinite (SE.height(:))))) error ("strel: HEIGHT must be a finite real matrix of the same size as NHOOD"); endif if (nnz (SE.height) == 0) SE.flat = true; endif case "ball" if (numel (varargin) == 2) radius = varargin{1}; height = varargin{2}; else ## TODO implement third option for number of periodic lines approximation error ("strel: a ball shape needs 2 arguments"); endif if (! is_positive_integer (radius)) error ("strel: RADIUS must be a positive integer"); elseif (! (isscalar (height) && isnumeric (height))) error ("strel: HEIGHT must be a real number"); endif # Ellipsoid: (x/radius)^2 + (y/radius)^2 + (z/height)^2 = 1 # We need only the 1 cells of SE.nhood [x, y] = meshgrid (-radius:radius, -radius:radius); SE.nhood = ((x.^2 + y.^2) <= radius^2); # X-Y circle SE.height = height / radius * SE.nhood .* sqrt (radius^2 - x .^2 - y.^2); SE.flat = false; case "cube" if (numel (varargin) == 1) SE.opt.edge = varargin{1}; else error ("strel: no EDGE specified for cube shape"); endif if (! is_positive_integer (SE.opt.edge)) error ("strel: EDGE value must be a positive integer"); endif SE.nhood = true (SE.opt.edge, SE.opt.edge, SE.opt.edge); SE.flat = true; case "diamond" if (numel (varargin) == 1) radius = varargin{1}; else error ("strel: no RADIUS specified for diamond shape"); endif if (! is_positive_integer (radius)) error ("strel: RADIUS must be a positive integer"); endif corner = tril (true (radius+1, radius), -1); SE.nhood = [rot90(tril(true(radius+1))) corner; corner' rot90(triu(true(radius),1))]; SE.flat = true; case "disk" if (nvar < 1 || nvar > 2) error ("strel: disk shape takes 1 or 2 arguments"); endif radius = varargin{1}; if (! is_positive_integer (radius)) error ("strel: RADIUS must be a positive integer"); endif n = 4; if (nvar > 1) n = varargin{2}; if (! isnumeric (n) && ! isscalar (n) && any (n != [0 4 6 8])) error ("strel: N for disk shape must be 0, 4, 6, or 8"); endif endif ## TODO implement approximation by periodic lines if (n != 0) error ("strel: N for disk shape not yet implemented, use N of 0"); endif [x, y] = meshgrid (-radius:radius, -radius:radius); r = sqrt (x.^2 + y.^2); SE.nhood = r <= radius; SE.flat = true; case "hypercube" if (numel (varargin) == 2) SE.opt.n = varargin{1}; SE.opt.edge = varargin{2}; else error ("strel: an hypercube shape needs 2 arguments"); endif if (! is_positive_integer (SE.opt.n)) error ("strel: N value must be a positive integer"); elseif (! is_positive_integer (SE.opt.edge)) error ("strel: EDGE value must be a positive integer"); endif SE.nhood = true (repmat (SE.opt.edge, 1, SE.opt.n)); SE.flat = true; case "hyperrectangle" if (numel (varargin) == 1) SE.opt.dimensions = varargin{1}; else error ("strel: no DIMENSIONS specified for rectangle shape"); endif if (! isnumeric (SE.opt.dimensions)) error ("strel: DIMENSIONS must be a 2 element vector"); elseif (! all (arrayfun (@is_positive_integer, SE.opt.dimensions(:)))) error ("strel: DIMENSIONS values must be positive integers"); endif SE.nhood = true (SE.opt.dimensions(:)); SE.flat = true; case "line" if (numel (varargin) == 2) linelen = varargin{1}; degrees = varargin{2}; else error ("strel: a line shape needs 2 arguments"); endif if (! (isscalar (linelen) && isnumeric (linelen) && linelen > 0)) error ("strel: LEN must be a positive real number"); elseif (! isnumeric (degrees)) error ("strel: DEG must be numeric"); endif ## 2d or 3d line dimens = numel (degrees) +1; if (dimens == 2) degrees = degrees(1); elseif (dimens == 3) alpha = degrees(1); phi = degrees(2); else error ("strel: DEG must be a 1 or 2 elements matrix"); endif ## TODO this was the 3dline and line options, which have separate code ## but a proper merge should be made. if (dimens == 2) ## Line length are always odd, to center strel at the middle of the line. ## We look it as a diameter of a circle with given slope # It computes only lines with angles between 0 and 44.9999 deg90 = mod (degrees, 90); if (deg90 > 45) alpha = pi * (90 - deg90) / 180; else alpha = pi * deg90 / 180; endif ray = (linelen - 1)/2; ## We are interested only in the discrete rectangle which contains the diameter ## However we focus our attention to the bottom left quarter of the circle, ## because of the central symmetry. c = round (ray * cos (alpha)) + 1; r = round (ray * sin (alpha)) + 1; ## Line rasterization line = false (r, c); m = tan (alpha); x = [1:c]; y = r - fix (m .* (x - 0.5)); indexes = sub2ind ([r c], y, x); line(indexes) = true; ## We view the result as 9 blocks. # Preparing blocks linestrip = line(1, 1:c - 1); linerest = line(2:r, 1:c - 1); z = false (r - 1, c); # Assemblying blocks SE.nhood = vertcat ( horzcat (z, linerest(end:-1:1,end:-1:1)), horzcat (linestrip, true, linestrip(end:-1:1,end:-1:1)), horzcat (linerest, z(end:-1:1,end:-1:1)) ); # Rotate/transpose/flip? sect = fix (mod (degrees, 180) / 45); switch (sect) case 1, SE.nhood = transpose (SE.nhood); case 2, SE.nhood = rot90 (SE.nhood, 1); case 3, SE.nhood = fliplr (SE.nhood); otherwise, # do nothing endswitch elseif (dimens == 3) ## This is a first implementation ## Stroke line from cells (x1, y1, z1) to (x2, y2, z2) alpha *= pi / 180; phi *= pi / 180; x1 = y1 = z1 = 0; x2 = round (linelen * sin (phi) * cos (alpha)); y2 = round (linelen * sin (phi) * sin (alpha)); z2 = round (linelen * cos (phi)); # Adjust x2, y2, z2 to have one central cell x2 += (! mod (x2, 2)) * sign0positive (x2); y2 += (! mod (y2, 2)) * sign0positive (y2); z2 += (! mod (z2, 2)) * sign0positive (z2); # Invert x x2 = -x2; # Tanslate parallelepiped to be in positive quadrant if (x2 < 0) x1 -= x2; x2 -= x2; endif if (y2 < 0) y1 -= y2; y2 -= y2; endif if (z2 < 0) z1 -= z2; z2 -= z2; endif # Compute index2es dim = abs ([(x2 - x1) (y2 - y1) (z2 - z1)]); m = max (dim); base = meshgrid (0:m - 1,1) + 0.5; a = floor ((x2 - x1)/m .* base); b = floor ((y2 - y1)/m .* base); c = floor ((z2 - z1)/m .* base); # Adjust indexes to be valid a -= min (a) - 1; b -= min (b) - 1; c -= min (c) - 1; indexes = sub2ind (dim, a, b, c); SE.nhood = false (dim); SE.nhood(indexes) = true; endif SE.flat = true; case "octagon" if (numel (varargin) == 1) SE.opt.apothem = apothem = varargin{1}; else error ("strel: no APOTHEM specified for octagon shape"); endif if (! is_nonnegative_integer (apothem) || mod (apothem, 3) != 0) error ("strel: APOTHEM must be a positive integer multiple of 3"); endif ## we look at it as 9 blocks. North AND South are the same and West TO ## East as well. We make the corner for NorthEast and rotate it for the ## other corners if (apothem == 0) SE.nhood = true (1); else cwide = apothem/3*2 + 1; iwide = apothem/3*2 - 1; N_and_S = true ([cwide iwide]); corner = tril (true (cwide)); SE.nhood = [rotdim(corner), N_and_S, corner; true([iwide (2*apothem + 1)]); transpose(corner), N_and_S, rotdim(corner, -1)]; endif SE.flat = true; case "pair" if (numel (varargin) == 1) offset = varargin{1}; else error ("strel: no OFFSET specified for pair shape"); endif if (! isnumeric (offset) || numel (offset) != 2) error ("strel: OFFSET must be a 2 element vector"); elseif (any (fix (offset) != offset)) error ("strel: OFFSET values must be integers"); endif lengths = abs (2*offset) + 1; SE.nhood = false (lengths); origin = (lengths + 1)/2; SE.nhood(origin(1), origin(2)) = true; SE.nhood(origin(1) + offset(1), origin(2) + offset(2)) = true; SE.flat = true; case "periodicline" if (numel (varargin) == 2) p = varargin{1}; v = varargin{2}; else error ("strel: a periodic line shape needs 2 arguments"); endif if (! is_positive_integer (p)) error ("strel: P must be a positive integer"); elseif (! isnumeric (v) || numel (v) != 2) error ("strel: V must be a 2 element vector"); elseif (any (fix (v) != v)) error ("strel: values of V must be integers"); endif lengths = abs (2*p*v) + 1; SE.nhood = false (lengths); origin = (lengths + 1)/2; for i = -p:p point = i*v + origin; SE.nhood(point(1), point(2)) = true; endfor case "rectangle" if (numel (varargin) == 1) SE.opt.dimensions = varargin{1}; else error ("strel: no DIMENSIONS specified for rectangle shape"); endif if (! isnumeric (SE.opt.dimensions) || numel (SE.opt.dimensions) != 2) error ("strel: DIMENSIONS must be a 2 element vector"); elseif (! is_positive_integer (SE.opt.dimensions(1)) || ! is_positive_integer (SE.opt.dimensions(2))) error ("strel: DIMENSIONS values must be positive integers"); endif SE.nhood = true (SE.opt.dimensions); SE.flat = true; case "square" if (numel (varargin) == 1) SE.opt.edge = varargin{1}; else error ("strel: no EDGE specified for square shape"); endif if (! is_positive_integer (SE.opt.edge)) error ("strel: EDGE value must be positive integers"); endif SE.nhood = true (SE.opt.edge); SE.flat = true; otherwise error ("strel: unknown SHAPE `%s'", shape); endswitch SE = class (SE, "strel"); endfunction function retval = is_positive_integer (val) retval = isscalar (val) && isnumeric (val) && val > 0 && fix (val) == val; endfunction function retval = is_nonnegative_integer (val) retval = isscalar (val) && isnumeric (val) && val >= 0 && fix (val) == val; endfunction function retval = sign0positive (val) if (sign (val) == -1) retval = -1; else retval = 1; endif endfunction %!test %! shape = logical ([0 0 0 1]); %! assert (getnhood (strel (shape)), shape); %! assert (getnhood (strel ("arbitrary", shape)), shape); %! %! height = [0 0 0 3]; %! assert (getnhood (strel ("arbitrary", shape, height)), shape); %! assert (getheight (strel ("arbitrary", shape, height)), height); %!test %! shape = logical ([0 0 1]); %! height = [-2 1 3]; ## this works for matlab compatibility %! assert (getnhood (strel ("arbitrary", shape, height)), shape); %! assert (getheight (strel ("arbitrary", shape, height)), height); %!test %! shape = logical ([0 0 0 1 0 0 0 %! 0 1 1 1 1 1 0 %! 0 1 1 1 1 1 0 %! 1 1 1 1 1 1 1 %! 0 1 1 1 1 1 0 %! 0 1 1 1 1 1 0 %! 0 0 0 1 0 0 0]); %! height = [ 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 %! 0.00000 0.33333 0.66667 0.74536 0.66667 0.33333 0.00000 %! 0.00000 0.66667 0.88192 0.94281 0.88192 0.66667 0.00000 %! 0.00000 0.74536 0.94281 1.00000 0.94281 0.74536 0.00000 %! 0.00000 0.66667 0.88192 0.94281 0.88192 0.66667 0.00000 %! 0.00000 0.33333 0.66667 0.74536 0.66667 0.33333 0.00000 %! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000]; %! assert (getnhood (strel ("ball", 3, 1)), shape); %! assert (getheight (strel ("ball", 3, 1)), height, 0.0001); %!test %! shape = logical ([0 0 0 1 0 0 0 %! 0 0 1 1 1 0 0 %! 0 1 1 1 1 1 0 %! 1 1 1 1 1 1 1 %! 0 1 1 1 1 1 0 %! 0 0 1 1 1 0 0 %! 0 0 0 1 0 0 0]); %! assert (getnhood (strel ("diamond", 3)), shape); %!test %! shape = logical ([0 0 0 1 0 0 0 %! 0 1 1 1 1 1 0 %! 0 1 1 1 1 1 0 %! 1 1 1 1 1 1 1 %! 0 1 1 1 1 1 0 %! 0 1 1 1 1 1 0 %! 0 0 0 1 0 0 0]); %! assert (getnhood (strel ("disk", 3, 0)), shape); %!test %! shape = logical ([1 1 1]); %! assert (getnhood (strel ("line", 3.9, 20.17)), shape); %! shape = logical ([0 0 1 %! 0 1 0 %! 1 0 0]); %! assert (getnhood (strel ("line", 3.9, 20.18)), shape); %! shape = logical ([1 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 %! 0 0 1 0 0 0 0 0 0 %! 0 0 1 0 0 0 0 0 0 %! 0 0 0 1 0 0 0 0 0 %! 0 0 0 0 1 0 0 0 0 %! 0 0 0 0 0 1 0 0 0 %! 0 0 0 0 0 0 1 0 0 %! 0 0 0 0 0 0 1 0 0 %! 0 0 0 0 0 0 0 1 0 %! 0 0 0 0 0 0 0 0 1]); %! assert (getnhood (strel ("line", 14, 130)), shape); %!test %! se = strel ("octagon", 0); %! seq = getsequence (se); %! assert (getnhood (se), true (1)); %! assert (getnhood (seq(1)), true (1)); %! %! se = strel ("octagon", 3); %! seq = getsequence (se); %! shape = logical ([0 0 1 1 1 0 0 %! 0 1 1 1 1 1 0 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 0 1 1 1 1 1 0 %! 0 0 1 1 1 0 0]); %! assert (getnhood (se), shape); %! assert (size (seq), [4 1]); %! %! templ1 = logical ([0 0 0; 1 1 1; 0 0 0]); %! templ2 = logical ([0 1 0; 0 1 0; 0 1 0]); %! templ3 = logical ([1 0 0; 0 1 0; 0 0 1]); %! templ4 = logical ([0 0 1; 0 1 0; 1 0 0]); %! assert ({getnhood(seq(1)) getnhood(seq(2)) getnhood(seq(3)) getnhood(seq(4))}, %! {templ1 templ2 templ3 templ4}); %! %! seq = getsequence (strel ("octagon", 21)); %! assert (size (seq), [28 1]); %! assert (arrayfun (@(x) getnhood (seq(x)), 1:4:25, "UniformOutput", false), %! repmat ({templ1}, 1, 7)); %! assert (arrayfun (@(x) getnhood (seq(x)), 2:4:26, "UniformOutput", false), %! repmat ({templ2}, 1, 7)); %! assert (arrayfun (@(x) getnhood (seq(x)), 3:4:27, "UniformOutput", false), %! repmat ({templ3}, 1, 7)); %! assert (arrayfun (@(x) getnhood (seq(x)), 4:4:28, "UniformOutput", false), %! repmat ({templ4}, 1, 7)); %!test %! shape = logical ([1 1 0]'); %! assert (getnhood (strel ("pair", [-1 0])), shape); %! shape = logical ([1 0 0 0 0 0 0 %! 0 0 0 1 0 0 0 %! 0 0 0 0 0 0 0]); %! assert (getnhood (strel ("pair", [-1 -3])), shape); %! shape = logical ([0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 1 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 1]); %! assert (getnhood (strel ("pair", [2 3])), shape); %!test %! assert (getnhood (strel ("rectangle", [10 5])), true (10, 5)); %! assert (getnhood (strel ("square", 5)), true (5)); ## test how @strel/getsequence and indexing works fine %!shared se, seq %! se = strel ("square", 5); %! seq = getsequence (se); %! assert (class (se(1)), "strel") %! assert (class (se(1,1)),"strel") %! assert (class (seq), "strel") %! assert (class (seq(1)), "strel") %! assert (class (seq(2)), "strel") %! assert (numel (se), 1) %! assert (numel (seq), 2) %! assert (getnhood (seq(1)), true (5, 1)) %! assert (getnhood (seq(2)), true (1, 5)) %! assert (size (se), [1 1]) %! assert (size (seq), [2 1]) %! assert (isscalar (se), true) %! assert (isscalar (seq), false) %!error se(2); %!error seq(3); ## test reflection %!test %! se = strel ("arbitrary", [1 0 0; 1 1 0; 0 1 0], [2 0 0; 3 1 0; 0 3 0]); %! ref = reflect (se); %! assert (getnhood (ref), logical([0 1 0; 0 1 1; 0 0 1])); %! assert (getheight (ref), [0 3 0; 0 1 3; 0 0 2]); ## test input validation %!error strel() %!error strel("nonmethodthing", 2) %!error strel("arbitrary", "stuff") %!error strel("arbitrary", [0 0 1], [2 0 1; 4 5 1]) %!error strel("arbitrary", [0 0 1], "stuff") %!error strel("ball", -3, 1) %!error strel("diamond", -3) %!error strel("disk", -3) %!error strel("line", 0, 45) %!error strel("octagon", 3.5) %!error strel("octagon", 4) %!error strel("octagon", -1) %!error strel("pair", [45 67 90]) %!error strel("rectangle", 2) %!error strel("rectangle", [2 -5]) %!error strel("square", [34 1-2]) image-2.16.1/inst/@strel/PaxHeaders.61586/reflect.m0000644000000000000000000000006215005110255016435 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/reflect.m0000644000175000017500000000501015005110255020027 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Roberto Metere ## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{se2} =} reflect (@var{se}) ## Reflect structuring element of strel object. ## ## Returns another strel object with all its elements reflected. If @var{se} is ## a sequence of strel objects, reflects each one of them. If @var{se} is a ## non-flat structuring element, its height is reflected accordingly. ## ## Reflection is a rotation of 180 degrees around the center, including for ## N-dimensional matrices. ## ## @seealso{strel, @@strel/getheight, @@strel/getsequence, @@strel/translate} ## @end deftypefn function se = reflect (se) ## FIXME this should be done in a smarter way for non-arbitrary shapes (but ## then we may need to also change some of the options values...) if (isempty (se.seq)) se = rotate_strel (se); else for idx = 1:numel (se.seq) se.seq{idx} = rotate_strel (se.seq{idx}); endfor endif endfunction function se = rotate_strel (se) nhood = getnhood (se); height = getheight (se); if (se.flat) se = strel ("arbitrary", rotate (nhood)); else se = strel ("arbitrary", rotate (nhood), rotate (height)); endif endfunction function rot = rotate (ori) rot = reshape (ori(end:-1:1), size (ori)); ## For Matlab compatibility: ## Check if any of the sides has an even size. If so, we create a larger ## matrix, all even sized, and place the rotated matrix on the top left ## corner (and whatever top and left means for N dimensions) if (any (mod (size (ori)+1, 2))) ## get subcript indices of the elements that matter ori_ind = find (ori); [subs{1:ndims (ori)}] = ind2sub (size (ori), ori_ind); rot = zeros ((floor (size (ori) /2) *2) +1, class (ori)); rot_ind = sub2ind (size (rot), subs{:}); rot(rot_ind) = ori(ori_ind); endif endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/size.m0000644000000000000000000000006215005110255015763 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/size.m0000644000175000017500000000241615005110255017364 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{TF} =} isflat (@var{SE}) ## Say if a structuring element object is flat or not. ## ## Although in accordance with me TF should be a logical value, it is typed ## double due to compatibility with MATLAB ## (Perhaps there is a good reason which I can't figure out now) ## ## @seealso{strel} ## @end deftypefn ## TODO: if SE is an array of strel, this function return results for any ## strel included. function vals = size (se) if (isempty (se.seq)) vals = [1 1]; else vals = size (se.seq); endif endfunction image-2.16.1/inst/@strel/PaxHeaders.61586/getnhood.m0000644000000000000000000000006215005110255016620 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/getnhood.m0000644000175000017500000000170315005110255020217 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Roberto Metere ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{NHOOD} =} getnhood (@var{SE}) ## Return the neighborhood of structuring element SE. ## ## @seealso{getneighbors, strel} ## @end deftypefn function NHOOD = getnhood (SE) NHOOD = SE.nhood; endfunctionimage-2.16.1/inst/@strel/PaxHeaders.61586/translate.m0000644000000000000000000000006215005110255017006 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/translate.m0000644000175000017500000000223115005110255020402 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Roberto Metere ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{SE2} =} translate (@var{SE}, @var{V}) ## Generate a new structuring element, which is SE translated in rows and ## columns as expressed in the offset 2-dimensional array V. ## ## @seealso{reflect, strel} ## @end deftypefn ## TODO: If SE is an array of structuring element objects, then it reflects ## each element of SE. function SE2 = translate (SE) error ("translate: not yet implemented"); endfunctionimage-2.16.1/inst/@strel/PaxHeaders.61586/getheight.m0000644000000000000000000000006215005110255016761 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@strel/getheight.m0000644000175000017500000000205115005110255020355 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Roberto Metere ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{H} =} getheight (@var{SE}) ## Return the heights of a non-flat structuring element. ## If SE is flat, then all heights are zeros. ## ## @seealso{getnhood, strel} ## @end deftypefn function H = getheight (SE) if (SE.flat) H = zeros (size (SE.nhood)); else H = SE.height; endif endfunction image-2.16.1/inst/PaxHeaders.61586/imgradient.m0000644000000000000000000000006215005110255015703 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imgradient.m0000644000175000017500000000673315005110255017312 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Brandon Miles ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{gradMag}, @var{gradDir}] =} imgradient (@var{img}) ## @deftypefnx {Function File} {[@var{gradMag}, @var{gradDir}] =} imgradient (@var{img}, @var{method}) ## @deftypefnx {Function File} {[@var{gradMag}, @var{gradDir}] =} imgradient (@var{gx}, @var{gy}) ## Compute the gradient magnitude and direction in degrees for an image. ## ## These are computed from the @var{gx} and @var{xy} gradients using ## @code{imgradientxy}. The first input @var{img} is a gray scale image to ## compute the edges on. The second input @var{method} controls the method ## used to calculate the gradients. Alternatively the first input @var{gx} ## can be the x gradient and the second input @var{gy} can be the y gradient. ## ## The first output @var{gradMag} returns the magnitude of the gradient. ## The second output @var{gradDir} returns the direction in degrees. ## ## The @var{method} input argument must be a string specifying one of the ## methods supported by @code{imgradientxy}. ## ## @seealso{edge, imgradientxy} ## @end deftypefn function [gradMag, gradDir] = imgradient (img, method = "sobel") if (nargin < 1 || nargin > 2) print_usage (); elseif (ndims (img) != 2) error("imgradient: IMG must be a 2 dimensional matrix"); endif if (ischar (method)) [gradX, gradY] = imgradientxy (img, method); else ## we already got gX and gY, just confirm it's good data if (! size_equal (img, method)) error("imgradient: GX and GY must be of equal size") endif gradX = img; gradY = method; endif gradMag = sqrt (gradX.^2 + gradY.^2); if (nargout > 1) ## Why imgradient invert vertical when computing the angle ## Use atan2(-gy,gx)*pi/180. See http://stackoverflow.com/questions/18549015 gradDir = atan2d (-gradY, gradX); endif endfunction %!test %! A = [0 1 0 %! 1 1 1 %! 0 1 0]; %! %! [gMag, gDir] = imgradient (A); %! assert (gMag,[sqrt(18) 4 sqrt(18); 4 0 4; sqrt(18),4,sqrt(18)]); %! assert (gDir,[-45 -90 -135; -0 -0 -180; 45 90 135]); %! %! ## the following just test if passing gx and gy separately gets %! ## us the same as the image and method though imgradient %! [gxSobel, gySobel] = imgradientxy (A, "Sobel"); %! [gxPrewitt, gyPrewitt] = imgradientxy (A, "Prewitt"); %! [gxCd, gyCd] = imgradientxy (A, "CentralDifference"); %! [gxId, gyId] = imgradientxy (A, "IntermediateDifference"); %! %! assert (imgradient (A), %! imgradient (gxSobel, gySobel)); %! assert (imgradient (A, "Sobel"), %! imgradient (gxSobel, gySobel)); %! assert (imgradient (A, "Prewitt"), %! imgradient(gxPrewitt, gyPrewitt)); %! assert (imgradient (A, "CentralDifference"), %! imgradient (gxCd, gyCd)); %! assert (imgradient (A, "IntermediateDifference"), %! imgradient (gxId, gyId)); image-2.16.1/inst/PaxHeaders.61586/psnr.m0000644000000000000000000000006215005110255014542 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/psnr.m0000644000175000017500000000415515005110255016145 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{peaksnr}] =} psnr (@var{A}, @var{ref}) ## @deftypefnx {Function File} {[@var{peaksnr}] =} psnr (@var{A}, @var{ref}, @var{peak}) ## @deftypefnx {Function File} {[@var{peaksnr}, @var{snr}] =} psnr (@dots{}) ## Compute peak signal-to-noise ratio. ## ## Computes the peak signal-to-noise ratio for image @var{A}, using ## @var{ref} as reference. @var{A} and @var{ref} must be of same size ## and class. ## ## The optional value @var{peak} defines the highest value for the image ## and its default is class dependent (see @code{getrangefromclass}). ## ## The second output @var{snr} is the simple signal-to-noise ratio. ## ## @seealso{immse} ## @end deftypefn function [peaksnr, snr] = psnr (A, ref, peak) if (nargin < 2 || nargin > 3) print_usage (); elseif (! size_equal (A, ref)) error ("psnr: A and REF must be of same size"); elseif (! strcmp (class (A), class (ref))) error ("psnr: A and REF must have same class"); end if (nargin < 3) peak = getrangefromclass (A)(2); elseif (! isscalar (peak)) error ("psnr: PEAK must be a scalar value"); endif ## simpler way to keep single precision if they are single if (isinteger (A)) A = double (A); ref = double (ref); endif mse = immse (A, ref); peaksnr = 10 * log10 ((peak.^2) / mse); if (nargout > 1) snr = 10 * log10 ((sumsq (A(:)) / numel (A)) / mse); endif endfunction image-2.16.1/inst/PaxHeaders.61586/wiener2.m0000644000000000000000000000006215005110255015133 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/wiener2.m0000644000175000017500000003141215005110255016532 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## Copyright (C) 2017 David Miguel Susano Pinto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{J} =} wiener2 (@var{I}) ## @deftypefnx {Function File} {@var{J} =} wiener2 (@var{I}, @var{nhood}) ## @deftypefnx {Function File} {@var{J} =} wiener2 (@var{I}, @var{noise}) ## @deftypefnx {Function File} {@var{J} =} wiener2 (@var{I}, @var{nhood}, @var{noise}) ## @deftypefnx {Function File} {[@var{J}, @var{noise}] =} wiener2 (@dots{}) ## Apply an adaptive noise reduction filter. ## ## The wiener2 function locally applies a linear averaging filter to the input ## image @var{I}. The averaging operation will be performed using a ## neighbourhood of each pixel of size @var{nhood}. ## The strength of the averaging depends on the local variance in this ## neighborhood and on the given variance of the @var{noise} (of mean zero). ## As a result, pixels in a region with higher contrast will be smoothed less, ## and pixels in a region with lower contrast will be smoothed more. ## ## This operation is useful to remove noise from the image, while blurring edges ## much less compared to a global linear averaging filter. ## ## @var{nhood} defaults to @code{repmat (3, [1 ndims(I)])} which in ## the most common case of 2D images would be @code{[3 3]}. ## The @var{noise} variance will be estimated by the mean variance ## in the neighborhoods, if not given. ## ## Despite the function name, @var{I} may have any number of ## dimensions. However, beware of singleton dimensions and border ## effects because @var{nhood} defaults to a vector of 3's for all ## dimensions. It may be adequate to manually set @var{nhood} to ## length 1 for singleton dimensions. Also, in the specific case of ## RGB images where @var{I} is an array of size MxNx3, @var{nhood} ## will default to @code{[3 3 3]} while @code{[3 3]} may be more ## useful. ## ## @seealso{imfilter, medfilt2} ## @end deftypefn function [denoised, noise] = wiener2 (im, nhood, noise = []) if (nargin < 1 || nargin > 3) print_usage (); elseif (! isnumeric (im) && ! islogical (im)) error ("wiener2: I must be a numeric array"); endif nd = ndims (im); if (nargin == 1) ## wiener2 (I) nhood = repmat (3, [nd 1]); elseif (isempty (nhood) || isscalar (nhood)) ## wiener2 (I, noise) noise = nhood; nhood = repmat (3, [nd 1]); endif if (! isreal (noise) || (! isempty (noise) && ! isscalar (noise))) error ("wiener2: NOISE must be a real number"); elseif (! isreal (nhood) || ! isvector (nhood) || any (nhood < 0) || any (nhood != fix (nhood))) error ("wiener2: NHOOD must be a vector of non-negative integers"); endif cls = class (im); if (! isfloat (im)) im = im2double (im); endif [denoised, noise] = wiener_filter (im, nhood, noise); denoised = imcast (denoised, cls); endfunction function [mean_im, noise] = wiener_filter (im, nhood, noise) ## do the Wiener filtering: ## The algorithm is taken from the book ## "Two-Dimensional Signal and Image Processing" ## by Jae S. Lim, Prentice Hall Ptr, 1st edition 1989. ## Equations (9.26), (9.27) and (9.29) on page 538 - 539. ## ## im_out = mean_im + variance_orig / (variance_orig + variance_noise) * (im - mean_im) ## with ## variance_orig = variance_im - variance_noise if variance_im > variance_noise ## variance_orig = 0 otherwise ## and ## mean_im = "mean value of pixel neighborhood" in im ## variance_im = "variance of pixel neighbohood" in im ## variance_noise = "variance of noise" to be removed (mean_noise assumed to be zero) box_filter = fspecial ("average", nhood); mean_im = convn (im, box_filter, "same"); variance_im = convn (im.^2, box_filter, "same") - mean_im.^ 2; if (isempty (noise)) noise = mean (variance_im(:)); endif variance_im = max (0, variance_im - noise); mean_im += variance_im ./ (variance_im + noise) .* (im - mean_im); endfunction %!shared im0, im0_out, im0_n %! im0 = ones (5, 5); %! im0_out = ones (5, 5); %! im0_out(1:4:5, 1:4:5) = 0.67111; %! im0_out(1:4:5, 2:4) = 0.78074; %! im0_out(2:4, 1:4:5) = 0.78074; %! im0_n = 0.1462; ## test input syntax: %!error wiener2 () %!assert (wiener2 (im0)) %!assert (wiener2 (im0, [2, 3])) %!assert (wiener2 (im0, 0.5)) %!assert (wiener2 (im0, [2, 3], 0.5)) %!error wiener2 (im0, [2, 3], 0.5, 2) ## test dimensions and classes: %!test %! [im_out, noise_out] = wiener2 (im0); %! assert (size (im_out), size (im0)) %! assert (class (noise_out), "double") %! assert (numel (noise_out), 1) %!assert (wiener2 (im0), im0_out, 1e-5) %!assert (wiener2 (single (im0)), single (im0_out), 1e-5) %!assert (class (wiener2 (single (im0))), "single") %!assert (wiener2 (im2uint8 (im0)), im2uint8 (im0_out)) %!assert (class (wiener2 (im2uint8 (im0))), "uint8") %!assert (wiener2 (im2uint16 (im0)), im2uint16 (im0_out), 1) %!assert (class (wiener2 (im2uint16 (im0))), "uint16") %!assert (wiener2 (im2int16 (im0)), im2int16 (im0_out), 1) %!assert (class (wiener2 (im2int16 (im0))), "int16") ## test calculation results: %!test %! im_out = wiener2 (im0); %! assert (im_out, im0_out, 1e-4) %! [out, n] = wiener2 (im0); %! assert (out, im0_out, 1e-4) %! assert (n, im0_n, 1e-4) %!test %! im1 = zeros (5, 5); %! im1(2:4, 2:4) = 1; %! im1_out = [ %! 0.1111 0.2222 0.2726 0.2222 0.1111; %! 0.2222 0.5911 0.7274 0.5911 0.2222; %! 0.2726 0.7274 1.0000 0.7274 0.2726; %! 0.2222 0.5911 0.7274 0.5911 0.2222; %! 0.1111 0.2222 0.2726 0.2222 0.1111]; %! im1_n = 0.1817; %! im1_out_55 = [ %! 0.1600 0.2400 0.2400 0.2400 0.1600; %! 0.2400 0.4667 0.4667 0.4667 0.2400; %! 0.2400 0.4667 0.4667 0.4667 0.2400; %! 0.2400 0.4667 0.4667 0.4667 0.2400; %! 0.1600 0.2400 0.2400 0.2400 0.1600]; %! im1_n_55 = 0.1920; %! im1_out_05 = [ %! 0.1111 0.2222 0.3333 0.2222 0.1111; %! 0.2222 0.4444 0.6667 0.4444 0.2222; %! 0.3333 0.6667 1.0000 0.6667 0.3333; %! 0.2222 0.4444 0.6667 0.4444 0.2222; %! 0.1111 0.2222 0.3333 0.2222 0.1111]; %! im1_out_55_05 = [ %! 0.1600 0.2400 0.2400 0.2400 0.1600; %! 0.2400 0.3600 0.3600 0.3600 0.2400; %! 0.2400 0.3600 0.3600 0.3600 0.2400; %! 0.2400 0.3600 0.3600 0.3600 0.2400; %! 0.1600 0.2400 0.2400 0.2400 0.1600]; %! im1_out_35 = [ %! 0.1333 0.2000 0.2000 0.2000 0.1333; %! 0.2642 0.5156 0.5156 0.5156 0.2642; %! 0.3230 0.6770 0.6770 0.6770 0.3230; %! 0.2642 0.5156 0.5156 0.5156 0.2642; %! 0.1333 0.2000 0.2000 0.2000 0.1333]; %! im1_out_51 = [ %! 0 0.2400 0.2400 0.2400 0 %! 0 0.7600 0.7600 0.7600 0 %! 0 0.7600 0.7600 0.7600 0 %! 0 0.7600 0.7600 0.7600 0 %! 0 0.2400 0.2400 0.2400 0]; %! assert (wiener2 (im1), im1_out, 1e-4) %! [out, n] = wiener2 (im1); %! assert (out, im1_out, 1e-4) %! assert (n, im1_n, 1e-4) %! assert (wiener2 (im1, [5, 5]), im1_out_55, 1e-4) %! [out, n] = wiener2 (im1, [5, 5]); %! assert (out, im1_out_55, 1e-4) %! assert (n, im1_n_55, 1e-4) %! assert (wiener2 (im1, 0.5), im1_out_05, 1e-4) %! assert (wiener2 (im1, [5, 5], 0.5), im1_out_55_05, 1e-4) %! assert (wiener2 (im1, [3, 5]), im1_out_35, 1e-4) %! assert (wiener2 (im1, [5, 1]), im1_out_51, 1e-4) %!test %! ## Tests for even-sized neighbourhood %! im1 = zeros (5, 5); %! im1(2:4, 2:4) = 1; %! %! im1_out_23 = [ %! 0.1667 0.2233 0.2978 0.2233 0.1667 %! 0.2233 0.7767 1.0000 0.7767 0.2233 %! 0.2233 0.7767 1.0000 0.7767 0.2233 %! 0.1667 0.5533 0.7022 0.5533 0.1667 %! 0 0 0 0 0]; %! %! im1_out_43 = [ %! 0.1667 0.2813 0.3750 0.2813 0.1667 %! 0.2500 0.6250 0.7500 0.6250 0.2500 %! 0.2500 0.6250 0.7500 0.6250 0.2500 %! 0.1667 0.4375 0.6250 0.4375 0.1667 %! 0.0833 0.1667 0.2500 0.1667 0.0833]; %! %! im1_out_44 = [ %! 0.2500 0.3018 0.3018 0.2500 0.1250 %! 0.3018 0.6647 0.6647 0.4971 0.1875 %! 0.3018 0.6647 0.6647 0.4971 0.1875 %! 0.2500 0.4971 0.4971 0.2500 0.1250 %! 0.1250 0.1875 0.1875 0.1250 0.0625]; %! %! assert (wiener2 (im1, [2, 3]), im1_out_23, 1e-4) %! assert (wiener2 (im1, [4, 3]), im1_out_43, 1e-4) %! assert (wiener2 (im1, [4, 4]), im1_out_44, 1e-4) %!test %! im2 = zeros (5, 5); %! im2(2:4, 2:4) = 70; %! im2(3, 3) = 90; %! im2 = uint8 (im2); %! im2_out = uint8 ([ %! 8 16 20 16 8 ; %! 16 46 54 46 16; %! 20 54 72 54 20; %! 16 46 54 46 16; %! 8 16 20 16 8 ]); %! im2_n = 0.0146; %! im2_out_55 = uint8 ([ %! 12 18 18 18 12; %! 18 32 32 32 18; %! 18 32 35 32 18; %! 18 32 32 32 18; %! 12 18 18 18 12]); %! im2_n_55 = 0.0160; %! im2_out_03 = uint8 ([ %! 8 16 23 16 8 ; %! 16 33 49 33 16; %! 23 49 72 49 23; %! 16 33 49 33 16; %! 8 16 23 16 8 ]); %! im2_n_03 = 0.3000; %! [out, n] = wiener2 (im2); %! assert (out, im2_out) %! assert (n, im2_n, 1e-4) %! [out, n] = wiener2 (im2, [5, 5]); %! assert (out, im2_out_55) %! assert (n, im2_n_55, 1e-4) %! [out, n] = wiener2 (im2, 0.3); %! assert (out, im2_out_03) %! assert (n, im2_n_03, 1e-4) %!test %! im3 = zeros (5, 5); %! im3(2:4, 2:4) = 70; %! im3(3, 3) = 20; %! im3 = uint8 (im3); %! im3_out = uint8 ([ %! 8 16 19 16 8 ; %! 16 32 47 32 16; %! 19 47 64 47 19; %! 16 32 47 32 16; %! 8 16 19 16 8 ]); %! im3_n = 0.0134; %! [out, n] = wiener2 (im3); %! assert (out, im3_out) %! assert (n, im3_n, 1e-4) %!test %! im4 = 50 .* ones (5, 5); %! im4(:, 1) = 200; %! im4(:, 2) = 100; %! im4 = uint8 (im4); %! im4_out = uint8 ([ %! 148 90 44 33 22; %! 161 111 67 50 33; %! 161 111 67 50 33; %! 161 111 67 50 33; %! 148 90 44 33 22]); %! im4_n = 0.0398; %! [out, n] = wiener2 (im4); %! assert (out, im4_out) %! assert (n, im4_n, 1e-4) %!test %! im5 = zeros (100, 100); %! im5(30, 30) = 1; %! im5_out_center = [ %! 0 0 0 0 0 0 0; %! 0 0 0 0 0 0 0; %! 0 0 0.0001 0.0001 0.0001 0 0; %! 0 0 0.0001 0.9992 0.0001 0 0; %! 0 0 0.0001 0.0001 0.0001 0 0; %! 0 0 0 0 0 0 0; %! 0 0 0 0 0 0 0]; %! im5_n = 8.8889e-5; %! [out, n] = wiener2 (im5); %! out_center = out(27:33, 27:33); %! assert (out_center, im5_out_center, 1e-4) %! assert (n, im5_n, 1e-4) %!test %! im = zeros (100, 10, 10); %! im(5, 5, 5) = 1; %! %! [out, n] = wiener2 (im, [3 3]); %! expected_out = im; %! expected_out(4:6,4:6,5) = [ %! 0.0001 0.0001 0.0001 %! 0.0001 0.9992 0.0001 %! 0.0001 0.0001 0.0001]; %! assert (out, expected_out, eps) %! %! [out, n] = wiener2 (im, [3 3 3]); %! expected_out = im; %! expected_out(4:6,4:6,4:6) = 0.0001; %! expected_out(5,5,5) = 0.9974; %! assert (out, expected_out, eps) %! %! ## Default in ND, use nhood 3 even for singleton dimensions. %! assert (wiener2 (im), wiener2 (im, [3 3 3])) %! %! im = reshape (im, [100 10 1 10]); %! expected_out = im; %! expected_out(4:6,4:6,1,4:6) = 0.0001/3; %! expected_out(5,5,1,5) = 1-(80*(0.0001/3)); %! assert (wiener2 (im), wiener2 (im, [3 3 3 3])) %! assert (wiener2 (im), expected_out, eps) %! %! expected_out = im; %! expected_out(4:6,4:6,1,4:6) = 0.0001; %! expected_out(5,5,1,5) = 0.9974; %! assert (wiener2 (im, [3 3 1 3]), expected_out, eps) %!test %! expected = [0.2222 0.3926 0.3926 0.3926 0.2222]; %! assert (wiener2 (ones (1, 5)), expected, .0001) %!assert (wiener2 ([]), []) %!assert (wiener2 (logical ([0 0; 1 1])), true (2, 2)) %!demo %! I = phantom (); %! J = imnoise (I, "gaussian", 0, 0.02); %! figure, imshow (J); %! title ("Image with added Gaussian noise"); %! K = wiener2 (J, [5 5]); %! figure, imshow (K); %! title ("Image with noise reduced by wiener2 filtering"); image-2.16.1/inst/PaxHeaders.61586/analyze75read.m0000644000000000000000000000006215005110255016233 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/analyze75read.m0000644000175000017500000000502315005110255017631 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012-2013 Adam H Aitkenhead ## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{image} =} analyze75read (@var{filename}) ## @deftypefnx {Function File} {@var{image} =} analyze75read (@var{header}) ## Read image data of an Analyze 7.5 file. ## ## @var{filename} must be the path for an Analyze 7.5 file or the path for a ## directory with a single .hdr file can be specified. Alternatively, the file ## @var{header} can be specified as returned by @code{analyze75info}. ## ## @seealso{analyze75info, analyze75write} ## @end deftypefn function data = analyze75read (filename) if (nargin != 1) print_usage; elseif (isstruct (filename)) if (!isfield (filename, "Filename")) error ("analyze75read: structure given does not have a `Filename' field."); else header = filename; filename = filename.Filename; endif elseif (!ischar (filename)) error ("analyze75read: `filename' must be either a string or a structure."); endif fileprefix = analyze75filename (filename); if (!exist ("header", "var")) header = analyze75info ([fileprefix, ".hdr"]); endif ## Finally start reading the actual file [fidI, err] = fopen ([fileprefix, ".img"]); if (fidI < 0) error ("analyze75read: unable to fopen `%s': %s", [fileprefix, ".img"], err); endif if (strcmp (header.ImgDataType, "DT_SIGNED_SHORT")); datatype = "int16"; elseif (strcmp (header.ImgDataType, "DT_SIGNED_INT")); datatype = "int32"; elseif (strcmp (header.ImgDataType, "DT_FLOAT")); datatype = "single"; elseif (strcmp (header.ImgDataType, "DT_DOUBLE")); datatype = "double"; endif data = zeros (header.Dimensions, datatype); data(:) = fread (fidI, datatype, header.ByteOrder); fclose (fidI); ## Rearrange the data data = permute (data, [2,1,3]); endfunction image-2.16.1/inst/PaxHeaders.61586/imopen.m0000644000000000000000000000006215005110255015047 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imopen.m0000644000175000017500000001120115005110255016440 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imopen (@var{img}, @var{SE}) ## Perform morphological opening. ## ## The matrix @var{img} must be numeric while @var{SE} can be a: ## @itemize @bullet ## @item ## strel object; ## @item ## array of strel objects as returned by `@@strel/getsequence'; ## @item ## matrix of 0's and 1's. ## @end itemize ## ## The opening corresponds to an erosion followed by a dilation of @var{img}, ## using the same @var{SE}, i.e., it is equivalent to: ## @example ## imdilate (imerode (img, se), se); ## @end example ## ## @seealso{imdilate, imerode, imclose} ## @end deftypefn function opened = imopen (img, se) if (nargin != 2) print_usage (); elseif (! isimage (img)) error("imopen: IMG must be a numeric matrix"); endif se = prepare_strel ("imopen", se); ## Perform filtering opened = imdilate (imerode (img, se), se); endfunction %!shared in, out %! in = [ 0 0 0 1 1 1 0 0 1 1 %! 0 1 0 1 1 1 0 0 0 1 %! 1 1 1 1 1 0 0 0 0 0 %! 0 1 1 1 1 0 0 0 0 0 %! 0 0 0 1 0 0 0 0 1 0 %! 0 0 0 0 0 0 0 1 1 1 %! 0 0 0 0 1 0 1 0 1 0 %! 0 0 0 1 1 1 1 1 0 0 %! 0 0 0 0 1 1 1 0 0 0 %! 0 0 0 1 1 1 0 0 0 0]; %! %! out = [ 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0]; %!assert (imopen (logical (in), ones (3)), logical (out)); %! %! out = [80 80 1 8 15 51 51 51 51 40 %! 80 80 7 8 15 54 55 55 55 40 %! 4 7 7 8 15 54 55 55 55 40 %! 17 17 17 7 3 54 55 55 55 28 %! 17 17 17 2 9 54 54 54 52 33 %! 17 17 17 29 29 29 29 26 33 33 %! 5 5 13 29 29 29 30 32 39 39 %! 6 6 13 29 29 29 30 32 39 39 %! 10 12 77 77 77 35 35 35 39 39 %! 10 12 77 77 77 35 35 35 27 27]; %!assert (imopen (magic (10), ones (3)), out); %!assert (imopen (uint8 (magic (10)), strel ("square", 3)), uint8 (out)); %! %! ## using a se that will be decomposed in 2 pieces %! out =[ 1 1 1 8 15 40 40 40 40 40 %! 4 4 4 8 15 40 40 40 40 40 %! 4 4 4 8 15 40 40 40 40 40 %! 5 5 5 3 3 28 28 28 28 28 %! 5 5 5 2 9 28 28 28 28 28 %! 5 5 13 26 26 26 26 26 26 26 %! 5 5 13 29 29 29 29 29 27 27 %! 6 6 13 29 29 29 29 29 27 27 %! 6 6 13 29 29 29 29 29 27 27 %! 6 6 13 29 29 29 29 29 27 27]; %!assert (imopen (magic (10), ones(5)), out); %! %! ## using a weird non-symmetric and even-size se %! out =[ 7 7 1 8 15 55 51 51 41 40 %! 7 7 7 8 16 55 55 55 51 41 %! 4 9 7 7 16 54 55 54 55 47 %! 25 25 9 9 3 52 54 52 54 28 %! 25 24 25 2 9 33 52 34 52 34 %! 17 24 29 31 29 30 33 26 33 34 %! 17 5 29 31 31 31 30 32 39 33 %! 10 6 13 35 35 29 31 32 45 39 %! 10 12 77 36 36 35 35 31 45 45 %! 11 12 77 77 77 36 36 35 27 45]; %!assert (imopen (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out); image-2.16.1/inst/PaxHeaders.61586/colorangle.m0000644000000000000000000000006215005110255015705 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/colorangle.m0000644000175000017500000001333215005110255017305 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Ricardo Fantin da Costa ## ## This program is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {} {} colorangle (@var{rgb1}, @var{rgb2}) ## Compute angle between RGB colors in degrees. ## ## Colors are represented as 3 element row vectors, for their RGB ## values. The angle between @var{rgb1} and @var{rgb2} is defined as: ## ## @tex ## $$ ## cos (ANGLE) = \frac{RGB1 \cdot RGB2}{|RGB1| |RGB2|} ## $$ ## @end tex ## @ifnottex ## @example ## @group ## dot (@var{rgb1}, @var{rgb2}) ## cos (@var{angle}) = --------------------------- ## norm (@var{rgb1}) * norm (@var{rgb2}) ## @end group ## @end example ## @end ifnottex ## ## This is a binary operator so standard automatic broadcasting rules ## apply. ## ## @end deftypefn ## Author: Ricardo Fantin da Costa ## Created: 2018-03-26 function angles = colorangle (rgb1, rgb2) if (nargin != 2) print_usage (); endif rgb1 = check_rgb (rgb1, "RGB1"); rgb2 = check_rgb (rgb2, "RGB2"); if (rows (rgb1) != rows (rgb2) && (rows (rgb1) != 1 && rows (rgb2) != 1)) error ("Octave:invalid-input-arg", "colorangle: RGB1 and RGB2 must have one or same number of colors"); endif norm1 = sqrt (sumsq (rgb1, 2)); norm2 = sqrt (sumsq (rgb2, 2)); ## Would be nice if dot() had automatic broadcasting, see ## https://savannah.gnu.org/bugs/index.php?55077. In the mean time, ## we do this. if (rows (rgb1) == rows (rgb2)) dot_products = dot (rgb1, rgb2, 2); elseif (rows (rgb1) > rows (rgb2)) dot_products = rgb1 * rgb2.'; else dot_products = rgb2 * rgb1.'; endif warning ("off", "Octave:divide-by-zero", "local"); angles = rad2deg (acos (dot_products ./ (norm1 .* norm2))); ## For Matlab compatibility, return 0 instead of NaN for this cases. angles(norm1 == 0 & norm2 == 0) = 0; ## Complex values may come out of acos. This will happen if acos ## input is larger than 1, which may happen due to floating point ## error, as in `colorangle ([1 1 1], [1 1 1])` angles = real (angles); endfunction function rgb = check_rgb (rgb, name) validateattributes (rgb, {"numeric"}, {"real"}, "colorangle", name); if (numel (rgb) == 3) ## For Matlab compatibility, if this is a single rgb color, accept ## a vector in any dimension. rgb = rgb(:).'; elseif (columns (rgb) != 3) error ("Octave:invalid-input-arg", "colorangle: %s must be a 3 element or Nx3 array", name); endif endfunction %!error id=Octave:invalid-fun-call colorangle () %!error id=Octave:invalid-fun-call colorangle (1, 2, 3) %!error colorangle (2, 3) %!error colorangle ([1, 2], [3, 4]) %!error id=Octave:expected-real colorangle ([1, 2, 3j], [4, 5, 6]) %!error id=Octave:expected-real colorangle ([1, 2, 3], [4j, 5, 6]) %!error id=Octave:invalid-type colorangle ("abc", "def") %!test %! assert (colorangle ([0 0 0], [0 1 0]), NaN) %! assert (colorangle ([0 0 0], [0 1 1]), NaN) %! assert (colorangle ([0 1 0], [0 0 0]), NaN) %! assert (colorangle ([1 1 0], [0 0 0]), NaN) %! assert (colorangle ([1 1 1], [1 1 1]), 0) ## This is for Matlab compatibility. If one colour is [0 0 0], then ## it's at the origin and there's no angle to the other colour. Both ## Octave and Matlab return NaN in this case. The thing is what to do ## when both colours are [0 0 0]. There's no angle to measure, hence ## NaN, but they're at the same position hence zero. %!assert (colorangle ([0 0 0], [0 0 0]), 0) %!assert (colorangle ([1 0 0], [-1 0 0]), 180) %!assert (colorangle ([0 0 1], [1 0 0]), 90) %!assert (colorangle ([0; 0; 1], [1 0 0]), 90) %!assert (colorangle ([0, 0, 1], [1; 0; 0]), 90) %!assert (colorangle ([0.5 0.61237 -0.61237], [0.86603 0.35355 -0.35355]), 30.000270917, 1e-4) %!assert (colorangle ([0.1582055390, 0.2722362096, 0.1620813305], [0.0717 0.1472 0.0975]), 5.09209927, 1e-6) %!assert (colorangle ([0.0659838500, 0.1261619536, 0.0690643667], [0.0717 0.1472 0.0975]), 5.10358588, 1e-6) %!assert (colorangle ([0.436871170, 0.7794672250, 0.4489702582], [0.0717 0.1472 0.0975]), 5.01339769, 1e-6) %!test %! a = [1 0 0]; %! b = [1 1 0]; %! expected = colorangle (a, b); %! assert (colorangle (a.', b.'), expected) %! assert (colorangle (a, b.'), expected) %! assert (colorangle (a.', b), expected) %! assert (colorangle (vec (a, 3), b.'), expected) %!assert (colorangle ([1 0 0; 0 1 1], [1 1 1; 2 3 4]), %! [colorangle([1 0 0], [1 1 1]); colorangle([0 1 1], [2 3 4])]) %!test %! a = [1 0 0; 0.5 1 0; 0 1 1; 1 1 1]; %! b = [0 1 0]; %! expected = zeros (4, 1); %! for i = 1:4 %! expected(i) = colorangle (a(i,:), b); %! endfor %! assert (colorangle (a, b), expected) %! assert (colorangle (b, a), expected) %!xtest %! a = [1 2 3]; %! b = [2 3 4]; %! c = [5 6 7]; %! d = [3 1 1]; %! %! ac = colorangle (c, a); %! bc = colorangle (b, c); %! ad = colorangle (a, d); %! bd = colorangle (b, d); %! %! assert (colorangle (a, cat (3, c, d)), %! cat (3, [ac ad])) %! %! assert (colorangle (cat (3, a, b), cat (3, c, d)), %! cat (3, [ac cd])) %! %! assert (colorangle (cat (1, a, b), cat (3, c, d)), %! reshape ([ac bc ad bd], [2 2])) image-2.16.1/inst/PaxHeaders.61586/tforminv.m0000644000000000000000000000006215005110255015424 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/tforminv.m0000644000175000017500000000433515005110255017027 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Pantxo Diribarne ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{UV}] =} tforminv (@var{T}, @var{XY}) ## @deftypefnx {Function File} {[@var{U}, @var{V}] =} tforminv (@var{T}, @var{X}, @var{Y}) ## ## ## Given a transform structure @var{T}, transform coordinates @var{XY} ## in the output space into coordinates @var{UV} in the input space. ## ## Input and output coordinates may be given/retrieved either as a ## n-by-2 array, or as two n-by-1 vectors. ## ## The function makes use of the "inverse_fcn" field of the transform ## structure @var{T}, which should thus be defined. ## @seealso{maketform, cp2tform, tformfwd} ## @end deftypefn ## Author: Pantxo Diribarne function varargout = tforminv (T, varargin) if (nargin > 3 || nargin < 2) print_usage (); elseif (! istform (T)) error ("tforminv: expect a transform structure as first argument") elseif (nargin == 2) XX = varargin{1}; if (columns (XX) != 2) error ("tforminv: expect n-by-2 array as second argument") endif else if (!isvector (varargin{1}) || !isvector (varargin{2})) error ("tforminv: expect vectors as coordinates") elseif (!all (size (varargin{1}) == size (varargin{2}))) error ("tforminv: expect two vectors the same size") elseif (columns (varargin{1}) != 1) error ("tforminv: expect column vectors") endif XX = [varargin{1} varargin{2}]; endif UU = T.inverse_fcn(XX, T); if (nargin == 3) varargout{1} = UU(:,1); varargout{2} = UU(:,2); else varargout{1} = UU; endif endfunction image-2.16.1/inst/PaxHeaders.61586/imregionalmin.m0000644000000000000000000000006215005110255016412 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imregionalmin.m0000644000175000017500000000765315005110255020023 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} imregionalmin (@var{img}) ## @deftypefnx {Function File} {} imregionalmin (@var{img}, @var{conn}) ## Compute regional minima. ## ## Returns a logical matrix, same size as the input @var{img}, with the ## regional minima. ## ## The optional argument @var{conn}, defines the connectivity. It can ## be a scalar value or a boolean matrix (see @code{conndef} for details). ## Defaults to @code{conndef (ndims (@var{img}), "maximal")} ## ## Regional minima should not be mistaken with local minima. Local minima ## are pixels whose value is less or equal to all of its neighbors. ## A regional minima is the connected component of pixels whose values are ## all less than the neighborhood of the minima (the connected component, ## not its individual pixels). ## All pixels belonging to a regional minima are local minima, but the ## inverse is not true. ## ## @seealso{imreconstruct, imregionalmax} ## @end deftypefn function bw = imregionalmin (img, conn) if (nargin < 1 || nargin > 2) print_usage (); endif if (nargin < 2) conn = conndef (ndims (img), "maximal"); else conn = conndef (conn); endif if (isfloat (img)) img = - img; else img = imcomplement (img); endif bw = imregionalmax (img, conn); endfunction %!test %! a = [ %! 7 3 9 3 10 3 %! 4 2 3 10 1 3 %! 1 4 6 9 4 10 %! 8 7 9 3 4 8 %! 5 9 3 3 8 9 %! 3 6 9 4 1 10]; %! %! a4 = logical ([ %! 0 0 0 1 0 0 %! 0 1 0 0 1 0 %! 1 0 0 0 0 0 %! 0 0 0 1 0 0 %! 0 0 1 1 0 0 %! 1 0 0 0 1 0]); %! assert (imregionalmin (a, 4), a4) %! assert (imregionalmin (uint8 (a), 4), a4) %! assert (imregionalmin (int8 (a), 4), a4) %! %! a8 = logical ([ %! 0 0 0 0 0 0 %! 0 0 0 0 1 0 %! 1 0 0 0 0 0 %! 0 0 0 0 0 0 %! 0 0 0 0 0 0 %! 1 0 0 0 1 0]); %! assert (imregionalmin (a), a8) %! assert (imregionalmin (a, 8), a8) %! assert (imregionalmin (uint8 (a), 8), a8) %! assert (imregionalmin (int8 (a), 8), a8) %!test %! a = [ %! 4 8 5 -1 8 7 %! -1 4 0 7 1 1 %! 6 1 2 6 7 0 %! 6 1 5 -2 5 9 %! 1 4 -1 0 0 2 %! 4 6 1 0 7 1]; %! %! a4 = logical ([ %! 0 0 0 1 0 0 %! 1 0 1 0 0 0 %! 0 1 0 0 0 1 %! 0 1 0 1 0 0 %! 1 0 1 0 0 0 %! 0 0 0 0 0 1]); %! assert (imregionalmin (a, 4), a4) %! assert (imregionalmin (int8 (a), 4), a4) %! %! a8 = logical ([ %! 0 0 0 1 0 0 %! 1 0 0 0 0 0 %! 0 0 0 0 0 1 %! 0 0 0 1 0 0 %! 0 0 0 0 0 0 %! 0 0 0 0 0 0]); %! assert (imregionalmin (a), a8) %! assert (imregionalmin (a, 8), a8) %! assert (imregionalmin (int8 (a), 8), a8) %!test %! ## test float input images %! im0 = peaks (); %! im1 = im0 ./ 100; %! max_pos_expected = [1; 49; 664; 1286; 1302; 2401]; %! max0 = imregionalmin (im0); %! max0_pos = find (max0); %! max1 = imregionalmin (im1); %! assert (max1, max0) %! assert (max0_pos, max_pos_expected) image-2.16.1/inst/PaxHeaders.61586/bwhitmiss.m0000644000000000000000000000006215005110255015571 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwhitmiss.m0000644000175000017500000000642115005110255017172 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} bwhitmiss (@var{bw}, @var{se1}, @var{se2}) ## @deftypefnx {Function File} {} bwhitmiss (@var{bw}, @var{interval}) ## Perform binary hit-or-miss transform. ## ## This transform returns the set of positions, where the structuring ## element @var{se1} fits in the foregrond of @var{bw}, while the ## structuring element @var{se2} misses it completely. It is equivalent ## to: ## ## @example ## imerode (@var{bw}, @var{se1}) & imerode (! @var{bw}, @var{se2}) ## @end example ## ## For example, the following will remove every pixel with adjacent ## horizontal foreground pixels: ## ## @example ## >> bw = [ 0 1 0 1 1 0 ## 0 1 0 1 1 0 ## 0 1 0 1 1 0]; ## ## >> bwhitmiss (bw, [1; 0; 1], [1 0 1]) ## @result{} ans = ## ## 0 1 0 0 0 0 ## 0 1 0 0 0 0 ## 0 1 0 0 0 0 ## @end example ## ## Note that while @var{se1} and @var{se2} must have disjoint neighbourhoods ## for this transform to be meaningful, no error or warning is throw about ## it. ## ## Alternatively a single array @var{interval} can be defined, of values ## from @code{[1 0 -1]}. In this case, the two structuring elements are ## extracted as: ## ## @example ## @var{se1} = (@var{interval} == 1) ## @var{se2} = (@var{interval} == -1) ## @end example ## ## @seealso{bwmorph} ## @end deftypefn function bw = bwhitmiss(im, varargin) ## Checkinput if (nargin != 2 && nargin != 3) print_usage(); endif if (! isreal(im)) error("bwhitmiss: first input argument must be a real matrix"); endif ## Get structuring elements if (nargin == 2) # bwhitmiss (im, interval) interval = varargin{1}; if (!isreal(interval)) error("bwhitmiss: second input argument must be a real matrix"); endif if (!all( (interval(:) == 1) | (interval(:) == 0) | (interval(:) == -1) )) error("bwhitmiss: second input argument can only contain the values -1, 0, and 1"); endif se1 = (interval == 1); se2 = (interval == -1); else # bwhitmiss (im, se1, se2) se1 = varargin{1}; se2 = varargin{2}; if (!all((se1(:) == 1) | (se1(:) == 0)) || !all((se2(:) == 1) | (se2(:) == 0))) error("bwhitmiss: structuring elements can only contain zeros and ones."); endif endif ## Perform filtering bw = imerode (im, se1) & imerode (! im, se2); endfunction %!test %! bw1 = repmat ([0 1 0 1 1], [3 1]); %! bw2 = repmat ([0 1 0 0 0], [3 1]); %! assert (bwhitmiss (bw1, [1; 0; 1], [1 0 1]), logical (bw2)) %! assert (bwhitmiss (bw1, [0 1 0; -1 0 -1; 0 1 0]), logical (bw2)) image-2.16.1/inst/PaxHeaders.61586/isbw.m0000644000000000000000000000006215005110255014524 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/isbw.m0000644000175000017500000000674315005110255016134 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## Copyright (C) 2011, 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} isbw (@var{img}) ## @deftypefnx {Function File} {} isbw (@var{img}, @var{logic}) ## Return true if @var{img} is a black and white image. ## ## A variable can be considered a black and white image if it is a ## non-sparse, real array of size @nospell{MxNx1xK}, and, ## depending on the value of @var{logic}: ## ## @table @asis ## @item @qcode{"logical"} (default) ## @var{img} must be of class logical. ## ## @item @qcode{"non-logical"} ## all values in @var{img} are either 1 or 0. ## @end table ## ## @emph{Note}: despite their suggestive names, the functions isbw, ## isgray, isind, and isrgb, are ambiguous since it is not always possible ## to distinguish between those image types. For example: an uint8 matrix ## can be both a grayscale and indexed image; a grayscale image may have ## values outside the range [0 1]. They are good to dismiss input as an ## invalid image type, but not for identification. ## ## @seealso{im2bw, isgray, isind, islogical, isrgb} ## @end deftypefn function bool = isbw (BW, logic = "logical") if (nargin () < 1 || nargin () > 2) print_usage (); endif bool = false; if (isimage (BW) && ndims (BW) < 5 && size (BW, 3) == 1) if (strcmpi (logic, "logical")) ## this is the matlab compatible way (before they removed the function) bool = islogical (BW); elseif (strcmpi (logic, "non-logical")) bool = islogical (BW) || ispart (@is_bw_nonlogical, BW); else error ("isbw: LOGIC must be the string 'logical' or 'non-logical'") endif endif endfunction function bool = is_bw_nonlogical (BW) bool = ! any ((BW(:) != 1) & (BW(:) != 0)); endfunction %!shared img %! img = round (rand (10)); %!assert (isbw (img, "non-logical"), true); %!assert (isbw (img, "logical"), false); %!assert (isbw (logical (img), "logical"), true); %!assert (isbw (logical (img), "non-logical"), true); ## change when the different value is near the start and then in middle, ## because of the way we test part of the image before the rest %!test %! img(1, 1) = 2; %! assert (isbw (img, "non-logical"), false); %!test %! a( 1, 1) = 1; %! a(50, 50) = 2; %! assert (isbw (a, "non-logical"), false); %!assert (isbw (rand (5, 5, 1, 4) > 0.5), true) %!assert (isbw (rand (5, 5, 3, 4) > 0.5), false) %!assert (isbw (rand (5, 5, 3) > 0.5), false) %!assert (isbw (rand (5, 5, 1, 3, 4) > 0.5), false) %!assert (isbw (randi ([0 1], 5, 5, 1, 4), "non-logical"), true) %!assert (isbw (randi ([0 1], 5, 5, 3, 4), "non-logical"), false) %!assert (isbw (randi ([0 1], 5, 5, 3), "non-logical"), false) %!assert (isbw (randi ([0 1], 5, 5, 1, 3, 4), "non-logical"), false) %!assert (isbw (single ([0 0 1]), "non-logical"), true) %!assert (isbw ([0 NaN 1], "non-logical"), false) image-2.16.1/inst/PaxHeaders.61586/imbothat.m0000644000000000000000000000006215005110255015367 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imbothat.m0000644000175000017500000001574515005110255017001 0ustar00avinoamavinoam00000000000000## Copyright (C) 2005 Carvalho-Mariel ## Copyright (C) 2010-2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imbothat (@var{img}, @var{SE}) ## Perform morphological bottom hat filtering. ## ## The matrix @var{img} must be numeric while @var{SE} can be a: ## @itemize @bullet ## @item ## strel object; ## @item ## array of strel objects as returned by `@@strel/getsequence'; ## @item ## matrix of 0's and 1's. ## @end itemize ## ## A bottom hat transform corresponds to the difference between the closing ## of @var{img} and @var{img} itself, i.e., it is equivalent to: ## @example ## imclose (img, se) - img; ## @end example ## ## A bottom-hat transform is also known as 'black', or 'closing', top-hat ## transform. ## ## @seealso{imerode, imdilate, imopen, imclose, imtophat, mmgradm} ## @end deftypefn function black = imbothat (img, se) if (nargin != 2) print_usage (); elseif (! isimage (img)) error("imbothat: IMG must be a numeric matrix"); endif se = prepare_strel ("imbothat", se); ## Perform filtering ## Note that in case that the transform is to applied to a logical image, ## subtraction must be handled in a different way (x & !y) instead of (x - y) ## or it will return a double precision matrix if (islogical (img)) black = imclose (img, se) & ! img; else black = imclose (img, se) - img; endif endfunction %!assert (imbothat (ones (3), [1 1; 0 1]), zeros (3)); %!assert (imbothat (true (3), [1 1; 0 1]), false (3)); %!shared in, out, se %! in = [ 0 0 0 1 1 1 0 0 1 1 %! 0 1 0 1 1 1 0 0 0 1 %! 1 1 1 1 1 0 0 0 0 0 %! 0 1 1 1 1 0 0 0 0 0 %! 0 0 0 1 0 0 0 0 1 0 %! 0 0 0 0 0 0 0 1 1 1 %! 0 0 0 0 1 0 1 0 1 0 %! 0 0 0 1 1 1 1 1 0 0 %! 0 0 0 0 1 1 1 0 0 0 %! 0 0 0 1 1 1 0 0 0 0]; %! %! out = [ 1 1 1 0 0 0 1 1 0 0 %! 1 0 1 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 1 %! 1 0 0 0 0 0 0 0 0 1 %! 0 0 0 0 1 0 0 0 0 1 %! 0 0 0 1 1 1 1 0 0 0 %! 0 0 0 1 0 1 0 1 0 1 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 1 0 0 0]; %!assert (imbothat (logical (in), ones (3)), logical (out)); %! %! out = [ 7 0 15 8 1 6 0 13 6 24 %! 0 8 9 2 0 0 16 7 0 23 %! 89 7 0 41 39 7 12 7 0 23 %! 8 1 69 40 58 1 6 2 0 43 %! 7 0 63 59 52 0 0 0 14 32 %! 62 55 6 7 0 7 0 23 16 1 %! 56 74 0 2 0 0 16 14 7 0 %! 0 73 69 0 0 19 15 8 1 0 %! 8 6 0 0 6 13 9 2 0 6 %! 7 0 0 19 0 14 7 0 23 0]; %!assert (imbothat (magic (10), ones (3)), out); %!assert (imbothat (uint8 (magic (10)), strel ("square", 3)), uint8 (out)); %! %! ## using a se that will be decomposed in 2 pieces %! out =[ 7 0 87 66 59 7 0 19 12 30 %! 0 13 81 60 58 1 19 13 6 29 %! 89 12 0 54 52 20 18 7 0 23 %! 8 6 69 53 71 14 12 2 0 43 %! 7 0 63 73 66 14 7 0 23 41 %! 76 69 14 7 0 30 23 46 39 7 %! 70 88 9 2 0 24 42 40 33 6 %! 14 87 80 0 0 43 41 34 27 0 %! 84 82 0 0 19 37 35 28 26 19 %! 89 82 0 20 13 36 29 22 45 13]; %!assert (imbothat (magic (10), ones(5)), out); %! %! ## using a weird non-symmetric and even-size se %! out =[ 0 0 15 8 1 3 0 7 0 18 %! 0 8 53 59 0 0 14 13 0 17 %! 84 0 0 40 38 6 13 6 0 23 %! 2 0 42 47 58 0 6 0 0 41 %! 0 0 62 59 52 0 0 0 16 35 %! 6 58 13 6 0 3 19 19 35 1 %! 0 18 0 0 0 0 15 13 6 0 %! 0 17 69 0 0 17 17 8 0 0 %! 8 67 0 0 0 15 9 2 0 6 %! 7 0 0 17 10 42 7 0 19 0]; %!assert (imbothat (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out); %! %! ## N dimensional and weird se %! in = reshape (magic(16), [4 8 4 2]); %! se = ones (3, 3, 3); %! se(:,:,1) = [1 0 1; 0 1 1; 0 0 0]; %! se(:,:,3) = [1 0 1; 0 1 1; 0 0 1]; %! out = zeros (size (in)); %! out(:,:,1,1) = [ %! 0 17 81 145 237 146 64 0 %! 205 128 64 0 0 37 83 147 %! 175 111 47 0 0 64 117 181 %! 0 64 128 209 173 109 45 0]; %! out(:,:,2,1) = [ %! 235 142 78 18 0 23 69 133 %! 0 35 103 163 215 128 46 0 %! 0 64 128 195 183 123 48 0 %! 153 93 43 0 14 78 146 215]; %! out(:,:,3,1) = [ %! 0 25 89 153 229 142 64 0 %! 201 128 64 0 0 41 91 155 %! 167 103 57 0 0 64 125 189 %! 0 64 146 217 165 101 37 0]; %! out(:,:,4,1) = [ %! 227 142 78 14 0 31 77 141 %! 0 43 107 171 211 128 46 0 %! 0 64 128 203 179 115 48 0 %! 149 99 35 0 18 82 146 223]; %! out(:,:,1,2) = [ %! 0 33 97 161 221 146 64 0 %! 189 125 61 0 0 53 99 163 %! 159 95 31 0 0 64 128 197 %! 0 64 128 225 157 93 29 0]; %! out(:,:,2,2) = [ %! 219 142 78 18 0 39 85 149 %! 0 51 119 179 199 128 46 0 %! 0 64 128 211 167 107 43 0 %! 137 77 27 0 14 78 146 231]; %! out(:,:,3,2) = [ %! 0 41 105 169 213 142 64 0 %! 185 121 64 0 0 57 107 171 %! 151 87 41 0 0 64 128 205 %! 0 64 146 233 149 85 21 0]; %! out(:,:,4,2) = [ %! 211 142 78 14 0 47 93 157 %! 0 59 123 187 195 128 46 0 %! 0 64 128 219 163 99 35 0 %! 133 83 19 0 18 82 146 239]; %!assert (imbothat (in, se), out); image-2.16.1/inst/PaxHeaders.61586/impyramid.m0000644000000000000000000000006215005110255015553 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/impyramid.m0000644000175000017500000002151615005110255017156 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Avinoam Kalma ## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} impyramid (@var{im}, @var{direction}) ## Compute gaussian pyramid expansion or reduction. ## ## Create image which is one level up or down in the Gaussian ## pyramid. @var{direction} must be @qcode{"reduce"} or ## @qcode{"expand"}. These operations are only done in the first ## two dimensions, so that even if @var{im} is a N dimensional ## array, only the number of rows and columns will change. ## ## The @qcode{"reduce"} stage is done by low-pass filtering and ## subsampling of 1:2 in each axis. If the size of the original ## image is [M N], the size of the reduced image is ## @qcode{[ceil((M+1)/2) ceil((N+1)/2)]}. ## ## The @qcode{"expand"} stage is done by upsampling the image ## (2:1 in each axis), and then low-pass filtering. If the size ## of the original image is [M N], the size of the expanded image ## is @code{[2M-1 2N-1]}. ## ## Note that image processing pyramids are upside down, so ## @qcode{"reduce"} is going one level @emph{down} in the pyramid, ## while @qcode{"expand"} is going one level @emph{up} in the pyramid. ## ## @example ## @group ## impyramid (im, "reduce"); # return reduced image (one level down) ## impyramid (im, "expand"); # return expanded image (one level up) ## @end group ## @end example ## ## The low-pass filter is defined according to Burt & Adelson [1] ## @code{W(i,j) = w(i)w(j)} where ## @code{w = [0.25-alpha/2 0.25 alpha 0.25 0.25-alpha/2]} with ## @code{alpha = 0.375} ## ## [1] Peter J. Burt and Edward H. Adelson (1983). The Laplacian Pyramid ## as a Compact Image Code. IEEE Transactions on Communications, ## vol. COM-31(4), 532-540. ## ## @seealso{imresize, imfilter} ## @end deftypefn ## Author: Avinoam Kalma function imp = impyramid (im, direction) if (nargin != 2) print_usage (); elseif (! isnumeric (im) && ! isbool (im)) error ("impyramid: IM must be numeric or logical") endif ## low pass filters to be used alpha = 0.375; filt_horz = [(0.25-alpha/2) 0.25 alpha 0.25 (0.25-alpha/2)]; filt_vert = filt_horz.'; nd = ndims (im); sz = size (im); cl = class (im); switch (tolower (direction)) case "reduce" ## vertical low pass filtering im = padarray (im, floor (size (filt_vert) /2), "replicate"); im = convn (im, filt_vert, "valid"); ## horizontal low pass filtering im = padarray (im, floor (size (filt_horz) /2), "replicate"); im = convn (im, filt_horz, "valid"); im = cast (im, cl); ## subsampling idx = repmat ({":"}, 1, nd); idx([1 2]) = {1:2:sz(1), 1:2:sz(2)}; imp = im(idx{:}); case "expand" ## Create image, twice the size (rows and columns only), ## with the original image on the odd pixels. imp_sz = sz .* postpad ([2 2], nd, 1); imp_sz([1 2]) -= 1; imp = zeros (imp_sz, cl); idx = repmat ({":"}, 1, nd); idx([1 2]) = {1:2:imp_sz(1), 1:2:imp_sz(2)}; imp(idx{:}) = im; ## horizontal low pass filtering imp = padarray (imp, floor (size (filt_horz) /2)); imp = convn (imp, filt_horz, "valid"); imp *= 2; ## vertical low pass filtering imp = padarray (imp, floor (size (filt_vert) /2)); imp = convn (imp, filt_vert, "valid"); imp *= 2; imp = cast (imp, cl); otherwise error ("impyramid: DIRECTION must be 'reduce' or 'expand'") endswitch endfunction ## Note that there are small differences, 1 and 2 gray levels, between ## the results here (the ones we get in Octave), and the ones we should ## have for Matlab compatibility. This is specially true for elements ## in the border, and worse when expanding. %!xtest %! ## bug #51979 (results are not matlab compatible) %! in = [116 227 153 69 146 194 59 130 139 106 %! 2 47 137 249 90 75 16 24 158 44 %! 155 68 46 84 166 156 69 204 32 152 %! 71 221 137 230 210 153 192 115 30 118 %! 107 143 108 52 51 73 101 21 175 90 %! 54 158 143 77 26 168 113 229 165 225 %! 9 47 133 135 130 207 236 43 19 73]; %! %! reduced = [ %! 114 139 131 103 111 %! 97 122 141 111 100 %! 103 123 112 123 122 %! 47 107 134 153 94]; %! %! expanded = [ %! 115 154 185 178 150 122 105 116 138 159 158 117 78 86 112 129 133 120 103 %! 69 98 128 141 146 152 152 139 125 127 121 87 55 58 81 113 131 112 84 %! 40 54 74 100 131 167 184 157 119 104 92 64 41 44 66 100 121 103 74 %! 76 69 65 75 97 130 153 148 131 122 108 80 61 79 103 105 98 97 98 %! 120 105 88 77 78 96 121 143 155 154 140 112 98 124 143 109 74 91 123 %! 117 129 134 119 107 125 153 173 180 172 156 143 138 146 140 96 60 83 122 %! 99 139 170 157 139 156 181 188 180 164 151 154 156 140 112 81 65 84 110 %! 101 136 163 153 133 132 138 136 130 122 120 130 133 108 82 86 99 104 104 %! 103 126 143 136 116 97 81 73 73 82 94 105 105 87 78 108 138 133 116 %! 90 116 139 139 122 96 69 52 53 80 109 114 111 116 128 148 163 164 160 %! 66 99 131 140 131 109 83 62 62 102 142 144 138 154 169 164 157 169 184 %! 41 68 99 121 130 122 107 92 95 133 173 182 172 156 135 114 105 121 142 %! 21 38 64 98 124 131 127 123 129 160 194 212 199 144 82 52 48 65 85]; %! %! assert (impyramid (uint8 (in), "reduce"), uint8 (reduced)) %! assert (impyramid (uint8 (in), "expand"), uint8 (expanded)) ## Test that that reduction and expansion are done in the ## first 2 dimensions only. %!test %! in = randi ([0 255], [40 39 3 5], "uint8"); %! red = impyramid (in, "reduce"); %! for p = 1:3 %! for n = 1:5 %! assert (red(:,:,p,n), impyramid (in(:,:,p,n), "reduce")) %! endfor %! endfor %! %! exp = impyramid (in, "expand"); %! for p = 1:3 %! for n = 1:5 %! assert (exp(:,:,p,n), impyramid (in(:,:,p,n), "expand")) %! endfor %! endfor %!xtest %! ## bug #51979 (results are not matlab compatible) %! in = repmat (uint8 (255), [10 10]); %! assert (impyramid (in, "reduce"), repmat (uint8 (255), [5 5])) %! assert (impyramid (in, "expand"), repmat (uint8 (255), [19 19])) %!xtest %! ## bug #51979 (results are not matlab compatible) %! in = logical ([ %! 1 0 1 1 0 0 1 1 0 0 %! 1 1 0 0 0 1 0 0 1 0 %! 0 1 1 0 1 1 1 1 1 1 %! 1 0 1 0 1 0 1 0 1 1 %! 1 1 1 0 0 0 1 1 1 1 %! 0 0 1 1 0 0 1 0 0 0 %! 0 0 1 1 0 1 1 0 1 1 %! 1 1 0 0 1 0 0 0 1 0 %! 1 1 1 1 1 1 0 1 0 0 %! 1 1 0 0 1 0 0 0 1 0]); %! %! reduced = logical ([ %! 1 1 0 1 0 %! 1 1 0 1 1 %! 1 1 0 1 1 %! 0 1 0 0 0 %! 1 1 1 0 0]); %! %! expanded = logical ([ %! 1 1 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 %! 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 %! 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 1 1 0 0 %! 1 1 1 1 0 0 0 0 0 1 1 1 1 0 1 1 1 1 1 %! 0 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 %! 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 %! 1 1 0 1 1 0 0 0 1 0 0 1 1 1 0 1 1 1 1 %! 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 %! 0 0 1 1 1 1 0 0 0 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 %! 0 0 0 0 1 1 1 0 0 0 0 1 1 0 0 0 0 0 0 %! 0 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 %! 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 %! 1 1 1 1 0 0 0 1 1 1 0 0 0 0 0 0 1 0 0 %! 1 1 1 1 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0 %! 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 %! 1 1 1 1 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0 %! 1 1 1 1 0 0 0 1 1 1 0 0 0 0 0 0 1 0 0]); %! %! assert (impyramid (in, "reduce"), reduced) %! assert (impyramid (in, "expand"), expanded) image-2.16.1/inst/PaxHeaders.61586/imadjust.m0000644000000000000000000000006215005110255015400 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imadjust.m0000644000175000017500000004511515005110255017004 0ustar00avinoamavinoam00000000000000## Copyright (C) 1999,2000 Kai Habel ## Copyright (C) 2004 Josep Monés i Teixidor ## Copyright (C) 2015 Carnë Draug ## Copyright (C) 2015 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imadjust (@var{I}) ## @deftypefnx {Function File} {} imadjust (@var{I}, [@var{low_in}; @var{high_in}]) ## @deftypefnx {Function File} {} imadjust (@var{I}, [@var{low_in}; @var{high_in}],[@var{low_out}; @var{high_out}]) ## @deftypefnx {Function File} {} imadjust (@dots{}, @var{gamma}) ## @deftypefnx {Function File} {} imadjust (@var{cmap}, @dots{}) ## @deftypefnx {Function File} {} imadjust (@var{RGB}, @dots{}) ## Adjust image or colormap intensity (values). ## ## Returns an image of equal dimensions to @var{I}, @var{cmap}, or ## @var{RGB}, with its intensity values adjusted, usually for the ## purpose of increasing the image contrast. ## ## The values are rescaled according to the input and output limits, ## @var{low_in} and @var{high_in}, and @var{low_out} and @var{high_out} ## respectively. The first pair sets the lower and upper limits ## on the input image, values above and below them being clipped. ## The second pair sets the lower and upper limits for the output ## image, the interval to which the image will be scaled after ## clipping the input limits. ## ## For example: ## ## @example ## imadjust (img, [0.2; 0.9], [0; 1]) ## @end example ## ## will clip all values in @var{img} outside the range [0.2 0.9], ## and then rescale them linearly into the range [0 1]. ## ## The input and output limits must be defined as arrays of 2 rows ## with values in the [0 1] range. Each 2 rows column corresponds ## to a single plane in the input image (or each column of a ## colormap), thus supporting images with any number of dimensions. ## If the limits have only 2 elements, the same limits are on all planes. ## This format is matched to @code{stretchlim} which is designed ## to create the input limits for @code{imadjust}. ## ## By default, the limits are adjusted to maximize the contrast, using ## the whole range of values in the image class; and cause a 2% ## saturation (1% on the lower and upper end of the image). It is ## equivalent to: ## ## @example ## imadjust (@var{I}, stretchlim (@var{I}, 0.01), [0; 1]) ## @end example ## ## A common usage is to maximize the display range without saturation: ## ## @example ## imadjust (img, stretchlim (img, 0)) # adjustment performed per plane ## imadjust (img, stretchlim (img(:), 0)) # equal adjustment to all planes ## @end example ## ## For sake of @sc{Matab} compatibility, an empty array in any of ## the limits is interpreted as @code{[0; 1]}. ## ## If @var{low_out} is higher than @var{high_out}, the output image ## will be reversed (image negative or complement). ## ## The @var{gamma} value shapes the mapping curve between the input ## and output elements. It defaults to 1, a linear mapping. Higher ## values of @var{gamma} will curve the mapping downwards and to the right, ## increasing the contrast in the brighter (higher) values of the ## input image. Lower values of @var{gamma} will curve the mapping ## upwards and to the left, increasing the contrast in the darker (lower) ## values of the input image. ## ## As with the limits, @var{gamma} can have different values for each ## plane, a 1 row column per plane, thus supporting input images with ## any number of dimensions. If only a scalar, the same value is used ## in all planes. ## ## The formula used to perform the mapping (omitting saturation) is: ## ## @example ## low_out + (high_out - low_out) .* ((I - low_in) / (high_in - low_in)) .^ gamma ## @end example ## ## @seealso{brighten, contrast, histeq, stretchlim} ## @end deftypefn function adj = imadjust (img, in, out = [0; 1], gamma = 1) if (nargin () < 1 || nargin () > 4) print_usage (); endif if (! isimage (img)) error ("imadjust: I, RGB, or CMAP must be an image or a colormap"); elseif (! isnumeric (img)) ## isimage() allows for boolean images which imadjust should not error ("imadjust: I, RGB, or CMAP must be numeric"); endif sz = size (img); if (iscolormap (img)) was_colormap = true; img = reshape (img, [sz(1) 1 sz(2)]); sz = size (img); else was_colormap = false; endif n_planes = prod (sz(3:end)); if (nargin () < 2) in = stretchlim (img, 0.01); else in = parse_limits (in, sz); endif out = parse_limits (out, sz); if (! isfloat (gamma) || any (gamma < 0)) error ("imadjust: GAMMA must be a non-negative floating point") elseif (isscalar (gamma)) gamma = repmat (gamma, [1 n_planes]); elseif (! isequal (size (gamma)(2:end), sz(3:end))) error ("imadjust: GAMMA must be a scalar or 1 row per plane") endif if (isfloat (img)) ## To make the computations in N dimensions, we make heavy use of ## broadcasting so reshape to have a single value per plane. in = reshape (in, [2 1 sz(3:end)]); out = reshape (out, [2 1 sz(3:end)]); gamma = reshape (gamma, [1 1 sz(3:end)]); adj = imadjust_direct (img, in, out, gamma); else # must be integer ## We create a LUT and use intlut instead of a simply converting the ## whole image to single or double. This is mainly for memory ## efficiency but also less computationally intensive. Do not ## forget that in scientific images, 500MB uint8 images are not ## uncommon so we don't want to convert them to double. cls = class(img); lut = linspace (0, 1, double (intmax (cls)) - double (intmin (cls)) + 1); ## If there's a single plane or the adjustment are all the same, ## we only need to create one LUT. if (n_planes == 1 || (all ((in(:,:) == in(:,1))(:)) && all ((out(:,:) == out(:,1))(:)) && all (gamma == gamma(1)))) lut = imadjust_direct (lut, in(:,1), out(:,1), gamma(1)); adj = intlut (img, imcast (lut, cls)); else ## Seems like we have different adjustments for each plane. We could ## be smarter than loop over each plane. We could check the unique ## adjustment configurations and loop over them instead. However, ## I'll guess that if the adjustments are not all equal, they are ## likely all different too so this is simpler. adj = zeros (size (img), cls); for i = 1:n_planes lut_adj = imadjust (lut, in(:,i), out(:,i), gamma(i)); adj(:,:,i) = intlut (img(:,:,i), imcast (lut_adj, cls)); endfor endif endif if (was_colormap) adj = reshape (adj, [sz(1) sz(3)]); endif endfunction function limits = parse_limits (limits, sz) if (isempty (limits)) limits = repmat ([0; 1], [1 sz(3:end)]); else if (! isfloat (limits)) error ("imadjust: IN and OUT must be numeric floating-point arrays"); elseif (min (limits(:)) < 0 || max (limits(:)) > 1) error ("imadjust: IN and OUT must be on the range [0 1]"); endif ## Only reshape back into 2 row column for a single plane. ## Require the correct format otherwise. if (numel (limits) == 2) limits = repmat (limits(:), [1 sz(3:end)]); elseif (rows (limits) != 2 || ! isequal (sz(3:end), size (limits)(2:end))) error ("imadjust: IN and OUT must be a 2 row column per plane"); endif endif endfunction ## The code that actually does imadjust without any input checking and ## reshaping. So we can call it from imadjust when we know things are good. function adj = imadjust_direct (img, in, out, gamma) max_scale = all ((out == [0; 1])(:)); max_scale_complement = all ((out == [1; 0])(:)); lo_idx = [1 repmat({":"}, 1, ndims (in))]; hi_idx = [2 repmat({":"}, 1, ndims (in))]; li = in(lo_idx{:}); hi = in(hi_idx{:}); if (max_scale) ## This is the most common case. Used to stretch all the values ## into the [0 1], which can be computed much more efficiently. adj = ((img - li) ./ (hi - li)) .^ gamma; adj(adj > 1) = 1; adj(adj < 0) = 0; elseif (max_scale_complement) adj = ((img - li) ./ (hi - li)) .^ gamma; adj = 1 - adj; adj(adj > 1) = 1; adj(adj < 0) = 0; else # this covers all cases but may be slower than needed lo = out(lo_idx{:}); ho = out(hi_idx{:}); ## Image negative is computed if ho < lo although nothing special is ## needed, since formula automatically handles it. adj = (img < li) .* lo; adj += (img >= li & img < hi) .* (lo + (ho - lo) .* ((img - li) ./ (hi - li)) .^ gamma); adj += (img >= hi) .* ho; endif endfunction %!error imadjust ("bad argument"); %!error imadjust ([1:100], "bad argument", [], 1); %!error <2 row column per plane> imadjust ([1:100], [0 1 1], [], 1); %!error <2 row column per plane> imadjust ([1:100], [], [0 1 1], 1); %!error imadjust ([1:100], [], [], [0; 1]); %!error imadjust (rand (5, 5, 3), [], [], [0 1]); %!error imadjust ([1:100], [0; 1], [], -1); %!error imadjust ([1:100], [0; 5], []); %!error imadjust ([1:100], [-2; 1], []); %!error imadjust ([1:100], [], [0; 4]); %!error imadjust ([1:100], [], [-2; 1]); %!error imadjust (rand (5) > .5); ## Test default values to 1% on each end saturated and [] as [0; 1] %!test %! im = [0.01:0.01:1]; %! assert (imadjust (im), [0 linspace(0, 1, 98) 1], eps) %! assert (imadjust (im), imadjust (im, stretchlim (im, 0.01), [0; 1], 1)) %! assert (imadjust (im, []), imadjust (im, [0; 1], [0; 1], 1)) %! assert (imadjust (im, [], []), imadjust (im, [0; 1], [0; 1], 1)) %! assert (imadjust (im, [], [.25 .75]), imadjust (im, [0; 1], [.25; .75], 1)) %! assert (imadjust (im, [.25; .75], []), imadjust (im, [.25; .75], [0; 1], 1)) %!assert (imadjust (linspace (0, 1), [], [.25 .75]), linspace (.25, .75, 100), eps) ## test with only input arg %!assert (imadjust (linspace (0, 1, 100),[1/99; 98/99]), %! [0 linspace(0, 1, 98) 1], eps) %!shared cm %! cm = [[0:8]' [1:9]' [2:10]'] / 10; ## a colormap %!assert (imadjust (cm, [0; 1], [0.5; 1]), (cm /2) + .5) ## with params in row %!assert (imadjust (cm, [0 1], [0.5 1]), (cm /2) + .5) ## a colormap, different output adjustment on each channel %!assert (imadjust (cm, [0; 1], [.1 .2 .3; .7 .8 .9]), %! (cm*.6) + [.1 .2 .3], eps) ## a colormap, different input adjustment on each channel %!assert (imadjust (cm, [.2 .4 .6; .7 .8 .9], [0; 1]), %! [[0 0 linspace(0, 1, 6) 1]' ... %! [0 0 0 linspace(0, 1, 5) 1]' ... %! [0 0 0 0 linspace(0, 1, 4) 1]'], eps) ### a colormap, different input and output on each %!assert (imadjust (cm, [.2 .4 .6; .7 .8 .9], [0 .1 .2; .8 .9 1]), %! [[0 0 linspace(0, .8, 6) .8]' ... %! [.1 .1 .1 linspace(.1, .9, 5) .9]' ... %! [.2 .2 .2 .2 linspace(.2, 1, 4) 1]'], eps) ## a colormap, different gamma, input and output on each %!assert (imadjust (cm, [.2 .4 .6; .7 .8 .9], [0 .1 .2; .8 .9 1], [0.5 1 2]), %! [[0 0 0 (((([.3 .4 .5 .6]-.2)/.5).^.5)*.8) .8 .8]' ... %! [.1 .1 .1 linspace(.1, .9, 5) .9]' ... %! [.2 .2 .2 .2 .2 ((((([.7 .8]-.6)/.3).^2).*.8)+.2) 1 1]'], eps*10) ## Handling values outside the [0 1] range %!test %! im = [-0.4:.1:0.8 %! 0.0:.1:1.2 %! 0.1:.1:1.3 %! -0.4:.2:2.0]; %! %! ## just clipping %! assert (imadjust (im, [0; 1], [0; 1]), %! [0 0 0 0 (0:.1:.8) %! (0:.1:1) 1 1 %! (.1:.1:1) 1 1 1 %! 0 0 (0:.2:1) 1 1 1 1 1], eps) %! %! ## clipping and invert %! assert (imadjust (im, [0; 1], [1; 0]), %! [1 1 1 1 (1:-.1:.2) %! (1:-.1:0) 0 0 %! (.9:-.1:0) 0 0 0 %! 1 1 (1:-.2:0) 0 0 0 0 0], eps) %! %! ## rescale %! assert (imadjust (im, [.2; .7], [.1; .9]), %! [1 1 1 1 1 1 1 2.6 4.2 5.8 7.4 9 9 %! 1 1 1 2.6 4.2 5.8 7.4 9 9 9 9 9 9 %! 1 1 2.6 4.2 5.8 7.4 9 9 9 9 9 9 9 %! 1 1 1 1 4.2 7.4 9 9 9 9 9 9 9]/10, eps) %! %! ## rescale and invert %! assert (imadjust (im, [.2; .7], [.9; .1]), %! [9 9 9 9 9 9 9 7.4 5.8 4.2 2.6 1 1 %! 9 9 9 7.4 5.8 4.2 2.6 1 1 1 1 1 1 %! 9 9 7.4 5.8 4.2 2.6 1 1 1 1 1 1 1 %! 9 9 9 9 5.8 2.6 1 1 1 1 1 1 1]/10, eps) ## adjusting only the gamma and nothing else %!assert (imadjust (linspace (0, 1), [], [], 2), linspace (0, 1) .^ 2) %!shared oRGB %! oRGB = zeros (10, 1, 3); %! oRGB(:,:,1) = [0 linspace(0,1,6) 1 1 1]'; %! oRGB(:,:,2) = [0 0 linspace(0,1,6) 1 1]'; %! oRGB(:,:,3) = [0 0 0 linspace(0,1,6) 1]'; %!assert (imadjust (oRGB, [0; 1], [0; 1]), oRGB) %!assert (imadjust (oRGB, [.2; .8], [0; 1]), %! reshape ([[0 0 0 1/3 2/3 1 1 1 1 1]' %! [0 0 0 0 1/3 2/3 1 1 1 1]' %! [0 0 0 0 0 1/3 2/3 1 1 1]'], [10 1 3]), eps) %!assert (imadjust (oRGB, [.2; .8], [.1; .9]), %! reshape ([[.1 .1 .1 (1/3)+(.1/3) (2/3)-(.1/3) .9 .9 .9 .9 .9]' %! [.1 .1 .1 .1 (1/3)+(.1/3) (2/3)-(.1/3) .9 .9 .9 .9]' %! [.1 .1 .1 .1 .1 (1/3)+(.1/3) (2/3)-(.1/3) .9 .9 .9]'], %! [10 1 3]), eps) %!assert (imadjust (oRGB, [.2; .8], [.2; .8]), %! reshape ([[2 2 2 4 6 8 8 8 8 8]' %! [2 2 2 2 4 6 8 8 8 8]' %! [2 2 2 2 2 4 6 8 8 8]']/10, [10 1 3]), eps) ## aRGB, different output for each channel %!assert (imadjust (oRGB, [0; 1], [.1 .2 .3; .9 .8 .7]), %! reshape ([[1 1 2.6 4.2 5.8 7.4 9 9 9 9]' %! [2 2 2 3.2 4.4 5.6 6.8 8 8 8]' %! [3 3 3 3 3.8 4.6 5.4 6.2 7 7]']/10, [10 1 3]), eps) ## a RGB, different input for each channel %!assert (imadjust (oRGB, [.1 .2 .3; .9 .8 .7], [0; 1]), %! reshape ([[0 0 .125 .375 .625 .875 1 1 1 1]' %! [0 0 0 0 1/3 2/3 1 1 1 1]' %! [0 0 0 0 0 .25 .75 1 1 1]'], [10 1 3]), eps*10) ## a RGB, different input and output on each %!assert (imadjust (oRGB, [.1 .2 .3; .9 .8 .7], [.2 0 .4; .5 1 .7 ]), %! reshape ([[.2 .2 .2375 .3125 .3875 .4625 .5 .5 .5 .5]' %! [0 0 0 0 1/3 2/3 1 1 1 1]' %! [.4 .4 .4 .4 .4 .475 .625 .7 .7 .7]'], [10 1 3]), eps) ## Test for ND dimensional images %!test %! img = rand (4, 4, 2, 3, 4); %! adj = zeros (4, 4, 2, 3, 4); %! for p = 1:2 %! for q = 1:3 %! for r = 1:4 %! adj(:,:,p,q,r) = imadjust (img(:,:,p,q,r)); %! endfor %! endfor %! endfor %! assert (imadjust (img), adj) ## Test for ND dimensional images with N dimensional arguments %!test %! img = rand (4, 4, 2, 3, 2); %! adj = zeros (4, 4, 2, 3, 2); %! in = reshape ([ 3 5 7 9 11 13 15 17 19 21 23 25; %! 97 95 93 91 89 87 85 83 81 79 77 75] / 100, [2 2 3 2]); %! out = reshape ([ 5 7 9 11 14 15 17 19 21 23 25 27; %! 95 93 91 89 87 85 83 81 79 77 75 73] / 100, [2 2 3 2]); %! gamma = reshape (0.6:.1:1.7, [1 2 3 2]); %! for p = 1:2 %! for q = 1:3 %! for r = 1:2 %! adj(:,:,p,q,r) = imadjust (img(:,:,p,q,r), in(:,p,q,r), %! out(:,p,q,r), gamma(1,p,q,r)); %! endfor %! endfor %! endfor %! assert (imadjust (img, in, out, gamma), adj, eps) ## Test how empty matrix is not really the default value %!test %! in = int16 (1:6); %! assert (imadjust (in), int16 ([-32768 -19661 -6554 6553 19660 32767])) %! assert (imadjust (in, []), in) ## ## Test images of integer class ## %!test %! in = uint8([ %! 35 1 6 26 19 24 %! 3 32 7 21 23 25 %! 31 9 2 22 27 20 %! 8 28 33 17 10 15 %! 30 5 34 12 14 16 %! 4 36 29 13 18 11]); %! out = uint8([ %! 12 0 0 1 0 0 %! 0 8 0 0 0 0 %! 7 0 0 0 2 0 %! 0 3 9 0 0 0 %! 6 0 11 0 0 0 %! 0 13 4 0 0 0]); %! assert (imadjust (in, [.1 .9], [0 1]), out); %!test %! in = uint8([ %! 140 4 24 104 76 96 %! 12 128 28 84 92 100 %! 124 36 8 88 108 80 %! 32 112 132 68 40 60 %! 120 20 136 48 56 64 %! 16 144 116 52 72 44]); %! out = uint8([ %! 143 0 0 98 63 88 %! 0 128 3 73 83 93 %! 123 13 0 78 103 68 %! 8 108 133 53 18 43 %! 118 0 138 28 38 48 %! 0 148 113 33 58 23]); %! assert (imadjust (in, [.1 .9], [0 1]), out); %!test %! in_u8 = randi ([0 255], 5, 5, 2, 3, "uint8"); %! in_u16 = randi ([0 65535], 5, 5, 2, 3, "uint16"); %! in_i16 = randi ([-32768 32767], 5, 5, 2, 3, "int16"); %! in_u8_d = im2double (in_u8); %! in_u16_d = im2double (in_u16); %! in_i16_d = im2double (in_i16); %! lim_u8 = eps + 0.5 / double (intmax ("uint8")); %! lim_u16 = eps + 0.5 / double (intmax ("uint16")); %! lim_i16 = eps + 0.5 / ( double (intmax("int16")) - double (intmin ("int16")) ); %! %! ## default values %! assert (im2double (imadjust (in_u8)), imadjust (in_u8_d), lim_u8) %! assert (im2double( imadjust (in_u16)), imadjust (in_u16_d), lim_u16) %! assert (im2double( imadjust (in_i16)), imadjust (in_i16_d), lim_i16) %! %! ## single adjustment for all planes %! args = {[.3; .7], [.1; .9], [1.5]}; %! assert (im2double (imadjust (in_u8, args{:})), imadjust (in_u8_d, args{:}), lim_u8) %! assert (im2double (imadjust (in_u16, args{:})), imadjust (in_u16_d, args{:}), lim_u16) %! assert (im2double (imadjust (in_i16, args{:})), imadjust (in_i16_d, args{:}), lim_i16) %! %! ## single adjustment for all planes (mixed with some complement) %! args = {reshape([.2 .3 .25 .1 0 .1; .9 .7 .85 .9 1 .8], [2 2 3]), %! reshape([.1 .2 .05 .9 1 .3; .9 .85 .7 .1 0 .9], [2 2 3]), %! reshape([1 .75 1 1.2 1.5 2], [1 2 3])}; %! assert (im2double (imadjust (in_u8, args{:})), imadjust (in_u8_d, args{:}), lim_u8) %! assert (im2double (imadjust (in_u16, args{:})), imadjust (in_u16_d, args{:}), lim_u16) %! assert (im2double (imadjust (in_i16, args{:})), imadjust (in_i16_d, args{:}), lim_i16) %! %! ## test use of [] as limit and negative %! args = {[], [.95; 0], 1.25}; %! assert (im2double (imadjust (in_u8, args{:})), imadjust (in_u8_d, args{:}), lim_u8) %! assert (im2double (imadjust (in_u16, args{:})), imadjust (in_u16_d, args{:}), lim_u16) %! assert (im2double (imadjust (in_i16, args{:})), imadjust (in_i16_d, args{:}), lim_i16) image-2.16.1/inst/PaxHeaders.61586/colfilt.m0000644000000000000000000000006215005110255015214 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/colfilt.m0000644000175000017500000001544415005110255016622 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} colfilt (@var{A}, @var{block_size}, @var{block_type}, @var{func}) ## @deftypefnx {Function File} {} colfilt (@var{A}, @var{block_size}, @var{subsize}, @var{block_type}, @var{func}, @dots{}) ## @deftypefnx {Function File} {} colfilt (@var{A}, "indexed", @dots{}) ## @deftypefnx {Function File} {} colfilt (@dots{}, @var{func}, @var{extra_args}, @dots{}) ## Apply function to matrix blocks ## ## Executes the function @var{func} on blocks of size @var{block_size}, ## taken from the matrix @var{A}. Both the matrix @var{A}, and the block ## can have any number of dimensions. ## ## The different blocks are organized into a matrix, each block as a ## single column, and passed as the first to the function handle ## @var{func}. Any input arguments to @code{colfilt} after @var{func} ## are passed to @var{func} after the blocks matrix. ## ## Blocks can be of two different types as defined by the string @var{block_type}: ## ## @table @asis ## @item @qcode{"distinct"} ## Each block is completely distinct from the other, with no overlapping ## elements. @var{func} must return a matrix of exactly the same size as ## its input. ## ## @item @qcode{"sliding"} ## Each possible block of size @var{block_size} inside @var{A} is used. ## @var{func} should act along the column dimension (be a column ## compression function) and return a vector of length equal to the ## number of columns of its input. ## ## @end table ## ## The optional argument @var{subsize} divides @var{A} into smaller pieces ## before generating the matrices with one block per column in order to ## save memory. It is currently only accepted for @sc{Matlab} compatibility. ## ## If @var{A} is an indexed image, the second argument should be the ## string @qcode{"indexed"} so that any required padding is done correctly. ## The padding value will be 0 except for indexed images of class uint8 ## and uint16. ## ## This function is mostly useful to apply moving or sliding window filter ## when @var{block_type} is "sliding". However, for many cases, specialized ## functions perform much faster. For the following common cases, consider ## the suggested alternatives; ## ## @table @asis ## @item moving average ## A moving average filter is equivalent to convolve with a matrix ## of @code{1/@var{N}} sized @var{block_size}, where @var{N} is the total ## number of elements in a block. Use ## @code{convn (@var{A}, (1/@var{N}) * ones (@var{block_size}) *, "same")} ## ## @item maximum or minimum ## This is the equivalent to a dilation and erosion. Use @code{imdilate} or ## @code{imerode}. ## ## @item any or all ## Same as dilation and erosion but with logical input. Use @code{imdilate} ## or @code{imerode} with @code{logical (@var{A})}. ## ## @item median ## Use @code{medfilt2} if @var{A} is only 2 dimensional, and @code{ordfiltn} ## with the @code{floor (prod (@var{N}/ 2)} th element, where @var{N} is the ## total number of elements in a block (add 1 if it is an even number). ## ## @item sort or nth_element ## Use @code{ordfiltn}. ## ## @item standard deviation ## Use @code{stdfilt}. ## ## @item sum ## Use a matrix of 1 to perform convolution, ## @code{convn (@var{A}, ones (@var{block_size}), "same")} ## ## @end table ## ## @seealso{bestblk, blockproc, col2im, im2col, nlfilter} ## @end deftypefn function B = colfilt (A, varargin) ## Input check if (nargin < 4) print_usage (); endif [p, block_size, padval] = im2col_check ("colfilt", nargin, A, varargin{:}); subsize = size (A); if (numel (varargin) < p) print_usage (); elseif (isnumeric (varargin{p}) && isvector (varargin{p})) subsize = varargin{p++}; subsize = postpad (subsize, ndims (A), 1); subsize = min (subsize, size (A)); subsize = max (subsize, block_size); endif ## We need at least 2 more arguments (block type and function) if (numel (varargin) < p +1) print_usage (); endif ## Next one needs to be block type block_type = varargin{p++}; if (! ischar (block_type)) error ("colfilt: BLOCK_TYPE must be a string"); endif ## followed by the function func = varargin{p++}; if (! isa (func, "function_handle")) error ("colfilt: FUNC must be a function handle"); endif ## anything after this are extra arguments to func extra_args = varargin(p:end); switch (tolower (block_type)) case "sliding" ## Function must return a single vector, one element per column, ## i.e., should act along the elements of each column. ## TODO for some large blocks, we may easily try to create matrix ## too large for Octave (see im2col documentation about the ## size). May be a good idea to split it into smaller images ## even if subsize is that large, so that we never go above ## sizemax (). ## However, this can be tricky. After splitting the image in ## smaller blocks, they can't be distinct, some parts need ## to overlap otherwise when we put them back together, we'll ## introduce many artifacts. padded = pad_for_sliding_filter (A, block_size, padval); cols = im2col (padded, block_size, "sliding"); B = col2im (func (cols, extra_args{:}), block_size, size (padded), "sliding"); case "distinct" ## Function must return a matrix with the same number of elements ## as its input, specially the same number of rows. ## One of the options of this function is to do the block ## processing already from big blocks from the original matrix ## in order to save memory. While this may make sense with ## sliding blocks, not so much here since cols will have the same ## size as A, and so will B. cols = im2col (A, block_size, "distinct"); B = col2im (func (cols, extra_args{:}), block_size, size (A), "distinct"); otherwise error ("colfilt: invalid BLOCK_TYPE `%s'.", block_type); endswitch endfunction %!demo %! ## Perform moving average filter with a 4x4 window %! A = magic (12) %! colfilt (A, [4 4], "sliding", @mean) %!test %! A = reshape (1:36, [6 6]); %! assert (colfilt (A, [2 2], [3 3], "sliding", @sum), %! conv2 (A, ones (2), "same")); image-2.16.1/inst/PaxHeaders.61586/impixel.m0000644000000000000000000000006215005110255015227 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/impixel.m0000644000175000017500000001501015005110255016622 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} impixel () ## @deftypefnx {Function File} {} impixel (@var{img}, @var{x}, @var{y}) ## @deftypefnx {Function File} {} impixel (@var{ind}, @var{map}, @var{x}, @var{y}) ## @deftypefnx {Function File} {} impixel (@var{xdata}, @var{ydata}, @var{img}, @var{x}, @var{y}) ## @deftypefnx {Function File} {} impixel (@var{xdata}, @var{ydata}, @var{ind}, @var{map}, @var{x}, @var{y}) ## @deftypefnx {Function File} {[@var{x}, @var{y}, @var{p}] =} impixel (@dots{}) ## Get pixel values. ## ## For any image @var{img}, or indexed image @var{ind} with colormap @var{map}, ## returns the pixel values at the image coordinates @var{x} and @var{y}. ## ## The 2 element vectors @var{xdata} and @var{ydata} can be used to set an ## alternative coordinate system. ## ## If more than one output argument is requested, also returns the @var{x} and ## @var{y} coordinates for the image. ## ## @itemize @bullet ## @item ## The pixel values are always returned in RGB style triples, even when ## @var{img} is a grayscale image. ## ## @item ## The value for pixel coordinates outside the image limits is NaN. ## ## @item ## Because a floating-point is required to represent a NaN, the pixel ## values will be of class double if input is double, and single otherwise. ## @end itemize ## ## @end deftypefn function varargout = impixel (varargin) if (nargin > 6) print_usage (); ## interactive usage elseif (nargin <= 2) ## FIXME not yet implemented print_usage (); if (nargin == 0) ## If using the current image, it is possible that xData and yData ## were changed? We will confirm later is they were tampered with. xData = get (gcf (), "xData"); yData = get (gcf (), "yData"); else ## with given image, otherwise we will use current image [img, map, is_indexed] = get_image (varargin{:}); endif ## If only 2 output arguments are requested in interactive mode, then ## only the coordinates are required, no need to do anything else. if (nargout <= 2) varargout(1:2) = {x y}; return endif ## non-interactive usage else x = varargin{end-1}; y = varargin{end}; if (! isnumeric (x) || ! isreal (x) || ! isnumeric (y) || ! isreal (y)) error ("impixel: X and Y must be real numbers"); endif x = x(:); y = y(:); if (nargin >= 5) [img, map, is_indexed] = get_image (varargin{3:end-2}); xData = varargin{1}; yData = varargin{2}; if (! isnumeric (xData) || ! isnumeric (yData)) ## For Matlab compatibility we do not check if there's ## only 2 elements, or if they are real numbers error ("impixel: XDATA and YDATA must be numeric"); endif else [img, map, is_indexed] = get_image (varargin{1:end-2}); xData = 1:columns (img); yData = 1:rows (img); endif endif ## We need to return NaN if the requested pixels are outside the image ## limits. interp2() will respect the input class, which means it will ## return a 0 instead of NaN if the image is an integer class. Because ## of that, we convert it to single. If the input image was double, then ## we let it be. if (isinteger (img)) img = single (img); if (is_indexed) ## There's an offset in indexed images depending on their class. An ## indexed image from integer class, matches the value 0 to row 1 of the ## colormap. An indexed image from a float class, matches value 1 to ## row 1. Since we are changing the class, we need to readjust it. img++; endif endif xx = linspace (min (xData), max (xData), columns (img)); yy = linspace (min (yData), max (yData), rows (img)); data = interp2 (xx, yy, img(:,:,1), x, y, "nearest"); if (ndims (img) == 3 && size (img, 3) == 3) ## We can't use interp3() because XI and YI will be used to select entire ## columns and vectors instead of matched coordinates for ch = 2:3 data(:,ch) = interp2 (xx, yy, img(:,:,ch), x, y, "nearest"); endfor endif if (is_indexed) bad = isnan (data); data(bad) = 1; data = map(data(:),:); data([bad bad bad]) = NA; elseif (isvector (data)) ## If we have a vector but the image was not indexed, it must have ## been a grayscale image. We need to repeat the values into a Nx3 ## matrix as if they were RGB values. data = [data(:) data(:) data(:)]; endif if (nargout > 1) varargout(1:3) = {x y data} else varargout(1) = {data}; endif endfunction function [img, map, is_indexed] = get_image (img, map = []) if (! isimage (img)) error ("impixel: invalid image"); endif is_indexed = false; if (nargin > 2) error ("impixel: too many input arguments"); elseif (nargin == 2) is_indexed = true; if (! iscolormap (map)) error ("impixel: invalid colormap"); elseif (! isind (img)) error ("impixel: invalid indexed image"); endif endif endfunction %!shared img2d, img3d %! img2d = uint8 (magic (10)); %! img3d(:,:,1) = img2d; %! img3d(:,:,2) = img2d + 1; %! img3d(:,:,3) = img2d + 2; %! img3d = uint8 (img3d); %! %!assert (impixel (img2d, 2, 2), single ([80 80 80])); %!assert (impixel (img2d, -2, 2), single ([NA NA NA])); %! %!assert (impixel (img2d, [1 10], [1 10]), single ([92 92 92; 59 59 59])); %!assert (impixel (img3d, [1 10], [1 10]), single ([92 93 94; 59 60 61])); %!assert (impixel (double (img2d), [1 10], [1 10]), [92 92 92; 59 59 59]); %! %!assert (impixel ([1 10], [1 10], img2d, [1 10], [1 10]), single ([92 92 92; 59 59 59])); %!assert (impixel ([3 12], [-4 12], img2d, [1 10], [1 10]), single ([NA NA NA; 44 44 44])); %!assert (impixel ([3 5], [-4 3], img2d, [1 10], [1 10]), single ([NA NA NA; NA NA NA])); %! %! ## the following returns double because it's an indexed image %!assert (impixel ([3 12], [-4 12], img2d, gray (100), [1 10], [1 10]), [NA NA NA; 4/9 4/9 4/9]); image-2.16.1/inst/PaxHeaders.61586/qtdecomp.m0000644000000000000000000000006215005110255015374 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/qtdecomp.m0000644000175000017500000002315015005110255016773 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{S} =} qtdecomp (@var{I}) ## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{threshold}) ## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{threshold}, @var{mindim}) ## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{threshold}, [@var{mindim} @var{maxdim}]) ## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{fun}) ## @deftypefnx {Function File} {@var{S} =} qtdecomp (@var{I}, @var{fun}, @var{P1}, @var{P2}, @dots{}) ## Performs quadtree decomposition. ## ## qtdecomp decomposes a square image @var{I} into four equal-sized ## blocks. Then it performs some kind of test on each block to decide if ## it should decompose them further. This process is repeated ## iteratively until there's no block left to be decomposed. ## ## Note that blocks are not decomposed if their dimensions are not even. ## ## The output is a sparse matrix whose non-zero elements determine the ## position of the block (the element is at top-left position in the ## block) and size of each block (the value of the element determines ## length of a side of the square-shaped block). ## ## S = qtdecomp(I) decomposes an intensity image @var{I} as described ## above. By default it doesn't split a block if all elements are equal. ## ## S = qtdecomp(I, threshold) decomposes an image as described, but only ## splits a block if the maximum value in the block minus the minimum ## value is greater than @var{threshold}, which is a value between 0 and ## 1. If @var{I} is of class uint8, @var{threshold} is multiplied by 255 ## before use. Also, if@var{I} is of class uint16, @var{threshold} is ## multiplied by 65535. ## ## S = qtdecomp(I, threshold, mindim) decomposes an image using the ## @var{threshold} as just described, but doesn't produce blocks smaller ## than mindim. ## ## S = qtdecomp(I, threshold, [mindim maxdim]) decomposes an image as ## described, but produces blocks that can't be bigger than maxdim. It ## decomposes to maxdim even if it isn't needed if only @var{threshold} ## was considered. ## ## S = qtdecomp(I, fun) decomposes an image @var{I} and uses function ## @var{fun} to decide if a block should be splitted or not. @var{fun} ## is called with a m-by-m-by-k array of m-by-m blocks to be ## considered, and should return a vector of size k, whose elements ## represent each block in the stacked array. @var{fun} sets the ## corresponding value to 1 if the block should be split, and 0 ## otherwise. ## ## S = qtdecomp(I, fun, @dots{}) behaves as qtdecomp(I, fun) but passes ## extra parameters to @var{fun}. ## ## @seealso{qtgetblk, qtsetblk} ## @end deftypefn function S = qtdecomp (I, p1, varargin) if (nargin < 1) print_usage; elseif (! issquare (I)) error("qtdecomp: I should be square."); endif ## current size (assumed to be square) curr_size=size(I,1); ## initial mindim to a sensible value mindim=1; ## sensible default maxdim value maxdim=curr_size; if (nargin<2) ## Initialize decision method variable ## We could have implemented threshold as a function and use an ## uniform interface (function handle) to decide whether to split or ## not blocks. We have decided not to do so because block ## rearrangement that is needed as a parameter to functions is ## expensive. decision_method=0; elseif (isreal(p1)) ## p1 is threshold threshold=p1; decision_method=1; if(strcmp(typeinfo(I), 'uint8 matrix')) threshold*=255; elseif(strcmp(typeinfo(I), 'uint16 matrix')) threshold*=65535; endif if (nargin>3) print_usage; elseif (nargin==3) dims=varargin{1}; if (isvector(dims)&&length(dims)==2) mindim=dims(1); maxdim=dims(2); elseif (isreal(dims)) mindim=dims; else error("qtdecomp: third parameter must be 'mindim' or '[mindim maxdim]'"); endif ## we won't check if mindim or maxdim are powers of 2. It's too ## restrictive and don't need it at all. endif elseif strcmp(typeinfo(p1),"function handle") ... || strcmp(typeinfo(p1),"inline function") ## function handles seem to return true to isscalar fun=p1; decision_method=2; else error("qtdecomp: second parameter must be a integer (threshold) or a function handle (fun)."); endif ## initialize results matrices res=[]; ## bool to flag end finished=false; ## array of offsets to blocks to evaluate offsets=[1,1]; if (maxdim0) divs=2^initial_splits; if (rem(curr_size,divs)!=0) error("qtdecomp: Can't decompose I enough times to fulfill maxdim requirement."); endif ## update curr_size curr_size/=divs; if(curr_size0) ## check other ending conditions: ## is size is odd? ## is splitted size < than mindim? if ((rem(curr_size,2)!=0)||((curr_size/2) ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} im2single (@var{img}) ## @deftypefnx {Function File} {} im2single (@var{img}, "indexed") ## Convert image to single precision. ## ## The conversion of @var{img} to single precision, is dependent ## on the type of input image. The following input classes are supported: ## ## @table @samp ## @item uint8, uint16, and int16 ## The whole range of values from the class (see @code{getrangefromclass}) ## are scaled for the interval [0 1], e.g., if input image was uint8, ## intensity values of 0, 127, and 255, are converted to intensity of ## 0, 0.498, and 1. ## ## @item logical ## True and false values are assigned a value of 0 and 1 respectively. ## ## @item double ## Values are cast to double precision. ## ## @item single ## Returns the same image. ## ## @end table ## ## If the second argument is the string @qcode{"indexed"}, then values are ## cast to single precision, and a +1 offset is applied if input is ## an integer class. ## ## @seealso{im2bw, imcast, im2uint8, im2double, im2int16, im2uint16} ## @end deftypefn function imout = im2single (img, varargin) if (nargin < 1 || nargin > 2) print_usage (); elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed")) error ("im2single: second input argument must be the string \"indexed\""); endif imout = imcast (img, "single", varargin{:}); endfunction %!assert (im2single (single ([1 2 3])), single ([1 2 3])); %!assert (im2single ([1 2 3]), single ([1 2 3])); %!assert (im2single (uint8 ([0 127 128 255])), single ([0 127/255 128/255 1])); %!assert (im2single (uint16 ([0 127 128 65535])), single ([0 127/65535 128/65535 1])); %!assert (im2single (int16 ([-32768 -32767 -32766 32767])), single ([0 1/65535 2/65535 1])); %!assert (im2single (uint8 ([0 1 255]), "indexed"), single ([1 2 256])); %!assert (im2single (uint16 ([0 1 2557]), "indexed"), single ([1 2 2558])); %!assert (im2single ([3 25], "indexed"), single ([3 25])); %!error im2single ([0 1 2], "indexed"); %!error im2single (int16 ([17 8]), "indexed"); %!error im2single (int16 ([-7 8]), "indexed"); %!error im2single ([false true], "indexed"); image-2.16.1/inst/PaxHeaders.61586/imgaussfilt.m0000644000000000000000000000006215005110255016107 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imgaussfilt.m0000644000175000017500000004514615005110255017517 0ustar00avinoamavinoam00000000000000## Copyright (C) 2023 ## Johannes Wirbser and ## Sarah Tiefert ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{J} = imgaussfilt(@var{img}) ## @deftypefnx {Function File} @var{J} = imgaussfilt(@dots{_}, @var{sigma}) ## @deftypefnx {Function File} @var{J} = imgaussfilt(@dots{}, @var{name}, @var{value}, @dots{}) ## ## Filters the image @var{img} with a 2d gaussian kernel and returns a smoothed image @var{J}. ## ## Parameters: ## @table @samp ## @item @var{img} ## The image to be filtered. Must be an image type, cannot be logical or non-numeric. ## @item @var{sigma} ## The standard deviation of the gausian filter. Can be a scalar or a two element vector ## If @var{sigma} is a vector, the function will use a square gaussian kernel. ## By default @var{sigma} is 0.5. ## @item @var{name}, @var{value} ## Additional options as name-value pairs: ## @item padding ## Determines how the image is padded. Value can be one of the following: ## @table @samp ## @item S ## Pads the image with the scalar S. ## @item "replicate" (default) ## Pads the image with the border pixel value. ## @item "symmetric" ## Pads the image by mirroring it at the image border. ## @item "circular" ## Pads the image with pixel values from the opposite image border, ## essentially treating the image as periodic. ## @end table ## @item Filter Domain ## Determines the domain in which to perform the filtering. ## Values can be one of the following: ## @table @samp ## @item "auto" ## The function determines the filter domain based on internal heuristics. ## @item "frequency" ## Perform convolution in the frequency domain. ## @item "spatial" ## Perform convolution in the spatial domain. ## @end table ## @end table ## @seealso{imfilter, imboxfilt} ## @end deftypefn function filtered_image = imgaussfilt (img, varargin) type_of_img = ""; optional_arguments = []; if (nargin < 1) print_usage (); endif if (islogical (img) || ! isimage (img) ) # test if img is a proper image error ("imgaussfilt: img needs to be an image"); endif ## convert img into double and remember original type img_class = class (img); img = double (img); ## check if we have optional input arguments optional_arguments = varargin; ## get inputs, the function returns the default value for any input not given. [sigma, padding, filter_size, filter_domain] = handle_optional_input ( optional_arguments); ## create filtermatrix (depending on filter_size and normalization_factor) [fil1, fil2] = create_gaussfilter (filter_size, sigma); ## create padding filter_rows = max (size (fil2)); filter_cols = max (size (fil1)); pad_rows = filter_rows / 2; pad_cols = filter_cols / 2; im = padarray (img, floor ([pad_rows, pad_cols]), padding); if (! isodd (filter_rows)) im = im(2:end,:,:); endif if (! isodd (filter_cols)) im = im(:,2:end,:); endif ## Filter Image ## two step filtering is used to increase filtering speed ## get size values to conserve original shape: [imrows, imcols, imchannels, tmp] = size (img); img_shape = size (img); if(strcmpi (filter_domain, "auto")) if(filter_size(1) + filter_size(2) > 600) filter_domain = "frequency"; else filter_domain = "spatial"; endif endif ## Change shape to 3 dim matrix img = reshape (img, imrows, imcols, (imchannels * tmp)); im_size = size (im); for channels = (imchannels*tmp):-1:1 if(strcmpi (filter_domain, "frequency")) im1 = filter_with_fft (im(:,:,channels), sigma); filtered_image(:,:,channels) = im1( ceil(pad_rows) : im_size(1)-floor(pad_rows), ceil(pad_cols) : im_size(2)-floor(pad_cols)); else ## Filter the image with fil1 im1(:,:,channels) = conv2 (im(:,:,channels), fil1, "valid"); ## Filter the image with fil2 filtered_image(:,:,channels) = conv2 (im1(:,:,channels), fil2, "valid"); endif endfor filtered_image = reshape (filtered_image, img_shape); # turn result back to original shape filtered_image = cast (filtered_image, img_class); # turn result back to original datatype endfunction function [filt_img_fft] = filter_with_fft (im_pad, sigma) size_1 = size (im_pad, 2); size_2 = size (im_pad, 1); x1 = linspace (-(size_1-1)/2, (size_1-1)/2, size_1); ## make numbers in list always as whole numbers, caused problem with x.5 ## numbers on even picture sie x2 = linspace (-(size_2-1)/2, (size_2-1)/2, size_2); if(mod (size_1, 2) == 0) x1 -= 0.5; endif if(mod (size_2, 2) == 0) x2 -= 0.5; endif ## lines fil_x1 = exp (-(x1.^2) ./ (2*sigma(2)^2)); fil_x1 /= sum (fil_x1); ## columns fil_x2 = exp (-(x2.^2) ./ (2*sigma(1)^2))'; fil_x2 /= sum (fil_x2); fil1_fft = fft (fil_x1); # lines fil2_fft = fft (fil_x2); # lines im_fft_1 = fft (im_pad, [], 2); im1_fft = im_fft_1 .* fil1_fft; im1 = ifft (im1_fft, [], 2); im1 = ifftshift (im1, 2); im_fft_2 = fft (im1, [], 1); filt_img_fft = im_fft_2 .* fil2_fft; filt_img_fft = ifft (filt_img_fft, [], 1); filt_img_fft = ifftshift (filt_img_fft, 1); filt_img_fft = real (filt_img_fft); endfunction ## Start of help functions: function res = isodd(value) res = all ((mod (value, 2) == 1)); endfunction function [sigma, padding, filter_size, filter_domain] = handle_optional_input ( optional_arguments) num_of_option_arg = length (optional_arguments); sigma_is_given = isodd (num_of_option_arg); name_value_is_given = num_of_option_arg > 1; sigma = get_sigma (sigma_is_given, optional_arguments); [padding, filter_size, filter_domain] = get_name_value_pairs (sigma_is_given, name_value_is_given, optional_arguments, sigma); endfunction function sigma = get_sigma (sigma_is_given, optional_arguments) sigma = 0.5; if(sigma_is_given) sigma = optional_arguments{1}; if(any (sigma < 0 ) || any (! isreal (sigma))) ## Must be a positive real numeric error ("imgaussfilt: sigma has to be a positive real numeric"); endif if(! isscalar (sigma)) dim = size (sigma); if((dim(1) + dim(2)) > 3) # Does the given vector have the right format? error (["imgaussfilt: sigma must be scalar or vector ", ... "with two values"]); endif endif endif if isscalar (sigma) sigma = [sigma, sigma]; endif endfunction function [padding, filter_size, filter_domain] = get_name_value_pairs ( sigma_is_given, name_value_is_given, optional_arguments, sigma) padding_options = {"replicate", "circular", "symmetric"}; filter_domain_options = {"auto", "frequency", "spatial"}; padding = "replicate"; index_first_name = 1 + double (sigma_is_given); filter_size = 2 * ceil (2 * sigma) + 1; filter_domain = "auto"; if(name_value_is_given) ## Check what kind of name value pair is given and test correctness of input for idx = (index_first_name:2:length(optional_arguments)) name = optional_arguments{idx}; value = optional_arguments{idx+1}; if ! ischar (name) error ("imgaussfilt: the name value must be string") endif if(strcmpi (name, "FilterSize")) filter_size = value; if(! isodd (filter_size) || any (filter_size < 0) || any (filter_size != round (filter_size))) error ("imgaussfilt: filter_size has to be an odd, positive integer"); endif if(! isscalar (filter_size)) dim = size (filter_size); if((dim(1) + dim(2)) > 3) # Does the given vector have the right format? error (["imgaussfilt: filter_size musst be scalar or vector ", ... "with two values"]); endif else filter_size = [filter_size, filter_size]; endif elseif(strcmpi (name, "padding")) if(isnumeric (value) || any (strcmpi (padding_options, value))) padding = value; else error ("imgaussfilt: padding option must be 'replicate', 'circular' \ 'symmetric' or a numeric scalar") endif elseif(strcmpi (name, "filterDomain")) if(! any (strcmpi (filter_domain_options, value))) error ("imgaussfilt: filterDomain must be either 'auto', 'frequency' \ or 'spatial'"); endif filter_domain = value; else error (["imgaussfilt: cannot handle option '", name,"'"]); endif endfor endif endfunction function [fil1, fil2] = create_gaussfilter (filter_size, sigma) fil1 = gauss (sigma(2), filter_size(2)); fil2 = gauss (sigma(1), filter_size(1))'; fil1 /= sum (fil1); fil2 /= sum (fil2); endfunction function retval = gauss(sigma, filter_size) x = linspace (-(filter_size-1) / 2, (filter_size-1) / 2, filter_size ); retval = exp (-(x.^2) / (2 * sigma^2)); endfunction ## Just img test, correct Syntax %!assert (imgaussfilt(ones (5) * 9)); %!assert (isa (imgaussfilt (uint8 (ones (5))), "uint8")); %!assert (isa (imgaussfilt (uint16 (ones (5))), "uint16")); %!assert (isa (imgaussfilt (uint32 (ones (5))), "uint32")); %!assert (isa (imgaussfilt (uint64 (ones (5))), "uint64")); %!assert (isa (imgaussfilt (int8 (ones (5))), "int8")); %!assert (isa (imgaussfilt (int16 (ones (5))), "int16")); %!assert (isa (imgaussfilt (int32 (ones (5))), "int32")); %!assert (isa (imgaussfilt (single (ones (5))), "single")); %!assert (isa (imgaussfilt (double (ones (5))), "double")); ## illegal datatypes for img %!error (imgaussfilt (true (5))); %!error (imgaussfilt (5i+9)); %!error (imgaussfilt ({"sdg","sdgsd"})); %!error (imgaussfilt ("sdjgkhsdkl")); %!error (imgaussfilt (struct("x", "34", "y", "67"))) ## just img test, illegal syntax %!error (imgaussfilt ()); %!error (imgaussfilt ("asdf")); % test sigma: %!assert(imgaussfilt(ones (5), 6)); %!assert(imgaussfilt(ones (5), 1.7)); %!assert (imgaussfilt (ones (5), [1, 4])); %!error(imgaussfilt(ones (5), -0.5)); %!error (imgaussfilt (ones (3), "asdf")); %!error (imgaussfilt (ones (3), [3,5,6])); %!error (imgaussfilt (ones (3), [3,-5])); %!error (imgaussfilt (ones (3), [-3, 4])); %!error (imgaussfilt (ones (3), [-3, -4])); ## tests for padding, valid input %!assert (imgaussfilt (ones (5), "padding", "circular")); %!assert (imgaussfilt (ones (5), "padding", "symmetric")); %!assert (imgaussfilt (ones (3), "padding", "replicate")); %!assert (imgaussfilt (ones (3), "Padding", "Replicate")); %!assert (imgaussfilt (ones (3), "Padding", 5)); ## tests for padding, invalid input %!error (imgaussfilt (ones (3), "circular")); %!error (imgaussfilt (ones (3), "symmetric", "padding")); %!error (imgaussfilt (ones (3), "padding")); %!error (imgaussfilt (ones (3), "padding", "ciircular")); %!error (imgaussfilt (ones (3), "padding", [2, 3])); %!error(imgaussfilt(ones (5), 5i)); % test filter size syntax: %!assert (imgaussfilt (ones (5), "filtersize", 3)); %!assert (imgaussfilt (ones (5), "FilterSize", [5, 7])); %!assert (imgaussfilt (ones (5), "FilterSize", [5, 3]')); %!error (imgaussfilt (ones (5), "filter", 3)); %!error (imgaussfilt (ones (5), "filtersize", 4)); %!error (imgaussfilt (ones (5), "filtersize", 5.5)); %!error (imgaussfilt (ones (5), "filterSize", [5, 2])); %!error (imgaussfilt (ones (5), "filterSize", [5, 2]')); %!error (imgaussfilt (ones (5), "filtersize", [5, 7.5])); %!error (imgaussfilt (ones (5), "filtersize", [5, 7.5]')); % test padding syntaxes: %!assert (imgaussfilt (ones (5), "padding", "replicate")); %!assert (imgaussfilt (ones (5), "padding", "circular")); %!assert (imgaussfilt (ones (5), "padding", "symmetric")); %!assert (imgaussfilt (ones (5), "padding", 5)); % test Filter domain: %!assert (imgaussfilt (ones (5), "FilterDomain", "auto")); %!assert (imgaussfilt (ones (5), "FilterDomain", "frequency")); %!assert (imgaussfilt (ones (5), "FilterDomain", "spatial")); %!error (imgaussfilt (ones (5), "FilterDomain", "asdf")); %!error (imgaussfilt (ones (5), "FilterDomain", 4)); %!test %! input = [1, 1, 1; 10, 10, 10; 100, 100, 100]; %! expected = [12.5028 12.5028 12.5028 %! 18.6271 18.6271 18.6271 %! 79.8702 79.8702 79.8702]; %! output = imgaussfilt (input, "padding", "circular"); %! assert (output, expected, 0.0001); %!test %! input = [1, 1, 1; 10, 10, 10; 100, 100, 100]; %! expected = [1.9586 1.9586 1.9586 %! 18.6271 18.6271 18.6271 %! 90.4144 90.4144 90.4144]; %! output = imgaussfilt (input, "padding", "replicate"); %! assert (output, expected, 0.0001); %!test %! input = [1 1 1 1 1 1 %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 1 1 1 1 1]; %! expected = [1.0114 1.0953 1.1067 1.1067 1.0953 1.0114 %! 1.0953 1.7980 1.8930 1.8930 1.7980 1.0953 %! 1.1067 1.8930 1.9995 1.9995 1.8930 1.1067 %! 1.1067 1.8930 1.9995 1.9995 1.8930 1.1067 %! 1.0953 1.7980 1.8930 1.8930 1.7980 1.0953 %! 1.0114 1.0953 1.1067 1.1067 1.0953 1.0114]; %! output = imgaussfilt (input, "filterSize", 5, "padding", "replicate"); %! assert (output, expected, 0.0001); %!test %! input = [1 1 1 1 1 1 %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 1 1 1 1 1]; %! expected = [1.0114 1.0956 1.1070 1.1070 1.0956 1.0114 %! 1.0956 1.7980 1.8930 1.8930 1.7980 1.0956 %! 1.1070 1.8930 1.9995 1.9995 1.8930 1.1070 %! 1.1070 1.8930 1.9995 1.9995 1.8930 1.1070 %! 1.0956 1.7980 1.8930 1.8930 1.7980 1.0956 %! 1.0114 1.0956 1.1070 1.1070 1.0956 1.0114]; %! output = imgaussfilt (input, "filterSize", 5, "padding", "symmetric"); %! assert (output, expected, 0.0001); ## test for 4 dim image: %!test %! input = zeros(3,3,3,3); %! input (2,2,:,:) = 9; %! output = imgaussfilt (input); %! expected = ones (3,3,3,3); %! assert (size(output), size(expected), eps); ## test for 4 dim image, channels are independent %!test %! a1 = ones (5); %! a2 = ones (5) * 2; %! a3 = ones (5) * 3; %! a4 = ones (5) * 4; %! im = cat (4, a1, a2, a3, a4); %! expected = im; %! output = imgaussfilt (im); %! assert (output, expected, 0.0001); %!test %! input = zeros(5,7); %! input(3,4) = 1; %! expected = [ -0.0000 0.0001 0.0006 0.0011 0.0006 0.0001 -0.0000 %! 0.0000 0.0062 0.0397 0.0736 0.0397 0.0062 0.0000 %! 0 0.0250 0.1593 0.2953 0.1593 0.0250 -0.0000 %! 0 0.0062 0.0397 0.0736 0.0397 0.0062 -0.0000 %! -0.0000 0.0001 0.0006 0.0011 0.0006 0.0001 -0.0000 ]; %! output = imgaussfilt(input, [0.6,0.9], "filterDomain", "frequency"); %! assert (output, expected, 0.01); ## test paddings for fft %!test %! input = zeros(8); %! input(6,4) = 1; %! input(6,5) = 1; %! input(7,4) = 2; %! input(7,5) = 2; %! expected_replicate = [-0.0000 -0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 %! 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 %! 0.0000 0.0000 -0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 %! 0.0000 0.0030 0.0163 0.0352 0.0352 0.0163 0.0030 0.0000 %! 0.0000 0.0163 0.0892 0.1932 0.1932 0.0892 0.0163 0.0000 %! 0.0000 0.0352 0.1932 0.4184 0.4184 0.1932 0.0352 0.0000 %! 0 0.0352 0.1932 0.4184 0.4184 0.1932 0.0352 0 %! -0.0000 0.0192 0.1055 0.2284 0.2284 0.1055 0.0192 0.0000]; %! expected_circular = [-0.0000 0.0030 0.0163 0.0352 0.0352 0.0163 0.0030 0.0000 %! 0.0000 -0.0000 -0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 %! 0.0000 0.0000 -0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 %! 0.0000 0.0030 0.0163 0.0352 0.0352 0.0163 0.0030 0.0000 %! 0.0000 0.0163 0.0892 0.1932 0.1932 0.0892 0.0163 0.0000 %! 0 0.0352 0.1932 0.4184 0.4184 0.1932 0.0352 0.0000 %! 0.0000 0.0352 0.1932 0.4184 0.4184 0.1932 0.0352 0.0000 %! 0.0000 0.0163 0.0892 0.1932 0.1932 0.0892 0.0163 0.0000]; %! expected_symmetric = [-0.0000 -0.0000 -0.0000 0.0000 0.0000 -0.0000 -0.0000 -0.0000 %! 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 %! 0.0000 0.0000 -0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 %! 0.0000 0.0030 0.0163 0.0352 0.0352 0.0163 0.0030 0.0000 %! 0 0.0163 0.0892 0.1932 0.1932 0.0892 0.0163 0 %! 0.0000 0.0352 0.1932 0.4184 0.4184 0.1932 0.0352 0 %! 0 0.0352 0.1932 0.4184 0.4184 0.1932 0.0352 0 %! 0.0000 0.0163 0.0892 0.1932 0.1932 0.0892 0.0163 0.0000]; %! output_symmetric = imgaussfilt(input, 1, "filterDomain", "frequency", "padding", "symmetric"); %! output_circular = imgaussfilt(input, 1, "filterDomain", "frequency", "padding", "circular"); %! output_replicate = imgaussfilt(input, 1, "filterDomain", "frequency", "padding", "replicate"); %! assert (output_symmetric, expected_symmetric, 0.3); %! assert (output_circular, expected_circular, 0.3); %! assert (output_replicate, expected_replicate, 0.3); % Expected outputs are values from Matlab(there's some tolerance) ## test orientation for different sigma values in two axis %!test %! im = zeros (11); %! im(6,6) = 1; %! out = imgaussfilt (im, [2, 0.5]); %! assert (sum (out(6,:)), 0.2042, 0.0001); %! assert (sum (out(:,6)), 0.7870, 0.0001); image-2.16.1/inst/PaxHeaders.61586/qtsetblk.m0000644000000000000000000000006215005110255015411 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/qtsetblk.m0000644000175000017500000000505115005110255017010 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{J} =} qtsetblk (@var{I}, @var{S}, @var{dim}, @var{vals}) ## Set block values in a quadtree decomposition. ## ## J=qtsetblk(I,S,dim,vals) sets all the @var{dim}-by-@var{dim} blocks ## in the quadtree decomposition (@var{S} returned by qtdecomp) of ## @var{I} to @var{dim}-by-@var{dim} blocks in @var{vals}, which is ## itself a @var{dim}-by-@var{dim}-by-k array. k is the number of ## @var{dim}-by-@var{dim} blocks in the quadtree decomposition. ## @seealso{qtdecomp, qtgetblk} ## @end deftypefn function J = qtsetblk (I, S, dim, vals) if (nargin != 4) print_usage; endif ## get blocks [ii,ji,v]=find(S); ## filter the ones which match dim idx=find(v==dim); if(size(vals,3)num blocks error("qtsetblk: k (vals 3rd dimension) is not equal to number of blocks."); endif ii=ii(idx); ji=ji(idx); ## calc end vertex ie=ii+dim-1; je=ji+dim-1; J=I; for b=1:length(idx) J(ii(b):ie(b),ji(b):je(b))=vals(:,:,b); endfor endfunction %!demo %! J=qtsetblk(eye(4),qtdecomp(eye(4)),2,ones(2,2,2)) %! % Sets upper-right and lower-left blocks of 2*2 zeros to ones %!shared A, S %! A=[ 1, 4, 2, 5,54,55,61,62; %! 3, 6, 3, 1,58,53,67,65; %! 3, 6, 3, 1,58,53,67,65; %! 3, 6, 3, 1,58,53,67,65; %! 23,42,42,42,99,99,99,99; %! 27,42,42,42,99,99,99,99; %! 23,22,26,25,99,99,99,99; %! 22,22,24,22,99,99,99,99]; %! S = qtdecomp (A, 10); %!test %! R=A; %! vals=zeros(4,4,2); %! vals(:,:,1)=reshape([1:16],4,4); %! vals(:,:,2)=reshape([21:36],4,4); %! R(1:4,1:4)=reshape([1:16],4,4); %! R(5:8,5:8)=reshape([21:36],4,4); %! assert(qtsetblk(A,S,4,vals),R); %!test %! R=A; %! R(1:4,5:8)=1; %! R(7:8,1:4)=1; %! R(5:6,3:4)=1; %! assert(qtsetblk(A,S,2,ones(2,2,7)),R); %!test %! R=A; %! R(5:6,1:2)=10; %! assert(qtsetblk(A,S,1,ones(1,1,4)*10),R); image-2.16.1/inst/PaxHeaders.61586/edge.m0000644000000000000000000000006215005110255014464 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/edge.m0000644000175000017500000007315615005110255016076 0ustar00avinoamavinoam00000000000000## Copyright (C) 1999 Andy Adler ## ## Copyright (C) 2008 Søren Hauberg ## Copyright (C) 2015 Hartmut Gimpel ## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{bw}, @var{thresh}] =} edge (@var{im}, @var{method}, @dots{}) ## Find edges using various methods. ## ## The image @var{im} must be 2 dimensional and grayscale. The @var{method} ## must be a string with the string name. The other input arguments are ## dependent on @var{method}. ## ## @var{bw} is a binary image with the identified edges. @var{thresh} is ## the threshold value used to identify those edges. Note that @var{thresh} ## is used on a filtered image and not directly on @var{im}. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Canny"}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Canny"}, @var{thresh}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Canny"}, @var{thresh}, @var{sigma}) ## Find edges using the Canny method. ## ## @var{thresh} is two element vector for the hysteresis thresholding. ## The lower and higher threshold values are the first and second elements ## respectively. If it is a scalar value, the lower value is set to ## @code{0.4 * @var{thresh}}. ## ## @var{sigma} is the standard deviation to be used on the Gaussian filter ## that is used to smooth the input image prior to estimating gradients. ## Defaults to @code{sqrt (2)}. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Kirsch"}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Kirsch"}, @var{thresh}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Kirsch"}, @var{thresh}, @var{direction}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Kirsch"}, @var{thresh}, @var{direction}, @var{thinning}) ## Find edges using the Kirsch approximation to the derivatives. ## ## Edge points are defined as points where the length of the gradient exceeds ## a threshold @var{thresh}. ## ## @var{thresh} is the threshold used and defaults to twice the square root of the ## mean of the gradient squared of @var{im}. ## ## @var{direction} is the direction of which the gradient is ## approximated and can be @qcode{"vertical"}, @qcode{"horizontal"}, ## or @qcode{"both"} (default). ## ## @var{thinning} can be the string @qcode{"thinning"} (default) or ## @qcode{"nothinning"}. This controls if a simple thinning procedure ## is applied to the edge image such that edge points also need to have ## a larger gradient than their neighbours. The resulting "thinned" ## edges are only one pixel wide. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Lindeberg"}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Lindeberg"}, @var{sigma}) ## Find edges using the the differential geometric single-scale edge ## detector by Tony Lindeberg. ## ## @var{sigma} is the scale (spread of Gaussian filter) at which the edges ## are computed. Defaults to @code{2}. ## ## This method does not use a threshold value and therefore does not return ## one. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"LoG"}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"LoG"}, @var{thresh}, @var{sigma}) ## Find edges by convolving with the Laplacian of Gaussian (LoG) filter, ## and finding zero crossings. ## ## Only zero crossings where the filter response is larger than @var{thresh} ## are retained. @var{thresh} is automatically computed as 0.75*@math{M}, ## where @math{M} is the mean of absolute value of LoG filter response. ## ## @var{sigma} sets the spread of the LoG filter. Default to @code{2}. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Prewitt"}, @dots{}) ## Find edges using the Prewitt approximation to the derivatives. ## ## This method is the same as Kirsch except a different approximation ## gradient is used. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Roberts"}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Roberts"}, @var{thresh}) ## @deftypefnx {Function File} {} edge (@var{im}, @qcode{"Roberts"}, @var{thresh}, @var{thinning}) ## Find edges using the Roberts approximation to the derivatives. ## ## This method is similar to Kirsch except a different approximation ## gradient is used, and the default @var{thresh} is multiplied by sqrt(1.5). ## In addition, there it does not accept the @var{direction} argument. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Sobel"}, @dots{}) ## Find edges using the Sobel approximation to the derivatives. ## ## This method is the same as Kirsch except a different approximation ## gradient is used. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"zerocross"}, @var{thresh}, @var{filter}) ## Find edges by convolving with a user-supplied filter and finding zero ## crossings. ## ## @end deftypefn ## @deftypefn {Function File} {} edge (@var{im}, @qcode{"Andy"}, @var{thresh}, @var{params}) ## Find edges by the original (Andy Adlers) @code{edge} algorithm. ## The steps are ## @enumerate ## @item ## Do a Sobel edge detection and to generate an image at ## a high and low threshold. ## @item ## Edge extend all edges in the LT image by several pixels, ## in the vertical, horizontal, and 45 degree directions. ## Combine these into edge extended (EE) image. ## @item ## Dilate the EE image by 1 step. ## @item ## Select all EE features that are connected to features in ## the HT image. ## @end enumerate ## ## The parameters for the method is given in a vector: ## @table @asis ## @item params(1)==0 or 4 or 8 ## Perform x connected dilation (step 3). ## @item params(2) ## Dilation coefficient (threshold) in step 3. ## @item params(3) ## Length of edge extension convolution (step 2). ## @item params(4) ## Coefficient of extension convolution in step 2. ## @end table ## defaults = [8, 1, 3, 3] ## ## @seealso{fspecial} ## ## @end deftypefn function [bw_out, thresh, varargout] = edge (im, method = "sobel", varargin) if (nargin < 1 || nargin > 5) print_usage (); endif if (! isgray (im)) error ("edge: IM must be a grayscale image"); endif method = tolower (method); switch (method) case {"sobel", "prewitt", "kirsch"} [bw, thresh] = edge_kirsch_prewitt_sobel (im, method, varargin{:}); case "roberts" [bw, thresh, varargout{1:2}] = edge_roberts (im, varargin{:}); case "log" [bw, thresh] = edge_log (im, varargin{:}); case "zerocross" [bw, thresh] = edge_zerocross (im, varargin{:}); case "canny" [bw, thresh] = edge_canny (im, varargin{:}); case "lindeberg" [bw] = edge_lindeberg (im, varargin{:}); case "andy" [bw, thresh] = edge_andy (im, varargin{:}); otherwise error ("edge: unsupported edge detector `%s'", method); endswitch if (nargout == 0) imshow (bw); else bw_out = bw; endif endfunction function [bw, thresh] = edge_kirsch_prewitt_sobel (im, method, varargin) if (numel (varargin) > 3) error ("edge: %s method takes at most 3 extra arguments", method); endif ## This supports thinning, direction, and thresh arguments in any ## order. Matlab madness I tell you. varargin = tolower (varargin); direction_mask = (strcmp (varargin, "both") | strcmp (varargin, "horizontal") | strcmp (varargin, "vertical")); thinning_mask = (strcmp (varargin, "thinning") | strcmp (varargin, "nothinning")); thresh_mask = cellfun (@(x) isnumeric (x) && (isscalar (x) || isempty (x)), varargin); if (! all (direction_mask | thinning_mask | thresh_mask)) error ("edge: %s method takes only THRESH, DIRECTION, and THINNING arguments", method); endif if (nnz (direction_mask) > 1) error ("edge: more than 1 direction argument defined"); elseif (any (direction_mask)) direction = varargin{direction_mask}; else direction = "both"; endif if (nnz (thinning_mask) > 1) error ("edge: more than 1 thinning argument defined"); elseif (any (thinning_mask) && strcmp (varargin{thinning_mask}, "nothinning")) thinning = false; else thinning = true; endif if (nnz (thresh_mask) > 1) error ("edge: more than 1 threshold argument defined"); elseif (any (thresh_mask)) thresh = varargin{thresh_mask}; else thresh = []; endif ## For better speed, the calculation is done with ## squared edge strenght values (strength2) and ## squared threshold values (thresh2). h1 = fspecial (method); h1 ./= sum (abs (h1(:))); # normalize h1 im = im2double (im); switch (direction) case "horizontal" strength2 = imfilter (im, h1, "replicate").^2; case "vertical" strength2 = imfilter (im, h1', "replicate").^2; case "both" strength2 = (imfilter (im, h1, "replicate").^2 + imfilter (im, h1', "replicate").^2); otherwise error ("edge: unknown DIRECTION `%s' for %s method", direction, method); endswitch if (isempty (thresh)) thresh2 = 4 * mean (strength2(:)); thresh = sqrt (thresh2); else thresh2 = thresh .^ 2; endif if (thinning) ## Keep edge strengths for use in non-maximum ## suppresion in the simple_thinning step. strength2(strength2<=thresh2) = 0; bw = simple_thinning (strength2); else bw = strength2 > thresh2; endif endfunction function [bw, thresh, g45, g135] = edge_roberts (im, varargin) if (numel (varargin) > 2) error ("edge: Roberts method takes at most 2 extra arguments"); endif ## This supports thinning, direction, and thresh arguments in any ## order. Matlab madness I tell you. varargin = tolower (varargin); thinning_mask = (strcmp (varargin, "thinning") | strcmp (varargin, "nothinning")); thresh_mask = cellfun (@(x) isnumeric (x) && (isscalar (x) || isempty (x)), varargin); if (! all (thinning_mask | thresh_mask)) error ("edge: roberts method takes only THRESH and THINNING arguments"); endif if (nnz (thinning_mask) > 1) error ("edge: more than 1 thinning argument defined"); elseif (any (thinning_mask) && strcmp (varargin{thinning_mask}, "nothinning")) thinning = false; else thinning = true; endif if (nnz (thresh_mask) > 1) error ("edge: more than 1 threshold argument defined"); elseif (any (thresh_mask)) thresh = varargin{thresh_mask}; else thresh = []; endif h1 = [1 0; 0 -1] ./ 2; h2 = [0 1; -1 0] ./ 2; g45 = imfilter (im, h1, "replicate"); g135 = imfilter (im, h2, "replicate"); strength2 = g45.^2 + g135.^2; if (isempty (thresh)) thresh2 = 6 * mean (strength2(:)); thresh = sqrt (thresh2); else thresh2 = thresh .^ 2; endif if (thinning) ## Keep edge strengths for use in non-maximum ## suppresion in the simple_thinning step. strength2(strength2<=thresh2) = 0; bw = simple_thinning (strength2); else bw = strength2 > thresh2; endif endfunction function [bw, thresh] = edge_log (im, thresh = [], sigma = 2) if (nargin > 1 && (! isnumeric (thresh) || all (numel (thresh) != [0 1]))) error ("edge: THRESH for LoG method must be a numeric scalar or empty"); endif if (nargin > 2 && (! isnumeric (sigma) || ! isscalar (sigma))) error ("edge: SIGMA for LoG method must be a numeric scalar"); endif f = fspecial ("log", (2 * ceil (3*sigma)) +1, sigma); g = conv2 (im, f, "same"); if (isempty (thresh)) thresh = 0.75*mean(abs(g(:))); endif bw = (abs (g) > thresh) & zerocrossings (g); endfunction function [bw, thresh] = edge_zerocross (im, thresh, f) ## because filter is a required argument, so is thresh if (nargin != 3) error ("edge: a FILTER and THRESH are required for the zerocross method"); elseif (! isnumeric (thresh) || all (numel (thresh) != [0 1])) error ("edge: THRESH for zerocross method must be a numeric scalar or empty"); elseif (! isnumeric (f)) error ("edge: FILTER for zerocross method must be numeric"); endif g = conv2 (im, f, "same"); if (isempty (thresh)) thresh = mean (abs (g(:))); endif bw = (abs (g) > thresh) & zerocrossings(g); endfunction function [bw, thresh] = edge_canny (im, thresh = [], sigma = sqrt (2)) if (nargin > 1 && (! isnumeric (thresh) || all (numel (thresh) != [0 1 2]))) error ("edge: THRESH for Canny method must have 0, 1, or 2 elements"); endif if (nargin > 2 && (! isnumeric (sigma) || ! isscalar (sigma))) error ("edge: SIGMA for Canny method must be a numeric scalar"); endif ## Gaussian filtering to change the edge scale. ## Treat each dimensions separately for performance. gauss = fspecial ("gaussian", [1 (8*ceil(sigma))], sigma); im = im2double (im); J = imfilter (im, gauss, "replicate"); J = imfilter (J, gauss', "replicate"); ## edge detection with Prewitt filter (treat dimensions separately) p = [1 0 -1]/2; Jx = imfilter (J, p, "replicate"); Jy = imfilter (J, p', "replicate"); Es = sqrt (Jx.^2 + Jy.^2); Es_max = max (Es(:)); if (Es_max > 0) Es ./= Es_max; endif Eo = pi - mod (atan2 (Jy, Jx) - pi, pi); if (isempty (thresh)) tmp = mean(abs(Es(:))); thresh = [0.4*tmp, tmp]; elseif (numel (thresh) == 1) thresh = [0.4*thresh thresh]; else thresh = thresh(:).'; # always return a row vector endif bw = nonmax_suppress(Es, Eo, thresh(1), thresh(2)); endfunction function [bw] = edge_lindeberg (im, sigma = 2) if (nargin > 1 && (! isnumeric (sigma) || ! isscalar (sigma))) error ("edge: SIGMA for Lindeberg method must be a numeric scalar"); endif ## Filters for computing the derivatives Px = [-1 0 1; -1 0 1; -1 0 1]; Py = [1 1 1; 0 0 0; -1 -1 -1]; Pxx = conv2 (Px, Px, "full"); Pyy = conv2 (Py, Py, "full"); Pxy = conv2 (Px, Py, "full"); Pxxx = conv2 (Pxx, Px, "full"); Pyyy = conv2 (Pyy, Py, "full"); Pxxy = conv2 (Pxx, Py, "full"); Pxyy = conv2 (Pyy, Px, "full"); ## Change scale L = imsmooth (double (im), "Gaussian", sigma); ## Compute derivatives Lx = conv2 (L, Px, "same"); Ly = conv2 (L, Py, "same"); Lxx = conv2 (L, Pxx, "same"); Lyy = conv2 (L, Pyy, "same"); Lxy = conv2 (L, Pxy, "same"); Lxxx = conv2 (L, Pxxx, "same"); Lyyy = conv2 (L, Pyyy, "same"); Lxxy = conv2 (L, Pxxy, "same"); Lxyy = conv2 (L, Pxyy, "same"); ## Compute directional derivatives Lvv = Lx.^2.*Lxx + 2.*Lx.*Ly.*Lxy + Ly.^2.*Lyy; Lvvv = Lx.^3.*Lxxx + 3.*Lx.^2.*Ly.*Lxxy ... + 3.*Lx.*Ly.^2.*Lxyy + 3.*Ly.^3.*Lyyy; ## Perform edge detection bw = zerocrossings (Lvv) & Lvvv < 0; endfunction ## The 'andy' edge detector that was present in older versions of 'edge'. function [imout, thresh] = edge_andy (im, thresh, param2) [n,m]= size(im); xx= 2:m-1; yy= 2:n-1; filt= [1 2 1;0 0 0; -1 -2 -1]/8; tv= 2; imo= conv2(im, rot90(filt), 'same').^2 + conv2(im, filt, 'same').^2; if nargin<2 || thresh==[]; thresh= sqrt( tv* mean(mean( imo(yy,xx) )) ); end # sum( imo(:)>thresh ) / prod(size(imo)) dilate= [1 1 1;1 1 1;1 1 1]; tt= 1; sz=3; dt=3; if nargin>=3 # 0 or 4 or 8 connected dilation if length(param2) > 0 if param2(1)==4 ; dilate= [0 1 0;1 1 1;0 1 0]; elseif param2(1)==0 ; dilate= 1; end end # dilation threshold if length(param2) > 2; tt= param2(2); end # edge extension length if length(param2) > 2; sz= param2(3); end # edge extension threshold if length(param2) > 3; dt= param2(4); end end fobliq= [0 0 0 0 1;0 0 0 .5 .5;0 0 0 1 0;0 0 .5 .5 0;0 0 1 0 0; 0 .5 .5 0 0;0 1 0 0 0;.5 .5 0 0 0;1 0 0 0 0]; fobliq= fobliq( 5-sz:5+sz, 3-ceil(sz/2):3+ceil(sz/2) ); xpeak= imo(yy,xx-1) <= imo(yy,xx) & imo(yy,xx) > imo(yy,xx+1) ; ypeak= imo(yy-1,xx) <= imo(yy,xx) & imo(yy,xx) > imo(yy+1,xx) ; imht= ( imo >= thresh^2 * 2); # high threshold image imht(yy,xx)= imht(yy,xx) & ( xpeak | ypeak ); imht([1,n],:)=0; imht(:,[1,m])=0; % imlt= ( imo >= thresh^2 / 2); # low threshold image imlt= ( imo >= thresh^2 / 1); # low threshold image imlt(yy,xx)= imlt(yy,xx) & ( xpeak | ypeak ); imlt([1,n],:)=0; imlt(:,[1,m])=0; # now we edge extend the low thresh image in 4 directions imee= ( conv2( imlt, ones(2*sz+1,1) , 'same') > tt ) | ... ( conv2( imlt, ones(1,2*sz+1) , 'same') > tt ) | ... ( conv2( imlt, eye(2*sz+1) , 'same') > tt ) | ... ( conv2( imlt, rot90(eye(2*sz+1)), 'same') > tt ) | ... ( conv2( imlt, fobliq , 'same') > tt ) | ... ( conv2( imlt, fobliq' , 'same') > tt ) | ... ( conv2( imlt, rot90(fobliq) , 'same') > tt ) | ... ( conv2( imlt, flipud(fobliq) , 'same') > tt ); # imee(yy,xx)= conv2(imee(yy,xx),ones(3),'same') & ( xpeak | ypeak ); imee= conv2(imee,dilate,'same') > dt; # % ff= find( imht==1 ); % imout = bwselect( imee, rem(ff-1, n)+1, ceil(ff/n), 8); imout = imee; endfunction ## An auxiliary function that performs a very simple thinning. ## Strength is an image containing the edge strength. ## bw contains a 1 in (r,c) if ## 1) strength(r,c) is greater than both neighbours in the ## vertical direction, OR ## 2) strength(r,c) is greater than both neighbours in the ## horizontal direction. ## Note the use of OR. function bw = simple_thinning(strength) [r c] = size(strength); x = ( strength > [ zeros(r,1) strength(:,1:end-1) ] & ... strength > [ strength(:,2:end) zeros(r,1) ] ); y = ( strength > [ zeros(1,c); strength(1:end-1,:) ] & ... strength > [ strength(2:end,:); zeros(1,c) ] ); bw = x | y; endfunction ## Auxiliary function. Finds the zero crossings of the ## 2-dimensional function f. (By Etienne Grossmann) function z = zerocrossings(f) z0 = f<0; ## Negative [R,C] = size(f); z = zeros(R,C); z(1:R-1,:) |= z0(2:R,:); ## Grow z(2:R,:) |= z0(1:R-1,:); z(:,1:C-1) |= z0(:,2:C); z(:,2:C) |= z0(:,1:C-1); z &= !z0; ## "Positive zero-crossings"? endfunction ## Test the madness of arbitrary order of input for prewitt, kirsch, ## and sobel methods. %!test %! im = [ %! 249 238 214 157 106 69 60 90 131 181 224 247 252 250 250 %! 250 242 221 165 112 73 62 91 133 183 225 248 252 250 251 %! 252 246 228 173 120 78 63 90 130 181 224 248 253 251 251 %! 253 248 232 185 132 87 62 80 116 170 217 244 253 251 252 %! 253 249 236 198 149 101 66 71 101 155 206 238 252 252 252 %! 254 250 240 210 164 115 73 69 92 143 196 232 252 253 252 %! 70 70 68 61 49 36 24 22 26 38 52 63 70 70 70 %! 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 %! 62 63 62 59 51 42 33 25 22 26 36 45 56 60 62 %! 252 253 252 246 221 190 157 114 90 90 118 157 203 235 248 %! 251 253 254 251 233 209 182 136 103 92 107 139 185 225 245 %! 251 253 254 253 243 227 206 163 128 108 110 133 175 217 242 %! 252 253 254 254 249 241 228 195 164 137 127 139 172 212 239 %! ] / 255; %! %! methods = {"kirsch", "prewitt", "sobel"}; %! for m_i = 1:numel (methods) %! method = methods{m_i}; %! %! bw = edge (im, method, 0.2, "both", "thinning"); %! assert (edge (im, method, 0.2), bw) %! %! args = perms ({0.2, "both", "thinning"}); %! for i = 1:rows (args) %! assert (edge (im, method, args{i,:}), bw) %! endfor %! %! bw = edge (im, method, 0.2, "vertical", "nothinning"); %! args = perms ({0.2, "vertical", "nothinning"}); %! for i = 1:rows (args) %! assert (edge (im, method, args{i,:}), bw) %! endfor %! %! bw = edge (im, method, 0.2, "vertical", "thinning"); %! args = perms ({0.2, "vertical"}); %! for i = 1:rows (args) %! assert (edge (im, method, args{i,:}), bw) %! endfor %! %! bw = edge (im, method, 0.2, "both", "nothinning"); %! args = perms ({0.2, "nothinning"}); %! for i = 1:rows (args) %! assert (edge (im, method, args{i,:}), bw) %! endfor %! endfor %!error %! bw = edge (rand (10), "sobel", 0.2, 0.4) %!error %! bw = edge (rand (10), "sobel", "thinning", "nothinning") %!error %! bw = edge (rand (10), "sobel", "both", "both") %!error %! bw = edge (rand (10), "sobel", [0.2 0.7], "both", "thinning") %!error %! bw = edge (rand (10), "kirsch", 0.2, 0.4) %!error %! bw = edge (rand (10), "kirsch", "thinning", "nothinning") %!error %! bw = edge (rand (10), "kirsch", "both", "both") %!error %! bw = edge (rand (10), "kirsch", [0.2 0.7], "both", "thinning") %!error %! bw = edge (rand (10), "prewitt", 0.2, 0.4) %!error %! bw = edge (rand (10), "prewitt", "thinning", "nothinning") %!error %! bw = edge (rand (10), "prewitt", "both", "both") %!error %! bw = edge (rand (10), "prewitt", [0.2 0.7], "both", "thinning") %!test %! im = [ %! 249 238 214 157 106 69 60 90 131 181 224 247 252 250 250 %! 250 242 221 165 112 73 62 91 133 183 225 248 252 250 251 %! 252 246 228 173 120 78 63 90 130 181 224 248 253 251 251 %! 253 248 232 185 132 87 62 80 116 170 217 244 253 251 252 %! 253 249 236 198 149 101 66 71 101 155 206 238 252 252 252 %! 254 250 240 210 164 115 73 69 92 143 196 232 252 253 252 %! 70 70 68 61 49 36 24 22 26 38 52 63 70 70 70 %! 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 %! 62 63 62 59 51 42 33 25 22 26 36 45 56 60 62 %! 252 253 252 246 221 190 157 114 90 90 118 157 203 235 248 %! 251 253 254 251 233 209 182 136 103 92 107 139 185 225 245 %! 251 253 254 253 243 227 206 163 128 108 110 133 175 217 242 %! 252 253 254 254 249 241 228 195 164 137 127 139 172 212 239 %! ] / 255; %! %! bw = edge (im, "roberts", .2, "thinning"); %! assert (edge (im, "roberts", 0.2), bw) %! assert (edge (im, "roberts", "thinning", 0.2), bw) %! %! bw = edge (im, "roberts", .2, "nothinning"); %! assert (edge (im, "roberts", "nothinning", 0.2), bw) %!error %! bw = edge (rand (10), "roberts", 0.2, 0.4) %!error %! bw = edge (rand (10), "roberts", "thinning", "nothinning") %!error %! bw = edge (rand (10), "roberts", "both", "thinning") ## test how canny threshold arguments are always a row vector %!test %! im = rand (10); %! [~, thresh] = edge (im, "canny"); %! assert (size (thresh), [1 2]) %! [~, thresh] = edge (im, "canny", [.2 .6]); %! assert (thresh, [.2 .6]) %! [~, thresh] = edge (im, "canny", [.2; .6]); %! assert (thresh, [.2 .6]) ## test SOBEL edge detector %!test %! in = zeros (5); %! in(3,3) = 1; %! %! E = logical ([ %! 0 0 0 0 0 %! 0 0 1 0 0 %! 0 1 0 1 0 %! 0 0 1 0 0 %! 0 0 0 0 0]); %! assert (edge (in), E) %! assert (edge (uint8 (in.*100)), E) %! assert (edge (in, "sobel"), E) %! assert (edge (in, "sobel", 0), E) %! assert (edge (in, "sobel", 1), false (5)) %! %! [E, auto_thresh] = edge (in); %! assert (auto_thresh, 0.2449, 1e-4) %! %! V = logical([ %! 0 0 0 0 0 %! 0 1 0 1 0 %! 0 1 0 1 0 %! 0 1 0 1 0 %! 0 0 0 0 0]); %! assert (edge (in, "sobel", 0, "vertical"), V) %! %! H = logical ([ %! 0 0 0 0 0 %! 0 1 1 1 0 %! 0 0 0 0 0 %! 0 1 1 1 0 %! 0 0 0 0 0]); %! assert (edge (in, "sobel", 0, "horizontal"), H) %! %! V = false (5); %! V(3,2) = true; %! V(3,4) = true; %! assert (edge (in, "sobel", [], "vertical"), V) %! %! H = false (5); %! H(2,3) = true; %! H(4,3) = true; %! assert (edge (in, "sobel", [], "horizontal"), H) %!test %! A = ones (5); %! A(3, 3) = 0; %! expected = logical ([ %! 0 0 0 0 0 %! 0 0 1 0 0 %! 0 1 0 1 0 %! 0 0 1 0 0 %! 0 0 0 0 0]); %! assert (edge (A), expected) ## test PREWITT edge detector %!test %! in = zeros (5); %! in(3, 3) = 1; %! %! E = logical ([ %! 0 0 0 0 0 %! 0 1 0 1 0 %! 0 0 0 0 0 %! 0 1 0 1 0 %! 0 0 0 0 0]); %! %! assert (edge (in, "prewitt"), E) %! %! [~, auto_thresh] = edge (in, "prewitt"); %! assert (auto_thresh, 0.2309, 1e-4) %! %! V = logical([ %! 0 0 0 0 0 %! 0 1 0 1 0 %! 0 1 0 1 0 %! 0 1 0 1 0 %! 0 0 0 0 0]); %! assert (edge (in, "prewitt", 0, "vertical"), V) %! %! H = logical ([ %! 0 0 0 0 0 %! 0 1 1 1 0 %! 0 0 0 0 0 %! 0 1 1 1 0 %! 0 0 0 0 0]); %! assert (edge (in, "prewitt", 0, "horizontal"), H) ## test ROBERTS edge detector %!test %! in = zeros (5); %! in(3,3) = 1; %! in(3,4) = 0.9; %! %! E = logical ([ %! 0 0 0 0 0 %! 0 0 1 0 0 %! 0 0 1 0 0 %! 0 0 0 0 0 %! 0 0 0 0 0]); %! %! assert (edge (in, "roberts"), E) %! %! [~, auto_thresh] = edge (in, "roberts"); %! assert (auto_thresh, 0.6591, 1e-4) %! %! E45 = [0 0 0 0 0 %! 0 -0.5 -0.45 0 0 %! 0 0 0.50 0.45 0 %! 0 0 0 0 0 %! 0 0 0 0 0]; %! E135 = [0 0 0 0 0 %! 0 0 -0.50 -0.45 0 %! 0 0.5 0.45 0 0 %! 0 0 0 0 0 %! 0 0 0 0 0]; %! %! [~, ~, erg45, erg135] = edge (in, "roberts"); %! assert (erg45, E45) %! assert (erg135, E135) ## test CANNY edge detector %!xtest %! ## The edge image is correct and Matlab compatible so those should %! ## pass. However, the threshold values used to generate the edge %! ## image are not the same as Matlab. %! %! in_8 = fspecial ("gaussian", [8 8], 2); %! in_8 /= in_8(4,4); %! in_8_uint8 = im2uint8 (in_8); %! %! ## Matlab changed their implementation of the Canny method in %! ## release 2011a. We are compatible with their new implementation %! ## but for testing purposes, this is the expected result for the %! ## old implementation. %! out_8_old = logical ([ %! 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 0 0 0 %! 0 0 1 0 0 1 0 0 %! 0 1 0 0 0 0 1 0 %! 0 1 0 0 0 0 1 0 %! 0 0 1 0 0 1 0 0 %! 0 0 0 1 1 0 0 0 %! 0 0 0 0 0 0 0 0]); %! %! out_8 = logical ([ %! 0 0 0 0 0 0 0 0 %! 0 1 1 1 1 1 0 0 %! 0 1 0 0 0 1 0 0 %! 0 1 0 0 0 1 0 0 %! 0 1 0 0 0 1 0 0 %! 0 1 1 1 1 1 0 0 %! 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0]); %! out_thresh = [0.34375 0.859375]; %! %! [obs_edge, obs_thresh] = edge (in_8, "Canny"); %! assert (obs_edge, out_8) %! assert (obs_thresh, out_thresh) %! %! [obs_edge_givethresh, obs_thresh_givethresh] ... %! = edge (in_8, "Canny", out_thresh); %! assert (obs_edge_givethresh, out_8) %! assert (obs_thresh_givethresh, out_thresh) %! %! [obs_edge_uint8, obs_thresh_uint8] = edge (in_8_uint8, "Canny"); %! assert (obs_edge_uint8, out_8) %! assert (obs_thresh_uint8, out_thresh) %!xtest %! ## The edge image is correct and Matlab compatible so those should %! ## pass. However, the threshold values used to generate the edge %! ## image are not the same as Matlab. %! %! in_9 = fspecial ("gaussian", [9 9], 2); %! in_9 /= in_9(5,5); %! %! ## Matlab changed their implementation of the Canny method in %! ## release 2011a. We are compatible with their new implementation %! ## but for testing purposes, this is the expected result for the %! ## old implementation. %! out_9_old = logical ([ %! 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 %! 0 0 1 0 0 0 1 0 0 %! 0 0 1 0 0 0 1 0 0 %! 0 0 1 0 0 0 1 0 0 %! 0 0 0 1 1 1 0 0 0 %! 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0]); %! %! out_9 = logical ([ %! 0 0 0 0 0 0 0 0 0 %! 0 0 1 1 1 1 0 0 0 %! 0 1 1 0 0 1 1 0 0 %! 0 1 0 0 0 0 1 0 0 %! 0 1 0 0 0 0 1 0 0 %! 0 1 1 0 0 1 1 0 0 %! 0 0 1 1 1 1 0 0 0 %! 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0]); %! out_thresh = [0.35 0.875]; %! %! [obs_edge, obs_thresh] = edge (in_9, "Canny"); %! assert (obs_edge, out_9) %! assert (obs_thresh, out_thresh) %! %! [obs_edge_givethresh, obs_thresh_givethresh] ... %! = edge (in_9, "Canny", out_thresh); %! assert (obs_edge_givethresh, out_9) %! assert (obs_thresh_givethresh, out_thresh) image-2.16.1/inst/PaxHeaders.61586/imcomplement.m0000644000000000000000000000006215005110255016251 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imcomplement.m0000644000175000017500000000542315005110255017653 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imcomplement (@var{A}) ## Compute image complement or negative. ## ## Intuitively this corresponds to the intensity of bright and dark ## regions being reversed. The exact operation performed is dependent ## on the class of the image. ## ## @table @asis ## @item floating point ## Since floating point images are meant to have values in the range [0 1], ## this is equivalent @code{A -1}. This leads to values within the range ## to be inverted while others to stay equally distant from the limits. ## ## @item logical ## Equivalent to @code{! A} ## ## @item integer ## Inverts the values within the range of the data type. This is ## equivalent to @code{bitcmp (@var{A})}. ## ## @end table ## ## @seealso{imadd, imdivide, imlincomb, immultiply, imsubtract} ## @end deftypefn function B = imcomplement (A) if (nargin != 1) print_usage (); endif if (isfloat (A)) B = 1 - A; elseif (islogical (A)) B = ! A; elseif (isinteger (A)) if (intmin (class (A)) < 0) ## for signed integers, we use bitcmp B = bitcmp (A); else ## this is currently more efficient than bitcmp (but hopefully core ## will change) B = intmax (class (A)) - A; endif else error("imcomplement: A must be an image but is of class `%s'", class (A)); endif endfunction %!assert (imcomplement (10), -9); %!assert (imcomplement (single (10)), single (-9)); %!assert (imcomplement (0.2), 0.8); %!assert (imcomplement (uint8 (0)), uint8 (255)); %!assert (imcomplement (uint8 (1)), uint8 (254)); %!assert (imcomplement (uint16 (0)), uint16 (65535)); %!assert (imcomplement (uint16 (1)), uint16 (65534)); %!assert (imcomplement (int8 (-128)), int8 ( 127)); %!assert (imcomplement (int8 ( 127)), int8 (-128)); %!assert (imcomplement (int16 (-1)), int16 ( 0)); %!assert (imcomplement (int16 ( 0)), int16 (-1)); %!assert (imcomplement (int16 ( 1)), int16 (-2)); %!assert (imcomplement ([true false true]), [false true false]) %!error imcomplement ("not an image") image-2.16.1/inst/PaxHeaders.61586/imremap.m0000644000000000000000000000006215005110255015212 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imremap.m0000644000175000017500000002601315005110255016612 0ustar00avinoamavinoam00000000000000## Copyright (C) 2006 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{warped} = imremap (@var{im}, @var{XI}, @var{YI}) ## @deftypefnx{Function File} @var{warped} = imremap (@var{im}, @var{XI}, @var{YI}, @var{interp}, @var{extrapval}) ## @deftypefnx{Function File} @var{warped} = imremap (@var{im}, @var{XI}, @var{YI}, "bicubic", @var{padding}) ## Applies any geometric transformation to the image @var{im}. ## ## The arguments @var{XI} and @var{YI} are lookup tables that define the resulting ## image ## @example ## @var{warped}(y,x) = @var{im}(@var{YI}(y,x), @var{XI}(y,x)) ## @end example ## where @var{im} is assumed to be a continuous function, which is achieved ## by interpolation. Note that the image @var{im} is expressed in a (X, Y)-coordinate ## system and not a (row, column) system. ## ## The optional argument @var{method} defines the interpolation method to be ## used. All methods supported by @code{interp2} can be used. By default, the ## @code{linear} method is used. ## ## For @sc{matlab} compatibility, the methods @code{bicubic} (same as ## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as ## @code{linear}) are also supported. ## ## All values of the result that fall outside the original image will ## be set to @var{extrapval}. The default value of @var{extrapval} is 0. ## For bicubic interpolation it is possible to apply @var{padding} instead. ## Valid padding methods are: "replicate", "symmetric", "reflect", "circular". ## ## @seealso{imperspectivewarp, imrotate, imresize, imshear, interp2} ## @end deftypefn function [warped] = imremap (im, XI, YI, interp = "linear", extrapval = 0) interp = interp_method (interp); if (nargin < 3 || nargin > 5) print_usage (); elseif (! ((isnumeric (im) || islogical (im)) && ! issparse (im) && ! isempty (im)) || ndims (im) > 3) error ("imremap: IM must be a grayscale or RGB image.") elseif (! (size_equal (XI, YI) || (isvector (XI) && isvector (YI))) || ! ismatrix (XI) || ! isnumeric (XI)) error ("imremap: XI and YI must be matrices of the same size or vectors"); elseif (! ischar (interp)) error ("imremap: INTERP must be a string with interpolation method") elseif (! isscalar (extrapval) && ! (ischar (extrapval) && strcmp (interp, "cubic"))) error ("imremap: Specify a scalar EXTRAPVAL for constant padding or in case of bicubic interpolation a string for the PADDING method"); endif ## check if interpolation points are a meshgrid and reduce to vectors if possible if (! isvector (XI) && size_equal (XI, YI) && all (all (repmat (XI(1, :), [rows(XI), 1]) == XI & repmat (YI(:, 1), [1, columns(YI)]) == YI))) XI = XI(1, :); YI = YI(:, 1); endif ## if XI and YI are vectors, make sure XI is a row vector and YI a column vector for broadcasting if (iscolumn (XI) && ! isvector (im)) XI = XI'; endif if (isrow (YI) && ! isvector (im)) YI = YI'; endif ## for bicubic interpolation do not use interp2, but another implementation for Matlab compatibility if (strcmp (interp, "cubic")) padding = "symmetric"; if (ischar (extrapval)) padding = extrapval; endif ## interpolate warped = bicubic_conv (double (im), XI, YI, padding); if (isscalar (extrapval)) ## values got padded for smooth borders, but constant padding has been requested outside = (XI < 0.5) | (XI > columns (im) + 0.5) | ... (YI < 0.5) | (YI > rows (im) + 0.5); outside = repmat (outside, [1, 1, size(im,3)]); warped(outside) = extrapval; endif else sz = size (im); n_planes = prod (sz(3:end)); sz(1) = size (YI, 1); sz(2) = size (XI, 2); warped = zeros (sz); for i = 1:n_planes # 1-pixel image planes: if isscalar (im(:,:,i)) if (all (XI(:) == 1) && all (YI(:) == 1)) warped(:,:,i) = double (im(:,:,i)); else warped(:,:,i) = extrapval; endif # row image planes: elseif size(im, 1) == 1 if (all (YI(:) == 1 )) warped(:,:,i) = ones (sz(1), 1) * interp1 ([1:size(im,2)], ... double (im(1,:,i)), XI, interp, extrapval); else warped(:,:,i) = extrapval; endif # col image planes: elseif size(im, 2) == 1 if (all (XI(:) == 1)) warped(:,:,i) = interp1 ([1:size(im,1)], double (im(:,1,i)), YI, ... interp, extrapval) * ones (1, sz(2)); else warped(:,:,i) = extrapval; endif # 2d image planes: else warped(:,:,i) = interp2 (double (im(:,:,i)), XI, YI, interp, extrapval); endif endfor endif ## we return image on same class as input warped = cast (warped, class (im)); endfunction ## Cubic interpolation in 1d using a convolution kernel with a = -0.5 for MATLAB compatibility. Bicubic interpolation in interp2 is not MATLAB compatible. function w = cubic01 (d, a) ## requires: all (abs (d(:)) <= 1) absd = abs (d); w = (a+2) * absd.^3 - (a+3) * absd.^2 + 1; endfunction function w = cubic12 (d, a) ## requires all (1 < abs (d(:)) && abs (d(:)) <= 2) absd = abs (d); w = a * absd.^3 - 5*a * absd.^2 + 8*a * absd - 4*a; endfunction function p = intpolcub (I1, I2, I3, I4, D, a = -0.5) ## requires: all (0 <= D(:) && D(:) < 1) && a < 1 && size_equal(I1, I2) && size_equal(I1, I3) && size_equal(I1, I4) p = I1 .* cubic12 (-1-D, a) + I2 .* cubic01 (-D, a) + ... I3 .* cubic01 (1-D, a) + I4 .* cubic12 (2-D, a); endfunction ## padding by changing indices. Cannot mimic constant value padding, like zero padding function idx = pad_indices (i, sz, method = "symmetric") if strcmp (method, "replicate") idx = max (min (i, sz), 1); elseif strcmp (method, "symmetric") idx = i - 1; m = mod (idx, sz); odd = mod (floor (idx / sz), 2) == 1; idx(odd) = sz - m(odd); idx(!odd) = m(!odd) + 1; elseif strcmp (method, "reflect") idx = i - 1; while (any (idx(:) < 0 | idx(:) >= sz)) idx(idx < 0) = -idx(idx < 0); idx(idx >= sz) = 2*sz - 2 - idx(idx >= sz); endwhile idx += 1; elseif strcmp (method, "circular") idx = mod (i - 1, sz) + 1; else error (['Invalid argument for PADDING. Valid are "replicate", "symmetric", "reflect", "circular". You gave "', method, '"']) endif endfunction ## extract elements using meshgrid from 2d or 3d matrix function B = idx3 (A, Y, X) sz = size(A); if (length (sz) == 2) i = sub2ind (sz, Y, X); else X3 = repmat (X, [1, 1, sz(3)]); Y3 = repmat (Y, [1, 1, sz(3)]); ## Z is simply a 3d matrix, where the first channel is full of 1s, the second full of 2s, etc. Z3 = reshape (repelem (1:sz(3), size (X, 1) * size (X, 2), 1), ... [size(X,1), size(X,2), sz(3)]); i = sub2ind (sz, Y3, X3, Z3); endif B = A(i); endfunction ## bicubic interpolation using convolution kernel function out = bicubic_conv (img, XI, YI, padding = "symmetric") ## make padded indices for smooth borders K = floor(XI); DX = XI - K; K_1 = pad_indices (K - 1, columns (img), padding); K0 = pad_indices (K , columns (img), padding); K1 = pad_indices (K + 1, columns (img), padding); K2 = pad_indices (K + 2, columns (img), padding); L = floor(YI); DY = YI - L; L_1 = pad_indices (L - 1, rows (img), padding); L0 = pad_indices (L , rows (img), padding); L1 = pad_indices (L + 1, rows (img), padding); L2 = pad_indices (L + 2, rows (img), padding); if (isvector (XI) && !isvector (img)) ## rectilinear interpolation grid using vectors ## interpolate in y-direction out_y = intpolcub (img(L_1,:,:), img(L0,:,:), img(L1,:,:), img(L2,:,:), DY); ## interpolate in x-direction out = intpolcub (out_y(:,K_1,:), out_y(:,K0,:), out_y(:,K1,:), out_y(:,K2,:), DX); else ## meshgrid interpolation ## interpolate in y-direction at gridpoints out_y_1 = intpolcub (idx3 (img, L_1, K_1), idx3 (img, L0, K_1), idx3 (img, L1, K_1), idx3 (img, L2, K_1), DY); out_y0 = intpolcub (idx3 (img, L_1, K0), idx3 (img, L0, K0), idx3 (img, L1, K0), idx3 (img, L2, K0), DY); out_y1 = intpolcub (idx3 (img, L_1, K1), idx3 (img, L0, K1), idx3 (img, L1, K1), idx3 (img, L2, K1), DY); out_y2 = intpolcub (idx3 (img, L_1, K2), idx3 (img, L0, K2), idx3 (img, L1, K2), idx3 (img, L2, K2), DY); ## interpolate in x-direction out = intpolcub (out_y_1, out_y0, out_y1, out_y2, DX); endif endfunction %!demo %! ## Generate a synthetic image and show it %! I = tril(ones(100)) + abs(rand(100)); I(I>1) = 1; %! I(20:30, 20:30) = !I(20:30, 20:30); %! I(70:80, 70:80) = !I(70:80, 70:80); %! figure, imshow(I); %! ## Resize the image to the double size and show it %! [XI, YI] = meshgrid(linspace(1, 100, 200)); %! warped = imremap(I, XI, YI); %! figure, imshow(warped); %!demo %! ## Generate a synthetic image and show it %! I = tril(ones(100)) + abs(rand(100)); I(I>1) = 1; %! I(20:30, 20:30) = !I(20:30, 20:30); %! I(70:80, 70:80) = !I(70:80, 70:80); %! figure, imshow(I); %! ## Rotate the image around (0, 0) by -0.4 radians and show it %! [XI, YI] = meshgrid(1:100); %! R = [cos(-0.4) sin(-0.4); -sin(-0.4) cos(-0.4)]; %! RXY = [XI(:), YI(:)] * R; %! XI = reshape(RXY(:,1), [100, 100]); YI = reshape(RXY(:,2), [100, 100]); %! warped = imremap(I, XI, YI); %! figure, imshow(warped); %!test %! ## Test padding indirectly %! I = repmat([ 1 2 3 4 ], [4, 1]); %! xi = [-6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11]; %! exp_rep = [ 1 1 1 1 1 1 1 1 2 3 4 4 4 4 4 4 4 4]; %! exp_sym = [ 2 3 4 4 3 2 1 1 2 3 4 4 3 2 1 1 2 3]; %! exp_ref = [ 2 1 2 3 4 3 2 1 2 3 4 3 2 1 2 3 4 3]; %! exp_cir = [ 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3]; %! yi = 2.5; %! %! # rectilinear grid codepath %! assert (imremap (I, xi, yi, "bicubic", "replicate"), exp_rep); %! assert (imremap (I, xi, yi, "bicubic", "symmetric"), exp_sym); %! assert (imremap (I, xi, yi, "bicubic", "reflect"), exp_ref); %! assert (imremap (I, xi, yi, "bicubic", "circular"), exp_cir); %! %! # meshgrid codepath %! XI = [xi/2; xi; xi/2]; % cannot be reduced to vector, we will assert only middle row %! YI = repmat ([1.5; yi; 3.5], [1, length(xi)]); %! assert (imremap (I, XI, YI, "bicubic", "replicate")(2,:), exp_rep); %! assert (imremap (I, XI, YI, "bicubic", "symmetric")(2,:), exp_sym); %! assert (imremap (I, XI, YI, "bicubic", "reflect")(2,:), exp_ref); %! assert (imremap (I, XI, YI, "bicubic", "circular")(2,:), exp_cir); image-2.16.1/inst/PaxHeaders.61586/mmgradm.m0000644000000000000000000000006215005110255015204 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/mmgradm.m0000644000175000017500000000532515005110255016607 0ustar00avinoamavinoam00000000000000## Copyright (C) 2010, 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} mmgradm (@var{img}) ## @deftypefnx {Function File} {} mmgradm (@var{img}, @var{se_dil}) ## @deftypefnx {Function File} {} mmgradm (@var{img}, @var{se_dil}, @var{se_ero}) ## Perform morphological gradient. ## ## The matrix @var{img} must be numeric whose gradients is calculated, while ## @var{se_dil} and @var{se_ero} are the structuring elements for the dilation ## and erosion respectively. They can be a: ## @itemize @bullet ## @item ## strel object; ## @item ## array of strel objects as returned by `@@strel/getsequence'; ## @item ## matrix of 0's and 1's. ## @end itemize ## ## The @var{se_dil} and @var{se_ero} default to the elementary cross, i.e.: ## @example ## [ 0 1 0 ## 1 1 1 ## 0 1 0]; ## @end example ## ## The basic morphological gradient corresponds to a matrix erosion ## subtracted to its dilation, which is equivalent to: ## @example ## imdilate (img, se_dil) - imerode (img, se_ero) ## @end example ## ## To perform the half-gradients by erosion or dilation, or the internal or ## external gradients, simply pass an empty matrix as structuring element: ## @example ## mmgradm (img, [], se_ero) # half-gradient by erosion or internal gradient ## mmgradm (img, se_dil, []) # half-gradient by dilation or external gradient ## @end example ## ## @seealso{imerode, imdilate, imopen, imclose, imtophat, imbothat} ## @end deftypefn function grad = mmgradm (img, se_dil = strel ("diamond", 1), se_ero = strel ("diamond", 1)) ## This function does not exist in Matlab. It is meant to be compatible ## with the mmgradm function from the SDC morphology toolbox if (nargin < 1 || nargin > 3) print_usage (); elseif (! isimage (img)) error("imtophat: IMG must be a numeric matrix"); endif se_dil = prepare_strel ("mmgradm", se_dil); se_ero = prepare_strel ("mmgradm", se_ero); dilated = imdilate (img, se_dil); eroded = imerode (img, se_ero); if (islogical (img)) grad = dilated & ! eroded; else grad = dilated - eroded; endif endfunction image-2.16.1/inst/PaxHeaders.61586/colorgradient.m0000644000000000000000000000006215005110255016414 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/colorgradient.m0000644000175000017500000000300715005110255020012 0ustar00avinoamavinoam00000000000000## Author: Paul Kienzle ## This program is granted to the public domain. ## -*- texinfo -*- ## @deftypefn {Function File} {@var{M} =} colorgradient (@var{C}, @var{w}, @var{n}) ## Define a colour map which smoothly traverses the given colors. ## @var{C} contains the colours, one row per r,g,b value. ## @var{w}(i) is the relative length of the transition from colour i to colour i+1 ## in the entire gradient. The default is ones(rows(C)-1,1). ## n is the length of the colour map. The default is rows(colormap). ## ## E.g., ## @example ## colorgradient([0,0,1; 1,1,0; 1,0,0]) # blue -> yellow -> red ## x = linspace(0,1,200); ## imagesc(x(:,ones(30,1)))'; ## @end example ## @end deftypefn function ret = colorgradient (C, w, n) if nargin < 1 || nargin > 3 print_usage; endif if nargin == 1 n = rows(colormap); w = ones(length(C)-1,1); elseif nargin == 2 if (length(w) == 1) n = w; w = ones(rows(C)-1,1); else n = rows(colormap); endif endif if (length(w)+1 != rows(C)) error("must have one weight for each color interval"); endif w = 1+round((n-1)*cumsum([0;w(:)])/sum(w)); map = zeros(n,3); for i=1:length(w)-1 if (w(i) != w(i+1)) map(w(i):w(i+1),1) = linspace(C(i,1),C(i+1,1),w(i+1)-w(i)+1)'; map(w(i):w(i+1),2) = linspace(C(i,2),C(i+1,2),w(i+1)-w(i)+1)'; map(w(i):w(i+1),3) = linspace(C(i,3),C(i+1,3),w(i+1)-w(i)+1)'; endif endfor if nargout == 0 colormap(map); else ret = map; endif endfunction image-2.16.1/inst/PaxHeaders.61586/integralImage3.m0000644000000000000000000000006215005110255016413 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/integralImage3.m0000644000175000017500000000753115005110255020017 0ustar00avinoamavinoam00000000000000## Copyright (C) 2019 Avinoam Kalma ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} integralImage (@var{img}) ## Calculate the 3D integral image. ## ## @var{img} is the input image for 3D integral image calculation. ## ## The value of the 3D integral image is J = cumsum (cumsum (cumsum (I), 2), 3) ## with padding. ## The padding adds a zeros plane, zero rows and zero column, so size (J) = size (I) + 1. ## ## @seealso{integralImage, cumsum} ## @end deftypefn function J = integralImage3 (I) if (nargin != 1) print_usage (); endif if (! isimage (I)) error ("integralImage3: I should be an image"); endif if (ndims (I) > 3) error ("integralImage3: I should be a 3-dimensional image"); endif if (! isa (I, "double")) I = double (I); endif J = cumsum (cumsum (cumsum (I), 2), 3); J = padarray (J, [1 1 1], "pre"); endfunction %!test %! assert (integralImage3 (zeros (4)), zeros (5, 5, 2)); %!test %! J_res = zeros (2, 2, 2); %! J_res(2, 2, 2) = 10; %! assert (integralImage3 (10), J_res); %!test %! J = integralImage3 (10); %! assert (class (J), "double"); %! J = integralImage3 (uint8 (10)); %! assert (class (J), "double"); %!test %! I = [1, 2; 3, 4]; %! J = integralImage3 (I); %! J_res = zeros (3, 3, 2); %! J_res(2:3, 2:3, 2) = [1 3; 4 10]; %! assert (J, J_res) %!test %! I1 = [1, 2; 3, 4]; %! I2 = [5, 6; 7, 8]; %! I3 = [9, 10; 11, 12]; %! I = cat (3, I1, I2, I3); %! J = integralImage3 (I); %! J2 = [0 0 0; 0 1 3; 0 4 10]; %! J3 = [0 0 0; 0 6 14; 0 16 36]; %! J4 = [0 0 0; 0 15 33; 0 36 78]; %! J_res = cat (3, zeros (3), J2, J3, J4); %! assert (J, J_res) %!test %! I = magic (5); %! J = integralImage3 (I); %! J_res = zeros (6, 6, 2); %! J_res(:, :, 2) = [0 0 0 0 0 0; %! 0 17 41 42 50 65; %! 0 40 69 77 99 130; %! 0 44 79 100 142 195; %! 0 54 101 141 204 260; %! 0 65 130 195 260 325]; %! assert (J, J_res) %!# test of 3d input image: %!test %! K = magic (8); %! K = reshape (K, [4 4 4]); %! L = integralImage3 (K); %! L1_ML = zeros (5); %! L2_ML = [0 0 0 0 0; %! 0 64 96 98 132; %! 0 73 146 203 260; %! 0 90 212 316 388; %! 0 130 260 390 520]; %! L3_ML = [0 0 0 0 0; %! 0 67 134 197 260; %! 0 130 260 390 520; %! 0 193 386 583 780; %! 0 260 520 780 1040]; %! L4_ML = [0 0 0 0 0; %! 0 127 222 291 392; %! 0 203 406 593 780; %! 0 287 606 903 1168; %! 0 390 780 1170 1560]; %! L5_ML = [0 0 0 0 0; %! 0 134 268 394 520; %! 0 260 520 780 1040; %! 0 386 772 1166 1560; %! 0 520 1040 1560 2080]; %! L_ML = cat (3, L1_ML, L2_ML, L3_ML, L4_ML, L5_ML); %! assert (L, L_ML) %!# test of 2d input image: %!test %! X = ones (3); %! Y = integralImage3 (X); %! Y_ML = zeros (4, 4, 2); %! Y_ML(:, :, 2) = [0 0 0 0; 0 1 2 3; 0 2 4 6; 0 3 6 9]; %! assert(Y, Y_ML); %!error id=Octave:invalid-fun-call %! integralImage3 (); %!error id=Octave:invalid-fun-call %! integralImage3 (zeros (3), zeros (3)); %!error %! integralImage3 ("abcd"); %!error %! integralImage3 (1+i); %!error %! integralImage3 (reshape (1:81, 3, 3, 3, 3)); image-2.16.1/inst/PaxHeaders.61586/imextendedmax.m0000644000000000000000000000006215005110255016414 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imextendedmax.m0000644000175000017500000001354215005110255020017 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} @ imextendedmax (@var{im}, @var{h}) ## @deftypefnx {Function File} {} @ imextendedmax (@var{im}, @var{h}, @var{conn}) ## Caculate the (morphological) extended maxima of an image @var{im}. ## ## This function returns a binary image that marks the extended maxima ## of the input image @var{im}. Those extended maxima are definded as the ## regional maxima of the h-maximum transform of the input image (which removed ## all regional maxima of a height less then h beforehand). ## ## The input image @var{im} needs to be a real and nonsparse numeric array (of any dimension), ## and the height parameter @var{h} a non-negative scalar number. ## ## The definition of "neighborhood" for this morphological operation can be set ## with the connectivity parameter @var{conn}, ## which defaults to 8 for 2D images, to 26 for 3D images and to ## @code{conn(ndims(n), "maximal")} in general. @var{conn} can be given as scalar value ## or as a boolean matrix (see @code{conndef} for details). ## ## The output is a binary image of same shape as the input image @var{im}. ## ## @seealso{imextendedmin, imhmax, imregionalmax, imreconstruct} ## @end deftypefn ## Algorithm: ## * The 'classical' reference for this morphological "extended maximum" function ## is the book "Morphological Image Analysis" by P. Soille ## (Springer, 2nd edition, 2004), chapter 6.3.4 "Extended and h-extrema". ## It says: "The extended minima EMIN are definded as the regional minima ## of the corresponding h-minima transformation, the extended maxima ## EMAX being definded by duality: ## EMAX_h(f) = RMAX[HMAX_h(f)], ## EMIN_h(f) = RMIN[HMIN_h(f)].". ## * A more easily accessible reference is for example the following ## web page by Régis Clouard: ## https://clouard.users.greyc.fr/Pantheon/experiments/morphology/index-en.html#extremum ## It says: "The extended maxima EMAX are definded as the regional maxima of ## the corresponding h-maxima transformation: ## EMAX_h(f) = RMAX( HMAX_h(f) )" ## (We will call the grayscale image im instead of f.) function bw = imextendedmax (im, h, varargin) ## retrieve input parameters, set default value: if (nargin == 3) conn = varargin{1}; iptcheckconn (conn, "imextendedmax", "CONN"); elseif (nargin == 2) conn = conndef (ndims (im), "maximal"); else print_usage (); endif ## check input parameters: if (! isnumeric (im) || ! isreal (im) || issparse (im) ) error ("imextendedmax: IM must be a real and nonsparse numeric array"); endif if (! isnumeric (h) || ! isscalar (h) || ! isreal (h) || (h<0) ) error ("imextendedmax: H must be a non-negative scalar number"); endif ## do the actual calculation: bw = imregionalmax (imhmax (im, h, conn), conn); endfunction %!shared im0, bw0_h2_out %! im0 = uint8 ([0 0 0 0 0; %! 0 1 2 1 0; %! 0 2 5 2 0; %! 0 1 2 1 0; %! 0 0 0 0 0]); %! bw0_h2_out = false (5); %! bw0_h2_out(3,3) = true; ## test input syntax: %!error imextendedmax () %!error imextendedmax (im0) %!error imextendedmax ("hello", 2) %!error imextendedmax (i.*im0, 2) %!error imextendedmax (sparse (im0), 2) %!error imextendedmax (im0, -2) %!error imextendedmax (im0, 'a') %!error imextendedmax (im0, ones (2)) %!error imextendedmax (im0, 2*i) %!assert (imextendedmax (im0, 2), bw0_h2_out) %!assert (imextendedmax (double (im0), 2), bw0_h2_out) %!assert (imextendedmax (im0, 2, 8), bw0_h2_out) %!assert (imextendedmax (im0, 2, 4), bw0_h2_out) %!assert (imextendedmax (im0, 2, true (3)), bw0_h2_out) ## test output class and shape: %!test %! out = imextendedmax (im0, 2); %! assert (size (out), size (im0)) %! assert (class (out), "logical") %!test %! out = imextendedmax (single (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "logical") %!test %! out = imextendedmax (uint8 (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "logical") %!test %! out = imextendedmax (uint16 (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "logical") %!test %! im = cat (3, im0, im0, im0, im0); %! out = imextendedmax (im, 2); %! assert (size (out), size (im)) ## test calculation results: %!test %! im = zeros (10); %! im(2:4, 2:4) = 3; %! im(6:8, 6:8) = 8; %! expected_4 = false (10); %! expected_4(6:8, 6:8) = true; %! expected_2 = expected_4; %! expected_2(2:4, 2:4) = true; %! out = imextendedmax (im, 4); %! assert (out, expected_4, eps) %! out = imextendedmax (0.1.*im, 0.4); %! assert (out, expected_4, eps) %! out = imextendedmax (im, 2); %! assert (out, expected_2, eps) %!test %! im2 = zeros (10); %! im2(2:4, 2:4) = 3; %! im2(6:9, 6:9)=8; %! im2(5, 5)=8; %! im2(6, 7)=0; %! im2(7, 8)=0; %! expected_8 = false (10); %! expected_8(6:9, 6:9) = true; %! expected_8(5, 5) = true; %! expected_8(6, 7) = false; %! expected_8(7, 8) = false; %! expected_4 = expected_8; %! expected_4(2:4, 2:4) = true; %! out2 = imextendedmax (im2, 2); %! assert (out2, expected_8, eps) %! out2 = imextendedmax (im2, 2, 4); %! assert (out2, expected_4, eps) %! out2 = imextendedmax (im2, 2, 8); %! assert (out2, expected_8, eps) image-2.16.1/inst/PaxHeaders.61586/fspecial.m0000644000000000000000000000006215005110255015346 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/fspecial.m0000644000175000017500000005051715005110255016754 0ustar00avinoamavinoam00000000000000## Copyright (C) 2005 Søren Hauberg ## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} fspecial (@var{type}, @dots{}) ## Create spatial filters for image processing. ## ## @var{type} is a string specifying the filter name. The input arguments ## that follow are type specific. The return value is a correlation kernel, ## often to be used by @code{imfilter}. ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("average") ## @deftypefnx {Function File} {} fspecial ("average", @var{lengths}) ## Rectangular averaging filter. ## ## The optional argument @var{lengths} controls the size of the filter. ## If @var{lengths} 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. ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("disk") ## @deftypefnx {Function File} {} fspecial ("disk", @var{radius}) ## Circular averaging filter. ## ## The optional argument @var{radius} controls the ## radius of the filter. If @var{radius} 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 centred at the middle of the element @var{R}+1,@var{R}+1. ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("gaussian") ## @deftypefnx {Function File} {} fspecial ("gaussian", @var{lengths}) ## @deftypefnx {Function File} {} fspecial ("gaussian", @var{lengths}, @var{sigma}) ## Create Gaussian filter. ## ## Returns a N dimensional Gaussian distribution with standard ## deviation @var{sigma} and centred in an array of size @var{lengths}. ## ## @var{lengths} defaults to @code{[3 3]} and @var{sigma} to 0.5. ## If @var{lengths} is a scalar, it returns a square matrix of side ## @var{lengths}, .i.e., its value defines both the number of rows and ## columns. ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("log") ## @deftypefnx {Function File} {} fspecial ("log", @var{lengths}) ## @deftypefnx {Function File} {} fspecial ("log", @var{lengths}, @var{std}) ## Laplacian of Gaussian. ## ## The optional argument @var{lengths} controls the size of the ## filter. If @var{lengths} 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{std} sets spread of the filter. By default ## a spread of @math{0.5} is used. ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("laplacian") ## @deftypefnx {Function File} {} fspecial ("laplacian", @var{alpha}) ## 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 ]; ## @end example ## ## where @var{alpha} is a number between 0 and 1. By default it is @math{0.2}. ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("unsharp") ## @deftypefnx {Function File} {} fspecial ("unsharp", @var{alpha}) ## 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}]; ## @end example ## ## where @var{alpha} is a number between 0 and 1. By default it is @math{0.2}. ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("motion") ## @deftypefnx {Function File} {} fspecial ("motion", @var{lengths}) ## @deftypefnx {Function File} {} fspecial ("motion", @var{lengths}, @var{angle}) ## Motion blur filter of width 1 pixel. ## ## The optional input argument @var{lengths} ## controls the length of the filter, which by default is 9. The argument @var{angle} ## controls the angle of the filter, which by default is 0 degrees. ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("sobel") ## Horizontal Sobel edge filter. ## ## The following filter is returned ## ## @example ## [ 1 2 1 ## 0 0 0 ## -1 -2 -1 ] ## @end example ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("prewitt") ## Horizontal Prewitt edge filter. ## ## The following filter is returned ## ## @example ## [ 1 1 1 ## 0 0 0 ## -1 -1 -1 ] ## @end example ## ## @end deftypefn ## ## @deftypefn {Function File} {} fspecial ("kirsch") ## Horizontal Kirsch edge filter. ## ## The following filter is returned ## ## @example ## [ 3 3 3 ## 3 0 3 ## -5 -5 -5 ] ## @end example ## ## @seealso{conv2, convn, filter2, imfilter} ## ## @end deftypefn ## Remarks by Søren Hauberg (jan. 2nd 2007) ## The motion filter and most of the documentation was taken from Peter Kovesi's ## GPL'ed implementation of fspecial from ## http://www.csse.uwa.edu.au/~pk/research/matlabfns/OctaveCode/fspecial.m function f = fspecial (type, arg1, arg2) if (nargin < 1) print_usage (); endif switch lower (type) case "average" if (nargin > 2) print_usage (); elseif (nargin == 1) fsize = [3 3]; elseif (! isreal (arg1) || isempty (arg1) || ! isvector (arg1) || any (arg1 < 0) || any (arg1 != fix (arg1))) error ("fspecial: LENGTHS must be a vector of non-negative integers"); elseif (isscalar (arg1)) fsize = [arg1 arg1]; else fsize = arg1; endif val = 1 ./ prod (fsize); f = repmat (val, fsize); case "disk" ## fspecial ("disk", radius = [5]) if (nargin == 1) r = 5; elseif (isreal (arg1) && isscalar (arg1)) r = arg1; else error ("fspecial: RADIUS for disk must be a real scalar"); endif 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" ## fspecial ("gaussian", lengths = [3 3], sigma = 0.5) if (nargin < 2) lengths = [3 3]; else validateattributes (arg1, {"numeric"}, {">", 0, "integer"}, "fspecial (\"gaussian\")", "LENGTHS"); if (isempty (arg1)) error ("fspecial (\"gaussian\"): LENGTHS must not be empty"); elseif (numel (arg1) == 1) lengths = [arg1 arg1]; else lengths = arg1(:).'; endif endif if (nargin < 3) sigma = 0.5; else ## TODO add support for different sigmas for each dimension validateattributes (arg2, {"numeric"}, {">", 0, "scalar"}, "fspecial (\"gaussian\")", "SIGMA"); sigma = arg2; endif lengths -= 1; lengths /= 2; pos = arrayfun ("colon", -lengths, lengths, "uniformoutput", false); dist = 0; for d = 1:numel(lengths) dist = dist + (vec (pos{d}, d) .^2); endfor f = exp (- (dist) / (2 * (sigma.^2))); f /= sum (f(:)); case "laplacian" ## Get alpha if (nargin > 1 && isscalar (arg1)) alpha = arg1; if (alpha < 0 || alpha > 1) error ("fspecial: second argument must be between 0 and 1"); endif else alpha = 0.2; endif ## Compute filter f = (4/(alpha+1))*[alpha/4, (1-alpha)/4, alpha/4; ... (1-alpha)/4, -1, (1-alpha)/4; ... alpha/4, (1-alpha)/4, alpha/4]; case "log" ## Get hsize if (nargin > 1 && isreal (arg1)) if (length (arg1 (:)) == 1) hsize = [arg1, arg1]; elseif (length (arg1 (:)) == 2) hsize = arg1; else error ("fspecial: second argument must be a scalar or a vector of two scalars"); endif else hsize = [5, 5]; endif ## Get sigma if (nargin > 2 && isreal (arg2) && length (arg2 (:)) == 1) sigma = arg2; else sigma = 0.5; endif ## Compute the filter h1 = hsize (1)-1; h2 = hsize (2)-1; [x, y] = meshgrid(0:h2, 0:h1); x = x-h2/2; y = y = y-h1/2; gauss = exp( -( x.^2 + y.^2 ) / (2*sigma^2) ); f = ( (x.^2 + y.^2 - 2*sigma^2).*gauss )/( 2*pi*sigma^6*sum(gauss(:)) ); case "motion" ## Taken (with some changes) from Peter Kovesis implementation ## (http://www.csse.uwa.edu.au/~pk/research/matlabfns/OctaveCode/fspecial.m) ## FIXME: The implementation is not quite matlab compatible. if (nargin > 1 && isreal (arg1)) len = arg1; else len = 9; endif if (mod (len, 2) == 1) sze = [len, len]; else sze = [len+1, len+1]; end if (nargin > 2 && isreal (arg2)) angle = arg2; else angle = 0; endif ## First generate a horizontal line across the middle f = zeros (sze); f (floor (len/2)+1, 1:len) = 1; # Then rotate to specified angle f = imrotate (f, angle, "bilinear", "loose"); f = f / sum (f (:)); case "prewitt" ## The filter f = [1, 1, 1; 0, 0, 0; -1, -1, -1]; case "sobel" ## The filter f = [1, 2, 1; 0, 0, 0; -1, -2, -1]; case "kirsch" ## The filter f = [3, 3, 3; 3, 0, 3; -5, -5, -5]; case "unsharp" ## Get alpha if (nargin > 1 && isscalar (arg1)) alpha = arg1; if (alpha < 0 || alpha > 1) error ("fspecial: second argument must be between 0 and 1"); endif else alpha = 0.2; endif ## Compute filter f = (1/(alpha+1))*[-alpha, alpha-1, -alpha; ... alpha-1, alpha+5, alpha-1; ... -alpha, alpha-1, -alpha]; otherwise error ("fspecial: filter type '%s' is not supported", type); endswitch endfunction ## ## Tests for disk shape ## ## 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 ## ## Tests for gaussian shape ## %!error %! fspecial ("gaussian", 0) %!error %! fspecial ("gaussian", 3.9) %!assert (fspecial ("gaussian"), fspecial ("gaussian", 3, 0.5)) %!assert (fspecial ("gaussian"), fspecial ("gaussian", [3 3], 0.5)) %!test %! c = ([-1:1].^2) + ([-1:1]'.^2); %! gauss = exp (- (c / (2 * (0.5 ^ 2)))); %! f = gauss / sum (gauss(:)); %! assert (fspecial ("gaussian"), f) %! %! expected = [ %! 0.01134373655849507 0.08381950580221061 0.01134373655849507 %! 0.08381950580221061 0.61934703055717721 0.08381950580221061 %! 0.01134373655849507 0.08381950580221061 0.01134373655849507]; %! assert (f, expected, eps) ## An implementation of the function for 2d, we must also check it ## against some of the values. Note that hsize is (radius -1) and ## only works for odd lengths. %!function f = f_gaussian_2d (hsize, sigma) %! c = ([(-hsize(1)):(hsize(1))]'.^2) + ([(-hsize(2)):(hsize(2))].^2); %! gauss = exp (- (c ./ (2 * (sigma .^ 2)))); %! f = gauss ./ sum (gauss(:)); %!endfunction %!test %! f = fspecial ("gaussian"); %! assert (f, f_gaussian_2d ([1 1], .5)) %! expected = [ %! 0.01134373655849507 0.08381950580221061 0.01134373655849507 %! 0.08381950580221061 0.61934703055717721 0.08381950580221061 %! 0.01134373655849507 0.08381950580221061 0.01134373655849507]; %! assert (f, expected, eps) %!test %! f = fspecial ("gaussian", 7, 2); %! assert (f, f_gaussian_2d ([3 3], 2)) %! expected = [ %! 0.00492233115934352 %! 0.00919612528958620 %! 0.01338028334410124 %! 0.01516184737296414 %! 0.01338028334410124 %! 0.00919612528958620 %! 0.00492233115934352 %! 0.00919612528958620 %! 0.01718062389630964 %! 0.02499766026691484 %! 0.02832606006174462 %! 0.02499766026691484 %! 0.01718062389630964 %! 0.00919612528958620 %! 0.01338028334410124 %! 0.02499766026691484 %! 0.03637138107390363 %! 0.04121417419979795 %! 0.03637138107390363 %! 0.02499766026691484 %! 0.01338028334410124 %! 0.01516184737296414 %! 0.02832606006174462 %! 0.04121417419979795 %! 0.04670177773892775]; %! expected = reshape ([expected; expected((end-1):-1:1)], [7 7]); %! assert (f, expected, eps) %!test %! f = fspecial ("gaussian", [7 5], 2); %! assert (f, f_gaussian_2d ([3 2], 2)) %! expected = [ %! 0.01069713252648568 %! 0.01998487459872362 %! 0.02907782096336423 %! 0.03294948784319031 %! 0.02907782096336423 %! 0.01998487459872362 %! 0.01069713252648568 %! 0.01556423598706978 %! 0.02907782096336423 %! 0.04230797985750011 %! 0.04794122192790870 %! 0.04230797985750011 %! 0.02907782096336423 %! 0.01556423598706978 %! 0.01763658993191515 %! 0.03294948784319031 %! 0.04794122192790870 %! 0.05432452146574315]; %! expected = reshape ([expected; expected((end-1):-1:1)], [7 5]); %! assert (f, expected, eps) %!test %! f = fspecial ("gaussian", [4 2], 2); %! expected = [0.10945587477855045 0.14054412522144952]; %! expected = expected([1 1; 2 2; 2 2; 1 1]); %! assert (f, expected, eps) %!test %! expected =[0.04792235409415088 0.06153352068439959 0.07901060453704994]; %! expected = expected([1 2 2 1; 2 3 3 2; 2 3 3 2; 1 2 2 1]); %! assert (fspecial ("gaussian", 4, 2), expected) %!function f = f_gaussian_3d (lengths, sigma) %! [x, y, z] = ndgrid (-lengths(1):lengths(1), -lengths(2):lengths(2), %! -lengths(3):lengths(3)); %! sig_22 = 2 * (sigma.^2); %! f = exp (-((x.^2)/sig_22 + (y.^2)/sig_22 + (z.^2)/sig_22)); %! f = f / sum (f(:)); %!endfunction %!test %! obs = fspecial ("gaussian", [5 5 5]); %! assert (obs, f_gaussian_3d ([2 2 2], .5)) %! %! u_values = [ %! 0.00000000001837155 %! 0.00000000741161178 %! 0.00000005476481523 %! 0.00000299005759843 %! 0.00002209370333384 %! 0.00016325161336690 %! 0.00120627532940896 %! 0.00891323607975882 %! 0.06586040141635063 %! 0.48664620076350640]; %! expected = zeros (5, 5, 5); %! expected([1 5 21 25 101 105 121 125]) = u_values(1); %! expected([2 4 6 10 16 20 22 24 26 30 46 50 76 80 96 100 102 104 106 110 116 120 122 124]) = u_values(2); %! expected([3 11 15 23 51 55 71 75 103 111 115 123]) = u_values(3); %! expected([7 9 17 19 27 29 31 35 41 45 47 49 77 79 81 85 91 95 97 99 107 109 117 119]) = u_values(4); %! expected([8 12 14 18 28 36 40 48 52 54 56 60 66 70 72 74 78 86 90 98 108 112 114 118]) = u_values(5); %! expected([13 53 61 65 73 113]) = u_values(6); %! expected([32 34 42 44 82 84 92 94]) = u_values(7); %! expected([33 37 39 43 57 59 67 69 83 87 89 93]) = u_values(8); %! expected([38 58 62 64 68 88]) = u_values(9); %! expected([63]) = u_values(10); %! assert (obs, expected, 4 * eps) %!test %! obs = fspecial ("gaussian", [5 5 5], 1); %! assert (obs, f_gaussian_3d ([2 2 2], 1)) %! %! u_values = [ %! 0.00016177781678373 %! 0.00072503787330278 %! 0.00119538536377748 %! 0.00324939431236223 %! 0.00535734551968363 %! 0.00883276951279243 %! 0.01456277497493249 %! 0.02400995686159072 %! 0.03958572658629712 %! 0.06526582943894763]; %! expected = zeros (5, 5, 5); %! expected([1 5 21 25 101 105 121 125]) = u_values(1); %! expected([2 4 6 10 16 20 22 24 26 30 46 50 76 80 96 100 102 104 106 110 116 120 122 124]) = u_values(2); %! expected([3 11 15 23 51 55 71 75 103 111 115 123]) = u_values(3); %! expected([7 9 17 19 27 29 31 35 41 45 47 49 77 79 81 85 91 95 97 99 107 109 117 119]) = u_values(4); %! expected([8 12 14 18 28 36 40 48 52 54 56 60 66 70 72 74 78 86 90 98 108 112 114 118]) = u_values(5); %! expected([13 53 61 65 73 113]) = u_values(6); %! expected([32 34 42 44 82 84 92 94]) = u_values(7); %! expected([33 37 39 43 57 59 67 69 83 87 89 93]) = u_values(8); %! expected([38 58 62 64 68 88]) = u_values(9); %! expected([63]) = u_values(10); %! assert (obs, expected, eps) %!test %! obs = fspecial ("gaussian", [3 4 1 5], 3); %! assert (find (obs == max (obs(:))), [29; 32]) %! assert (size (obs), [3 4 1 5]) %! assert (obs(:)(1:30), obs(:)(end:-1:31)) %!test %! f = repmat (1/9, [3 3]); %! assert (fspecial ("average", [3 3]), f) %! %! ## Test default %! assert (fspecial ("average"), fspecial ("average", [3 3])) %! assert (fspecial ("average"), fspecial ("average", [3])) %! %! f = repmat (1/21, [3 7]); %! assert (fspecial ("average", [3 7]), f) %! %! f = repmat (1/40, [4 5 1 2]); %! assert (fspecial ("average", [4 5 1 2]), f) %! ## Behave even if it's a column vector %! assert (fspecial ("average", [4 5 1 2]'), f) image-2.16.1/inst/PaxHeaders.61586/ntsc2rgb.m0000644000000000000000000000006215005110255015304 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/ntsc2rgb.m0000644000175000017500000001440415005110255016705 0ustar00avinoamavinoam00000000000000## Copyright (C) 1994-2017 John W. Eaton ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {} {@var{rgb_map} =} ntsc2rgb (@var{yiq_map}) ## @deftypefnx {} {@var{rgb_img} =} ntsc2rgb (@var{yiq_img}) ## Transform a colormap or image from luminance-chrominance (NTSC) space to ## red-green-blue (RGB) color space. ## ## Implementation Note: ## The conversion matrix is chosen to be the inverse of the matrix used for ## rgb2ntsc such that ## ## @example ## x == ntsc2rgb (rgb2ntsc (x)) ## @end example ## ## @sc{matlab} uses a slightly different matrix where rounding means the ## equality above does not hold. ## @seealso{rgb2ntsc, hsv2rgb, ind2rgb} ## @end deftypefn function rgb = ntsc2rgb (yiq) if (nargin != 1) print_usage (); endif ## Unlike other colorspace conversion functions, we do not accept ## integers as valid input. We check this before ## colorspace_conversion_input_check() which is general and would ## convert integers to double assuming a [0 1] interval range. ## The reason for not supporting integers here is that there's no ## common such conversion. If we were to support a conversion ## the most reasonable definition would be to convert the YIQ ## from their integer range into the ranges: ## Y = [ 0 1.106] ## I = [-0.797 0.587] ## Q = [-0.322 0.426] ## See https://savannah.gnu.org/patch/?8709#comment11 if (! isfloat (yiq)) error ("ntsc2rgb: YIQ must be of floating point class"); endif [yiq, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check ("ntsc2rgb", "YIQ", yiq, true); ## Conversion matrix constructed from 'inv (rgb2ntsc matrix)'. ## See programming notes in rgb2ntsc.m. Note: Matlab matrix for inverse ## is slightly different. We prefer this matrix so that ## x == ntsc2rgb (rgb2ntsc (x)) rather than maintaining strict compatibility ## with Matlab. trans = [ 1.0, 1.0, 1.0; 0.95617, -0.27269, -1.10374; 0.62143, -0.64681, 1.70062 ]; rgb = yiq * trans; ## Note that if the input is of class single, we also return an image ## of class single. This is Matlab incompatible by design, since ## Matlab always returning class double, is a Matlab bug (see patch #8709) ## truncating / scaling of double rgb values for Matlab compatibility rgb = max (0, rgb); idx = any (rgb > 1, 2); rgb(idx,:) = rgb(idx,:) ./ max (rgb(idx,:), [], 2); rgb = colorspace_conversion_revert (rgb, cls, sz, is_im, is_nd, is_int, false); endfunction %!shared trans %! trans = [ 1.0, 1.0, 1.0; %! 0.95617, -0.27269, -1.10374; %! 0.62143, -0.64681, 1.70062 ]; ## Test pure R, G, B colors %!assert (ntsc2rgb ([.299 .596 .211]), [1 0 0], 1e-5) %!assert (ntsc2rgb ([.587 -.274 -.523]), [0 1 0], 1e-5) %!assert (ntsc2rgb ([.114 -.322 .312]), [0 0 1], 1e-5) %!test %! rgb_map = rand (64, 3); %! assert (ntsc2rgb (rgb2ntsc (rgb_map)), rgb_map, 1e-3); %!test %! rgb_img = rand (64, 64, 3); %! assert (ntsc2rgb (rgb2ntsc (rgb_img)), rgb_img, 1e-3); ## test cropping of rgb output %!assert (ntsc2rgb ([1.5 0 0]), [1 1 1]) ## Test scaling of output. After conversion, cut of negative values ## and scaling of all the others relative to the maximum above 1. %!test %! ntsc = [0.4229 0.0336 0.7184]; %! rgb = ntsc * trans; # [0.9014 -0.0509 1.6075] %! rgb(1) /= rgb(3); # scaled based on the maximum %! rgb(2) = 0; # cut to 0 %! rgb(3) = 1; # cut to 1 %! assert (ntsc2rgb (ntsc), rgb); ## test scaling when conversion has more than one value above 1 ## (check that it does pick the maximum) %!test %! ntsc = [0.8229 0.3336 0.7184]; %! rgb = ntsc * trans; # [1.58831 0.26726 1.67642] %! rgb /= rgb(3); %! assert (ntsc2rgb (ntsc), rgb); ## check scaling for more than 1 row %!test %! ntsc = [0.4229 0.0336 0.7184 %! 0.8229 0.3336 0.7184]; %! rgb = ntsc * trans; # [0.9014 -0.0509 1.6075; 1.58831 0.26726 1.67642] %! rgb(1,1) /= rgb(1,3); %! rgb(1,2) = 0; %! rgb(1,3) = 1; %! rgb(2,:) /= rgb(2,3); %! assert (ntsc2rgb (ntsc), rgb); ## Test input validation %!error ntsc2rgb () %!error ntsc2rgb (1,2) %!error ntsc2rgb (uint8 (1)) %!error ntsc2rgb (ones (2,2)) %!error ntsc2rgb (ones ([10 10 3], "uint8")) %!error ntsc2rgb (ones ([10 10 3], "uint16")) %!error ntsc2rgb (ones ([10 10 3], "int16")) ## Test ND input %!test %! yiq = rand (16, 16, 3, 5); %! rgb = zeros (size (yiq)); %! for i = 1:5 %! rgb(:,:,:,i) = ntsc2rgb (yiq(:,:,:,i)); %! endfor %! assert (ntsc2rgb (yiq), rgb); ## Test output class and size for input images. ## Most of the tests only test for colormap input. %!test %! rgb = ntsc2rgb (rand (10, 10, 3)); %! assert (class (rgb), "double"); %! assert (size (rgb), [10 10 3]); %!test %! rgb = ntsc2rgb (rand (10, 10, 3, "single")); %! assert (class (rgb), "single"); %! assert (size (rgb), [10 10 3]); %!test %! ntsc = (rand (10, 10, 3) * 3 ) - 0.5; # values outside range [0 1] %! rgb = ntsc2rgb (ntsc); %! assert (class (rgb), "double"); %! assert (size (rgb), [10 10 3]); %!test %! ntsc = (rand (10, 10, 3, "single") * 3 ) - 0.5; # values outside range [0 1] %! rgb = ntsc2rgb (ntsc); %! assert (class (rgb), "single"); %! assert (size (rgb), [10 10 3]); %!test %! ntsc_double = reshape ([.299 .587 .114 0 .596 -.274 -.322 0 .211 -.523 .312 0], %! [2 2 3]); %! expected = reshape ([1 0 0 0 0 1 0 0 0 0 1 0], [2 2 3]); %! %! assert (ntsc2rgb (ntsc_double), expected, 1e-5); %! assert (ntsc2rgb (single (ntsc_double)), single (expected), 1e-5); image-2.16.1/inst/PaxHeaders.61586/bwboundaries.m0000644000000000000000000000006215005110255016244 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwboundaries.m0000644000175000017500000000733515005110255017652 0ustar00avinoamavinoam00000000000000## Copyright (C) 2010 Søren Hauberg ## Copyright (C) Andrew Kelly, IPS Radio & Space Services ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{boundaries} = } bwboundaries(@var{BW}) ## @deftypefnx {Function File} {@var{boundaries} = } bwboundaries(@var{BW}, @var{conn}) ## @deftypefnx {Function File} {@var{boundaries} = } bwboundaries(@var{BW}, @var{conn}, @var{holes}) ## @deftypefnx {Function File} {[@var{boundaries}, @var{labels}] = } bwboundaries(@dots{}) ## @deftypefnx {Function File} {[@var{boundaries}, @var{labels}, @var{num_labels}] = } bwboundaries(@dots{}) ## Trace the boundaries of the objects in a binary image. ## ## @var{boundaries} is a cell array in which each element is the boundary of an ## object in the binary image @var{BW}. The clockwise boundary of each object is ## computed by the @code{boundary} function. ## ## By default the boundaries are computed using 8-connectivity. This can be ## changed to 4-connectivity by setting @var{conn} to 4. ## ## By default @code{bwboundaries} computes all boundaries in the image, i.e. ## both interior and exterior object boundaries. This behaviour can be changed ## through the @var{holes} input argument. If this is @t{'holes'}, ## both boundary types are considered. If it is instead @t{'noholes'}, only exterior ## boundaries will be traced. ## ## If two or more output arguments are requested, the algorithm also returns ## the labelled image computed by @code{bwlabel} in @var{labels}. The number ## of labels in this image is optionally returned in @var{num_labels}. ## @seealso{bwlabel, bwlabeln, bwperim, fchcode} ## @end deftypefn function [bound, labels, num_labels] = bwboundaries (bw, conn=8, holes="holes") # check arguments if (nargin < 1) error ("bwboundaries: not enough input arguments"); endif if (! ismatrix (bw)) error ("bwboundaries: first input argument must be a NxM matrix"); endif if (!isscalar (conn) || (conn != 4 && conn != 8)) error ("bwboundaries: second input argument must be 4 or 8"); endif if (!ischar (holes) || !any (strcmpi (holes, {'holes', 'noholes'}))) error ("bwboundaries: third input must be either \'holes\' or \'noholes\'"); endif bw = logical (bw); # process each connected region separately [labels, num_labels] = bwlabel (bw, conn); bound = cell (num_labels, 1); for k = 1:num_labels bound {k} = __boundary__ ((labels == k), conn); endfor # compute internal boundaries as well? if (strcmpi (holes, "holes")) filled = bwfill (bw, "holes", conn); holes = (filled & !bw); [intBounds, intLabels, numIntLabels] = bwboundaries (holes, conn, "noholes"); bound (end+1 : end+numIntLabels, 1) = intBounds; intLabels (intLabels != 0) += num_labels; labels += intLabels; endif endfunction %!demo %! ## Generate a simple image %! bw = false (100); %! bw (10:30, 40:80) = true; %! bw (40:45, 40:80) = true; %! %! ## Find boundaries %! bounds = bwboundaries (bw); %! %! ## Plot result %! imshow (bw); %! hold on %! for k = 1:numel (bounds) %! plot (bounds {k} (:, 2), bounds {k} (:, 1), 'r', 'linewidth', 2); %! endfor %! hold off image-2.16.1/inst/PaxHeaders.61586/houghtf.m0000644000000000000000000000006215005110255015224 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/houghtf.m0000644000175000017500000000760715005110255016634 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{H} = houghtf (@var{bw}) ## @deftypefnx{Function File} @var{H} = houghtf (@var{bw}, @var{method}) ## @deftypefnx{Function File} @var{H} = houghtf (@var{bw}, @var{method}, @var{arg}) ## Perform the Hough transform for lines or circles. ## ## The @var{method} argument chooses between the Hough transform for lines and ## circles. It can be either "line" (default) or "circle". ## ## @strong{Line Detection} ## ## If @var{method} is "line", the function will compute the Hough transform for ## lines. A line is parametrised in @var{r} and @var{theta} as ## @example ## @var{r} = x*cos(@var{theta}) + y*sin(@var{theta}), ## @end example ## where @var{r} is distance between the line and the origin, while @var{theta} ## is the angle of the vector from the origin to this closest point. The result ## @var{H} is an @var{N} by @var{M} matrix containing the Hough transform. Here, ## @var{N} is the number different values of @var{r} that has been attempted. ## This is computed as @code{2*diag_length - 1}, where @code{diag_length} is ## the length of the diagonal of the input image. @var{M} is the number of ## different values of @var{theta}. These can be set through the third input ## argument @var{arg}. This must be a vector of real numbers, and is by default ## @code{pi*(-90:90)/180}. ## ## @strong{Circle Detection} ## ## If @var{method} is "circle" the function will compute the Hough transform for ## circles. The circles are parametrised in @var{r} which denotes the radius of ## the circle. The third input argument @var{arg} must be a real vector containing ## the possible values of @var{r}. ## If the input image is @var{N} by @var{M}, then the result @var{H} will be an ## @var{N} by @var{M} by @var{K} array, where @var{K} denotes the number of ## different values of @var{r}. ## ## As an example, the following shows how to compute the Hough transform for circles ## with radius 3 or 7 in the image @var{im} ## @example ## bw = edge(im); ## H = houghtf(bw, "circle", [3, 7]); ## @end example ## Here @var{H} will be an NxMx2 array, where @var{H}(:,:,1) will contain the ## Hough transform for circles with radius 3, and @var{H}(:,:,2) for radius 7. ## To find good circles you now need to find local maximas in @var{H}. If you ## find a local maxima in @var{H}(row, col, 1) it means that a good circle exists ## with center (row,col) and radius 3. One way to locate maximas is to use the ## @code{immaximas} function. ## ## @seealso{hough_line, hough_circle, immaximas} ## @end deftypefn function [accum, R] = houghtf(bw, varargin) ## Default arguments method = "line"; args = {}; ## Check input arguments if (nargin == 0) error("houghtf: not enough input arguments"); endif if (! ismatrix (bw)) error("houghtf: BW must be a 2-dimensional matrix"); endif if (nargin > 1) if (ischar(varargin{1})) method = varargin{1}; args = varargin(2:end); else args = varargin; endif endif ## Choose method switch (lower(method)) case "line" [accum, R] = hough_line(bw, args{:}); case "circle" accum = hough_circle(bw, args{:}); otherwise error("houghtf: unsupported method '%s'", method); endswitch endfunction image-2.16.1/inst/PaxHeaders.61586/psf2otf.m0000644000000000000000000000006215005110255015143 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/psf2otf.m0000644000175000017500000000660615005110255016551 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} psf2otf (@var{psf}) ## @deftypefnx {Function File} {} psf2otf (@var{psf}, @var{outsize}) ## Compute OTF from PSF. ## ## Returns the Optical Transfer Function (OTF) of the Point Spread ## Function @var{psf}. ## ## The optional argument @var{outsize} defines the size of the computed ## @var{otf}. The input @var{psf} is post-padded with zeros previous to ## the OTF computation. ## ## @seealso{circshift, fft2, fftn, otf2psf} ## @end deftypefn function otf = psf2otf (psf, outsize) if (nargin < 1 || nargin > 2) print_usage (); elseif (! isnumeric (psf)) error ("psf2otf: PSF must be numeric") endif insize = size (psf); if (nargin > 1) if (! isnumeric (outsize) || ! isvector (outsize)) error ("psf2otf: OUTSIZE must be a numeric vector"); endif n = max (numel (outsize), numel (insize)); outsize = postpad (outsize(:), n, 1); insize = postpad (insize(:), n, 1); if (any (outsize < insize)) error ("psf2otf: OUTSIZE must be larger than or equal than PSF size"); endif psf = padarray (psf, outsize - insize, "post", "zeros"); endif psf = circshift (psf, - floor (insize / 2)); otf = fftn (psf); endfunction ## Basic usage, 1, 2, and 3 dimensional %!test %! psf = rand (6, 1); %! assert (psf2otf (psf), fft (circshift (psf, [-3]))); %!test %! psf = rand (6, 6); %! assert (psf2otf (psf), fft2 (circshift (psf, [-3 -3]))); %!test %! psf = rand (6, 6, 6); %! assert (psf2otf (psf), fftn (circshift (psf, [-3 -3 -3]))); ## Test when length of some sides are odd %!test %! psf = rand (7, 1); %! assert (psf2otf (psf), fft (circshift (psf, [-3]))); %!test %! psf = rand (7, 7); %! assert (psf2otf (psf), fft2 (circshift (psf, [-3 -3])), 1e-14); %!test %! psf = rand (6, 7, 8); %! assert (psf2otf (psf), fftn (circshift (psf, [-3 -3 -4])), 6e-14); ## Test the outsize/padding option %!test %! psf = rand (6, 1); %! ppsf = [psf; 0]; %! assert (psf2otf (psf, 7), fft (circshift (ppsf, [-3]))); %!test %! psf = rand (6, 1); %! ppsf = [[psf; 0] zeros(7, 6)]; %! assert (psf2otf (psf, [7 7]), fft2 (circshift (ppsf, [-3 0]))); %!test %! psf = rand (6, 6); %! ppsf = [psf zeros(6, 1)]; %! assert (psf2otf (psf, [6 7]), fft2 (circshift (ppsf, [-3 -3])), 1e-14); %!error psf2otf ('foo') %!error psf2otf (rand (16), 14) %!error psf2otf (rand (16), [14 14]) %!error psf2otf (rand (16), [18]) %!error psf2otf (rand (16), [18 14]) ## a less random test, also testing for complex inputs %!test %! psf = fspecial ("gaussian", 16) + 1i; %! otf = psf2otf (psf); %! assert (otf2psf (otf), psf, eps); image-2.16.1/inst/PaxHeaders.61586/wavelength2rgb.m0000644000000000000000000000006215005110255016501 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/wavelength2rgb.m0000644000175000017500000001444115005110255020103 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 William Krekeler ## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{rgb} =} wavelength2rgb (@var{wavelength}) ## @deftypefnx {Function File} {@var{rgb} =} wavelength2rgb (@var{wavelength}, @var{class}) ## @deftypefnx {Function File} {@var{rgb} =} wavelength2rgb (@var{wavelength}, @var{class}, @var{gamma}) ## Convert wavelength in nm into an RGB value set. ## ## Given a N-dimensional matrix @var{wavelength} with color values in nm, returns ## a RGB image with N+3 dimensions. ## ## @group ## @example ## wavelength2rgb (400) ## @result{} [0.51222 0.00000 0.70849] ## ## wavelength2rgb ([400 410]) ## @result{(:,:,1)} 0.51222 0.49242 ## @result{(:,:,2)} 0 0 ## @result{(:,:,3)} 0.70849 0.85736 ## @end example ## @end group ## ## The @var{rgb} class can be specified with @var{class}. Possible values are ## double (default), single, uint8, uint16, and int16. ## ## @group ## @example ## wavelength2rgb (400) ## @result{} 0.51222 0.00000 0.70849 ## ## wavelength2rgb (400, "uint8") ## @result{} 131 0 181 ## @end example ## @end group ## ## The luminance of colors can be adjusted with @var{gamma} which must a scalar ## value in the range [0 1]. Defaults to 0.8. ## ## Reference: ## @itemize @bullet ## @item @uref{http://stackoverflow.com/questions/2374959/algorithm-to-convert-any-positive-integer-to-an-rgb-value} ## @item @uref{http://www.midnightkite.com/color.html} per Dan Bruton ## @end itemize ## @end deftypefn function rgb = wavelength2rgb (wavelength, out_class = "double", gamma = 0.8) if (nargin < 1 || nargin > 3) print_usage; elseif (!isnumeric (wavelength) || any (wavelength <= 0)) error ("wavelength2rgb: wavelength must a positive numeric"); elseif (!ischar (out_class) || all (!strcmpi (out_class, {"single", "double", "uint8", "uint16", "int16"}))) error ("wavelength2rgb: unsupported class `%s'", char (out_class)); elseif (!isnumeric (gamma) || !isscalar (gamma) || gamma > 1 || gamma < 0) error ("wavelength2rgb: gamma must a numeric scalar between 1 and 0"); endif ## initialize rgb. One extra dimension of size 3 for RGB. Later on, we will ## use ndims and when input is a scalar, ndims still returns 2 which means ## output would be 1x1x3. Check this and adjust later on if (isscalar (wavelength)) rgb = zeros (1, 3); size_adjust = 1; else rgb = zeros ([size(wavelength), 3]); size_adjust = 0; endif ## this RGBmask's will be used for broadcasting later Rmask = Gmask = Bmask = false ([ones(1, ndims (wavelength) - size_adjust) 3]); Rmask(1) = true; Gmask(2) = true; Bmask(3) = true; ## for each group of wavelengths we calculate the mask, expand for the 3 ## channels and use Rmask, Gmask and Bmask with broadcasting to select the ## right one. Will skip some channels since their values would be zero and ## we already initialized the matrix with zeros() get_rgb_mask = @(mask) repmat (mask, [ones(1, ndims (mask) - size_adjust) 3]); mask = wavelength >= 380 & wavelength < 440; rgbmask = get_rgb_mask (mask); rgb(rgbmask & Rmask) = -(wavelength(mask) - 440) / 60; # 60 comes from 440-380 ## skiping green channel (values of zero) rgb(rgbmask & Bmask) = 1; mask = wavelength >= 440 & wavelength < 490; rgbmask = get_rgb_mask (mask); ## skiping red channel (values of zero) rgb(rgbmask & Gmask) = (wavelength(mask) - 440) / 50; # 50 comes from 490-440 rgb(rgbmask & Bmask) = 1; mask = wavelength >= 490 & wavelength < 510; rgbmask = get_rgb_mask (mask); ## skiping red channel (values of zero) rgb(rgbmask & Gmask) = 1; rgb(rgbmask & Bmask) = -(wavelength(mask) - 510) / 20; # 20 comes from 510-490 mask = wavelength >= 510 & wavelength < 580; rgbmask = get_rgb_mask (mask); rgb(rgbmask & Rmask) = (wavelength(mask) - 510) / 70; # 70 comes from 580-510 rgb(rgbmask & Gmask) = 1; ## skiping blue channel (values of zero) mask = wavelength >= 580 & wavelength < 645; rgbmask = get_rgb_mask (mask); rgb(rgbmask & Rmask) = 1; rgb(rgbmask & Gmask) = -(wavelength(mask) - 645) / 65; # 65 comes from 645-580 ## skiping blue channel (values of zero) mask = wavelength >= 645 & wavelength <= 780; rgbmask = get_rgb_mask (mask); rgb(rgbmask & Rmask) = 1; ## skiping green channel (values of zero) ## skiping blue channel (values of zero) ## all other wavelengths have values of zero in all channels (black) ## let intensity fall off near the vision limits ## set the factor factor = zeros (size (wavelength)); mask = wavelength >= 380 & wavelength < 420; factor(mask) = 0.3 + 0.7*(wavelength(mask) - 380) / 40; # 40 = 420 - 380 mask = wavelength >= 420 & wavelength <= 700; factor(mask) = 1; mask = wavelength > 700 & wavelength <= 780; factor(mask) = 0.3 + 0.7*(780 - wavelength(mask)) / 80; # 80 = 780 - 700 ## for other wavelengths, factor is 0 ## expand factor for the 3 channels factor = repmat (factor, [ones(1, ndims (factor) - size_adjust) 3]); ## correct rgb rgb = (rgb .* factor) .^gamma; ## scale to requested class switch tolower (out_class) case {"single"} rgb = im2single (rgb); case {"double"} ## do nothing, already class double case {"uint8"} rgb = im2uint8 (rgb); case {"uint16"} rgb = im2uint16 (rgb); case {"int16"} rgb = im2int16 (rgb); otherwise error ("wavelength2rgb: unsupported class `%s'", out_class) endswitch endfunction %!demo %! %! ##draw RGB values for wavelengths between 350 and 800 nm %! RGB = wavelength2rgb (350:800); %! rgbplot (squeeze (RGB), "composite"); %! axis off; image-2.16.1/inst/PaxHeaders.61586/isind.m0000644000000000000000000000006215005110255014666 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/isind.m0000644000175000017500000000442615005110255016272 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## Copyright (C) 2011, 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} isind (@var{img}) ## Return true if @var{img} is an indexed image. ## ## A variable can be considered an indexed image if it is a non-sparse, ## real array of size @nospell{MxNx1xK}, and: ## ## @itemize @bullet ## @item of class double with all integers above zero; ## @item of class uint8 or uint16. ## @end itemize ## ## @emph{Note}: despite their suggestive names, the functions isbw, ## isgray, isind, and isrgb, are ambiguous since it is not always possible ## to distinguish between those image types. For example: an uint8 matrix ## can be both a grayscale and indexed image; a grayscale image may have ## values outside the range [0 1]. They are good to dismiss input as an ## invalid image type, but not for identification. ## ## @seealso{ind2gray, ind2rgb, isbw, isgray, isindex, isrgb} ## @end deftypefn function bool = isind (img) if (nargin != 1) print_usage; endif bool = false; if (isimage (img) && ndims (img) < 5 && size (img, 3) == 1) if (isfloat (img)) bool = isindex (img); elseif (any (isa (img, {"uint8", "uint16"}))) bool = true; endif endif endfunction %!assert (isind ([]), false); %!assert (isind (1:10), true); %!assert (isind (0:10), false); %!assert (isind (1), true); %!assert (isind (0), false); %!assert (isind ([1.3 2.4]), false); %!assert (isind ([1 2; 3 4]), true); %!assert (isind (randi (100, 10, 10, 1, 4)), true); %!assert (isind (randi (100, 10, 10, 3, 4)), false); %!assert (isind (randi (100, 10, 10, 1, 4, 2)), false); image-2.16.1/inst/PaxHeaders.61586/cp2tform.m0000644000000000000000000000006215005110255015314 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/cp2tform.m0000644000175000017500000002616015005110255016717 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Pantxo Diribarne ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{T} =} cp2tform (@var{in_cp}, @var{out_cp}, @var{ttype}) ## @deftypefnx {Function File} {@var{T} =} cp2tform (@dots{}, @var{opt}) ## Return a transformation structure @var{T} (see "help maketform" ## for the form of the structure) that can be further used to ## transform coordinates between an input space and an ouput space. ## ## The transform is inferred from two n-by-2 arrays, @var{in_cp} and ## @var{out_cp}, which contain the coordinates of n control points in ## the two 2D spaces. ## Transform coefficients are stored in @var{T}.tdata. Interpretation of ## transform coefficients depends on the requested transform type @var{ttype}: ## ## @table @asis ## @item "affine" ## Return both forward (input space to output space) and inverse transform ## coefficients @var{T}.tdata.T and @var{T}.tdata.Tinv. Transform ## coefficients are 3x2 matrices which can be used as follows: ## ## @example ## @group ## @var{in_cp} = [@var{out_cp} ones(rows (out_cp) ,1)] * T.tdata.Tinv ## @var{out_cp} = [@var{in_cp} ones(rows (in_cp) ,1)] * T.tdata.T ## @end group ## @end example ## This transformation is well suited when parallel lines in one space ## are still parallel in the other space (e.g. shear, translation, @dots{}). ## ## @item "nonreflective similarity" ## Same as "affine" except that the transform matrices T and Tinv have ## the form ## @example ## @group ## Tcoefs = [a -b; ## b a; ## c d] ## @end group ## @end example ## This transformation may represent rotation, scaling and ## translation. Reflection is not included. ## ## @item "similarity" ## Same as "nonreflective similarity" except that the transform matrices T and Tinv may also have ## the form ## @example ## @group ## Tcoefs = [a b; ## b -a; ## c d] ## @end group ## @end example ## This transformation may represent reflection, rotation, scaling and ## translation. Generates a warning if the nonreflective similarity is ## better suited. ## ## @item "projective" ## Return both forward (input space to output space) and inverse transform ## coefficients @var{T}.tdata.T and @var{T}.tdata.Tinv. Transform ## coefficients are 3x3 matrices which can ## be used as follows: ## ## @example ## @group ## [u v w] = [@var{out_cp} ones(rows (out_cp) ,1)] * T.tdata.Tinv ## @var{in_cp} = [u./w, v./w]; ## [x y z] = [@var{in_cp} ones(rows (in_cp) ,1)] * T.tdata.T ## @var{out_cp} = [x./z y./z]; ## @end group ## @end example ## This transformation is well suited when parallel lines in one space ## all converge toward a vanishing point in the other space. ## ## @item "polynomial" ## Here the @var{opt} input argument is the order of the polynomial ## fit. @var{opt} must be 2, 3 or 4 and input control points number must ## be respectively at least 6, 10 or 15. Only the inverse transform ## (output space to input space) is included in the structure @var{T}. ## Denoting x and y the output space coordinate vector and u, v the ## the input space coordinates. Inverse transform coefficients are ## stored in a (6,10 or 15)-by-2 matrix which can be used as follows: ## ## @example ## @group ## Second order: ## [u v] = [1 x y x*y x^2 y^2] * T.tdata.Tinv ## @end group ## @group ## Third order: ## [u v] = [1 x y x*y x^2 y^2 y*x^2 x*y^2 x^3 y^3] * T.tdata.Tinv ## @end group ## @group ## Fourth order: ## [u v] = [1 x y x*y x^2 y^2 y*x^2 x*y^2 x^3 y^3 x^3*y x^2*y^2 x*y^3 x^4 y^4] * T.tdata.Tinv ## @end group ## @end example ## This transform is well suited when lines in one space become curves ## in the other space. ## @end table ## @seealso{tformfwd, tforminv, maketform} ## @end deftypefn function trans = cp2tform (crw, cap, ttype, opt) if (nargin < 3) print_usage (); endif if (! all (size (crw) == size (cap)) || columns (crw) != 2) error ("cp2tform: expect the 2 first input arguments to be (m x 2) matrices") elseif (! ischar (ttype)) error ("cp2tform: expect a string as third input argument") endif ttype = lower (ttype); switch ttype case {'nonreflective similarity', 'similarity', 'affine', 'projective'} trans = gettrans (ttype, cap, crw); case 'polynomial' if (nargin < 4) error ("cp2tform: expect a fourth input argument for 'polynomial'") elseif (! isscalar (opt)) error ("cp2tform: expect a scalar as fourth argument") endif trans = gettrans (ttype, cap, crw, round (opt)); otherwise error ("cp2tform: expect 'nonreflective similarity', 'similarity', 'affine' or 'polynomial' as third input argument") endswitch endfunction function trans = gettrans (ttype, cap, crw, ord = 0) switch ttype case "nonreflective similarity" x = cap(:,1); y = cap(:,2); u = crw(:,1); v = crw(:,2); tmp0 = zeros(size(u)); tmp1 = ones(size(u)); A = [u v tmp1 tmp0 ; v -u tmp0 tmp1]; B = [x; y]; tmat = A\B; tmat = [tmat(1) -tmat(2); tmat(2) tmat(1); tmat(3) tmat(4)]; trans = maketform ("affine", tmat); case "similarity" x = cap(:,1); y = cap(:,2); u = crw(:,1); v = crw(:,2); tmp0 = zeros(size(u)); tmp1 = ones(size(u)); #without reflection A = [u v tmp1 tmp0 ; v -u tmp0 tmp1]; B = [x; y]; tmat1 = A\B; resid = norm (A*tmat1 - B); #with reflection A = [u v tmp1 tmp0 ; -v u tmp0 tmp1]; tmat2 = A\B; if (norm (A*tmat2 - B) < resid) tmat = [tmat2(1) tmat2(2); tmat2(2) -tmat2(1); tmat2(3) tmat2(4)]; else tmat = [tmat1(1) -tmat1(2); tmat1(2) tmat1(1); tmat1(3) tmat1(4)]; warning ("cp2tform: reflection not included.") endif trans = maketform ("affine", tmat); case "affine" tmat = [crw ones(rows(crw), 1)]\cap; trans = maketform ("affine", tmat); case "projective" x = cap(:,1); y = cap(:,2); u = crw(:,1); v = crw(:,2); tmp0 = zeros(size(u)); tmp1 = ones(size(u)); A = [-u -v -tmp1 tmp0 tmp0 tmp0 x.*u x.*v x; tmp0 tmp0 tmp0 -u -v -tmp1 y.*u y.*v y]; B = - A(:,end); A(:,end) = []; tmat = A\B; tmat(9) = 1; tmat = reshape (tmat, 3, 3); trans = maketform ("projective", tmat); case "polynomial" x = cap(:,1); y = cap(:,2); u = crw(:,1); v = crw(:,2); tmp1 = ones(size(x)); ndims_in = 2; ndims_out = 2; forward_fcn = []; inverse_fcn = @inv_polynomial; A = [tmp1, x, y, x.*y, x.^2, y.^2]; B = [u v]; switch ord case 2 case 3 A = [A, y.*x.^2 x.*y.^2 x.^3 y.^3]; case 4 A = [A, y.*x.^2 x.*y.^2 x.^3 y.^3]; A = [A, x.^3.*y x.^2.*y.^2 x.*y.^3 x.^4 y.^4]; otherwise error ("cp2tform: supported polynomial orders are 2, 3 and 4.") endswitch tmat = A\B; trans = maketform ("custom", ndims_in, ndims_out, ... forward_fcn, inverse_fcn, tmat); otherwise error ("cp2tform: invalid TTYPE %s.", ttype); endswitch endfunction function out = inv_polynomial (x, pst) out = []; for ii = 1:2 p = pst.tdata(:,ii); if (rows (p) == 6) ## 2nd order out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + ... p(5)*x(:,1).^2 + p(6)*x(:,2).^2; elseif (rows (p) == 10) ## 3rd order out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + ... p(5)*x(:,1).^2 + p(6)*x(:,2).^2 + p(7)*x(:,2).*x(:,1).^2 + ... p(8)*x(:,1).*x(:,2).^2 + p(9)*x(:,1).^3 + p(10)*x(:,2).^3; elseif (rows (p) == 15) ## 4th order out(:,ii) = p(1) + p(2)*x(:,1) + p(3)*x(:,2) + p(4)*x(:,1).*x(:,2) + ... p(5)*x(:,1).^2 + p(6)*x(:,2).^2 + p(7)*x(:,2).*x(:,1).^2 + ... p(8)*x(:,1).*x(:,2).^2 + p(9)*x(:,1).^3 + p(10)*x(:,2).^3 + ... p(11)*x(:,2).*x(:,1).^3 + p(12)*x(:,2).^2.*x(:,1).^2+ ... p(13)*x(:,1).*x(:,2).^3 + p(14)*x(:,1).^4 + p(15)*x(:,2).^4; endif endfor endfunction %!function [crw, cap] = coords (npt = 1000, scale = 2, dtheta = pi/3, %! dx = 2, dy = -6, sig2noise = 1e32) %! theta = (rand(npt, 1)*2-1)*2*pi; %! R = rand(npt,1); %! y = R.*sin(theta); %! x = R.*cos(theta); %! crw = [y x]; %! %! thetap = theta + dtheta; %! Rap = R * scale; %! %! yap = Rap.*sin(thetap); %! yap = yap + dy; %! yap = yap + rand (size (yap)) * norm (yap) / sig2noise; %! %! xap = Rap.*cos(thetap); %! xap = xap + dx; %! xap = xap + rand (size (xap)) * norm (xap) / sig2noise; %! cap = [yap xap]; %!endfunction %!test %! npt = 100000; %! [crw, cap] = coords (npt); %! ttype = 'projective'; %! T = cp2tform (crw, cap, ttype); %! crw2 = tforminv (T, cap); %! finalerr = norm (crw - crw2)/npt; %! assert (finalerr < 2*eps, "norm = %3.2e ( > 2*eps)", finalerr) %!test %! npt = 100000; %! [crw, cap] = coords (npt); %! ttype = 'affine'; %! T = cp2tform (crw, cap, ttype); %! crw2 = tforminv (T, cap); %! finalerr = norm (crw - crw2)/npt; %! assert (finalerr < 2*eps, "norm = %3.2e ( > 2*eps)", finalerr) %!test %! npt = 100000; %! [crw, cap] = coords (npt); %! ttype = 'nonreflective similarity'; %! T = cp2tform (crw, cap, ttype); %! crw2 = tforminv (T, cap); %! finalerr = norm (crw - crw2)/npt; %! assert (finalerr < 3*eps, "norm = %3.2e ( > 3*eps)", finalerr) %!test %! npt = 100000; %! [crw, cap] = coords (npt); %! cap(:,2) *= -1; % reflection around y axis %! ttype = 'similarity'; %! T = cp2tform (crw, cap, ttype); %! crw2 = tforminv (T, cap); %! finalerr = norm (crw - crw2)/npt; %! assert (finalerr < 3*eps, "norm = %3.2e ( > 3*eps)", finalerr) %!xtest %! npt = 100000; %! [crw, cap] = coords (npt); %! ttype = 'polynomial'; %! ord = 2; %! T = cp2tform (crw, cap, ttype, ord); %! crw2 = tforminv (T, cap); %! finalerr = norm (crw - crw2)/npt; %! assert (finalerr < eps, "norm = %3.2e ( > eps)", finalerr) %!xtest %! npt = 100000; %! [crw, cap] = coords (npt); %! ttype = 'polynomial'; %! ord = 3; %! T = cp2tform (crw, cap, ttype, ord); %! crw2 = tforminv (T, cap); %! finalerr = norm (crw - crw2)/npt; %! assert (finalerr < eps, "norm = %3.2e ( > eps)", finalerr) %!xtest %! npt = 100000; %! [crw, cap] = coords (npt); %! ttype = 'polynomial'; %! ord = 4; %! T = cp2tform (crw, cap, ttype, ord); %! crw2 = tforminv (T, cap); %! finalerr = norm (crw - crw2)/npt; %! assert (finalerr < 6*eps, "norm = %3.2e ( > 6*eps)", finalerr) image-2.16.1/inst/PaxHeaders.61586/col2im.m0000644000000000000000000000006215005110255014745 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/col2im.m0000644000175000017500000002127615005110255016353 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} col2im (@var{B}, @var{block_size}, @var{A_size}) ## @deftypefnx {Function File} {} col2im (@var{B}, @var{block_size}, @var{A_size}, @var{block_type}) ## Rearrange block columns back into matrix. ## ## Rearranges columns of the matrix @var{B}, representing blocks of size ## @var{block_size} from a matrix of size @var{A_size}, back into its ## original size (usually close to @var{A_size}. This function is most ## useful as reverse operation to @code{im2col}. ## ## ## Blocks are assumed to be from one of two types as defined by ## @var{block_type} (defaults to @qcode{"sliding"}): ## ## @table @asis ## @item @qcode{"distinct"} ## Each column of @var{B} is assumed to be distinct blocks, with no ## overlapping elements, of size @var{block_size}, to rebuild a matrix of ## size @var{A_size}. Any padding that may have been required to form ## @var{B} from a matrix of @var{A_size}, is removed accordingly. ## ## @item @qcode{"sliding"} ## This reshapes @var{B} into a matrix of size ## @code{@var{A_size} - @var{block_size} +1}. Sliding blocks are most useful ## to apply a sliding window filter with functions that act along columns. ## In this situation, @var{B} is usually a row vector, so that if ## @var{block_size} is [1 1], @var{A_SIZE} will be the size of the output ## matrix. When converting a matrix into blocks with @code{im2col}, there ## will be less blocks to account to borders, so if @var{block_size} is the ## same in both @code{col2im} and @code{im2col}, @var{A_size} can be the size ## out the output from @code{im2col}. ## ## @end table ## ## Blocks are assumed to have been from a matrix, the same direction elements ## are organized in an Octave matrix (top to bottom, then left to right), and ## the direction that blocks are taken in @code{im2col}. ## ## @group ## @example ## ## Get distinct blocks of size [2 3] from A into columns, and ## ## put them back together into the original position ## A = reshape (1:24, [4 6]) ## B = im2col (A, [2 3], "distinct") ## col2im (B, [2 3], [4 6], "distinct") ## @end example ## ## @example ## ## Get sliding blocks of size [2 3] from A into columns, calculate ## ## the mean of each block (mean of each column), and reconstruct A. ## ## This is the equivalent to a sliding window filter and ignoring ## ## borders. ## A = reshape (1:24, [4 6]) ## B = im2col (A, [2 3], "sliding") ## C = mean (B); ## col2im (C, [1 1], [3 4], "sliding") ## @end example ## @end group ## ## @seealso{blockproc, bestblk, colfilt, im2col, nlfilter, reshape} ## @end deftypefn function A = col2im (B, block_size, A_size, block_type = "sliding") if (nargin < 3 || nargin > 4) print_usage (); elseif (! isnumeric (B) && ! islogical (B)) error ("col2im: B must be a numeric of logical matrix or vector"); elseif (! isnumeric (block_size) || ! isvector (block_size)) error("col2im: BLOCK_SIZE must be a numeric vector"); elseif (! isnumeric (A_size) || ! isvector (A_size)) error("col2im: A_SIZE must be a numeric vector"); elseif (! ischar (block_type)) error ("col2im: BLOCK_TYPE must be a string"); endif ## Make sure dimensions are row vectors block_size = block_size(:).'; A_size = A_size(:).'; ## expand size to include singleton dimensions if required block_size(end+1:numel (A_size)) = 1; A_size(end+1:numel (block_size)) = 1; switch (tolower (block_type)) case "distinct" ## A_size is out_size for distinct blocks if (prod (block_size) != rows (B)) error (["col2im: number of rows in B, must equal number of " ... "elements per block (prod (BLOCK_SIZE))"]); elseif (numel (B) < prod (A_size)) error ("col2im: not enough elements in B for a matrix of A_SIZE"); endif ## Calculate the number of blocks accross each dimension, and ## how much of padding we will need to remove (we calculate the ## number of cumulative elements by dimension, so the first ## element is the number of rows that are pad, the second ## second is the number of columns that are pad times the number ## of elements in a column, and so on for other dimensions) blocks = ceil (A_size ./ block_size); padding = mod (-A_size, block_size) .* [1 cumprod(A_size(1:end-1))]; cum_blk_size = [1 cumprod(block_size(1:end-1))]; cum_blocks = [1 cumprod(blocks(1:end-1))]; ## End of each block dimension in the column end_blk = ceil (cum_blk_size .* (block_size -1)); ## How much in the columns we need to shift to move to the ## next block per dimension. stride = rows (B) * cum_blocks; ## Last block for each dimension last_blk = stride .* (blocks -1); ind = 1; for dim = 1:numel(A_size) ind = ind(:) + (0:cum_blk_size(dim):end_blk(dim)); ind = ind(:) + (0:stride(dim):last_blk(dim)); ind(end+1-padding(dim):end) = []; # remove padding endfor A = reshape (B(ind(:)), A_size); case "sliding" out_size = A_size - block_size +1; if (prod (out_size) != numel (B)) error ("col2im: can't resize B in matrix sized (A_SIZE - BLOCK_SIZE +1)"); endif A = reshape (B, out_size); otherwise error ("col2im: invalid BLOCK_TYPE `%s'.", block_type); endswitch endfunction %!demo %! ## Divide A using distinct blocks and then reverse the operation %! A = [ 1:10 %! 11:20 %! 21:30 %! 31:40]; %! B = im2col (A, [2 5], "distinct") %! C = col2im (B, [2 5], [4 10], "distinct") %!demo %! ## Get sliding blocks of size from A into columns, calculate the %! ## mean of each block (mean of each column), and reconstruct A %! ## after a median filter. %! A = reshape (1:24, [4 6]) %! B = im2col (A, [2 3], "sliding") %! C = mean (B); %! col2im (C, [1 1], [3 4], "sliding") %!error col2im (ones (10), [5 5], [10 10], "wrong_block_type"); %!error col2im (ones (10), [1 1], [ 7 7], "sliding"); %!error col2im (ones (10), [3 3], [10 10], "distinct") %!error col2im (ones (10), [5 5], [10 11], "distinct"); ## test sliding %!assert (col2im (sum (im2col (magic (10), [3 3], "sliding")), [1 1], [8 8]), %! convn (magic (10), ones (3, 3), "valid")); %!test %! B = ones (1, (10-2+1)*(7-3+1)); %! A = ones ((10-2+1), (7-3+1)); %! assert (col2im (B, [2 3], [10 7]), A); %! %! ## same but different classes %! assert (col2im (int16 (B), [2 3], [10 7]), int16 (A)); %! assert (col2im (single (B), [2 3], [10 7]), single (A)); %! assert (col2im (logical (B), [2 3], [10 7]), logical (A)); ## test default to sliding %!test %! a = rand (10)(:); %! assert (col2im (a, [1 1], [10 10]), col2im (a, [1 1], [10 10], "sliding")) ## test distinct %!shared A, B %! v = [1:10]'; %! r = reshape (1:10, [2 5]); %! B = [v v+10 v+20 v+30 v+40 v+50]; %! A = [r r+30 %! r+10 r+40 %! r+20 r+50]; %! assert (col2im (B, [2 5], [6 10], "distinct"), A); ## respect different classes %!assert (col2im (int16 (B), [2 5], [6 10], "distinct"), int16 (A)); %!assert (col2im (logical (B), [2 5], [6 10], "distinct"), logical (A)); %!assert (col2im (single (B), [2 5], [6 10], "distinct"), single (A)); ## Test for columns with padding %!test %! a = rand (10, 8); %! b = im2col (a, [5 5], "distinct"); %! assert (col2im (b, [5 5], [10 8], "distinct"), a); %! %! a = rand (8); %! b = im2col (a, [5 5], "distinct"); %! assert (col2im (b, [5 5], [8 8], "distinct"), a); ## Test N-dimensional %!shared a, b %! ## Same number of multiple dimensions %! a = rand (10, 10, 10); %! b = im2col (a, [5 5 5], "distinct"); %!assert (col2im (b, [5 5 5], [10 10 10], "distinct"), a); %! %! ## Different number of dimensions %! a = rand (10, 10, 10); %! b = im2col (a, [5 5], "distinct"); %!assert (col2im (b, [5 5], [10 10 10], "distinct"), a); %! %! ## Removing padding from multiple dimensions %! a = rand (10, 10, 7); %! b = im2col (a, [5 5 3], "distinct"); %!assert (col2im (b, [5 5 3], [10 10 7], "distinct"), a); %! %! a = rand (10, 10, 7); %! b = im2col (a, [5 5 5 2], "distinct"); %!assert (col2im (b, [5 5 5 2], [10 10 7], "distinct"), a); image-2.16.1/inst/PaxHeaders.61586/imregionalmax.m0000644000000000000000000000006215005110255016414 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imregionalmax.m0000644000175000017500000000630615005110255020017 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} imregionalmax (@var{img}) ## @deftypefnx {Function File} {} imregionalmax (@var{img}, @var{conn}) ## Compute regional maxima. ## ## Returns a logical matrix, same size as the input @var{img}, with the ## regional maxima. ## ## The optional argument @var{conn}, defines the connectivity. It can ## be a scalar value or a boolean matrix (see @code{conndef} for details). ## Defaults to @code{conndef (ndims (@var{img}), "maximal")} ## ## Regional maxima should not be mistaken with local maxima. Local maxima ## are pixels whose value is greater or equal to all of its neighbors. ## A regional maxima is the connected component of pixels whose values are ## all higher than the neighborhood of the maxima (the connected component, ## not its individual pixels). ## All pixels belonging to a regional maximum are local maxima, but the ## inverse is not true. ## ## @seealso{immaximas, imreconstruct, imregionalmin} ## @end deftypefn function bw = imregionalmax (img, conn) if (nargin < 1 || nargin > 2) print_usage (); endif if (nargin < 2) conn = conndef (ndims (img), "maximal"); else conn = conndef (conn); endif if (islogical (img)) bw = img; else ## we could probably still make this more efficient if (isfloat (img)) recon = imreconstruct (img, img + __eps__ (img), conn); else recon = imreconstruct (img, img + 1, conn); endif bw = (recon == img); endif endfunction %!test %! a = [ %! 7 3 9 3 10 3 %! 4 2 3 10 1 3 %! 1 4 6 9 4 10 %! 8 7 9 3 4 8 %! 5 9 3 3 8 9 %! 3 6 9 4 1 10]; %! %! a4 = [ %! 1 0 1 0 1 0 %! 0 0 0 1 0 0 %! 0 0 0 0 0 1 %! 1 0 1 0 0 0 %! 0 1 0 0 0 0 %! 0 0 1 0 0 1]; %! assert (imregionalmax (a, 4), logical (a4)) %! a8 = [ %! 1 0 0 0 1 0 %! 0 0 0 1 0 0 %! 0 0 0 0 0 1 %! 0 0 0 0 0 0 %! 0 0 0 0 0 0 %! 0 0 0 0 0 1]; %! assert (imregionalmax (a, 8), logical (a8)) %! assert (imregionalmax (a), logical (a8)) %!test %! ## test float input images %! im0 = peaks (); %! im1 = im0 ./ 100; %! max_pos_expected = [1000; 1214; 1691; 2353]; %! max0 = imregionalmax (im0); %! max0_pos = find (max0); %! max1 = imregionalmax (im1); %! assert (max1, max0) %! assert (max0_pos, max_pos_expected) image-2.16.1/inst/PaxHeaders.61586/findbounds.m0000644000000000000000000000006215005110255015713 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/findbounds.m0000644000175000017500000000541015005110255017311 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Pantxo Diribarne ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{outbnd} =} findbounds (@var{T}, @var{inbnd}) ## Estimate bounds for spatial transformation. ## ## Given a transformation structure @var{T} (see e.g. maketform) ## and bounds @var{inbnd} (2-by-ndims_in) in an input space, returns ## an estimation of the bounds in the output space @var{outbnd} ## (2-by-ndims_out). For instance two dimensionnal bounds could ## be represented as : [xmin ymin; xmax ymax]. If @var{T} does not ## define a forward transform (i.e. for 'polynomial'), the output ## bounds are infered using fsolve and the inverse transform. ## ## @seealso{maketform, cp2tform, tformfwd} ## @end deftypefn ## Author: Pantxo Diribarne function [outbnd] = findbounds (T, inbnd) if (nargin != 2) print_usage (); elseif (! istform (T)) error ("imtransform: T must be a transformation structure (see `maketform')"); elseif (! all (size (inbnd) == [2 T.ndims_in])) error ("imtransform: INBDN must have same size as T.ndims_in"); endif ## Control points grid if (columns (inbnd) == 2) [xcp, ycp] = meshgrid (linspace (inbnd(1,1), inbnd(2,1), 3), linspace (inbnd(1,2), inbnd(2,2), 3)); xcp = reshape (xcp, numel (xcp), 1); ycp = reshape (ycp, numel (ycp), 1); xycp = [xcp, ycp]; else error ("findbounds: support only 2D inputbounds."); endif ## Output bounds if (!is_function_handle (T.forward_fcn)) outbnd = zeros (size (xycp)); for ii = 1:rows (xycp) fun = @(x) tforminv (T, x) - xycp(ii,:); outbnd(ii,:) = fsolve (fun, xycp(ii,:)); endfor else outbnd = tformfwd (T, xycp); endif outbnd = [min(outbnd); max(outbnd)]; endfunction %!test %! im = checkerboard (); %! theta = pi/6; %! T = maketform ('affine', [cos(theta) -sin(theta); ... %! sin(theta) cos(theta); 0 0]); %! inbnd = [0 0; 1 1]; %! outbnd = findbounds (T, inbnd); %! diag = 2^.5; %! ang = pi/4; %! assert (diff (outbnd(:,1)), diag * abs (cos (theta - ang)), eps) %! assert (diff (outbnd(:,2)), diag * abs (cos (theta - ang)), eps) image-2.16.1/inst/PaxHeaders.61586/rgb2lab.m0000644000000000000000000000006215005110255015073 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/rgb2lab.m0000644000175000017500000001103615005110255016472 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{lab} =} rgb2lab (@var{rgb}) ## @deftypefnx {Function File} {@var{lab_map} =} rgb2lab (@var{rgb_map}) ## Transform a colormap or image from sRGB to CIE L*a*b* color space. ## ## A color in the RGB space consists of red, green, and blue intensities. ## The input RGB values are interpreted as nonlinear sRGB values ## with the white point D65. This means the input values are assumed to ## be in the colorimetric (sRGB) colorspace. ## ## A color in the CIE L*a*b* (or CIE Lab) space consists of lightness L* and ## two color-opponent dimensions a* and b*. The whitepoint is taken as D65. ## The CIE L*a*b* colorspace is also a colorimetric colorspace. It is designed ## to incorporate the human perception of color differences. ## ## Input values of class double, single, uint8 or uint16 are accepted. ## Output class is generally of type double, only input type single will ## result in an output type of single. The shape of the input is ## conserved. ## ## note: This function returns slightly different values than the Matlab ## version. But it has a better "round trip accuracy" (<2e-5) ## for RGB -> Lab -> RGB. ## ## @seealso{lab2rgb, rgb2xyz, rgb2hsv, rgb2ind, rgb2ntsc} ## @end deftypefn ## Author: Hartmut Gimpel ## algorithm taken from the following book: ## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015) function lab = rgb2lab (rgb) if (nargin != 1) print_usage (); endif [rgb, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check ("rgb2lab", "RGB", rgb, 0); ## transform from non-linear sRGB values to CIE XYZ values xyz = rgb2xyz (rgb); ## transform from CIE XYZ values to CIE L*a*b* values lab = xyz2lab (xyz); # always return values of type double for Matlab compatibility (exception: type single) lab = colorspace_conversion_revert (lab, cls, sz, is_im, is_nd, is_int, 1); endfunction ## Test pure colors, gray and some other colors ## (This set of test values is taken from the book by Burger.) %!assert (rgb2lab ([0 0 0]), [0, 0, 0], 1e-2) %!assert (rgb2lab ([1 0 0]), [53.24, 80.09, 67.20], 1e-2) %!assert (rgb2lab ([1 1 0]), [97.14, -21.55, 94.48], 1e-2) %!assert (rgb2lab ([0 1 0]), [87.74, -86.18, 83.18], 1e-2) %!assert (rgb2lab ([0 1 1]), [91.11, -48.09, -14.13], 1e-2) %!assert (rgb2lab ([0 0 1]), [32.30, 79.19, -107.86], 1e-2) %!assert (rgb2lab ([1 0 1]), [60.32, 98.24, -60.83], 1e-2) %!assert (rgb2lab ([1 1 1]), [100, 0.00, 0.00], 1e-2) %!assert (rgb2lab ([0.5 0.5 0.5]), [53.39, 0.00, 0.00], 1e-2) %!assert (rgb2lab ([0.75 0 0]), [39.77, 64.51, 54.13], 1e-2) %!assert (rgb2lab ([0.5 0 0]), [25.42, 47.91, 37.91], 1e-2) %!assert (rgb2lab ([0.25 0 0]), [9.66, 29.68, 15.24], 1e-2) %!assert (rgb2lab ([1 0.5 0.5]), [68.11, 48.39, 22.83], 1e-2) ## Test tolarant input checking on floats %!assert (rgb2lab ([1.5 1 1]), [111.47, 43.42, 17.98], 1e-2) %!test %! rgb_map = rand (64, 3); %! assert (lab2rgb (rgb2lab (rgb_map)), rgb_map, 2e-5); %!test %! rgb_img = rand (64, 64, 3); %! assert (lab2rgb (rgb2lab (rgb_img)), rgb_img, 2e-5); ## support sparse input %!assert (rgb2lab (sparse ([0 0 1])), sparse ([32.30, 79.19, -107.86]), 1e-2) %!assert (rgb2lab (sparse ([0 1 1])), sparse ([91.11, -48.09, -14.13]), 1e-2) %!assert (rgb2lab (sparse ([1 1 1])), sparse ([100, 0.00, 0.00]), 1e-2) ## support integer input (and double output) %!assert (rgb2lab (uint8([255 255 255])), [100, 0.00, 0.00], 1e-2) ## conserve class of single input %!assert (class (rgb2lab (single([1 1 1]))), 'single') ## Test input validation %!error rgb2lab () %!error rgb2lab (1,2) %!error rgb2lab ({1}) %!error rgb2lab (ones (2,2)) ## Test ND input %!test %! rgb = rand (16, 16, 3, 5); %! lab = zeros (size (rgb)); %! for i = 1:5 %! lab(:,:,:,i) = rgb2lab (rgb(:,:,:,i)); %! endfor %! assert (rgb2lab (rgb), lab) image-2.16.1/inst/PaxHeaders.61586/lab2uint8.m0000644000000000000000000000006215005110255015370 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/lab2uint8.m0000644000175000017500000000525415005110255016774 0ustar00avinoamavinoam00000000000000## Copyright (C) 2016 Carnë Draug ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} lab2double (@var{lab}) ## Convert L*a*b* data to uint8 precision. ## ## @var{lab} must be a L*a*b* image or colormap, i.e., its dimensions ## must be MxNx3xK or Mx3. Its type must be double, single, uint16, ## or uint8. ## ## When converted from double or single, L* values must range from 0 to ## 100, while a* and b* range from -128 to 127. Values outside this range ## will be capped. ## ## @seealso{lab2double, lab2rgb, lab2single, lab2uint8, lab2uin16, lab2xyz} ## @end deftypefn function [lab] = lab2uint8 (lab) if (nargin () != 1) print_usage (); endif lab = lab2cls (lab, "uint8"); endfunction ## Instead of testing the lab2uint8 function here, we test the ## conversion from uint8 type. The actual tests for lab2uint8, ## are spread all other lab2* functions. This makes the tests ## simpler. %!test %! cm_uint8 = uint8 ([0 1 2 3 4 127 128 200 254 255]); %! cm_uint8 = repmat (cm_uint8(:), [1 3]); %! im2d_uint8 = reshape (cm_uint8, [5 2 3]); %! imnd_uint8 = permute (im2d_uint8, [1 4 3 2]); %! %! cm_uint16 = uint16 ([0 256 512 768 1024 32512 32768 51200 65024 65280]); %! cm_uint16 = repmat (cm_uint16(:), [1 3]); %! assert (lab2uint16 (cm_uint8), cm_uint16) %! im2d_uint16 = reshape (cm_uint16, [5 2 3]); %! assert (lab2uint16 (im2d_uint8), im2d_uint16) %! assert (lab2uint16 (imnd_uint8), permute (im2d_uint16, [1 4 3 2])) %! %! l1 = 100/255; %! cm = [ %! 0 -128 -128 %! l1 -127 -127 %! 2*l1 -126 -126 %! 3*l1 -125 -125 %! 4*l1 -124 -124 %! 127*l1 -1 -1 %! 128*l1 0 0 %! 200*l1 72 72 %! 254*l1 126 126 %! 100 127 127]; %! im2d = reshape (cm, [5 2 3]); %! imnd = permute (im2d, [1 4 3 2]); %! %! assert (lab2double (cm_uint8), cm) %! assert (lab2double (im2d_uint8), im2d) %! assert (lab2double (imnd_uint8), imnd) %! %! assert (lab2single (cm_uint8), single (cm)) %! assert (lab2single (im2d_uint8), single (im2d)) %! assert (lab2single (imnd_uint8), single (imnd)) image-2.16.1/inst/PaxHeaders.61586/imfuse.m0000644000000000000000000000006215005110255015050 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imfuse.m0000644000175000017500000006424615005110255016462 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{c} =} imfuse (@var{a}, @var{b}) ## @deftypefnx {} {[@var{c}, @var{rc}] =} imfuse (@var{a}, @var{ra}, @var{b}, @var{rb}) ## @deftypefnx {} {@var{c} =} imfuse (@dots{}, @var{method}) ## @deftypefnx {} {@var{c} =} imfuse (@dots{}, @var{name}, @var{value}) ## Composite of two images. ## ## Combines two images using a specified @var{method}. The smaller image gets ## padded with zeros to match the size of the bigger one. The @var{method} is ## a char array and can have one of the following values: ## ## "falsecolor": Default. Display each image as one (or more) [R G B] ## channels of the output image. Images can be assigned to the output channels ## by passing the "ColorChannels" option (see below). ## ## "blend": Combines the images using alpha blending with both images equally ## transparent. ## ## "checkerboard": Masks both images with a 16x16 checkerboard stretched to fit ## the output image, each image masked with a negative version of the other's ## mask and combines the result. The top left tile contains the top left part ## of the image @var{a}. ## ## "diff": Outputs an image that represents the absolute difference of ## grayscale versions of the images. The result is also grayscale. ## ## "montage": Places @var{b} on the right side of @var{a}. This method is ## useful for comparing a modified image with its original. ## ## Intensities of the images can be scaled before creating @var{c} by ## providing the "Scaling" option which can take one of the following values: ## ## "independent": Default. Intensities of both images are scaled independently ## of each other. ## ## "joint": Intensities of both images are scaled as if all the pixels belonged ## to a single image coposed of @var{a} and @var{b}. ## ## "none": No scaling is applied. ## ## Output of the "falsecolor" method can be further modified by providing the ## "ColorChannels" option assigning image to one or two output [R G B] ## channels, given a three-element vector with values 0, 1 or 2, e.g. [0 2 1], ## that assigns @var{a} to the blue channel and @var{b} to the green channel. ## 0 means neither image gets assigned. Accepts also two char-array values ## that represent shorthands for commonly used vectors: "green-magenta" ## for [2 1 2] and "red-cyan" for [1 2 2]. ## ## When given @var{ra} and @var{rb}, images are positioned according to their ## positons in world coordinate system. The output image spans the combined ## extent of the images. Since both images can have different resolutions in ## both dimensions, the resolution of the output image in each dimension is ## the finer resolution of the two. Resulting spatial referencing object ## is returned as @var{rc}. ## ## ## @seealso{imshowpair} ## @end deftypefn function varargout = imfuse (varargin) if (nargin < 2) print_usage (); endif [a, b, ra, rb, method, scaling, color_channels] = parse_varargin (varargin{:}); out_color_mode = get_out_color_mode (a, b, method); out_channels = get_out_channels (a, b, out_color_mode); img_a = adjust_channels (a, out_color_mode, out_channels); img_b = adjust_channels (b, out_color_mode, out_channels); [img_a, img_b] = apply_scaling (img_a, img_b, scaling); [img_a, img_b, rc] = resize_images (img_a, ra, img_b, rb, out_channels); switch (method) case "falsecolor" images = {img_a, img_b}; img_c = zeros ([size(img_a, 1), size(img_a, 2), 3]); for i = 1:length (color_channels) channel = color_channels(i); if (channel > 0) part = images{channel}; img_c(:, :, i) = part; endif endfor case "blend" img_c = 0.5 * (round(255 * img_a) + round(255 * img_b)) ./ 255; case "checkerboard" [m, n, _] = size (img_a); mask = checkerboard_mask (m, n); img_c = (img_a .* mask) + (img_b .* (! mask)); case "diff" diff = abs(img_a - img_b); img_c = diff ./ max (diff (:)); case "montage" img_c = [img_a, img_b]; endswitch varargout{1} = uint8 (round (single (255) * img_c)); if (! isempty (rc)) varargout{2} = rc; endif endfunction function [a, b, ra, rb, method, scaling, color_channels] = parse_varargin(varargin) check_is_image (varargin{1}, "A"); a = varargin{1}; if (isimage (varargin{2})) b = varargin{2}; varargin(1:2) = []; ra = []; rb = []; elseif (is_imref (varargin{2})) if (length (varargin) < 4) error ("Octave:invalid-input-arg", ... "expected at least 4 arguments: A, RA, B, RB"); endif validateattributes (varargin{2}, {"imref2d"}, {}, "imfuse", "RA"); check_is_image (varargin{3}, "B"); validateattributes (varargin{4}, {"imref2d"}, {}, "imfuse", "RB"); ra = varargin{2}; b = varargin{3}; rb = varargin{4}; varargin(1:4) = []; endif method_names = {"falsecolor", "blend", "checkerboard", "diff", "montage", ... "interpolation"}; scaling_names = {"independent", "joint", "none"}; scaling = scaling_names{1}; method = []; green_magenta = [2, 1, 2]; red_cyan = [1, 2, 2]; color_channels = green_magenta; while (! isempty (varargin)) if (isempty (method) && is_valid_method (varargin{1}, method_names)) method = lower (varargin{1}); if (strcmp (method, "interpolation")) error ("Octave:invalid-input-arg", ... "imshowpair: INTERPOLATION not implemented yet"); endif varargin(1) = []; else if (length (varargin) < 2) error ("Octave:invalid-input-arg", "expected NAME, VALUE pairs or incorrect display method"); endif validateattributes (varargin{1}, {"char"}, {"nonempty"}, "imfuse", ... "NAME"); validateattributes (varargin{2}, {}, {"nonempty"}, "imfuse", "VALUE"); name = varargin{1}; value = varargin{2}; switch (lower (name)) case "scaling" if (! is_valid_scaling (value, scaling_names)) error ("Octave:invalid-input-arg", "imfuse: SCALING expected to\ be one of 'independent', 'joint', 'none'"); endif scaling = value; case "colorchannels" if (ischar (value)) switch (value) case "green-magenta" color_channels = green_magenta; case "red-cyan" color_channels = red_cyan; otherwise error ("Octave:invalid-input-arg", ... "imfuse: expected COLORCHANNELS to be one of 'green-magenta',\ 'red-cyan'"); endswitch else validateattributes (value, {"numeric"}, ... {"integer", "vector", "numel", 3, ">=", 0, "<=", 2}, ... "imfuse", "COLORCHANNELS") if (! is_valid_channels (value)) error ("Octave:invalid-input-arg", ... "imfuse: COLORCHANNELS must include values 1 and 2"); endif color_channels = value; endif case "interpolation" error ("Octave:invalid-input-arg", ... "imshowpair: INTERPOLATION not implemented yet"); otherwise error ("Octave:invalid-input-arg", ... strcat (name, " is not a recognized parameter")); endswitch varargin(1:2) = []; endif endwhile if (isempty (method)) method = method_names{1}; endif endfunction function check = is_imref (arg) check = isa (arg, "imref2d"); endfunction function check = is_valid_method (arg, method_names) check = ischar (arg) && ismember (lower (arg), method_names); endfunction function check = is_valid_scaling (arg, scaling_names) check = ischar (arg) && ismember (lower (arg), scaling_names); endfunction function check = is_valid_channels (arg) check = find (arg == 1) != 0 && find (arg == 2) != 0; endfunction function check_is_image (arg, name) if (! isimage (arg)) error ("Octave:invalid-input-arg", ... strcat(name, " expected to be logical, RGB or grayscale image")); endif endfunction function rgb = to_rgb (img) if (isrgb (img)) rgb = img; else rgb = repmat (img, [1, 1, 3]); endif endfunction function rc = get_output_spatial_ref (ra, rb) ## Output world extent x_world_a = ra.XWorldLimits; y_world_a = ra.YWorldLimits; x_world_b = rb.XWorldLimits; y_world_b = rb.YWorldLimits; x_world_d = [min(x_world_a(1), x_world_b(1)), ... max(x_world_a(2), x_world_b(2))]; y_world_d = [min(y_world_a(1), y_world_b(1)), ... max(y_world_a(2), y_world_b(2))]; ## Images can have different resolutions in both dimensions ## so the question is: what is the final resolution (image size)? ## Solution: for each dimension compute the number of pixels in the ## output image using the finer resolution of the two images. if (ra.PixelExtentInWorldX <= rb.PixelExtentInWorldX) x_ref = ra; else x_ref = rb; endif if (ra.PixelExtentInWorldY <= rb.PixelExtentInWorldY) y_ref = ra; else y_ref = rb; endif [iXA, _] = worldToIntrinsic (x_ref, x_world_d, y_world_d); [_, iYB] = worldToIntrinsic (y_ref, x_world_d, y_world_d); img_size = [ceil(iYB(2) - iYB(1)), ceil(iXA(2) - iXA(1))]; rc = imref2d (img_size, x_world_d, y_world_d); endfunction function c = checkerboard_mask (m, n) v = repmat (eye (2), [8, 8]); c = logical (imresize (v, [m, n], "nearest")); endfunction function [img_a, img_b, rc] = resize_images (a, ra, b, rb, out_channels) if (isempty (ra) || isempty (rb)) dims_a = size (a); dims_b = size (b); maxdims = max (dims_a(1:2), dims_b(1:2)); m = maxdims(1); n = maxdims(2); out_dims = [m, n, out_channels]; img_a = resize_image (a, out_dims, "pad"); img_b = resize_image (b, out_dims, "pad"); rc = []; else rc = get_output_spatial_ref (ra, rb); m = rc.ImageSize(1); n = rc.ImageSize(2); out_dims = [m, n, out_channels]; img_a = resize_image_spatial (a, ra, rc, out_dims); img_b = resize_image_spatial (b, rb, rc, out_dims); endif endfunction function out_color_mode = get_out_color_mode (a, b, method) switch (method) case "falsecolor" out_color_mode = "gray"; case "blend" out_color_mode = "same"; case "checkerboard" out_color_mode = "same"; case "diff" out_color_mode = "gray"; case "montage" if (isrgb (a) || isrgb (b)) out_color_mode = "same"; else out_color_mode = "gray"; endif endswitch endfunction function out_channels = get_out_channels (a, b, out_color_mode) switch (out_color_mode) case "gray" out_channels = 1; case "same" if (isrgb (a) || isrgb (b)) out_channels = 3; else out_channels = 1; endif endswitch endfunction function img = resize_image_spatial (x, rx, rc, out_dims) img = zeros (out_dims); out_dims = get_spatial_output_dims (rx, rc, out_dims); [x_world, y_world] = intrinsicToWorld (rx, [1, rx.ImageSize(2)], ... [1, rx.ImageSize(1)]); [row_subs, col_subs] = worldToSubscript (rc, x_world, y_world); sub_img = resize_image (x, out_dims, "stretch"); img(row_subs(1):row_subs(2), col_subs(1):col_subs(2), :) = sub_img; endfunction function out_dims = get_spatial_output_dims (rx, rc, out_dims) [x_world, y_world] = intrinsicToWorld (rx, [1, rx.ImageSize(2)], ... [1, rx.ImageSize(1)]); [row_subs, col_subs] = worldToSubscript (rc, x_world, y_world); out_dims = [row_subs(2) - row_subs(1) + 1, col_subs(2) - col_subs(1) + 1, ... out_dims(3)]; endfunction function out_img = adjust_channels (in_img, out_color_mode, out_channels) switch (out_color_mode) case "gray" if (isrgb (in_img)) out_img = rgb2gray (in_img); else out_img = in_img; endif case "same" if (out_channels == 3) out_img = to_rgb (in_img); else out_img = in_img; endif endswitch endfunction function out_img = resize_image (in_img, out_dims, resize_mode) switch (resize_mode) case "pad" out_img = resize (in_img, out_dims); case "stretch" out_img = imresize (in_img, out_dims(1:2)); endswitch endfunction function [a_scaled, b_scaled] = apply_scaling (a, b, scaling) a_intensity_range = []; b_intensity_range = []; a_shift = 0; b_shift = 0; switch (scaling) case "joint" if (isrgb (a)) max_a = max (a, [], 1); max_a = max (max_a, [], 2); min_a = min (a, [], 1); min_a = min (min_a, [], 2); endif if (isrgb (b)) max_b = max (b, [], 1); max_b = max (max_b, [], 2); min_b = min (b, [], 1); min_b = min (min_b, [], 2); endif if (! (isrgb (a) && isrgb (b))) max_a = max (double (a(:))); max_b = max (double (b(:))); min_a = min (double (a(:))); min_b = min (double (b(:))); endif min_ab = min (min_a, min_b); max_ab = max (max_a, max_b); a_shift = min_ab; b_shift = min_ab; a_intensity_range = max_ab - min_ab; b_intensity_range = max_ab - min_ab; case "independent" a_elems = a(:); b_elems = b(:); a_shift = min (a_elems); b_shift = min (b_elems); a_intensity_range = max (a_elems) - min (a_elems); b_intensity_range = max (b_elems) - min (b_elems); case "none" if (isinteger (a)) a_intensity_range = 255; endif if (isinteger (b)) b_intensity_range = 255; endif endswitch a_scaled = scale_img (a, a_intensity_range, a_shift); b_scaled = scale_img (b, b_intensity_range, b_shift); endfunction function out_img = scale_img (in_img, intensity_range, color_shift) if (! isempty (intensity_range)) if (intensity_range == 0) intensity_range = 1; endif out_img = (single (in_img) - single (color_shift)) ./ single (intensity_range); else out_img = in_img; endif endfunction %!error id=Octave:invalid-fun-call imfuse () %!error id=Octave:invalid-input-arg imfuse (1, 1, "xxx") %!error id=Octave:invalid-input-arg imfuse (1, 1, "interpolation") %!error id=Octave:invalid-input-arg imfuse (1, 1, "ColorChannels", [0 0 0]) %!error id=Octave:invalid-input-arg imfuse (1, 1, "ColorChannels", [1 1 1]) %!error id=Octave:invalid-input-arg imfuse (1, 1, "ColorChannels", [2 2 2]) %!error id=Octave:expected-less-equal imfuse (1, 1, "ColorChannels", [42 0 0]) %!error id=Octave:expected-greater-equal imfuse (1, 1, "ColorChannels", [-1 2 0]) %!error id=Octave:invalid-input-arg imfuse (1, 1, "ColorChannels", "deep-purple") %!assert (imfuse (1, 2, "blend"), uint8 (0)) %!assert (imfuse (1, 2, "blend", "Scaling", "independent"), uint8 (0)) %!assert (imfuse (1, 2, "blend", "Scaling", "joint"), uint8 (128)) %!assert (imfuse (1, 2, "blend", "Scaling", "none"), uint8 (255)) %!assert (imfuse (1, 2, "falsecolor"), uint8 (zeros (1, 1, 3))) %!test %! a = [0 1 2]; %! b = [0 10 20]; %! expected = uint8 (repmat ([0 128 255], [1 1 3])); %! assert (imfuse (a, b), expected); %!test %! a = uint8 ([0 1 2]); %! b = uint8 ([0 10 20]); %! expected = uint8 (repmat ([0 128 255], [1 1 3])); %! assert (imfuse (a, b), expected); %!test %! a = uint8 ([0 1 2]); %! b = uint8 ([0 10 20]); %! expected = uint8 (repmat ([0 128 255], [1 1 3])); %! assert (imfuse (a, b, "falsecolor"), expected); %!test %! a = logical([0 1 1]); %! b = logical([0 1 1]); %! expected = uint8 (repmat ([0 255 255], [1 1 3])); %! assert (imfuse (a, b), expected); %!test %! a = logical([0 1 1]); %! b = logical([0 1 1]); %! expected = uint8 (repmat ([0 255 255], [1 1 3])); %! assert (imfuse (a, b, "falsecolor"), expected); %!test %! a = [0 1 2]; %! b = [0 10 20]; %! expected = uint8 (repmat ([0 255 255], [1 1 3])); %! assert (imfuse (a, b, "Scaling", "none"), expected); %!test %! a = uint8 ([0 1 2]); %! b = uint8 ([0 10 20]); %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 10 20]; %! expected(:, :, 2) = [0 1 2]; %! expected(:, :, 3) = [0 10 20]; %! assert (imfuse (a, b, "Scaling", "none"), expected); %!test %! a = [0 1 2]; %! b = uint8 ([0 10 20]); %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 10 20]; %! expected(:, :, 2) = [0 255 255]; %! expected(:, :, 3) = [0 10 20]; %! assert (imfuse (a, b, "Scaling", "none"), expected); %!test %! a = uint8 ([0 1 2]); %! b = [0 10 20]; %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 255 255]; %! expected(:, :, 2) = [0 1 2]; %! expected(:, :, 3) = [0 255 255]; %! assert (imfuse (a, b, "Scaling", "none"), expected); %!test %! a = [0 .1 2]; %! b = [0 .01 .02]; %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 3 5]; %! expected(:, :, 2) = [0 26 255]; %! expected(:, :, 3) = [0 3 5]; %! assert (imfuse (a, b, "Scaling", "none"), expected); %!test %! a = [0 1 2]; %! b = [0 10 20]; %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 128 255]; %! expected(:, :, 2) = [0 13 26]; %! expected(:, :, 3) = [0 128 255]; %! assert (imfuse (a, b, "Scaling", "joint"), expected); %!test %! a = uint8 ([0 1 2]); %! b = [0 10 20]; %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 128 255]; %! expected(:, :, 2) = [0 13 26]; %! expected(:, :, 3) = [0 128 255]; %! assert (imfuse (a, b, "Scaling", "joint"), expected); %!test %! a = [0 150 300]; %! b = uint8 ([0 10 20]); %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 9 17]; %! expected(:, :, 2) = [0 128 255]; %! expected(:, :, 3) = [0 9 17]; %! assert (imfuse (a, b, "Scaling", "joint"), expected); %!test %! a = uint8 ([0 1 2]); %! b = uint8 ([0 10 20]); %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 128 255]; %! expected(:, :, 2) = [0 13 26]; %! expected(:, :, 3) = [0 128 255]; %! assert (imfuse (a, b, "Scaling", "joint"), expected); %!test %! a = [0 1 2]; %! b = [0 10 20]; %! expected = uint8 (zeros ([1, 3, 3])); %! expected(:, :, 1) = [0 0 0]; %! expected(:, :, 2) = [0 128 255]; %! expected(:, :, 3) = [0 13 26]; %! assert (imfuse (a, b, "Scaling", "joint", "ColorChannels", [0 2 1]), expected); %!test %! a = [0 1 2]; %! b = [0 10 15]; %! c = imfuse (a, b, "ColorChannels", "red-cyan"); %! expected = uint8 (zeros (1, 3, 3)); %! expected(:, :, 1) = [0 128 255]; %! expected(:, :, 2) = [0 170 255]; %! expected(:, :, 3) = [0 170 255]; %! assert (c, expected); %!test %! a = [0 1 2]; %! b = [0 10 15]; %! c = imfuse (a, b, "ColorChannels", "green-magenta"); %! expected = uint8 (zeros (1, 3, 3)); %! expected(:, :, 1) = [0 170 255]; %! expected(:, :, 2) = [0 128 255]; %! expected(:, :, 3) = [0 170 255]; %! assert (c, expected); %!test %! a = [0 5 2]; %! b = [0 10 20]; %! assert (imfuse (a, b, "diff"), uint8 ([0 213 255])); %!test %! a = [0 5 2]; %! b = [0 10 20]; %! assert (imfuse (a, b, "diff", "Scaling", "joint"), uint8 ([0 71 255])); %!test %! a = [0 5 2]; %! b = [0 10 20]; %! assert (imfuse (a, b, "blend"), uint8 ([0 192 179])); %!test %! a = magic (5); %! b = a'; %! c = imfuse (a, b, "falsecolor"); %! expected = zeros (5, 5, 3); %! expected(:, :, 1) = [ %! 170 234 32 96 106 %! 244 43 53 117 181 %! 0 64 128 191 255 %! 74 138 202 213 11 %! 149 159 223 21 85]; %! expected(:, :, 2) = [ %! 170 244 0 74 149 %! 234 43 64 138 159 %! 32 53 128 202 223 %! 96 117 191 213 21 %! 106 181 255 11 85]; %! expected(:, :, 3) = [ %! 170 234 32 96 106 %! 244 43 53 117 181 %! 0 64 128 191 255 %! 74 138 202 213 11 %! 149 159 223 21 85]; %! assert (c, uint8 (expected)); %!test %! a = magic (5); %! b = a'; %! assert (imfuse (uint8 (a), uint8 (b), "blend", "Scaling", "none"), %! uint8 ([17 24 3 9 13 %! 24 5 7 13 17 %! 3 7 13 20 24 %! 9 13 20 21 3 %! 13 17 24 3 9])); %!test %! a = magic (5); %! b = 2 * a'; %! assert (imfuse (a, b, "blend", "Scaling", "independent"), %! uint8 ([170 239 16 85 128 %! 239 43 59 128 170 %! 16 59 128 197 239 %! 85 128 197 213 16 %! 128 170 239 16 85])); %!test %! a = magic (5); %! b = 2 * a'; %! assert (imfuse (a, b, "blend", "Scaling", "joint"), %! uint8 ([128 177 18 68 91 %! 180 34 44 94 130 %! 11 47 96 146 182 %! 63 99 149 159 13 %! 102 125 175 16 65])); %!test %! a = [0 1.2 5]; %! b = [5 6.13 12]; %! assert (imfuse (a, b, "blend"), uint8 ([0 51 255])); %!test %! a = [0 5 2]; %! b = [0 10 20]; %! assert (imfuse (a, b, "blend", "Scaling", "joint"), uint8 ([0 96 141])); %!test %! a = [0 5 2]; %! b = [0 10 20]; %! assert (imfuse (a, b, "montage"), uint8 ([0 255 102 0 128 255])); %!test %! a = zeros (1, 100); %! b = 2 * ones (1, 100); %! assert (imfuse (a, b, "montage"), uint8 ([zeros(1, 200)])); %! assert (imfuse (a, b, "montage", "Scaling", "none"), %! uint8 ([zeros(1, 100), 255 * ones(1, 100)])); %!test %! a = zeros (1, 100, 3); %! b = 2 * ones (1, 100); %! assert (imfuse (a, b, "montage"), uint8 ([zeros(1, 200, 3)])); %!test %! a = 0.1 * ones (50, 50); %! b = 0.2 * ones (50, 50); %! c = imfuse (a, b, "checkerboard", "Scaling", "none"); %! d = imresize (repmat([26, 51; 51, 26], [8, 8]), [50, 50], "nearest"); %! assert (all (c(:) == d(:))); %!test %! a = zeros (2, 2); %! b = zeros (2, 2); %! ra = imref2d (size (a), [0, 2], [0, 2]); %! rb = imref2d (size (b), [0, 2], [2, 4]); %! [c, rc] = imfuse (a, ra, b, rb, "falsecolor"); %! assert (rc.ImageSize, [4, 2]); %! assert (rc.XWorldLimits, [0, 2]); %! assert (rc.YWorldLimits, [0, 4]); %! assert (rc.PixelExtentInWorldX, 1); %! assert (rc.PixelExtentInWorldY, 1); %! assert (rc.ImageExtentInWorldX, 2); %! assert (rc.ImageExtentInWorldY, 4); %! assert (rc.XIntrinsicLimits, [0.5, 2.5]); %! assert (rc.YIntrinsicLimits, [0.5, 4.5]); %! assert (c, uint8 (zeros (4, 2, 3))); %!xtest %! a = zeros (5, 3); %! b = ones (6, 5); %! ra = imref2d (size (a), [15, 30], [2, 4]); %! rb = imref2d (size (b), [10, 50], [5.5, 6.7]); %! [c, rc] = imfuse (a, ra, b, rb, "falsecolor"); %! assert (rc.ImageSize, [24, 8]); %! assert (rc.XWorldLimits, [10, 50]); %! assert (rc.YWorldLimits, [2, 6.7]); %! assert (rc.PixelExtentInWorldX, 5); %! assert (rc.PixelExtentInWorldY, 0.19583333, 10e-9); %! assert (rc.ImageExtentInWorldX, 40); %! assert (rc.ImageExtentInWorldY, 4.7); %! assert (rc.XIntrinsicLimits, [0.5, 8.5]); %! assert (rc.YIntrinsicLimits, [0.5, 24.5]); %! expected = uint8 (zeros (24, 8, 3)); %! expected(19:23, 2:7, 1) = 255 * ones (5, 6); %! expected(19:23, 2:7, 3) = 255 * ones (5, 6); %! assert (c, expected); %!test %! a = uint8 (reshape (1:1:9, [1 3 3])); %! b = uint8 (reshape (10:2:26, [1 3 3])); %! c = imfuse (a, b); %! expected = uint8 (zeros (1, 3, 3)); %! expected(:, :, 1) = [0 128 255]; %! expected(:, :, 2) = [0 128 255]; %! expected(:, :, 3) = [0 128 255]; %! assert (c, expected); %!test %! a = uint8 (reshape (1:1:9, [1 3 3])); %! b = uint8 (reshape (10:2:26, [1 3 3])); %! c = imfuse (a, b, "Scaling", "independent"); %! expected = uint8 (zeros (1, 3, 3)); %! expected(:, :, 1) = [0 128 255]; %! expected(:, :, 2) = [0 128 255]; %! expected(:, :, 3) = [0 128 255]; %! assert (c, expected); %!test %! a = uint8 (reshape (1:1:9, [1 3 3])); %! b = uint8 (reshape (10:2:26, [1 3 3])); %! c = imfuse (a, b, "Scaling", "joint"); %! expected = uint8 (zeros (1, 3, 3)); %! expected(:, :, 1) = [191 223 255]; %! expected(:, :, 2) = [0 16 32]; %! expected(:, :, 3) = [191 223 255]; %! assert (c, expected); %!test %! a = uint8 (reshape (1:1:9, [1 3 3])); %! b = uint8 (reshape (10:2:26, [1 3 3])); %! c = imfuse (a, b, "Scaling", "none"); %! expected = uint8 (zeros (1, 3, 3)); %! expected(:, :, 1) = [15 17 19]; %! expected(:, :, 2) = [3 4 5]; %! expected(:, :, 3) = [15 17 19]; %! assert (c, expected); %!xtest %! a = zeros (5, 3); %! b = ones (5, 3); %! ra = imref2d (size (a), [10, 20], [30, 40]); %! rb = imref2d (size (b), [10, 20], [30, 40]); %! [c, rc] = imfuse (a, ra, b, rb, "falsecolor"); %! expected = uint8 (zeros (5, 3, 3)); %! expected(:, 1:2, 1) = 255 * ones (5, 2); %! expected(:, 1:2, 3) = 255 * ones (5, 2); %! assert (rc.ImageSize, [5, 3]); %! assert (rc.XWorldLimits, [10, 20]); %! assert (rc.YWorldLimits, [30, 40]); %! assert (rc.PixelExtentInWorldX, 3.33333333, 10e-9); %! assert (rc.PixelExtentInWorldY, 2); %! assert (rc.ImageExtentInWorldX, 10); %! assert (rc.ImageExtentInWorldY, 10); %! assert (rc.XIntrinsicLimits, [0.5, 3.5]); %! assert (rc.YIntrinsicLimits, [0.5, 5.5]); %! assert (c, expected); %!test %! a = zeros (5, 5); %! b = ones (5, 5); %! ra = imref2d (size (a), [10, 20], [30, 40]); %! rb = imref2d (size (b), [10, 20], [30, 40]); %! [c, rc] = imfuse (a, ra, b, rb, "falsecolor"); %! expected = uint8 (zeros (5, 5, 3)); %! assert (rc.ImageSize, [5, 5]); %! assert (rc.XWorldLimits, [10, 20]); %! assert (rc.YWorldLimits, [30, 40]); %! assert (rc.PixelExtentInWorldX, 2); %! assert (rc.PixelExtentInWorldY, 2); %! assert (rc.ImageExtentInWorldX, 10); %! assert (rc.ImageExtentInWorldY, 10); %! assert (rc.XIntrinsicLimits, [0.5, 5.5]); %! assert (rc.YIntrinsicLimits, [0.5, 5.5]); %! assert (c, expected); %!test %! a = magic (5); %! b = ones (5, 5); %! ra = imref2d (size (a), [10, 20], [30, 40]); %! rb = imref2d (size (b), [10, 20], [30, 40]); %! [c, rc] = imfuse (a, ra, b, rb, "falsecolor", "Scaling", "independent"); %! expected = uint8 (zeros (5, 5, 3)); %! expected(:, :, 2) = [ %! 170 244 0 74 149 %! 234 43 64 138 159 %! 32 53 128 202 223 %! 96 117 191 213 21 %! 106 181 255 11 85]; %! assert (rc.ImageSize, [5, 5]); %! assert (rc.XWorldLimits, [10, 20]); %! assert (rc.YWorldLimits, [30, 40]); %! assert (rc.PixelExtentInWorldX, 2); %! assert (rc.PixelExtentInWorldY, 2); %! assert (rc.ImageExtentInWorldX, 10); %! assert (rc.ImageExtentInWorldY, 10); %! assert (rc.XIntrinsicLimits, [0.5, 5.5]); %! assert (rc.YIntrinsicLimits, [0.5, 5.5]); %! assert (c, expected); image-2.16.1/inst/PaxHeaders.61586/affine3d.m0000644000000000000000000000006215005110255015237 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/affine3d.m0000644000175000017500000001363715005110255016647 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Carnë Draug ## Copyright (C) 2015 Motherboard ## ## 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 . classdef affine3d < affine ## -*- texinfo -*- ## @deftypefn {Function File} {@var{tform} =} affine3d (@var{T}) ## @deftypefnx {Function File} {@var{tform} =} affine3d () ## tform is a representation of an affine 3D transform. ## Calling affine3D without parameters, (@code{affine3d ()}) produces the identity ## transformation. ## ## affine3d takes a transpose of the affine matrix as described in ## standard literature it performs the transformation as follows: ## v = (u*T)(1:3) ## where u = [x y z 1] and T = [a b c 0; d e f 0; g h i 0; j k l 1] ## [a b c; d e f; g h i] is a transposed rotation\shear matrix, ## [j k l] is the translation vector, where j = dx, k = dy, l = dz. ## ## affine3d methods: ## @table @asis ## @item @qcode{"invert"} ## @code{invert (tform)} - produces the inverse transform of affine3d transform ## @item @qcode{"isRigid"} ## @code{isRigid (tform)} - checks if transform tform is only rotation or translation. ## @item @qcode{"isSimilarity"} ## @code{isSimilarity (tform)} - checks if transform tform is only homogeneous scaling, ## rotation, reflection or translation. ## @item @qcode{"isTranslation"} ## @code{isTranslation (tform)} - checks if transform tform is is a pure translation ## @item @qcode{"outputLimits"} ## @code{outputLimits (tform, xlims, ylims, zlims)} - given a bounding cube corner ## coordinates in xlims, ylims and zlims (top left front, right bottom back) - ## returns the new bounding cube after transformation. ## @item @qcode{"transformPointsForward"} ## @code{transformPointsForward(tform, u, v, w)} - apply transformation tform ## on the set of u, v, w points (1xn vectors) ## @code{transformPointsForward(tform, U)} - apply transformation tform ## on U (3xn matrix) ## @item @qcode{"transformPointsInverse"} ## @code{transformPointsInverse(tform, u, v, w)} - apply the inverse transformation ## of tform on the set of u, v, w points (1xn vectors) ## @code{transformPointsInverse(tform, U)} - apply the inverse transformation ## of tform on U (3xn matrix) ## ## @end table ## ## @seealso{affine2d} ## @end deftypefn methods function this = affine3d (T) if (nargin > 1) error ("affine3d: usage - affine3d(T), where T is a 4x4 matrix") endif if (nargin == 0) T = eye(4); endif this@affine (T); endfunction ## given a bounding cube corner coordinates in xlims, ylims and ## zlims (top left front, right bottom back) - return the new ## bounding cube after transformation. function [limitsX, limitsY, limitsZ] = outputLimits (this, xlims, ylims, zlims) if (nargin ~= 4) error ("outputLimits usage: outputLimits (tform, xlims, ylims, zlims)") endif xlims2 = [xlims(:); xlims(:); xlims(:); xlims(:)]; ylims2 = [ylims(:); ylims(end:-1:1)(:); ylims(:); ylims(end:-1:1)(:)]; zlims2 = [zlims(:); zlims(:); zlims(end:-1:1)(:); zlims(end:-1:1)(:)]; temp = [xlims2 ylims2 zlims2 ones(8,1)]; temp = temp*this.T; limitsX = [min(temp(1:8,1)), max(temp(1:8,1))]; limitsY = [min(temp(1:8,2)), max(temp(1:8,2))]; limitsZ = [min(temp(1:8,3)), max(temp(1:8,3))]; endfunction endmethods endclassdef %!test %! Sx = 1.2; %! Sy = 1.6; %! Sz = 2.4; %! A = [Sx 0 0 0; 0 Sy 0 0; 0 0 Sz 0; 0 0 0 1]; %! tform = affine3d (A); %! [X, Y, Z] = transformPointsForward (tform, 5, 10, 3); %! assert ([X Y Z], [6 16 7.2], 5*eps) %! [U, V, W] = transformPointsInverse (tform, X, Y, Z); %! assert ([U V W], [5 10 3], eps) %! assert (! isRigid (tform)) %! assert (! isTranslation (tform)) %! assert (! isSimilarity (tform)) %!test %! A = [3 1 2 0; 4 5 8 0; 6 2 1 0; 0 0 0 1]; %! tform = affine3d (A); %! [X, Y, Z] = transformPointsForward (tform, 2, 3, 5); %! assert (X, 48, eps) %! assert (Y, 27, eps) %! assert (Z, 33, eps) %! [U, V, W] = transformPointsInverse (tform, X, Y, Z); %! assert (U, 2, 50*eps) %! assert (V, 3, 50*eps) %! assert (W, 5, 50*eps) %! assert (! isRigid (tform)) %! assert (! isTranslation (tform)) %! assert (! isSimilarity (tform)) %!test %! A = [1 0 0 0; 0 1 0 0; 0 0 1 0; 5 10 1 1]; %! tform = affine3d (A); %! X = transformPointsForward (tform, [1 2 3; 4 5 6; 7 8 9]); %! assert (round (X), [6, 12, 4; 9, 15, 7; 12, 18, 10]) %! U = transformPointsInverse (tform, X); %! assert (round (U), [1 2 3; 4 5 6; 7 8 9]) %! assert (isRigid (tform)) %! assert (isTranslation (tform)) %! assert (isSimilarity (tform)) %!test %! Sx = 1.2; %! Sy = 1.6; %! Sz = 2.4; %! A = [Sx 0 0 0; 0 Sy 0 0; 0 0 Sz 0; 0 0 0 1]; %! tform = affine3d (A); %! [xlim, ylim, zlim] = outputLimits (tform, [1 128], [1 128], [1 27]); %! assert (xlim, [ 1.2000 153.6000],1e-8) %! assert (ylim, [1.6000 204.8000], 1e-8) %! assert (zlim, [2.4000 64.8000], 1e-8) %!error affine3d (1, 2) %!error outputLimits (affine2d()) %!test %! a = 23; %! M = [cosd(a) 0 sind(a) 0; %! 0 1 0 0; %! -sind(a) 0 cosd(a) 0; %! 0 0 0 1]; %! tform = affine3d (M); %! tform2 = invert (tform); %! assert (tform.T * tform2.T, diag([1 1 1 1]), eps); %!test %! tform = affine3d; %! assert (tform.T, eye (4)) %! assert (tform.Dimensionality, 3) image-2.16.1/inst/PaxHeaders.61586/isgray.m0000644000000000000000000000006215005110255015056 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/isgray.m0000644000175000017500000000554115005110255016461 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## Copyright (C) 2011, 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} isgray (@var{img}) ## Return true if @var{img} is a grayscale image. ## ## A variable can be considered a grayscale image if it is a non-sparse, ## real array of size @nospell{MxNx1xK}, and: ## ## @itemize @bullet ## @item of floating point class with values in the [0 1] range or NaN; ## @item of class uint8, uint16, or int16. ## @end itemize ## ## @emph{Note}: despite their suggestive names, the functions isbw, ## isgray, isind, and isrgb, are ambiguous since it is not always possible ## to distinguish between those image types. For example: an uint8 matrix ## can be both a grayscale and indexed image; a grayscale image may have ## values outside the range [0 1]. They are good to dismiss input as an ## invalid image type, but not for identification. ## ## @seealso{gray2ind, isbw, isind, isrgb} ## @end deftypefn function bool = isgray (img) if (nargin () != 1) print_usage (); endif bool = false; if (isimage (img) && ndims (img) < 5 && size (img, 3) == 1) if (isfloat (img)) bool = ispart (@is_float_image, img); elseif (any (isa (img, {"uint8", "uint16", "int16"}))) bool = true; endif endif endfunction %!assert (isgray ([0 0 1; 1 0 1]), true) %!assert (isgray (zeros (3)), true) %!assert (isgray (ones (3)), true) %!test %! a = rand (10); %! assert (isgray (a), true); %! a(5, 5) = 2; %! assert (isgray (a), false); %!test %! a = uint8 (randi (255, 10)); %! assert (isgray (a), true); %! a = int8 (a); %! assert (isgray (a), false); %!test %! a = rand (10); %! a(50) = NaN; %! assert (isgray (a), true); %!assert (isgray (rand (5, 5, 1, 4)), true); %!assert (isgray (rand (5, 5, 3, 4)), false); %!assert (isgray (rand (5, 5, 3)), false); %!assert (isgray (rand (5, 5, 1, 3, 4)), false); %!assert (isgray (rand (5, "single")), true) ## While having some NaN is ok, having all NaN is not %!assert (isgray ([.1 .2 .3; .4 NaN .6; .7 .8 .9]), true) %!assert (isgray ([.1 .2 .3; NA NaN .6; .7 .8 .9]), true) %!assert (isgray ([.1 .2 .3; NA .5 .6; .7 .8 .9]), true) %!assert (isgray (NaN (5)), false) %!assert (isgray (NA (5)), false) image-2.16.1/inst/PaxHeaders.61586/stretchlim.m0000644000000000000000000000006215005110255015736 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/stretchlim.m0000644000175000017500000003264115005110255017342 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Monés i Teixidor ## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} stretchlim (@var{I}) ## @deftypefnx {Function File} {} stretchlim (@var{RGB}) ## @deftypefnx {Function File} {} stretchlim (@dots{}, @var{tol}) ## Find limits to contrast stretch an image. ## ## Returns a 2 element column vector, @code{[@var{low}; @var{high}]}, ## with the pair of intensities to contrast stretch @var{I} which ## saturates at most @var{tol} of the image. The output of this ## function matches the input expected by @code{imadjust}. ## ## The input argument @var{tol}, controls the fraction of the image to be ## saturated and defaults to @code{[0.01 0.99]}, i.e., a 1% saturation ## on both sides of the image histogram. It can be specified in two ways: ## ## @itemize ## @item a two element vector with lower and higher fraction of the ## the image to be saturated. These values must be in the range ## [0 1], the display range of images of floating point class. ## ## @item a scalar value with the fraction of image to be saturated on ## each side; e.g., a @var{tol} with a value of @code{0.05} is equivalent ## to @code{[0.05 0.95]}. This value must be in the range @code{[0 0.5]}. ## ## @end itemize ## ## A common use case wanting to maximize contrast without causing any ## saturation. In such case @var{tol} should be 0 (zero). It is the ## equivalent to @code{[min(@var{I}(:)); max(@var{I}(:))]} for a single ## plane. ## ## The return values are of class double and in the range [0 1] regardless ## of the input image class. These values are scaled in the image class ## range (see @code{im2double}). ## ## If the input is a RGB image, i.e., the 3rd dimension has length 3, then ## it returns a @code{[2 3]} matrix with separate limits for each colour. ## It will actually do this for each plane, so an input of size, ## @code{[M N P]} will return a @code{[2 P]} matrix. ## ## Higher dimensions of @var{I} are also valid. As in the 3 dimensional ## case, the limits for each 2 dimensional plane are returned in a 2 row ## vector for each 2 dimensional plane, one dimension below. That is, an ## input of size @code{[M N P Q R]} will return an array of size ## @code{[2 P Q R]}. ## ## Note the detail that @var{tol} is the maximum fraction of saturation. ## It is rare that there is a value for that exact saturation. In such ## case, @code{stretchlim} will always round down and saturate less. ## @var{tol} is the saturation limit. For example, if @var{tol} is ## @code{0.10}, but there are only values that will lead to 5 or 11% ## saturation, it will return the value for a 5% saturation. ## ## @seealso{brighten, contrast, histeq, imadjust} ## @end deftypefn function low_high = stretchlim (img, tol = [0.01 0.99]) if (nargin () <1 || nargin () > 2) print_usage (); endif if (! isimage (img)) error("stretchlim: I or RGB must be an image"); endif ## Handle tol if (nargin > 1) if (! isnumeric (tol)) error ("stretchlim: TOL must be numeric"); endif if (isscalar (tol)) if (min (tol) < 0 || max (tol) > 0.5) error ("stretchlim: TOL must be in the [0 1] range"); endif tol = [tol (1-tol)]; elseif (isvector (tol)) if (numel (tol) != 2) error ("stretchlim: TOL must be a 2 element vector"); endif endif endif ## tol is about the percentage of values that will be saturated. ## So instead of percentages, we convert to the actual number of ## pixels that need to be saturated. After sorting the values in ## the image, that number of pixels simply becomes the index for ## the limits. ## ## Note that the actual intensity value that we set the limits to, ## is not saturated. Only the values below or above the lower and ## higher limits it will be considered saturated. ## ## And since most images will have repeated values in the pixels, ## chances are that there's not a limit that would cause only the ## exact percentage of pixels to be saturated. In such cases, we ## must prefer a limit that would saturate less pixels than the ## requested, rather than the opposite. ## ## We want to compute this for each plane, so we reshape the image ## in order to have each plane into a single column, while respecting ## any other dimensions beyond the 3rd. sz = postpad (size (img), max (3, ndims (img)), 1); plane_length = sz(1) * sz(2); img = reshape (img, [plane_length sz(3:end)]); lo_idx = floor (tol(1) * plane_length) + 1; hi_idx = ceil (tol(2) * plane_length); if (lo_idx == 1 && hi_idx == plane_length) ## special case, equivalent to tol [0 1], even if tol was not ## actually [0 1] but the image size would effectively make it. low_high = [min(img, [], 1); max(img, [], 1)]; else strides = reshape ((0:plane_length:(numel(img)-1)), [1 sz(3:end)]); lo_hi_idx = [lo_idx; hi_idx] + strides; sorted = sort (img, 1); low_high = sorted(lo_hi_idx); endif low_high = im2double (low_high); ## This will only happen if the input was floating point and with ## values already outside the [0 1] range (common for users to simply ## convert an uint8 image to double and forget that display is [0 1] ## only). low_high(low_high < 0) = 0; low_high(low_high > 1) = 1; ## If a plane has a single value, then both limits will have the same ## value. In such case, we must return [0; 1] equal_cols = low_high(1,:) == low_high(2,:); low_high(:, equal_cols) = repmat ([0; 1], 1, nnz (equal_cols)); endfunction %!error (stretchlim ()); %!error (stretchlim ("bad parameter")); #%!error (stretchlim (zeros (10, 10, 3, 2))); %!error (stretchlim (zeros (10, 10), "bad parameter")); %!error (stretchlim (zeros (10, 10), 0.01, 2)); ## default parameters %!assert (stretchlim (0.01:.01:1), [0.02; 0.99]) %!assert (stretchlim (0.01:.01:1), stretchlim (0.01:.01:1, [0.01 0.99])) ## use scalar for tol %!assert (stretchlim (0.01:.01:1, 0.15), stretchlim (0.01:.01:1, [0.15 0.85])) ## this is different than Matlab but it looks like it's a Matlab bug ## (Matlab returns [0.018997482261387; 0.951003280689708]) ## We actually have differences from Matlab which sometimes returns ## values that are not present in the image. %!assert (stretchlim (0.01:.01:1, [0.01,0.95]), [0.02; 0.95], eps) ## corner case of zero tolerance %!assert (stretchlim (0.01:.01:1, 0), [0.01; 1]) %!test %! im = rand (5); %! assert (stretchlim (im, 0), [min(im(:)); max(im(:))]) %!test %! im = rand (5, 5, 3); %! assert (stretchlim (im, 0), %! [min(im(:,:,1)(:)) min(im(:,:,2)(:)) min(im(:,:,3)(:)); %! max(im(:,:,1)(:)) max(im(:,:,2)(:)) max(im(:,:,3)(:))]) ## corner case where tol is not zero but the image is so small that ## it might as well be. %!test %! im = rand (5); %! assert (stretchlim (im, 0.03), [min(im(:)); max(im(:))]) %! assert (stretchlim (im, 0.0399), [min(im(:)); max(im(:))]) ## Test with non double data-types %!assert (stretchlim (uint8 (1:100)), im2double (uint8 ([2; 99]))) %!assert (stretchlim (uint8 (1:100), .25), im2double (uint8 ([26; 75]))) %!assert (stretchlim (uint16 (1:1000)), im2double (uint16 ([11; 990]))) %!assert (stretchlim (int16 (-100:100)), im2double (int16 ([-98; 98]))) %!assert (stretchlim (single (0.01:.01:1)), %! double (single (0.01:.01:1)([2; 99])).') ## non uniform histogram tests %!assert (stretchlim (uint8 ([1 repmat(2, [1, 90]) 92:100]), 0.05), %! im2double (uint8 ([2; 95]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 4]) 6:100]), 0.05), %! im2double (uint8 ([6; 95]))) ## test limit rounding (actually, lack of rounding, we always round down) ## Note that this tests were different in the image package before v2.6. ## Back then we performed rounding of the fraction that was saturated. %!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) 7:100]), 0.05), %! im2double (uint8 ([2; 95]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 6]) 8:100]), 0.05), %! im2double (uint8 ([2; 95]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 7]) 9:100]), 0.05), %! im2double (uint8 ([2; 95]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 8]) 10:100]), 0.05), %! im2double (uint8 ([2; 95]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.04), %! im2double (uint8 ([2; 96]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.05), %! im2double (uint8 ([2; 95]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.06), %! im2double (uint8 ([3; 94]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.07), %! im2double (uint8 ([3; 93]))) %!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.08), %! im2double (uint8 ([3; 92]))) ## test RGB %!test %! RGB = zeros (100, 1, 3, "uint16"); %! RGB(:,:,1) = [1:1:100]; %! RGB(:,:,2) = [2:2:200]; %! RGB(:,:,3) = [4:4:400]; %! assert (stretchlim (RGB) , im2double (uint16 ([2 4 8; 99 198 396]))) ## test other 3D lengths %!test %! im6c = zeros (100, 1, 6, "uint16"); %! im6c(:,:,1) = [1:1:100]; %! im6c(:,:,2) = [2:2:200]; %! im6c(:,:,3) = [4:4:400]; %! im6c(:,:,4) = [8:8:800]; %! im6c(:,:,5) = [16:16:1600]; %! im6c(:,:,6) = [32:32:3200]; %! assert (stretchlim (im6c) , %! im2double (uint16 ([2 4 8 16 32 64; 99 198 396 792 1584 3168]))) %!test %! im = [0 0 .1 .1 .1 .1 .2 .2 .2 .4 .4 .6 .6 .7 .7 .9 .9 .9 1 1]; %! %! assert (stretchlim (im), [0; 1]) %! %! ## Consider the returned lower limit in this test. A lower limit %! ## of 0.1 will saturate two elements (10%), while 0.2 will saturate %! ## 6 elements (30%). Both have the same distance to 20% but returning %! ## 0.1 is Matlab compatible. %! ## Now looking at the higher limit. A limit of .9 will saturate %! ## 2 elements (10%), while a limit of 0.7 will saturate 5 elements (25%). %! ## However, for Matlab compatibility we must return .9 even though %! ## 25% would be closer to 20%. %! ## Basically, it's not just rounded. %! assert (stretchlim (im, .2), [0.1; 0.9]) %! %! assert (stretchlim (im, .15), [0.1; 0.9]) %! assert (stretchlim (im, .1), [0.1; 0.9]) %! assert (stretchlim (im, .25), [0.1; 0.7]) %! %! ## Reorder the vector of values (real images don't have the values %! ## already sorted), just to be sure it all works. %! im([6 3 16 11 7 17 14 8 5 19 15 1 2 4 18 13 9 20 10 12]) = im; %! assert (stretchlim (im, .2), [0.1; 0.9]) %! assert (stretchlim (im, .15), [0.1; 0.9]) %! assert (stretchlim (im, .1), [0.1; 0.9]) %! assert (stretchlim (im, .25), [0.1; 0.7]) ## odd length images to test rounding of saturated fraction. With a 1% ## fraction to be saturated and 991 elements, that's 9.91 pixels. Since ## TOL is the limit, we must saturate the top and bottom 9 pixels (not 10). %!assert (stretchlim (0.01:.001:1), [0.019; 0.991], eps) %!assert (stretchlim (0.01:.001:1, [0.01,0.95]), [0.019; 0.951], eps) %!assert (stretchlim (0.01:.001:1, 0), [0.01; 1], eps) %!assert (stretchlim (single (0.01:.001:1)), %! double (single (0.01:.001:1)([10; 982])).', eps) ## Test ignoring floating point values outside [0 1] and how they do ## count for the fraction of saturated values, but are simply clipped ## to the [0 1] range %!test %! assert (stretchlim ([(.05:.05:1) (2:4)], 0.2), [0.25; 0.95], eps) %! assert (stretchlim ([(.05:.05:1) (2:5)], 0.2), [0.25; 1], eps) %! assert (stretchlim ([(.05:.05:1) (2:6)], 0.2), [0.3; 1], eps) %! assert (stretchlim ([(.05:.05:1) (2:7)], 0.2), [0.3; 1], eps) %!test %! assert (stretchlim ([(-6:0) (.05:.05:1)], 0.2), [0; 0.75], eps) %! assert (stretchlim ([(-5:0) (.05:.05:1)], 0.2), [0; 0.75], eps) ## Test N dimensions %!test %! im = rand (4, 4, 2, 3, 2); %! rv = zeros (2, 2, 3, 2); %! for p = 1:2 %! for q = 1:3 %! for r = 1:2 %! rv(:,p,q,r) = stretchlim (im(:,:,p,q,r), 0.25); %! endfor %! endfor %! endfor %! assert (stretchlim (im, 0.25), rv) ## The same but important because of our special code path for tol == 0 %!test %! im = rand (4, 4, 2, 3, 2); %! rv = zeros (2, 2, 3, 2); %! for p = 1:2 %! for q = 1:3 %! for r = 1:2 %! rv(:,p,q,r) = stretchlim (im(:,:,p,q,r), 0); %! endfor %! endfor %! endfor %! assert (stretchlim (im, 0), rv) ## Corner case there's a single value in the whole image %!assert (stretchlim (zeros (5)), [0; 1]) %!assert (stretchlim (ones (5)), [0; 1]) %!assert (stretchlim (.6 * ones (5)), [0; 1]) %!assert (stretchlim (zeros (3, 3, 3, 3)), repmat ([0; 1], [1 3 3])) %!assert (stretchlim ([0 .5 .5 .5 .5 1], .2), [0; 1]) ## Cornier case when some of the planes have a single unique value %!test %! im = repmat ((magic (5) -1) / 24, [1 1 3 3]); %! im(:,:,1,1) = 0; %! im(:,:,2,2) = .5; %! im(:,:,3,3) = 1; %! lims = stretchlim (im, 0.2); %! assert (size (lims), [2 3 3]) %! assert (lims(:, [2 3 4 6 7 8]), %! repmat ([(1/24)*round(24*.2); 1-((1/24)*round(24*.2))], [1 6]), eps) %! assert (lims(:, [1 5 9]), repmat ([0; 1], [1 3])) image-2.16.1/inst/PaxHeaders.61586/imsubtract.m0000644000000000000000000000006215005110255015735 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imsubtract.m0000644000175000017500000001022115005110255017327 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{out} =} imsubtract (@var{a}, @var{b}) ## @deftypefnx {Function File} {@var{out} =} imsubtract (@var{a}, @var{b}, @var{class}) ## Subtract image or constant to an image. ## ## If @var{a} and @var{b} are two images of same size and class, @var{b} is subtracted ## to @var{a}. Alternatively, if @var{b} is a floating-point scalar, its value is subtracted ## to the image @var{a}. ## ## The class of @var{out} will be the same as @var{a} unless @var{a} is logical ## in which case @var{out} will be double. Alternatively, it can be ## specified with @var{class}. ## ## @emph{Note 1}: you can force output class to be logical by specifying ## @var{class}. This is incompatible with @sc{matlab} which will @emph{not} honour ## request to return a logical matrix. ## ## @emph{Note 2}: the values are truncated to the mininum value of the output ## class. ## ## @emph{Note 3}: values are truncated before the operation so if input images are ## unsigned integers and the request output class is a signed integer, it may lead ## to unexpected results: ## ## @example ## @group ## imsubtract (uint8 ([23 190]), uint8 ([24 200]), "int8") ## @result{} -1 0 ## @end group ## @end example ## ## Because both 190 and 200 were truncated to 127 before subtraction, their difference ## is zero. ## @seealso{imabsdiff, imadd, imcomplement, imdivide, imlincomb, immultiply} ## @end deftypefn function img = imsubtract (img, val, out_class = class (img)) if (nargin < 2 || nargin > 3) print_usage; elseif (any (isa (img, {"uint8", "uint16", "uint32", "uint64"})) && any (strcmpi (out_class, {"int8", "int16", "int32", "int64"}))) ## because we convert the images before the subtraction, if input is: ## imsubtract (uint8(150), uint8 (200), "int8"); ## rsult will be 0 because both values are truncated to 127 before subtraction. ## There is no matlab compatibility issue because matlab does not have the option ## to specify output class in imsubtract warning ("input images are unsigned integers but requested output is signed integer. This may lead to unexpected results."); endif [img, val] = imarithmetics ("imsubtract", img, val, out_class); ## The following makes the code imcompatible with matlab on certain cases. ## This is on purpose. Read comments in imadd source for the reasons if (nargin > 2 && strcmpi (out_class, "logical")) img = img > val; else img = img - val; endif endfunction %!assert (imsubtract (uint8 ([23 250]), uint8 ([24 50])), uint8 ([ 0 200])); # default to first class and truncate %!assert (imsubtract (uint8 ([23 250]), 10), uint8 ([13 240])); # works subtracting a scalar %!assert (imsubtract (uint8 ([23 250]), uint8 ([24 50]), "uint16"), uint16 ([ 0 200])); # defining output class works (not in matlab) %!assert (imsubtract (logical ([ 1 0]), logical ([ 1 1])), double ([ 0 -1])); # return double for two logical images %!assert (imsubtract (logical ([ 1 0]), logical ([ 1 1]), "logical"), logical ([ 0 0])); # this is matlab incompatible on purpose ## input need to have same class %!error imsubtract (uint8 ([23 250]), uint16 ([23 250])); ## signed integers kinda work (not in matlab) %!warning imsubtract (uint8 ([23 250]), uint8 ([24 255]), "int8"); %!test %! warning ("off", "all"); %! assert (imsubtract (uint8 ([23 250]), uint8 ([24 255]), "int8"), %! int8 ([-1 0])) image-2.16.1/inst/PaxHeaders.61586/imclearborder.m0000644000000000000000000000006215005110255016372 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imclearborder.m0000644000175000017500000001150715005110255017774 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} imclearborder (@var{im}) ## @deftypefnx {Function File} {} imclearborder (@var{im}, @var{conn}) ## Clear borders of objects or ligher structures. ## ## On the simplest case of binary images, this function removes objects ## that touch the image borders. In the case of grayscale images, lighter ## regions (higher intensity values) that touch the image border get removed. ## ## To be more exact, this is equivalent to use @code{imreconstruct} using ## the @var{im} borders as marker, and setting all unchanged elements to ## a background value. ## ## Element connectivity @var{conn}, to define the size of objects, can be ## specified with a numeric scalar (number of elements in the neighborhood): ## ## @table @samp ## @item 4 or 8 ## for 2 dimensional matrices; ## @item 6, 18 or 26 ## for 3 dimensional matrices; ## @end table ## ## or with a binary matrix representing a connectivity array. Defaults to ## @code{conndef (ndims (@var{bw}), "maximal")} which is equivalent to ## @var{conn} of 8 and 26 for 2 and 3 dimensional matrices respectively. ## ## @seealso{imreconstruct} ## @end deftypefn function im = imclearborder (im, conn) if (nargin < 1 || nargin > 2) print_usage (); elseif (! isimage (im)) error ("imclearborder: IM must be an image"); endif if (nargin < 2) conn = conndef (ndims (im), "maximal"); else conn = conndef (conn); endif bg_val = cast (getrangefromclass (im)(1), class (im)); marker = get_borders (im, conn, bg_val); border_elems = imreconstruct (marker, im, conn) == im; im(border_elems) = bg_val; endfunction function borders = get_borders (im, conn, val) im_size = size (im); borders = repmat (val, im_size); tmp_idx = repmat ({":"}, [1 ndims(im)]); tmp_conn_idx = repmat ({":"}, [1 ndims(conn)]); for dim = 1:min (ndims (im), ndims (conn)) conn_idx = tmp_conn_idx; conn_idx{dim} = [1 3]; if (im_size(dim) == 1 || ! any (conn(conn_idx{:})(:))) continue endif idx = tmp_idx; idx{dim} = [1 im_size(dim)]; borders(idx{:}) = im(idx{:}); endfor endfunction ## TODO check what exactly does Matlab do with in grayscale images, specially ## in the case of signed integers and floating point with negative values. ## We are different from the Matlab documentation suggests but that's ## because Matlab documentation sounds wrong to me. %!test %! a = logical ([ %! 0 1 0 0 1 0 0 0 0 1 %! 1 0 0 0 0 1 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 0 1 0 1 0 1 0 0 1 %! 0 0 0 0 0 0 0 1 1 0 %! 0 0 1 0 0 1 0 1 0 0 %! 0 1 0 1 0 1 1 0 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 1 0 1 1 0 0 0 %! 0 0 0 1 1 0 0 0 1 0]); %! %! a4 = logical ([ %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 1 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 0 0 1 0 1 0 1 0 0 0 %! 0 0 0 0 0 0 0 1 1 0 %! 0 0 1 0 0 1 0 1 0 0 %! 0 1 0 0 0 1 1 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 1 1 0 0 0 %! 0 0 0 0 0 0 0 0 0 0]); %! %! a8 = logical ([ %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 1 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0]); %! %! assert (imclearborder (a, 4), a4) %! assert (imclearborder (a, [0 1 0; 1 1 1; 0 1 0]), a4) %! assert (imclearborder (a), a8) %! assert (imclearborder (a, 8), a8) %! assert (imclearborder (a, ones (3)), a8) %!test %! a = false (5, 5, 3); %! a(2:4,2:4,:) = true; %! assert (imclearborder (a, 4), a) %! %! a(1,2) = true; %! a4 = a; %! a4(:,:,1) = false; %! assert (imclearborder (a, 4), a4) image-2.16.1/inst/PaxHeaders.61586/entropyfilt.m0000644000000000000000000000006215005110255016137 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/entropyfilt.m0000644000175000017500000001363515005110255017545 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{E} =} entropyfilt (@var{im}) ## @deftypefnx{Function File} {@var{E} =} entropyfilt (@var{im}, @var{domain}) ## @deftypefnx{Function File} {@var{E} =} entropyfilt (@var{im}, @var{domain}, @var{padding}, @dots{}) ## Computes the local entropy in a neighbourhood around each pixel in an image. ## ## The entropy of the elements of the neighbourhood is computed as ## ## @example ## @var{E} = -sum (@var{P} .* log2 (@var{P})) ## @end example ## ## where @var{P} is the distribution of the elements of @var{im}. The distribution ## is approximated using a histogram with @var{nbins} cells. If @var{im} is ## @code{logical} then two cells are used. For other classes 256 cells ## are used. ## ## When the entropy is computed, zero-valued cells of the histogram are ignored. ## ## The neighbourhood is defined by the @var{domain} binary mask. Elements of the ## mask with a non-zero value are considered part of the neighbourhood. By default ## a 9 by 9 matrix containing only non-zero values is used. ## ## At the border of the image, extrapolation is used. By default symmetric ## extrapolation is used, but any method supported by the @code{padarray} function ## can be used. Since extrapolation is used, one can expect a lower entropy near ## the image border. ## ## @seealso{entropy, paddarray, stdfilt} ## @end deftypefn function retval = entropyfilt (I, domain = true (9), padding = "symmetric", varargin) ## Check input if (nargin == 0) error ("entropyfilt: not enough input arguments"); endif if (! isnumeric (I)) error ("entropyfilt: I must be numeric"); endif if (! isnumeric (domain) && ! islogical (domain)) error ("entropyfilt: DOMAIN must be a logical matrix"); endif domain = logical (domain); ## Get number of histogram bins if (islogical (I)) nbins = 2; else nbins = 256; endif ## Convert to 8 or 16 bit integers if needed ## (accepting single, int8, int16, int32, int64, uint64 is Octave-only) switch (class (I)) case {"double", "single", "int16", "int32", "int64", "uint16", "uint32", "uint64"} I = im2uint8 (I); # because this is what Matlab seems to do case {"logical", "int8", "uint8"} ## Do nothing otherwise error ("entropyfilt: cannot handle images of class '%s'", class (I)); endswitch ## Pad image pad = floor (size (domain)/2); I = padarray (I, pad, padding, varargin {:}); even = (round (size (domain)/2) == size (domain)/2); idx = cell (1, ndims (I)); for k = 1:ndims (I) idx {k} = (even (k)+1):size (I, k); endfor I = I (idx {:}); retval = __spatial_filtering__ (I, domain, "entropy", zeros (size (domain)), nbins); endfunction %!test %! a = log2 (9) * ones (5, 5); %! b = -(2*log2 (2/9) + log2 (1/9))/3; %! a(1,2:4) = b; %! a(5,2:4) = b; %! a(2:4,1) = b; %! a(2:4,5) = b; %! c = -(4*log2 (4/9) + 4*log2 (2/9) + log2 (1/9))/9; %! a(1,1) = c; %! a(5,1) = c; %! a(1,5) = c; %! a(5,5) = c; %! assert (entropyfilt (uint8 (magic (5)), ones (3, 3)), a, 2*eps); %!test %! assert (entropyfilt (uint8 (ones (10, 10))), zeros (10, 10)); ## some (Matlab compatible) tests on simple 2D-images (classes double, uint8, uint16): %!test %! A = zeros (3,3); %! B = ones (3,3); %! C = [1 1 1; 2 2 2; 3 3 3]; %! D = C'; %! E = ones (3,3); %! E(2,2) = 2; %! F = 3 .* ones (3,3); %! F(2,2) = 1; %! G = [-1 2 7; -5 2 8; -7 pi 9]; %! H = [5 2 8; 1 -3 1; 5 1 0]; %! Hf = mat2gray(H); %! X = uint8(abs(H)); %! P = [0.2 0.201 0.204; 0.202 0.203 0.205; 0.205 0.206 0.202]; %! Q = uint16([100 101 103; 100 105 102; 100 102 103]); %! R = uint8([1 2 3 4 5; 11 12 13 14 15; 21 22 4 5 6; 5 5 3 2 1; 15 14 14 14 14]); %! Aout = zeros (3); %! Bout = zeros (3); %! Cout = zeros (3); %! Dout = zeros (3); %! Eout = zeros (3); %! Fout = zeros (3); %! Gout_1 = -sum([2 7]./9.*log2([2 7]./9)); %! Gout_2 = -sum([3 6]./9.*log2([3 6]./9)); %! Gout_3 = -sum([4 5]./9.*log2([4 5]./9)); %! Gout = [Gout_1 Gout_2 Gout_3; Gout_1 Gout_2 Gout_3; Gout_1 Gout_2 Gout_3]; %! Hout_5 = -sum([2 7]./9.*log2([2 7]./9)) ; %! Hout = [0.8916 0.8256 0.7412; 0.8256 Hout_5 0.6913; 0.7412 0.6913 0.6355]; %! Hfout_5 = -sum([3 2 1 1 1 1]./9.*log2([3 2 1 1 1 1]./9)); %! Hfout = [2.3613 2.3296 2.2252; 2.4571 Hfout_5 2.3090; 2.4805 2.4488 2.3445]; %! Xout_5 = -sum([1 1 1 1 2 3]./9.*log2([1 1 1 1 2 3]./9)); %! Xout = [2.3613 2.3296 2.2252; 2.4571 Xout_5 2.3090; 2.4805 2.4488 2.3445]; %! Pout_5 = -sum([1 2 6]./9.*log2([1 2 6]./9)); %! Pout = [1.1137 1.1730 1.2251; 1.1595 Pout_5 1.2774; 1.1556 1.2183 1.2635]; %! Qout = zeros(3); %! Rout = [3.5143 3.5700 3.4871 3.4957 3.4825; %! 3.4705 3.5330 3.4341 3.4246 3.3890; %! 3.3694 3.4063 3.3279 3.3386 3.3030; %! 3.3717 3.4209 3.3396 3.3482 3.3044; %! 3.4361 3.5047 3.3999 3.4236 3.3879]; %! assert (entropyfilt (A), Aout); %! assert (entropyfilt (B), Bout); %! assert (entropyfilt (C), Cout); %! assert (entropyfilt (D), Dout); %! assert (entropyfilt (E), Eout); %! assert (entropyfilt (F), Fout); %! assert (entropyfilt (G), Gout, 1e-4); %! assert (entropyfilt (H), Hout, 1e-4); %! assert (entropyfilt (Hf), Hfout, 1e-4); %! assert (entropyfilt (X), Xout, 1e-4); %! assert (entropyfilt (P), Pout, 1e-4); %! assert (entropyfilt (Q), Qout); %! assert (entropyfilt (R), Rout, 1e-4); image-2.16.1/inst/PaxHeaders.61586/tformfwd.m0000644000000000000000000000006215005110255015410 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/tformfwd.m0000644000175000017500000000433315005110255017011 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012 Pantxo Diribarne ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{XY}] =} tformfwd (@var{T}, @var{UV}) ## @deftypefnx {Function File} {[@var{X}, @var{Y}] =} tformfwd (@var{T}, @var{U}, @var{V}) ## ## Given a transform structure @var{T}, transform coordinates @var{UV} ## in the input space into coordinates @var{XY} in the output space. ## ## Input and output coordinates may be given/retrieved either as a ## n-by-2 array, or as two n-by-1 vectors. ## ## The function makes use of the "forward_fcn" field of the transform ## structure @var{T}, which should thus be defined. ## @seealso{maketform, cp2tform, tforminv} ## @end deftypefn ## Author: Pantxo Diribarne function varargout = tformfwd (T, varargin) if (nargin > 3 || nargin < 2) print_usage (); elseif (! istform (T)) error ("tformfwd: expect a transform structure as first argument") elseif (nargin == 2) XX = varargin{1}; if (columns (XX) != 2) error ("tformfwd: expect n-by-2 array as second argument") endif else if (!isvector (varargin{1}) || !isvector (varargin{2})) error ("tformfwd: expect vectors as coordinates") elseif (!all (size (varargin{1}) == size (varargin{2}))) error ("tformfwd: expect two vectors the same size") elseif (columns (varargin{1}) != 1) error ("tformfwd: expect column vectors") endif XX = [varargin{1} varargin{2}]; endif UU = T.forward_fcn(XX, T); if (nargin == 3) varargout{1} = UU(:,1); varargout{2} = UU(:,2); else varargout{1} = UU; endif endfunction image-2.16.1/inst/PaxHeaders.61586/imsharpen.m0000644000000000000000000000006215005110255015546 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imsharpen.m0000644000175000017500000002120515005110255017144 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Avinoam Kalma ## Copyright (C) 2017 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imsharpen (@var{im}) ## @deftypefnx {Function File} {} imsharpen (@var{im}, @var{option}, @var{value}, @dots{}) ## Sharpen image using unsharp masking. ## ## @var{im} must be a grayscale or RGB image. The unsharp masking can ## be controlled with @var{option}-@var{value} pairs. ## ## The unsharp masking technique is equivalent to: ## ## @example ## @var{im} + @var{k} * (@var{im} - smooth (@var{im})) ## @end example ## ## where @var{im} is a grayscale image and @command{smooth} performs ## gaussian smoothing. RGB images are transformed to Lab colorspace, ## the L channel is sharpen to L', and L'ab is transformed back to RGB. ## See @url{https://en.wikipedia.org/wiki/Unsharp_masking, ## "Unsharp masking" in Wikipedia} ## ## The following options control the unsharp masking: ## ## @table @asis ## @item @qcode{"Radius"} ## Sigma of Gaussian Filter for the smoothing stage. Must be a ## positive number. Defaults to 1. ## ## @item @qcode{"Amount"} ## Magnitude of the overshoot @var{k}. Must be a non-negative ## number. Defaults to 0.8. ## ## @item @qcode{"Threshold"} ## Minimum brightness change that will be sharpened. Must be in the ## range [0 1]. Defaults to 0. ## ## @end table ## ## Examples: ## ## @example ## @group ## out = imsharpen (im); # Using imsharpen with default values ## out = imsharpen (im, "Radius", 1.5); ## out = imsharpen (im, "Amount", 1.2); ## out = imsharpen (im, "Threshold", 0.5); ## out = imsharpen (im, "Radius", 1.5, "Amount", 1.2, "Threshold", 0.5); ## @end group ## @end example ## ## @seealso{imfilter, fspecial} ## @end deftypefn function [sharp] = imsharpen (im, varargin) if (nargin == 0) print_usage (); elseif (! isnumeric (im) && ! isbool (im)) error ("imsharpen: IM must be numeric or logical"); elseif (ndims (im) > 4 || all (size (im, 3) != [1 3])) error ("imsharpen: IM must be a grayscale or RGB image"); endif p = inputParser (); p.addParamValue ("Radius", 1, @(x) isnumeric (x) && isscalar (x)); p.addParamValue ("Amount", 0.8, @(x) isnumeric (x) && isscalar (x)); p.addParamValue ("Threshold", 0, @(x) isnumeric (x) && isscalar (x)); p.parse (varargin{:}); if (p.Results.Radius <= 0) error ("imsharpen: RADIUS should be positive"); elseif (p.Results.Amount < 0) error ("imsharpen: AMOUNT should be non-negative"); elseif (p.Results.Threshold < 0 || p.Results.Threshold > 1) error ("imsharpen: THRESHOLD should be in the range [0:1]"); endif imsharpen_size = ceil (max (4 * p.Results.Radius +1, 3)); if (mod (imsharpen_size, 2) == 0) imsharpen_size += 1; endif if (size (im, 3) == 1) sharp = USMGray (im, imsharpen_size, p.Results.Radius, p.Results.Amount, p.Results.Threshold); else sharp = USMColor (im, imsharpen_size, p.Results.Radius, p.Results.Amount, p.Results.Threshold); endif sharp = imcast (sharp, class (im)); endfunction ## UnSharp Masking of gray images function [sharp] = USMGray (im, hsize, sigma, amount, thresh) f = fspecial ("gaussian", hsize, sigma); sharp = im2double (im); filtered = imfilter (sharp, f, "replicate"); g = sharp - filtered; if (thresh > 0) absg = abs (g); thresh *= max (absg(:)); g(absg <= thresh) = 0; endif sharp += amount*g; endfunction ## UnSharp Masking of color images ## Transform image to CIELab color space, perform UnSharp Masking on L channel, ## and transform back to RGB. function [sharp] = USMColor (im, hsize, sigma, amount, thresh) lab = rgb2lab (im); lab(:,:,1) = USMGray (lab(:,:,1), hsize, sigma, amount, thresh); sharp = lab2rgb (lab); endfunction %!test %! A = zeros (7, 7); %! A(4,4) = 1; %! B = [ %! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 %! 0.00000 -0.00238 -0.01064 -0.01755 -0.01064 -0.00238 0.00000 %! 0.00000 -0.01064 -0.04771 -0.07866 -0.04771 -0.01064 0.00000 %! 0.00000 -0.01755 -0.07866 1.67032 -0.07866 -0.01755 0.00000 %! 0.00000 -0.01064 -0.04771 -0.07866 -0.04771 -0.01064 0.00000 %! 0.00000 -0.00238 -0.01064 -0.01755 -0.01064 -0.00238 0.00000 %! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000]; %! assert (imsharpen (A), B, 5e-6) %!test %! A = zeros (7, 7); %! A(4,4) = 1; %! B = [ %! -0.0035147 -0.0065663 -0.0095539 -0.0108259 -0.0095539 -0.0065663 -0.0035147 %! -0.0065663 -0.0122674 -0.0178490 -0.0202255 -0.0178490 -0.0122674 -0.0065663 %! -0.0095539 -0.0178490 -0.0259701 -0.0294280 -0.0259701 -0.0178490 -0.0095539 %! -0.0108259 -0.0202255 -0.0294280 1.7666538 -0.0294280 -0.0202255 -0.0108259 %! -0.0095539 -0.0178490 -0.0259701 -0.0294280 -0.0259701 -0.0178490 -0.0095539 %! -0.0065663 -0.0122674 -0.0178490 -0.0202255 -0.0178490 -0.0122674 -0.0065663 %! -0.0035147 -0.0065663 -0.0095539 -0.0108259 -0.0095539 -0.0065663 -0.0035147]; %! assert (imsharpen (A, "radius", 2), B, 5e-8) %!test %! A = zeros (7, 7); %! A(4,4) = 1; %! assert (imsharpen (A, "radius", 0.01), A) %!test %! A = zeros (7, 7); %! A(4,4) = 1; %! B = A; %! B(3:5,3:5) = -0.000000000011110; %! B(3:5,4) = -0.000002981278097; %! B(4,3:5) = -0.000002981278097; %! B(4,4) = 1.000011925156828; %! assert (imsharpen (A, "radius", 0.2), B, eps*10) %!test %! A = zeros (7, 7); %! A(4,4) = 1; %! B = [ %! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 %! 0.00000 -0.00297 -0.01331 -0.02194 -0.01331 -0.00297 0.00000 %! 0.00000 -0.01331 -0.05963 -0.09832 -0.05963 -0.01331 0.00000 %! 0.00000 -0.02194 -0.09832 1.83790 -0.09832 -0.02194 0.00000 %! 0.00000 -0.01331 -0.05963 -0.09832 -0.05963 -0.01331 0.00000 %! 0.00000 -0.00297 -0.01331 -0.02194 -0.01331 -0.00297 0.00000 %! 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000]; %! assert (imsharpen (A, "amount", 1), B, 5e-6) %!test %! A = zeros (7, 7); %! A(4,4) = 1; %! B = zeros (7, 7); %! B(4,4) = 1.670317742690299; %! B(4,3) = -0.078656265079077; %! B(3,4) = -0.078656265079077; %! B(4,5) = -0.078656265079077; %! B(5,4) = -0.078656265079077; %! assert (imsharpen (A, "Threshold", 0.117341762), B, eps*10) %!test %! A = zeros (7, 7); %! A(4,4) = 1; %! B = zeros (7, 7); %! B(4,4) = 1.670317742690299; %! assert (imsharpen (A, "Threshold", 0.117341763), B, eps*10) ## uint8 test %!test %! A = zeros (7, 7, "uint8"); %! A(3:5,3:5) = 150; %! B = zeros (7, 7, "uint8"); %! B(3:5,3:5) = 211; %! B(4,3:5) = 195; %! B(3:5,4) = 195; %! B(4,4) = 175; %! assert (imsharpen (A), B) ## uint8 test %!test %! A = zeros (7, 7, "uint8"); %! A(3:5,3:5) = 100; %! B = zeros (7, 7, "uint8"); %! B(3:5,3:5) = 173; %! assert (imsharpen (A, "radius", 4), B) ## color image test #1 %!test %! A = zeros (7, 7, 3, "uint8"); %! A(4,4,:) = 255; %! assert (imsharpen (A), A) ## Matlab result is different by 1 grayscale %!xtest %! A = zeros(7,7,3, "uint8"); %! A(4,4,1) = 255; %! B = A; %! B(4,4,2) = 146; # Octave result is 145; %! B(4,4,3) = 100; # Octave result is 99; %! assert (imsharpen (A), B) ## Matlab result is different by 1 grayscale %!xtest %! A = zeros (7, 7, 3, "uint8"); %! A(3:5,3:5,1) = 100; %! A(3:5,3:5,2) = 150; %! B = A; %! B(3:5,3:5,1) = 164; %! B(3:5,4,1) = 146; # Octave result is 147 %! B(4,3:5,1) = 146; # Octave result is 145 %! B(4,4,1) = 125; # Octave result is 126 %! B(3:5,3:5,2) = 213; %! B(3:5,4,2) = 195; # Octave result is 196 %! B(4,3:5,2) = 195; # Octave result is 196 %! B(4,4,2) = 175; %! B(3:5,3:5,3) = 79; %! B(3:5,4,3) = 62; %! B(4,3:5,3) = 62; %! B(4,4,3) = 40; # Octave result is 39 %! assert (imsharpen (A), B) ## Test input validation %!error imsharpen () %!error imsharpen (ones (3, 3), "Radius") %!error imsharpen (ones (3, 3), "Radius", 0) %!error imsharpen (ones (3, 3), "Amount", -1) %!error imsharpen (ones (3, 3), "Threshold", 1.5) %!error imsharpen (ones (3, 3), "Threshold", -1) %!error imsharpen (ones (3, 3), "foo") %!error imsharpen ("foo") image-2.16.1/inst/PaxHeaders.61586/imgetfile.m0000644000000000000000000000006215005110255015525 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imgetfile.m0000644000175000017500000000632215005110255017126 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{path}, @var{flag}] =} imgetfile () ## @deftypefnx {Function File} {[@dots{}] =} imgetfile (@var{options}, @dots{}) ## Open GUI dialogue to select image files. ## ## The GUI dialogue opened is exactly the same as @code{uigetfile} but uses ## recognized image file formats as file filter. All other options from ## @code{uigetfile} are accepted. ## ## The return value @var{path} is a string with the full filepath of the ## selected file. If the @qcode{"MultiSelect"} option is selected, then a ## cell array of strings is returned. ## ## @var{flag} is a logical value, true if there was any issue with file ## selection such as the user closing or cancelling the dialogue with selecting ## a file. It has a value of false otherwise. ## ## @example ## [filepath, flag] = imgetfile (); ## if (flag) ## error ("A file must be selected"); ## endif ## @end example ## ## There is no guarantee that @code{imread} will be capable to read all ## files selected via this dialogue. Possible reasons for this are: ## ## @itemize @bullet ## @item ## the filter uses all the extensions as obtained from @code{imformats}. ## This only means that a format with such extensions is registered, not ## necessarily that read or write support has been implemented; ## ## @item ## the dialogue also has a "All files (*)" filter, so a user is actually ## able to select any file; ## ## @item ## the file may be corrupt; ## ## @item ## it is the file content, and not the file extension, that defines the file ## format. A file may have a file extension that is equal to an image file ## format and not actually be an image file. ## ## @end itemize ## ## The opposite is also true. @code{imread} is able to guess the file ## format even when the file extension is incorrect (or even in the absence ## of a file extension). A file that is filtered out may still be readable ## by @code{imread} or @code{imfinfo}. ## ## @seealso{imformats, uigetfile} ## @end deftypefn function [filepath, flag] = imgetfile (varargin) ext = strcat ("*.", [imformats().ext]); im_filter = strjoin (ext, ";"); [fname, fpath] = uigetfile ({im_filter, "Image files"}, varargin{:}); if (isequal (fname, 0)) flag = true; m = strcmpi (varargin, "MultiSelect"); if (any (m) && strcmpi (varargin{find (m) +1}, "on")) filepath = {}; else filepath = ""; endif else flag = false; filepath = fullfile (fpath, fname); endif endfunction ## Remove from test statistics. No real tests possible. %!assert (1) image-2.16.1/inst/PaxHeaders.61586/iptcheckmap.m0000644000000000000000000000006215005110255016050 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/iptcheckmap.m0000644000175000017500000000454615005110255017457 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} iptcheckmap (@var{in}, @var{func_name}, @var{var_name}, @var{pos}) ## Check if argument is valid colormap. ## ## If @var{in} is not a valid colormap, gives a properly formatted error message. ## @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. ## ## A valid colormap is a 2-D matrix with 3 columns of doubles with values between ## 0 and 1 (inclusive), that refer to the intensity levels of red, green and blue. ## ## @seealso{colormap} ## @end deftypefn function iptcheckmap (in, func_name, var_name, pos) if (nargin != 4) print_usage; 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 ## 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 (!iscolormap (in)) error ("Function %s expected input number %d, %s, to be a valid colormap.\n\ Valid colormaps must be nonempty, double, 2-D matrices with 3 columns.\n", func_name, pos, var_name); endif endfunction %!test ("iptcheckmap (jet(64), 'func', 'var', 2)"); # simple must work %!fail ("iptcheckmap (3, 'func', 'var', 2)"); # not a colormap image-2.16.1/inst/PaxHeaders.61586/immultiply.m0000644000000000000000000000006215005110255015765 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/immultiply.m0000644000175000017500000000532015005110255017363 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{out} =} immultiply (@var{a}, @var{b}) ## @deftypefnx {Function File} {@var{out} =} immultiply (@var{a}, @var{b}, @var{class}) ## Multiply image by another image or constant. ## ## If @var{a} and @var{b} are two images of same size and class, the images are ## multiplied. Alternatively, if @var{b} is a floating-point scalar, @var{a} is ## multiplied by it. ## ## The class of @var{out} will be the same as @var{a} unless @var{a} is logical ## in which case @var{out} will be double. Alternatively, it can be ## specified with @var{class}. ## ## @emph{Note}: the values are truncated to the mininum value of the output ## class. ## @seealso{imabsdiff, imadd, imcomplement, imdivide, imlincomb, imsubtract} ## @end deftypefn function img = immultiply (img, val, out_class = class (img)) if (nargin < 2 || nargin > 3) print_usage; endif [img, val] = imarithmetics ("immultiply", img, val, out_class, nargin); ## have to check how matlab behaves in this situation. 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 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 200])); # 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 image-2.16.1/inst/PaxHeaders.61586/edgetaper.m0000644000000000000000000000006215005110255015520 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/edgetaper.m0000644000175000017500000001041115005110255017113 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} edgetaper (@var{img}, @var{psf}) ## Blur border (edges) of image to prevent ringing artifacts. ## ## @emph{Warning}: this function is not @sc{Matlab} compatible and ## is likely to change in the future. ## ## @end deftypefn function imgt = edgetaper (img, psf) if (nargin != 2) print_usage (); elseif (! isnumeric (img)) error ("edgetaper: IMG must be numeric") elseif (! isnumeric (psf)) error ("edgetaper: PSF must be numeric") endif img_size = size (img); psf_size = size (psf); n = max (numel (img_size), numel (psf_size)); img_size = postpad (img_size(:), n, 1); psf_size = postpad (psf_size(:), n, 1); ## beware of psf_size = [16 16 1 8] paired with img_size [512 512 1 50] ## which are valid, singleton dimensions do not count for this check if (any ((psf_size > (img_size / 2)) & (psf_size > 1))) error ("edgetaper: PSF must be smaller than half of IMG dimensions") endif psf = psf ./ sum (psf(:)); # we use it for blurring so the sum must be 1 ## FIXME this function is not Matlab compatible. I have no clue what ## Matlab is doing but is definitely not what they claim on the ## documentation. There are no references for the function and ## the documentation is sparse and incorrect. ## ## I have found papers that compare their method for reducing ## boundary artifacts against Matlab's edgetaper but even they ## do not comment it. ## ## It an implementation of edgetaper that is close to Matlab's ## documentation for the function. If anyone has patience, please ## fix this. ## ## Some questions about it: ## ## 1. the autocorrelation of the PSF is twice the size of the PSF ## but it will still be way smaller than the image. How can it be ## used to make a weighted mean between the blurred and the original ## image? We pretty much split it and only use it on the borders ## but looks like we end up with an image too blurred. ## ## 2. how do they blur the image? I will guess they pad the ## image but how? ## ## Note: always test this function with input as double precision ## since it returns the same class as input and may hide our ## differences. blurred = fftconvn (img, psf, "same"); xpsf = normalized_autocorrelation (psf); ## The following will expand a ND matrix into a larger size ## repeating the center elements, e.g., ## ## 1 2 2 2 3 ## 1 2 3 4 5 5 5 6 ## 4 5 6 => 4 5 5 5 6 ## 7 8 9 4 5 5 5 6 ## 7 8 8 8 9 ## note the xpsf will always have odd sizes (2*psf_size +1) xdims = ndims (xpsf); xpsf_size = size (xpsf)(:); idim = arrayfun (@(c, n, f) [1:c repmat(c, [1 n]) c:f], ceil (xpsf_size /2), img_size(1:xdims) - xpsf_size -1, xpsf_size, "UniformOutput", false); subs = cell (xdims, 1); [subs{:}] = ndgrid (idim{:}); inds = sub2ind (xpsf_size, subs{:}); weights = xpsf(inds); imgt = (img .* weights) + (blurred .* (1 - weights)); imgt = cast (imgt, class (img)); endfunction function acn = normalized_autocorrelation (psf) idx = arrayfun (@colon, size (psf), repmat (-1, [1 ndims(psf)]), repmat (1, [1 ndims(psf)]), "UniformOutput", false); ac = convn (psf, conj (psf(idx{:}))); acn = ac / max (ac(:)); endfunction %!assert (class (edgetaper (rand (100), rand (16))), "double") %!assert (class (edgetaper (randi (255, 100, "uint8"), rand (16))), "uint8") image-2.16.1/inst/PaxHeaders.61586/nonmax_supress.m0000644000000000000000000000006215005110255016644 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/nonmax_supress.m0000644000175000017500000000225215005110255020243 0ustar00avinoamavinoam00000000000000## Copyright (C) 2019 David Miguel Susano Pinto ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Loadable Function} {@dots{}} nonmax_supress (@dots{}) ## Incorrect spelling of nonmax_suppress function. ## ## This function was originally released with the incorrect spelling. ## This function with the incorrect name is kept for backwards ## compatibility reasons. ## ## @seealso{nonmax_suppress} ## @end deftypefn function [varargout] = nonmax_supress (varargin) [varargout{1:nargout}] = nonmax_suppress (varargin{:}); endfunction image-2.16.1/inst/PaxHeaders.61586/rho_filter.m0000644000000000000000000000006215005110255015715 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/rho_filter.m0000644000175000017500000001312515005110255017315 0ustar00avinoamavinoam00000000000000## Copyright (C) 2010 Alex Opie ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{filtered} =} rho_filter (@var{proj}, @var{type}, @var{scaling}) ## ## Filters the parallel ray projections in the columns of @var{proj}, ## according to the filter type chosen by @var{type}. @var{type} ## can be chosen from ## @itemize ## @item 'none' ## @item 'Ram-Lak' (default) ## @item 'Shepp-Logan' ## @item 'Cosine' ## @item 'Hann' ## @item 'Hamming' ## @end itemize ## ## If given, @var{scaling} determines the proportion of frequencies ## below the nyquist frequency that should be passed by the filter. ## The window function is compressed accordingly, to avoid an abrupt ## truncation of the frequency response. ## ## @end deftypefn ## @deftypefn {Function File} {[@var{filtered}, @var{filter}] =} rho_filter (@dots{}) ## ## This form also returns the frequency response of the filter in ## the vector @var{filter}. ## ## Performs rho filtering on the parallel ray projections provided. ## ## Rho filtering is performed as part of the filtered back-projection ## method of CT image reconstruction. It is the filtered part of ## the name. ## The simplest rho filter is the Ramachadran-Lakshminarayanan (Ram-Lak), ## which is simply |rho|, where rho is the radial component of spatial ## frequency. However, this can cause unwanted amplification of noise, ## which is what the other types attempt to minimise, by introducing ## roll-off into the response. The Hann and Hamming filters multiply ## the standard response by a Hann or Hamming window, respectively. ## The cosine filter is the standard response multiplied by a cosine ## shape, and the Shepp-Logan filter multiplies the response with ## a sinc shape. The 'none' filter performs no filtering, and is ## included for completeness and to enable incorporating this function ## easily into scripts or functions that may offer the ability to choose ## to apply no filtering. ## ## This function is designed to be used by the function @command{iradon}, ## but has been exposed to facilitate custom inverse radon transforms ## and to more clearly break down the process for educational purposes. ## The operations ## @example ## filtered = rho_filter (proj); ## reconstruction = iradon (filtered, 1, 'linear', 'none'); ## @end example ## are exactly equivalent to ## @example ## reconstruction = iradon (proj, 1, 'linear', 'Ram-Lak'); ## @end example ## ## Usage example: ## @example ## P = phantom (); ## projections = radon (P); ## filtered_projections = rho_filter (projections, 'Hamming'); ## reconstruction = iradon (filtered_projections, 1, 'linear', 'none'); ## figure, imshow (reconstruction, []) ## @end example ## ## @end deftypefn function [filtered_proj, filt] = rho_filter (proj, type, scaling) filtered_proj = proj; if (nargin < 3) scaling = 1; endif if (nargin < 2) || (size (type) == 0) type = 'ram-lak'; endif if (strcmpi (type, 'none')) filt = 1; return; endif if (scaling > 1) || (scaling < 0) error ('Scaling factor must be in [0,1]'); endif ## Extend the projections to a power of 2 new_len = 2 * 2^nextpow2 (size (filtered_proj, 1)); filtered_proj (new_len, 1) = 0; ## Scale the frequency response int_len = (new_len * scaling); if (mod (floor (int_len), 2)) int_len = ceil (int_len); else int_len = floor (int_len); endif ## Create the basic filter response rho = scaling * (0:1 / (int_len / 2):1); rho = [rho'; rho(end - 1:-1:2)']; ## Create the window to apply to the filter response if (strcmpi (type, 'ram-lak')) filt = 1; elseif (strcmpi (type, 'hamming')) filt = fftshift (hamming (length (rho))); elseif (strcmpi (type, 'hann')) filt = fftshift (hanning (length (rho))); elseif (strcmpi (type, 'cosine')) f = 0.5 * (0:length (rho) - 1)' / length (rho); filt = fftshift (sin (2 * pi * f)); elseif (strcmpi (type, 'shepp-logan')) f = (0:length (rho) / 2)' / length (rho); filt = sin (pi * f) ./ (pi * f); filt (1) = 1; filt = [filt; filt(end - 1:-1:2)]; else error ('rho_filter: Unknown window type'); endif ## Apply the window filt = filt .* rho; ## Pad the response to the correct length len_diff = new_len - int_len; if (len_diff != 0) pad = len_diff / 2; filt = padarray (fftshift (filt), pad); filt = fftshift (filt); endif filtered_proj = fft (filtered_proj); ## Perform the filtering for i = 1:size (filtered_proj, 2) filtered_proj (:, i) = filtered_proj (:, i) .* filt; endfor ## Finally bring the projections back to the spatial domain filtered_proj = real (ifft (filtered_proj)); ## Chop the projections back to their original size filtered_proj (size (proj, 1) + 1:end, :) = []; endfunction %!demo %! P = phantom (); %! projections = radon (P); %! filtered_projections = rho_filter (projections, 'Hamming'); %! reconstruction = iradon (filtered_projections, 1, 'linear', 'none'); %! figure, imshow (reconstruction, []) image-2.16.1/inst/PaxHeaders.61586/@imref2d0000644000000000000000000000013215005111723014754 xustar0030 mtime=1746179027.054724981 30 atime=1746179027.278726152 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/0000755000175000017500000000000015005111723016431 5ustar00avinoamavinoam00000000000000image-2.16.1/inst/@imref2d/PaxHeaders.61586/contains.m0000644000000000000000000000006215005110255017026 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/contains.m0000644000175000017500000000474415005110255020435 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{tf} =} contains (@var{r}, @var{xWorld}, @var{yWorld}) ## Determine if image contains points in world coordinate system. ## ## Outputs a logical array @var{tf}, where the i-th nonzero value means the ## point (@var{xWorld}(i), @var{yWorld}(i)) lies within the bounds of ## an image associated with a spatial referencing object @var{r}. ## ## @seealso{imref2d, imref3d} ## @end deftypefn function tf = contains (r, xWorld, yWorld) if (nargin != 3) print_usage(); endif validateattributes (xWorld, {"numeric"}, ... {"real"}, "imref2d", "xWorld"); validateattributes (yWorld, {"numeric"}, ... {"real"}, "imref2d", "yWorld"); if (! all (size (xWorld) == size (yWorld))) error ("Octave:invalid-input-arg", ... "xWorld and yWorld must be of the same size"); endif xWorldLimits = r.XWorldLimits; yWorldLimits = r.YWorldLimits; containsX = xWorld >= xWorldLimits(1) & xWorld <= xWorldLimits(2); containsY = yWorld >= yWorldLimits(1) & yWorld <= yWorldLimits(2); tf = containsX & containsY; endfunction %!error id=Octave:invalid-fun-call contains (imref2d) %!error id=Octave:invalid-fun-call contains (imref2d, 1) %!error id=Octave:invalid-fun-call contains (imref2d, 1, 2, 3) %!error id=Octave:invalid-input-arg contains (imref2d, 1, [2, 3]) %!error id=Octave:invalid-input-arg contains (imref2d, [1, 2], 3) %!error id=Octave:expected-real contains (imref2d, 0, j) %!error id=Octave:expected-real contains (imref2d, j, 0) %!assert (contains (imref2d, [], []), logical( zeros (0, 0))) %!assert (contains (imref2d, [1, 2; 3, 4], [5, -6; 7, 8]), logical (zeros (2, 2))) %!test %! r = imref2d ([256, 256]); %! assert (contains(r, [5, 8, 8], [5, 10, 257]), logical([1, 1, 0]))image-2.16.1/inst/@imref2d/PaxHeaders.61586/intrinsicToWorld.m0000644000000000000000000000006215005110255020525 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/intrinsicToWorld.m0000644000175000017500000000610415005110255022124 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {[@var{xWorld}, @var{yWorld}] =} intrinsicToWorld (@var{r}, @var{xIntrinsic}, @var{yIntrinsic}) ## Convert from intrinsic to world coordinates. ## ## Converts intrinsic coordinates of the image associated with the spatial ## referencing object @var{r} to world coordinates @var{xWorld} ## and @var{yWorld}. If a point (@var{xIntrinsic}(i), @var{yIntrinsic}(i)) ## falls outside the intrinsic bounds of the image, the world coordinates are ## extrapolated, possibly resulting in negative values. ## ## @seealso{imref2d, imref3d, worldToIntrinsic} ## @end deftypefn function [xWorld, yWorld] = intrinsicToWorld (r, xIntrinsic, yIntrinsic) if (nargin != 3) print_usage (); endif validateattributes (xIntrinsic, {"numeric"}, ... {"real"}, "imref2d", "xIntrinsic"); validateattributes (yIntrinsic, {"numeric"}, ... {"real"}, "imref2d", "yIntrinsic"); if (! all (size (xIntrinsic) == size (yIntrinsic))) error ("Octave:invalid-input-arg", ... "xIntrinsic and yIntrinsic must be of the same size"); endif xIntrinsicLimits = r.XIntrinsicLimits; yIntrinsicLimits = r.YIntrinsicLimits; xWorldLimits = r.XWorldLimits; yWorldLimits = r.YWorldLimits; xWorld = xWorldLimits(1) + r.PixelExtentInWorldX * ... (xIntrinsic - xIntrinsicLimits(1)); yWorld = yWorldLimits(1) + r.PixelExtentInWorldY * ... (yIntrinsic - yIntrinsicLimits(1)); endfunction %!error id=Octave:invalid-fun-call intrinsicToWorld (imref2d) %!error id=Octave:invalid-fun-call intrinsicToWorld (imref2d, 1, 2, 3) %!error id=Octave:expected-real intrinsicToWorld (imref2d, 1j, 2) %!error id=Octave:expected-real intrinsicToWorld (imref2d, 1, 2j) %!error id=Octave:invalid-input-arg intrinsicToWorld (imref2d, [1, 2], 3) %!error id=Octave:invalid-input-arg intrinsicToWorld (imref2d, [1], [2, 3]) %!test %! r = imref2d ([512, 512], 0.3125, 0.3125); %! xIntrinsic = [34, 442]; %! yIntrinsic = [172, 172]; %! [xWorld, yWorld] = intrinsicToWorld (r, xIntrinsic, yIntrinsic); %! assert (xWorld, [10.625, 138.125]) %! assert (yWorld, [53.75, 53.75]) %!test %! [xWorld, yWorld] = intrinsicToWorld (imref2d, -5.3, -2.8); %! assert (xWorld, -5.3) %! assert (yWorld, -2.8) %!test %! [xW, yW] = intrinsicToWorld (imref2d, [1, 2; 3, 4], [2, 3; 5, 9]); %! assert (xW, [1, 2; 3, 4]) %! assert (yW, [2, 3; 5, 9])image-2.16.1/inst/@imref2d/PaxHeaders.61586/subsref.m0000644000000000000000000000006215005110255016661 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/subsref.m0000644000175000017500000000373315005110255020265 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{r} =} subref (@var{val}, @var{idx}) ## ## @seealso{} ## @end deftypefn function r = subsref (val, idx) if (strcmp (idx(1).type, ".")) switch (idx(1).subs) case "ImageSize" r = val.ImageSize; case "XWorldLimits" r = val.XWorldLimits; case "YWorldLimits" r = val.YWorldLimits; case "PixelExtentInWorldX" r = val.PixelExtentInWorldX; case "PixelExtentInWorldY" r = val.PixelExtentInWorldY; case "ImageExtentInWorldX" r = val.ImageExtentInWorldX; case "ImageExtentInWorldY" r = val.ImageExtentInWorldY; case "XIntrinsicLimits" r = val.XIntrinsicLimits; case "YIntrinsicLimits" r = val.YIntrinsicLimits; otherwise error ("Octave:invalid-indexing", ... strcat ("unknown property '", idx(1).subs, "' for class imref2d")); endswitch if (length (idx) > 1) switch (idx(2).type) case "()" i = idx(2).subs; r = r(i{1}); otherwise error ("Octave:invalid-indexing", ... strcat ("can't index '", idx(1).subs, "' with ", idx(2).type)); endswitch endif endif endfunction image-2.16.1/inst/@imref2d/PaxHeaders.61586/disp.m0000644000000000000000000000006215005110255016147 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/disp.m0000644000175000017500000000274015005110255017550 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} disp (@var{r}) ## ## @seealso{} ## @end deftypefn function disp (r) printf("%s with properties:\n", class (r)); printf("\n"); printf(" XWorldLimits: [%d %d]\n", r.XWorldLimits); printf(" YWorldLimits: [%d %d]\n", r.YWorldLimits); printf(" ImageSize: [%d %d]\n", r.ImageSize); printf(" PixelExtentInWorldX: %d\n", r.PixelExtentInWorldX); printf(" PixelExtentInWorldY: %d\n", r.PixelExtentInWorldY); printf(" ImageExtentInWorldX: %d\n", r.ImageExtentInWorldX); printf(" ImageExtentInWorldY: %d\n", r.ImageExtentInWorldY); printf(" XIntrinsicLimits: [%d %d]\n", r.XIntrinsicLimits); printf(" YIntrinsicLimits: [%d %d]\n", r.YIntrinsicLimits); endfunction image-2.16.1/inst/@imref2d/PaxHeaders.61586/subsasgn.m0000644000000000000000000000006215005110255017035 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/subsasgn.m0000644000175000017500000000571715005110255020445 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{rout} =} subsasgn (@var{r}, @var{index}, @var{val}) ## ## @seealso{} ## @end deftypefn function rout = subsasgn (r, index, val) switch (index.type) case "." fld = index.subs; switch (fld) case "ImageSize" imageSize = val; if (length (imageSize) < 2) error ("Octave:invalid-input-arg", ... "ImageSize must have at least two elements"); endif validateattributes (imageSize, {"numeric"}, ... {"positive", "integer", "vector"}, "imref2d", "imageSize"); m = imageSize(1); n = imageSize(2); rout = r; rout.ImageSize = imageSize; rout.PixelExtentInWorldX = r.ImageExtentInWorldX / n; rout.PixelExtentInWorldY = r.ImageExtentInWorldY / m; rout.XIntrinsicLimits = [0.5, n + 0.5]; rout.YIntrinsicLimits = [0.5, m + 0.5]; case "XWorldLimits" xWorldLimits = val; validateattributes (xWorldLimits, {"numeric"}, ... {"increasing", "real", "vector", "size", [1, 2]}, ... "imref2d", "xWorldLimits"); imageSize = r.ImageSize; imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1); rout = r; rout.XWorldLimits = val; rout.ImageExtentInWorldX = imageExtentInWorldX; rout.PixelExtentInWorldX = imageExtentInWorldX / imageSize(2); case "YWorldLimits" yWorldLimits = val; validateattributes (yWorldLimits, {"numeric"}, ... {"increasing", "real", "vector", "size", [1, 2]}, ... "imref2d", "yWorldLimits"); imageSize = r.ImageSize; imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1); rout = r; rout.YWorldLimits = val; rout.ImageExtentInWorldY = imageExtentInWorldY; rout.PixelExtentInWorldY = imageExtentInWorldY / imageSize(1); otherwise error ("Octave:invalid-indexing", ... "@imref2d/subsasgn: invalid property '%s'", fld); endswitch otherwise error ("Octave:invalid-indexing", "@imref2d/subsasgn: invalid index type") endswitch endfunctionimage-2.16.1/inst/@imref2d/PaxHeaders.61586/sizesMatch.m0000644000000000000000000000006215005110255017322 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/sizesMatch.m0000644000175000017500000000366715005110255020734 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{TF} =} sizesMatch (@var{r}, @var{A}) ## Determine if object and image are size-compatible. ## ## Outputs logical 1 (true) if the first two dimensions of an n-dimensional ## image @var{A} match the image size of the spatial referencing object @var{r}, ## otherwise outputs zero (false). ## ## @seealso{imref2d, imref3d} ## @end deftypefn function TF = sizesMatch (r, A) if (nargin != 2) print_usage(); endif sizeA = size(A); TF = all(sizeA(1:2) == r.ImageSize); endfunction %!error id=Octave:invalid-fun-call sizesMatch (imref2d) ## example from MATLAB documentation %!test %! I = zeros (256, 256); %! r = imref2d ([256, 256]); %! assert (sizesMatch (r, I), true) %! I2 = zeros (246, 300); %! assert (sizesMatch (r, I2), false) ## accepts empty image %!test %! r = imref2d ([256, 256]); %! assert (sizesMatch (r, []), false) ## accepts 1-D image %!test %! r = imref2d ([256, 256]); %! assert (sizesMatch (r, 42), false) ## accepts N-D image %!test %! r = imref2d ([256, 256]); %! assert (sizesMatch (r, zeros (256, 256, 3, 2)), true) %!test %! I = zeros (384, 512, 3); %! r = imref2d (size (I)); %! assert (sizesMatch (r, I), true)image-2.16.1/inst/@imref2d/PaxHeaders.61586/imref2d.m0000644000000000000000000000006215005110255016540 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/imref2d.m0000644000175000017500000002600615005110255020142 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{r} =} imref2d ## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize}) ## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize}, @var{pixelExtentInWorldX}, @var{pixelExtentInWorldY}) ## @deftypefnx {} {@var{r} =} imref2d (@var{imageSize}, @var{xWorldLimits}, @var{yWorldLimits}) ## Reference 2-D image to world coordinates. ## ## Creates an imref2d object referencing a 2-D m-by-n image with the size ## @var{imageSize} to world coordinates. The world extent is either given ## by @var{xWorldLimits} and @var{yWorldLimits} or computed from ## @var{pixelExtentInWorldX} and @var{pixelExtentInWorldY}. ## @var{imageSize} is [2, 2] by default. ## ## Intrinsic coordinates are x = 1.0, y = 1.0 in the center of the top left ## pixel and x = n, y = m in the center of the bottom right pixel. ## Spatial resolution in each dimension can be different. ## ## imref2d object has the following properties: ## ## ImageSize - two element integer vector with image height and width ## in pixels. ## ## XWorldLimits - limits of the image along the x-axis in world units ## specified as a two element real vector @code{[xMin, xMax]}. ## ## YWorldLimits - limits of the image along the y-axis in world units ## specified as a two element real vector @code{[yMin, yMax]}. ## ## PixelExtentInWorldX - pixel extent along the x-axis in world units ## specified as a real scalar. ## ## PixelExtentInWorldY - pixel extent along the y-axis in world units ## specified as a real scalar. ## ## ImageExtentInWorldX - image extent along the x-axis in world units ## specified as a real scalar. ## ## ImageExtentInWorldY - image extent along the y-axis in world units ## specified as a real scalar. ## ## XIntrinsicLimits - limits of the image along the x-axis in intrinsic ## units, equals to @code{[n - 0.5, n + 0.5]}. ## ## YIntrinsicLimits - limits of the image along the y-axis in intrinsic ## units, equals to @code{[m - 0.5, m + 0.5]}. ## ## @seealso{imref3d} ## @end deftypefn function r = imref2d (imageSize, varargin) if (nargin > 1 && nargin != 3) print_usage(); endif if (nargin == 0) imageSize = [2, 2]; endif if (nargin > 0) if (length (imageSize) < 2) error ("Octave:invalid-input-arg", ... "ImageSize must have at least two elements"); endif validateattributes (imageSize, {"numeric"}, ... {"positive", "integer", "vector"}, "imref2d", "imageSize"); endif imageSize = imageSize(1:2); m = imageSize(1); n = imageSize(2); if (numel (varargin) == 0) xWorldLimits = [0.5, n + 0.5]; yWorldLimits = [0.5, m + 0.5]; elseif (numel (varargin) == 2) if (isscalar (varargin{1})) validateattributes (varargin{1}, {"numeric"}, ... {"real", "positive", "scalar"}, "imref2d", "pixelExtentInWorldX"); validateattributes (varargin{2}, {"numeric"}, ... {"real", "positive", "scalar"}, "imref2d", "pixelExtentInWorldY"); pixelExtentInWorldX = varargin{1}; pixelExtentInWorldY = varargin{2}; else validateattributes (varargin{1}, {"numeric"}, ... {"real", "increasing", "vector", "size", [1, 2]}, "imref2d", ... "xWorldLimits"); validateattributes (varargin{2}, {"numeric"}, ... {"real", "increasing", "vector", "size", [1, 2]}, "imref2d", ... "yWorldLimits"); xWorldLimits = varargin{1}; yWorldLimits = varargin{2}; endif endif if (exist ("pixelExtentInWorldX") && exist ("pixelExtentInWorldY")) imageExtentInWorldX = pixelExtentInWorldX * n; imageExtentInWorldY = pixelExtentInWorldY * m; xWorldLimits = [pixelExtentInWorldX / 2, imageExtentInWorldX + ... pixelExtentInWorldX / 2]; yWorldLimits = [pixelExtentInWorldY / 2, imageExtentInWorldY + ... pixelExtentInWorldY / 2]; elseif (exist ("xWorldLimits") && exist ("yWorldLimits")) imageExtentInWorldX = xWorldLimits(2) - xWorldLimits(1); imageExtentInWorldY = yWorldLimits(2) - yWorldLimits(1); pixelExtentInWorldX = imageExtentInWorldX / n; pixelExtentInWorldY = imageExtentInWorldY / m; endif xIntrinsicLimits = [0.5, n + 0.5]; yIntrinsicLimits = [0.5, m + 0.5]; r.ImageSize = imageSize; r.XWorldLimits = xWorldLimits; r.YWorldLimits = yWorldLimits; r.PixelExtentInWorldX = pixelExtentInWorldX; r.PixelExtentInWorldY = pixelExtentInWorldY; r.ImageExtentInWorldX = imageExtentInWorldX; r.ImageExtentInWorldY = imageExtentInWorldY; r.XIntrinsicLimits = xIntrinsicLimits; r.YIntrinsicLimits = yIntrinsicLimits; r = class (r, "imref2d"); endfunction %!error id=Octave:invalid-fun-call imref2d (1, 2, 3, 4) %!error id=Octave:invalid-input-arg imref2d (42) %!error id=Octave:invalid-input-arg imref2d ([42]) %!error id=Octave:expected-integer imref2d ([4.2, 42]) %!error id=Octave:expected-positive imref2d ([0, 0]) %!error id=Octave:expected-positive imref2d ([-4, 2]) %!error id=Octave:expected-positive imref2d ([4, 2], 0, 2) %!error id=Octave:expected-positive imref2d ([4, 2], 2, 0) %!error id=Octave:expected-real imref2d ([4, 2], j, 2) %!error id=Octave:expected-real imref2d ([4, 2], 2, j) %!error id=Octave:expected-real imref2d ([4, 2], [j, 2], [3, 4]) %!error id=Octave:expected-real imref2d ([4, 2], [1, 2], [j, 4]) %!error id=Octave:expected-vector imref2d ([4, 2], [], []) %!error id=Octave:expected-vector imref2d ([4, 2], [], [1]) %!error id=Octave:expected-scalar imref2d ([4, 2], [1], []) %!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [0]) %!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [1, 2, 3]) %!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2, 3], [1, 2]) %!error id=Octave:incorrect-size imref2d ([4, 2], [1; 2], [1, 2]) %!error id=Octave:incorrect-size imref2d ([4, 2], [1, 2], [1; 2]) %!error id=Octave:invalid-indexing imref2d().InvalidProperty %!error id=Octave:expected-increasing imref2d ([100 200], [1.5 0.5], [2.5 3.5]) %!error id=Octave:expected-increasing imref2d ([100 200], [1.5 2.5], [2.5 1.5]) %!test %! r = imref2d; %! assert (r.XWorldLimits, [0.5, 2.5]) %! assert (r.YWorldLimits, [0.5, 2.5]) %! assert (r.ImageSize, [2, 2]) %! assert (r.PixelExtentInWorldX, 1) %! assert (r.PixelExtentInWorldY, 1) %! assert (r.ImageExtentInWorldX, 2) %! assert (r.ImageExtentInWorldY, 2) %! assert (r.XIntrinsicLimits, [0.5, 2.5]) %! assert (r.YIntrinsicLimits, [0.5, 2.5]) %!test %! r = imref2d ([100, 200]); %! assert (r.XWorldLimits, [0.5, 200.5]) %! assert (r.YWorldLimits, [0.5, 100.5]) %! assert (r.ImageSize, [100, 200]) %! assert (r.PixelExtentInWorldX, 1) %! assert (r.PixelExtentInWorldY, 1) %! assert (r.ImageExtentInWorldX, 200) %! assert (r.ImageExtentInWorldY, 100) %! assert (r.XIntrinsicLimits, [0.5, 200.5]) %! assert (r.YIntrinsicLimits, [0.5, 100.5]) %!test %! xWorldLimits = [2, 5]; %! yWorldLimits = [3, 6]; %! r = imref2d ([291, 240], xWorldLimits, yWorldLimits); %! assert (r.XWorldLimits, [2, 5]) %! assert (r.YWorldLimits, [3, 6]) %! assert (r.ImageSize, [291, 240]) %! assert (r.PixelExtentInWorldX, 0.0125) %! assert (r.PixelExtentInWorldY, 0.0103, 1e-3) %! assert (r.ImageExtentInWorldX, 3) %! assert (r.ImageExtentInWorldY, 3) %! assert (r.XIntrinsicLimits, [0.5, 240.5]) %! assert (r.YIntrinsicLimits, [0.5, 291.5]) %!test %! pixelExtentInWorldX = 0.3125; %! pixelExtentInWorldY = 0.3125; %! r = imref2d ([512, 512], pixelExtentInWorldX, pixelExtentInWorldY); %! assert (r.XWorldLimits, [0.15625, 160.1562], 1e-4) %! assert (r.YWorldLimits, [0.15625, 160.1562], 1e-4) %! assert (r.ImageSize, [512, 512]) %! assert (r.PixelExtentInWorldX, 0.3125) %! assert (r.PixelExtentInWorldY, 0.3125) %! assert (r.ImageExtentInWorldX, 160) %! assert (r.ImageExtentInWorldY, 160) %! assert (r.XIntrinsicLimits, [0.5, 512.5]) %! assert (r.YIntrinsicLimits, [0.5, 512.5]) %!test %! pixelExtentInWorldX = 0.1; %! pixelExtentInWorldY = 0.4; %! r = imref2d ([100, 200], pixelExtentInWorldX, pixelExtentInWorldY); %! assert (r.XWorldLimits, [0.05, 20.05], 1e-4) %! assert (r.YWorldLimits, [0.2, 40.2], 1e-4) %! assert (r.ImageSize, [100, 200]) %! assert (r.PixelExtentInWorldX, 0.1) %! assert (r.PixelExtentInWorldY, 0.4) %! assert (r.ImageExtentInWorldX, 20) %! assert (r.ImageExtentInWorldY, 40) %! assert (r.XIntrinsicLimits, [0.5, 200.5]) %! assert (r.YIntrinsicLimits, [0.5, 100.5]) ## changing ImageSize %!test %! r = imref2d; %! assert (r.XWorldLimits, [0.5, 2.5]) %! assert (r.YWorldLimits, [0.5, 2.5]) %! assert (r.ImageSize, [2, 2]) %! assert (r.PixelExtentInWorldX, 1) %! assert (r.PixelExtentInWorldY, 1) %! assert (r.ImageExtentInWorldX, 2) %! assert (r.ImageExtentInWorldY, 2) %! assert (r.XIntrinsicLimits, [0.5, 2.5]) %! assert (r.YIntrinsicLimits, [0.5, 2.5]) %! r.ImageSize = [800, 600]; %! assert (r.XWorldLimits, [0.5, 2.5]) %! assert (r.YWorldLimits, [0.5, 2.5]) %! assert (r.ImageSize, [800, 600]) %! assert (r.PixelExtentInWorldX, 0.003333, 1e-5) %! assert (r.PixelExtentInWorldY, 0.0025) %! assert (r.ImageExtentInWorldX, 2) %! assert (r.ImageExtentInWorldY, 2) %! assert (r.XIntrinsicLimits, [0.5, 600.5]) %! assert (r.YIntrinsicLimits, [0.5, 800.5]) ## changing XWorldLimits and YWorldLimits %!test %! r = imref2d; %! assert (r.XWorldLimits, [0.5, 2.5]) %! assert (r.YWorldLimits, [0.5, 2.5]) %! assert (r.ImageSize, [2, 2]) %! assert (r.PixelExtentInWorldX, 1) %! assert (r.PixelExtentInWorldY, 1) %! assert (r.ImageExtentInWorldX, 2) %! assert (r.ImageExtentInWorldY, 2) %! assert (r.XIntrinsicLimits, [0.5, 2.5]) %! assert (r.YIntrinsicLimits, [0.5, 2.5]) %! r.XWorldLimits = [-60, 13.33]; %! r.YWorldLimits = [-900.8, -560.26]; %! assert (r.XWorldLimits, [-60, 13.33]) %! assert (r.YWorldLimits, [-900.8, -560.26]) %! assert (r.PixelExtentInWorldX, 36.6650) %! assert (r.PixelExtentInWorldY, 170.27, 1e-5) %! assert (r.ImageExtentInWorldX, 73.33, 1e-5) %! assert (r.ImageExtentInWorldY, 340.54, 1e-5) %! assert (r.XIntrinsicLimits, [0.5, 2.5]) %! assert (r.YIntrinsicLimits, [0.5, 2.5]) %!test %! r = imref2d; %! fail ("r.XWorldLimits = []", "") %! fail ("r.XWorldLimits = [1]", "") %! fail ("r.XWorldLimits = [j]", "") %! fail ("r.XWorldLimits = [1; 2]", "") %! fail ("r.YWorldLimits = []", "") %! fail ("r.YWorldLimits = [1]", "") %! fail ("r.YWorldLimits = [j]", "") %! fail ("r.YWorldLimits = [1; 2]", "") %!assert (imref2d ([4, 2, 3]).ImageSize, [4, 2]);image-2.16.1/inst/@imref2d/PaxHeaders.61586/worldToIntrinsic.m0000644000000000000000000000006215005110255020525 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/worldToIntrinsic.m0000644000175000017500000000551515005110255022131 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {[@var{xIntrinsic}, @var{yIntrinsic}] =} worldToIntrinsic (@var{r}, @var{xWorld}, @var{yWorld}) ## Convert from world to intrinsic coordinates. ## ## Converts world coordinates @var{xWorld} and @var{yWorld} to intrinsic ## coordinates @var{xIntrinsic} and @var{yIntrinsic} of an image associated ## with the spatial referencing object @var{r}. If a point ## (@var{xWorld}(i), @var{yWorld}(i)) falls outside the bounds of the image, ## its intrinsic coordinates are extrapolated, possibly resulting in ## negative values. ## ## @seealso{imref2d, imref3d, intrinsicToWorld} ## @end deftypefn function [xIntrinsic, yIntrinsic] = worldToIntrinsic (r, xWorld, yWorld) if (nargin != 3) print_usage (); endif validateattributes (xWorld, {"numeric"}, ... {"real"}, "imref2d", "xWorld"); validateattributes (yWorld, {"numeric"}, ... {"real"}, "imref2d", "yWorld"); if (! all (size (xWorld) == size (yWorld))) error ("Octave:invalid-input-arg", ... "xWorld and yWorld must be of the same size"); endif xIntrinsicLimits = r.XIntrinsicLimits; yIntrinsicLimits = r.YIntrinsicLimits; xWorldLimits = r.XWorldLimits; yWorldLimits = r.YWorldLimits; xIntrinsic = xIntrinsicLimits(1) + (xWorld - xWorldLimits(1)) ... / r.PixelExtentInWorldX; yIntrinsic = yIntrinsicLimits(1) + (yWorld - yWorldLimits(1)) ... / r.PixelExtentInWorldY; endfunction %!error id=Octave:invalid-fun-call worldToIntrinsic (imref2d) %!error id=Octave:invalid-fun-call worldToIntrinsic (imref2d, 1, 2, 3) %!error id=Octave:expected-real worldToIntrinsic (imref2d, 1j, 2) %!error id=Octave:expected-real worldToIntrinsic (imref2d, 1, 2j) %!error id=Octave:invalid-input-arg worldToIntrinsic (imref2d, [1, 2], 3) %!error id=Octave:invalid-input-arg worldToIntrinsic (imref2d, [1], [2, 3]) %!test %! r = imref2d ([512, 512], 0.3125, 0.3125); %! xW = [38.44, 39.44, 38.44, -0.2]; %! yW = [68.75, 68.75, 75.75, -1]; %! [xI, yI] = worldToIntrinsic (r, xW, yW); %! assert (xI, [123.008, 126.208, 123.008, -0.64], 1e-6) %! assert (yI, [220, 220, 242.4, -3.2], 1e-6)image-2.16.1/inst/@imref2d/PaxHeaders.61586/worldToSubscript.m0000644000000000000000000000006215005110255020541 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/@imref2d/worldToSubscript.m0000644000175000017500000000544715005110255022151 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {[@var{i}, @var{j}] =} worldToSubscript (@var{r}, @var{xWorld}, @var{yWorld}) ## Convert world coordinates to row and column subscripts. ## ## Converts world coordinates to row and column subscripts of an image ## associated with the spatial referencing object @var{r}. A point located at ## (@var{xWorld}(i), @var{yWorld}(i)) world coordinates maps to row and column ## subscripts @var{i}(i) and @var{j}(i) respectively. Note the reversed order ## of the dimensions. If the point falls outside the bounds of the image, both ## its row and column subscripts are NaN. ## ## @seealso{imref2d, imref3d, worldToIntrinsic} ## @end deftypefn function [i, j] = worldToSubscript (r, xWorld, yWorld) if (nargin != 3) print_usage (); endif validateattributes (xWorld, {"numeric"}, ... {"real"}, "imref2d", "xWorld"); validateattributes (yWorld, {"numeric"}, ... {"real"}, "imref2d", "yWorld"); if (! all (size (xWorld) == size (yWorld))) error ("Octave:invalid-input-arg", ... "xWorld and yWorld must be of the same size"); endif [xIntrinsic, yIntrinsic] = worldToIntrinsic (r, xWorld, yWorld); xIntrinsicLimits = r.XIntrinsicLimits; yIntrinsicLimits = r.YIntrinsicLimits; inImage = contains (r, xWorld, yWorld); xIntrinsic(! inImage) = NaN; yIntrinsic(! inImage) = NaN; i = round (yIntrinsic); j = round (xIntrinsic); endfunction %!error id=Octave:invalid-fun-call worldToSubscript (imref2d) %!error id=Octave:invalid-fun-call worldToSubscript (imref2d, 1, 2, 3) %!error id=Octave:expected-real worldToSubscript (imref2d, 1j, 2) %!error id=Octave:expected-real worldToSubscript (imref2d, 1, 2j) %!error id=Octave:invalid-input-arg worldToSubscript (imref2d, [1, 2], 3) %!error id=Octave:invalid-input-arg worldToSubscript (imref2d, [1], [2, 3]) %!test %! r = imref2d ([512, 512], 0.3125, 0.3125); %! xW = [38.44, 39.44, 38.44, -0.2]; %! yW = [68.75, 68.75, 75.75, -1]; %! [rS, cS] = worldToSubscript (r, xW, yW); %! assert (rS, [220, 220, 242, NaN]) %! assert (cS, [123, 126, 123, NaN])image-2.16.1/inst/PaxHeaders.61586/std2.m0000644000000000000000000000006215005110255014434 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/std2.m0000644000175000017500000000522215005110255016033 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{s} = std2 (@var{I}) ## Returns the standard deviation for a 2d real type matrix. ## ## Uses @code{std (double (I(:)))} for integer type input and @code{std (I(:))} ## for floating type input (single/double) ## ## @seealso{mean2,std} ## @end deftypefn function s = std2 (I) if !(nargin == 1) print_usage (); endif if (! isnumeric (I)) error("std2: I must be a numeric vector or matrix"); endif if (isfloat (I)) s = std (I(:)); else s = std (double (I(:))); endif endfunction %!test %! a = std2 (uint16 (eye (10))); %! b = std (eye (10)(:)); %! assert (a, b); %! A = eye (10) - 0.1; %! A2 = A .* A; %! s = sum (A2(:)); %! res = sqrt (s / 99); %! assert (std2 (eye (10)), res, eps); %! assert (std2 (single (eye (10))), single(res), 2 * eps('single')); %! assert (std2 (uint8 (eye (10))), res, eps); %! assert (std2 (int8 (eye (10))), res, eps); %! assert (std2 (uint8 (eye (10))), res, eps); %! assert (std2 (uint16 (eye (10))), res, eps); %! assert (std2 (int16 (eye (10))), res, eps); %! assert (std2 (int32 (eye (10))), res, eps); %! assert (std2 (uint32 (eye (10))), res, eps); %! assert (std2 (int64 (eye (10))), res, eps); %! assert (std2 (uint64 (eye (10))), res, eps); %! assert (std2 (int64 (2^63 * eye (10))) / 2^63, res, eps); %! assert (std2 (uint64 (2^64 * eye (10))) / 2^64, res, eps); %! assert (class (std2 (eye (10))), 'double'); %! assert (class (std2 (single (eye (10)))), 'single'); %! assert (class (std2 (uint8 (eye (10)))), 'double'); %! ## Verify std2 accepts complex data types %!assert (std2 ([2, 3+4i; 55, 66+77i]), 50.760056474883214, 10*eps) %! ## Expected test failures due to int64 -> double conversion limitations. %!error assert (std2 (int64 (2^64 * eye (10))) / 2^64, std2 (eye (10))) %!error assert (std2 (uint64 (2^65 * eye (10))) / 2^65, std2 (eye (10))) %!error std2 () %!error std2 ('aaa') %!error std2 (eye (10), eye (10)) image-2.16.1/inst/PaxHeaders.61586/rgb2ycbcr.m0000644000000000000000000000006215005110255015437 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/rgb2ycbcr.m0000644000175000017500000000541715005110255017044 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{YCbCrmap} =} rgb2ycbcr (@var{cmap}) ## @deftypefnx {Function File} {@var{YCbCr} =} rgb2ycbcr (@var{RGB}) ## @deftypefnx {Function File} {@dots{} =} rgb2ycbcr (@dots{}, [@var{Kb} @var{Kr}]) ## @deftypefnx {Function File} {@dots{} =} rgb2ycbcr (@dots{}, @var{standard}) ## Convert RGB values to YCbCr. ## ## The conversion changes the image @var{RGB} or colormap @var{cmap}, from ## the RGB color model to YCbCr (luminance, chrominance blue, and chrominance ## red). @var{RGB} must be of class double, single, uint8, or uint16. ## ## The formula used for the conversion is dependent on two constants, @var{Kb} ## and @var{Kr} which can be specified individually, or according to existing ## standards: ## ## @table @asis ## @item "601" (default) ## According to the ITU-R BT.601 (formerly CCIR 601) standard. Its values ## of @var{Kb} and @var{Kr} are 0.114 and 0.299 respectively. ## @item "709" ## According to the ITU-R BT.709 standard. Its values of @var{Kb} and ## @var{Kr} are 0.0722 and 0.2116 respectively. ## @item "2020" ## According to the ITU-R BT.2020 standard. Its values of @var{Kb} and ## @var{Kr} are 0.0593 and 0.2627 respectively. ## @end table ## ## @seealso{hsv2rgb, ntsc2rgb, rgb2hsv, rgb2ntsc} ## @end deftypefn function ycbcr = rgb2ycbcr (rgb, standard = "601") if (nargin < 1 || nargin > 2) print_usage (); endif ycbcr = ycbcrfunc ("rgb2ycbcr", rgb, standard); endfunction %!test %! in(:,:,1) = magic (5); %! in(:,:,2) = magic (5); %! in(:,:,3) = magic (5); %! out(:,:,1) = [31 37 17 23 29 %! 36 20 22 28 30 %! 19 21 27 33 35 %! 25 26 32 34 19 %! 25 31 37 18 24]; %! out(:,:,2) = 128; %! out(:,:,3) = 128; %! assert (rgb2ycbcr (uint8 (in)), uint8 (out)); %!shared cbcr %! cbcr = 0.5019607843137255; %! out(1:10, 1) = linspace (16/255, 235/255, 10); %! out(:, [2 3]) = cbcr; %! assert (rgb2ycbcr (gray (10)), out, 0.00001); %!assert (rgb2ycbcr ([1 1 1]), [0.92157 cbcr cbcr], 0.0001); %!assert (class (rgb2ycbcr (single (rand (5, 5, 3)))), "single") image-2.16.1/inst/PaxHeaders.61586/bwunpack.m0000644000000000000000000000006215005110255015372 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwunpack.m0000644000175000017500000000716615005110255017002 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Martin Janda ## Copyright (C) 2018 David Miguel Susano Pinto ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {} bwunpack (@var{bwp}) ## @deftypefnx {} {} bwunpack (@var{bwp}, @var{m}) ## Unpack binary image. ## ## Each row of the packed binary image @var{bwp}, represented as an ## uint32 matrix, is unpacked into 32 rows of logical values. The ## unpacking is done such that the least significant bit of the first ## element in @var{bwp} maps to the first element in the unpacked ## image, and the most significant bit to the 32th element. ## ## The unpacked image will be a logical array with @var{m} rows. ## The length of the other dimensions will be the same as @var{bwp}. ## If @var{m} is not specified, it will unpack all bits in @var{bwp}, ## otherwise the extra bits will be considered padding resulting ## from the packing. See the help text for @code{bwpack} for ## details. ## ## @seealso{bwpack, bitpack, bitunpack} ## @end deftypefn function bw = bwunpack (bwp, m) if (nargin < 1) print_usage (); endif if (! isa (bwp, "uint32")) error ("Octave:invalid-input-arg", "bwunpack: BWP must be an uint32 array"); endif class_size = 32; # number of elements packed into a uint32 if (nargin < 2) m = rows (bwp) * class_size; elseif (m < 0 || fix (m) != m) error ("Octave:invalid-input-arg", "bwunpack: M must be a non-negative integer"); elseif (m > (rows (bwp) * class_size)) error ("Octave:invalid-input-arg", ["bwunpack: M must not be larger than the number of bits " ... "on each column"]); endif packed_rows = rows (bwp) * class_size; dims = size (bwp); bw = reshape (bitunpack (bwp), [packed_rows dims(2:end)]); bw(m+1:end,:) = []; # remove padding if any endfunction %!error id=Octave:invalid-fun-call bwunpack () %!error bwunpack (uint8 (1)) %!error bwunpack (uint32 (1), -1) %!error bwunpack (uint32 (1), 4.2) %!xtest %! ## bug #55521 %! assert (bwunpack (uint32 (2.^[0:31])), logical (eye (32))) %!xtest %! ## bug #55521 %! assert (bwunpack (uint32 (repmat (7, [1 3 3 3])), 3), true (3, 3, 3, 3)) %!assert (bwunpack (uint32 (zeros (0, 0))), false (0, 0)) %!assert (bwunpack (uint32 (zeros (0, 0)), 0), false (0, 0)) %!assert (bwunpack (uint32 (zeros (0, 5)), 0), false (0, 5)) %!assert (bwunpack (uint32 (zeros (0, 5, 7)), 0), false (0, 5, 7)) %!assert (bwunpack (uint32 (zeros (1, 0))), false (32, 0)) %!assert (bwunpack (uint32 (zeros (2, 0, 7))), false (64, 0, 7)) %!assert (bwunpack (uint32 (zeros (2, 0, 7))), false (64, 0, 7)) %!assert (bwunpack (uint32 (zeros (2, 0, 7)), 60), false (60, 0, 7)) ## Unpacking more bytes than what's available on the input needs to be ## an error. This works in Matlab but it's their bug and the result ## is not even reproducible. %!error %! bwunpack (uint32 (1), 1042) image-2.16.1/inst/PaxHeaders.61586/analyze75write.m0000644000000000000000000000006215005110255016452 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/analyze75write.m0000644000175000017500000002431115005110255020051 0ustar00avinoamavinoam00000000000000%% Copyright (C) 2012 Adam H Aitkenhead %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; either version 3 of the License, or %% (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; If not, see . %% -*- texinfo -*- %% @deftypefn {Function File} analyze75write (@var{filename}, @var{data}, @var{header}) %% @deftypefnx {Function File} analyze75write (@var{filename}, @var{data}, @var{x}, @var{y}, @var{z}) %% @deftypefnx {Function File} analyze75write (@var{filename}, @var{data}, @var{header}, @var{x}, @var{y}, @var{z}) %% Write image data to an Analyze 7.5 file. %% %% @var{filename} is the path to write the Analyze 7.5 file; @var{data} is %% the 3D image data; @var{header} is a structure containing the file %% information; @var{x}, @var{y}, @var{z} are lists of the x,y,z coordinates %% (in cm) of the data grid. %% %% @seealso{analyze75info, analyze75read} %% @end deftypefn %% Author: Adam H. Aitkenhead function analyze75write (varargin); if (nargin ~= 3 && nargin ~= 5 && nargin ~= 6) print_usage; else filename = varargin{1}; data = varargin{2}; if (nargin ==3) header = varargin{3}; x = ( (0:header.Dimensions(2)-1) - (header.Dimensions(2)-1)/2 ) * header.PixelDimensions(2); y = ( (0:header.Dimensions(1)-1) - (header.Dimensions(1)-1)/2 ) * header.PixelDimensions(1); z = ( (0:header.Dimensions(3)-1) - (header.Dimensions(3)-1)/2 ) * header.PixelDimensions(3); % Convert mm to cm if strncmpi(header.VoxelUnits,'mm',2)==1 x = x / 10; y = y / 10; z = z / 10; end elseif (nargin ==5) header = struct; x = varargin{3}; y = varargin{4}; z = varargin{5}; elseif (nargin ==6) header = varargin{3}; x = varargin{4}; y = varargin{5}; z = varargin{6}; end end if (~ischar (filename)) error ('analyze75write: `filename'' must be a string.'); end %% Strip the filename of the extension fileextH = strfind (filename, '.hdr'); fileextI = strfind (filename, '.img'); if (~isempty (fileextH)) fileprefix = filename(1:fileextH(end)-1); elseif (~isempty (fileextI)) fileprefix = filename(1:fileextI(end)-1); else fileprefix = filename; end % Check the byteorder if (nargin == 6) if (isfield (header, 'ByteOrder')) && (any (strcmpi (header.ByteOrder, {'ieee-be', 'b'}))) warning ('analyze75write: No support for big-endian. Please consider submitting a patch. Attempting to write as little-endian'); end end header.ByteOrder = 'ieee-le'; %% Rearrange the data data = permute(data,[2,1,3]); % Force uniform slice spacing if (max(unique(diff(z))) - min(unique(diff(z))) > 1e-4) warning ('analyze75write: Data slices must be equally spaced. Attempting to interpolate the data onto equally spaced slices.') [data,z] = interpslices(data,x,y,z); end % Check certain header fields header.PixelDimensions = single(10 * [x(2)-x(1),y(2)-y(1),z(2)-z(1)]); header.Dimensions = int16([size(data),1]); header.HdrFileSize = int32(348); header.VoxelUnits = 'mm '; header.GlobalMin = int32(min(data(:))); header.GlobalMax = int32(max(data(:))); header.FileName = [fileprefix,'.hdr']; % Descriptor: Generate a string containing the coordinates of the first voxel (stored in the header for information only) origintext = ['Coordinates of first voxel: ',num2str( 10* [x(1),y(1),z(1)] ,' %07.2f' )]; header.Descriptor = sprintf('%-80s',origintext); % Determine the type of data if (isa(data,'int16')) header.ImgDataType = 'DT_SIGNED_SHORT'; header.BitDepth = int16(16); DataTypeLabel = int16(4); DataTypeString = 'int16'; elseif (isa(data,'int32')) header.ImgDataType = 'DT_SIGNED_INT'; header.BitDepth = int16(32); DataTypeLabel = int16(8); DataTypeString = 'int32'; elseif (isa(data,'single')) header.ImgDataType = 'DT_FLOAT'; header.BitDepth = int16(32); DataTypeLabel = int16(16); DataTypeString = 'single'; elseif (isa(data,'double')) header.ImgDataType = 'DT_DOUBLE'; header.BitDepth = int16(64); DataTypeLabel = int16(64); DataTypeString = 'double'; end % Write the .hdr file fidH = fopen([fileprefix,'.hdr'],'w'); fwrite(fidH,header.HdrFileSize,'int32',header.ByteOrder); % HdrFileSize fwrite(fidH,repmat(' ',1,10),'char',header.ByteOrder); % HdrDataType fwrite(fidH,repmat(' ',1,18),'char',header.ByteOrder); % DatabaseName fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % Extents fwrite(fidH,zeros(1,1,'int16'),'int16',header.ByteOrder); % SessionError fwrite(fidH,'r','char',header.ByteOrder); % Regular fwrite(fidH,repmat(' ',1,1),'char',header.ByteOrder); % unused fwrite(fidH,zeros(1,1,'int16'),'int16',header.ByteOrder); % unused fwrite(fidH,header.Dimensions(1),'int16',header.ByteOrder); % Dimensions(1) fwrite(fidH,header.Dimensions(2),'int16',header.ByteOrder); % Dimensions(2) fwrite(fidH,header.Dimensions(3),'int16',header.ByteOrder); % Dimensions(3) fwrite(fidH,header.Dimensions(4),'int16',header.ByteOrder); % Dimensions(4) fwrite(fidH,zeros(1,3,'int16'),'int16',header.ByteOrder); % unused fwrite(fidH,header.VoxelUnits,'char',header.ByteOrder); % VoxelUnits if (isfield(header,'CalibrationUnits')) % CalibrationUnits fwrite(fidH,sprintf('%-8s',header.CalibrationUnits(1:min([8,numel(header.CalibrationUnits)]))),'char',header.ByteOrder); else fwrite(fidH,repmat(' ',1,8),'char',header.ByteOrder); end fwrite(fidH,zeros(1,1,'int16'),'int16',header.ByteOrder); % unused fwrite(fidH,DataTypeLabel,'int16',header.ByteOrder); % ImgDataType fwrite(fidH,header.BitDepth,'int16',header.ByteOrder); % BitDepth fwrite(fidH,zeros(1,1,'int16'),'int16',header.ByteOrder); % unused fwrite(fidH,zeros(1,1,'single'),'float',header.ByteOrder); % unused fwrite(fidH,header.PixelDimensions(1),'float',header.ByteOrder); % PixelDimensions(1) fwrite(fidH,header.PixelDimensions(2),'float',header.ByteOrder); % PixelDimensions(2) fwrite(fidH,header.PixelDimensions(3),'float',header.ByteOrder); % PixelDimensions(3) fwrite(fidH,zeros(1,4,'single'),'float',header.ByteOrder); % unused fwrite(fidH,zeros(1,1,'single'),'float',header.ByteOrder); % VoxelOffset fwrite(fidH,zeros(1,3,'single'),'float',header.ByteOrder); % unused fwrite(fidH,zeros(1,1,'single'),'float',header.ByteOrder); % CalibrationMax fwrite(fidH,zeros(1,1,'single'),'float',header.ByteOrder); % CalibrationMin fwrite(fidH,zeros(1,1,'single'),'float',header.ByteOrder); % Compressed fwrite(fidH,zeros(1,1,'single'),'float',header.ByteOrder); % Verified fwrite(fidH,header.GlobalMax,'int32',header.ByteOrder); % GlobalMax fwrite(fidH,header.GlobalMin,'int32',header.ByteOrder); % GlobalMin fwrite(fidH,header.Descriptor,'char',header.ByteOrder); % Descriptor fwrite(fidH,'none ','char',header.ByteOrder); % AuxFile fwrite(fidH,repmat(' ',1,1),'char',header.ByteOrder); % Orientation fwrite(fidH,repmat(' ',1,10),'char',header.ByteOrder); % Originator fwrite(fidH,repmat(' ',1,10),'char',header.ByteOrder); % Generated fwrite(fidH,repmat(' ',1,10),'char',header.ByteOrder); % Scannumber if (isfield(header,'PatientID')) % PatientID fwrite(fidH,sprintf('%-10s',header.PatientID(1:min([10,numel(header.PatientID)]))),'char',header.ByteOrder); else fwrite(fidH,repmat(' ',1,10),'char',header.ByteOrder); end if (isfield(header,'ExposureDate')) % ExposureDate fwrite(fidH,sprintf('%-10s',header.ExposureDate(1:min([10,numel(header.ExposureDate)]))),'char',header.ByteOrder); elseif (isfield(header,'StudyDate')) fwrite(fidH,sprintf('%-10s',header.StudyDate(1:min([10,numel(header.StudyDate)]))),'char',header.ByteOrder); else fwrite(fidH,repmat(' ',1,10),'char',header.ByteOrder); end if (isfield(header,'ExposureTime')) % ExposureTime fwrite(fidH,sprintf('%-10s',header.ExposureTime(1:min([10,numel(header.ExposureTime)]))),'char',header.ByteOrder); elseif (isfield(header,'StudyTime')) fwrite(fidH,sprintf('%-10s',header.StudyTime(1:min([10,numel(header.StudyTime)]))),'char',header.ByteOrder); else fwrite(fidH,repmat(' ',1,10),'char',header.ByteOrder); end fwrite(fidH,repmat(' ',1,3),'char',header.ByteOrder); % unused fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % Views fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % VolumesAdded fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % StartField fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % FieldSkip fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % OMax fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % OMin fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % SMax fwrite(fidH,zeros(1,1,'int32'),'int32',header.ByteOrder); % SMin fclose(fidH); % Write the .img file fidI = fopen([fileprefix,'.img'],'w'); fwrite(fidI,data,DataTypeString,header.ByteOrder); fclose(fidI); end function [datanew,znew] = interpslices(data,x,y,z) znew = z(1):min(diff(z)):z(end); datanew = zeros(numel(x),numel(y),numel(znew),class(data)); for loopN = 1:numel(znew) datanew(:,:,loopN) = interpn(x,y,z,data,x,y,znew(loopN)); end end image-2.16.1/inst/PaxHeaders.61586/roicolor.m0000644000000000000000000000006215005110255015410 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/roicolor.m0000644000175000017500000000414315005110255017010 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{BW} =} roicolor (@var{A}, @var{low}, @var{high}) ## @deftypefnx {Function File} {@var{BW} = } roicolor (@var{A},@var{v}) ## Select a Region Of Interest of an image based on color. ## ## BW = roicolor(A,low,high) selects a region of interest (ROI) of an ## image @var{A} returning a black and white image in a logical array (1 for ## pixels inside ROI and 0 outside ROI), which is formed by all pixels ## whose values lie within the colormap range specified by [@var{low} ## @var{high}]. ## ## BW = roicolor(A,v) selects a region of interest (ROI) formed by all ## pixels that match values in @var{v}. ## @end deftypefn function BW = roicolor (A, p1, p2) if (nargin < 2 || nargin > 3) print_usage; endif if (nargin == 2) if (!isvector(p1)) error("BW = roicolor(A, v): v should be a vector."); endif BW=logical(zeros(size(A))); for c=p1 BW|=(A==c); endfor elseif (nargin==3) if (!isscalar(p1) || !isscalar(p2)) error("BW = roicolor(A, low, high): low and high must be scalars."); endif BW=logical((A>=p1)&(A<=p2)); endif endfunction %!demo %! roicolor([1:10],2,4); %! % Returns '1' where input values are between 2 and 4 (both included). %!assert(roicolor([1:10],2,4),logical([0,1,1,1,zeros(1,6)])); %!assert(roicolor([1,2;3,4],3,3),logical([0,0;1,0])); %!assert(roicolor([1,2;3,4],[1,4]),logical([1,0;0,1])); image-2.16.1/inst/PaxHeaders.61586/bwmorph.m0000644000000000000000000000006215005110255015236 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwmorph.m0000644000175000017500000013143715005110255016645 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} bwmorph (@var{bw}, @var{operation}) ## @deftypefnx {Function File} {} bwmorph (@var{bw}, @var{operation}, @var{n}) ## Perform morphological operation on binary image. ## ## For a binary image @var{bw}, performs the morphological @var{operation}, ## @var{n} times. All possible values of @var{operation} are listed on the ## table below. By default, @var{n} is 1. If @var{n} is @code{Inf}, the ## operation is continually performed until it no longer changes the image. ## ## In some operations, @var{bw} can be a binary matrix with any number of ## dimensions (see details on the table of operations). ## ## Note that the output will always be of class logical, independently of ## the class of @var{bw}. ## ## @table @samp ## @item bothat ## Performs a bottom hat operation, a closing operation (which is a ## dilation followed by an erosion) and finally subtracts the original ## image (see @code{imbothat}). @var{bw} can have any number of ## dimensions, and @code{strel ("hypercube", ndims (@var{bw}), 3)} is ## used as structuring element. ## ## @item bridge ## Performs a bridge operation. Sets a pixel to 1 if it has two nonzero ## neighbours which are not connected, so it "bridges" them. There are ## 119 3-by-3 patterns which trigger setting a pixel to 1. ## ## @item clean ## Performs an isolated pixel remove operation. Sets a pixel to 0 if all ## of its eight-connected neighbours are 0. @var{bw} can have any number ## of dimensions in which case connectivity is @code{(3^ndims(@var{bw})) -1}, ## i.e., all of the elements around it. ## ## @item close ## Performs closing operation, which is a dilation followed by erosion ## (see @code{imclose}). @var{bw} can have any number of dimensions, ## and @code{strel ("hypercube", ndims (@var{bw}), 3)} is used as ## structuring element. ## ## @item diag ## Performs a diagonal fill operation. Sets a pixel to 1 if that ## eliminates eight-connectivity of the background. ## ## @item dilate ## Performs a dilation operation (see @code{imdilate}). @var{bw} can have ## any number of dimensions, and ## @code{strel ("hypercube", ndims (@var{bw}), 3)} is used as ## structuring element. ## ## @item endpoints ## Finds the endpoints of a skeleton. The skeleton can be ## computed using @code{bwmorph (bw, "skel")}. ## ## @item erode ## Performs an erosion operation (see @code{imerode}). @var{bw} can have ## any number of dimensions, and ## @code{strel ("hypercube", ndims (@var{bw}), 3)} is used as ## structuring element. ## ## @item fill ## Performs a interior fill operation. Sets a pixel to 1 if all ## four-connected pixels are 1. @var{bw} can have any number ## of dimensions in which case connectivity is @code{(2*ndims(@var{bw}))}. ## ## @item hbreak ## Performs a H-break operation. Breaks (sets to 0) pixels that are ## H-connected. ## ## @item majority ## Performs a majority black operation. Sets a pixel to 1 if the majority ## of the pixels (5 or more for a two dimensional image) in a 3-by-3 window ## is 1. If not set to 0. @var{bw} can have any number of dimensions in ## which case the window has dimensions @code{repmat (3, 1, ndims (@var{bw}))}. ## ## @item open ## Performs an opening operation, which is an erosion followed by a ## dilation (see @code{imopen}). @var{bw} can have any number of ## dimensions, and @code{strel ("hypercube", ndims (@var{bw}), 3)} ## is used as structuring element. ## ## @item remove ## Performs a iterior pixel remove operation. Sets a pixel to 0 if ## all of its four-connected neighbours are 1. @var{bw} can have any number ## of dimensions in which case connectivity is @code{(2*ndims(@var{bw}))}. ## ## @item shrink ## Performs a shrink operation. Sets pixels to 0 such that an object ## without holes erodes to a single pixel (set to 1) at or near its ## center of mass. An object with holes erodes to a connected ring lying ## midway between each hole and its nearest outer boundary. It preserves ## Euler number. ## ## @item skel ## Performs a skeletonization operation. It calculates a "median axis ## skeleton" so that points of this skeleton are at the same distance of ## its nearby borders. It preserver Euler number. Please read ## compatibility notes for more info. ## ## It uses the same algorithm as skel-pratt but this could change for ## compatibility in the future. ## ## @item skel-lantuejoul ## Performs a skeletonization operation as described in Gonzalez & Woods ## "Digital Image Processing" pp 538-540. The text references Lantuejoul ## as author of this algorithm. ## ## It has the beauty of being a clean and simple approach, but skeletons ## are thicker than they need to and, in addition, not guaranteed to be ## connected. ## ## This algorithm is iterative. It will be applied the minimum value of ## @var{n} times or number of iterations specified in algorithm ## description. It's most useful to run this algorithm with @code{n=Inf}. ## ## @var{bw} can have any number of dimensions. ## ## @item skel-pratt ## Performs a skeletonization operation as described by William K. Pratt ## in "Digital Image Processing". ## ## @item spur ## Performs a remove spur operation. It sets pixel to 0 if it has only ## one eight-connected pixel in its neighbourhood. ## ## @item thicken ## Performs a thickening operation. This operation "thickens" objects ## avoiding their fusion. Its implemented as a thinning of the ## background. That is, thinning on negated image. Finally a diagonal ## fill operation is performed to avoid "eight-connecting" objects. ## ## @item thin-pratt ## Performs a thinning operation, according to W. K. Pratt, ## "Digital Image Processing", 3rd Edition, pp 413-414. ## ## @item thin ## Performs a thinning operation. When n=Inf, thinning sets pixels to 0 ## such that an object without holes is converted to a stroke ## equidistant from its nearest outer boundaries. If the object has ## holes it creates a ring midway between each hole and its near outer ## boundary. This differ from shrink in that shrink converts objects ## without holes to a single pixels and thin to a stroke. It preserves ## Euler number. ## ## @item tophat ## Performs a top hat operation, a opening operation (which is an ## erosion followed by a dilation) and finally subtracts the original ## image (see @code{imtophat}). @var{bw} can have any number of ## dimensions, and @code{strel ("hypercube", ndims (@var{bw}), 3)} ## is used as structuring element. ## @end table ## ## Some useful concepts to understand operators: ## ## Operations are defined on 3-by-3 blocks of data, where the pixel in ## the center of the block. Those pixels are numerated as follows: ## ## @multitable @columnfractions 0.05 0.05 0.05 ## @item X3 @tab X2 @tab X1 ## @item X4 @tab X @tab X0 ## @item X5 @tab X6 @tab X7 ## @end multitable ## ## @strong{Neighbourhood definitions used in operation descriptions:} ## @table @code ## @item 'four-connected' ## It refers to pixels which are connected horizontally or vertically to ## X: X1, X3, X5 and X7. ## @item 'eight-connected' ## It refers to all pixels which are connected to X: X0, X1, X2, X3, X4, ## X5, X6 and X7. ## @end table ## ## @strong{Compatibility notes:} ## @table @code ## @item 'skel' ## Algorithm used here is described in Pratt's book. When applying it to ## the "circles" image in MATLAB documentation, results are not the ## same. Perhaps MATLAB uses Blum's algorithm (for further info please ## read comments in code). ## @item 'skel-pratt' ## This option is not available in MATLAB. ## @item 'skel-lantuejoul' ## This option is not available in MATLAB. ## @item 'thin-pratt' ## This option is not available in MATLAB. ## @item 'thicken' ## This implementation also thickens image borders. This can easily be ## avoided i necessary. MATLAB documentation doesn't state how it behaves. ## @end table ## ## References: ## W. K. Pratt, "Digital Image Processing" ## Gonzalez and Woods, "Digital Image Processing" ## ## @seealso{imdilate, imerode, imtophat, imbothat, makelut, applylut} ## @end deftypefn function bw2 = bwmorph (bw, operation, n = 1) if (nargin < 2 || nargin > 3) print_usage (); elseif (! isimage (bw)) error ("bwmorph: BW must be a binary image"); elseif (! ischar (operation)) error ("bwmorph: OPERATION must be a string"); elseif (! isnumeric (n) || ! isscalar (n)) error ("bwmorph: N must be a scalar number"); endif ## For undocumented Matlab compatibility if (n < 0) n = 1; endif ## Anything is valid, just convert it to logical bw = logical (bw); ## Some operations have no effect after being applied the first time. ## Those will set this to true and later set N to 1 (only exception is ## if N is set to 0 but even then we can't skip since we will display ## the image or return it depending on nargout) loop_once = false; post_morph = []; # post processing command (only if needed) switch (tolower (operation)) case "bothat" loop_once = true; se = strel ("hypercube", ndims (bw), 3); morph = @(x) imbothat (x, se); # case "branchpoints" # ## not implemented case "bridge" loop_once = true; ## see __bridge_lut_fun__ for rules ## lut = makelut ("__bridge_lut_fun__", 3); lut = logical ([0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;0;0;0;1;0;0;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;0;0;0;1;0;0;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;1;1;1;1;1;1;0;0;0;0;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]); morph = @(x) applylut (x, lut); case "clean" ## Remove elements that are surrounded by false elements. So we ## create a hypercube kernel of side length 3 and values 1, and ## center value with the connectivity value. After convolution, ## only true elements with another one surrounding it, will have ## values above the connectivity (remember that the input matrix ## is binary). loop_once = true; kernel = ones (repmat (3, 1, ndims (bw))); connectivity = numel (kernel) -1; kernel(ceil (numel (kernel) /2)) = connectivity; # n-dimensional center morph = @(x) convn (x, kernel, "same") > connectivity; case "close" loop_once = true; se = strel ("hypercube", ndims (bw), 3); morph = @(x) imclose (x, se); case "diag" ## see __diagonal_fill_lut_fun__ for rules ## lut = makelut ("__diagonal_fill_lut_fun__", 3); lut = logical ([0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;1;1;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;1;1;0;0;0;0;0;0;1;1;0;0;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]); morph = @(x) applylut (x, lut); case "dilate" se = strel ("hypercube", ndims (bw), 3); morph = @(x) imdilate (x, se); case "endpoints" ## lut = makelut ("__endpoints_fun__", 3); lut = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;0;1;1;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;1;1;1;1;0;]); morph = @(x) applylut (x, lut); case "erode" ## Matlab bwmorph acts different than their imerode. I'm unsure ## the cause of the bug but it seems to manifest on the image border ## only. It may be they have implemented this routines in both the ## im* functions, and in bwmorph. The rest of bwmorph that uses ## erosion (open, close, bothat, and tophat a least), suffer the ## same problem. We do not replicate the bug and use imerode. ## In 2013, Mathworks has confirmed this bug and will try to fix it ## for their next releases. So, do NOT fix this for "compatibility". se = strel ("hypercube", ndims (bw), 3); morph = @(x) imerode (x, se); case "fill" ## Fill elements that are surrounded by true elements (with ## connectivity 4 and its equivalent to N dimensions). ## So we create a hypercube kernel with the connected pixels as 1 ## and the center with the connectivity value. After convolution, ## only true elements, or elements surrounded by true elements ## will have a values >= connectivity. loop_once = true; kernel = conndef (ndims (bw), "minimal"); connectivity = nnz (kernel) -1; kernel(ceil (numel (kernel) /2)) = connectivity; morph = @(x) convn (x, kernel, "same") >= connectivity; case "hbreak" loop_once = true; ## lut = makelut (inline ("x(2,2)&&!(all(x==[1,1,1;0,1,0;1,1,1])||all(x==[1,0,1;1,1,1;1,0,1]))", "x"), 3); ## which is the same as lut = repmat ([false(16, 1); true(16, 1)], 16, 1); # identity lut([382 472]) = false; # the 2 exceptions morph = @(x) applylut (x, lut); case "majority" ## If the majority of the elements surrounding an element is true, ## it changes to true. We do this using convolution, with an ## hypercube kernel, any value of the convolution above the half ## number of elements becomes tru kernel = ones (repmat (3, 1, ndims (bw))); majority = numel (kernel) /2; morph = @(x) convn (x, kernel, "same") >= majority; case "open" loop_once = true; se = strel ("hypercube", ndims (bw), 3); morph = @(x) imopen (x, se); case "remove" ## Remove elements that are surrounded by true elements by 4 connectivity. ## We create a 4 connectivity kernel with -1 values, and a center with ## the number of -1 values. Only true elements can have positives values, ## with a maximum of 4 (or whatever is the connectivity value for that ## number of dimensions) from the kernel center, and -1 per each of the ## connectivity. If all are true, its value will go down to 0. loop_once = true; kernel = - conndef (ndims (bw), "minimal"); kernel(ceil (numel (kernel) /2)) = nnz (kernel) -1; morph = @(x) convn (x, kernel, "same") > 0; case "shrink" ## lut1 = makelut ("__conditional_mark_patterns_lut_fun__", 3, "S"); lut1 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;0;1;1;1;1;0;1;0;0;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;1;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;0;1;1;0;0]); ## lut2 = makelut (inline ("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'S')", "m"), 3); lut2 = logical ([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;0;0;1;0;0;0;0;0;1;0; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;0;0;0;0;0;0;1;1;0;0;1;0; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;0;0;0;0;1;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;0;0;1;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;1;0;1;0;0;1;0;1;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;1;1;0;1;0;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;0;0;1;0;1;0;0;1;0;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;0;0;0;1;0;1;0;0;1;0;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]); morph = @(x) x & applylut (applylut (x, lut1), lut2); case {"skel", "skel-pratt"} ## WARNING: Result doesn't look as MATLAB's sample. It has been ## WARNING: coded following Pratt's guidelines for what he calls ## WARNING: is a "reasonably close approximation". I couldn't find ## WARNING: any bug. ## WARNING: Perhaps MATLAB uses Blum's algorithm (which Pratt ## WARNING: refers to) in: H. Blum, "A Transformation for ## WARNING: Extracting New Descriptors of Shape", Symposium Models ## WARNING: for Perception of Speech and Visual Form, W. ## WARNING: Whaten-Dunn, Ed. MIT Press, Cambridge, MA, 1967. ## lut1 = makelut ("__conditional_mark_patterns_lut_fun__", 3, "K"); lut1 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;1;1;1;1;0]); ## lut2 = makelut (inline ("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'K')", "m") ,3); lut2 = logical([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;0;1;0;0;0;1;0;1;1;0;0;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;0;0;0;1;1;0;0;1;1;0;0;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;0;0;0;1;0;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;1;1;0;1;1;1;0;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;0;0;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]); morph = @(x) x & applylut (applylut (x, lut1), lut2); post_morph = @(x) bwmorph (x, "bridge"); case "skel-lantuejoul" ## This transform does not fit well in the same loop as the others, ## since each iteration requires values from the previous one. Because ## of this, we will set n to 0 in the end. However, we must take care ## to not touch the input image if n already is zero. if (n > 0) se = strel ("hypercube", ndims (bw), 3); bw_tmp = false (size (bw)); # skeleton result i = 1; while (i <= n) if (! any (bw(:))) ## If erosion from the previous result is 0-matrix then we are ## over because the top-hat transform below will also be a 0-matrix ## and we will be |= to a 0-matrix. break endif ebw = imerode (bw, se); ## the right hand side of |= is the top-hat transform. However, ## we are not using imtophat because we will also want the output ## of the erosion for the next iteration. This saves us calling ## imerode twice for the same thing. bw_tmp |= bw & ! imdilate (ebw, se); bw = ebw; i++; endwhile bw = bw_tmp; n = 0; # don't do anything else endif case "spur" ## lut = makelut(inline("xor(x(2,2),(sum((x&[0,1,0;1,0,1;0,1,0])(:))==0)&&(sum((x&[1,0,1;0,0,0;1,0,1])(:))==1)&&x(2,2))","x"),3); ## which is the same as lut = repmat ([false(16, 1); true(16,1)], 16, 1); # identity lut([18, 21, 81, 273]) = false; # 4 qualifying patterns morph = @(x) applylut (x, lut); case "thicken" if (n > 0) pads = repmat (2 * min ([max(size (bw)) n]), [1 ndims(bw)]); bw = padarray (bw, pads, false); bw = bwmorph (! bw, "thin-pratt", n); loop_once = true; morph = @(x) bwmorph (x, "diag"); ## Remove ND padding idx = arrayfun (@colon, pads +1, size (bw) -pads, "UniformOutput", false); post_morph = @(x) ! x(idx{:}); endif case "thin-pratt" ## lut1 = makelut ("__conditional_mark_patterns_lut_fun__", 3, "T"); lut1 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;0;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;0;0;0;0;0;0;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;1;1;0;1;0;0;0;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;1;1;1;1;0;0;1;1;0;0]); ## lut2 = makelut (inline ("!m(2,2)||__unconditional_mark_patterns_lut_fun__(m,'T')", "m"), 3); lut2 = logical ([1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;1;1;0;1;0;1;1;0;0;0;0;1;0; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;0;0;0;0;0;1;1;0;0;1;0; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;1;1;1;0;0;0;1;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;1;1;0;0;1;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;0;1;0;0;1;0;1;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;0;1;0;1;1;1;0;1;0;1;0;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;1;0;1;0;0;1;0;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;0;1;0;0;1;0;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1;0;1;1;1;1;1;1;1; 1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1]); morph = @(x) x & applylut (applylut (x, lut1), lut2); case "thin" ## lut1 = makelut (@__thin_fun1__, 3); lut1 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;0;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;0;1;0;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;0;0;1;0;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;1;0;0;1;1;0;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;0;0;1;0;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;1;0;0;1;1;0;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;0;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;0;0;1;0;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;1;0;0;1;1;0;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;0;0;1;0;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;0;1;1;1;0;0;1;1;0;1;1;1;]); ## lut2 = makelut (@__thin_fun2__, 3); lut2 = logical ([0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;0;1;1;0;0;1;1;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;1;1;0;0;1;1;0;0;1;1;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;0;0;0;1;1;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;0;0;1;1;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;0;0;1;1;0;0; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;0;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1; 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;]); morph = @(x) applylut (applylut (x, lut1), lut2); case "tophat" ## top hat filtering has no effect after being performed once ## (inherits this behaviour from closing and opening) loop_once = true; se = strel ("hypercube", ndims (bw), 3); morph = @(x) imtophat (x, se); otherwise error ("bwmorph: unknown OPERATION '%s' requested", operation); endswitch if (loop_once && n > 1) n = 1; endif bw2_tmp = bw; ## make sure bw2_tmp will exist later, even if n == 0 i = 1; while (i <= n) ## a for loop wouldn't work because n can be Inf bw2_tmp = morph (bw); if (isequal (bw, bw2_tmp)) ## if it doesn't change we don't need to process it further break endif bw = bw2_tmp; i++; endwhile ## process post processing commands if needed if (! isempty (post_morph) && n > 0) bw2_tmp = post_morph (bw2_tmp); endif if (nargout > 0) bw2 = bw2_tmp; else imshow (bw2_tmp); endif endfunction %!demo %! bwmorph (true (11), "shrink", Inf) %! # Should return 0 matrix with 1 pixel set to 1 at (6,6) ## Test skel-lantuejoul using Gonzalez & Woods example (fig 8.39) %!test %! slBW = logical ([ 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 %! 0 0 1 1 0 0 0 %! 0 0 1 1 0 0 0 %! 0 0 1 1 1 0 0 %! 0 0 1 1 1 0 0 %! 0 1 1 1 1 1 0 %! 0 1 1 1 1 1 0 %! 0 1 1 1 1 1 0 %! 0 1 1 1 1 1 0 %! 0 1 1 1 1 1 0 %! 0 0 0 0 0 0 0]); %! %! rslBW = logical ([ 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 %! 0 0 1 1 0 0 0 %! 0 0 1 1 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 1 0 0 0 %! 0 0 0 1 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 1 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0]); %! assert (bwmorph (slBW, "skel-lantuejoul", 1), [rslBW(1:5,:); false(7, 7)]); %! assert (bwmorph (slBW, "skel-lantuejoul", 2), [rslBW(1:8,:); false(4, 7)]); %! assert (bwmorph (slBW, "skel-lantuejoul", 3), rslBW); %! assert (bwmorph (slBW, "skel-lantuejoul", Inf), rslBW); ## Test for bug #39293 %!test %! bw = [ %! 0 1 1 1 1 1 %! 0 1 1 1 1 1 %! 0 1 1 1 1 1 %! 1 1 1 1 1 1 %! 1 1 1 1 1 1 %! 1 1 1 1 1 1 %! 1 1 1 1 1 0 %! 1 1 1 1 1 0 %! 1 1 1 1 1 0]; %! %! final = logical ([ %! 0 1 0 0 0 1 %! 0 0 1 0 1 0 %! 0 0 0 1 0 0 %! 0 0 0 1 0 0 %! 0 0 1 1 0 0 %! 0 0 1 0 0 0 %! 0 0 1 0 0 0 %! 0 1 0 1 0 0 %! 1 0 0 0 1 0]); %! assert (bwmorph (bw, "skel", Inf), final) %! assert (bwmorph (bw, "skel", 3), final) %!error bwmorph ("not a matrix", "dilate") ## this makes sense to be an error but for Matlab compatibility, it is not %!assert (bwmorph (magic (10), "dilate"), imdilate (logical (magic (10)), ones (3))); %!test %! in = logical ([1 1 0 0 1 0 1 0 0 0 1 1 1 0 1 1 0 1 0 0 %! 1 1 1 0 1 0 1 1 1 1 0 1 0 1 0 0 0 0 0 0 %! 0 1 1 1 0 1 1 0 0 0 1 1 0 0 1 1 0 0 1 0 %! 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 1 1 0 0 1 %! 0 1 0 0 1 1 0 1 1 0 0 0 0 0 1 1 0 0 1 0 %! 0 0 1 1 1 1 1 0 0 1 0 1 1 1 0 0 1 0 0 1 %! 0 1 1 1 1 1 1 0 1 1 1 0 0 0 1 0 0 1 0 0 %! 1 0 1 1 1 0 1 1 0 1 0 0 1 1 1 0 0 1 0 0 %! 1 0 1 1 1 0 1 0 0 1 0 0 1 1 0 0 1 1 1 0 %! 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 1 1 0 0 %! 1 1 1 1 1 1 0 1 0 1 0 0 0 0 0 0 1 0 1 1 %! 0 1 0 1 1 0 0 1 1 1 0 0 0 0 0 0 0 1 0 0 %! 0 0 1 1 0 1 1 1 1 0 0 1 0 0 0 0 1 0 1 1 %! 0 0 1 1 0 0 1 1 1 0 0 0 1 1 1 1 0 0 0 0 %! 0 0 1 0 0 0 0 0 0 1 0 0 1 1 1 1 0 0 0 0 %! 0 0 0 0 0 0 1 1 1 0 0 0 1 1 1 1 1 0 0 0 %! 0 1 0 0 0 1 1 0 1 1 0 0 1 1 1 0 1 1 1 1 %! 1 0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 1 1 1 %! 0 0 1 1 0 1 1 1 1 0 0 0 0 1 1 0 1 1 1 1 %! 0 1 1 0 0 1 0 0 1 1 0 0 1 0 0 1 0 0 0 1]); %! se = strel ("arbitrary", ones (3)); %! %! assert (bwmorph (in, "dilate"), imdilate (in, se)); %! assert (bwmorph (in, "dilate", 3), imdilate (imdilate (imdilate (in, se), se), se)); %! assert (bwmorph (in, "bothat"), imbothat (in, se)); %! assert (bwmorph (in, "tophat"), imtophat (in, se)); %! assert (bwmorph (in, "open"), imopen (in, se)); %! assert (bwmorph (in, "close"), imclose (in, se)); %!assert (bwmorph ([1 0 0; 1 0 1; 0 0 1], "bridge"), logical ([1 1 0; 1 1 1; 0 1 1])); %!assert (bwmorph ([0 0 0; 1 0 1; 0 0 1], "clean"), logical ([0 0 0; 0 0 1; 0 0 1])); %!assert (bwmorph ([0 0 0; 0 1 0; 0 0 0], "clean"), false (3)); %!assert (bwmorph ([0 1 0; 1 0 0; 0 0 0], "diag"), logical ([1 1 0; 1 1 0; 0 0 0])); %!test %! in = logical ([0 1 0 1 0 %! 1 1 1 0 1 %! 1 0 0 1 0 %! 1 1 1 0 1 %! 1 1 1 1 1]); %! out = logical ([0 1 0 1 0 %! 1 1 1 1 1 %! 1 0 0 1 0 %! 1 1 1 1 1 %! 1 1 1 1 1]); %! assert (bwmorph (in, "fill"), out); %!assert (bwmorph ([1 1 1; 0 1 0; 1 1 1], "hbreak"), logical ([1 1 1; 0 0 0; 1 1 1])); %!test %! in = logical ([0 1 0 0 0 %! 1 0 0 1 0 %! 1 0 1 0 0 %! 1 1 1 1 1 %! 1 1 1 1 1]); %! %! out = logical ([0 1 0 0 0 %! 1 0 0 1 0 %! 1 0 1 0 0 %! 1 1 0 1 1 %! 1 1 1 1 1]); %! assert (bwmorph (in, "remove"), out); %! %! out = logical ([0 1 0 0 0 %! 1 0 0 1 0 %! 1 0 1 0 0 %! 1 1 0 1 1 %! 1 1 1 1 1]); %! assert (bwmorph (in, "remove", Inf), out); %!xtest %! ## tests for spur are failing (matlab incompatible) %! in = logical ([0 1 0 0 0 %! 1 0 0 1 0 %! 1 0 1 0 0 %! 1 1 1 1 1 %! 1 1 1 1 1]); %! %! out = logical ([0 1 0 0 0 %! 1 0 0 0 0 %! 1 0 1 0 0 %! 1 1 1 1 1 %! 1 1 1 1 1]); %! assert (bwmorph (in, "spur"), out); %! %! out = logical ([0 1 0 0 0 %! 1 0 0 0 0 %! 1 0 0 0 0 %! 1 1 1 1 1 %! 1 1 1 1 1]); %! assert (bwmorph (in, "spur", Inf), out); ## several tests for "thicken": %!test %! bw = false (3, 3); %! bw(3, 1) = true; %! out = bwmorph (bw, "thicken", 0); %! assert (out, bw) %!test %! bw = false (8, 7); %! bw(8, 1) = true; %! expected = logical ([ %! 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 %! 1 1 1 0 0 0 0 %! 1 1 1 1 0 0 0 %! 1 1 1 1 1 0 0 %! 1 1 1 1 1 1 0 %! 1 1 1 1 1 1 1]); %! out = bwmorph (bw, "thicken", 6); %! assert (out, expected) %!test %! bw = false (8, 7); %! bw(2, 4) = true; %! expected = logical ([ %! 0 0 1 1 1 0 0 %! 0 1 1 1 1 1 0 %! 0 0 1 1 1 0 0 %! 0 0 0 1 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0]); %! out = bwmorph (bw, "thicken", 2); %! assert (out, expected) %!test %! bw = false (8, 7); %! bw (6, 3) = true ; %! expected1 = logical ([ %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 1 0 0 0 0 %! 0 1 1 1 0 0 0 %! 0 0 1 0 0 0 0 %! 0 0 0 0 0 0 0]); %! expected3 = logical ([ %! 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 %! 0 0 1 0 0 0 0 %! 0 1 1 1 0 0 0 %! 1 1 1 1 1 0 0 %! 1 1 1 1 1 1 0 %! 1 1 1 1 1 0 0 %! 0 1 1 1 0 0 0]); %! out1 = bwmorph (bw, "thicken", 1); %! out3 = bwmorph (bw, "thicken", 3); %! assert (out1, expected1) %! assert (out3, expected3) %!test %! bw = false (10, 10); %! bw(2, 3) = true; %! bw(7, 7) = true; %! out_inf = bwmorph (bw, "thicken", Inf); %! assert (out_inf(1, 9), false) %!test %! bw = false (3, 3); %! bw(3, 1) = true; %! out = bwmorph (bw, "thicken", 4); %! assert (out, true (3, 3)) %!xtest %! ## bug #44396 %! in = [ %! 0 0 0 1 0 %! 1 1 1 1 0 %! 0 0 1 1 0 %! 0 0 1 1 0 %! 0 0 0 1 0]; %! out = [ %! 0 0 0 0 0 %! 0 1 1 0 0 %! 0 0 0 1 0 %! 0 0 0 0 0 %! 0 0 0 0 0]; %! assert (bwmorph (in, "shrink"), logical (out)); %!test %! H = false (7,7); %! H(2:3,2:3) = 1; %! H(5:6,5:6) = 1; %! T = logical([0 0 0 0 0 0 0; %! 0 0 0 0 0 0 0; %! 0 1 0 0 0 0 0; %! 0 0 0 0 0 0 0; %! 0 0 0 0 0 0 0; %! 0 0 0 0 1 0 0; %! 0 0 0 0 0 0 0]); %! out = bwmorph (H, "thin", 1); %! assert (T, out) %! %! H(4:6,4:6) = 1; %! T = logical([0 0 0 0 0 0 0; %! 0 0 0 0 0 0 0; %! 0 1 1 0 0 0 0; %! 0 0 0 1 0 0 0; %! 0 0 0 0 1 0 0; %! 0 0 0 0 0 0 0; %! 0 0 0 0 0 0 0]); %! out = bwmorph (H, "thin", 1); %! assert (T, out) %! %! H3 = [0 0 0 0 0 0; %! 0 1 1 1 0 0; %! 0 1 1 1 0 0; %! 0 0 0 1 0 1; %! 0 0 0 0 1 1; %! 0 0 0 1 1 1]; %! out3 = bwmorph (H3, "thin", 1); %! expected3 = logical( %! [0 0 0 0 0 0; %! 0 0 0 0 0 0; %! 0 1 1 0 0 0; %! 0 0 0 1 0 1; %! 0 0 0 0 1 0; %! 0 0 0 1 1 0]); %! assert (out3, expected3) %! %! out33 = bwmorph (H3, "thin", 2); %! expected33 = logical( %! [0 0 0 0 0 0; %! 0 0 0 0 0 0; %! 0 1 1 0 0 0; %! 0 0 0 1 0 1; %! 0 0 0 0 1 0; %! 0 0 0 1 0 0]); %! assert (out33, expected33) %! %! out333 = bwmorph (H3, "thin", inf); %! assert (out333, expected33) %!test ## test "endpoints" %! in = logical ([ %! 1 0 0 0 %! 0 1 0 0 %! 0 0 1 0 %! 0 0 0 0]); %! out = logical ([ %! 1 0 0 0 %! 0 0 0 0 %! 0 0 1 0 %! 0 0 0 0]); %! assert (bwmorph (in, "endpoints"), out); %! %! A = logical ([0 0 0 0 0; 0 0 1 0 0; 0 1 1 1 0; 0 0 1 0 0; 0 0 0 0 0]); %! B = logical ([0 0 0 0 0; 0 0 1 0 0; 0 1 0 1 0; 0 0 1 0 0; 0 0 0 0 0]); %! assert (bwmorph (A, "endpoints"), B); %! %! A = logical ([0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 1 1 %! 0 0 1 1 1 1 0 0 %! 0 0 0 1 1 0 0 0 %! 0 0 1 1 1 1 0 0 %! 0 1 0 0 0 0 1 0 %! 1 0 0 0 0 0 0 1]); %! B = logical ([0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 1 %! 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 0 0 0 %! 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 1]); %! assert (bwmorph (A, "endpoints"), B); %! %! A = logical([0 0 0 0 0; 0 1 1 1 0; 0 1 1 1 0; 0 1 1 1 0; 0 0 0 0 0]); %! B = logical([0 0 0 0 0; 0 1 1 1 0; 0 1 0 1 0; 0 1 1 1 0; 0 0 0 0 0]); %! assert (bwmorph (A, "endpoints"), B); %! assert (bwmorph (B, "endpoints"), zeros (5, "logical")); %! %! A = logical([0,0,0,0,0,0,0,0,0,0,0,0,0,0 %! 0,0,1,1,1,1,1,1,1,1,1,1,0,0 %! 0,0,0,0,0,0,0,0,0,0,0,0,0,0]); %! B = logical([0,0,0,0,0,0,0,0,0,0,0,0,0,0 %! 0,0,1,0,0,0,0,0,0,0,0,1,0,0 %! 0,0,0,0,0,0,0,0,0,0,0,0,0,0]); %! C = logical([1,0,0,0,0,0,0,0,0,0,0,0,0,1 %! 1,1,0,0,0,0,0,0,0,0,0,0,1,1 %! 1,0,0,0,0,0,0,0,0,0,0,0,0,1]); %! assert (bwmorph (!A, "endpoints"), C); image-2.16.1/inst/PaxHeaders.61586/qtgetblk.m0000644000000000000000000000006215005110255015375 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/qtgetblk.m0000644000175000017500000000732215005110255016777 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{vals}] =} qtgetblk (@var{I}, @var{S}, @var{dim}) ## @deftypefnx {Function File} {[@var{vals},@var{idx}] =} qtgetblk (@var{I}, @var{S}, @var{dim}) ## @deftypefnx {Function File} {[@var{vals},@var{r},@var{c}] =} qtgetblk (@var{I}, @var{S}, @var{dim}) ## Obtain block values from a quadtree decomposition. ## ## [vals]=qtgetblk(I,S,dim) returns a dim-by-dim-by-k array in ## @var{vals} which contains the dim-by-dim blocks in the quadtree ## decomposition (@var{S}, which is returned by qtdecomp) of @var{I}. If ## there are no blocks, an empty matrix is returned. ## ## [vals,idx]=qtgetblk(I,S,dim) returns @var{vals} as described above. ## In addition, it returns @var{idx}, a vector which contains the linear ## indices of the upper left corner of each block returned (the same ## result as find(full(S)==dim)). ## ## [vals,r,c]=qtgetblk(I,S,dim) returns @var{vals} as described, and two ## vectors, @var{r} and @var{c}, which contain the row and column ## coordinates of the blocks returned. ## ## @seealso{qtdecomp, qtsetblk} ## @end deftypefn function [varargout] = qtgetblk(I, S, dim) if (nargin != 3 || nargout > 3) print_usage; endif ## get blocks [i,j,v]=find(S); ## filter the ones which match dim idx=find(v==dim); if(length(idx)==0) for i=1:nargout varargout{i}=[]; endfor else r=i(idx); c=j(idx); ## copy to a dim-by-dim-by-k array vals=zeros(dim,dim,length(idx)); for i=1:length(idx) vals(:,:,i)=I(r(i):r(i)+dim-1,c(i):c(i)+dim-1); endfor varargout{1}=vals; if(nargout==3) varargout{2}=r; varargout{3}=c; elseif(nargout==2) varargout{2}=(c-1)*rows(I)+r; endif endif endfunction %!demo %! [vals,r,c]=qtgetblk(eye(4),qtdecomp(eye(4)),2) %! % Returns 2 blocks, at [1,3] and [3,1] (2*2 zeros blocks) %!shared A,S %! A=[ 1, 4, 2, 5,54,55,61,62; %! 3, 6, 3, 1,58,53,67,65; %! 3, 6, 3, 1,58,53,67,65; %! 3, 6, 3, 1,58,53,67,65; %! 23,42,42,42,99,99,99,99; %! 27,42,42,42,99,99,99,99; %! 23,22,26,25,99,99,99,99; %! 22,22,24,22,99,99,99,99]; %! S=qtdecomp(A,10); %!test %! [va]=qtgetblk(A,S,8); %! [vb,r,c]=qtgetblk(A,S,8); %! [vc,i]=qtgetblk(A,S,8); %! assert(va, vb); %! assert(va, vc); %! assert(i,[]); %! assert(r,[]); %! assert(c,[]); %! R=[]; %! assert(va,R); %!test %! [va]=qtgetblk(A,S,4); %! [vb,r,c]=qtgetblk(A,S,4); %! [vc,i]=qtgetblk(A,S,4); %! assert(va, vb); %! assert(va, vc); %! assert(i, find(full(S)==4)); %! assert(r,[1;5]); %! assert(c,[1;5]); %! R=zeros(4,4,2); %! R(:,:,1)=A(1:4,1:4); %! R(:,:,2)=A(5:8,5:8); %! assert(va,R); %!test %! [va]=qtgetblk(A,S,2); %! [vb,r,c]=qtgetblk(A,S,2); %! [vc,i]=qtgetblk(A,S,2); %! assert(va, vb); %! assert(va, vc); %! assert(i, find(full(S)==2)); %! assert(r,[7;5;7;1;3;1;3]); %! assert(c,[1;3;3;5;5;7;7]); %! R=zeros(2,2,7); %! R(:,:,1)=A(7:8,1:2); %! R(:,:,2)=A(5:6,3:4); %! R(:,:,3)=A(7:8,3:4); %! R(:,:,4)=A(1:2,5:6); %! R(:,:,5)=A(3:4,5:6); %! R(:,:,6)=A(1:2,7:8); %! R(:,:,7)=A(3:4,7:8); %! assert(va,R); image-2.16.1/inst/PaxHeaders.61586/imshear.m0000644000000000000000000000006215005110255015210 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imshear.m0000644000175000017500000001342515005110255016613 0ustar00avinoamavinoam00000000000000## Copyright (C) 2002 Jeff Orchard ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imshear (@var{M}, @var{axis}, @var{alpha}, @var{bbox}) ## Applies a shear to the image @var{M}. ## ## The argument @var{M} is either a matrix or an RGB image. ## ## @var{axis} is the axis along which the shear is to be applied, and can ## be either 'x' or 'y'. ## For example, to shear sideways is to shear along the 'x' axis. Choosing ## 'y' causes an up/down shearing. ## ## @var{alpha} is the slope of the shear. For an 'x' shear, it is the ## horizontal shift (in pixels) applied to the pixel above the ## center. For a 'y' shear, it is the vertical shift (in pixels) ## applied to the pixel just to the right of the center pixel. ## ## NOTE: @var{alpha} does NOT need to be an integer. ## ## @var{bbox} can be one of 'loose', 'crop' or 'wrap'. ## 'loose' allows the image to grow to accommodate the new transformed image. ## 'crop' keeps the same size as the original, clipping any part of the image ## that is moved outside the bounding box. ## 'wrap' keeps the same size as the original, but does not clip the part ## of the image that is outside the bounding box. Instead, it wraps it back ## into the image. ## ## If called with only 3 arguments, @var{bbox} is set to 'loose' by default. ## @end deftypefn function g = imshear(m, axis, alpha, bbox, noshift) if (nargin < 3 || nargin > 5) print_usage (); endif if (! isnumeric (m) || islogical (m) || ndims (m) > 3 ... || all (size (m, 3) != [1, 3])) error ("imshear: M must be a matrix or RBG image"); endif if (! any (strcmpi (axis, {'x', 'y'}))) error ("imshear: AXIS must be either 'X' or 'Y'") endif if (! isnumeric (alpha) || ! isscalar (alpha)) error ("imshear: ALPHA must be a numeric scalar"); endif # The code below only does y-shearing. This is because of # the implementation of fft (operates on columns, but not rows). # So, transpose first for x-shearing. if ( strcmp(axis, "x")==1 ) m = m'; endif if (nargin < 5) noshift = 0; if (nargin < 4) bbox = "loose"; else if (! any (strcmpi (bbox, {"loose", "crop", "wrap"}))) error ("imshear: BBOX must be either 'loose', 'crop' or 'wrap'"); endif endif else if (! isscalar (noshift) || all (noshift != [0, 1])) error ("imshear: NOSHIFT must be true of false"); endif endif [ydim_orig xdim_orig] = size(m); if ( strcmp(bbox, "wrap") == 0 ) ypad = ceil( (xdim_orig+1)/2 * abs(alpha) ); m = padarray (m, ypad); endif [ydim_new xdim_new] = size(m); xcentre = ( xdim_new + 1 ) / 2; ycentre = ( ydim_new + 1 ) / 2; # This applies FFT to columns of m (x-axis remains a spatial axis). # Because the way that fft and fftshift are implemented, the origin # will move by 1/2 pixel, depending on the polarity of the image # dimensions. # # If dim is even (=2n), then the origin of the fft below is located # at the centre of pixel (n+1). ie. if dim=16, then centre is at 9. # # If dim is odd (=2n+1), then the origin of the fft below is located # at the centre of pixel (n). ie. if dim=15, then centre is at 8. if ( noshift==1 ) M = fft(m); else #M = imtranslate(fft(imtranslate(m, -xcentre, ycentre, "wrap")), xcentre, -ycentre, "wrap"); M = fftshift(fft(fftshift(m))); endif [ydim xdim] = size(m); x = zeros(ydim, xdim); # Find coords of the origin of the image. if ( noshift==1 ) xc_coord = 1; yc_coord = 1; l = (1:ydim)' - yc_coord; r = (1:xdim) - xc_coord; if ( strcmp(bbox, "wrap")==1 ) l((ydim/2):ydim) = l((ydim/2):ydim) - ydim; r((xdim/2):xdim) = r((xdim/2):xdim) - xdim; endif else xc_coord = (xdim+1)/2; yc_coord = (ydim+1)/2; l = (1:ydim)' - yc_coord; r = (1:xdim) - xc_coord; endif x = l * r; Ms = M.* exp(2*pi*I*alpha/ydim * x); if ( noshift==1 ) g = abs(ifft(Ms)); else #g = abs(imtranslate( ifft( imtranslate(Ms, -xcentre, ycentre, "wrap") ), xcentre, -ycentre, "wrap")); g = abs( fftshift(ifft(ifftshift(Ms))) ); endif if ( strcmp(bbox, "crop")==1 ) g = g(ypad+1:ydim_orig+ypad, :); endif # Un-transpose if x-shearing was wanted if ( strcmp(axis, "x")==1 ) g = g'; endif endfunction %!error imshear () %!error imshear (1) %!error imshear (1, "x") %!error imshear (1, "x", 3, "loose", 5, 6) %!error imshear ("foo", "x", 3) %!error imshear ({1, 2, 3}, "x", 3) %!error imshear (reshape (1:24, 4, 3, 2), "x", 3) %!error imshear (reshape (1:24, 2, 3, 2, 2), "x", 3) %!error imshear (1, "Z", 3) %!error imshear (1, "x", "foo") %!error imshear (1, "x", "f") %!error imshear (1, "x", [1, 2, 3]) %!error imshear (1, "x", {3}) %!error imshear (1, "x", 3, "foo") %!error imshear (1, "x", 3, "loose", 7) %!error imshear (1, "x", 3, "loose", "foo") %!error imshear (1, "x", 3, "loose", [1 0]) image-2.16.1/inst/PaxHeaders.61586/radon.m0000644000000000000000000000006215005110255014663 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/radon.m0000644000175000017500000000665215005110255016272 0ustar00avinoamavinoam00000000000000## Copyright (C) 2007 Alexander Barth ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{RT},@var{xp}] =} radon(@var{I}, @var{theta}) ## @deftypefnx {Function File} {[@var{RT},@var{xp}] =} radon(@var{I}) ## ## Calculates the 2D-Radon transform of the matrix @var{I} at angles given in ## @var{theta}. To each element of @var{theta} corresponds a column in @var{RT}. ## The variable @var{xp} represents the x-axis of the rotated coordinate. ## If @var{theta} is not defined, then 0:179 is assumed. ## @end deftypefn function [RT,xp] = radon (I,theta) ## Input checking if (nargin == 0 || nargin > 2) print_usage (); elseif (nargin == 1) theta = 0:179; endif if (! isimage (I) || ndims (I) != 2) error ("radon: I must be a MxN numeric matrix"); endif if (! isvector (theta)) error ("radon: second input must be a vector"); endif if (! isnumeric (theta)) error ("radon: second input must be a numeric vector"); endif if (! isa (I, 'double')) I = double (I); endif [m, n] = size (I); # center of image xc = floor ((m+1)/2); yc = floor ((n+1)/2); # divide each pixel into 2x2 subpixels d = reshape (I,[1 m 1 n]); d = d([1 1],:,[1 1],:); d = reshape (d,[2*m 2*n])/4; b = ceil (sqrt (sum (size (I).^2))/2 + 1); xp = [-b:b]'; sz = size(xp); [X,Y] = ndgrid (0.75 - xc + [0:2*m-1]/2,0.75 - yc + [0:2*n-1]/2); X = X(:)'; Y = Y(:)'; d = d(:)'; th = theta*pi/180; for l=1:length (theta) # project each pixel to vector (-sin(th), cos(th)) Xp = -sin (th(l)) * X + cos (th(l)) * Y; ip = Xp + b + 1; k = floor (ip); frac = ip-k; RT(:,l) = accumarray (k',d .* (1-frac),sz) + accumarray (k'+1,d .* frac,sz); endfor endfunction %!test %! A = radon (ones (2,2), 30); %! assert (A, [0 0 0.608253175473055 2.103325780167649 1.236538105676658 0.051882938682637 0]',1e-10) %!test %!# testing all types %! A = radon (single (ones (2,2)), 90); %! assert (A, B) %! A = radon (double (ones (2,2)), 90); %! assert (A, B) %! A = radon (int8 (ones (2,2)), 90); %! assert (A, B) %! A = radon (int32 (ones (2,2)), 90); %! assert (A, B) %! A = radon (int64 (ones (2,2)), 90); %! assert (A, B) %! A = radon (uint8 (ones (2,2)), 90); %! assert (A, B) %! A = radon (uint16 (ones (2,2)), 90); %! assert (A, B) %! A = radon (uint32 (ones (2,2)), 90); %! assert (A, B) %! A = radon (uint64 (ones (2,2)), 90); %! B = [0, 0.25, 1.75, 1.75, 0.25, 0. 0.]'; %! assert (A, B) %! bug #58567 %! A = radon (logical (ones (2,2)), 90); %! assert (A, B) %!error %! radon (); %!error %! radon ('xxx'); %!error %! radon (ones (2, 2), ones (2,2)); %!error %! radon (ones (2, 2), 'xxx'); image-2.16.1/inst/PaxHeaders.61586/imadd.m0000644000000000000000000000006215005110255014636 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imadd.m0000644000175000017500000000715115005110255016240 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{out} =} imadd (@var{a}, @var{b}) ## @deftypefnx {Function File} {@var{out} =} imadd (@var{a}, @var{b}, @var{class}) ## Add image or constant to an image. ## ## If @var{a} and @var{b} are two images of same size and class, the images are ## added. Alternatively, if @var{b} is a floating-point scalar, its value is added ## to the image @var{a}. ## ## The class of @var{out} will be the same as @var{a} unless @var{a} is logical ## in which case @var{out} will be double. Alternatively, it can be ## specified with @var{class}. ## ## @emph{Note 1}: you can force output class to be logical by specifying ## @var{class}. This is incompatible with @sc{matlab} which will @emph{not} honour ## request to return a logical matrix. ## ## @emph{Note 2}: the values are truncated to the maximum value of the output ## class. ## @seealso{imabsdiff, imcomplement, imdivide, imlincomb, immultiply, imsubtract} ## @end deftypefn function img = imadd (img, val, out_class = class (img)) if (nargin < 2 || nargin > 3) print_usage; endif [img, val] = imarithmetics ("imadd", img, val, out_class); ## output class is the same as input img, unless img is logical in which case ## it should be double. Tested in matlab by Freysh at ##matlab: ## - if you imadd 2 logical matrix, it's not the union. You actually get values of 2 ## - the previous is true even if you specify "logical" as output class. It does ## not honors the request, output will be double class anyway, not even a ## warning will be issued (but we in octave are nicer and will) ## - you can specify smaller integer types for output than input and values ## are truncated. Input uint16 and request uint8, it will be respected ## this is matlab imcompatible on purpose. We are compatible and return double ## anyway, even if both input are logical (and wether this is correct is ## already debatable), but if the user forcedly requests output class to be ## logical, then he must be expecting it (matlab returns double anyway and ## ignores request). if (nargin > 2 && strcmpi (out_class, "logical")) img = img | val; else img = img + val; endif endfunction %!assert (imadd (uint8 ([23 250]), uint8 ([23 250])), uint8 ([46 255])); # default to first class and truncate %!assert (imadd (uint8 ([23 250]), 10), uint8 ([33 255])); # works adding a scalar %!assert (imadd (uint8 ([23 250]), uint8 ([23 250]), "uint16"), uint16 ([46 500])); # defining output class works %!assert (imadd (logical ([ 1 0]), logical ([ 1 1])), double ([ 2 1])); # return double for two logical images %!assert (imadd (logical ([ 1 0]), logical ([ 1 1]), "logical"), logical ([ 1 1])); # this is matlab incompatible on purpose %!fail ("imadd (uint8 ([23 250]), uint16 ([23 250]))"); # input need to have same class image-2.16.1/inst/PaxHeaders.61586/lab2xyz.m0000644000000000000000000000006215005110255015153 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/lab2xyz.m0000644000175000017500000001247015005110255016555 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{xyz} =} lab2xyz (@var{lab}) ## @deftypefnx {Function File} {@var{xyz_map} =} lab2xyz (@var{lab_map}) ## Transform a colormap or image from CIE L*a*b* to CIE XYZ color space. ## ## A color in the CIE L*a*b* (or CIE Lab) space consists of lightness L* and ## two color-opponent dimensions a* and b*. The whitepoint is taken as D65. ## The CIE L*a*b* colorspace is a colorimetric colorspace, meaning that their values ## do not depend on the display device hardware. This colorspace is designed ## to incorporate the human perception of color differences. ## ## A color in the CIE XYZ color space consists of three values X, Y and Z. ## Those values are also designed to be colorimetric. ## ## Input values of class single and double are accepted. ## The shape and the class of the input are conserved. ## ## The input values of L* are normally in the inteval [0, 100] ## and the values of a* and b* in the interval [-127, 127]. ## ## @seealso{xyz2lab, rgb2lab, rgb2hsv, rgb2ind, rgb2ntsc} ## @end deftypefn ## Author: Hartmut Gimpel ## algorithm taken from the following book: ## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015) function xyz = lab2xyz (lab) if (nargin != 1) print_usage (); endif [lab, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check ("lab2xyz", "Lab", lab, 1); # currently only accept single and double inputs (as Matlab does) # (Integer types would be possible, but would need an explanation in the # help text how to scale them.) ## use the whitepoint D65 (reference: en.wikipedia.org/wiki/Illuminant_D65) D65 = [0.95047, 1, 1.08883]; # Matlab truncates to D65_Matlab = [0.9504, 1.0000, 1.0888]; ## transformation Lab -> XYZ L = lab(:,1); a = lab(:,2); b = lab(:,3); L_prime = (L + 16) ./ 116; x = D65(1) .* f (L_prime + a./500); y = D65(2) .* f (L_prime); z = D65(3) .* f (L_prime - b./200); xyz = [x, y, z]; # always return values of type double for Matlab compatibility (exception: type single) xyz = colorspace_conversion_revert (xyz, cls, sz, is_im, is_nd, is_int, 1); endfunction function out = f (in) epsilon = (6/29)^3; kappa = 1/116 * (29/3)^3; out = in; mask = in.^3 > epsilon; out(mask) = in(mask).^3; out(! mask) = (in(! mask) - 16/116)./kappa; endfunction ## Test pure colors, gray and some other colors ## (This set of test values is taken from the book by Burger.) %!assert (lab2xyz ([0, 0, 0]), [0 0 0], 1e-3) %!assert (lab2xyz ([53.24, 80.09, 67.20]), [0.4125, 0.2127, 0.0193], 1e-3) %!assert (lab2xyz ([97.14, -21.55, 94.48]), [0.7700, 0.9278, 0.1385], 1e-3) %!assert (lab2xyz ([87.74, -86.18, 83.18]), [0.3576, 0.7152, 0.1192], 1e-3) %!assert (lab2xyz ([91.11, -48.09, -14.13]), [0.5380, 0.7873, 1.0694], 1e-3) %!assert (lab2xyz ([32.30, 79.19, -107.86]), [0.1804, 0.07217, 0.9502], 1e-3) %!assert (lab2xyz ([60.32, 98.24, -60.83]), [0.5929, 0.28484, 0.9696], 1e-3) %!assert (lab2xyz ([100, 0.00, 0.00]), [0.9505, 1.0000, 1.0888], 1e-3) %!assert (lab2xyz ([53.39, 0.00, 0.00]), [0.2034, 0.2140, 0.2330], 1e-3) %!assert (lab2xyz ([39.77, 64.51, 54.13]), [0.2155, 0.1111, 0.0101], 1e-3) %!assert (lab2xyz ([25.42, 47.91, 37.91]), [0.0883, 0.0455, 0.0041], 1e-3) %!assert (lab2xyz ([9.66, 29.68, 15.24]), [0.02094, 0.0108, 0.00098], 1e-3) %!assert (lab2xyz ([68.11, 48.39, 22.83]), [0.5276, 0.3812, 0.2482], 1e-3) ## Test tolarant input checking on floats %!assert (lab2xyz ([150 130 130]), [4.596, 2.931, 0.519], 1e-3) %!test %! lab_map = rand (64, 3); %! lab_map(:,1) = lab_map(:,1) .* 100; %! lab_map(:,2) = lab_map(:,2) .* 254 - 127; %! lab_map(:,3) = lab_map(:,3) .* 254 - 127; %! assert (xyz2lab (lab2xyz (lab_map)), lab_map, 1e-5); %!test %! lab_img = rand (64, 64, 3); %! lab_img(:,:,1) = lab_img(:,:,1) .* 100; %! lab_img(:,:,2) = lab_img(:,:,2) .* 254 - 127; %! lab_img(:,:,3) = lab_img(:,:,3) .* 254 - 127; %! assert (xyz2lab (lab2xyz (lab_img)), lab_img, 1e-5); ## support sparse input %!assert (lab2xyz (sparse ([0 0 0])), [0 0 0], 1e-3) %!assert (lab2xyz (sparse ([100, 0.00, 0.00])), [0.9505, 1.0000, 1.0888], 1e-3) ## conserve class of single input %!assert (class (lab2xyz (single([50 50 50]))), 'single') ## Test input validation %!error lab2xyz () %!error lab2xyz (1,2) %!error lab2xyz ({1}) %!error lab2xyz (ones (2,2)) ## Test ND input %!test %! lab = rand (16, 16, 3, 5); %! lab(:,:,1,:) = lab(:,:,1,:) .* 100; %! lab(:,:,2,:) = lab(:,:,2,:) .* 254 - 127; %! lab(:,:,3,:) = lab(:,:,3,:) .* 254 - 127; %! xyz = zeros (size (lab)); %! for i = 1:5 %! xyz(:,:,:,i) = lab2xyz (lab(:,:,:,i)); %! endfor %! assert (lab2xyz (lab), xyz) image-2.16.1/inst/PaxHeaders.61586/imlincomb.m0000644000000000000000000000006215005110255015531 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imlincomb.m0000644000175000017500000001147515005110255017137 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{out} =} imlincomb (@var{fac}, @var{img}) ## @deftypefnx {Function File} {@var{out} =} imlincomb (@var{fac1}, @var{img1}, @var{fac2}, @var{img2}, @dots{}) ## @deftypefnx {Function File} {@var{out} =} imlincomb (@var{fac1}, @var{img1}, @var{fac2}, @var{img2}, @dots{}, @var{K}) ## @deftypefnx {Function File} {@var{out} =} imlincomb (@dots{}, @var{class}) ## Combine images linearly. ## ## Returns the computed image as per: ## ## @var{out} = @var{fac}1*@var{img}1 + @var{fac}2*@var{img}2 + @dots{} + @var{fac}n*@var{img}n ## ## or ## ## @var{out} = @var{fac}1*@var{img}1 + @var{fac}2*@var{img}2 + @dots{} + @var{fac}n*@var{img}n + @var{K} ## ## The images @var{img}1..n must all be of same size and class. The factors @var{fac}1..n ## must all be floating-point scalars, and @var{K} (if given) is a real constant. ## ## The class of @var{out} will be the same as @var{img}s unless @var{img}s are logical ## in which case @var{out} will be double. Alternatively, it can be specified ## with @var{class}. ## ## If applying several arithmetic operations on images, @code{imlincomb} is more ## precise since calculations are performed at double precision. ## ## @emph{Note 1}: you can force output class to be logical by specifying ## @var{class} though it possibly doesn't make a lot of sense. ## ## @seealso{imadd, imcomplement, imdivide, immultiply, imsubtract} ## @end deftypefn function out = imlincomb (varargin) if (nargin < 2) print_usage; endif if (isnumeric (varargin{end})) ## last argument is not a class ## use default for output class; the class of first image (second argument) out_class = class (varargin{2}); def_class = true; last = nargin; else ## last argument is requested output class out_class = varargin{end}; def_class = false; last = nargin-1; endif if (rem (last, 2) == 1) # There's a constant at the end to add add = varargin{last}; last--; else add = 0; endif facI = 1:2:last-1; # index for factors imgI = 2:2:last; # index for images imgC = class (varargin{2}); # class of the first image out = zeros (size (varargin{2})); for i = 1:numel (imgI) ## we keep index the images from varargin rather than copying to new variable to ## avoid taking up a lot of memory if (!isreal (varargin{facI(i)}) || !isscalar (varargin{facI(i)}) || !isfloat (varargin{facI(i)})) error ("factor to multiply each image must be a real, floating-point, scalar."); elseif ((!isnumeric (varargin{imgI(i)}) && !islogical (varargin{imgI(i)})) ... || isempty (varargin{imgI(i)}) || issparse (varargin{imgI(i)}) ... || !isreal (varargin{imgI(i)}) || !isa (varargin{imgI(i)}, imgC)) error ("images must be a numeric or logical, non-empty, non-sparse real matrix of the same class."); endif img = double(varargin{imgI(i)}) .* double(varargin{facI(i)}); out += img; endfor ## Adding the constant if (add != 0) out += add; endif ## this is probably matlab imcompatible since by their documentation, they don't even ## support logical matrix. If specified by user, respect and return a logical ## matrix. Otherwise, return a double, even if images were all logical if (strcmpi (out_class, "logical") && def_class) cnv = @double; else cnv = str2func (tolower (out_class)); endif out = cnv (out); endfunction %!assert (imlincomb (0.5, uint8 ([255 10]), 0.5, uint8 ([50 20])), %! uint8 ([153 15])); # default to first class and truncate %!assert (imlincomb (0.5, uint8 ([255 10]), 0.5, uint8 ([50 20]), "uint16"), %! uint16 ([153 15])); # defining output class works ## Now with a constant value applied to the whole image %!assert (imlincomb (0.5, uint8 ([255 10]), 0.5, uint8 ([50 20]), 10), %! uint8 ([163 25])); # default to first class and truncate %!assert (imlincomb (0.5, uint8 ([255 10]), 0.5, uint8 ([50 20]), 1000, "uint16"), %! uint16 ([1153 1015])); # defining output class works %!assert (imlincomb (0.5, uint8 ([255 10]), 0.5, uint8 ([50 20]), 1000), %! uint8 ([255 255])); # defining output class works image-2.16.1/inst/PaxHeaders.61586/immse.m0000644000000000000000000000006215005110255014672 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/immse.m0000644000175000017500000000354415005110255016276 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} immse (@var{x}, @var{y}) ## Compute mean squared error. ## ## Calculates the mean squared error (MSE), between the arrays @var{x} and ## @var{y}. @var{x} and @var{y} must be of same size and class. ## ## The returned value will be a scalar double, unless @var{x} and @var{y} ## are of class single in which case, it returns a scalar single. ## ## @seealso{psnr} ## @end deftypefn function [mse] = immse (x, y) if (nargin != 2) print_usage (); elseif (! size_equal (x, y)) error ("immse: X and Y must be of same size"); elseif (! strcmp (class (x), class (y))) error ("immse: X and Y must have same class"); end if (isinteger (x)) x = double (x); y = double (y); endif err = x - y; mse = sumsq (err(:)) / numel (err); endfunction %!error immse (rand (10), rand (12)) %!error immse (uint8 ([0 1 2 3]), uint16 ([0 1 2 3])) %!error immse (double ([0 1 2 3]), single ([0 1 2 3])) %!assert (immse (magic (5), magic (5)), 0) %!assert (immse (single (magic (5)), single (magic (5))), single (0)) %!assert (immse (uint8 (magic (5)), uint8 (magic (5))), 0) image-2.16.1/inst/PaxHeaders.61586/phantom.m0000644000000000000000000000006215005110255015226 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/phantom.m0000644000175000017500000002041415005110255016625 0ustar00avinoamavinoam00000000000000## Copyright (C) 2010 Alex Opie ## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{P}} = phantom () ## @deftypefnx {Function File} {@var{P}} = phantom (@var{model}) ## @deftypefnx {Function File} {@var{P}} = phantom (@var{E}) ## @deftypefnx {Function File} {@var{P}} = phantom (@dots{}, @var{n}) ## @deftypefnx {Function File} {[@var{P}, @var{E}]} = phantom (@dots{}) ## Create computational phantom head. ## ## A phantom is a known object (either real or purely mathematical) that is ## used for testing image reconstruction algorithms. The Shepp-Logan phantom ## is a popular mathematical model of a cranial slice, made up of a set of ## overlaying ellipses. This allows rigorous testing of computed tomography ## (CT) algorithms as it can be analytically transformed with the radon ## transform (see the functions @code{radon} and @code{iradon}). ## ## The phantom @var{P}, is created by overlaying ellipses as defined by the ## matrix @var{E} or one of the standard @var{model}s, in a square of size ## @var{n} by @var{n} (defaults to 256). ## ## The available standard @var{model}s (use the output argument @var{E} to ## inspect the details of the different ellipses) are: ## ## @table @asis ## @item @qcode{"Shepp-Logan"} ## This is the original Shepp-Logan model with 10 ellipses as described in ## Table 1 of @cite{Shepp, Lawrence A., and Benjamin F. Logan. "The Fourier ## reconstruction of a head section." Nuclear Science, IEEE Transactions on ## 21, no. 3 (1974): 21-43.} ## ## @item @qcode{"Modified Shepp-Logan"} (default) ## A modification of the original Shepp-Logan model to give a better contrast, ## as described in Table B.3 of @cite{Toft, Peter Aundal. "The radon ## transform-theory and implementation." PhD diss., Department of Mathematical ## Modelling, Technical University of Denmark, 1996.} ## ## @end table ## ## A 6 column matrix @var{E} can be used to generate a custom image by ## superimposing arbitrary ellipses. Each row defines a single ellipse, with ## each column for the values of @{I, a, b, x0, y0, phi@}: ## ## @table @abbr ## @item I ## is the additive intensity of the ellipse ## ## @item a ## is the length of the major axis ## ## @item b ## is the length of the minor axis ## ## @item x0 ## is the horizontal offset of the centre of the ellipse ## ## @item y0 ## is the vertical offset of the centre of the ellipse ## ## @item phi ## is the counterclockwise rotation of the ellipse in degrees, ## measured as the angle between the x axis and the ellipse major axis. ## ## @end table ## ## The image bounding box in the algorithm is @{[-1, -1], [1, 1]@}, so the ## values of a, b, x0, y0 should all be specified with this in mind. ## ## Example: ## ## @example ## @group ## P = phantom (512); ## imshow (P); ## @end group ## @end example ## ## @seealso{iradon, radon} ## @end deftypefn function [head, ellipses] = phantom (varargin) if (nargin > 2) print_usage () endif ## Would be really cool if we implemented a 3D phantom as already described ## in Cheng Guan Koay, Joelle E. Sarlls, and Evren Ozarslan (2007). ## "Three-Dimensional Analytical Magnetic Resonance Imaging Phantom in the ## Fourier Domain". Magnetic Resonance in Medicine 58:430 - 436. ## The Table 1 on their paper to generate the 3D model, would take 8 columns, ## an extra value for z axis coordinates, and extra axis length. ## They mention other phantom heads as more canonical 3D head phantoms (read ## the introduction) ## Defaults ellipses = mod_shepp_logan (); n = 256; if (nargin) ## Check validity of N chk_n = @(x) isnumeric (x) && isscalar (x) && ceil (x) == x; in = varargin{1}; if (ischar (in)) switch (tolower (in)) case "shepp-logan", ellipses = shepp_logan (); case "modified shepp-logan", ellipses = mod_shepp_logan (); otherwise error ("phantom: unknown MODEL `%s'", in); endswitch elseif (isnumeric (in) && ndims (in) == 2 && columns (in) == 6) ellipses = in; elseif (chk_n (in)) n = in; ## If N is the first argument, we can't have more if (nargin > 1) print_usage (); endif else error ("phantom: first argument must either be MODEL, E, or N"); endif ## If there is a second input argument, must be N if (nargin > 1) if (chk_n (varargin{2})) n = varargin{2}; else error ("phantom: N must be numeric scalar"); endif endif endif ## Initialize blank image head = zeros (n); # Create the pixel grid xvals = (-1 : 2 / (n - 1) : 1); xgrid = repmat (xvals, n, 1); for i = 1:rows (ellipses) I = ellipses (i, 1); a2 = ellipses (i, 2)^2; b2 = ellipses (i, 3)^2; x0 = ellipses (i, 4); y0 = ellipses (i, 5); phi = ellipses (i, 6) * pi / 180; # Rotation angle in radians ## Create the offset x and y values for the grid x = xgrid - x0; y = rot90 (xgrid) - y0; cos_p = cos (phi); sin_p = sin (phi); ## Find the pixels within the ellipse locs = find (((x .* cos_p + y .* sin_p).^2) ./ a2 ... + ((y .* cos_p - x .* sin_p).^2) ./ b2 <= 1); ## Add the ellipse intensity to those pixels head(locs) += I; endfor endfunction function ellipses = shepp_logan () ## Standard head phantom, taken from Shepp & Logan ## ## Note that the first element of this matrix, the gray value for the first ## ellipse (human skull), has a value of 1.0 even though the paper gives it a ## a value of 2.0 (see Table 1 on page 32 and Figure 1 on page 34). This ## change is so that the **head** intensity values appear in the range [0 1] ## rather than the range [1 2]. ## ## **The problem with this** ## ## The background still need an intensity value which is going to be 0. This ## means that we can't distinguish between the background and the ventricles ## (ellipse "c" and "d" whose intensities are a + b + c and a + b + d, see ## Figure 1) since they will have an intensity value of 0 (actually, because ## of machine precision the ventricules will be almost 0). But if we didn't ## made this change, the ** image** range would be [0 2] with all of the head ## details compressed in half of the display range. Also, Matlab seems to be ## doing the same. ellipses = [ 1 0.69 0.92 0 0 0 -0.98 0.6624 0.874 0 -0.0184 0 -0.02 0.11 0.31 0.22 0 -18 -0.02 0.16 0.41 -0.22 0 18 0.01 0.21 0.25 0 0.35 0 0.01 0.046 0.046 0 0.1 0 0.01 0.046 0.046 0 -0.1 0 0.01 0.046 0.023 -0.08 -0.605 0 0.01 0.023 0.023 0 -0.606 0 0.01 0.023 0.046 0.06 -0.605 0]; endfunction function ellipses = mod_shepp_logan () ## Modified version of Shepp & Logan's head phantom, adjusted to improve ## contrast. Taken from Peter Toft PhD thesis, Table B.3 ellipses = [ 1.0 0.69 0.92 0.0 0.0 0 -0.8 0.6624 0.874 0.0 -0.0184 0 -0.2 0.11 0.31 0.22 0.0 -18 -0.2 0.16 0.41 -0.22 0.0 18 0.1 0.21 0.25 0.0 0.35 0 0.1 0.046 0.046 0.0 0.1 0 0.1 0.046 0.046 0.0 -0.1 0 0.1 0.046 0.023 -0.08 -0.605 0 0.1 0.023 0.023 0.0 -0.606 0 0.1 0.023 0.046 0.06 -0.605 0]; endfunction %!demo %! P = phantom (512); %! imshow (P); image-2.16.1/inst/PaxHeaders.61586/imcrop.m0000644000000000000000000000006215005110255015051 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imcrop.m0000644000175000017500000002263015005110255016452 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014-2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imcrop () ## @deftypefnx {Function File} {} imcrop (@var{img}) ## @deftypefnx {Function File} {} imcrop (@var{ind}, @var{cmap}) ## @deftypefnx {Function File} {} imcrop (@var{h}) ## @deftypefnx {Function File} {} imcrop (@dots{}, @var{rect}) ## @deftypefnx {Function File} {[@var{cropped}] =} imcrop (@dots{}) ## @deftypefnx {Function File} {[@var{cropped}, @var{rect}] =} imcrop (@dots{}) ## @deftypefnx {Function File} {[@var{x}, @var{y}, @var{cropped}, @var{rect}] =} imcrop (@dots{}) ## Crop image. ## ## Displays the image @var{img} in a figure window and waits for the user to ## select two points defining a bounding box. For an indexed image, a ## corresponding colormap can be specified in @var{cmap}. For multi-dimensional ## images (each 2D image is concatenated in the 4th dimension), only the ## first image is displayed. ## ## If no image data is given, uses the current figure or figure from graphics ## handle @var{h}. ## ## Non-interactive usage is supported if the last input argument is 4 element ## vector @var{rect} defining the region of interest. The first two elements ## specify the initial @var{x_ini} and @var{y_ini} coordinates, and the last ## two the @var{width} and @var{height}, i.e., ## @code{@var{rect} = [@var{x_ini} @var{y_ini} @var{width} @var{height}]}. ## Note how this the opposite of the majority of Octave indexing rules where ## rows come before columns. ## ## Returns the @var{cropped} image and a vector @var{rect} with the ## coordinates and size for @var{cropped}. If more than 3 output arguments ## are requested, also returns the @var{x} and @var{y} data that define ## the coordinate system. ## ## @emph{Note}: the values in @var{rect} are not necessarily integer values ## and can't always be used directly as index values for other images. To ## crop the same region from a multiple images of the same size, either using ## a multi-dimensional image: ## ## @example ## @group ## nd_img = cat (4, img1, img2, img3, img4); ## cropped = imcrop (nd_img); ## cropped_1 = cropped(:,:,:,1); ## cropped_2 = cropped(:,:,:,2); ## cropped_3 = cropped(:,:,:,3); ## cropped_4 = cropped(:,:,:,4); ## @end group ## @end example ## ## or multiple calls to @code{imcrop}: ## ## @example ## @group ## [cropped_1, rect] = imcrop (img1); ## cropped_2 = imcrop (img2, rect); ## cropped_3 = imcrop (img3, rect); ## cropped_4 = imcrop (img4, rect); ## @end group ## @end example ## ## @seealso{impixel, imshow} ## @end deftypefn ## TODO not yet implemented ## @deftypefnx {Function File} {} imcrop (@var{xData}, @var{yData}, @dots{}) function varargout = imcrop (varargin) ## Screw Matlab and their over complicated API's! How can we properly ## parse all the possible alternative calls? See ## http://www.youtube.com/watch?v=1oZWacjmYm8 to understand how such ## API's develop. ## There is no check for this things, anything is valid. We (Octave) ## are at least checking the number of elements otherwise the input ## parsing would be horrible. valid_rect = @(x) numel (x) == 4; valid_system = @(x) numel (x) == 2; rect = []; interactive = true; # is interactive usage alt_system = false; # was an alternative coordinate system requested? from_fig = false; # do we have the image data or need to fetch from figure? if (nargin > 5) print_usage (); endif rect = []; if (numel (varargin) > 1 && valid_rect (varargin{end})) interactive = false; rect = varargin{end}; varargin(end) = []; endif xdata = []; ydata = []; if (numel (varargin) > 2 && valid_system (varargin{1}) && valid_system (varargin{2})) ## requested messy stuff ## we should probably reuse part of what impixel does alt_system = true; xdata = varargin{1}; ydata = varargin{2}; varargin([1 2]) = []; error ("imcrop: messing around with coordinate system is not implemented"); endif ## After we remove all that extra stuff, we are left with the image fnargin = numel (varargin); if (fnargin > 2) print_usage (); elseif (fnargin == 0) ## use current figure from_fig = true; h = gcf (); ## We check isscalar() because ishandle() accepts arrays of handles, and we ## check "!= 0" because 0 is and handle for the "root figure" which is ## invalid for imcrop (see bug #42714). elseif (fnargin == 1 && isscalar (varargin{1}) && varargin{1} != 0 && ishandle (varargin{1})) ## use specified figure from_fig = true; h = varargin{1}; elseif (interactive) ## leave input check to imshow h = nd_imshow (varargin{:}); elseif (isimage (varargin{1})) ## we have the image data and it's not interactive, so there is ## nothing to do. We only check the minimum in the image. else print_usage (); endif if (from_fig) hax = get (h, "currentaxes"); himage = findobj (hax, "type", "image"); if (! isempty (himage)) himage = himage(1); else error ("imcrop: expect the current axes to contain an image") endif cdata = get (himage, "cdata"); xdata = get (himage, "xdata"); ydata = get (himage, "ydata"); else cdata = varargin{1}; if (! alt_system) xdata = [1 columns(cdata)]; ydata = [1 rows(cdata)]; endif endif ## Finally, crop the image if (interactive) [x, y] = ginput (2); if (x(2) < x(1)) [x(1), x(2)] = deal (x(2), x(1)); endif if (y(2) < y(1)) [y(1), y(2)] = deal (y(2), y(1)); endif rect = [x(1) y(1) x(2)-x(1) y(2)-y(1)]; endif i_ini = max (round ([rect(1) rect(2)]), [1 1]); i_end = min (round ([rect(1)+rect(3) rect(2)+rect(4)]), size (cdata)([2 1])); img = cdata(i_ini(2):i_end(2), i_ini(1):i_end(1),:,:); # don't forget RGB and ND images ## Even the API for the output is complicated if (nargout == 0 && interactive) figure (); ## In case we have a colormap or something like that, use ## it again when displaying the cropped image. nd_imshow (img, varargin{2:end}); elseif (nargout < 3) varargout{1} = img; varargout{2} = rect; else varargout{1} = xdata; varargout{2} = ydata; varargout{3} = img; varargout{4} = rect; endif endfunction ## shadows core function to support ND image. If we have one, use ## the first frame only function h = nd_imshow (varargin) size (varargin{1}); h = imshow (varargin{1}(:,:,:,1), varargin{2:end}); endfunction ## test typical non-interactive usage, grayscale image %!test %! a = randi (255, [100 100]); %! rect = [20 30 3 5]; %! assert (nthargout ([1 2], @imcrop, a, rect), {a(30:35, 20:23) rect}); %! assert (nthargout (2, @imcrop, a, rect), rect); %! assert (nthargout ([3 4], 4, @imcrop, a, rect), {a(30:35, 20:23) rect}); ## test typical non-interactive usage, RGB image %!test %! rgb = randi (255, [100 100 3]); %! rect = [20 30 3 5]; %! assert (nthargout ([1 2], @imcrop, rgb, rect), {rgb(30:35, 20:23,:) rect}); %! assert (nthargout (2, @imcrop, rgb, rect), rect); %! assert (nthargout ([3 4], 4, @imcrop, rgb, rect), {rgb(30:35, 20:23,:) rect}); ## test typical non-interactive usage, indexed image %!test %! a = randi (255, [100 100]); %! rect = [20 30 3 5]; %! cmap = jet (255); %! assert (nthargout ([1 2], @imcrop, a, cmap, rect), {a(30:35, 20:23) rect}); %! assert (nthargout (2, @imcrop, a, cmap, rect), rect); %! assert (nthargout ([3 4], 4, @imcrop, a, cmap, rect), {a(30:35, 20:23) rect}); ## test typical non-interactive usage, logical image %!test %! a = rand (100) > 0.5; %! rect = [20 30 3 5]; %! assert (nthargout ([1 2], @imcrop, a, rect), {a(30:35, 20:23) rect}); %! assert (nthargout (2, @imcrop, a, rect), rect); %! assert (nthargout ([3 4], 4, @imcrop, a, rect), {a(30:35, 20:23) rect}); ## 0 is the root figure (always true figure handle), so make sure we use ## scalar 0 as image data, not as figure handle. %!assert (imcrop (0, [0.5 0.5 0.9 0.9]), 0); %!assert (imcrop (zeros (5), [1 1 1 1]), zeros (2)); ## test out of bounds region of interest to crop (bug #49456) %!test %! im = magic (5); %! assert (imcrop (im, [1 1 5 5]), im) %! assert (imcrop (im, [0 0 5 5]), im) %! assert (imcrop (im, [1 1 2 5]), im(:,1:3)) %! assert (imcrop (im, [1 -3 2 5]), im(1:2,1:3)) %! assert (imcrop (im, [5 -3 2 5]), im(1:2,5)) ## out of bounds ROIs with non-square images (bug #54370) %!test %! im = [1:7] .* [1; 2; 3; 4; 5]; %! assert (imcrop (im, [1 1 5 5]), im(:,1:6)) %! assert (imcrop (im, [0 0 5 5]), im(:,1:5)) %! assert (imcrop (im, [1 1 2 5]), im(:,1:3)) %! assert (imcrop (im, [1 -3 2 7]), im(1:4,1:3)) %! assert (imcrop (im, [7 -3 2 7]), im(1:4,7)) %!test %! ## Matlab returns [] (size 0x0) for this cases, while we return %! ## [] (size 2x0). We are not compatible by design. If it ever %! ## becomes an issue to anyone we can review this decision. %! assert (imcrop (magic (5), [6 -3 2 5]), zeros (2, 0)) image-2.16.1/inst/PaxHeaders.61586/subimage.m0000644000000000000000000000006215005110255015354 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/subimage.m0000644000175000017500000000507015005110255016754 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} subimage (@var{bw}) ## @deftypefnx {Function File} {} subimage (@var{img}) ## @deftypefnx {Function File} {} subimage (@var{rgb}) ## @deftypefnx {Function File} {} subimage (@var{ind}, @var{cmap}) ## @deftypefnx {Function File} {} subimage (@var{x}, @var{y}, @dots{}) ## @deftypefnx {Function File} {@var{h} =} subimage (@dots{}) ## Display images in subplots. ## ## A single figure, even with multiple subplots, is limited to a single ## colormap. With the exception of truecolor images, images will use the ## figure colormap which make it impossible to have multiple images with ## different display. This function transforms any image in truecolor ## to workaround this limitation. ## ## The new subimage is displayed as if using @code{image}. The optional ## arguments @var{x} and @var{y} are passed to @code{image} to specify the ## range of the axis labels. ## ## @seealso{image, imagesc, imshow, subplot} ## @end deftypefn function h = subimage (varargin) if (nargin < 1 || nargin > 4) print_usage (); endif if (nargin < 3) alternative_xy = false; im_ind = 1; else alternative_xy = true; im_ind = 3; if (numel (varargin{1}) == 2 && numel (varargin{2}) == 2) x = varargin{1}; y = varargin{2}; else error ("subimage: X and Y must be two element vectors each"); endif endif im = varargin{im_ind}; if (numel (varargin) > im_ind) rgb = ind2rgb (im, varargin{im_ind +1}); elseif (isbw (im)) rgb = repmat (im2uint8 (im), [1 1 3 1]); elseif (isgray (im)) rgb = repmat (im, [1 1 3 1]); elseif (isrgb (im)) rgb = im; else error ("subimage: no valid BW, IMG, IND, or RGB images in input arguments"); endif if (alternative_xy) tmp_h = image (x, y, rgb); else tmp_h = image (rgb); endif if (nargout > 0) h = tmp_h; endif endfunction image-2.16.1/inst/PaxHeaders.61586/getrangefromclass.m0000644000000000000000000000006215005110255017266 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/getrangefromclass.m0000644000175000017500000000530515005110255020667 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011-2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{range} =} getrangefromclass (@var{img}) ## Return display range of image. ## ## For a given image @var{img}, returns the 1x2 element matrix @var{range} ## with the display range (minimum and maximum display values) for an ## image of that class. ## ## Images of different classes have different display ranges, the ranges ## of values that Octave will interpret between black to white. For an ## integer image, the range is from @code{intmin} to @code{intmax} of ## that class; for images of class logical, single, or double, the range ## is [0 1]. ## ## Note that @var{range} will be of class double, independently of the class ## of @var{img}. ## ## @example ## @group ## getrangefromclass (ones (5)) # note that class is 'double' ## @result{} [0 1] ## getrangefromclass (logical (ones (5))) ## @result{} [0 1] ## getrangefromclass (int8 (ones (5))) ## @result{} [-128 127] ## @end group ## @end example ## ## @seealso{intmin, intmax, bitmax} ## @end deftypefn function r = getrangefromclass (img) if (nargin != 1) print_usage (); elseif (! isimage (img)) error ("getrangefromclass: IMG must be an image"); endif cl = class (img); if (isinteger (img)) r = [intmin(cl) intmax(cl)]; elseif (any (strcmp (cl, {"single", "double", "logical"}))) r = [0 1]; else error ("getrangefromclass: unrecognized image class `%s'", cl) endif r = double (r); endfunction %!shared img %! img = ones (5); %!assert (getrangefromclass (double (img)), [0 1]); # double returns [0 1] %!assert (getrangefromclass (single (img)), [0 1]); # single returns [0 1] %!assert (getrangefromclass (logical (img)), [0 1]); # logical returns [0 1] %!assert (getrangefromclass (int8 (img)), [-128 127]); # checks int %!assert (getrangefromclass (uint8 (img)), [0 255]); # checks unit %!fail ("getrangefromclass ('string')"); # fails with strings %!fail ("getrangefromclass ({3, 4})"); # fails with cells image-2.16.1/inst/PaxHeaders.61586/bwselect.m0000644000000000000000000000006215005110255015370 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwselect.m0000644000175000017500000000654215005110255016775 0ustar00avinoamavinoam00000000000000## Copyright (C) 1999 Andy Adler ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{imout}, @var{idx}] =} bwselect(@var{im}, @var{cols}, @var{rows}, @var{connect}) ## Select connected regions in a binary image. ## ## @table @code ## @item @var{im} ## binary input image ## @item [@var{cols}, @var{rows}] ## vectors of starting points (x,y) ## @item @var{connect} ## connectedness 4 or 8. default is 8 ## @item @var{imout} ## the image of all objects in image im that overlap ## pixels in (cols,rows) ## @item @var{idx} ## index of pixels in imout ## @end table ## @end deftypefn function [imout, idx] = bwselect (im, cols, rows, connect) if (nargin < 3 || nargin > 4) print_usage(); elseif (nargin == 3) connect = 8; endif if (connect != 4 && connect != 8) error ("bwselect: connect should be 4 or 8") endif [~, idx] = bwfill (! im, cols, rows, 12 - connect); imout = false (size (im)); imout(idx) = true; if (nargout == 0) figure; imshow(imout); endif endfunction %!test %! BW = zeros(5, 'logical'); %! BW(3, 4) = 1; %! BW(4, 3) = 1; %! res8 = bwselect (BW, 3, 4, 8); %! assert (res8, BW) %! res4 = bwselect (BW, 3, 4, 4); %! res8_expected = BW; %! res8_expected(4, 3) = 1; %! assert (res8, res8_expected) %!test %! A = [0 1 0 0 1; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 1 0]; %! R4 = zeros(5, 'logical'); %! R4(1, 1) = 1; %! R8 = logical([1 0 1 1 0; 0 1 0 1 1; 0 1 0 0 1; 0 0 0 1 1; 0 1 1 0 1]); %! out = bwselect (A, 1, 1, 4); %! assert (out, zeros (5, 'logical')) %! out = bwselect (A, 1, 1, 8); %! assert (out, zeros (5, 'logical')) %! out = bwselect (! A, 1, 1, 4); %! assert (out, R4) %! out = bwselect (! A, 1, 1, 8); %! assert (out, R8) %! %! B4 = logical([0 0 0 0 0; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 0 0]); %! B8 = logical([0 1 0 0 0; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 1 0]); %! out = bwselect (A, 3, 3, 4); %! assert (out, B4) %! out = bwselect (A, 3, 3, 8); %! assert (out, B8) %! out = bwselect (A, 3, 3); %! assert (out, B8) %! %! C4 = logical ([0 0 1 1 0; 0 0 0 1 1; 0 0 0 0 1; 0 0 0 1 1; 0 0 0 0 1]); %! C8 = logical ([1 0 1 1 0; 0 1 0 1 1; 0 1 0 0 1; 0 0 0 1 1; 0 1 1 0 1]); %! out = bwselect (! A, 3, 1, 8); %! assert (out, C8) %! out = bwselect (! A, 3, 1); %! assert (out, C8) %! out = bwselect (! A, 3, 1, 4); %! assert (out, C4) %! %! D4 = logical ([0 0 0 0 0; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 0 0]); %! D8 = logical ([0 1 0 0 0; 1 0 1 0 0; 1 0 1 1 0; 1 1 1 0 0; 1 0 0 1 0]); %! out = bwselect (A, [3 1], [1 3], 4); %! assert (out, D4); %! out = bwselect (A, [3 1], [1 3], 8); %! assert (out, D8); %! out = bwselect (A, [3 1], [1 3]); %! assert (out, D8); %!test %!error id=Octave:invalid-fun-call bwselect () %!error id=Octave:invalid-fun-call bwselect ("aaa") image-2.16.1/inst/PaxHeaders.61586/houghlines.m0000644000000000000000000000006215005110255015725 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/houghlines.m0000644000175000017500000003100715005110255017324 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{lines} =} @ houghlines (@var{BW}, @var{theta}, @var{rho}, @var{peaks}) ## @deftypefnx {Function File} {@var{lines} =} @ houghlines (@dots{}, @var{property}, @var{value}, @dots{}) ## Extract line segments from a Hough transform. ## ## This function takes as inputs the binary 2d image @var{BW} and the vectors @var{theta} and @var{rho} with the coodinates ## of the Hough transform (as returned by the @code{hough} function). Its @var{peaks} input is an n-by-2 array where each row ## contains the coodinates of a peak of interest in the Hough transform. (Those peaks in the Hough transform can be ## found with the @code{houghpeaks} function.) ## ## The result @var{lines} of this function contains information about all the line segments ## in the image @var{BW} that correspond to the given @var{peak} positions of the Hough transform. ## The @var{lines} output is a struct array where each of the elements has the following four ## components to describe a single line segment: @code{point1} has the xy-coordinates of the first pixel, ##@code{point2} the xy-coordinates of the last pixel, @code{theta} its angle to the vertical axis and @code{rho} its ## distance to the image origin. (output coordinate convention: [x, y] = [column, row]) ## ## Additionally the following optional property-value-pairs can be used: ## @table @asis ## @item @var{FillGap} ## Gaps between line segments that are shorter or equal than @var{FillGap} will be ignored and both sides will still be ## considered as part of the same line segment. ## This value defaults to 20. ## ## @item @var{MinLength} ## Line segments that are shorter than @var{MinLength} will be suppressed in the output. ## This value defaults to 40. ## @end table ## ## @seealso{hough, houghpeaks} ## @end deftypefn ## Algorithm: ## The Matlab help page does not cite any reference ## for the algorithm of this function. ## ## For this Octave implementation the information ## on Matlab's help page, as well as the information ## from this book was used: ## "Digital Image Processing using Matlab" ## by R.C. Gonzalez, R. E. Woods and S. L. Eddins ## McGrawHill, 2nd edition 2010. ## (Chapter 10.2.2. "Toolbox Hough Functions") ## ## The result is the following straight forward (brute force?) ## implementation. The individual steps are commented ## in the code below. function lines = houghlines (BW, theta, rho, peaks, varargin) ## retrieve the input parameters: fillgap = []; minlength = []; if ((nargin < 4) || (nargin > 8) || any (nargin == [5, 7])) print_usage (); endif for n = 5:2:(nargin-1) # process parameter-values pairs if (strcmpi (varargin{n-4}, "fillgap")) fillgap = varargin{n-4+1}; elseif (strcmpi (varargin{n-4}, "minlength")) minlength = varargin{n-4+1}; else error ("houghlines: invalid PROPERTY given") endif endfor ## set default parameters: if (isempty (fillgap)) fillgap = 20; endif if (isempty (minlength)) minlength = 40; endif ## check input parameters: if (! isimage (BW) || ndims (BW) != 2) error ("houghlines: BW must be a logical or numeric 2d array"); endif if (! isimage (theta) || ! isnumeric (theta) || ! isvector (theta)) error ("houghlines: THETA must be a numeric vector"); endif if (! isimage (rho) || ! isnumeric (rho) || ! isvector (rho)) error ("houghlines: RHO must be a numeric vector"); endif if (! isimage (peaks) || ! isnumeric (peaks) || ndims (peaks) > 2 || size (peaks, 2) != 2) error ("houghlines: PEAKS must be a n-by-2 numeric array"); endif if (! isnumeric (fillgap) || ! isreal (fillgap) || fillgap <= 0 || ! isscalar (fillgap)) error ("houghlines: FILLGAP must be a positive scalar number"); endif if (! isnumeric (minlength) || ! isreal (minlength) || minlength <= 0 || ! isscalar (minlength)) error ("houghlines: MINLENGTH must be a positive scalar number"); endif ## start the calculation: lines = struct ([]); numpeaks = size (peaks, 1); numlines = 0; ## find all foreground pixels and transform their ## coordinates to conventions of Hough transform ## xy and (1,1) based [allpixels_r, allpixels_c] = find (BW); origin = [1 1]; allpixels_x = allpixels_c - origin(1); allpixels_y = allpixels_r - origin(2); ## process each given Hough peak individually for n = 1:numpeaks rho_p_idx = peaks(n, 1); theta_p_idx = peaks(n, 2); rho_p = rho(rho_p_idx); # distance from "origin" pixel at (1,1) theta_p = theta(theta_p_idx); # measured clockwise to the vertical axis, in degrees ## Find all the image pixels that belong to this ## Hough accumulator cell with theta_p and rho_p: ## (What rho would those pixels have, if they really had theta_p?) ## (rho2idx_factor is a precaution for when hough.m will be able ## to deal with the RhoResolution parameter.) rho_all = allpixels_x .* cosd (theta_p) +allpixels_y .* sind (theta_p); rho2idx_factor = (length (rho) -1) ./ (rho(end) - rho(1)); rho_all_idx = round(( rho_all - rho(1) ) .* rho2idx_factor) + 1; peak_pixels_idx = find (rho_all_idx == rho_p_idx); ## transform coordinates to output convention: xy and (0,0) based peak_pixels_x = allpixels_x(peak_pixels_idx) + origin(1); peak_pixels_y = allpixels_y(peak_pixels_idx) + origin(2); if (length (peak_pixels_x) == 0) continue # avoid special cases for empty peak_pixel vectors endif ## order those image pixels, the "faster" axis first: ## (to avoid excessive index jumps in "wide" lines) x_span = max (peak_pixels_x) - min (peak_pixels_x); y_span = max (peak_pixels_y) - min (peak_pixels_y); if (x_span > y_span) peak_pixels_yx = sortrows ([peak_pixels_y, peak_pixels_x], [1 2]); else peak_pixels_yx = sortrows ([peak_pixels_y, peak_pixels_x], [2 1]); endif peak_pixels = [peak_pixels_yx(:,2), peak_pixels_yx(:,1)]; # weired re-ordering needed for compatibility ## calculate the euclidean distance between adjacent (ordered) pixels: dist = sqrt (diff (peak_pixels(:,1)).^2 + diff (peak_pixels(:,2)).^2); ## split line into segments, which are separated by more than fillgap: ## (always use very first and very last pixel in peak_pixels) endpoint_idx = find (dist > fillgap); num_peak_pixels = size (peak_pixels, 1); endpoint_idx = [0; endpoint_idx; num_peak_pixels]; for m = 2 : length (endpoint_idx) first_pixel = peak_pixels(endpoint_idx(m-1)+1, :); # point after last endpoint last_pixel = peak_pixels(endpoint_idx(m), :); # this endpoint length_segment = sqrt (sum((last_pixel - first_pixel).^2)); ## save this segment if it is long enough: if (length_segment < minlength) continue; else numlines += 1; lines(numlines).point1 = first_pixel; lines(numlines).point2 = last_pixel; lines(numlines).theta = theta_p; lines(numlines).rho = rho_p; endif endfor # line segments endfor # peaks endfunction %!shared BW0, theta0, rho0, peaks0_1, peaks0_2, lines0_1, lines0_2, BW1, theta1, rho1, peaks1, lines1 %! BW0 = logical([0 0 0 0 1; 0 0 0 1 0; 1 0 1 0 0; 0 1 0 0 0; 1 1 1 1 1]); %! theta0 = [-90:89]; %! rho0 = [-7:7]; %! peaks0_1 = [11 130]; %! peaks0_2 = [11 130; 4 1]; %! lines0_1 = struct ("point1", {[1,5]}, "point2", {[5,1]}, "theta", {39}, "rho", {3}); %! lines0_2 = struct ("point1", {[1,5], [1,5]}, "point2", {[5,1],[5,5]}, "theta", {39,-90}, "rho", {3, -4}); %! BW1 = diag(ones(50,1)); %! theta1 = [-90:89]; %! rho1 = -70:70; %! peaks1 = [71 46]; %! lines1 = struct ("point1", {[1 1]}, "point2", {[50 50]}, "theta", {-45}, "rho", {0}); ## test input syntax: %!error houghlines () %!error houghlines (BW1) %!error houghlines (BW1, theta1) %!error houghlines (BW1, theta1, rho1) %!assert (houghlines (BW1, theta1, rho1, peaks1), lines1) %!error (houghlines (BW1, theta1, rho1, peaks1, [1 2 3])) %!assert (houghlines (BW1, theta1, rho1, peaks1, "FillGap", 5), lines1) %!assert (houghlines (BW1, theta1, rho1, peaks1, "MinLength", 2), lines1) %!assert (houghlines (BW1, theta1, rho1, peaks1, "FillGap", 5, "MinLength", 2), lines1) %!assert (houghlines (BW1, theta1, rho1, peaks1, "MinLength", 2, "FillGap", 5), lines1) %!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", 2, [1 2 3]) %!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", 2, "FillGap", 5, [1 2 3]) %!assert (houghlines (double (BW1), theta1, rho1, peaks1), lines1) %!error houghlines (ones(5, 5, 5), theta1, rho1, peaks1) %!error houghlines ("nonsense", theta1, rho1, peaks1) %!error houghlines (BW1, ones(5), rho1, peaks1) %!error houghlines (BW1, "nonsense", rho1, peaks1) %!error houghlines (BW1, theta1, ones(5), peaks1) %!error houghlines (BW1, theta1, "nonsense", peaks1) %!error houghlines (BW1, theta1, rho1, ones(5)) %!error houghlines (BW1, theta1, rho1, ones(2,2,2)) %!error houghlines (BW1, theta1, rho1, "nonsense") %!error houghlines (BW1, theta1, rho1, peaks1, "nonsense", 5) %!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", -5) %!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", [3 4]) %!error houghlines (BW1, theta1, rho1, peaks1, "MinLength", "nonsense") %!error houghlines (BW1, theta1, rho1, peaks1, "FillGap", -5) %!error houghlines (BW1, theta1, rho1, peaks1, "FillGap", [3 4]) %!error houghlines (BW1, theta1, rho1, peaks1, "FillGap", "nonsense") ## output class and structure: %!test %! out = houghlines(BW0, theta0, rho0, peaks0_2, "MinLength", 1); %! assert (out, lines0_2) # includes class = struct, size = [1,2] %!test # for empty output %! n = 100; %! BW = false (n); %! a = 50; % line starts at left side at row a %! b = 3; % slope of line is 1:b %! for column = 1:n %! if (rem (column, b) == 0) %! row = a - column/b; %! BW(row, column) = true; %! BW(row, column+1) = true; %! end %! end %! theta = [-90: 89]; %! rho = [-141:141]; %! peaks = [188, 163]; %! out = houghlines(BW, theta, rho, peaks, 'FillGap', 1, 'MinLength', 5); %! assert (out, struct([])) ## test calculation results: %!test %! out0_1 = houghlines(BW0, theta0, rho0, peaks0_1, 'MinLength', 1); %! out0_2 = houghlines(BW0, theta0, rho0, peaks0_2, 'MinLength', 1); %! assert (out0_1, lines0_1); %! assert (out0_2, lines0_2); %!test %! out = houghlines(BW1, theta1, rho1, peaks1); %! assert (out, lines1); %!test %! n = 100; %! BW = false (n); %! a = 50; % line starts at left side at row a %! b = 3; % slope of line is 1:b %! for column = 1:n %! if (rem (column, b) == 0) %! row = a - column/b; %! BW(row, column) = true; %! BW(row, column+1) = true; %! end %! end %! theta = [-90:89]; %! rho = [-141:141]; %! peaks = [188, 163]; %! lines_1 = struct ("point1", {[99 17]}, "point2", {[3 49]}, "theta", {72}, "rho", {46}); %! out_1 = houghlines(BW, theta, rho, peaks); %! out_n = houghlines(BW, theta, rho, peaks, 'FillGap', 1, 'MinLength', 1); %! assert (out_1, lines_1) %! assert (size (out_n), [1, 29]) ## show instructive demo: %!demo %! I = checkerboard (30, 1, 1); %! I = imnoise(I, "salt & pepper", 0.2); %! figure, imshow (I); %! title ("noisy image with some lines"); %! BW = edge (I, "canny"); %! figure, imshow(BW); %! title ("edge image"); %! [H, theta, rho] = hough (BW); %! figure, imshow (mat2gray (H), [], "XData", theta, "YData", rho); %! title ("Hough transform of edge image \n 2 peaks marked"); %! axis on; xlabel("theta [degrees]"); ylabel("rho [pixels]"); %! peaks = houghpeaks (H, 2); %! peaks_rho = rho(peaks(:,1)); %! peaks_theta = theta(peaks(:,2)); %! hold on; plot (peaks_theta, peaks_rho, "sr"); hold off; %! lines = houghlines (BW, theta, rho, peaks); %! figure, imshow (I), hold on; %! for n = 1:length (lines) %! points = [lines(n).point1; lines(n).point2]; %! plot (points(:,1), points(:,2), "r"); %! endfor %! title ("the two strongest lines (edges) in the image"), hold off; image-2.16.1/inst/PaxHeaders.61586/rangefilt.m0000644000000000000000000000006215005110255015533 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/rangefilt.m0000644000175000017500000000657315005110255017144 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{R} =} rangefilt (@var{im}) ## @deftypefnx{Function File} {@var{R} =} rangefilt (@var{im}, @var{domain}) ## @deftypefnx{Function File} {@var{R} =} rangefilt (@var{im}, @var{domain}, @var{padding}, @dots{}) ## Computes the local intensity range in a neighbourhood around each pixel in ## an image. ## ## The intensity range of the pixels of a neighbourhood is computed as ## ## @example ## @var{R} = max (@var{x}) - min (@var{x}) ## @end example ## ## where @var{x} is the value of the pixels in the neighbourhood, ## ## The neighbourhood is defined by the @var{domain} binary mask. Elements of the ## mask with a non-zero value are considered part of the neighbourhood. By default ## a 3 by 3 matrix containing only non-zero values is used. ## ## At the border of the image, extrapolation is used. By default symmetric ## extrapolation is used, but any method supported by the @code{padarray} function ## can be used. ## ## @seealso{paddarray, entropyfilt, stdfilt} ## @end deftypefn function retval = rangefilt (I, domain = true (3), padding = "symmetric", varargin) ## Check input if (nargin == 0) error ("rangefilt: not enough input arguments"); endif if (! isnumeric (I) && ! islogical (I)) error ("rangefilt: I must be a numeric or logical array"); endif if (! isnumeric (domain) && ! islogical (domain)) error ("rangefilt: DOMAIN must be a logical array"); endif domain = logical (domain); ## Pad image pad = floor (size (domain)/2); I = padarray (I, pad, padding, varargin {:}); even = (round (size (domain)/2) == size (domain)/2); idx = cell (1, ndims (I)); for k = 1:ndims (I) idx {k} = (even (k)+1):size (I, k); endfor I = I (idx {:}); retval = __spatial_filtering__ (I, domain, "range", zeros (size (domain)), 0); endfunction %!test %! im = rangefilt (ones (5)); %! assert (im, zeros (5)); ## some (Matlab compatible) tests on simple 2D-images: %!test %! A = zeros (3,3); %! B = ones (3,3); %! C = [1 1 1; 2 2 2; 3 3 3]; %! D = C'; %! E = ones (3,3); %! E(2,2) = 2; %! F = 3 .* ones (3,3); %! F(2,2) = 1; %! G = [-1 2 7; -5 2 8; -7 pi 9]; %! H = [5 2 8; 1 -3 1; 5 1 0]; %! A_out = [0 0 0; 0 0 0; 0 0 0]; %! B_out = [0 0 0; 0 0 0; 0 0 0]; %! C_out = [1 1 1; 2 2 2; 1 1 1]; %! D_out = [1 2 1; 1 2 1; 1 2 1]; %! E_out = [1 1 1; 1 1 1; 1 1 1]; %! F_out = [2 2 2; 2 2 2; 2 2 2]; %! G_out = [7 13 6; 7+pi 16 7; 7+pi 16 7]; %! H_out = [8 11 11; 8 11 11; 8 8 4]; %! assert (rangefilt (A), A_out) %! assert (rangefilt (B), B_out) %! assert (rangefilt (C), C_out) %! assert (rangefilt (D), D_out) %! assert (rangefilt (E), E_out) %! assert (rangefilt (F), F_out) %! assert (rangefilt (G), G_out, eps) %! assert (rangefilt (H), H_out) image-2.16.1/inst/PaxHeaders.61586/histeq.m0000644000000000000000000000006215005110255015055 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/histeq.m0000644000175000017500000000673515005110255016466 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Kai Habel ## Copyright (C) 2008 Jonas Wagner ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{J} =} histeq (@var{I}, @var{n}) ## Equalize histogram of grayscale image. ## ## The histogram contains ## @var{n} bins, which defaults to 64. ## ## @var{I}: Image in double format, with values from 0.0 to 1.0. ## ## @var{J}: Returned image, in double format as well. ## ## Note that the algorithm used for histogram equalization gives results ## qualitatively comparable but numerically different from @sc{matlab} ## implementation. ## ## @seealso{imhist, mat2gray, brighten} ## @end deftypefn function J = histeq (I, n = 64) if (nargin < 1 || nargin > 3) print_usage (); endif if (isempty (I)) J = []; return endif [r, c] = size (I); I = mat2gray (I); [X, map] = gray2ind (I, n); [nn, xx] = imhist (I, n); Icdf = 1 / prod (size (I)) * cumsum (nn); J = reshape (Icdf(X + 1), r, c); endfunction ## FIXME: the method we are using is different from Matlab so our results ## are slightly different. The following xtest show the Matlab ## results that we should be aiming at. %!assert (histeq ([]), []); ## One value %!assert (histeq (0), 1); %!assert (histeq (1), 1); %!assert (histeq (1.5), 1); %!assert (histeq (zeros (100, 200)), ones (100, 200)); # matrix ## Two values %!xtest assert (histeq ([0 1]), [0.4920634921 1], 10^-8); %!xtest assert (histeq ([0 1]'), [0.4920634921 1]', 10^-8); # column array %!xtest assert (histeq ([0 255]), [0.4920634921 1], 10^-8); %!xtest assert (histeq (uint8 ([0 1])), [ 125 190]); # uint8 %!xtest assert (histeq (uint8 ([0 255])), [ 125 255]); %!xtest assert (histeq (uint16 ([0 1])), [65535 65535]); # uint16 %!xtest assert (histeq (uint16 ([0 255])), [32247 48891]); %!xtest assert (histeq (uint16 ([0 256])), [32247 48891]); %!xtest assert (histeq (uint16 ([0 65535])), [32247 65535]); ## Three values %!test assert (histeq ([0 1 1] ), [ 1/3 1 1] , 10^-8); %!test assert (histeq ([0 0 1]'), [ 2/3 2/3 1]', 10^-8); %!xtest assert (histeq ([0 1 2] ), [ 1/3 1 1] , 10^-8); %!xtest assert (histeq (uint8 ([0 1 2])), [ 85 125 215]); %!xtest assert (histeq (uint16 ([0 1 2])), [65535 65535 65535]); %!xtest assert (histeq (uint16 ([0 100 200])), [43690 43690 55133]); ## Many values %!xtest %! J = [20 32 57 81 105 125 150 174 198 223 247]; %! assert (histeq (uint8 (0:10:100)), J); %!xtest %! J = [0.0793650794 %! 0.1269841270 %! 0.2222222222 %! 0.3174603175 %! 0.4126984127 %! 0.4920634921 %! 0.5873015873 %! 0.6825396825 %! 0.7777777778 %! 0.8730158730 %! 1.0000000000]; %! assert (histeq (0:0.1:1), J', 10^-8); image-2.16.1/inst/PaxHeaders.61586/otsuthresh.m0000644000000000000000000000006215005110255015770 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/otsuthresh.m0000644000175000017500000001254615005110255017376 0ustar00avinoamavinoam00000000000000## Copyright (C) 2018 Avinoam Kalma ## Copyright (C) 2018 David Miguel Susano Pinto ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {} {[@var{level}, @var{sep}] =} otsuthresh (@var{hist}) ## Compute global image threshold for histogram using Otsu's method. ## ## Given an image histogram @var{hist} finds the optimal threshold ## value @var{level} for conversion to a binary image with ## @code{im2bw}. ## ## The Otsu's method chooses the threshold value that minimises the ## intraclass variance between two classes, the background and ## foreground. The method is described in @cite{Nobuyuki Otsu ## (1979). "A threshold selection method from gray-level histograms", ## IEEE Trans. Sys., Man., Cyber. 9 (1): 62-66}. ## ## The second output, @var{sep} represents the ``goodness'' (or ## separability) of the threshold at @var{level}. It is a value ## within the range [0 1], the lower bound (zero) being attainable by, ## and only by, histograms having a single constant grey level, and ## the upper bound being attainable by, and only by, two-valued ## pictures. ## ## @seealso{graythresh, im2bw} ## @end deftypefn function [varargout] = otsuthresh (hist) if (nargin != 1) print_usage (); endif if (! isvector (hist) || ! isnumeric (hist) || ! isreal (hist) || any (isinf (hist)) || any (isnan (hist)) || any (hist < 0) || any (hist != fix (hist))) error ("otsuthresh: HIST must be a vector of non-negative integers"); endif hist = double (hist); [varargout{1:nargout}] = graythresh (hist(:).', "otsu"); endfunction %!test %! histo = zeros (1, 256); %! histo([ 29 33 37 41 46 50 54 58 62 66 70 74 78 82 ... %! 86 90 94 98 102 106 110 114 118 122 126 131 135 139 ... %! 143 147 151 155 159 163 167 171 175 179 183 187 191 195 ... %! 199 203 207 211 216 220 224 228 232 236 240 244 248 252]) = ... %! [2 27 51 144 132 108 43 29 22 21 22 20 10 16 17 12 13 14 12 13 ... %! 15 25 19 20 23 37 23 65 92 84 87 54 50 54 33 73 76 64 57 58 47 ... %! 48 30 27 22 20 20 11 12 12 11 7 17 31 37 31]; %! assert (otsuthresh (histo), 114.5/255) %!test %! I = max (phantom (), 0); %! H = imhist (I); %! assert (otsuthresh (H), 178/255) %! assert (otsuthresh (H'), 178/255) %! H = imhist (I, 10); %! assert (otsuthresh (H), 170/255) %!assert (otsuthresh (100), 0) %!assert (otsuthresh (zeros (256, 1)), 0) %!assert (otsuthresh (zeros (5, 1)), 0) %!assert (otsuthresh (uint8 ([10 20 30])), 0.5) %!assert (otsuthresh (int32 ([100 200 300])), 0.5) %!assert (otsuthresh (int32 ([100 200])), 0) %!assert (otsuthresh (single ([10 20 30 40])), 1/3); %!assert (otsuthresh (uint16 ([10 20 30 40 50 60 70 80 90 100])), 5/9) %!assert (otsuthresh (int16 ([10 20 30 40 50 60 70 80 90 100])), 5/9) %!assert (otsuthresh (int16 (1:255)), 156/254) %!assert (otsuthresh (int16 (1:1023)), 631/1022) %!assert (otsuthresh (int8 (1:1023)), 541/1022) %!test %! warning ("off", "Octave:data-file-in-path", "local"); %! S = load ("penny.mat"); %! h = imhist (uint8 (S.P)); %! assert (otsuthresh (h), 94/255); %!test %! I = max (phantom (), 0); %! h = imhist (I, 5); %! assert (otsuthresh (h), 0.625); %!error id=Octave:invalid-fun-call otsuthresh () %!error id=Octave:invalid-fun-call otsuthresh (ones (10), 5) %!error otsuthresh ([]) %!error otsuthresh ([Inf 10]) %!error otsuthresh ([10 NA]) %!error otsuthresh ([10 NaN]) %!error otsuthresh (zeros (5)) %!error otsuthresh ([10 -10]) %!error otsuthresh ("foo") %!demo %! I = max (phantom (), 0); %! figure; imshow (I); %! title ("Original image"); %! h = imhist (I); %! t = otsuthresh (h); %! J = im2bw (I); %! figure; imshow (J); %! title_line = sprintf ("Black and white image after thresholding, t=%g", %! t*255); %! title (title_line); %!demo %! warning ("off", "Octave:data-file-in-path", "local"); %! S = load ("penny.mat"); %! I = uint8 (S.P); %! figure; imshow (I); %! title ("Original penny image"); %! h = imhist (I); %! t = otsuthresh (h); %! J = im2bw (I); %! figure; imshow (J); %! title_line = sprintf ("Black and white penny image after thresholding, t=%g", %! t*255); %! title (title_line); %! I = 255 - I; %! figure; imshow(I); %! title ("Negative penny image"); %! h = imhist (I); %! t = otsuthresh (h); %! J = im2bw (I); %! figure; imshow (J); %! title_line = sprintf ("Black and white negative penny image after thresholding, t=%g", %! t*255); %! title (title_line); image-2.16.1/inst/PaxHeaders.61586/padarray.m0000644000000000000000000000006215005110255015363 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/padarray.m0000644000175000017500000006127315005110255016772 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} padarray (@var{A}, @var{padsize}) ## @deftypefnx {Function File} {} padarray (@dots{}, @var{padval}) ## @deftypefnx {Function File} {} padarray (@dots{}, @var{pattern}) ## @deftypefnx {Function File} {} padarray (@dots{}, @var{direction}) ## Pad array or matrix. ## ## Adds padding of length @var{padsize}, to a numeric matrix @var{A}. ## @var{padsize} must be a vector of non-negative values, each of them ## defining the length of padding to its corresponding dimension. For ## example, if @var{padsize} is [4 5], it adds 4 rows (1st dimension) ## and 5 columns (2nd dimension), to both the start and end of @var{A}. ## ## If there's less values in @var{padsize} than number of dimensions in @var{A}, ## they're assumed to be zero. Singleton dimensions of @var{A} are also ## padded accordingly (except when @var{pattern} is @qcode{"reflect"}). ## ## The values used in the padding can either be a scalar value @var{padval}, or ## the name of a specific @var{pattern}. Available patterns are: ## ## @table @asis ## @item @qcode{"zeros"} (default) ## Pads with the value 0 (same as passing a @var{padval} of 0). This is the ## default. ## ## @item @qcode{"circular"} ## Pads with a circular repetition of elements in @var{A} (similar to ## tiling @var{A}). ## ## @item @qcode{"replicate"} ## Pads replicating the values at the border of @var{A}. ## ## @item @qcode{"symmetric"} ## Pads with a mirror reflection of @var{A}. ## ## @item @qcode{"reflect"} ## Same as "symmetric", but the borders are not used in the padding. Because ## of this, it is not possible to pad singleton dimensions. ## ## @end table ## ## By default, padding is done in both directions. To change this, ## @var{direction} can be one of the following values: ## ## @table @asis ## @item @qcode{"both"} (default) ## Pad each dimension before the first element of @var{A} the number ## of elements defined by @var{padsize}, and the same number again after ## the last element. This is the default. ## ## @item @qcode{"pre"} ## Pad each dimension before the first element of @var{A} the number of ## elements defined by @var{padsize}. ## ## @item @qcode{"post"} ## Pad each dimension after the last element of @var{A} the number of ## elements defined by @var{padsize}. ## ## @end table ## ## @seealso{cat, flip, resize, prepad, postpad} ## @end deftypefn function B = padarray(A, padsize, varargin) if (nargin < 2 || nargin > 4) print_usage (); elseif (! isvector (padsize) || ! isnumeric (padsize) || any (padsize < 0) || any (padsize != fix (padsize))) error ("padarray: PADSIZE must be a vector of non-negative integers"); endif ## Assure padsize is a row vector padsize = padsize(:).'; if (! any (padsize)) ## Nothing to do here B = A; return endif ## Default values padval = 0; pattern = ""; direction = "both"; ## There won't be more than 2 elements in varargin ## We have to support setting the padval (shape) and direction in any ## order. Both examples must work: ## padarray (A, padsize, "circular", "pre") ## padarray (A, padsize, "pre", "circular") for opt = 1:numel(varargin) val = varargin{opt}; if (ischar (val)) if (any (strcmpi (val, {"pre", "post", "both"}))) direction = val; elseif (any (strcmpi (val, {"circular", "replicate", "reflect", "symmetric"}))) pattern = val; elseif (strcmpi (val, "zeros")) padval = 0; else error ("padarray: unrecognized string option `%s'", val); endif elseif (isscalar (val)) padval = val; else error ("padarray: PADVAL and DIRECTION must be a string or a scalar"); endif endfor fancy_pad = false; if (! isempty (pattern)) fancy_pad = true; endif ## Check direction pre = any (strcmpi (direction, {"pre", "both"})); post = any (strcmpi (direction, {"post", "both"})); ## Create output matrix B_ndims = max ([numel(padsize) ndims(A)]); A_size = size (A); P_size = padsize; A_size(end+1:B_ndims) = 1; # add singleton dimensions P_size(end+1:B_ndims) = 0; # assume zero for missing dimensions pre_pad_size = P_size * pre; B_size = A_size + pre_pad_size + (P_size * post); ## insert input matrix into output matrix A_idx = cell (B_ndims, 1); for dim = 1:B_ndims A_idx{dim} = (pre_pad_size(dim) +1):(pre_pad_size(dim) + A_size(dim)); endfor if (post && ! pre && (padval == 0 || fancy_pad)) ## optimization for post padding only with zeros B = resize (A, B_size); else B = repmat (cast (padval, class (A)), B_size); B(A_idx{:}) = A; endif if (fancy_pad) ## Init a template "index all" cell array template_idx = repmat ({":"}, [B_ndims 1]); circular = replicate = symmetric = reflect = false; switch (tolower (pattern)) case "circular", circular = true; case "replicate", replicate = true; case "symmetric", symmetric = true; case "reflect", reflect = true; otherwise error ("padarray: unknown PADVAL `%s'.", pattern); endswitch ## For a dimension of the input matrix of size 1, since reflect does ## not includes the borders, it is not possible to pad singleton dimensions. if (reflect && any ((! (A_size -1)) & P_size)) error ("padarray: can't add %s padding to singleton dimensions", pattern); endif ## For symmetric and reflect: ## ## The idea is to split the padding into 3 different cases: ## bits ## Parts of the input matrix that are used for the padding. ## In most user cases, there will be only this padding, ## complete will be zero, and so bits will be equal to padsize. ## complete ## Number of full copies of the input matrix are used for ## the padding (for reflect, "full" size is actually minus 1). ## This is divided into pair and unpaired complete. In most ## cases, this will be zero. ## pair complete ## Number of pairs of complete copies. ## unpaired complete ## This is either 1 or 0. If 1, then the complete copy closer ## to the output borders has already been flipped so that if ## there's bits used to pad as well, they don't need to be flipped. ## ## Reasoning pair and unpaired complete: when the pad is much larger ## than the input matrix, we must pay we must pay special attention to ## symmetric and reflect. In a normal case (the padding is smaller than ## the input), we just use the flipped matrix to pad and we're done. ## In other cases, if the input matrix is used multiple times on the ## pad, every other copy of it must NOT be flipped (the padding must be ## symmetric itself) or the padding will be circular. if (reflect) A_cut_size = A_size -1; complete = floor (P_size ./ A_cut_size); bits = rem (P_size, A_cut_size); pair_size = A_cut_size * 2; pair_complete = floor (complete / 2); unpaired_complete = mod (complete, 2); else complete = floor (P_size ./ A_size); bits = rem (P_size, A_size); if (circular) complete_size = complete .* A_size; elseif (symmetric) pair_complete = floor (complete / 2); pair_size = A_size * 2; unpaired_complete = mod (complete, 2); endif endif dim = 0; for s = padsize dim++; if (s == 0) ## skip this dimension if no padding requested continue endif if (circular) dim_idx = template_idx; source_idx = template_idx; A_idx_end = A_idx{dim}(end); A_idx_ini = A_idx{dim}(1); if (complete(dim)) dim_pad_size(1:B_ndims) = 1; dim_pad_size(dim) = complete(dim)*pre + complete(dim)*post; dim_idx{dim} = []; if (pre) dim_idx{dim} = [(bits(dim) +1):(complete_size(dim) + bits(dim))]; endif if (post) dim_idx{dim} = [dim_idx{dim} (A_idx_end +1):(A_idx_end + complete_size(dim))]; endif source_idx{dim} = A_idx{dim}; B(dim_idx{:}) = repmat (B(source_idx{:}), dim_pad_size); endif if (pre) if (bits(dim)) dim_idx{dim} = 1:bits(dim); source_idx{dim} = (A_idx_end - bits(dim) +1):A_idx_end; B(dim_idx{:}) = B(source_idx{:}); endif endif if (post) if (bits(dim)) dim_idx{dim} = (B_size(dim) -bits(dim) +1):B_size(dim); source_idx{dim} = A_idx_ini:(A_idx_ini + bits(dim) -1); B(dim_idx{:}) = B(source_idx{:}); endif endif elseif (replicate) dim_pad_size(1:B_ndims) = 1; dim_pad_size(dim) = P_size(dim); dim_idx = template_idx; source_idx = template_idx; if (pre) dim_idx{dim} = 1:P_size(dim); source_idx{dim} = P_size(dim) +1; B(dim_idx{:}) = repmat (B(source_idx{:}), dim_pad_size); endif if (post) dim_idx{dim} = (A_idx{dim}(end) +1):B_size(dim); source_idx{dim} = A_idx{dim}(end); B(dim_idx{:}) = repmat (B(source_idx{:}), dim_pad_size); endif ## The idea behind symmetric and reflect passing is the same so the ## following cases have similar looking code. However, there's small ## adjustements everywhere that makes it really hard to merge as a ## common case. elseif (symmetric) dim_idx = template_idx; source_idx = template_idx; A_idx_ini = A_idx{dim}(1); A_idx_end = A_idx{dim}(end); if (pre) if (bits(dim)) dim_idx{dim} = 1:bits(dim); if (unpaired_complete(dim)) source_idx{dim} = (A_idx_end - bits(dim) +1):A_idx_end; B(dim_idx{:}) = B(source_idx{:}); else source_idx{dim} = A_idx_ini:(A_idx_ini + bits(dim) -1); B(dim_idx{:}) = flip (B(source_idx{:}), dim); endif endif endif if (post) if (bits(dim)) dim_idx{dim} = (B_size(dim) - bits(dim) +1):B_size(dim); if (unpaired_complete(dim)) source_idx{dim} = A_idx_ini:(A_idx_ini + bits(dim) -1); B(dim_idx{:}) = B(source_idx{:}); else source_idx{dim} = (A_idx_end - bits(dim) +1):A_idx_end; B(dim_idx{:}) = flip (B(source_idx{:}), dim); endif endif endif if (complete(dim)) dim_pad_size(1:B_ndims) = 1; source_idx{dim} = A_idx{dim}; flipped_source = flip (B(source_idx{:}), dim); endif if (pair_complete(dim)) dim_pad_size(dim) = pair_complete(dim); dim_idx{dim} = []; if (pre) dim_idx{dim} = [(1 + bits(dim) + (A_size(dim)*unpaired_complete(dim))):(A_idx_ini -1)]; B(dim_idx{:}) = repmat (cat (dim, B(source_idx{:}), flipped_source), dim_pad_size); endif if (post) dim_idx{dim} = [(A_idx_end +1):(A_idx_end + (pair_size(dim) * pair_complete(dim)))]; B(dim_idx{:}) = repmat (cat (dim, flipped_source, B(source_idx{:})), dim_pad_size); endif endif if (unpaired_complete(dim)) source_idx = template_idx; if (pre) dim_idx{dim} = (1 + bits(dim)):(bits(dim) + A_size(dim)); B(dim_idx{:}) = flipped_source(source_idx{:}); endif if (post) dim_idx{dim} = (B_size(dim) - bits(dim) - A_size(dim) +1):(B_size(dim) - bits(dim)); B(dim_idx{:}) = flipped_source(source_idx{:}); endif endif elseif (reflect) dim_idx = template_idx; source_idx = template_idx; A_idx_ini = A_idx{dim}(1); A_idx_end = A_idx{dim}(end); if (pre) if (bits(dim)) dim_idx{dim} = 1:bits(dim); if (unpaired_complete(dim)) source_idx{dim} = (A_idx_end - bits(dim)):(A_idx_end -1); B(dim_idx{:}) = B(source_idx{:}); else source_idx{dim} = (A_idx_ini +1):(A_idx_ini + bits(dim)); B(dim_idx{:}) = flip (B(source_idx{:}), dim); endif endif endif if (post) if (bits(dim)) dim_idx{dim} = (B_size(dim) - bits(dim) +1):B_size(dim); if (unpaired_complete(dim)) source_idx{dim} = (A_idx_ini +1):(A_idx_ini + bits(dim)); B(dim_idx{:}) = B(source_idx{:}); else source_idx{dim} = (A_idx_end - bits(dim)):(A_idx_end -1); B(dim_idx{:}) = flip (B(source_idx{:}), dim); endif endif endif if (complete(dim)) dim_pad_size(1:B_ndims) = 1; source_idx{dim} = A_idx{dim}; flipped_source = flip (B(source_idx{:}), dim); endif if (pair_complete(dim)) dim_pad_size(dim) = pair_complete(dim); dim_idx{dim} = []; if (pre) flipped_source_idx = source_idx; flipped_source_idx{dim} = 1:A_cut_size(dim); source_idx{dim} = A_idx_ini:(A_idx_end -1); dim_idx{dim} = [(1 + bits(dim) + (A_cut_size(dim)*unpaired_complete(dim))):(A_idx_ini -1)]; B(dim_idx{:}) = repmat (cat (dim, B(source_idx{:}), flipped_source(flipped_source_idx{:})), dim_pad_size); endif if (post) flipped_source_idx = source_idx; flipped_source_idx{dim} = 2:A_size(dim); source_idx{dim} = (A_idx_ini +1):A_idx_end; dim_idx{dim} = [(A_idx_end +1):(A_idx_end + (pair_size(dim) * pair_complete(dim)))]; B(dim_idx{:}) = repmat (cat (dim, flipped_source(flipped_source_idx{:}), B(source_idx{:})), dim_pad_size); endif endif if (unpaired_complete(dim)) source_idx = template_idx; if (pre) source_idx{dim} = 1:(A_size(dim)-1); dim_idx{dim} = (1 + bits(dim)):(bits(dim) + A_size(dim) -1); B(dim_idx{:}) = flipped_source(source_idx{:}); endif if (post) source_idx{dim} = 2:A_size(dim); dim_idx{dim} = (B_size(dim) - bits(dim) - A_size(dim) +2):(B_size(dim) - bits(dim)); B(dim_idx{:}) = flipped_source(source_idx{:}); endif endif endif endfor endif endfunction %!demo %! padarray([1,2,3;4,5,6],[2,1]) %! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns of 0 %!demo %! padarray([1,2,3;4,5,6],[2,1],5) %! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns of 5 %!demo %! padarray([1,2,3;4,5,6],[2,1],0,'pre') %! % pads [1,2,3;4,5,6] with a left and top border of 2 rows and 1 columns of 0 %!demo %! padarray([1,2,3;4,5,6],[2,1],'circular') %! % pads [1,2,3;4,5,6] with a whole 'circular' border of 2 rows and 1 columns %! % border 'repeats' data as if we tiled blocks of data %!demo %! padarray([1,2,3;4,5,6],[2,1],'replicate') %! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns which %! % 'replicates' edge data %!demo %! padarray([1,2,3;4,5,6],[2,1],'symmetric') %! % pads [1,2,3;4,5,6] with a whole border of 2 rows and 1 columns which %! % is symmetric to the data on the edge ## Test default padval and direction %!assert (padarray ([1;2], [1]), [0;1;2;0]); %!assert (padarray ([3 4], [0 2]), [0 0 3 4 0 0]); %!assert (padarray ([1 2 3; 4 5 6], [1 2]), %! [zeros(1, 7); 0 0 1 2 3 0 0; 0 0 4 5 6 0 0; zeros(1, 7)]); ## Test padding on 3D array %!test %! assert (padarray ([1 2 3; 4 5 6], [3 2 1]), %! cat(3, zeros(8, 7), %! [ [ zeros(3, 7) ] %! [zeros(2, 2) [1 2 3; 4 5 6] zeros(2, 2) ] %! [ zeros(3,7)] ], %! zeros (8, 7))); ## Test if default param are ok %!assert (padarray ([1 2], [4 5]), padarray ([1 2], [4 5], 0)); %!assert (padarray ([1 2], [4 5]), padarray ([1 2], [4 5], "both")); ## Test literal padval %!assert (padarray ([1;2], [1], i), [i; 1; 2; i]); ## Test directions (horizontal) %!assert (padarray ([1;2], [1], i, "pre"), [i; 1; 2]); %!assert (padarray ([1;2], [1], i, "post"), [1; 2; i]); %!assert (padarray ([1;2], [1], i, "both"), [i; 1; 2; i]); ## Test directions (vertical) %!assert (padarray ([1 2], [0 1], i, "pre"), [i 1 2]); %!assert (padarray ([1 2], [0 1], i, "post"), [1 2 i]); %!assert (padarray ([1 2], [0 1], i, "both"), [i 1 2 i]); ## Test vertical padsize %!assert (padarray ([1 2], [0;1], i, "both"), [i 1 2 i]); ## Test circular padding %!test %! A = [1 2 3; 4 5 6]; %! B = repmat (A, 7, 9); %! assert (padarray (A, [1 2], "circular", "pre"), B(2:4,2:6)); %! assert (padarray (A, [1 2], "circular", "post"), B(3:5,4:8)); %! assert (padarray (A, [1 2], "circular", "both"), B(2:5,2:8)); %! ## This tests when padding is bigger than data %! assert (padarray (A, [5 10], "circular", "both"), B(2:13,3:25)); % Test circular padding with int* uint* class types %!test %! A = int8 ([1 2 3; 4 5 6]); %! B = repmat (A, 7, 9); %! assert (padarray (A, [1 2], "circular", "pre"), B(2:4,2:6)); %! assert (padarray (A, [1 2], "circular", "post"), B(3:5,4:8)); %! assert (padarray (A, [1 2], "circular", "both"), B(2:5,2:8)); %! ## This tests when padding is bigger than data %! assert (padarray (A, [5 10], "circular", "both"), B(2:13,3:25)); ## Test replicate padding %!test %! A = [1 2; 3 4]; %! B = kron (A, ones (10, 5)); %! assert (padarray (A, [9 4], "replicate", "pre"), B(1:11,1:6)); %! assert (padarray (A, [9 4], "replicate", "post"), B(10:20,5:10)); %! assert (padarray (A, [9 4], "replicate", "both"), B); %! ## same with uint class %! assert (padarray (uint8 (A), [9 4], "replicate", "pre"), uint8 (B(1:11,1:6))); %! assert (padarray (uint8 (A), [9 4], "replicate", "post"), uint8 (B(10:20,5:10))); %! assert (padarray (uint8 (A), [9 4], "replicate", "both"), uint8 (B)); ## Test symmetric padding %!test %! A = [1:3 %! 4:6]; %! HA = [3:-1:1 %! 6:-1:4]; %! VA = [4:6 %! 1:3]; %! VHA = [6:-1:4 %! 3:-1:1]; %! B = [VHA VA VHA %! HA A HA %! VHA VA VHA]; %! assert (padarray (A, [1 2], "symmetric", "pre"), B(2:4,2:6)); %! assert (padarray (A, [1 2], "symmetric", "post"), B(3:5,4:8)); %! assert (padarray (A, [1 2], "symmetric", "both"), B(2:5,2:8)); %! ## same with int class %! assert (padarray (int16 (A), [1 2], "symmetric", "pre"), int16 (B(2:4,2:6))); %! assert (padarray (int16 (A), [1 2], "symmetric", "post"), int16 (B(3:5,4:8))); %! assert (padarray (int16 (A), [1 2], "symmetric", "both"), int16 (B(2:5,2:8))); ## Repeat some tests with int* uint* class types %!assert (padarray (int8 ([1; 2]), [1]), int8 ([0; 1; 2; 0])); %!assert (padarray (uint8 ([3 4]), [0 2]), uint8 ([0 0 3 4 0 0])); %!assert (padarray (int16 ([1; 2]), [1], 4), int16 ([4; 1; 2; 4])); %!assert (padarray (uint16 ([1; 2]), [1], 0), uint16 ([0; 1; 2; 0])); %!assert (padarray (uint32 ([1; 2]), [1], 6, "post"), uint32 ([1; 2; 6])); %!assert (padarray (int32 ([1; 2]), [1], int32 (4), "pre"), int32 ([4; 1; 2])); ## Test symmetric and reflect for multiple lengths of padding (since the way ## it's done changes based on this). By iterating from 10 on a matrix of size ## 10, we catch the cases where there's only part of the matrix on the pad, a ## single copy of the matrix, a single copy with bits of non-flipped matrix, two ##copies of the matrix (flipped and non-flipped), the two copies with bits. %!test %! in = [ 7 5 1 3 %! 5 3 3 4 %! 7 5 2 3 %! 6 1 3 8]; %! padded = [ %! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 %! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 %! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 %! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 %! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 %! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 %! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 %! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 %! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 %! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 %! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 %! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 %! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 %! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 %! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 %! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 %! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 %! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 %! 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 5 7 7 5 1 3 3 1 %! 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 3 5 5 3 3 4 4 3 %! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 %! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 %! 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 1 6 6 1 3 8 8 3 %! 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2 5 7 7 5 2 3 3 2]; %! for ite = 1:10 %! assert (padarray (in, [ite ite], "symmetric"), padded((11-ite):(14+ite),(11-ite):(14+ite))); %! assert (padarray (in, [ite ite], "symmetric", "pre"), padded((11-ite):14,(11-ite):14)); %! assert (padarray (in, [ite ite], "symmetric", "post"), padded(11:(14+ite),11:(14+ite))); %! endfor %!test %! in = [ 7 5 4 9 %! 6 4 5 1 %! 5 3 3 3 %! 2 6 7 3]; %! padded = [ %! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 %! 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 %! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 %! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 %! 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 %! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 %! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 %! 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 %! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 %! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 %! 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 %! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 %! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 %! 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 %! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 %! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 %! 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 %! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 %! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 %! 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 7 3 7 6 2 6 %! 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 3 3 3 3 5 3 %! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 %! 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 4 9 4 5 7 5 %! 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4 5 1 5 4 6 4]; %! for ite = 1:10 %! assert (padarray (in, [ite ite], "reflect"), padded((11-ite):(14+ite),(11-ite):(14+ite))); %! assert (padarray (in, [ite ite], "reflect", "pre"), padded((11-ite):14,(11-ite):14)); %! assert (padarray (in, [ite ite], "reflect", "post"), padded(11:(14+ite),11:(14+ite))); %! endfor image-2.16.1/inst/PaxHeaders.61586/imshowpair.m0000644000000000000000000000006215005110255015742 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imshowpair.m0000644000175000017500000002175715005110255017354 0ustar00avinoamavinoam00000000000000## Copyright (C) 2021 Martin Janda ## ## This program is free software: you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see ## . ## -*- texinfo -*- ## @deftypefn {} {@var{c} =} imshowpair (@var{a}, @var{b}) ## @deftypefnx {} {[@var{c}, @var{rc}] =} imshowpair (@var{a}, @var{ra}, @var{b}, @var{rb}) ## @deftypefnx {} {@var{c} =} imshowpair (@dots{}, @var{method}) ## @deftypefnx {} {@var{c} =} imshowpair (@dots{}, @var{name}, @var{value}) ## ## Combines two images using a specified @var{method}. The smaller image gets ## padded with zeros to match the size of the bigger one. The @var{method} is ## a char array and can have one of the following values: ## ## "falsecolor": Default. Display each image as one (or more) [R G B] ## channels of the output image. Images can be assigned to the output channels ## by passing the "ColorChannels" option (see below). ## ## "blend": Combines the images using alpha blending with both images equally ## transparent. ## ## "checkerboard": Masks both images with a 16x16 checkerboard stretched to fit ## the output image, each image masked with a negative version of the other's ## mask and combines the result. The top left tile contains the top left part ## of the image @var{a}. ## ## "diff": Outputs an image that represents the absolute difference of ## grayscale versions of the images. The result is also grayscale. ## ## "montage": Places @var{b} on the right side of @var{a}. This method is ## useful for comparing a modified image with its original. ## ## Intensities of the images can be scaled before creating @var{c} by ## providing the "Scaling" option which can take one of the following values: ## ## "independent": Default. Intensities of both images are scaled independently ## of each other. ## ## "joint": Intensities of both images are scaled as if all the pixels belonged ## to a single image coposed of @var{a} and @var{b}. ## ## "none": No scaling is applied. ## ## Output of the "falsecolor" method can be further modified by providing the ## "ColorChannels" option assigning image to one or two output [R G B] ## channels, given a three-element vector with values 0, 1 or 2, e.g. [0 2 1], ## that assigns @var{a} to the blue channel and @var{b} to the green channel. ## 0 means neither image gets assigned. Accepts also two char-array values ## that represent shorthands for commonly used vectors: "green-magenta" ## for [2 1 2] and "red-cyan" for [1 2 2]. ## ## When given @var{ra} and @var{rb}, images are positioned according to their ## positons in world coordinate system. The output image spans the combined ## extent of the images. Since both images can have different resolutions in ## both dimensions, the resolution of the output image in each dimension is ## the finer resolution of the two. Resulting spatial referencing object ## is returned as @var{rc}. ## ## @seealso{imfuse} ## @end deftypefn function img = imshowpair (varargin) if (nargin < 2) print_usage (); endif [a, b, ra, rb, method, scaling, color_channels, parent] = parse_varargin(varargin{:}); if (isempty (ra) || isempty (rb)) c = imfuse (a, b, method, "ColorChannels", color_channels, "Scaling", scaling); else [c, rc] = imfuse (a, ra, b, rb, method, "ColorChannels", color_channels, "Scaling", scaling); endif img = imshow (c); if (! isempty (parent)) set (img, "parent", parent); endif endfunction function [a, b, ra, rb, method, scaling, color_channels, parent] = parse_varargin(varargin) check_is_image (varargin{1}, "A"); a = varargin{1}; if (isimage (varargin{2})) b = varargin{2}; varargin(1:2) = []; ra = []; rb = []; elseif (is_imref (varargin{2})) if (length (varargin) < 4) error ("Octave:invalid-input-arg", ... "expected at least 4 arguments: A, RA, B, RB"); endif validateattributes (varargin{2}, {"imref2d"}, {}, "imshowpair", "RA"); check_is_image (varargin{3}, "B"); validateattributes (varargin{4}, {"imref2d"}, {}, "imshowpair", "RB"); ra = varargin{2}; b = varargin{3}; rb = varargin{4}; varargin(1:4) = []; endif method_names = {"falsecolor", "blend", "checkerboard", "diff", "montage" ... "interpolation"}; scaling_names = {"independent", "joint", "none"}; scaling = scaling_names{1}; method = []; parent = []; green_magenta = [2, 1, 2]; red_cyan = [1, 2, 2]; color_channels = green_magenta; while (! isempty (varargin)) if (isempty (method) && is_valid_method (varargin{1}, method_names)) method = lower (varargin{1}); if (strcmp (method, "interpolation")) error ("Octave:invalid-input-arg", ... "imshowpair: INTERPOLATION not implemented yet"); endif varargin(1) = []; else if (length (varargin) < 2) msg = "expected NAME, VALUE pairs or incorrect display method"; error ("Octave:invalid-input-arg", ["imshowpair: ", msg]); endif validateattributes (varargin{1}, {"char"}, {"nonempty"}, "imshowpair", "NAME"); validateattributes (varargin{2}, {}, {"nonempty"}, "imshowpair", "VALUE"); name = varargin{1}; value = varargin{2}; switch (lower (name)) case "scaling" if (! is_valid_scaling (value, scaling_names)) msg = "SCALING expected to be one of 'independent', 'joint', 'none'"; error ("Octave:invalid-input-arg", ["imshowpair: ", msg]); endif scaling = value; case "colorchannels" if (ischar (value)) switch (value) case "green-magenta" color_channels = green_magenta; case "red-cyan" color_channels = red_cyan; otherwise msg = "expected COLORCHANNELS to be one of 'green-magenta', 'red-cyan'"; error ("Octave:invalid-input-arg", ["imshowpair: ", msg]); endswitch else validateattributes (value, {"numeric"}, ... {"integer", "vector", "numel", 3, ">=", 0, "<=", 2}, ... "imshowpair", "COLORCHANNELS") if (! is_valid_channels (value)) error ("Octave:invalid-input-arg", ... "imshowpair: COLORCHANNELS must include values 1 and 2"); endif color_channels = value; endif case "parent" parent = value; otherwise error ("Octave:invalid-input-arg", ... strcat (name, " is not a recognized parameter")); endswitch varargin(1:2) = []; endif endwhile if (isempty (method)) method = method_names{1}; endif endfunction function check = is_imref (arg) check = isa (arg, "imref2d"); endfunction function check = is_valid_method (arg, method_names) check = ischar (arg) && ismember (lower (arg), method_names); endfunction function check = is_valid_scaling (arg, scaling_names) check = ischar (arg) && ismember (lower (arg), scaling_names); endfunction function check = is_valid_channels (arg) check = find (arg == 1) != 0 && find (arg == 2) != 0; endfunction function check_is_image (arg, name) if (! isimage (arg)) error ("Octave:invalid-input-arg", ... strcat(name, " expected to be logical, RGB or grayscale image")); endif endfunction %!error id=Octave:invalid-fun-call imshowpair () %!error id=Octave:invalid-fun-call imshowpair (1) %!error id=Octave:invalid-input-arg imshowpair (uint8 (200.*rand (100)), ... %! uint8 (200.*rand (100)), "interpolation") %!error id=Octave:invalid-input-arg imshowpair (uint8 (200.*rand (100)), ... %! uint8 (200.*rand (100)), "xxxxx") %!error id=Octave:invalid-input-arg imshowpair (1, 1, "ColorChannels", [0 0 0]) %!error id=Octave:invalid-input-arg imshowpair (1, 1, "ColorChannels", [1 1 1]) %!error id=Octave:invalid-input-arg imshowpair (1, 1, "ColorChannels", [2 2 2]) %!error id=Octave:expected-less-equal imshowpair (1, 1, "ColorChannels", [42 0 0]) %!error id=Octave:expected-greater-equal imshowpair (1, 1, "ColorChannels", [-1 2 0]) %!error id=Octave:invalid-input-arg imshowpair (1, 1, "ColorChannels", "deep-purple") %!test %! A = uint8 (200.*rand (100)); %! B = uint8 (150.*rand (100)); %! RA = imref2d (size (A), 0.5, 0.5); %! RB = imref2d (size (B), 0.5, 0.5); %! figure; %! Ax=axes; %! assert (imshowpair (A, B)); %! assert (imshowpair (A, RA, B, RB)); %! assert (imshowpair (A, B, "blend")); %! assert (imshowpair (A, B, "falsecolor", "ColorChannels", "red-cyan")); %! assert (imshowpair (A, B, "Parent", Ax)); %! assert (imshowpair (A, B, "montage", "Scaling", "joint")); %! close; image-2.16.1/inst/PaxHeaders.61586/bwareafilt.m0000644000000000000000000000006215005110255015700 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwareafilt.m0000644000175000017500000002451215005110255017302 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} bwareafilt (@var{bw}, @var{range}) ## @deftypefnx {Function File} {} bwareafilt (@var{bw}, @var{n}) ## @deftypefnx {Function File} {} bwareafilt (@var{bw}, @var{n}, @var{keep}) ## @deftypefnx {Function File} {} bwareafilt (@dots{}, @var{conn}) ## Filter objects from image based on their sizes. ## ## Returns a logical matrix with the objects of @var{bw} filtered based ## on their area (defined by thei number of pixels). This function is ## equivalent to @code{bwpropfilt (@var{bw}, "Area", @dots{})}. ## ## To filter objects with a value on a specific interval, @var{range} must be ## a two-element vector with the interval @code{[@var{low} @var{high}]} ## (values are inclusive). ## ## Alternatively, a scalar @var{n} will select the objects with the N highest ## values. The @var{keep} option defaults to @qcode{"largest"} but can also ## be set to @qcode{"smallest"} to select the N objects with lower values. ## ## The last optional argument, @var{conn}, can be a connectivity matrix, or ## the number of elements connected to the center (see @command{conndef}). ## ## @seealso{bwareaopen, bwlabel, bwlabeln, bwconncomp, bwpropfilt, regionprops} ## @end deftypefn function bwfiltered = bwareafilt (bw, varargin) if (nargin < 2 || nargin > 4) print_usage (); endif bwfiltered = bwpropfilt (bw, "Area", varargin{:}); endfunction %!shared a2d, a3d %! a2d = [1 0 0 0 0 0 1 0 0 1 %! 1 0 0 1 0 1 0 1 0 1 %! 1 0 1 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 1 1 1 0 0 0 0 %! 1 1 0 1 0 0 0 1 0 0 %! 1 1 0 0 0 0 1 0 1 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 1 1 0 0 1]; %! %! a3d = a2d; %! a3d(:,:,2) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 1 1 0 0 1 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 1 1 0 0 0 0 %! 1 1 0 1 0 0 0 0 0 0 %! 1 0 0 0 0 0 1 0 0 0 %! 0 1 0 0 0 0 0 0 0 1 %! 1 1 0 0 0 0 1 0 0 0]; %! %! a3d(:,:,3) = [ %! 1 0 0 0 0 0 0 0 0 0 %! 0 1 0 1 1 0 0 1 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 1 %! 1 1 0 0 0 0 0 0 0 0]; %!test %! f2d = [0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 1 1 1 0 0 0 0 %! 1 1 0 1 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0]; %! assert (bwareafilt (a2d, 2), logical (f2d)); %! assert (bwareafilt (a2d, 2, 8), logical (f2d)); %! assert (bwareafilt (a2d, 2, 4), logical (f2d)); %!test %! f2d = [1 0 0 0 0 0 1 0 0 0 %! 1 0 0 0 0 1 0 1 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 1 1 1 0 0 0 0 %! 1 1 0 1 0 0 0 1 0 0 %! 1 1 0 0 0 0 1 0 1 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0]; %! assert (bwareafilt (a2d, 5), logical (f2d)); %! assert (bwareafilt (a2d, 5, 8), logical (f2d)); %!test %! f2d = [0 0 0 0 0 0 1 0 0 1 %! 0 0 0 1 0 1 0 1 0 1 %! 0 0 1 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 1 0 0 %! 0 0 0 0 0 0 1 0 1 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 1 1 0 0 1]; %! assert (bwareafilt (a2d, 11, "smallest", 4), logical (f2d)); %!test %! f2d = [1 0 0 0 0 0 1 0 0 0 %! 1 0 0 0 0 1 0 1 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 1 0 0 0 1 0 0 %! 0 0 0 0 0 0 1 0 1 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0]; %! assert (bwareafilt (a2d, [3 5]), logical (f2d)); %! assert (bwareafilt (a2d, [3 5], 8), logical (f2d)); %!test %! f2d = [1 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0]; %! assert (bwareafilt (a2d, [3 4], 4), logical (f2d)); %! assert (bwareafilt (a2d, [3 4], [0 1 0; 1 1 1; 0 1 0]), logical (f2d)); %!test %! f2d = [1 0 0 0 0 0 1 0 0 1 %! 1 0 0 1 0 1 0 1 0 1 %! 1 0 1 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 1 0 0 0 1 0 0 %! 0 0 0 0 0 0 1 0 1 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 1 1 0 0 0]; %! assert (bwareafilt (a2d, [2 4]), logical (f2d)); %! assert (bwareafilt (a2d, [2 4], 8), logical (f2d)); %! assert (bwareafilt (a2d, [2 4], ones (3)), logical (f2d)); %!test %! f3d = [0 0 0 0 0 0 1 0 0 0 %! 0 0 0 1 0 1 0 1 0 0 %! 0 0 1 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0]; %! %! f3d(:,:,2) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 0 0 1 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0]; %! %! f3d(:,:,3) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 0 0 1 0 0 %! 0 0 0 1 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0]; %! assert (bwareafilt (a3d, 2), logical (f3d)); %! assert (bwareafilt (a3d, 2, 26), logical (f3d)); %! assert (bwareafilt (a3d, 2, ones (3, 3, 3)), logical (f3d)); %!test %! f3d = [0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 1 1 1 0 0 0 0 %! 1 1 0 1 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0]; %! %! f3d(:,:,2) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 1 1 0 0 0 0 %! 1 1 0 1 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 0 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0]; %! %! f3d(:,:,3) = [ %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 0 0 0 1 1 1 0 0 0 0 %! 0 0 0 0 0 0 0 0 0 0 %! 1 0 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0 %! 1 1 0 0 0 0 0 0 0 0]; %! assert (bwareafilt (a3d, 2, 6), logical (f3d)); %! assert (bwareafilt (a3d, 2, conndef (3, "minimal")), logical (f3d)); image-2.16.1/inst/PaxHeaders.61586/lab2double.m0000644000000000000000000000006215005110255015573 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/lab2double.m0000644000175000017500000000773015005110255017200 0ustar00avinoamavinoam00000000000000## Copyright (C) 2016 Carnë Draug ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} lab2double (@var{lab}) ## Convert L*a*b* data to double precision. ## ## @var{lab} must be a L*a*b* image or colormap, i.e., its dimensions ## must be MxNx3xK or Mx3. Its type must be double, single, uint16, ## or uint8. ## ## When converted to double, L* values range from 0 to 100, while a* and ## b* range from -128 to 127. When converting from uint16, the upper limit ## is 65280 (higher values will be converted above the range). ## ## @seealso{lab2double, lab2rgb, lab2single, lab2uint8, lab2uin16, lab2xyz} ## @end deftypefn function [lab] = lab2double (lab) if (nargin () != 1) print_usage (); endif lab = lab2cls (lab, "double"); endfunction ## Instead of testing the lab2double function here, we test the ## conversion from double type. The actual tests for lab2double, ## are spread all other lab2* functions. This makes the tests ## simpler. %!test %! l_max_f = 100 + (25500 / 65280); %! ab_max_f = 127 + (255 / 256); %! cm = [ %! -Inf %! Inf %! NaN %! l_max_f %! ab_max_f %! -200 %! -129 %! -128 %! -128+(255/65280)*(0.499999) %! -128+(255/65280)*(0.500001) # should be 0.5, but float rounding error %! -128+(255/65280)*(0.500002) %! -127 %! -1 %! 0 %! (100/65280)*(0.499999) %! (100/65280)*(0.51) %! (100/65280)*(0.500001) %! 1 %! 99 %! 100 %! 101 %! 126 %! 127 %! 128 %! 254 %! 255 %! 256 %! 257]; %! cm = repmat (cm, [1 3]); %! im2d = reshape (cm, [7 4 3]); %! imnd = permute (im2d, [1 4 3 2]); %! %! cm_uint8 = uint8 ([ %! 0 0 0 %! 255 255 255 %! 255 255 255 %! 255 228 228 %! 255 255 255 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 1 1 %! 0 127 127 %! 0 128 128 %! 0 128 128 %! 0 128 128 %! 0 128 128 %! 3 129 129 %! 252 227 227 %! 255 228 228 %! 255 229 229 %! 255 254 254 %! 255 255 255 %! 255 255 255 %! 255 255 255 %! 255 255 255 %! 255 255 255 %! 255 255 255]); %! %! assert (lab2uint8 (cm), cm_uint8) %! im2d_uint8 = reshape (cm_uint8, [7 4 3]); %! assert (lab2uint8 (im2d), im2d_uint8) %! assert (lab2uint8 (imnd), permute (im2d_uint8, [1 4 3 2])) %! %! cm_uint16 = uint16 ([ %! 0 0 0 %! 65535 65535 65535 %! 65535 65535 65535 %! 65535 58468 58468 %! 65535 65535 65535 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 1 1 %! 0 1 1 %! 0 256 256 %! 0 32512 32512 %! 0 32768 32768 %! 0 32768 32768 %! 1 32768 32768 %! 1 32768 32768 %! 653 33024 33024 %! 64627 58112 58112 %! 65280 58368 58368 %! 65535 58624 58624 %! 65535 65024 65024 %! 65535 65280 65280 %! 65535 65535 65535 %! 65535 65535 65535 %! 65535 65535 65535 %! 65535 65535 65535 %! 65535 65535 65535]); %! %! assert (lab2uint16 (cm), cm_uint16) %! im2d_uint16 = reshape (cm_uint16, [7 4 3]); %! assert (lab2uint16 (im2d), im2d_uint16) %! assert (lab2uint16 (imnd), permute (im2d_uint16, [1 4 3 2])) %! %! assert (lab2single (cm), single (cm)) %! assert (lab2single (im2d), single (im2d)) %! assert (lab2single (imnd), single (imnd)) image-2.16.1/inst/PaxHeaders.61586/hough.m0000644000000000000000000000006215005110255014672 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/hough.m0000644000175000017500000002126615005110255016277 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{H}, @var{theta}, @var{rho}] =} @ ## hough (@var{BW}) ## @deftypefnx {Function File} {[@var{H}, @var{theta}, @var{rho}] =} @ ## hough (@var{BW}, @var{property}, @var{value}, @dots{}) ## Compute the Hough transform to find lines in a binary image. ## ## The resulting Hough transform matrix @var{H} (accumulator array) is ## 2D. Its rows correspond to the distance values @var{rho} and its ## columns to the angle values @var{theta}. Points of high value in ## @var{H} correspond to present lines in the given image. ## ## The distance @var{rho} is measured with respect to the image ## origin. The angle @var{theta} is measured clockwise to the ## vertical axis. ## ## The following @var{property} values are possible: ## ## @table @asis ## @item @qcode{"Theta"} ## A vector of angle values. The Hough transform will be calculated ## at those Theta angle values. The angles are given in degrees. ## Defaults to [-90:89]. ## ## @item @qcode{"ThetaResolution"} ## A scalar value to specify the Theta angles for the Hough transform ## in a different way. This will result in Theta = ## [-90:ThetaResolution:90], with the +90 angle excluded. ## ## @end table ## ## @seealso{hough_line, hough_circle, immaximas} ## @end deftypefn ## TODO: add option RhoResolution ## ## @item @qcode{"RhoResolution"} ## A scalar value to specify the Resolution of the @var{rho} distance ## values. Defaults to 1. function [H, theta, rho] = hough (bw, varargin) if (nargin < 1) print_usage (); endif validateattributes (bw, {"logical", "numeric"}, {"2d"}, "hough", "BW"); bw = logical (bw); ## set default parameters: theta = [-90:1:89]; theta_res = 1; ## process property/value pairs if (rem (numel (varargin), 2) != 0) error ("hough: PROPERTY/VALUE arguments must occur in pairs"); endif for idx = 1:2:(numel (varargin)) switch (tolower (varargin{idx})) case "rhoresolution" rho_res = varargin{idx+1}; ## This option is not yet implemented. The default will be 1 ## so error out if the user tries to set it to anything else. if (rho_res != 1) error ("hough: option RHORESOLUTION is not implemented"); endif case "thetaresolution" theta_res = varargin{idx+1}; if (! (isreal (theta_res) && isscalar (theta_res) && (theta_res > 0) && (theta_res < 180))) error ("hough: value THETARESOLUTION must be between 0 and 180"); endif case "theta" theta = varargin{idx+1}; if (! (isreal (theta) && isvector (theta))) error ("hough: values THETA must be a vector of real numbers"); endif otherwise error ("hough: unknown property `%s'", varargin{idx}); endswitch endfor if (theta_res != 1) theta = [-90:theta_res:90]; theta = theta(theta != 90); # exclude +90 degrees endif ## Matlab's hough.m function measures the angle theta clockwise to the ## vertical axis, but Octave's hough_line.cc measure this angle (as usual) ## counter-clockwise the the horizontal axis. Octave's hough_line also ## need radians, Matlab uses degrees. So we translate this. theta_oct = (-theta+90) * (pi/180); ## eventually call hough_line.cc to do the real work [H, rho] = hough_line (bw, theta_oct); endfunction %!shared BW0, BW1, BW2, BWx, BWy %! %! BW0 = false (5); %! BW0(2,2) = true; %! %! BW1 = zeros (100, 100); %! BW1(1,1) = 1; %! BW1(100,100) = 1; %! BW1(1,100) = 1; %! BW1(100, 1) = 1; %! BW1(50,50) = 1; %! %! n = 100; %! BW2 = false (n); %! a = 50; # line starts at left side at row a %! b = 3; # slope of line is 1:b %! for column = 1:n %! if (rem (column, b) == 0) %! row = a - column/b; %! BW2(row, column) = true; %! endif %! endfor %! %! BWx = false (10); %! BWx(:,5) = true; %! %! BWy = false (10); %! BWy(5,:) = true; %!test %! [H, T, R] = hough (BW1); %! assert (size (H), [283 180]); %!test %! [H, T, R] = hough (BW1, "Theta", [-90 0 45 79]); %! assert (size (H), [283 4]); %!test %! [H, T, R] = hough (BW1, "ThetaResolution", 0.5); %! assert (size (H), [283 360]); %!error hough ("foo") ## Non binary data, just gets cast to logical, not even rounding. %!test %! I = [0 0 1 0; 1 1 1 1; 0 0 1 1; 0 0 1 0]; %! I2 = I; %! for v = [0.7 0.2 5] %! I2(1,3) = v; %! assert (hough (I2), hough (I)) %! endfor %!error %! [H, T, R] = hough (BW0, "Theta"); %!error %! [H, T, R] = hough (BW0, "Theta", ones (10)); %!error %! [H, T, R] = hough (BW0, "Theta", [5 -i 7]); %!error %! [H, T, R] = hough (BW0, "RhoResolution", 0.5); ## RhoResolution defaults to 1 %!test %! [Hd, Td, Rd] = hough (BW0); %! [H1, T1, R1] = hough (BW0, "RhoResolution", 1); %! assert (Hd, H1) %! assert (Td, T1) %! assert (Rd, R1) %!test %! [H, theta, rho] = hough (BW2); %! H_max = max (H(:)); %! H_size = size (H); %! [~, max_idx_lin] = max (H(:)); %! [max_row, max_column] = ind2sub (size (H), max_idx_lin); %! theta_max = theta(max_column); %! rho_max = rho(max_row); %! assert (H_max , 33); %! assert (H_size, [283 180]); %! assert (max_row, 188); %! assert (max_column, 163); %! assert (theta_max, 72); %! assert (rho_max, 46); %!test %! [H, theta, rho] = hough (BW2, "Theta", [65:1:75]); %! H_max = max (H(:)); %! H_size = size (H); %! [~, max_idx_lin] = max (H(:)); %! [max_row, max_column] = ind2sub (size (H), max_idx_lin); %! theta_max = theta(max_column); %! rho_max = rho(max_row); %! assert (H_max , 33); %! assert (H_size, [283 11]); %! assert (max_row, 188); %! assert (max_column, 8); %! assert (theta_max, 72); %! assert (rho_max, 46); %!test %! [H, theta, rho] = hough (BW2, "Theta", [-90:0.5:89.5]); %! H_max = max (H(:)); %! H_size = size (H); %! [~, max_idx_lin] = max (H(:)); %! [max_row, max_column] = ind2sub (size (H), max_idx_lin); %! theta_max = theta(max_column); %! rho_max = rho(max_row); %! assert (H_max , 33); %! assert (H_size, [283 360]); %! assert (max_row, 188); %! assert (max_column, 324); %! assert (theta_max, 71.5); %! assert (rho_max, 46); %!test %! [H, theta, rho] = hough (BW2, "ThetaResolution", 0.5); %! H_max = max (H(:)); %! H_size = size (H); %! [~, max_idx_lin] = max (H(:)); %! [max_row, max_column] = ind2sub (size (H), max_idx_lin); %! theta_max = theta(max_column); %! rho_max = rho(max_row); %! assert (H_max , 33); %! assert (H_size, [283 360]); %! assert (max_row, 188); %! assert (max_column, 324); %! assert (theta_max, 71.5); %! assert (rho_max, 46); %!test %! [H, theta, rho] = hough (BWx); %! H_max = max (H(:)); %! [~, max_idx_lin] = max (H(:)); %! [max_row, max_column] = ind2sub (size (H), max_idx_lin); %! theta_max = theta(max_column); %! rho_max = rho(max_row); %! assert (H_max , 10); %! assert (max_column, 88); %! assert (theta_max, -3); %! assert (rho_max, 4); %!test %! [H, theta, rho] = hough (BWx); %! H_size = size (H); %! [~, max_idx_lin] = max (H(:)); %! [max_row, max_column] = ind2sub (size (H), max_idx_lin); %! assert (H_size, [27 180]); %! assert (max_row, 18); %!test %! [H, theta, rho] = hough (BWy); %! H_max = max (H(:)); %! [~, max_idx_lin] = max (H(:)); %! [max_row, max_column] = ind2sub (size (H), max_idx_lin); %! theta_max = theta(max_column); %! rho_max = rho(max_row); %! assert (H_max , 10); %! assert (max_column, 1); %! assert (theta_max, -90); %! assert (rho_max, -4); %!test %! [H, theta, rho] = hough (BWy); %! H_size = size (H); %! [~, max_idx_lin] = max (H(:)); %! [max_row, max_column] = ind2sub (size (H), max_idx_lin); %! assert (H_size, [27 180]); %! assert (max_row, 10); %!demo %! BW = zeros (100, 150); %! BW(30,:) = 1; %! BW(:, 65) = 1; %! BW(35:45, 35:50) = 1; %! for i = 1:90 %! BW(i,i) = 1; %! endfor %! BW = imnoise (BW, "salt & pepper"); %! figure (); %! imshow (BW); %! title ("BW"); %! [H, theta, rho] = hough (BW); %! H /= max (H(:)); %! figure (); %! imshow (H, "XData", theta, "YData", rho); %! title ("hough transform of BW"); %! axis on; %! xlabel ("angle \\theta [degrees]"); %! ylabel ("distance \\rho to origin [pixels]"); image-2.16.1/inst/PaxHeaders.61586/montage.m0000644000000000000000000000006215005110255015212 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/montage.m0000644000175000017500000003441715005110255016621 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} montage (@var{I}) ## @deftypefnx {Function File} {} montage (@var{X}, @var{cmap}) ## @deftypefnx {Function File} {} montage (@var{filenames}) ## @deftypefnx {Function File} {} montage (@dots{}, @var{param1}, @var{value1}, @dots{}) ## @deftypefnx {Function File} {@var{h} =} montage (@dots{}) ## Create montage from multiple images. ## ## The created montage will be of a single large image built from the 4D matrix ## @var{I}. @var{I} must be a MxNx1x@var{P} or MxNx3x@var{P} matrix for a ## grayscale and binary, or RGB image with @var{P} frames. ## ## Alternatively, @var{X} can be a MxNx1x@var{P} indexed image with @var{P} ## frames, with the colormap @var{cmap}, or a cell array of @var{filenames} ## for multiple images. ## ## @table @asis ## @item DisplayRange ## A vector with 2 or 0 elements setting the highest and lowest value for ## display range. It is interpreted like the @var{limits} argument to ## @code{imshow}. Of special significance is that an empty array uses the ## image minimum and maximum values for limits. Defaults to the limits of ## the image data type, i.e., the range returned by @code{getrangefromclass}. ## ## @item Indices ## A vector with the image indices to be displayed. Defaults to all images, ## i.e., @code{1:size (@var{I}, 4)}. ## ## @item Size ## Sets the montage layout size. Must be a 2 element vector setting ## [@var{nRows} @var{nCols}]. A value of NaN will be adjusted to the required ## value to display all images. If both values are NaN (default), it will ## find the most square layout capable of displaying all of the images. ## ## @item MarginColor ## Sets color for the margins between panels. Defaults to white. Must be a ## 1 or 3 element vector for grayscale or RGB images. ## ## @item MarginWidth ## Sets width for the margins between panels. Defaults to 0 pixels. Note that ## the margins are only between panels. ## ## @item BackgroundColor ## Sets the montage background color. Defaults to black. Must be a ## 1 or 3 element vector for grayscale or RGB images. This will only affect ## montages with more panels than images. ## @end table ## ## The optional return value H is a graphics handle to the created plot. ## ## @seealso{imshow, padarray, permute, reshape} ## @end deftypefn function h = montage (images, varargin) if (nargin < 1) print_usage (); endif if (iscellstr (images)) ## we are using cellfun instead of passing the "all" option ## to file_in_loadpath, so we know which of the figures was ## not found, and provide a more meaningful error message fullpaths = cellfun (@file_in_loadpath, images(:), "UniformOutput", false); lost = cellfun (@isempty, fullpaths); if (any (lost)) badpaths = strjoin (images(:)(lost), "\n"); error ("montage: unable to find files:\n%s", badpaths); endif ## supporting grayscale, indexed, and truecolor images in ## the same list, complicates things a bit. Also, don't forget ## some images may be multipage infos = cellfun (@imfinfo, fullpaths, "UniformOutput", false); nImg = sum (cellfun (@numel, infos)); # number of images ## in case of multipage images, different pages may have different sizes, ## so we must check the height and width of each page of each image height = infos{1}(1).Height; width = infos{1}(1).Width; if (any (cellfun (@(x) any (arrayfun (@(y) y.Height != height || y.Width != width, x)), infos))) error ("montage: all images must have the same size."); endif ## To save on memory, we find the image with highest bitdepth, and ## create a matrix with the correct dimensions, where the images will ## be read into. We could read all the images and maps with a single ## cellfun call, and that would be a bit simpler than deducing ## from imfinfo but then we'd end up taking up the double of memory ## as we reorganize and convert the images ## a multipage image could have a mixture of colortypes, so we must ## check the type of every single one. If they are all grayscale, that's ## nice, it will be one dimension less, otherwise 3rd dimension is for ## rgb values. Also, any indexed image will be later converted to ## truecolor with rgb2ind so it will count as double if (any (cellfun (@(x) any (strcmp ({x(:).ColorType}, "indexed")), infos))) cl = "double"; convt = @im2double; else maxbd = max (cellfun (@(x) max ([x(:).BitDepth]), infos)); switch (maxbd) case {8} cl = "uint8"; convt = @im2uint8; case {16} ## includes both uint18 and int16 cl = "uint16"; convt = @im2uint16; otherwise ## includes maxbd == 32 plus anything else. In case of anything ## unexpected, better play safe and use the one with more precision cl = "double"; convt = @im2double; endswitch endif if (all (cellfun (@(x) all (strcmp ({x(:).ColorType}, "grayscale")), infos))) images = zeros (height, width, 1, nImg, cl); else images = zeros (height, width, 3, nImg, cl); endif nRead = 0; # count of pages already read for idx = 1:numel (infos) img_info = infos{idx}; nPages = numel (img_info); nRead += nPages; page_range = nRead+1-nPages:nRead; filepath = img_info(1).Filename; ## we won't be handling the alpha channel, but matlab doesn't either if (size (images, 3) == 1 || all (strcmp ({img_info(:).ColorType}, "truecolor"))) ## sweet, no problems for sure [images(:,:,:,page_range), map] = imread (filepath, 1:nPages); else [tmp_img, map] = imread (filepath, 1:nPages); if (! isempty (map)) ## an indexed image. According to TIFF 6 specs, an indexed ## multipage image can have different colormaps for each page. ## How does imread behave with such images? And do they actually ## exist out there? tmp_img = ind2rgb (ind, map) elseif (size (tmp_img, 3) == 1) ## must be a grayscale image, propagate values to all channels tmp_img = repmat (tmp_img, [1 1 3 nPages]) else ## must be a truecolor image, do nothing endif images(:,:,:,page_range) = tmp_img; endif endfor ## we can't really distinguish between a multipage indexed and normal ## image. So we'll assume it's an indexed if there's a second argument ## that is a colormap elseif (isimage (images) && nargin > 1 && iscolormap (varargin{1})) images = ind2rgb (images, varargin{1}); varargin(1) = []; elseif (isimage (images)) ## all is nice else print_usage (); endif [height, width, channels, nImg] = size (images); p = inputParser (); p.FunctionName = "montage"; ## FIXME: inputParser was first implemented in the general package in the ## old @class type which allowed for a very similar interface to ## Matlab. classdef was implemented in the upcoming 4.0 release, ## which enabled inputParser to be implemented exactly the same and ## it is now part of Octave core. To prevent issues while all this ## versions are available, we check if the inputParser being used ## is in a @inputParser directory. ## ## Remove all this checks once the general package has been released ## again without the @inputParser class im_type_range = getrangefromclass (images); if (strfind (which ("inputParser"), ["@inputParser" filesep "inputParser.m"])) p = p.addParamValue ("DisplayRange", im_type_range, @(x) isnumeric (x) && any (numel (x) == [0 2])); p = p.addParamValue ("Indices", 1:nImg, @(x) isindex (x, nImg)); p = p.addParamValue ("Size", [NaN NaN], @(x) isnumeric (x) && numel (x) == 2); p = p.addParamValue ("MarginWidth", 0, @(x) isnumeric (x) && isscalar (x)); p = p.addParamValue ("MarginColor", im_type_range(2), @(x) isnumeric (x) && any (numel (x) == [1 3])); p = p.addParamValue ("BackgroundColor", im_type_range(1), @(x) isnumeric (x) && any (numel (x) == [1 3])); p = p.parse (varargin{:}); else p.addParamValue ("DisplayRange", im_type_range, @(x) isnumeric (x) && any (numel (x) == [0 2])); p.addParamValue ("Indices", 1:nImg, @(x) isindex (x, nImg)); p.addParamValue ("Size", [NaN NaN], @(x) isnumeric (x) && numel (x) == 2); p.addParamValue ("MarginWidth", 0, @(x) isnumeric (x) && isscalar (x)); p.addParamValue ("MarginColor", im_type_range(2), @(x) isnumeric (x) && any (numel (x) == [1 3])); p.addParamValue ("BackgroundColor", im_type_range(1), @(x) isnumeric (x) && any (numel (x) == [1 3])); p.parse (varargin{:}); endif ## remove unnecessary images images = images(:,:,:,p.Results.Indices); nImg = size (images, 4); ## 1) calculate layout of the montage nRows = p.Results.Size(1); nCols = p.Results.Size(2); if (isnan (nRows) && isnan (nCols)) ## We must find the smallest layout that is most square. The most square ## are the ones with smallest difference between height and width. And to ## find the smallest layout for each number of columns, is the one that ## requires less rows. The smallest layout will be used as a mask to choose ## the minimum from hxW_diff v_heights = cumsum (linspace (height, nImg*height, nImg)); v_widths = cumsum (linspace (width, nImg*width, nImg)); HxW_diff = abs (v_heights' - v_widths); small_layout = ((1:nImg)' .* (1:nImg)) >= nImg; small_layout = logical (diff (padarray (small_layout, 1, 0, "pre"))); HxW_diff(! small_layout) = Inf; ## When there is more than one layout that returns equal "squariness", ## we must pick the one with most columns. This is because monitors ## have more horizontal space, so a wider image will be better. Hence ## the "last". [nRows, nCols] = find (HxW_diff == min (HxW_diff(:)), 1, "last"); elseif (isnan (nRows)) nRows = ceil (nImg/nCols); elseif (isnan (nCols)) nCols = ceil (nImg/nRows); elseif (nCols * nRows < nImg) error ("montage: size of %ix%i is not enough for image with %i frames.", nRows, nCols, nImg); endif ## 2) build the image margin_width = p.Results.MarginWidth; back_color = fix_color (p.Results.BackgroundColor, channels); disp_img = zeros (height*nRows + margin_width*(nRows-1), width *nCols + margin_width*(nCols-1), channels, class (images)) + back_color; ## find the start and end coordinates for each of the images xRows = start_end (nRows, height, margin_width); xCols = start_end (nCols, width, margin_width); ## Using reshape and permute to build the final image turned out to ## be quite a problem. So yeah... we'll use a for loop. Anyway, the number ## of images on a montage is never very high, this function is unlikely ## to be the speed bottleneck for any usage, and will make the code ## much more readable. iRow = iCol = 1; for iImg = 1:nImg if (iCol > nCols) iCol = 1; iRow++; endif rRow = xRows(1,iRow):xRows(2,iRow); # range of rows rCol = xCols(1,iCol):xCols(2,iCol); # range of columns disp_img(rRow,rCol,:) = images(:,:,:,iImg); iCol++; endfor ## 3) color margins as required margin_color = fix_color (p.Results.MarginColor, channels); if (margin_width > 0 && any (margin_color != back_color)) mRows = linspace (xRows(2,1:end-1) +1, xRows(1,2:end) -1, margin_width) (:); mCols = linspace (xCols(2,1:end-1) +1, xCols(1,2:end) -1, margin_width) (:); ## a function that can be used to brodcast assignment bd_ass = @(x, y) subsasgn (x, struct ("type", "()", "subs", {{":"}}), y); disp_img(mRows,:,:) = bsxfun (bd_ass, disp_img(mRows,:,:), margin_color); disp_img(:,mCols,:) = bsxfun (bd_ass, disp_img(:,mCols,:), margin_color); endif ## 4) display the image tmp_h = imshow (disp_img, p.Results.DisplayRange); if (nargout > 0) h = tmp_h; endif endfunction ## given number of elements (n), the length or each, and the border length ## between then, returns start and end coordinates for each of the elements function [coords] = start_end (n, len, bord) coords = bord * (0:(n-1)) + len * (0:(n-1)) + 1; coords(2,:) = coords(1,:) + len - 1; endfunction ## color values can be given in grayscale or RGB values and may not match ## the values of the image. This function will make the color match the ## image and give a 1x1x(1||3) vector that can be used for broadcasting function color = fix_color (color, img_channels) if (numel (color) != img_channels) if (img_channels == 3) color = repmat (color, [3 1]); elseif (img_channels == 1) color = rgb2gray (reshape (color, [1 1 3])); else error ("montage: image has an unknown number (%d) of channels.", img_channels); endif endif color = reshape (color, [1 1 img_channels]); endfunction %!function cdata = montage_cdata (varargin) %! h = figure (); %! set (h, "visible", "off"); %! mh = montage (varargin{:}); %! cdata = get (mh, "cdata"); %! close (h); %!endfunction ## Test automatic distribution of panels %!test %! im = uint8 (ones (2, 2, 1, 5)) .* reshape ([1 2 3 4 5], [1 1 1 5]); %! cdata = montage_cdata (im); %! expected = uint8 ([ %! 1 1 2 2 3 3 %! 1 1 2 2 3 3 %! 4 4 5 5 0 0 %! 4 4 5 5 0 0 %! ]); %! assert (cdata, expected) %!test %! im = uint8 (ones (2, 4, 1, 6)) .* reshape ([1 2 3 4 5 6], [1 1 1 6]); %! cdata = montage_cdata (im); %! expected = uint8 ([ %! 1 1 1 1 2 2 2 2 %! 1 1 1 1 2 2 2 2 %! 3 3 3 3 4 4 4 4 %! 3 3 3 3 4 4 4 4 %! 5 5 5 5 6 6 6 6 %! 5 5 5 5 6 6 6 6 %! ]); %! assert (cdata, expected) image-2.16.1/inst/PaxHeaders.61586/fftconv2.m0000644000000000000000000000006215005110255015307 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/fftconv2.m0000644000175000017500000001304115005110255016704 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013-2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## ## This file incorporates work covered by the following copyright and ## permission notice: ## ## Copyright (C) 2004 Stefan van der Walt ## All rights reserved. ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions are met: ## ## 1 Redistributions of source code must retain the above copyright notice, ## this list of conditions and the following disclaimer. ## 2 Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. ## ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' ## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ## ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ## ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ## DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ## CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## -*- texinfo -*- ## @deftypefn {Function File} {} fftconv2 (@var{a}, @var{b}) ## @deftypefnx {Function File} {} fftconv2 (@var{v1}, @var{v2}, @var{a}) ## @deftypefnx {Function File} {} fftconv2 (@dots{}, @var{shape}) ## Convolve 2 dimensional signals using the FFT. ## ## This method is faster but less accurate than @var{conv2} for large @var{a} ## and @var{b}. It also uses more memory. A small complex component will be ## introduced even if both @var{a} and @var{b} are real. ## @seealso{conv2, fftconv, fft, ifft} ## @end deftypefn function X = fftconv2 (varargin) if (nargin < 2) print_usage (); endif nargs = nargin; # nargin minus the shape option if (ischar (varargin{end})) shape = varargin{end}; nargs--; else shape = "full"; endif rowcolumn = false; if (nargs == 2) ## usage: fftconv2(a, b[, shape]) a = varargin{1}; b = varargin{2}; elseif (nargs == 3) ## usage: fftconv2 (v1, v2, a[, shape]) rowcolumn = true; if (! isnumeric (varargin{3}) && ! islogical (varargin{3})) error ("fftconv2: A must be a numeric or logical array"); endif v1 = vec (varargin{1}); v2 = vec (varargin{2}, 2); orig_a = varargin{3}; else print_usage (); endif if (rowcolumn) a = fftconv2 (orig_a, v2); b = v1; endif ra = rows(a); ca = columns(a); rb = rows(b); cb = columns(b); A = fft2 (padarray (a, [rb-1 cb-1], "post")); B = fft2 (padarray (b, [ra-1 ca-1], "post")); X = ifft2 (A.*B); if (rowcolumn) rb = rows (v1); ra = rows (orig_a); cb = columns (v2); ca = columns (orig_a); endif switch (tolower (shape)) case "full", ## do nothing case "same", r_top = ceil ((rb + 1) / 2); c_top = ceil ((cb + 1) / 2); X = X(r_top:r_top + ra - 1, c_top:c_top + ca - 1); case "valid", X = X(rb:ra, cb:ca); otherwise error ("fftconv2: unknown convolution SHAPE `%s'", shape); endswitch endfunction ## usage: fftconv2(a,b,[, shape]) %!test %! a = repmat (1:10, 5); %! b = repmat (10:-1:3, 7); %! assert (fftconv2 (a, b), conv2 (a, b), 1.8e4*eps) %! assert (fftconv2 (b, a), conv2 (b, a), 1.8e4*eps) %! assert (fftconv2 (a, b, "full"), conv2 (a, b, "full"), 1.8e4*eps) %! assert (fftconv2 (b, a, "full"), conv2 (b, a, "full"), 1.8e4*eps) %! assert (fftconv2 (a, b, "same"), conv2 (a, b, "same"), 1.8e4*eps) %! assert (fftconv2 (b, a, "same"), conv2 (b, a, "same"), 1.8e4*eps) %! assert (isempty (fftconv2 (a, b, "valid"))); %! assert (fftconv2 (b, a, "valid"), conv2 (b, a, "valid"), 1e4*eps) ## usage: fftconv2(v1, v2, a[, shape]) %!test %! x = 1:4; %! y = 4:-1:1; %! a = repmat(1:10, 5); %! assert (fftconv2 (x, y, a), conv2 (x, y, a), 1e4*eps) %! assert (fftconv2 (x, y, a, "full"), conv2 (x, y, a, "full"), 1e4*eps) %! assert (fftconv2 (x, y, a, "same"), conv2 (x, y, a, "same"), 1e4*eps) %! assert (fftconv2 (x, y, a, "valid"), conv2 (x, y, a, "valid"), 1e4*eps) %!demo %! ## Draw a cross %! z = zeros (101, 101); %! z(50, :) = 1; %! z(:, 50) = 1; %! subplot (1, 3, 1) %! imshow (z); %! title ("Original thin cross") %! %! ## Draw a sinc blob %! b = getheight (strel ("ball", 10, 1)); %! subplot (1, 3, 2) %! imshow (b); %! title ("Sync blob") %! %! ## Convolve the cross with the blob %! fc = real (fftconv2 (z, b, "same")); %! subplot (1, 3, 3) %! imshow (fc, [min(fc(:)) max(fc(:))]) %! title ("Convolution in the frequency domain") image-2.16.1/inst/PaxHeaders.61586/ordfiltn.m0000644000000000000000000000006215005110255015401 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/ordfiltn.m0000644000175000017500000001140015005110255016773 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} ordfiltn (@var{A}, @var{nth}, @var{domain}) ## @deftypefnx {Function File} {} ordfiltn (@var{A}, @var{nth}, @var{domain}, @var{S}) ## @deftypefnx {Function File} {} ordfiltn (@dots{}, @var{padding}) ## N dimensional ordered filtering. ## ## Ordered filter replaces an element of @var{A} with the @var{nth} element ## element of the sorted set of neighbours defined by the logical ## (boolean) matrix @var{domain}. ## Neighbour elements are selected to the sort if the corresponding ## element in the @var{domain} matrix is true. ## ## The optional variable @var{S} is a matrix of size(@var{domain}). ## Values of @var{S} corresponding to nonzero values of domain are ## added to values obtained from @var{A} when doing the sorting. ## ## Optional variable @var{padding} determines how the matrix @var{A} ## is padded from the edges. See @code{padarray} for details. ## ## @seealso{medfilt2, padarray, ordfilt2} ## @end deftypefn ## This function is based on 'ordfilt2' by Teemu Ikonen ## which is released under GPLv2 or later. function retval = ordfiltn (A, nth, domain, varargin) ## Check input if (nargin < 3) print_usage (); elseif (! isnumeric (A) && ! islogical (A)) error ("ordfiltn: A must be a numeric or logical array"); elseif (! isscalar (nth) || nth <= 0 || fix (nth) != nth) error ("ordfiltn: second input argument must be a positive integer"); elseif (! isnumeric (domain) && ! islogical (domain)) error ("ordfiltn: DOMAIN must be a numeric or logical array or scalar"); elseif (isscalar (domain) && (domain <= 0 || fix (domain) != domain)) error ("ordfiltn: third input argument must be a positive integer, when it is a scalar"); endif if (isscalar (domain)) domain = true (repmat (domain, 1, ndims (A))); endif if (ndims (A) != ndims (domain)) error ("ordfiltn: first and second argument must have same dimensionality"); elseif (any (size (A) < size (domain))) error ("ordfiltn: domain array cannot be larger than the data array"); endif ## Parse varargin S = zeros (size (domain)); padding = 0; for idx = 1:length(varargin) opt = varargin{idx}; if (ischar (opt) || isscalar (opt)) padding = opt; elseif (isnumeric (opt) && size_equal (opt, domain)) S = opt; else error ("ordfiltn: unrecognized option from input argument #%i and class %s", 3 + idx, class (opt)); endif endfor A = pad_for_sliding_filter (A, size (domain), padding); ## Perform the filtering retval = __spatial_filtering__ (A, logical (domain), "ordered", S, nth); endfunction %!shared b, f, s %! b = [ 0 1 2 3 %! 1 8 12 12 %! 4 20 24 21 %! 7 22 25 18]; %! %! f = [ 8 12 12 12 %! 20 24 24 24 %! 22 25 25 25 %! 22 25 25 25]; %!assert (ordfiltn (b, 9, true (3)), f); %! %! f = [ 1 8 12 12 %! 8 20 21 21 %! 20 24 24 24 %! 20 24 24 24]; %!assert (ordfiltn (b, 8, true (3)), f); %! %! f = [ 1 2 8 12 %! 4 12 20 21 %! 8 22 22 21 %! 20 24 24 24]; %!assert (ordfiltn (b, 7, true (3), "symmetric"), f); %! %! f = [ 1 8 12 12 %! 4 20 24 21 %! 7 22 25 21 %! 7 22 25 21]; %!assert (ordfiltn (b, 3, true (3, 1)), f); %! %! f = [ 1 8 12 12 %! 4 20 24 18 %! 4 20 24 18 %! 4 20 24 18]; %!assert (ordfiltn (b, 3, true (4, 1)), f); %! %! f = [ 4 20 24 21 %! 7 22 25 21 %! 7 22 25 21 %! 7 22 25 21]; %!assert (ordfiltn (b, 4, true (4, 1)), f); %! %! s = [0 0 1 %! 0 0 1 %! 0 0 1]; %! f = [ 2 8 12 12 %! 9 20 22 21 %! 21 25 24 24 %! 21 25 24 24]; %!assert (ordfiltn (b, 8, true (3), s), f); %! %! b(:,:,2) = b(:,:,1) - 1; %! b(:,:,3) = b(:,:,2) - 1; %! f(:,:,1) = [ 1 8 11 11 %! 8 20 21 21 %! 20 24 24 24 %! 20 24 24 24]; %! f(:,:,2) = [ 6 10 11 11 %! 18 22 22 22 %! 20 24 24 24 %! 20 24 24 24]; %! f(:,:,3) = [ 0 7 10 10 %! 7 19 20 20 %! 19 23 23 23 %! 19 23 23 23]; %!assert (ordfiltn (b, 25, true (3, 3, 3)), f); image-2.16.1/inst/PaxHeaders.61586/imtophat.m0000644000000000000000000000006215005110255015405 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imtophat.m0000644000175000017500000001530715005110255017011 0ustar00avinoamavinoam00000000000000## Copyright (C) 2005 Carvalho-Mariel ## Copyright (C) 2010-2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imtophat (@var{img}, @var{SE}) ## Perform morphological top hat filtering. ## ## The matrix @var{img} must be numeric while @var{SE} can be a: ## @itemize @bullet ## @item ## strel object; ## @item ## array of strel objects as returned by `@@strel/getsequence'; ## @item ## matrix of 0's and 1's. ## @end itemize ## ## A top hat transform corresponds to the difference between @var{img}, ## and the opening of @var{img}, i.e., it is equivalent to: ## @example ## img - imopen (img, se); ## @end example ## ## Use @code{imbothat} to perform a 'black' or 'closing', top-hat transform ## (is is also known as bottom-hat transform). ## ## @seealso{imerode, imdilate, imopen, imclose, imbothat, mmgradm} ## @end deftypefn function white = imtophat (img, se) if (nargin != 2) print_usage (); elseif (! isimage (img)) error("imtophat: IMG must be a numeric matrix"); endif se = prepare_strel ("imtophat", se); ## Perform filtering ## Note that in case that the transform is to applied to a logical image, ## subtraction must be handled in a different way (x & !y) instead of (x - y) ## or it will return a double precision matrix if (islogical (img)) white = img & ! imopen (img, se); else white = img - imopen (img, se); endif endfunction %!assert (imtophat (ones (3), [1 1; 0 1]), zeros (3)); %!assert (imtophat (true (3), [1 1; 0 1]), false (3)); %!shared in, out, se %! in = [ 0 0 0 1 1 1 0 0 1 1 %! 0 1 0 1 1 1 0 0 0 1 %! 1 1 1 1 1 0 0 0 0 0 %! 0 1 1 1 1 0 0 0 0 0 %! 0 0 0 1 0 0 0 0 1 0 %! 0 0 0 0 0 0 0 1 1 1 %! 0 0 0 0 1 0 1 0 1 0 %! 0 0 0 1 1 1 1 1 0 0 %! 0 0 0 0 1 1 1 0 0 0 %! 0 0 0 1 1 1 0 0 0 0]; %! %! out = [ 0 0 0 0 0 0 0 0 1 1 %! 0 1 0 0 0 0 0 0 0 1 %! 1 1 1 1 1 0 0 0 0 0 %! 0 1 1 1 1 0 0 0 0 0 %! 0 0 0 1 0 0 0 0 1 0 %! 0 0 0 0 0 0 0 1 1 1 %! 0 0 0 0 1 0 1 0 1 0 %! 0 0 0 1 1 1 1 1 0 0 %! 0 0 0 0 1 1 1 0 0 0 %! 0 0 0 1 1 1 0 0 0 0]; %!assert (imtophat (logical (in), ones (3)), logical (out)); %! %! out = [12 19 0 0 0 16 23 0 7 0 %! 18 0 0 6 1 19 0 2 9 1 %! 0 74 81 12 7 0 1 8 15 7 %! 68 70 2 14 0 6 7 14 16 0 %! 69 76 8 0 0 7 14 21 0 1 %! 0 7 59 54 61 13 20 0 0 32 %! 18 0 69 60 62 19 0 0 0 27 %! 73 0 0 66 68 0 1 6 6 33 %! 0 0 17 19 1 0 2 9 7 14 %! 1 6 23 0 7 1 8 15 0 32]; %!assert (imtophat (magic (10), ones (3)), out); %!assert (imtophat (uint8 (magic (10)), strel ("square", 3)), uint8 (out)); %! %! ## using a se that will be decomposed in 2 pieces %! out =[91 98 0 0 0 27 34 11 18 0 %! 94 76 3 6 1 33 15 17 24 1 %! 0 77 84 12 7 14 16 23 30 7 %! 80 82 14 18 0 32 34 41 43 0 %! 81 88 20 0 0 33 40 47 24 6 %! 12 19 63 57 64 16 23 0 7 39 %! 18 0 69 60 62 19 1 3 12 39 %! 73 0 0 66 68 0 2 9 18 45 %! 4 6 81 67 49 6 8 15 19 26 %! 5 12 87 48 55 7 14 21 0 32]; %!assert (imtophat (magic (10), ones(5)), out); %! %! ## using a weird non-symmetric and even-size se %! out =[85 92 0 0 0 12 23 0 17 0 %! 91 73 0 6 0 18 0 2 13 0 %! 0 72 81 13 6 0 1 9 15 0 %! 60 62 10 12 0 8 8 17 17 0 %! 61 69 0 0 0 28 16 41 0 0 %! 0 0 47 52 61 12 16 0 0 31 %! 6 0 53 58 60 17 0 0 0 33 %! 69 0 0 60 62 0 0 6 0 33 %! 0 0 17 60 42 0 2 13 1 8 %! 0 6 23 0 7 0 7 15 0 14]; %!assert (imtophat (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out); %! %! ## N dimensional and weird se %! in = reshape (magic(16), [4 8 4 2]); %! se = ones (3, 3, 3); %! se(:,:,1) = [1 0 1; 0 1 1; 0 0 0]; %! se(:,:,3) = [1 0 1; 0 1 1; 0 0 1]; %! out = zeros (size (in)); %! out(:,:,1,1) = [ %! 239 146 82 18 0 19 83 133 %! 0 35 99 163 219 128 64 0 %! 0 46 128 195 187 123 59 0 %! 157 93 47 0 14 78 142 211]; %! out(:,:,2,1) = [ %! 0 21 85 149 233 146 64 0 %! 205 128 64 0 0 41 87 151 %! 171 107 57 0 0 64 121 185 %! 0 64 142 213 169 105 41 0]; %! out(:,:,3,1) = [ %! 231 146 78 14 0 27 77 137 %! 0 43 107 167 211 128 64 0 %! 0 46 128 199 179 119 51 0 %! 149 85 39 0 18 78 142 219]; %! out(:,:,4,1) = [ %! 0 29 93 157 225 128 64 0 %! 197 128 64 0 0 31 95 159 %! 163 99 53 0 0 61 125 189 %! 0 64 146 221 161 97 33 0]; %! out(:,:,1,2) = [ %! 223 146 82 18 0 35 99 149 %! 0 48 115 179 203 128 64 0 %! 0 46 128 211 171 107 43 0 %! 141 77 31 0 14 78 142 227]; %! out(:,:,2,2) = [ %! 0 37 101 165 217 146 64 0 %! 189 125 64 0 0 57 103 167 %! 155 91 41 0 0 64 128 201 %! 0 64 142 229 153 89 25 0]; %! out(:,:,3,2) = [ %! 215 146 78 14 0 43 93 153 %! 0 48 123 183 195 128 64 0 %! 0 46 128 215 163 103 35 0 %! 133 69 23 0 18 78 142 235]; %! out(:,:,4,2) = [ %! 0 45 109 173 209 128 64 0 %! 181 117 64 0 0 47 111 175 %! 147 83 37 0 0 64 128 205 %! 0 64 146 237 145 81 17 0]; %!assert (imtophat (in, se), out); image-2.16.1/inst/PaxHeaders.61586/tiff_tag_read.m0000644000000000000000000000006215005110255016336 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/tiff_tag_read.m0000644000175000017500000003326615005110255017746 0ustar00avinoamavinoam00000000000000## Copyright (C) 2010-2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag}) ## @deftypefnx {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag}, @var{ifd}) ## @deftypefnx {Function File} {[@var{value}, @var{offset}] =} tiff_tag_read (@var{file}, @var{tag}, "all") ## Read value of @var{tag}s from TIFF files. ## ## @var{file} must be a TIFF file and @var{tag} should be a tag ID. To check ## multiple tags, @var{tag} can be a vector. If @var{ifd} is supplied, only ## those IFDs (Image File Directory) will be read. As with @var{tag}, multiple ## IFDs can be checked by using a vector or with the string `all'. By default, ## only the first IFD is read. ## ## @var{value} and @var{offset} will be a matrix with a number of rows and ## columns equal to the number of @var{tag}s and @var{ifd}s requested. The index ## relate to the same order as the input. @var{offset} has the same structure as ## @var{value} and when equal to 1 its matching value on @var{value} will be an ## offset to a position in the file. ## ## @var{tag}s that can't be found will have a value of 0 and the corresponding ## @var{offset} will be 2. ## ## If an error occurs when reading @var{file} (such as lack of permissions of file ## is not a TIFF file), @var{offset} is set to -1 and @var{value} contains the ## error message. ## ## See the following examples: ## @example ## @group ## ## read value of tag 258 on IFD 1 (`off' will be 1 if `val' is an offset or 2 if not found) ## [val, off] = tiff_tag_read (filepath, 258); ## @end group ## @end example ## ## @example ## @group ## ## read value 258, 262, 254 o IFD 1 (`val' and `off' will be a 1x3 matrix) ## [val, off] = tiff_tag_read (filepath, [258 262 254]); ## if (off(1) == -1), error ("something happened: %s", val); endif ## off(2,1) # will be 1 if val(2,1) is an offset to a file position or 2 if tag was not found ## val(2,1) # value of tag 262 on IFD 1 ## @end group ## @end example ## ## @example ## @group ## ## read value 258, 262, 254 on the first 10 IFDs 1 (`val' and `off' will be a 1x10 matrix) ## [val, off] = tiff_tag_read (filepath, [258 262 254], 1:10); ## val(2,5) # value of tag 262 on IFD 5 ## @end group ## @end example ## ## @example ## @group ## ## read value 258, 262, 254 o IFD 1 (`val' and `off' will be a 1x3 matrix) ## [val, off] = tiff_tag_read (filepath, [258 262 254], "all"); ## val(2,end) # value of tag 262 on the last IFD ## @end group ## @end example ## ## @seealso{imread, imfinfo} ## @end deftypefn ## Based on the documentation at ## * http://en.wikipedia.org/wiki/Tagged_Image_File_Format ## * http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf ## * http://ibb.gsf.de/homepage/karsten.rodenacker/IDL/Lsmfile.doc ## * http://www.awaresystems.be/imaging/tiff/faq.html ## ## and the function tiff_read by F. Nedelec, EMBL (www.cytosim.org) ## * http://www.cytosim.org/misc/index.html ## ## Explanation of the TIFF file structure: ## ## The idea of multi-page images needs to be understood first. These allow one file ## to have multiple images. This may sound strange but consider situations such as ## an MRI scan (one file can then contain one scan which is multiple images across ## one of the axis) or time-lapse experiment (where one file is not unlike a movie). ## TIFF files support this by being like a container of single images, called IFD ## (Image File Directory). For each page there will be a single IFD. One can see a ## TIFF as an archive file of multiple images files that many times have a single file. ## ## Each TIFF file starts with a small header that identifies the file as TIFF. The ## header ends with the position on the file for the first IFD. Each IFD has multiple ## entries that hold information about the image of that IFD including where on the ## file is the actual image. Each IFD entry is identified by a tag. Each tag has a ## unique meaning; for example, the IFD entry with tag 259 will say the compression ## type (if any), of the image in that IFD. ## ## A TIFF file will always have at least one IFD and each IFD will always have at ## least one IFD entry. ## ## * On the TIFF image file header: ## bytes 00-01 --> byte order used within the file: "II" for little endian ## and "MM" for big endian byte ordering. ## bytes 02-03 --> number 42 that identifies the file as TIFF ## bytes 04-07 --> file offset (in bytes) of the first IFD (Image File Directory) ## ## Note: offset is always from the start of the file ("bof" in fread) and first ## byte has an offset of zero. ## ## * On a TIFF's IFD structure: ## bytes 00-01 --> number of entries (or tags or fields or directories) ## bytes 02-13 --> the IFD entry #0 ## bytes 14+=11 -> the IFD entry #N. Each will have exactly 12 bytes (the ## number of IFD entries was specified at the start of the IFD) ## bytes XX-XX --> file offset for next IFD (last 4 bytes of the IFD) or 0 ## if it's the last IFD ## ## * On an IFD entry structure: ## bytes 00-01 --> tag that identifies the entry ## bytes 02-03 --> entry type ## 1 --> BYTE (uint8) ## 2 --> ASCII ## 3 --> SHORT (uint16) ## 4 --> LONG (uint32) ## 5 --> RATIONAL (two LONGS) ## 6 --> SBYTE (int8) ## 7 --> UNDEFINED (8 bit) ## 8 --> SSHORT (int16) ## 9 --> SLONG (int32) ## 10 --> FLOAT (single IEEE precision) ## 11 --> DOUBLE (double IEEE precision) ## bytes 04-07 --> number of values (count) ## bytes 08-11 --> file offset (from the beginning of file) or value (only if ## it fits in 4 bytes). It is possible that the offset is for ## a structure and not a value so we return the offset ## ## Note: file offset of the value may point anywhere in the file, even after the image. ## ## Tags numbered >= 32768 are private tags ## Tags numbered on the 65000--65535 range are reusable tags function [val, off] = tiff_tag_read (file, tag, ifd = 1) if (nargin < 2 || nargin > 3) print_usage; elseif (!isnumeric (tag) || !isvector (tag)) error ("`tag' must be either a numeric scalar or vector with tags -- identifying number of a field"); elseif (!(ischar (ifd) && strcmpi (ifd, "all")) && !(isnumeric (ifd) && isvector (ifd) && all (ifd == fix (ifd)) && all (ifd > 0))) error ("`ifd' must be either the string `all' or numeric scalar or vector of positive integers with the IFD index"); endif [FID, msg] = fopen (file, "r", "native"); if (FID == -1) [val, off] = bad_exit (FID, sprintf ("Unable to fopen '%s': %s.", file, msg)); return endif ## read byte order byte_order = fread (FID, 2, "char=>char")'; # if we are retrieving a char, we need to transpose to get the string if (strcmp (byte_order, "II")) arch = "ieee-le"; # IEEE little endian format elseif (strcmp (byte_order,"MM")) arch = "ieee-be"; # IEEE big endian format else [val, off] = bad_exit (FID, sprintf ("First 2 bytes of '%s' returned '%s'. For TIFF should either be 'II' or 'MM'. Are you sure it's a TIFF.", file, byte_order)); return endif ## read number 42 nTIFF = fread (FID, 1, "uint16", arch); if (nTIFF != 42) [val, off] = bad_exit (FID, sprintf ("'%s' is not a TIFF (missing value 42 on header at offset 2. Instead got '%g').", file, tiff_id)); return endif if (ischar (ifd) && strcmpi (ifd, "all")) all_ifd = true; else all_ifd = false; endif ## default values for val and off def_val = 0; def_off = 2; ## start output values with default values if (ischar (ifd) && strcmpi (ifd, "all")) val = def_val * ones (numel (tag), 1); off = def_off * ones (numel (tag), 1); else val = def_val * ones (numel (tag), numel (ifd)); off = def_off * ones (numel (tag), numel (ifd)); endif ## read offset for the first IFD and move into it offset_IFD = fread (FID, 1, "uint32", arch); cIFD = 1; # current IFD while (offset_IFD != 0 && (all_ifd || any (ifd >= cIFD))) status = fseek (FID, offset_IFD, "bof"); if (status != 0) [val, off] = bad_exit (FID, sprintf ("error on fseek when moving to IFD #%g", cIFD)); return endif ## if checking on all IFD, add one column to the output if (all_ifd) val(:, end+1) = def_val; off(:, end+1) = def_off; endif ## read number of entries (nTag) and look for the desired tag ID nTag = fread (FID, 1, "uint16", arch); # number of tags in the IFD cTag = 1; # current tag while (nTag >= cTag) tagID = fread (FID, 1, "uint16", arch); # current tag ID if (any(tagID == tag)) # found one ## column number of this IFD in the output matrix: ## we don't know at start the number of IFD so if all IFD have been requested ## we can't find them in `ifd', we need to set the index for output manually if (all_ifd) iCol = cIFD; else iCol = (ifd == cIFD); endif [val(tagID == tag, iCol), ... off(tagID == tag, iCol) ] = read_value (FID, arch); # read tag value elseif (all (tag < tagID)) ## tags are in numeric order so if they wanted tags are all below current tag ID ## we can jump over to the next IFD skip_bytes = 10 + (12 * (nTag - cTag)); status = fseek (FID, skip_bytes, "cof"); # Move to the next IFD break else status = fseek (FID, 10, "cof"); # Move to the next tag if (status != 0) [val, off] = bad_exit (FID, sprintf ("error on fseek when moving out of tag #%g (tagID %g) on IFD %g.", cTag, tagID, cIFD)); return endif endif cTag++; endwhile offset_IFD = fread (FID, 1, "uint32", arch); cIFD++; endwhile fclose (FID); endfunction function [val, off] = read_value (FID, arch) position = ftell (FID); field_type = fread (FID, 1, "uint16", arch); count = fread (FID, 1, "uint32", arch); switch (field_type) case 1, nBytes = 1; precision = "uint8"; # BYTE = 8-bit unsigned integer case 2, nBytes = 1; precision = "uchar"; # ASCII = 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero) case 3, nBytes = 2; precision = "uint16"; # SHORT = 16-bit (2-byte) unsigned integer case 4, nBytes = 4; precision = "uint32"; # LONG = 32-bit (4-byte) unsigned integer case 5, nBytes = 8; precision = "uint32"; # RATIONAL = Two LONGs: the first represents the numerator of a fraction; the second, the denominator case 6, nBytes = 1; precision = "int8"; # SBYTE = An 8-bit signed (twos-complement) integer case 7, nBytes = 1; precision = "uchar"; # UNDEFINED = An 8-bit byte that may contain anything, depending on the definition of the field case 8, nBytes = 2; precision = "int16"; # SSHORT = A 16-bit (2-byte) signed (twos-complement) integer case 9, nBytes = 4; precision = "int32"; # SLONG = A 32-bit (4-byte) signed (twos-complement) integer case 10, nBytes = 8; precision = "int32"; # SRATIONAL = Two SLONG’s: the first represents the numerator of a fraction, the second the denominator case 11, nBytes = 4; precision = "float32"; # FLOAT = Single precision (4-byte) IEEE format case 12, nBytes = 8; precision = "float64"; # DOUBLE = Double precision (8-byte) IEEE format otherwise ## From the TIFF file specification (page 16, section 2: TIFF structure): ## "Warning: It is possible that other TIFF field types will be added in the ## future. Readers should skip over fields containing an unexpected field type." ## ## However, we only get to this point of the code if we are in the tag requested ## by the use so it makes sense to error if we don't supported it yet. error ("TIFF type %i not supported", field_type); endswitch if ((nBytes*count) > 4) off = true; val = fread (FID, 1, "uint32", arch); if (rem (val, 2) != 0) # file offset must be an even number warning ("Found an offset with an odd value %g (offsets should always be even numbers.", val); endif else off = false; switch precision case {5, 10} val = fread (FID, 2*count, precision, arch); val = val(1)/val(2); # the first represents the numerator of a fraction; the second, the denominator case {2} val = fread (FID, count, [precision "=>char"], arch)'; # if we are retrieving a char, we need to transpose to get the string otherwise val = fread (FID, count, precision, arch); endswitch ## adjust position to end of IFD entry (not all take up 4 Bytes) fseek (FID, 4 - (nBytes*count), "cof"); endif endfunction function [val, off] = bad_exit (FID, msg) off = -1; val = sprintf (msg); fclose (FID); endfunction image-2.16.1/inst/PaxHeaders.61586/im2uint8.m0000644000000000000000000000006215005110255015237 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/im2uint8.m0000644000175000017500000000636015005110255016642 0ustar00avinoamavinoam00000000000000## Copyright (C) 2007 Søren Hauberg ## Copyright (C) 2012-2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} im2uint8 (@var{img}) ## @deftypefnx {Function File} {} im2uint8 (@var{img}, "indexed") ## Convert image to uint8. ## ## 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: ## ## @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 uint8 class [0 255]. ## ## @item uint8 ## Returns the same image. ## ## @end table ## ## 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 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 imout = imcast (img, "uint8", varargin{:}); endfunction %!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 im2uint8 ([0 1 2], "indexed"); %!error im2uint8 (int16 ([17 8]), "indexed"); %!error im2uint8 (int16 ([-7 8]), "indexed"); %!error im2uint8 ([false true], "indexed"); %!error im2uint8 (uint16 (256), "indexed"); %!error im2uint8 (257, "indexed"); %!assert (im2uint8 ((1:255) ./ 256), uint8 ([1:128 128:254])) %!assert (im2uint8 ((0:255) ./ 256), uint8 ([0:128 128:254])) image-2.16.1/inst/PaxHeaders.61586/fchcode.m0000644000000000000000000000006215005110255015153 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/fchcode.m0000644000175000017500000000472715005110255016563 0ustar00avinoamavinoam00000000000000## Copyright (C) 2010 Andrew Kelly, IPS Radio & Space Services ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{fcc} = } fchcode (@var{bound}) ## Determine the Freeman chain code for a boundary. ## ## @code{fchcode} computes the Freeman chain code for the @var{n}-connected ## boundary @var{bound}. @var{n} must be either 8 or 4. ## ## @var{bound} is a K-by-2 matrix containing the row/column coordinates of points ## on the boundary. Optionally, the first point can be repeated as the last point, ## resulting in a (K+1)-by-2 matrix. ## ## @var{fcc} is a structure containing the following elements. ## ## @example ## x0y0 = Row/column coordinates where the code starts (1-by-2) ## fcc = Freeman chain code (1-by-K) ## diff = First difference of fcc (1-by-K) ## @end example ## ## The code uses the following directions. ## ## @example ## 3 2 1 ## 4 . 0 ## 5 6 7 ## @end example ## ## @seealso{bwboundaries} ## @end deftypefn function fcc = fchcode (bound) # ensure the boundary start and end points are the same if (!isempty (bound) && !isequal (bound (1, :), bound (end, :))) bound = [bound; bound(1, :)]; endif # number of boundary points n = max (0, rows (bound)-1); # structure in which to return results fcc = struct (... 'x0y0', zeros (1, n), ... 'fcc', zeros (1, n), ... 'diff', zeros (1, n) ... ); # an empty boundary? if (isempty (bound)) return; endif # direction map dir = [3, 2, 1; ... 4, NaN, 0; ... 5, 6, 7]; # coordinates ROW = 1; COL = 2; # direction changes as row/column indexes into DIR ch = 2 + diff (bound, 1, ROW); # starting point fcc.x0y0 = bound (1, :); # chain code fcc.fcc = dir (sub2ind (size (dir), ch (:, ROW), ch (:, COL)))'; # chain code difference fcc.diff = mod (diff ([fcc.fcc, fcc.fcc(1)]), 8); endfunction image-2.16.1/inst/PaxHeaders.61586/nlfilter.m0000644000000000000000000000006215005110255015377 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/nlfilter.m0000644000175000017500000001653415005110255017006 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} nlfilter (@var{A}, @var{block_size}, @var{func}) ## @deftypefnx {Function File} {} nlfilter (@var{A}, @var{block_size}, @var{func}, @dots{}) ## @deftypefnx {Function File} {} nlfilter (@var{A}, "indexed", @dots{}) ## Process matrix in sliding blocks with user-supplied function. ## ## Executes the function @var{fun} on each sliding block of ## size @var{block_size}, taken from the matrix @var{A}. Both the matrix ## @var{A}, and the block can have any number of dimensions. This function ## is specially useful to perform sliding/moving window functions such as ## moving average. ## ## The output will have the same dimensions @var{A}, each one of its values ## corresponding to the processing of a block centered at the same ## coordinates in @var{A}, with @var{A} being padded with zeros for the ## borders (see below for indexed images). In case any side of the block ## is of even length, the center is considered at indices ## @code{floor ([@var{block_size}/2] + 1)}. ## ## The argument @var{func} must be a function handle that takes matrices of ## size @var{block_size} as input and returns a single scalar. Any extra ## input arguments to @code{nlfilter} are passed to @var{func} after the ## block matrix. ## ## If @var{A} is an indexed image, the second argument should be the ## string @qcode{"indexed"} so that any required padding is done correctly ## as done by @code{im2col}. ## ## @emph{Note}: if @var{func} is a column compression function, i.e., it acts ## along a column to return a single value, consider using @code{colfilt} ## which usually performs faster. If @var{func} makes use of the colon ## operator to select all elements in the block, e.g., if @var{func} looks ## anything like @code{@@(x) sum (x(:))}, it is a good indication that ## @code{colfilt} should be used. In addition, many sliding block operations ## have their own specific implementations (see help text of @code{colfilt} ## for a list). ## ## @seealso{blockproc, col2im, colfilt, im2col} ## @end deftypefn function B = nlfilter (A, varargin) ## Input check [p, block_size, padval] = im2col_check ("nlfilter", nargin, A, varargin{:}); if (nargin < p) print_usage (); endif func = varargin{p++}; if (! isa (func, "function_handle")) error ("nlfilter: FUNC must be a function handle"); elseif (! isscalar (func (ones (block_size, class (A)), varargin{p:nargin-1}))) error ("nlfilter: FUNC must take a matrix of size BLOCK_SIZE and return a scalar"); endif ## Remaining params are parameters to fun. We need to place them into ## a separate variable, we can't varargin{p:nargin-1} inside the anonymous ## function because nargin will have a different value there extra_args = varargin(p:nargin -1); ## Pad image as required padded = pad_for_sliding_filter (A, block_size, padval); ## Get the blocks (one per column) blks = im2col (padded, block_size, "sliding"); ## New function that reshapes the array into a block before ## passing it to the actual user supplied function blk_func = @(x, z) func (reshape (blks(x:z), block_size), extra_args{:}); ## Perform the filtering blk_step = 1:(rows (blks)):(rows (blks) * columns (blks)); B = arrayfun (blk_func, blk_step, blk_step + rows (blks) -1); ## Back into its original shape B = reshape (B, size (A)); endfunction %!demo %! ## creates a "wide" diagonal (although it can be performed more %! ## efficiently with "imdilate (A, true (3))") %! nlfilter (eye (10), [3 3], @(x) any (x(:) > 0)) %!assert (nlfilter (eye (4), [2 3], @(x) sum (x(:))), %! [2 2 1 0 %! 1 2 2 1 %! 0 1 2 2 %! 0 0 1 1]); %!assert (nlfilter (eye (4), "indexed", [2 3], @(x) sum (x(:))), %! [4 2 1 2 %! 3 2 2 3 %! 2 1 2 4 %! 4 3 4 5]); %!assert (nlfilter (eye (4), "indexed", [2 3], @(x, y) sum (x(:)) == y, 2), %! logical ([0 1 0 1 %! 0 1 1 0 %! 1 0 1 0 %! 0 0 0 0])); ## Check uint8 and uint16 padding (only the padding since the class of ## the output is dependent on the function and sum() always returns double) %!assert (nlfilter (uint8 (eye (4)), "indexed", [2 3], @(x) sum (x(:))), %! [2 2 1 0 %! 1 2 2 1 %! 0 1 2 2 %! 0 0 1 1]); %!assert (nlfilter (int16 (eye (4)), "indexed", [2 3], @(x) sum (x(:))), %! [4 2 1 2 %! 3 2 2 3 %! 2 1 2 4 %! 4 3 4 5]); ## Check if function class is preserved %!assert (nlfilter (uint8 (eye (4)), "indexed", [2 3], @(x) int8 (sum (x(:)))), %! int8 ([2 2 1 0 %! 1 2 2 1 %! 0 1 2 2 %! 0 0 1 1])); %!test %! ## Effect of out of border elements. %! expected = [ %! 0.5 6.0 6.0 0.5 0 %! 5.5 10.5 13.5 10.5 4.0 %! 6.5 12.5 13.5 13.5 1.5 %! 10.5 12.5 15.5 11.0 1.0 %! 5.0 10.5 6.0 1.0 0 %! ]; %! assert (nlfilter (magic (5), [3 4], @(x) median (x(:))), expected) %!test %! ## The center pixel of a sliding window when its length is even %! ## sized is ceil ((size (NHOOD) +1) /2) %! expected = [ %! 24 24 24 16 16 %! 24 24 24 22 22 %! 23 23 22 22 22 %! 25 25 25 25 22 %! 25 25 25 25 21 %! ]; %! assert (nlfilter (magic (5), [3 4], @(x) max (x(:))), expected) ## nlfilter and imdilate use a different center pixel when the ## window/nhood have an even sized length. So to have imdilate behave ## like nlfilter, we need to flip those dimensions. %!function dilated = imdilate_like_nlfilter (im, nhood) %! even_nhood_dims = find (mod (size (nhood), 2) == 0); %! for i = 1:even_nhood_dims %! im = flip (im, i); %! endfor %! dilated = imdilate (im, nhood); %! for i = 1:even_nhood_dims %! dilated = flip (dilated, i); %! endfor %!endfunction ## Test N-dimensional %!test %! a = randi (65535, 20, 20, 20, "uint16"); %! ## extra dimensions on matrix only %! assert (nlfilter (a, [5 5], @(x) max(x(:))), imdilate (a, ones (5))) %! ## extra dimensions on both matrix and block %! assert (nlfilter (a, [5 5 5], @(x) max(x(:))), imdilate (a, ones ([5 5 5]))) %! ## extra dimensions and padding %! assert (nlfilter (a, [3 7], @(x) max(x(:))), imdilate (a, ones ([3 7]))) %! assert (nlfilter (a, [3 7 3], @(x) max(x(:))), imdilate (a, ones ([3 7 3]))) %!test %! a = randi (65535, 15, 15, 4, 8, 3, "uint16"); %! assert (nlfilter (a, [3 4 7 5], @(x) max(x(:))), %! imdilate_like_nlfilter (a, ones ([3 4 7 5]))) ## Just to make sure it's not a bug in imdilate %!test %! a = randi (65535, 15, 15, 4, 3, 8, "uint16"); %! ord = ordfiltn (a, 3, ones ([3 7 3 1 5])); %! assert (nlfilter (a, [3 7 3 1 5], @(x) sort (x(:))(3)), ord) %! assert (nlfilter (a, [3 7 3 1 5], @(x, y) sort (x(:))(y), 3), ord) image-2.16.1/inst/PaxHeaders.61586/labelmatrix.m0000644000000000000000000000006215005110255016064 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/labelmatrix.m0000644000175000017500000000455415005110255017472 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} labelmatrix (@var{cc}) ## Create labelled matrix from bwconncomp structure. ## ## Uses the structure as returned by the @code{bwconncomp} function to create ## a label matrix, where each individual object is assigned a positive number. ## A value of zero corresponds to the background. ## ## The class of the output matrix is dependent on the number of objects, being ## uint, uint16, uint32, or double, whichever is enough. ## ## @seealso{bwconncomp, bwlabel, bwlabeln, label2rgb, rgb2label} ## @end deftypefn function labelled = labelmatrix (cc) if (nargin != 1) print_usage (); elseif (! isstruct (cc) && ! all (isfield (cc, {"Connectivity", "ImageSize", "NumObjects", "PixelIdxList"}))) error ("labelmatrix: CC must be a struct as returned by bwconncomp"); endif n_obj = cc.NumObjects; if (n_obj < 256), cl = "uint8"; elseif (n_obj < 65536), cl = "uint16"; elseif (n_obj < 4294967296), cl = "uint32"; else, cl = "double"; endif labels = repelems (1:n_obj, [1:n_obj; cellfun("numel", cc.PixelIdxList)]); ind = cell2mat (cc.PixelIdxList'); labelled = zeros (cc.ImageSize, cl); labelled(ind) = labels; endfunction %!test %! cc = struct (); %! cc.Connectivity = 8; %! cc.ImageSize = [7 7]; %! cc.NumObjects = 4; %! cc.PixelIdxList = {[1;2], [5;7;12;13;14], [22;23], [26;32;33;36;37;38]}; %! %! l = uint8 ([ %! 1 0 0 3 0 4 0 %! 1 0 0 3 0 4 0 %! 0 0 0 0 0 4 0 %! 0 0 0 0 4 0 0 %! 2 2 0 4 4 0 0 %! 0 2 0 0 0 0 0 %! 2 2 0 0 0 0 0 %! ]); %! assert (labelmatrix (cc), l) image-2.16.1/inst/PaxHeaders.61586/medfilt2.m0000644000000000000000000000006215005110255015266 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/medfilt2.m0000644000175000017500000001175015005110255016670 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Teemu Ikonen ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} medfilt2 (@var{A}) ## @deftypefnx {Function File} {} medfilt2 (@var{A}, @var{nhood}) ## @deftypefnx {Function File} {} medfilt2 (@var{A}, [@var{M} @var{N}]) ## @deftypefnx {Function File} {} medfilt2 (@dots{}, @var{pad}) ## Two dimensional median filtering. ## ## Replaces elements of @var{A} with the median of their neighbours as defined ## by the true elements of logical matrix @var{nhood} or by a matrix of size ## @var{M} by @var{N}. The default @var{nhood} is a 3 by 3 matrix of true ## elements. ## ## @example ## @group ## ## median filtering specifying neighborhood dimensions ## medfilt2 (img) # default is [3 3] ## medfilt2 (img, [3 1]) # a 3x1 vector ## medfilt2 (img, [5 5]) # 5 element wide square ## @end group ## ## @group ## ## median filtering specifying neighborhood ## medfilt2 (img, true (5)) # same as [5 5] ## nhood = logical ([0 1 0 ## 1 1 1 ## 0 1 0]); ## medfilt2 (img, nhood) # 3 element wide cross ## @end group ## @end example ## ## The optional variable @var{pad} defines the padding used in augmenting ## the borders of @var{A}. See @code{padarray} for details. ## @seealso{ordfilt2, ordfiltn} ## @end deftypefn function retval = medfilt2 (A, varargin) if (nargin < 1 || nargin > 3) print_usage (); elseif (! isimage (A)) error ("medfilt2: A must be a real matrix") endif ## defaults padding = "zeros"; domain = true (3, 3); for idx = 1:numel (varargin) opt = varargin{idx}; if (ischar (opt) || isscalar (opt)) padding = opt; elseif (isnumeric (opt) || islogical (opt)) ## once we have removed the ability of selecting domain with a non logical ## matrix, we can use it to make this N dimensional domain = [3 3 3]. if (isvector (opt) && ! islogical (opt) && numel (opt) == 2) domain = true (opt); else if (! islogical (opt)) persistent warned = false; if (! warned) warned = true; warning ("medfilt2: to specify filter instead of dimensions use a matrix of logical class. This usage will be removed from future releases of the image package."); endif endif domain = logical (opt); endif else error ("medfilt2: unrecognized option of class %s", class (opt)) endif endfor ## TODO this should probably be implemented as an option of ## __spatial_filtering__ where median() cold be called directly n = nnz (domain); if ((n - 2*floor(n/2)) == 0) % n even - more work nth = floor (n/2); a = ordfiltn (A, nth, domain, padding); b = ordfiltn (A, nth + 1, domain, padding); retval = a./2 + b./2; # split into two divisions to avoid overflow on integer data else nth = floor (n/2) + 1; retval = ordfiltn (A, nth, domain, padding); endif endfunction %!shared b, f %! b = [ 0 1 2 3 %! 1 8 12 12 %! 4 20 24 21 %! 7 22 25 18]; %! f = [ 0 1 2 0 %! 1 4 12 3 %! 4 12 20 12 %! 0 7 20 0]; %!assert (medfilt2 (b), f); %! %! f = [ 0 1 2 3 %! 1 8 12 12 %! 4 20 24 18 %! 4 20 24 18]; %!assert (medfilt2 (b, true (3, 1)), f); %!assert (medfilt2 (b, [3 1]), f); %! %! f = [ 1 8 10 10 %! 1 8 12 12 %! 4 20 24 18 %! 7 20 24 18]; %!assert (medfilt2 (b, [3 1], 10), f); %!assert (medfilt2 (b, 10, [3 1]), f); %! %! f = [ 0.5 4.5 7.0 7.5 %! 2.5 14.0 18.0 15.0 %! 2.5 14.0 18.0 15.0 %! 2.0 10.0 12.0 9.0]; %!assert (medfilt2 (b, true (4, 1)), f); %!assert (medfilt2 (b, [4 1]), f); %!test %! A = zeros (3, 3); %! B = ones (3, 3); %! C = [1 1 1; 2 2 2; 3 3 3]; %! D = C'; %! E = ones (3, 3); %! E(2,2) = 2; %! F = 3 .* ones (3, 3); %! F(2,2) = 1; %! G = [-1 2 7; -5 2 8; -7 pi 9]; %! H = [5 2 8; 1 -3 1; 5 1 0]; %! A_out = [0 0 0; 0 0 0; 0 0 0]; %! B_out = [0 1 0; 1 1 1; 0 1 0]; %! C_out = [0 1 0; 1 2 1; 0 2 0]; %! D_out = [0 1 0; 1 2 2; 0 1 0]; %! E_out = [0 1 0; 1 1 1; 0 1 0]; %! F_out = [0 3 0; 3 3 3; 0 3 0]; %! G_out = [0 0 0; 0 2 2; 0 0 0]; %! H_out = [0 1 0; 1 1 0; 0 0 0]; %! assert (medfilt2 (A), A_out); %! assert (medfilt2 (B), B_out); %! assert (medfilt2 (C), C_out); %! assert (medfilt2 (D), D_out); %! assert (medfilt2 (E), E_out); %! assert (medfilt2 (F), F_out); %! assert (medfilt2 (G), G_out); %! assert (medfilt2 (H), H_out); image-2.16.1/inst/PaxHeaders.61586/makelut.m0000644000000000000000000000006215005110255015222 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/makelut.m0000644000175000017500000000515715005110255016630 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{lut} =} makelut (@var{fun}, @var{n}) ## @deftypefnx {Function File} {@var{lut} =} makelut (@var{fun}, @var{n}, @var{P1}, @var{P2}, @dots{}) ## Create a lookup table which can be used by applylut. ## ## lut = makelut(fun,n) returns a vector which can be used by applylut ## as a lookup table. ## ## @var{fun} can be a function object as created by inline, or simply a ## string which contains the name of a function. @var{fun} should accept a ## @var{n}-by-@var{n} matrix whose elements are binary (0 or 1) and ## returns an scalar (actually anything suitable to be included in a ## vector). ## ## makelut calls @var{fun} with all possible matrices and builds a ## vector with its result, suitable to be used by applylut. The length ## of this vector is 2^(@var{n}^2), so 16 for 2-by-2 and 512 for 3-by-3. ## ## makelut also passes parameters @var{P1}, @var{P2}, .... to @var{fun}. ## ## @seealso{applylut} ## @end deftypefn function lut = makelut (fun, n, varargin) if (nargin < 2) print_usage; elseif (n < 2) error ("makelut: n should be a natural number >= 2"); endif nq=n^2; c=2^nq; lut=zeros(c,1); w=reshape(2.^[nq-1:-1:0],n,n); for i=0:c-1 idx=bitand(w,i)>0; lut(i+1)= feval(fun, idx, varargin{:}); endfor endfunction %!demo %! makelut(@(x) sum(x(:))>=3, 2) %! % Returns '1' if one or more values %! % in the input matrix are 1 %!assert(prod(makelut(@(x) sum(x(:))==2, 2)==makelut(@(x, a, b, c, d) sum(x(:))==a*b*c*d,2,2/(3*4*5),3,4,5))); # test multiple params %!assert(prod(makelut(@(x) x(1,1)==1, 2)==[zeros(2^3,1);ones(2^3,1)])==1); # test 2-by-2 %!assert(prod(makelut(@(x) x(1,1)==1, 3)==[zeros(2^8,1);ones(2^8,1)])==1); # test 3-by-3 %!assert(prod(makelut(@(x) x(1,1)==1, 4)==[zeros(2^15,1);ones(2^15,1)])==1); # test 4-by-4 %!assert(prod(makelut(@(x) x(2,1)==1, 3)==[zeros(2^7,1);ones(2^7,1);zeros(2^7,1);ones(2^7,1)])==1); # another test for 3-by-3 image-2.16.1/inst/PaxHeaders.61586/houghpeaks.m0000644000000000000000000000006215005110255015716 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/houghpeaks.m0000644000175000017500000002616515005110255017326 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{peaks} =} @ houghpeaks (@var{H}) ## @deftypefnx {Function File} {@var{peaks} =} @ houghpeaks (@var{H}, @var{numpeaks}) ## @deftypefnx {Function File} {@var{peaks} =} @ houghpeaks (@var{H}, @dots{}, @var{property}, @var{value}, @dots{}) ## Find peaks in a Hough transform. ## ## The houghpeaks function finds positions of maximum value ("peaks") ## in the 2d matrix @var{H} of a Hough (line) transform. ## This hough transform @var{H} will normally come from the ## function hough. Its rows are distance coordinates (rho) of lines in an image, ## and its columns are angle coordinates (theta). ## ## The output @var{peaks} of the houghpeaks functions is a n-by-2 matrix where ## each line is an x (rho) and y (theta) coordinate of a found peak, sorted ## in descending order of the corresponding peak value. ## ## The number of returned peak coordinates can be limited with ## the @var{numpeaks} parameter, it defaults to 1. ## ## Additionally the following optional property-value-pairs can be used: ## @table @asis ## @item @var{Threshold} ## Maximum positions in @var{H} with a value below this threshold ## will not counted as peaks. ## This defaults to 50% of the maximum value in @var{H}. ## ## @item @var{NHoodSize} ## After finding one peak, a neighborhood of this size will ## be excluded from the search for further peaks. ## This parameter must be given as a two-element row vector. ## The first entry is the full width of the ## x (rho) neighborhood, the second entry of y (theta). ## Both numbers need to be odd integers. ## This defaults to the smallest odd number equal or bigger than size(H)/50. ## @end table ## ## @seealso{hough} ## @end deftypefn ## The algorithm is taken from the book ## "Digital Image Processing using MATLAB" ## by R. C. Gonzalez, R. E. Woods and S. L. Eddins, ## McGrawHill, 2nd edition 2010. function peaks = houghpeaks (H, varargin) ## retrieve the input parameters: numpeaks = []; threshold = []; nhoodsize = []; if ((nargin < 1) || (nargin > 6)) print_usage (); endif if (nargin/2 == round(nargin/2)) # even number of inputs (2, 4 or 6) ## houghpeaks (H, numpeaks) ## houghpeaks (H, numpeaks, property1, value1) ## houghpeaks (H, numpeaks, property1, value1, property2, value2) numpeaks = varargin{1}; n_start = 2; else # odd number of inputs (1, 3 or 5) ## houghpeaks (H) ## houghpeaks (H, property1, value1) ## houghpeaks (H, property1, value1, property2, value2) n_start = 1; endif for n = n_start:2:(nargin-1) # process parameter-values pairs if (strcmpi (varargin{n}, "threshold")) threshold = varargin{n+1}; elseif (strcmpi (varargin{n}, "nhoodsize")) nhoodsize = varargin{n+1}; else error ("houghpeaks: invalid PROPERTY given") endif endfor ## set default parameters: if (isempty (numpeaks)) numpeaks = 1; endif if (isempty (threshold)) threshold = 0.5 * max (H(:)); endif if (isempty (nhoodsize)) nhoodsize = size (H)/50; nhoodsize += 1; # for Matlab compatibilty (against their documentation) nhoodsize = 2*ceil ((nhoodsize-1)/2)+1; # odd number (equal or bigger) as in Matlab documentation nhoodsize = max (nhoodsize, 3); # for (undocumented) Matlab compatibility endif ## check input parameters: if (! isimage (H) || ndims(H) != 2) error ("houghpeaks: H must be a numeric 2d array"); endif if ((! isscalar (numpeaks)) || (numpeaks <= 0) || (numpeaks != round(numpeaks) )) error ("houghpeaks: NUMPEAKS must be a positive integer scalar.") endif if ((! isscalar (threshold) ) || (! isnumeric (threshold)) || (threshold < 0)) error ("houghpeaks: THRESHOLD must be a non-negative numeric scalar.") endif if ( (ndims (nhoodsize) != 2) || (any (size (nhoodsize) != [1 2])) || (! isnumeric (nhoodsize)) || (any (nhoodsize <= 0)) || (any (round ((nhoodsize-1)/2) * 2 + 1 != nhoodsize) ) ) error ("houghpeaks: NHOODSIZE must be a 2-element vector of positive odd integers") endif ## do the calculation ## ## The algorithm is taken from the above cited book, ## chapter 10.2.2 "Toolbox Hough Functions", ## section "Function houghpeaks". It says ## "The basic idea behind this procedure is to ## clean-up the peaks by setting to zero the Hough ## transform cells in the immediate neighborhood ## in which a peak was found." ## ## properties of the Hough transform data H: ## * rows (x) of H are distance (rho) ## and columns (y) of H are angle (theta) ## * H is anti-symmetric in theta-direction nhood = (nhoodsize-1)/2; nhoodx = nhood(1); nhoody = nhood(2); sizex = size (H, 1); sizey = size (H, 2); peaks = []; for n = 1:numpeaks ## find the next peak [maxval, maxind] = max (H(:)); [x0, y0] = ind2sub (size (H), maxind); ## if peak value is too low, stop the search if (maxval < threshold) break; endif peaks(n,:) = [x0, y0]; ## limit the size of the deleted neighborhood to H xmin = max (x0 - nhoodx, 1); xmax = min (x0 + nhoodx, sizex); ymin = max (y0 - nhoody, 1); ymax = min (y0 + nhoody, sizey); H(xmin:xmax, ymin:ymax) = 0; ## use anti-symmetry in theta direction ## to also delete points on "the other side" if ((y0 + nhoody > sizey) || (y0 - nhoody < 1)) xmin2 = sizex - xmax + 1; xmax2 = sizex - xmin + 1; if (y0 + nhoody > sizey) ymin2 = 1; ymax2 = y0 + nhoody - sizey; else # (y0 - nhoody < 1) ymin2 = y0 - nhoody + sizey; ymax2 = sizey; endif H(xmin2:xmax2, ymin2:ymax2) = 0; endif endfor endfunction %!shared im1 %! im1 = magic (5); ## test input syntax: %!error houghpeaks () %!error houghpeaks (1, 2, 3, 4, 5, 6, 7) %!assert (houghpeaks (im1)) %!assert (houghpeaks (im1, 2)) %!assert (houghpeaks (im1, "Threshold", 10)) %!assert (houghpeaks (im1, 2, "Threshold", 10)) %!assert (houghpeaks (im1, "NHoodSize", [3 3])) %!assert (houghpeaks (im1, 2, "NHoodSize", [3 3])) %!assert (houghpeaks (im1, "Threshold", 10, "NHoodSize", [3 3])) %!assert (houghpeaks (im1, "NHoodSize", [3 3], "Threshold", 10)) %!assert (houghpeaks (im1, 2, "Threshold", 10, "NHoodSize", [3 3])) %!assert (houghpeaks (im1, 2, "NHoodSize", [3 3], "Threshold", 10)) %!error houghpeaks (ones (5, 5, 5)) %!error houghpeaks ("hello") %!error houghpeaks (im1, 1.5) %!error houghpeaks (im1, -2) %!error houghpeaks (im1, [1 1]) %!error houghpeaks (im1, "Threshold", "hello") %!error houghpeaks (im1, "Threshold", -2) %!error houghpeaks (im1, "Threshold", [1 1]) %!error houghpeaks (im1, "NHoodSize", [3 3 3]) %!error houghpeaks (im1, "NHoodSize", "hello") %!error houghpeaks (im1, "NHoodSize", [-3 -3]) %!error houghpeaks (im1, "NHoodSize", [4 4]) ## test dimensions and classes: %!test %! out = houghpeaks (im1); %! assert (size (out), [1 2]) %! assert (class (out), "double") %!test %! out = houghpeaks (im1, 3); %! assert (size (out), [3 2]) ## test calculation results: %!test %! expected = [5 3; 1 2; 3 5; 1 5]; %! assert (houghpeaks (im1, 4), expected) # this checks for undocumented nhood >=3 %! assert (houghpeaks (im1, 4, "nhoodsize", [3,3]), expected) %! assert (houghpeaks (im1, 4, "threshold", 10), expected) %! assert (houghpeaks (im1, 4, "threshold", 24), expected(1:2,:)) %!test %! im2 = magic (7); %! expected_a = [7 4; 1 3; 3 1; 5 6]; %! expected_b = [7 4; 1 3; 4 7; 1 7]; %! assert (houghpeaks (im2, 4), expected_a) %! assert (houghpeaks (im2, 4, "nhoodsize", [5,5]), expected_b) %! assert (houghpeaks (im2, 4, "threshold", 24), expected_a) %! assert (houghpeaks (im2, 4, "threshold", 47), expected_a(1:2,:)) %!test %! im3 = magic (99); %! expected_a = [99 50; 1 49; 3 47; 5 45; 7 43; 9 41; 11 39]; %! expected_b = [99 50; 1 49; 7 43; 13 37; 19 31; 25 25; 31 19]; %! expected_c = [99 50; 1 49; 2 48; 3 47; 4 46; 5 45; 6 44]; %! assert (houghpeaks (im3, 7), expected_a) %! assert (houghpeaks (im3, 7, "nhoodsize", [11 11]), expected_b) %! assert (houghpeaks (im3, 7, "nhoodsize", [11 1]), expected_c) %! assert (houghpeaks (im3, 7, "nhoodsize", [11 1]), expected_c) %!test %! im4 = double (im2uint16 (peaks ())); %! expected_a = [37 15; 39 15; 41 15; 15 16; 17 16]; %! expected_b = [37 15; 15 16; 26 21; 37 26; 20 32]; %! expected_c = [37 15; 15 16; 35 16; 15 17; 35 17]; %! expected_d = [37 15; 38 15; 39 15; 40 15; 41 15]; %! assert (houghpeaks (im4, 5), expected_a) %! assert (houghpeaks (im4, 5, "nhoodsize", [21 21]), expected_b) %! assert (houghpeaks (im4, 5, "nhoodsize", [21 1]), expected_c) %! assert (houghpeaks (im4, 5, "nhoodsize", [1 21]), expected_d) %!test # tests use of anti-symmetry in H %! im5 = zeros (6,4); im5(2,1) = 1; im5(5,4) = 2; %! expected = [5 4; 2 1]; %! assert (houghpeaks (im5, 2, "nhoodsize", [1 1]), expected); %! assert (houghpeaks (im5, 2, "nhoodsize", [3 3]), expected(1,:)); %!test #test use of anti-symmetry in the other direction %! im6 = magic (100); %! expected_a = [1 1; 100 99; 1 4; 100 95; 1 8; 100 91; 1 12]; %! expected_b = [1 1; 100 95; 1 8; 100 87; 1 16; 100 79; 1 24]; %! expected_c = [1 1; 100 99; 100 98; 1 4; 1 5; 100 95; 100 94]; %! expected_d = expected_b; %! assert (houghpeaks (im6, 7), expected_a) %! assert (houghpeaks (im6, 7, "nhoodsize", [11 11]), expected_b) %! assert (houghpeaks (im6, 7, "nhoodsize", [11 1]), expected_c) %! assert (houghpeaks (im6, 7, "nhoodsize", [1 11]), expected_d) %!test # test undocumented Matlab default value for nhoodsize %! im = zeros (723, 180); %! im(585,136) = 8; %! im(593,135) = 7; %! im(310,46) = 6; %! expected = [585, 136; 310, 46]; %! assert (houghpeaks (im, 2), expected) %!test %! I = max (0, phantom ()); %! H = hough (I); %! P0 = [585, 136; 310, 46; 595, 136; 522, 104; 373, 46]; %! assert (houghpeaks (H, 5), P0) ## show instructive demo: %!demo %! I = checkerboard (30, 1, 1); %! I = imnoise(I, "salt & pepper", 0.2); %! figure, imshow (I); %! title ("noisy image with some lines"); %! BW = edge (I, "canny"); %! figure, imshow(BW); %! title ("edge image"); %! [H, theta, rho] = hough (BW); %! figure, imshow (mat2gray (H), [],"XData",theta,"YData",rho); %! title ("Hough transform of edge image \n 2 peaks marked"); %! axis on; xlabel("theta [degrees]"); ylabel("rho [pixels]"); %! peaks = houghpeaks (H, 2); %! peaks_rho = rho(peaks(:,1)) %! peaks_theta = theta(peaks(:,2)) %! hold on; %! plot(peaks_theta,peaks_rho,"sr"); %! hold off; image-2.16.1/inst/PaxHeaders.61586/lab2rgb.m0000644000000000000000000000006215005110255015073 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/lab2rgb.m0000644000175000017500000001160215005110255016471 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{rgb} =} lab2rgb (@var{lab}) ## @deftypefnx {Function File} {@var{rgb_map} =} lab2rgb (@var{lab_map}) ## Transform a colormap or image from CIE L*a*b* to sRGB color space. ## ## A color in the CIE L*a*b* (or CIE Lab) space consists of lightness L* and ## two color-opponent dimensions a* and b*. The whitepoint is taken as D65. ## The CIE L*a*b* colorspace is a colorimetric colorspace. It is additionally ## designed to incorporate the human perception of color differences. ## ## A color in the RGB space consists of red, green, and blue intensities. ## The input RGB values are interpreted as nonlinear sRGB values ## with the white point D65. This means the input values are assumed to ## be in a colorimetric (sRGB) colorspace. ## ## Input values of class single and double are accepted. ## The shape and the class of the input are conserved. ## ## The input values of L* are normally in the inteval [0, 100] ## and the values of a* and b* in the interval [-127, 127]. ## ## note: This function returns slightly different values than the Matlab ## version. But it has a better "round trip accuracy" (<2e-5) ## for RGB -> Lab -> RGB. ## ## @seealso{rgb2lab, rgb2xyz, rgb2hsv, rgb2ind, rgb2ntsc} ## @end deftypefn ## Author: Hartmut Gimpel ## algorithm taken from the following book: ## Burger, Burge "Digitale Bildverarbeitung", 3rd edition (2015) function rgb = lab2rgb (lab) if (nargin != 1) print_usage (); endif [lab, cls, sz, is_im, is_nd, is_int] ... = colorspace_conversion_input_check ("lab2rgb", "Lab", lab, 1); # currently only accept single and double inputs (as Matlab does) # (Integer types would be possible, but would need an explanation in the # help text how to scale them.) ## transform from CIE L*a*b* to CIE XYZ values xyz = lab2xyz (lab); ## transform from CIE XYZ to non-linear sRGB values rgb = xyz2rgb (xyz); # always return values of type double for Matlab compatibility (exception: type single) rgb = colorspace_conversion_revert (rgb, cls, sz, is_im, is_nd, is_int, 1); endfunction ## Test pure colors, gray and some other colors ## (This set of test values is taken from the book by Burger.) %!assert (lab2rgb ([0 0 0]), [0, 0, 0], 1e-3) %!assert (lab2rgb ([53.24, 80.09, 67.20]), [1 0 0], 1e-3) %!assert (lab2rgb ([97.14, -21.55, 94.48]), [1 1 0], 1e-3) %!assert (lab2rgb ([87.74, -86.18, 83.18]), [0 1 0], 1e-3) %!assert (lab2rgb ([91.11, -48.09, -14.13]), [0 1 1], 1e-3) %!assert (lab2rgb ([32.30, 79.19, -107.86]), [0 0 1], 1e-3) %!assert (lab2rgb ([60.32, 98.24, -60.83]), [1 0 1], 1e-3) %!assert (lab2rgb ([100, 0.00, 0.00]), [1 1 1], 1e-3) %!assert (lab2rgb ([53.39, 0.00, 0.00]), [0.5 0.5 0.5], 1e-3) %!assert (lab2rgb ([39.77, 64.51, 54.13]), [0.75 0 0], 1e-3) %!assert (lab2rgb ([25.42, 47.91, 37.91]), [0.5 0 0], 1e-3) %!assert (lab2rgb ([9.66, 29.68, 15.24]), [0.25 0 0], 1e-3) %!assert (lab2rgb ([68.11, 48.39, 22.83]), [1 0.5 0.5], 1e-3) ## Test tolarant input checking %!assert (lab2rgb ([150 130 130]), [2.714, 1.028, 0.492], 1e-3) %!test %! lab_map = rand (64, 3); %! lab_map(:,1) = lab_map(:,1) .* 100; %! lab_map(:,2) = lab_map(:,2) .* 254 - 127; %! lab_map(:,3) = lab_map(:,3) .* 254 - 127; %! assert (rgb2lab (lab2rgb (lab_map)), lab_map, 5e-3); %!test %! lab_img = rand (64, 64, 3); %! lab_img(:,:,1) = lab_img(:,:,1) .* 100; %! lab_img(:,:,2) = lab_img(:,:,2) .* 254 - 127; %! lab_img(:,:,3) = lab_img(:,:,3) .* 254 - 127; %! assert (rgb2lab (lab2rgb (lab_img)), lab_img, 5e-3); ## support sparse input %!assert (lab2rgb (sparse ([0 0 0])), [0 0 0], 1e-3) %!assert (lab2rgb (sparse ([100, 0.00, 0.00])), [1 1 1], 1e-3) ## conserve class of single input %!assert (class (lab2rgb (single([50 50 50]))), 'single') ## Test input validation %!error lab2rgb () %!error lab2rgb (1,2) %!error lab2rgb ({1}) %!error lab2rgb (ones (2,2)) ## Test ND input %!test %! lab = rand (16, 16, 3, 5); %! lab(:,:,1,:) = lab(:,:,1,:) .* 100; %! lab(:,:,2,:) = lab(:,:,2,:) .* 254 - 127; %! lab(:,:,3,:) = lab(:,:,3,:) .* 254 - 127; %! rgb = zeros (size (lab)); %! for i = 1:5 %! rgb(:,:,:,i) = lab2rgb (lab(:,:,:,i)); %! endfor %! assert (lab2rgb (lab), rgb) image-2.16.1/inst/PaxHeaders.61586/fftconvn.m0000644000000000000000000000006215005110255015403 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/fftconvn.m0000644000175000017500000001374315005110255017011 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} fftconvn (@var{A}, @var{B}) ## @deftypefnx {Function File} {} fftconvn (@var{A}, @var{B}, @var{shape}) ## Convolve N dimensional signals using the FFT for computation. ## ## This function is equivalent to @code{convn} but using the FFT. It ## convolves the two N dimensional @var{A} and @var{B}. The size of ## output is controlled by the option @var{shape} which removes the ## borders where boundary effects may be seen: ## ## @table @asis ## @item @qcode{"full"} (default) ## Return the full convolution. ## ## @item @qcode{"same"} ## Return central part of the convolution with the same size as @var{A}. ## ## @item @qcode{"valid"} ## Return only the parts which do not include zero-padded edges. ## ## @end table ## ## Using the FFT may be faster but this is not always the case and can ## be a lot worse, specially for smalls @var{A} and @var{B}. This performance ## increase also comes at the cost of increased memory usage, as well as a loss ## of precision. ## ## @example ## @group ## a = randi (255, 1024, 1024); ## b = randi (255, 10, 10); ## t = cputime (); convn (a, b); cputime () -t ## @result{} 0.096000 ## t = cputime (); fftconvn (a, b); cputime () -t ## @result{} 1.2560 ## ## b = randi (255, 50, 50); ## t = cputime (); convn (a, b); cputime () -t ## @result{} 2.3400 ## t = cputime (); fftconvn (a, b); cputime () -t ## @result{} 1.2560 ## @end group ## @end example ## ## Note how computation time for @code{convn} increased with the size of ## @var{B} but remained constant when using @code{fftconvn}. When ## performing the convolution, @code{fftconvn} zero pads both @var{A} and ## @var{B} so their lengths are a power of two on all dimensions. ## This may further increase memory usage but will also increase ## performance. In this example, the computation time will remain constant ## until @code{size (@var{A}) + size (@var{B}) -1} is greater than 2048 ## after which it will remain constant again until it reaches 4096. ## ## @example ## @group ## a = randi (255, 1024, 1024); ## b = randi (255, 50, 50); ## t = cputime (); fftconvn (a, b); cputime () -t ## @result{} 1.2760 ## a = randi (255, 2048-50+1, 2048-50+1); ## t = cputime (); fftconvn (a, b); cputime () -t ## @result{} 1.2120 ## a = randi (255, 2049-50+1, 2049-50+1); ## t = cputime (); fftconvn (a, b); cputime () -t ## @result{} 6.1520 ## a = randi (255, 4096-50+1, 4096-50+1); ## t = cputime (); fftconvn (a, b); cputime () -t ## @result{} 6.2360 ## a = randi (255, 4097-50+1, 4097-50+1); ## t = cputime (); fftconvn (a, b); cputime () -t ## @result{} 38.120 ## @end group ## @end example ## ## @seealso{convn, fftconv2, fftconv, padarray} ## @end deftypefn function C = fftconvn (A, B, shape = "full") if (nargin < 2 || nargin > 3) print_usage (); elseif (! isnumeric (A) || ! isnumeric (B)) error ("fftconvn: A and B must be numeric") endif nd = max (ndims (A), ndims (B)); A_size = get_sizes (A, nd); B_size = get_sizes (B, nd); fft_size = 2 .^ nextpow2 (A_size + B_size - 1); C = ifftn (fftn (A, fft_size(1:ndims(A))) .* fftn (B, fft_size(1:ndims(B)))); if (iscomplex (C) && isreal (A) && isreal (B)) C = real (C); endif switch (tolower (shape)) case "full" starts = repmat (1, [1 nd]); ends = A_size + B_size - 1; case "same" prepad = floor (B_size / 2); starts = prepad + 1; ends = A_size + prepad; case "valid" starts = B_size; ends = A_size; otherwise error ("fftconvn: unknown SHAPE `%s'", shape); endswitch if (any (starts > 1) || any (ends != fft_size)) idx = get_ndim_idx (starts, ends); C = C(idx{:}); endif endfunction ## returns the size of x but padded with 1 (singleton dimensions), to ## allow operations to be performed when the ndims do not match function sizes = get_sizes (x, n) sizes = postpad (size (x), n, 1, 2); endfunction ## starts and ends must have same length function idx = get_ndim_idx (starts, ends) idx = arrayfun (@colon, starts, ends, "UniformOutput", false); endfunction %!function test_shapes (a, b, precision) %! shapes = {"valid", "same", "full"}; %! for i = 1:3 %! shape = shapes{i}; %! assert (fftconvn (a, b, shape), convn (a, b, shape), precision); %! endfor %! assert (fftconvn (a, b), fftconvn (a, b, "full")); %!endfunction ## simplest case %!test test_shapes (randi (255, 100), randi (255, 10), 0.1) %!test test_shapes (randi (255, 100, 100), randi (255, 10, 10), 0.1) %!test test_shapes (randi (255, 100, 100, 100), randi (255, 10, 10, 10), 0.1) ## mix of number of dimensions %!test test_shapes (randi (255, 100, 50, 20), randi (255, 10, 7), 0.1) %!test test_shapes (randi (255, 100, 50, 20), randi (255, 10), 0.1) ## test near powers of 2 sizes %!test %! for s = [55 56 57 58] %! test_shapes (randi (255, 200, 200), randi (255, s, s), 0.1) %! endfor %!test %! for s = [203 204 205 206] %! test_shapes (randi (255, s, s), randi (255, 52, 52), 0.1) %! endfor ## test with other classes %!test test_shapes (randi (255, 100, 100, "uint8"), randi (255, 10, 10, "uint8"), 0.1) %!test test_shapes (randi (255, 100, 100, "uint8"), randi (255, 10, 10), 0.1) %!test test_shapes (randi (255, 100, 100, "single"), randi (255, 10, 10, "single"), 0.9) %!test test_shapes (randi (255, 100, 100, "single"), randi (255, 10, 10), 0.9) image-2.16.1/inst/PaxHeaders.61586/imfill.m0000644000000000000000000000006215005110255015034 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imfill.m0000644000175000017500000003742215005110255016442 0ustar00avinoamavinoam00000000000000## Copyright (C) 2016 Hartmut Gimpel ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{bw2} =} imfill (@var{bw}, "holes") ## @deftypefnx {Function File} {@var{bw2} =} imfill (@var{bw}, @var{conn}, "holes") ## @deftypefnx {Function File} {@var{bw2} =} imfill (@var{bw}, @var{locations}) ## @deftypefnx {Function File} {@var{bw2} =} imfill (@var{bw}, @var{locations}, @var{conn}) ## @deftypefnx {Function File} {@var{I2} =} imfill (@var{I}) ## @deftypefnx {Function File} {@var{I2} =} imfill (@var{I}, "holes") ## @deftypefnx {Function File} {@var{I2} =} imfill (@var{I}, @var{conn}) ## @deftypefnx {Function File} {@var{I2} =} imfill (@var{I}, @var{conn}, "holes") ## Fill image holes or regions. ## ## The image to be filled can be binary @var{bw} or grayscale @var{I}. ## Regions are a series of connected elements whose values are lower than ## at least one of its neighbor elements. ## ## The option @qcode{"holes"}, default for grayscale images, can be used ## to fill all holes, i.e., regions without border elements. ## ## Alternatively, the argument @var{locations} are coordinates for elements ## in the regions to be filled. It can be a a @var{n}-by-1 vector of linear ## indices or a @var{n}-by-@var{d} matrix of subscript indices where @var{n} ## is the number of points and @var{d} is the number of dimensions in the ## image. ## ## The argument @var{conn} specifies the connectivity used for filling ## the region and defaults to ## @code{conndef (ndims (@var{img}, @qcode{"minimal"})} which is 4 for ## the case of 2 dimensional images. ## ## @example ## im = [0 1 0 ## 1 0 1 ## 0 1 0]; ## ## imfill (im, "holes") ## ans = ## ## 0 1 0 ## 1 1 1 ## 0 1 0 ## @end example ## ## @seealso{bwfill, bwselect, imreconstruct} ## @end deftypefn function filled = imfill (img, varargin) if (nargin < 1 || nargin > 3) print_usage (); endif if (! isimage (img)) error ("imfill: IMG and BW must be a binary or grayscale image"); endif ## Default parameter values conn = conndef (ndims (img), "minimal"); fill_holes = false; if (nargin () == 1) if (islogical (img)) ## imfill (BW) error ("imfill: interactive usage is not yet supported"); else ## syntax: imfill (img) fill_holes = true; endif elseif (nargin () == 2) opt2 = varargin{1}; if (ischar (opt2)) ## syntax: imfill (BW, "holes") or imfill (IMG, "holes") validatestring (opt2, {"holes"}, "imfill", "OPTION"); fill_holes = true; elseif (! islogical (img)) ## syntax: imfill (IMG, CONN) fill_holes = true; iptcheckconn (opt2, "imfill", "CONN"); conn = opt2; elseif (islogical (img) && isnumeric (opt2) && isindex (opt2)) ## syntax: imfill (BW, LOCATIONS) locations = check_loc (opt2, img); else error ("imfill: second argument must be 'holes', a connectivity specification, or an index array"); endif elseif (nargin () == 3) opt2 = varargin{1}; opt3 = varargin{2}; if (ischar (opt3)) ## syntax: imfill (BW, CONN, "holes") or imfill (IMG, CONN, "holes") validatestring (opt3, {"holes"}, "imfill", "OPTION"); iptcheckconn (opt2, "imfill", "CONN"); conn = opt2; fill_holes = true; elseif (islogical (img) && isnumeric (opt2) && isindex (opt2)) ## syntax: imfill (BW, LOCATIONS, CONN) iptcheckconn(opt3, "imfill", "CONN"); conn = opt3; locations = check_loc (opt2, img); elseif (islogical (img) && isscalar (opt2) && opt2 == 0) ## syntax: imfill (BW, 0, CONN) error ("imfill: interactive usage is not yet supported"); else print_usage (); endif endif if (fill_holes) ## Hole filling algorithm adapted from the book Gonzalez & Woods ## "Digital Image Processing" (3rd Edition), section 9.5.9 ## "Morphological Reconstruction", subsection "Filling holes" (page 682): ## ## "Let I(x,y) denote a binary image and suppose that we form a ## marker image F that is 0 everywhere, except at the image border ## where it is set to 1-I; that is, ## ## / 1 - I(x,y) if (x,y) is on the border of I ## F(x,y) = | ## \ 0 otherwise ## ## Then ## ## H = complement (reconstruct (F, complement (I))) ## ## is a binary image equal to I with all holes filled." ## ## This is generalized to grayscale images with: ## ## * (1-I) -> complement(I) ## * 0 value -> -Inf value if non logical ## ## step 1: define the mask and marker as complement of input ## step 2: set border elements of marker as false/-Inf ## step 3: do morphological reconstruction ## step 4: complement of reconstruction has the holes filled. ## ## Beware of unusual connectivity definition which changes what is ## defined as border pixels. For example, [0 0 0; 1 1 1; 0 0 0], ## means that top and bottom row of an image are not border pixels. mask = imcomplement (img); if (islogical (img)) marker = set_nonborder_pixels (mask, conn, false); else marker = set_nonborder_pixels (mask, conn, -Inf); endif filled = imreconstruct (marker, mask, conn); filled = imcomplement (filled); else ## Using explicitly given marker pixels instead of "holes" option ## should only be used for logical images ## no border padding necessary in this case mask = imcomplement (img); marker = false (size (img)); marker (locations) = mask (locations); filled = imreconstruct (marker, mask, conn); ## Adjusted step 4: add the filled hole(s) FILLED to the original image IMG filled = img | filled; endif endfunction ## Helper function for LOCATIONS input checking. ## Drops locations outside of the image and proceeds with a warning. ## Additionally transforms matrix indices to linear indices. function loc_lin_idx = check_loc (loc, img) sz = size (img); if (ndims (loc) != 2) error ("imfill: LOCATIONS must be a n-by-1 or n-by-d sized index array"); elseif (columns (loc) == 1) # linear indices given idx_outside = loc > numel (img); loc(idx_outside) = []; loc_lin_idx = loc; elseif (columns (loc) == ndims (img)) # subscript indices given idx_outside = any (loc > sz, 2); loc(idx_outside,:) = []; loc_lin_idx = sub2ind (sz, num2cell (loc, 1){:}); else error ("imfill: LOCATIONS must be a n-by-1 or n-by-d sized index array"); endif if (any (idx_outside)) warning ("imfill: ignored LOCATIONS outside of image borders"); endif endfunction ## Set non border of pixels of IMG according to connectivity CONN to VAL. ## The only tricky part is handling unusual connectivity such as ## [0 0 0; 1 1 1; 0 0 0] which define top and bottom row as non border. ## Another case is 8 connectivity in 3d images. In such case, the first ## row and column of each page is border elements, but the first and last ## page itself are not. function marker = set_nonborder_pixels (img, conn, val) sz = size (img); nonborder_idx = repmat ({":"}, ndims (img), 1); conn_idx_tmp = repmat ({":"}, ndims (conn), 1); for dim = 1:ndims (conn) conn_idx = conn_idx_tmp; ## Because connectivity is symmetric by definition, we only need ## to check the first slice of that dimension. conn_idx{dim} = [1]; if (any (conn(conn_idx{:})(:))) nonborder_idx{dim} = 2:(sz(dim) -1); endif endfor marker = img; marker(nonborder_idx{:}) = val; endfunction ## test the possible INPUT IMAGE TYPES %!test %! I = uint8 (5.*[1 1 1; 1 0 1; 1 1 1]); %! bw = logical ([1 1 1; 1 0 1; 1 1 1]); %! I2 = uint8 (5.*ones (3)); %! bw2 = logical (ones (3)); %! %! assert (imfill (int8 (I)), int8 (I2)) %! assert (imfill (int16 (I)), int16 (I2)) %! assert (imfill (int32 (I)), int32 (I2)) %! assert (imfill (int64 (I)), int64 (I2)) %! assert (imfill (uint8 (I)), uint8 (I2)) %! assert (imfill (uint16 (I)), uint16 (I2)) %! assert (imfill (uint32 (I)), uint32 (I2)) %! assert (imfill (uint64 (I)), uint64 (I2)) %! assert (imfill (single (I)), single (I2)) %! assert (imfill (double (I)), double (I2)) %! assert (imfill (bw, "holes"), bw2) %! assert (imfill (uint8 (bw)), uint8 (bw2)) %!error %! imfill (i + ones (3, 3)); # complex input %!error %! imfill (sparse (double (I))); # sparse input %!error %! imfill (); %!error %! imfill (true (3), 4, "holes", 5) %!error %! imfill (false (3), ones (2, 3)) %!error %! imfill (false (3), ones (2, 3), 4) %!error %! imfill (false (3)) %!error %! imfill (false (3), 0, 4) %!warning %! bw = logical ([1 1 1; 1 0 1; 1 1 1]); %! assert (imfill (bw, [5 5]), bw) %! assert (imfill (bw, 15), bw) %! %! bw = repmat (bw, [1 1 3]); %! assert (imfill (bw, 30), bw) %! assert (imfill (bw, [2 2 5]), bw) ## test BINARY hole filling and binary filling from starting point %!test %! bw = logical ([1 0 0 0 0 0 0 0 %! 1 1 1 1 1 0 0 0 %! 1 0 0 0 1 0 1 0 %! 1 0 0 0 1 1 1 0 %! 1 1 1 1 0 1 1 1 %! 1 0 0 1 1 0 1 0 %! 1 0 0 0 1 0 1 0 %! 1 0 0 0 1 1 1 0]); %! bw2 = logical ([1 0 0 0 0 0 0 0 %! 1 1 1 1 1 0 0 0 %! 1 1 1 1 1 0 1 0 %! 1 1 1 1 1 1 1 0 %! 1 1 1 1 1 1 1 1 %! 1 0 0 1 1 1 1 0 %! 1 0 0 0 1 1 1 0 %! 1 0 0 0 1 1 1 0]); %! bw3 = logical ([1 0 0 0 0 0 0 0 %! 1 1 1 1 1 0 0 0 %! 1 1 1 1 1 0 1 0 %! 1 1 1 1 1 1 1 0 %! 1 1 1 1 0 1 1 1 %! 1 0 0 1 1 0 1 0 %! 1 0 0 0 1 0 1 0 %! 1 0 0 0 1 1 1 0]); %! assert (imfill (bw, "holes"), bw2) %! assert (imfill (bw, 8, "holes"), bw2) %! assert (imfill (bw, 4, "holes"), bw2) %! assert (imfill (bw, [3 3]), bw3) %! assert (imfill (bw, 19), bw3) %! assert (imfill (bw, [3 3], 4), bw3) %! assert (imfill (bw, 19, 4), bw3) %! assert (imfill (bw, [3 3], 8), bw2) %! assert (imfill (bw, 19, 8), bw2) %! assert (imfill (bw, [19; 20]), bw3) %! assert (imfill (bw, [19; 20], 4), bw3) %! assert (imfill (bw, [19; 20], 8), bw2) %!warning %! bw = logical ([1 1 1 1 1 1 1 %! 1 0 0 0 0 0 1 %! 1 0 1 1 1 0 1 %! 1 0 1 0 1 0 1 %! 1 0 1 1 1 0 1 %! 1 0 0 0 0 0 1 %! 1 1 1 1 1 1 1]); %! bw44 = logical ([1 1 1 1 1 1 1 %! 1 0 0 0 0 0 1 %! 1 0 1 1 1 0 1 %! 1 0 1 1 1 0 1 %! 1 0 1 1 1 0 1 %! 1 0 0 0 0 0 1 %! 1 1 1 1 1 1 1]); %! bw9 = logical ([1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 0 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1]); %! assert (imfill (bw, "holes"), logical (ones (7))) %! assert (imfill (bw, [4 4]), bw44) %! assert (imfill (bw, 9), bw9) %! assert (imfill (bw, [4 4; 10 10]), bw44) %!test %! bw = logical ([1 1 0 1 1]); %! assert (imfill (bw, "holes"), bw) %! bw = logical([1 1 0 1 1; 1 1 1 1 1]); %! assert (imfill (bw, "holes"), bw) ## test hole filling with extravagant connectivity definitions %!test %! I = zeros (5); %! I(:, [2 4]) = 1; %! I2_expected = [0 1 1 1 0 %! 0 1 1 1 0 %! 0 1 1 1 0 %! 0 1 1 1 0 %! 0 1 1 1 0]; %! I2 = imfill (I, [0 0 0; 1 1 1; 0 0 0], "holes"); %! assert (I2, I2_expected) %!test %! I = zeros (5); %! I(:, [2 4]) = 1; %! I2_expected = I; %! I2 = imfill (I, [0 1 0; 0 1 0; 0 1 0], "holes"); %! assert (I2, I2_expected) %!test # this test is Matlab compatible %! I = zeros (5); %! I(:, [2 4]) = 1; %! I2_expected = inf .* ones (5); %! I2 = imfill (I, [0 0 0; 0 1 0; 0 0 0], "holes"); %! assert (I2, I2_expected) %!test %! I = false (5); %! I(:, [2 4]) = true; %! I2_expected = true (5); %! I2 = imfill (I, [0 0 0; 0 1 0; 0 0 0], "holes"); %! assert (I2, I2_expected) ## test GRAYSCALE hole filling %!test %! I = uint8 ([10 20 80 85 20 %! 15 90 03 25 88 %! 05 85 02 50 83 %! 90 04 03 80 80 %! 10 81 83 85 30]); %! I2 = uint8 ([10 20 80 85 20 %! 15 90 80 80 88 %! 05 85 80 80 83 %! 90 80 80 80 80 %! 10 81 83 85 30]); %! I3 = uint8 ([10 20 80 85 20 %! 15 90 05 25 88 %! 05 85 05 50 83 %! 90 05 05 80 80 %! 10 81 83 85 30]); %! assert (imfill (I), I2) %! assert (imfill (I, 4), I2) %! assert (imfill (I, 4, "holes"), I2) %! assert (imfill (I, 8), I3) %! assert (imfill (I, "holes"), I2) ## Test dimensions of length 1 whose extremes may or may not be on the ## border due to less typical connectivity. %!test %! v_line = [0 1 0; 0 1 0; 0 1 0]; %! h_line = [0 0 0; 1 1 1; 0 0 0]; %! im = [0 1 0 0 1 0]; %! %! assert (imfill (im, h_line, "holes"), [0 1 1 1 1 0]) %! assert (imfill (im, v_line, "holes"), [0 1 0 0 1 0]) %! assert (imfill (im', h_line, "holes"), [0 1 0 0 1 0]') %! assert (imfill (im', v_line, "holes"), [0 1 1 1 1 0]') %! %! im = repmat (im, [1 1 5]); %! assert (imfill (im, h_line, "holes"), repmat ([0 1 1 1 1 0], [1 1 5])) %! assert (imfill (im, v_line, "holes"), im) %! %! im = permute (im, [2 1 3]); %! assert (imfill (im, h_line, "holes"), im) %! assert (imfill (im, v_line, "holes"), repmat ([0 1 1 1 1 0]', [1 1 5])) %!test %! im = logical ([0 0 0 0 0 0 %! 0 1 1 1 1 0 %! 0 1 0 0 1 0 %! 0 1 1 1 1 0 %! 0 0 0 0 0 0]); %! fi = logical ([0 0 0 0 0 0 %! 0 1 1 1 1 0 %! 0 1 1 1 1 0 %! 0 1 1 1 1 0 %! 0 0 0 0 0 0]); %! %! assert (imfill (cat (3, im, im, im), 8, 'holes'), cat (3, fi, fi, fi)) %! assert (imfill (cat (3, im, im, im), 'holes'), cat (3, im, im, im)) %! assert (imfill (cat (3, fi, im, fi), 'holes'), cat (3, fi, fi, fi)) %!test %! emp = false (5, 6); %! im = logical ([0 0 0 0 0 0 %! 0 1 1 1 1 0 %! 0 1 0 1 0 1 %! 0 1 1 1 1 0 %! 0 0 0 0 0 0]); %! fi = logical ([0 0 0 0 0 0 %! 0 1 1 1 1 0 %! 0 1 1 1 1 1 %! 0 1 1 1 1 0 %! 0 0 0 0 0 0]); %! fi1 = logical ([0 0 0 0 0 0 %! 0 1 1 1 1 0 %! 0 1 1 1 0 1 %! 0 1 1 1 1 0 %! 0 0 0 0 0 0]); %! fi2 = logical ([0 0 0 0 0 0 %! 0 1 1 1 1 0 %! 0 1 0 1 1 1 %! 0 1 1 1 1 0 %! 0 0 0 0 0 0]); %! %! assert (imfill (cat (3, im, im, im), [3 3 2]), cat (3, fi1, fi1, fi1)) %! assert (imfill (cat (3, im, im, im), [3 5 2]), cat (3, fi2, fi2, fi2)) %! assert (imfill (cat (3, im, im, im), [3 3 2; 3 5 2]), cat (3, fi, fi, fi)) %! assert (imfill (cat (3, emp, im, emp), [3 3 2]), true (5, 6, 3)) image-2.16.1/inst/PaxHeaders.61586/hough_circle.m0000644000000000000000000000006215005110255016213 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/hough_circle.m0000644000175000017500000000632215005110255017614 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{accum}= hough_circle (@var{bw}, @var{r}) ## Perform the Hough transform for circles with radius @var{r} on the ## black-and-white image @var{bw}. ## ## As an example, the following shows how to compute the Hough transform for circles ## with radius 3 or 7 in the image @var{im} ## @example ## bw = edge(im); ## accum = hough_circle(bw, [3, 7]); ## @end example ## If @var{im} is an NxM image @var{accum} will be an NxMx2 array, where ## @var{accum}(:,:,1) will contain the Hough transform for circles with ## radius 3, and @var{accum}(:,:,2) for radius 7. To find good circles you ## now need to find local maximas in @var{accum}, which can be a hard problem. ## If you find a local maxima in @var{accum}(row, col, 1) it means that a ## good circle exists with center (row,col) and radius 3. ## ## @seealso{houghtf} ## @end deftypefn function accum = hough_circle(bw, r) ## Check input arguments if (nargin != 2) error("hough_circle: wrong number of input arguments"); endif if (! ismatrix (bw)) error("hough_circle: BW must be a 2-dimensional matrix"); endif if (!isvector(r) || !isreal(r) || any(r<0)) error("hough_circle: radius arguments must be a positive vector or scalar"); endif ## Create the accumulator array. accum = zeros(size(bw,1), size(bw,2), length(r)); ## Find the pixels we need to look at [R, C] = find(bw); ## Iterate over different radius for j = 1:length(r) rad = r(j); ## Compute a filter containing the circle we're looking for. circ = circle(rad); ## Iterate over all interesting image points for i =1:length(R) row = R(i); col = C(i); ## Compute indices for the accumulator array a_rows = max(row-rad,1) : min(row+rad, size(accum,1)); a_cols = max(col-rad,1) : min(col+rad, size(accum,2)); ## Compute indices for the circle array (the filter) c_rows = max(rad-row+2,1) : min(rad-row+1+size(accum,1), size(circ,1)); c_cols = max(rad-col+2,1) : min(rad-col+1+size(accum,2), size(circ,2)); ## Update the accumulator array accum( a_rows, a_cols, j ) += circ ( c_rows, c_cols ); endfor endfor endfunction ## Small auxilary function that creates an (2r+1)x(2r+1) image containing ## a circle with radius r and center (r+1, r+1). function circ = circle(r) circ = zeros(round(2*r+1)); col = 1:size(circ,2); for row=1:size(circ,1) tmp = (row-(r+1)).^2 + (col-(r+1)).^2; circ(row,col) = (tmp <= r^2); endfor circ = bwmorph(circ, 'remove'); endfunction image-2.16.1/inst/PaxHeaders.61586/imhmax.m0000644000000000000000000000006215005110255015043 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imhmax.m0000644000175000017500000001360715005110255016450 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} @ imhmax (@var{im}, @var{h}) ## @deftypefnx {Function File} {} @ imhmax (@var{im}, @var{h}, @var{conn}) ## Caculate the morphological h-maximum transform of an image @var{im}. ## ## This function removes all regional maxima in the grayscale image @var{im} whose ## height is lower or equal to the given threshold level @var{h}, and it decreases the height ## of the remaining regional maxima by the value of @var{h}. (A "regional maximum" ## is defined as a connected component of pixels with an equal pixel value ## that is higher than the value of all its neighboring pixels. And the ## "height" of a regional maximum can be thought of as minimum pixel value difference ## between the regional maximum and its neighboring minima.) ## ## The input image @var{im} needs to be a real and nonsparse numeric array (of any dimension), ## and the height parameter @var{h} a non-negative scalar number. ## ## The definition of "neighborhood" for this morphological operation ## can be set with the connectivity parameter @var{conn}, ## which defaults to 8 for 2D images, to 26 for 3D images and to ## @code{conn(ndims(n), "maximal")} in general. @var{conn} can be given as scalar value ## or as a boolean matrix (see @code{conndef} for details). ## ## The output is a transformed grayscale image of same type and ## shape as the input image @var{im}. ## ## @seealso{imhmin, imregionalmax, imextendedmax, imreconstruct} ## @end deftypefn ## Algorithm: ## * The 'classical' reference for this morphological h-maximum function ## is the book "Morphological Image Analysis" by P. Soille ## (Springer, 2nd edition, 2004), chapter 6.3.4 "Extended and h-extrema". ## It says: "This is achieved by performing the reconstruction by dilation ## of [a grayscale image] f from f-h: ## HMAX_h(f) = R^delta_f (f - h)". ## * A more easily accessible reference is for example the following ## web page by Régis Clouard: ## https://clouard.users.greyc.fr/Pantheon/experiments/morphology/index-en.html#extremum ## It says: "It is defined as the [morphological] reconstruction by dilation ## of [a grayscale image] f subtracted by a height h." ## (We will call the grayscale image im instead of f.) function im2 = imhmax (im, h, varargin) ## retrieve input parameters, set default value: if (nargin == 3) conn = varargin{1}; iptcheckconn (conn, "imhmax", "CONN"); elseif (nargin == 2) conn = conndef (ndims (im), "maximal"); else print_usage (); endif ## check input parameters: if (! isnumeric (im) || ! isreal (im) || issparse (im) ) error ("imhmax: IM must be a real and nonsparse numeric array"); endif if (! isnumeric (h) || ! isscalar (h) || ! isreal (h) || (h<0) ) error ("imhmax: H must be a non-negative scalar number"); endif ## do the actual calculation: im2 = imreconstruct ((im-h), im, conn); endfunction %!shared im0, im0_h2_out %! im0 = uint8 ([0 0 0 0 0; %! 0 1 2 1 0; %! 0 2 5 2 0; %! 0 1 2 1 0; %! 0 0 0 0 0]); %! im0_h2_out = uint8 ([0 0 0 0 0; %! 0 1 2 1 0; %! 0 2 3 2 0; %! 0 1 2 1 0; %! 0 0 0 0 0]); ## test input syntax: %!error imhmax () %!error imhmax (im0) %!error imhmax ("hello", 2) %!error imhmax (i.*im0, 2) %!error imhmax (sparse (im0), 2) %!error imhmax (im0, -2) %!error imhmax (im0, 'a') %!error imhmax (im0, ones (2)) %!error imhmax (im0, 2*i) %!assert (imhmax (im0, 2), im0_h2_out) %!assert (imhmax (double (im0), 2), double (im0_h2_out)) %!assert (imhmax (im0, 2, 8), im0_h2_out) %!assert (imhmax (im0, 2, 4), im0_h2_out) %!assert (imhmax (im0, 2, true (3)), im0_h2_out) ## test output class and shape: %!test %! out = imhmax (double (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "double") %!test %! out = imhmax (single (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "single") %!test %! out = imhmax (uint8 (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "uint8") %!test %! out = imhmax (uint16 (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "uint16") %!test %! im = cat (3, im0, im0, im0, im0); %! out = imhmax (im, 2); %! assert (size (out), size (im)) ## test calculation result: %!test %! im = zeros (10); %! im(2:4, 2:4) = 3; %! im(6:8, 6:8) = 8; %! expected_4 = zeros (10); %! expected_4(6:8, 6:8) = 4; %! expected_2 = zeros (10); %! expected_2(2:4, 2:4) = 1; %! expected_2(6:8, 6:8) = 6; %! out = imhmax (im, 4); %! assert (out, expected_4, eps) %! out = imhmax (im, 2); %! assert (out, expected_2, eps) %! out = imhmax (0.1 .* im, 0.4); %! assert (out, 0.1 .* expected_4, eps) %!test %! im2 = zeros (10); %! im2(2:4, 2:4) = 3; %! im2(6:9, 6:9)=8; %! im2(5, 5)=8; %! im2(6, 7)=0; %! im2(7, 8)=0; %! expected_4 = zeros (10); %! expected_4(6:9, 6:9) = 4; %! expected_4(5, 5) = 4; %! expected_4(6, 7) = 0; %! expected_4(7, 8) = 0; %! expected_8 = expected_4; %! expected_8(2:4, 2:4) = 3; %! out2 = imhmax (im2, 4); %! assert (out2, expected_8, eps) %! out2 = imhmax (im2, 4, 4); %! assert (out2, expected_4, eps) %! out2 = imhmax (im2, 4, 8); %! assert (out2, expected_8, eps) image-2.16.1/inst/PaxHeaders.61586/checkerboard.m0000644000000000000000000000006215005110255016174 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/checkerboard.m0000644000175000017500000001537615005110255017606 0ustar00avinoamavinoam00000000000000## Copyright (C) 2012-2016 Carnë Draug ## Copyright (C) 2012 Pantxo Diribarne ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} checkerboard () ## @deftypefnx {Function File} {} checkerboard (@var{side}) ## @deftypefnx {Function File} {} checkerboard (@var{side}, @var{size}) ## @deftypefnx {Function File} {} checkerboard (@var{side}, @var{M}, @var{N}) ## @deftypefnx {Function File} {} checkerboard (@var{side}, @var{M}, @var{N}, @var{P}, @dots{}) ## Create checkerboard. ## ## The checkerboard is created by repeating a tile pattern and creating ## a block matrix of size @var{size}, or @var{M}x@var{N}. The tile pattern ## itself is made of four squares of @var{side} pixels wide. Note how the ## number of squares is twice of @var{size}. ## ## The tile pattern is white on black on the right side, and grey on black ## on the left side of the matrix. ## ## At the simplest case, a 2 by 2 pattern with squares of 1 pixel side. ## ## @example ## checkerboard (1, [2 2]) ## @result{} ## 0.0 1.0 0.0 0.7 ## 1.0 0.0 0.7 0.0 ## 0.0 1.0 0.0 0.7 ## 1.0 0.0 0.7 0.0 ## @end example ## ## Defaults to 4x4 tiles 10 pixels wide. ## ## N-Dimensional checkerboards are supported with @var{size} of any length ## or specifying dimension length arguments, i.e., @var{M}, @var{N}, @var{P}, ## @dots{}. ## ## @seealso{ndgrid, repmat} ## @end deftypefn function [board] = checkerboard (side = 10, varargin) if (nargin > 0 && ! (isscalar (side) && isnumeric (side) && side == fix (side) && side >= 0)) error ("checkerboard: SIDE must be a non-negative integer") endif if (numel (varargin) == 0) nd = 2; lengths = [4 4]; else if (any (! cellfun ("isnumeric", varargin))) error ("checkerboard: SIZE or MxNx... list must be numeric"); endif first_var = varargin{1}; if (numel (varargin) == 1) if (isscalar (first_var)) # checkerboard (SIDE, M) lengths = [first_var first_var]; else # checkerboard (SIDE, [M N P ...]) lengths = first_var; endif else # checkerboard (SIDE, M, N, P, ...) if (any (cellfun ("numel", varargin) > 1)) error ("checkerboard: M, N, P, ... must be numeric scalars") endif lengths = cell2mat (varargin); endif endif if (! isvector (lengths) || any (lengths < 0) || any (lengths != fix (lengths))) error ("checkerboard: SIZE or MxNx... list must be non-negative integer") endif nd = numel (lengths); grids = nthargout (1:nd, @ndgrid, linspace (-1, 1, 2*side)); tile = grids{1}; for d = 2:nd tile .*= grids{d}; endfor tile = tile < 0; board = double (repmat (tile, lengths)); ## Set left side of checkerboard to grey on black (0.7 instead of 1). ## ## ideally we would have an option to specify the dimension to do this ## (instead of dimension two - left and right). However, then we can't ## easily differentiate between length of last dimension and the number ## of dimension to do this, so leave it up to the user to permute the ## dimensions after. left_idx = repmat ({":"}, 1, nd); nc = columns (board); left_idx{2} = (nc/2 +1):nc; board(left_idx{:}) *= 0.7; endfunction %!demo %! ## Simplest case, default checkerboard size: %! ## 8 by 8 checkerboard, with squares 10 pixel wide %! board = checkerboard (); %! imshow (board) %!demo %! ## Simplest case, default checkerboard size: %! ## 8 by 16 checkerboard, with squares 5 pixel wide %! board = checkerboard (5, 4, 8); %! imshow (board) ## Special case of "SIZE == 0" still respects number of dimensions. %!assert (checkerboard (0), zeros (0, 0)) %!assert (checkerboard (0, 3), zeros (0, 0)) %!assert (checkerboard (0, 2, 4), zeros (0, 0)) %!assert (checkerboard (0, 2, 4, 3), zeros (0, 0, 0)) %!assert (checkerboard (0, 2, 4, 3, 2), zeros (0, 0, 0, 0)) ## Other special cases that leads to empty checkerboards. %!assert (checkerboard (1, 4, 2, 3, 0), zeros (8, 4, 6, 0)) %!assert (checkerboard (1, 4, 0, 3, 2), zeros (8, 0, 6, 4)) %!assert (checkerboard (2, 4, 0, 3, 2), zeros (16, 0, 12, 8)) %!test %! out = zeros (80); %! i1 = ((1:20:80) + (0:9)')(:); %! i2 = ((11:20:80) + (0:9)')(:); %! out(i1, i2) = 1; %! out(i2, i1) = 1; %! i1r = ((41:20:80) + (0:9)')(:); %! i2r = ((51:20:80) + (0:9)')(:); %! out(i2, i1r) = 0.7; %! out(i1, i2r) = 0.7; %! assert (checkerboard (), out) %! assert (checkerboard (10, 4, 4), out) %! assert (checkerboard (10, [4 4]), out) %! assert (checkerboard (10, [4; 4]), out) %!test %! out = zeros (8); %! out(2:2:8, 1:2:8) = 1; %! out(1:2:8, 2:2:8) = 1; %! out(1:2:8, 6:2:8) = 0.7; %! out(2:2:8, 5:2:8) = 0.7; %! assert (checkerboard (1), out) %! assert (checkerboard (1, 4), out) %! assert (checkerboard (1, 4, 4), out) %! assert (checkerboard (1, [4 4]), out) %!test %! out = zeros (10); %! out(2:2:10, 1:2:10) = 1; %! out(1:2:10, 2:2:10) = 1; %! out(1:2:10, 6:2:10) = 0.7; %! out(2:2:10, 7:2:10) = 0.7; %! assert (checkerboard (1, 5), out) %! assert (checkerboard (1, 5, 5), out) %! assert (checkerboard (1, [5 5]), out) %!test %! out = zeros (20); %! out([1:4:20 2:4:20], [3:4:20 4:4:20]) = 1; %! out([3:4:20 4:4:20], [1:4:20 2:4:20]) = 1; %! out([1:4:20 2:4:20], [11:4:20 12:4:20]) = 0.7; %! out([3:4:20 4:4:20], [13:4:20 14:4:20]) = 0.7; %! assert (checkerboard (2, 5), out) %! assert (checkerboard (2, 5, 5), out) %! assert (checkerboard (2, [5 5]), out) %!test %! out = zeros (4, 4, 4); %! out([1 3], 1, [1 3]) = 1; %! out([2 4], 2, [1 3]) = 1; %! out([1 3], 2, [2 4]) = 1; %! out([2 4], 1, [2 4]) = 1; %! out([1 3], 3, [1 3]) = 0.7; %! out([2 4], 4, [1 3]) = 0.7; %! out([1 3], 4, [2 4]) = 0.7; %! out([2 4], 3, [2 4]) = 0.7; %! assert (checkerboard (1, [2 2 2]), out) %! assert (checkerboard (1, 2, 2, 2), out) %!test %! out = zeros (8, 8, 8); %! out([1 2 5 6], [1 2], [1 2 5 6]) = 1; %! out([3 4 7 8], [3 4], [1 2 5 6]) = 1; %! out([1 2 5 6], [3 4], [3 4 7 8]) = 1; %! out([3 4 7 8], [1 2], [3 4 7 8]) = 1; %! out([1 2 5 6], [5 6], [1 2 5 6]) = 0.7; %! out([3 4 7 8], [7 8], [1 2 5 6]) = 0.7; %! out([1 2 5 6], [7 8], [3 4 7 8]) = 0.7; %! out([3 4 7 8], [5 6], [3 4 7 8]) = 0.7; %! assert (checkerboard (2, [2 2 2]), out) %! assert (checkerboard (2, 2, 2, 2), out) image-2.16.1/inst/PaxHeaders.61586/bestblk.m0000644000000000000000000000006215005110255015206 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bestblk.m0000644000175000017500000000654315005110255016614 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{blk_size} =} bestblk (@var{IMS}) ## @deftypefnx {Function File} {@var{blk_size} =} bestblk (@var{IMS}, @var{max}) ## @deftypefnx {Function File} {[@var{Mb}, @var{Nb}, @dots{}] =} bestblk (@dots{}) ## Calculate block best size for block processing. ## ## Given a matrix of size @var{IMS}, calculates the largest size for distinct ## blocks @var{blk_size}, that minimize padding and is smaller than or equal to ## @var{k} (defaults to 100) ## ## The output @var{blk_size} is a row vector for the block size. If there are ## multiple output arguments, the number of rows is assigned to the first ## (@var{Mb}), and the number of columns to the second (@var{Nb}), etc. ## ## To determine @var{blk_size}, the following is performed for each ## dimension: ## ## @enumerate ## @item ## If dimension @var{IMS} is less or equal than @var{k}, it returns the ## dimension value. ## ## @item ## If not, find the highest value between @code{min (dimension/10, k/2)} ## which minimizes padding. ## ## @end enumerate ## ## @seealso{blockproc, col2im, im2col} ## @end deftypefn function [varargout] = bestblk (ims, k = 100) if (nargin < 1 || nargin > 2) print_usage (); elseif (! isnumeric (ims) || ! isvector (ims) || any (ims(:) < 1)) error("bestblk: IMS must be a numeric vector of positive integers."); elseif (numel (ims) < 2) error ("bestblk: IMS must have at least 2 elements"); elseif (! isnumeric (k) || ! isscalar (k) || k < 1) error ("bestblk: K must be a positive scalar"); endif ims = floor (ims(:).'); k = floor (k); out = zeros (size (ims)); for dim = 1:numel (ims) if (ims(dim) <= k) out(dim) = ims(dim); else possible = k:-1:min (ims(dim) /10, k /2); [~, ind] = min (mod (-ims(dim), possible)); out(dim) = possible(ind); endif endfor if (nargout <= 1) varargout{1} = out; else varargout = mat2cell (out', ones (1, numel (out))); endif endfunction %!demo %! siz = bestblk ([200; 10], 50); %! disp (siz) %!error bestblk ("string") %!error bestblk ([100 200], "string") %!error <2 elements> bestblk ([100], 5) %!assert (bestblk ([ 10 12], 2), [ 2 2]); %!assert (bestblk ([ 10 12], 3), [ 2 3]); %!assert (bestblk ([300 100], 150), [150 100]); %!assert (bestblk ([256 128], 17), [ 16 16]); ## make sure we really pick the highest one %!assert (bestblk ([ 17 17], 3), [ 3 3]); ## Test default %!assert (bestblk ([230 470]), bestblk ([230 470], 100)) ## Test N-dimensional %!assert (bestblk ([10 12 10], 3), [2 3 2]); %!assert (bestblk ([ 9 12 9], 3), [3 3 3]); %!assert (bestblk ([10 12 10 11], 5), [5 4 5 4]); image-2.16.1/inst/PaxHeaders.61586/imperspectivewarp.m0000644000000000000000000000006215005110255017331 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imperspectivewarp.m0000644000175000017500000001216715005110255020736 0ustar00avinoamavinoam00000000000000## Copyright (C) 2006 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{warped} = imperspectivewarp(@var{im}, @var{P}, @var{interp}, @var{bbox}, @var{extrapval}) ## Applies the spatial perspective homogeneous transformation @var{P} to the image @var{im}. ## The transformation matrix @var{P} must be a 3x3 homogeneous matrix, or 2x2 or 2x3 ## affine transformation matrix. ## ## The optional argument @var{method} defines the interpolation method to be ## used. All methods supported by @code{interp2} can be used. By default, the ## @code{linear} method is used. ## ## For @sc{matlab} compatibility, the methods @code{bicubic} (same as ## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as ## @code{linear}) are also supported. ## ## By default the resulting image contains the entire warped image. In some situation ## you only parts of the warped image. The argument @var{bbox} controls this, and can ## be one of the following strings ## @table @code ## @item "loose" ## The entire warped result is returned. This is the default behavior. ## @item "crop" ## The central part of the image of the same size as the input image is returned. ## @item "same" ## The size and coordinate system, of the input image, are kept. ## @end table ## ## All values of the result that fall outside the original image will ## be set to @var{extrapval}. The default value of @var{extrapval} is 0. ## ## @seealso{imremap, imrotate, imresize, imshear, interp2} ## @end deftypefn function [warped] = imperspectivewarp(im, P, interp = "linear", bbox = "loose", extrapolation_value = 0) if (nargin < 2 || nargin > 5) print_usage (); elseif (! isimage (im)) error ("imperspectivewarp: IM must be a grayscale or RGB image.") elseif (! ischar (interp)) error ("imperspectivewarp: INTERP must be a string with interpolation method") elseif (! ischar (bbox) || ! any (strcmpi (bbox, {"loose", "crop", "same"}))) error ("imperspectivewarp: BBOX must be 'loose', 'crop' or 'same'"); elseif (! isscalar (extrapolation_value)) error ("imperspectivewarp: EXTRAPVAL must be a scalar"); endif interp = interp_method (interp); if (isnumeric (P) && ismatrix (P)) if (issquare(P) && rows(P) == 3) # 3x3 matrix if (P(3,3) != 0) P /= P(3,3); else error ("imperspectivewarp: P(3,3) must be non-zero"); endif elseif (rows(P) == 2 && (columns(P) == 2 || columns(P) == 3)) # 2x2 or 2x3 matrix P(3,3) = 1; else # unsupported matrix size error ("imperspectivewarp: transformation matrix must be 2x2, 2x3, or 3x3"); endif else error ("imperspectivewarp: transformation matrix not valid"); endif ## Do the transformation [y, x, tmp] = size(im); ## Transform corners corners = [1, 1, 1; 1, y, 1; x, 1, 1; x, y, 1]'; Tcorners = P*corners; Tx = Tcorners(1,:)./Tcorners(3,:); Ty = Tcorners(2,:)./Tcorners(3,:); ## Do cropping? x1 = round(min(Tx)); x2 = round(max(Tx)); y1 = round(min(Ty)); y2 = round(max(Ty)); # FIXME: This seems to work fine for rotations, but # somebody who knows computational geometry should # be able to come up with a better algorithm. if (strcmpi(bbox, "crop")) xl = x2 - x1 + 1; yl = y2 - y1 + 1; xd = (xl - x)/2; yd = (yl - y)/2; x1 += xd; x2 -= xd; y1 += yd; y2 -= yd; elseif (strcmpi(bbox, "same")) x1 = 1; x2 = x; y1 = 1; y2 = y; endif ## Transform coordinates [X, Y] = meshgrid(x1:x2, y1:y2); [sy, sx] = size(X); D = [X(:), Y(:), ones(sx*sy, 1)]'; PD = inv(P)*D; XI = PD(1,:)./PD(3,:); YI = PD(2,:)./PD(3,:); XI = reshape(XI, sy, sx); YI = reshape(YI, sy, sx); clear X Y D PD; warped = imremap (im, XI, YI, interp, extrapolation_value); endfunction %!demo %! ## Generate a synthetic image and show it %! I = tril(ones(100)) + abs(rand(100)); I(I>1) = 1; %! I(20:30, 20:30) = !I(20:30, 20:30); %! I(70:80, 70:80) = !I(70:80, 70:80); %! figure(), imshow(I); %! ## Resize the image to the double size and show it %! P = diag([1, 1, 0.5]); %! warped = imperspectivewarp(I, P); %! figure(), imshow(warped); %!demo %! ## Generate a synthetic image and show it %! I = tril(ones(100)) + abs(rand(100)); I(I>1) = 1; %! I(20:30, 20:30) = !I(20:30, 20:30); %! I(70:80, 70:80) = !I(70:80, 70:80); %! figure(), imshow(I); %! ## Rotate the image around (0, 0) by -0.4 radians and show it %! R = [cos(-0.4) sin(-0.4); -sin(-0.4) cos(-0.4)]; %! warped = imperspectivewarp(I, R, :, :, 0); %! figure(), imshow(warped); image-2.16.1/inst/PaxHeaders.61586/immaximas.m0000644000000000000000000000006215005110255015545 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/immaximas.m0000644000175000017500000001117315005110255017146 0ustar00avinoamavinoam00000000000000## Copyright (c) 2003-2005 Peter Kovesi ## School of Computer Science & Software Engineering ## The University of Western Australia ## http://www.csse.uwa.edu.au/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## The software is provided "as is", without warranty of any kind, express or ## implied, including but not limited to the warranties of merchantability, ## fitness for a particular purpose and noninfringement. In no event shall the ## authors or copyright holders be liable for any claim, damages or other ## liability, whether in an action of contract, tort or otherwise, arising from, ## out of or in connection with the software or the use or other dealings in the ## software. ## ## I've made minor changes compared to the original 'nonmaxsuppts' function developed ## by Peter Kovesi. The original is available at ## http://www.csse.uwa.edu.au/~pk/research/matlabfns/Spatial/nonmaxsuppts.m ## -- Søren Hauberg, 2008 ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{r}, @var{c}] =} immaximas (@var{im}, @var{radius}) ## @deftypefnx{Function File} {[@var{r}, @var{c}] =} immaximas (@var{im}, @var{radius}, @var{thresh}) ## @deftypefnx{Function File} {[@var{r}, @var{c}, @dots{}] =} immaximas (@dots{}) ## @deftypefnx{Function File} {[@dots{}, @var{val}] =} immaximas (@dots{}) ## Find local spatial maximas. ## ## Local spatial maximas should not be mistaken with regional maxima. ## See @code{imregionalmax} for the later. ## ## A local spatial maxima is ## defined as an image point with a value that is larger than all neighbouring ## values in a square region of width 2*@var{radius}+1. By default @var{radius} ## is 1, such that a 3 by 3 neighbourhood is searched. If the @var{thresh} input ## argument is supplied, only local maximas with a value greater than @var{thresh} ## are retained. ## ## The output vectors @var{r} and @var{c} contain the row-column coordinates ## of the local maximas. The actual values are computed to sub-pixel precision ## by fitting a parabola to the data around the pixel. If @var{im} is ## @math{N}-dimensional, then @math{N} vectors will be returned. ## ## If @var{im} is @math{N}-dimensional, and @math{N}+1 outputs are requested, ## then the last output will contain the image values at the maximas. Currently ## this value is not interpolated. ## ## @seealso{imregionalmax, ordfilt2, ordfiltn} ## @end deftypefn function varargout = immaximas(im, radius, thresh) ## Check input if (nargin == 0) error("immaximas: not enough input arguments"); endif if (nargin <= 1 || isempty(radius)) radius = 1; endif if (nargin <= 2) thresh = []; endif if (! isnumeric (im)) error("immaximas: IM must be a numeric array"); endif if (!isscalar(radius)) error("immaximas: second input argument must be a scalar or an empty matrix"); endif if (!isscalar(thresh) && !isempty(thresh)) error("immaximas: third input argument must be a scalar or an empty matrix"); endif ## Find local maximas nd = ndims(im); s = size(im); sze = 2*radius+1; mx = ordfiltn(im, sze^nd, ones(repmat(sze,1, nd), "logical"), "reflect"); mx2 = ordfiltn(im, sze^nd-1, ones(repmat(sze,1, nd), "logical"), "reflect"); # Find maxima, threshold immx = (im == mx) & (im != mx2); if (!isempty(thresh)) immx &= (im>thresh); endif ## Find local maximas and fit parabolas locally ind = find(immx); [sub{1:nd}] = ind2sub(s, ind); if (!isempty(ind)) w = 1; # Width that we look out on each side of the feature point to fit a local parabola ws = w*cumprod([1; s(:)]); ## We fit a parabola to the points in each dimension for d = 1:nd ## Indices of points above, below, left and right of feature point indminus1 = max(ind-ws(d), 1); indplus1 = min(ind+ws(d), numel(immx)); ## Solve quadratic c = im(ind); a = (im(indminus1) + im(indplus1))/2 - c; b = a + c - im(indminus1); shift = -w*b./(2*a); # Maxima of quadratic ## Move point sub{d} += shift; endfor endif ## Output varargout(1:nd) = sub(1:nd); if (nargout > nd) varargout{nd+1} = im(ind); endif endfunction image-2.16.1/inst/PaxHeaders.61586/poly2mask.m0000644000000000000000000000006215005110255015501 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/poly2mask.m0000644000175000017500000001553615005110255017111 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{BW} = } poly2mask (@var{x},@var{y},@var{m},@var{n}) ## Convert a polygon to a region mask. ## ## BW=poly2mask(x,y,m,n) converts a polygon, specified by a list of ## vertices in @var{x} and @var{y} and returns in a @var{m}-by-@var{n} ## logical mask @var{BW} the filled polygon. Region inside the polygon ## is set to 1, values outside the shape are set to 0. ## ## @var{x} and @var{y} should always represent a closed polygon, first ## and last points should be coincident. If they are not poly2mask will ## close it for you. If @var{x} or @var{y} are fractional they are ## nearest integer. ## ## If all the polygon or part of it falls outside the masking area ## (1:m,1:n), it is discarded or clipped. ## ## This function uses scan-line polygon filling algorithm as described ## in http://www.cs.rit.edu/~icss571/filling/ with some minor ## modifications: capability of clipping and scan order, which can ## affect the results of the algorithm (algorithm is described not to ## reach ymax, xmax border when filling to avoid enlarging shapes). In ## this function we scan the image backwards (we begin at ymax and end ## at ymin), and we don't reach ymin, xmin, which we believe should be ## compatible with MATLAB. ## @end deftypefn ## TODO: check how to create a logical BW without any conversion function BW = poly2mask (x, y, m, n) if (nargin != 4) print_usage (); endif ## check x and y x = round (x (:).'); y = round (y (:).'); if (length (x) < 3) error ("poly2mask: polygon must have at least 3 vertices."); endif if (length (x) != length (y)) error ("poly2mask: length of x doesn't match length of y."); endif ## create output matrix BW = false (m, n); ## close polygon if needed if ((x (1) != x (length (x))) || (y (1) != y (length (y)))) x = horzcat (x, x (1)); y = horzcat (y, y (1)); endif ## build global edge table ex = [x(1:length (x) - 1); x(1, 2:length (x))]; ## x values for each edge ey = [y(1:length (y) - 1); y(1, 2:length (y))]; ## y values for each edge idx = (ey (1, :) != ey (2, :)); ## eliminate horizontal edges ex = ex (:, idx); ey = ey (:, idx); eminy = min (ey); ## minimum y for each edge emaxy = max (ey); ## maximum y for each edge t = (ey == [eminy; eminy]); ## values associated to miny exminy = ex (:) (t); ## x values associated to min y exmaxy = ex (:) (!t); ## x values associated to max y emaxy = emaxy.'; ## we want them vertical now... eminy = eminy.'; m_inv = (exmaxy - exminy)./(emaxy - eminy); ## calculate inverse slope ge = [emaxy, eminy, exmaxy, m_inv]; ## build global edge table ge = sortrows (ge, [1, 3]); ## sort on eminy and exminy ## we add an extra dummy edge at the end just to avoid checking ## while indexing it ge = [-Inf, -Inf, -Inf, -Inf; ge]; ## initial parity is even (0) parity = 0; ## init scan line set to bottom line sl = ge (size (ge, 1), 1); ## init active edge table ## we use a loop because the table is sorted and edge list could be ## huge ae = []; gei = size (ge, 1); while (sl == ge (gei, 1)) ae = [ge(gei, 2:4); ae]; gei -= 1; endwhile ## calc minimum y to draw miny = min (y); if (miny < 1) miny = 1; endif while (sl >= miny) ## check vert clipping if (sl <= m) ## draw current scan line ## we have to round because 1/m is fractional ie = round (reshape (ae (:, 2), 2, size (ae, 1)/2)); ## this discards left border of image (this differs from version at ## http://www.cs.rit.edu/~icss571/filling/ which discards right ## border) but keeps an exception when the point is a vertex. ie (1, :) += (ie (1, :) != ie (2, :)); ## we'll clip too, just in case m,n is not big enough ie (1, (ie (1, :) < 1)) = 1; ie (2, (ie (2, :) > n)) = n; ## we eliminate segments outside window ie = ie (:, (ie (1, :) <= n)); ie = ie (:, (ie (2, :) >= 1)); for i = 1:columns (ie) BW (sl, ie (1, i):ie (2, i)) = true; endfor endif ## decrement scan line sl -= 1; ## eliminate edges that eymax==sl ## this discards ymin border of image (this differs from version at ## http://www.cs.rit.edu/~icss571/filling/ which discards ymax). ae = ae ((ae (:, 1) != sl), :); ## update x (x1=x0-1/m) ae (:, 2) -= ae (:, 3); ## update ae with new values while (sl == ge (gei, 1)) ae = vertcat (ae, ge (gei, 2:4)); gei -= 1; endwhile ## order the edges in ae by x value if (rows (ae) > 0) ae = sortrows (ae, 2); endif endwhile endfunction ## This should create a filled octagon %!demo %! s = [0:pi/4:2*pi]; %! x = cos (s) * 90 + 101; %! y = sin (s) * 90 + 101; %! bw = poly2mask(x, y, 200, 200); %! imshow (bw); ## This should create a 5-vertex star %!demo %! s = [0:2*pi/5:pi*4]; %! s = s ([1, 3, 5, 2, 4, 6]); %! x = cos (s) * 90 + 101; %! y = sin (s) * 90 + 101; %! bw = poly2mask (x, y, 200, 200); %! imshow (bw); %!# Convex polygons %!shared xs, ys, Rs, xt, yt, Rt %! xs=[3,3,10,10]; %! ys=[4,12,12,4]; %! Rs=zeros(16,14); %! Rs(5:12,4:10)=1; %! Rs=logical(Rs); %! xt=[1,4,7]; %! yt=[1,4,1]; %! Rt=[0,0,0,0,0,0,0; %! 0,0,1,1,1,1,0; %! 0,0,0,1,1,0,0; %! 0,0,0,1,0,0,0; %! 0,0,0,0,0,0,0]; %! Rt=logical(Rt); %!assert(poly2mask(xs,ys,16,14),Rs); # rectangle %!assert(poly2mask(xs,ys,8,7),Rs(1:8,1:7)); # clipped %!assert(poly2mask(xs-7,ys-8,8,7),Rs(9:16,8:14)); # more clipping %!assert(poly2mask(xt,yt,5,7),Rt); # triangle %!assert(poly2mask(xt,yt,3,3),Rt(1:3,1:3)); # clipped %!# Concave polygons %!test %! x=[3,3,5,5,8,8,10,10]; %! y=[4,12,12,8,8,11,11,4]; %! R=zeros(16,14); %! R(5:12,4:5)=1; %! R(5:8,6:8)=1; %! R(5:11,9:10)=1; %! R=logical(R); %! assert(poly2mask(x,y,16,14), R); %!# Complex polygons %!test %! x=[1,5,1,5]; %! y=[1,1,4,4]; %! R=[0,0,0,0,0,0; %! 0,0,1,1,0,0; %! 0,0,1,1,0,0; %! 0,1,1,1,1,0; %! 0,0,0,0,0,0]; %! R=logical(R); %! assert(poly2mask(x,y,5,6), R); image-2.16.1/inst/PaxHeaders.61586/ycbcr2rgb.m0000644000000000000000000000006215005110255015437 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/ycbcr2rgb.m0000644000175000017500000000444315005110255017042 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{cmap} =} ycbcr2rgb (@var{YCbCrmap}) ## @deftypefnx {Function File} {@var{RGB} =} ycbcr2rgb (@var{YCbCr}) ## @deftypefnx {Function File} {@dots{} =} ycbcr2rgb (@dots{}, [@var{Kb} @var{Kr}]) ## @deftypefnx {Function File} {@dots{} =} ycbcr2rgb (@dots{}, @var{standard}) ## Convert YCbCr color space to RGB. ## ## The conversion changes the image @var{YCbCr} or colormap @var{YCbCrmap}, ## from the YCbCr (luminance, chrominance blue, and chrominance red) ## color space to RGB values. @var{YCbCr} must be of class double, single, ## uint8, or uint16. ## ## The formula used for the conversion is dependent on two constants, @var{Kb} ## and @var{Kr} which can be specified individually, or according to existing ## standards: ## ## @table @asis ## @item "601" (default) ## According to the ITU-R BT.601 (formerly CCIR 601) standard. Its values ## of @var{Kb} and @var{Kr} are 0.114 and 0.299 respectively. ## @item "709" ## According to the ITU-R BT.709 standard. Its values of @var{Kb} and ## @var{Kr} are 0.0722 and 0.2116 respectively. ## @item "2020" ## According to the ITU-R BT.2020 standard. Its values of @var{Kb} and ## @var{Kr} are 0.0593 and 0.2627 respectively. ## @end table ## ## @seealso{hsv2rgb, ntsc2rgb, rgb2hsv, rgb2ntsc, rgb2ycbcr} ## @end deftypefn function rgb = ycbcr2rgb (ycbcr, standard = "601") if (nargin < 1 || nargin > 2) print_usage (); endif rgb = ycbcrfunc ("ycbcr2rgb", ycbcr, standard); endfunction %!assert (ycbcr2rgb (rgb2ycbcr (jet (10))), jet (10), 0.00001); %!assert (class (ycbcr2rgb (single (rand (5, 5, 3)))), "single") image-2.16.1/inst/PaxHeaders.61586/imfindcircles.m0000644000000000000000000000006215005110255016373 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imfindcircles.m0000644000175000017500000005326115005110255020000 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## Copyright (C) 2018 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{centers} =} imfindcircles (@var{im}, @var{radius}) ## @deftypefnx {Function File} {@var{centers} =} imfindcircles (@var{im}, @var{RadiusRange}) ## @deftypefnx {Function File} {@var{centers} =} imfindcircles (@dots{}, @var{property}, @var{value}) ## @deftypefnx {Function File} {[@var{centers}, @var{radii}, @var{strengths}] =} imfindcircles (@dots{}) ## Find circles in image using the circular Hough transform. ## ## This function finds circles in a given 2d image @var{im}. ## The image data can be grayscale, rgb color, or binary. ## The search is done only for circular shapes of approximatly ## the given @var{radius} (single value) or @var{RadiusRange} ## (a two element vector [r_min, r_max]). ## ## The output @var{centers} is a two column matrix, ## where each row contains the (fractional) x and y coordinates of ## the center of one found circle. The output rows are ordered ## according the @var{strengths} of the circles, the strongest ## circle first. ## ## The output @var{radii} is a column vector of the (fractional) ## radii of the found circles, in the same order as the circle centers. ## If @var{radius} is specified, instead of @var{RadiusRange}, then ## all values in @var{radii} are the same as @var{radius}. ## ## The output @var{strengths} is a measure of how "strong" ## the corresponding circle is. (A sharp, smooth and full circle gives ## roughly a value of one. The minimum value is zero.) ## ## Additionally the following optional property-value-pairs can be used: ## @table @var ## @item ObjectPolarity ## Either "bright" circles on darker background can be searched (default), ## or "dark" circles on brighter background. ## ## @item Method ## The default method "PhaseCode" is faster as the ## (currently unimplemented) method "TwoStage". ## For literature and details on the algorithm, see comments ## in the code. ## ## @item Sensitivity ## A value between 0 and 1 to say how strong ## a circle must be in order to be found. With a value ## of 1 no circles are discarded, default value is 0.85. ## (Use bigger values of Sensitivity if your circles are ## not properly found, until unwanted "ghost" circles ## start to appear.) ## ## @item EdgeThreshold ## A value between 0 and 1 to say how strong ## an edge point of the image must be, to be ## considered as a possible point on a circle circumference. ## With a value of 0 all possible ## edge points are considered, with a value of 1 only the ## edge point with the stronges gradient will be considered. ## As default value the output of @code{graythresh} (Otsu's threshold) ## of the gradient image is taken. ## @end table ## ## Notes: ## @itemize @bullet ## @item ## Only center points inside the image region can be found. ## @item ## For concentric cirles the output is unpredictable. ## @item ## For optimal speed, keep the radius range and the image size ## as small as possible. ## @item ## For radius values above 100 pixels the sensitivity and accuracy ## of this algorithm starts to decrease. ## @item ## For big radius ranges the sensitivity decreases. Try to keep ## r_max < 3 * r_min . ## @item ## RGB color images will automatically be converted to grayscale ## using the @code{rgb2gray} function. ## @item ## Binary images will automatically be converted to grayscale ## and be slightly smoothed before processing. ## @end itemize ## ## Compatibility note: The @var{centers} and @var{radii} outputs ## have good compatibility to the Matlab outputs. The @var{strengths} ## outputs differ a bit, but have mostly the same ordering. ## Currently only the (default) Matlab algorithm "PhaseCode" is implemented ## in Octave. ## ## @seealso{hough, hough_circle} ## @end deftypefn ## Algorithm: ## The following papers and books were used for the ## implementation of this function: ## ## [1] (for general information on the circle Hough transform ## and the basic algorithmic approach) ## E. R. Davies: "Computer & Machine Vision" ## Academic Press (2012), 4th edition ## chapter 12 "Circle and Ellipse Detection" ## [2] (for the 'phase code' algorithm to search for a radius range ## with just a 2-dimensional accumulator array) ## T. J. Artherton and D. J. Kerbyson: ## "Size invariant circle detection" ## Image and Vision Computing 17 (1999), p. 795-803. ## [3] (for 'state of the art' peak finding in the circular Hough ## transform accumulator array) ## C. Zhang, F. Huber, M. Knop, F. A. Hamprecht: ## "Yest cell detection and segmentation in bright field microscopy" ## IEEE Int. Symp. Biomed. Imag. (ISBI), April 2014, p. 1267-1270. ## ## A more detailed description of the individual steps of the ## algorithm is given in the following code itself. function [centers, radii, strengths] = imfindcircles (im, radiusrange, varargin) if (nargin < 2 || nargin > 10 || mod (numel (varargin), 2) != 0) print_usage (); endif p = inputParser (); p.FunctionName = "imfindcircles"; p.addParamValue ("ObjectPolarity", "bright", @(x) any (strcmpi (x, {"bright", "dark"}))); p.addParamValue ("Method", "PhaseCode", @(x) any (strcmpi (x, {"PhaseCode", "TwoStage"}))); p.addParamValue ("Sensitivity", 0.85, @(x) isnumeric (x) && isreal (x) && isscalar (x)); ## The default value of EdgeThreshold must be computed later. p.addParamValue ("EdgeThreshold", [], @(x) isnumeric (x) && isreal (x) && isscalar (x)); p.parse (varargin{:}); dark_circles = strcmpi (p.Results.ObjectPolarity, "dark"); sensitivity = p.Results.Sensitivity; edge_thresh = p.Results.EdgeThreshold; if (! isimage (im) || ! any (ndims (im) == [2, 3])) error ("imfindcircles: IM must be a logical or numeric 2d or 3d array"); elseif (ndims (im) == 3 && size (im, 3) != 3) error ("imfindcircles: the 3d image IM must be a RGB image"); endif if (all (numel (radiusrange) != [1 2])) error ("imfindcircles: RADIUS or RADIUSRANGE must be a vector of length 1 or 2"); elseif (! isnumeric (radiusrange) || any (radiusrange <= 0)) error ("imfindcircles: RADIUS or RADIUSRANGE must be positive") elseif (numel (radiusrange) == 2 && radiusrange(1) > radiusrange(2)) error ("imfindcircles: RADIUSRANGE(1) must be smaller than RADIUSRANGE(2)") endif if (strcmpi (p.Results.Method, "TwoStage")) error ("imfindcirles: the 'TwoStage' method is not yet implemented. Try 'PhaseCode' instead."); endif if (sensitivity < 0 || sensitivity > 1) error ("imfindcircles: 'Sensitivity' must be between 0 and 1"); endif if (! isempty (edge_thresh) && (edge_thresh < 0 || edge_thresh > 1)) error ("imfindcircles: 'EdgeThreshold' must be between 0 and 1"); endif H = cht_accumulator (im, radiusrange, edge_thresh, dark_circles); [centers, centers_ind, strengths] = cht_centers (H, sensitivity); ## Output values must be sorted by strength. [strengths, idx_sorted] = sort (strengths, "descend"); centers = centers(idx_sorted,:); if (nargout > 1) if (isscalar (radiusrange)) radii = repmat (radiusrange, numel (strengths), 1); else radii = cht_radii (H(centers_ind), radiusrange); ## Output values must be sorted by strength. radii = radii(idx_sorted); endif endif endfunction ## Convert image from RGB or logical types into a floating point 2D ## image that can be used for CHT. function im = cht_image_preparation (im) if (isinteger (im)) im = im2double (im); endif if (size (im, 3) == 3) im = rgb2gray (im); endif ## The Matlab help page mentions "preprocessing" of ## binary input images to "improve the accuracy". ## Unsure what they do here. ## We'll just do a little (carefully rotation symmetrical) ## smoothing as grayscale images, because we need ## the accurate gradient directions later on. (This wouldn't ## work properly with the binary images itself.) if (islogical (im)) im = im2single (im); gauss_filter = fspecial ("gaussian", [9 9], 1.5); im = imfilter (im, gauss_filter, "replicate"); endif endfunction ## calculate the intensity gradients values at edge pixels ## using the Sobel operator (see ref. [1], chapter 12.2) ## (we'll always call the first coordinate "x" here) function [G0, GxE, GyE, E_ind] = edge_intensity_gradients (im, edge_thresh) sobel_x = fspecial ("sobel"); sobel_y = sobel_x'; Gx = imfilter (im, sobel_x, "replicate"); Gy = imfilter (im, sobel_y, "replicate"); G = sqrt (Gx.^ 2 + Gy.^ 2); # sqrt is expensive, but yields higher angle precision ## get rid of empty gradient images, they would cause trouble later on: Gmax = max (G(:)); if (Gmax == 0) E_ind = [](:); else ## Find the edge pixels by thresholding the gradient image. If ## not set, estimate threshold with Otsu's algorithm. if (isempty (edge_thresh)) edge_thresh = graythresh (G ./ Gmax); endif E_ind = find (G > (edge_thresh * Gmax)); # edge pixels indices endif G0 = G(E_ind); GxE = Gx(E_ind); GyE = Gy(E_ind); endfunction function [xs, ys] = candidate_centers (im, R, edge_thresh) [G0, GxE, GyE, E_ind] = edge_intensity_gradients (im, edge_thresh); [Ex, Ey] = ind2sub (size (im), E_ind); ## generate stroke pixels (xs, ys): ## This does not involve sin and cos calculations, ## which helps for speed (see ref. [1].) ## (automatic broadcasting here, ## R is a row vector, the G's are column vectors.) xs = Ex - R .* (GxE ./ G0); # eq. 12.3 (ref. [1]) ys = Ey - R .* (GyE ./ G0); # eq. 12.4 (ref. [1]) ## round to integer pixel positions: xs = round (xs); ys = round (ys); endfunction ## visit all edge pixels and generate ## a "spoke" in the circular Hough transform: ## A spoke is a line of pixels which are a distance r1 to r2 ## away from the edge point, this distance is taken in ## the edge direction (gradient) of this very edge point. ## The resulting points on the spokes are candidates for circle ## center points. And each gradient pixel can vote for ## one center pixel candidate (for each radius value). ## All spoke points are summed up in the accumulator array H. ## (see equations 12.3 and 12.4 for xs and ys in ref. [1]) function H = cht_accumulator (im, radiusrange, edge_thresh, dark_circles) im = cht_image_preparation (im); if (isscalar (radiusrange)) R = radiusrange; else ## Range of circle radii. Step size of 0.5 covers all integer ## circle diameters. R = radiusrange(1):0.5:radiusrange(2); endif ## code the circle polarity in the radius sign: ## (different direction of "spoke", see ref. [1], chapter 12.2) if (dark_circles) [xs, ys] = candidate_centers (im, -R, edge_thresh); else [xs, ys] = candidate_centers (im, R, edge_thresh); endif ns = rows (xs); ## limit pixel position and code values to the image size: xs = xs(:); ys = ys(:); idx_outside = (xs < 1 | xs > rows (im) | ys < 1 | ys > columns (im)); xs(idx_outside) = []; ys(idx_outside) = []; if (isscalar (radiusrange)) codes = 1 ./ R; # phase coding is not useful for a single radius value else ## pre-calculate the phases and (complex) code values for all radii: ## We use the "log phase coding", which is best, according to ## ref. [2]. logR = log (R); phase = 2 .* pi .* ((logR - logR(1)) ./ (logR(end) - logR(1))); # eq. 8 (ref. [2]) code = exp (i .* phase); # eq. 5 (ref. [2]) code ./= R; # eq. 11 (ref. [2]) , radius normalization ## -> a full and perfect circle will give a value of 2pi in H codes = repmat (code, ns, 1)(:); codes(idx_outside) = []; endif ## add the code value of all those spoke pixels ## to the corresponding pixel position in H: H = accumarray ([xs, ys], codes, size (im)); endfunction ## Find local (regional) maximas in H, because they are the circle ## center pixels. This part of the algorithm is taken from ref. [3], ## section 2.2 "Detecting cell center candidates". function [centers, centers_ind, strengths] = cht_centers (H, sensitivity) ## Take the absolute value of the H accumulator array which may be ## complex if we had a radius range and phase coded annulus. H = abs (H); ## do some smoothing before searching for peak positions ## use a (rotation symmetric) gaussian filter for this (see ref. [3]) gauss_filter = fspecial ("gaussian", [9 9], 2); # a bigger filter might join nearby centers H = imfilter (H, gauss_filter, 0); # introduces small shifts of centers near the edge, # but padding "replicate" would be worse ## define threshold to suppress smaller peaks in H: peak_thresh = 1 - sensitivity; peak_thresh *= 2*pi; # because a full circle can add up to 2pi in H ## use the h-maxima transform to suppress maximas with a too low height: Hbig = imhmax (H, peak_thresh); ## find the regional maxima of those smoothed big peaks in H: Hmax_BW = imregionalmax (Hbig ./ max (Hbig(:))); ## Use those maxima as well as their surrounding peak ## to calculate a weighted average for the peak position: ## (This gives the circle center positions with the necessary ## subpixel accuracy.) props = regionprops (Hmax_BW, Hbig, "WeightedCentroid"); centers = cell2mat ({props.WeightedCentroid}'); ## for the "strength" return value, take the (smoothed) H: ## (Matlab seems to do this a bit different.) centers_ind = sub2ind (size (H), round (centers(:,2)), round (centers(:,1))); strengths = H(centers_ind) ./ (2*pi); # normalize a "full" circle to 1 endfunction ## calculate the circle radius from the complex phase in H: ## (i.e. undo the phase coding) function radii = cht_radii (H, radiusrange) r1 = radiusrange(1); r2 = radiusrange(2); c_phase = arg (H); c_phase(c_phase < 0) += (2*pi); radii = exp ((c_phase ./ (2*pi) .* (log (r2) - log (r1))) + log (r1)); endfunction %!shared im0, rgb0, im1 %! im0 = [0 0 0 0 0; %! 0 1 2 1 0; %! 0 2 5 2 0; %! 0 1 2 1 0; %! 0 0 0 0 0]; %! rgb0 = cat (3, im0, 3.*im0, 2.*im0); %! im1 = zeros (20); %! im1(2:6, 5:9) = 1; %! im1(13:19, 13:19) = 1; %!function image = circlesimage (numx, numy, centersx, centersy, rs, values) %! ## create an image with circles of given parameters %! num = length (centersx); %! image = zeros (numy, numx); %! [indy, indx] = meshgrid (1:numx, 1:numy); %! for n = 1:num %! centerx = centersx(n); %! centery = centersy(n); %! r = rs(n); %! value = values(n); %! dist_squared = (indx - centerx).^ 2 + (indy - centery).^ 2; %! image(dist_squared <= (r-0.5)^2) = value; %! endfor %!endfunction ## test input syntax: %!error imfindcircles () %!error imfindcircles (im0) %!error imfindcircles (im0, [1 2 3]) %!error imfindcircles (im0, -3) %!error imfindcircles (im0, 4+2*i) %!error imfindcircles (ones (5,5,4), 2) %!error imfindcircles (ones (5,5,5,5), 2) %!error imfindcircles (im0, [2 1]) %!error imfindcircles (im0, 2, "rubbish") %!error imfindcircles (im0, 2, "more", "rubbish") %!error imfindcircles (im0, 2, "ObjectPolarity", "rubbish") %!error imfindcircles (im0, 2, "ObjectPolarity", 5) %!error imfindcircles (im0, 2, "ObjectPolarity") %!error imfindcircles (im0, 2, "Method", "rubbish") %!error imfindcircles (im0, 2, "Method", 5) %!error imfindcircles (im0, 2, "Method") %!error imfindcircles (im0, 2, "Sensitivity", "rubbish") %!error imfindcircles (im0, 2, "Sensitivity") %!error imfindcircles (im0, 2, "Sensitivity", -0.1) %!error imfindcircles (im0, 2, "Sensitivity", 1.1) %!error imfindcircles (im0, 2, "Sensitivity", [0.1 0.2]) %!error imfindcircles (im0, 2, "EdgeThreshold", "rubbish") %!error imfindcircles (im0, 2, "EdgeThreshold") %!error imfindcircles (im0, 2, "EdgeThreshold", -0.1) %!error imfindcircles (im0, 2, "EdgeThreshold", 1.1) %!error imfindcircles (im0, 2, "EdgeThreshold", [0.1 0.2]) %!error imfindcircles (im0, 2, "EdgeThreshold", 0.1, "ObjectPolarity", "bright", %! "Sensitivity", 0.3, "Method", "PhaseCode", "more", 1) %!test # none of this should fail %! imfindcircles (im0, 2); %! imfindcircles (im0, [1 2]); %! imfindcircles (logical (im0), 2); %! imfindcircles (logical (im0), [1 2]); %! imfindcircles (rgb0, 2); %! imfindcircles (rgb0, [1 2]); %! imfindcircles (uint8 (im0), 2); %! imfindcircles (uint8 (im0), [1 2]); %! imfindcircles (im0, 2, "ObjectPolarity", "bright"); %! imfindcircles (im0, 2, "ObjectPolarity", "dark"); %! imfindcircles (im0, 2, "Method", "PhaseCode"); %! imfindcircles (im0, 2, "Sensitivity", 0.5); %! imfindcircles (im0, 2, "EdgeThreshold", 0.5); %! imfindcircles (im0, 2, "ObjectPolarity", "bright", "Method", "PhaseCode"); %! imfindcircles (im0, 2, "ObjectPolarity", "bright", "Sensitivity", 0.3, %! "Method", "PhaseCode"); %! imfindcircles (im0, 2, "EdgeThreshold", 0.1, "ObjectPolarity", "bright", %! "Sensitivity", 0.3, "Method", "PhaseCode"); ## output class, number and shape: %!test %! centers = imfindcircles (im1, 2); %! assert (size (centers, 2), 2) %! assert (class (centers), "double") %!test %! [centers, radii] = imfindcircles (im1, [1 5]); %! assert (size (centers, 2), 2) %! assert (size (radii, 2), 1) %! assert (class (radii), "double") %!test %! [centers, radii, strengths] = imfindcircles (im1, [1 5]); %! assert (size (strengths, 2), 1) %! assert (class (strengths), "double") %!error [a b c d] = imfindcircles (im0, 2); ## test calculation results: %!test ## sub-pixel accuracy of circle center %! xs = [95.7]; %! ys = [101.1]; %! rs = [50]; %! vals = [0.5]; %! im = circlesimage (200, 200, xs, ys, rs, vals); %! filt = ones (3) ./ 9; %! im = imfilter (im, filt); %! [centers, radii] = imfindcircles (im, [40 60]); %! assert (centers, [101.1, 95.7], 0.1); %! assert (radii, 50, 1); %!test %! ## specificity to circular shapes and strengths output value %! xs = [100 202]; %! ys = [101, 203]; %! rs = [40, 41]; %! vals = [0.8, 0.9]; %! im = circlesimage (300, 300, xs, ys, rs, vals); %! filt = ones (3) ./ 9; %! im = imfilter (im, filt); %! im(30:170, 50:100) = 0; %! im(20:120, 180:280) = 1; %! [centers, radii, strengths] = imfindcircles (im, [30 50], "Sensitivity", 0.9); %! assert (size (centers), [2 2]); %! assert (centers, [203, 202; 101, 100], 1.5); %! assert (radii, [40; 41], 2.5); %! assert (strengths(1) / strengths(2) > 1.8, true); %!test # radius range parameter & dark circles %! xs = [50, 420, 180]; %! ys = [80, 100, 200]; %! rs = [35, 30, 40]; %! vals = [0.7, 0.8, 0.9]; %! im = circlesimage (300, 500, xs, ys, rs, vals); %! filt = ones (3) ./ 9; %! im = imfilter (im, filt); %! [centers1, radii1] = imfindcircles (im, [28 36]); %! [centers2, radii2] = imfindcircles (im, [28 42]); %! assert (size (centers1), [2 2]); %! assert (centers1, [100 420; 80 50], 0.2); %! assert (radii1, [30; 35], 2); %! assert (size (centers2), [3 2]); %! im_dark = 1-im; %! [centers_dark, radii_dark, strengths_dark] = imfindcircles (im_dark, [25 42], "ObjectPolarity", "dark"); %! assert (sortrows (centers_dark), [80 50; 100 420; 200 180], 0.2); %! assert (sortrows (radii_dark), [30; 35; 40], 1); %!test # ability to find circles with big radius %! xs = [111, 555, 341]; %! ys = [222, 401, 161]; %! rs = [45, 50, 150]; %! vals = [0.6, 0.8, 0.7]; %! im = circlesimage (400, 701, xs, ys, rs, vals); %! [centers, radii] = imfindcircles (im, [140 160], "Sensitivity", 0.98); %! assert (centers, [161, 341], 0.2); %! assert (radii, 150, 1); %!test # overlapping circles %! xs = [105, 155]; %! ys = [202, 221]; %! rs = [45, 50]; %! vals = [0.5, 0.8]; %! im = circlesimage(385, 422, xs, ys, rs, vals); %! filt = ones (3) ./ 9; %! im = imfilter (im, filt); %! [centers, radii] = imfindcircles (im, [30 80]); %! assert (centers, [221, 155; 202, 105], 0.5); %! assert (radii, [50; 45], 1); %!test # overlapping circles, only 10 pixels apart %! xs = [155, 155]; %! ys = [175, 157]; %! rs = [50, 50]; %! vals = [0.7, 0.8]; %! im = circlesimage (300, 300, xs, ys, rs, vals); %! filt = ones (3) ./ 9; %! im = imfilter (im, filt); %! [centers, radii] = imfindcircles (im, [30 80], "Sensitivity", 0.95); %! assert (centers, [157, 155; 175, 155], 1); %! assert (radii, [50; 50], 1); %!test # edge threshold parameter %! xs = [100 202]; %! ys = [101, 203]; %! rs = [40, 41]; %! vals = [0.1, 0.9]; %! im = circlesimage (300, 300, xs, ys, rs, vals); %! filt = ones (3) ./ 9; %! im= imfilter (im, filt); %! [centers_auto, radii_auto] = imfindcircles (im, [30 50]); %! [centers_0, radii_0] = imfindcircles (im, [30 50], "EdgeThreshold", 0); %! [centers_05, radii_05] = imfindcircles (im, [30 50], "EdgeThreshold", 0.5); %! assert (centers_auto, [203, 202], 0.2); %! assert (radii_auto, 41, 1); %! assert (centers_0, [101, 100; 203, 202], 0.2); %! assert (radii_0, [40; 41], 1); %! assert (centers_05, [203, 202], 0.2); %! assert (radii_05, 41, 1); %!demo %! ## First generate an input image: %! model = [ 1.0 0.2 0.2 0.2 0.5 0 %! 1.0 0.3 0.3 -0.1 -0.2 0 %! -0.5 0.7 0.7 -0.5 0.5 0]; %! im = phantom (model); %! im(170:230,170:230) = 1; %! im = imfilter (im, fspecial ("average", 3)); %! im = imnoise (im, "salt & pepper"); %! imshow (im); %! %! ## Find and show circles with radius between 20 and 50: %! [centers, radii] = imfindcircles (im, [20 50]); %! viscircles (centers, radii) %! title ("found circles in red") image-2.16.1/inst/PaxHeaders.61586/viscircles.m0000644000000000000000000000006215005110255015726 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/viscircles.m0000644000175000017500000001475415005110255017337 0ustar00avinoamavinoam00000000000000## Copyright (C) 2015-2018 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} viscircles (@var{centers}, @var{radii}) ## @deftypefnx {Function File} {} viscircles (@var{hax}, @var{centers}, @var{radii}) ## @deftypefnx {Function File} {} viscircles (@dots{}, @var{property}, @var{value}) ## @deftypefnx {Function File} {@var{h} =} viscircles (@dots{}) ## Draw circles on figure. ## ## Circles are specified by a Nx2 matrix @var{centers} with x,y ## coordinates per row, and a N length vector @var{radii}. ## ## @example ## ## draw circles at [10 20] and [-10 -20] coordinates ## ## with radius of 10 and 20 respectively ## viscircles ([10 20; -10 -20], [10 20]) ## @end example ## ## The appearance of the drawn circles can be configured with the ## following properties names: ## ## @table @asis ## @item @qcode{"Color"} ## The color of the circle. Defaults to @qcode{"red"}. Can be defined ## via the color names or RGB triplets. See the help text for ## @code{plot} for further details on specifying colors in figures. ## ## @item @qcode{"LineStyle"} ## ## The line style of the circle. Defaults to @qcode{"-"} (solid ## line). See the help text for @code{plot} for possible values. ## ## @item @qcode{"LineWidth"} ## The width of the circle line. Defaults to 2. ## ## @item @qcode{"EnhanceVisibility"} ## Enhance visibility by drawing a white circle under the colored ## circle. Must be a logical value. Defaults to true. ## ## @end table ## ## @seealso{plot, line} ## @end deftypefn function h = viscircles (varargin) if (nargin < 2) print_usage (); endif args_ind = 1; if (mod (nargin, 2) == 0) hax = gca (); else hax = varargin{args_ind++}; if (! ishandle (hax)) error ("viscircles: HAX is not an axes graphics handle"); endif endif centers = varargin{args_ind++}; radii = varargin{args_ind++}; if (columns (centers) != 2) error ("viscircles: CENTERS must be a Nx2 matrix"); elseif (! isvector (radii)) error ("viscircles: RADII must be a vector"); elseif (rows (centers) != numel (radii)) error ("viscircles: RADII length must be equal to the rows of CENTERS"); endif p = inputParser (); p.FunctionName = "viscircles"; ## Original version of viscircles had EdgeColor and ## DrawBackgroundCircle parameters. They have been renamed Color ## and EnhanceVisibility on later Matlab versions but keep them for ## backwards compatibility. params = struct ("old_name", {"DrawBackgroundCircle"; "EdgeColor"}, "new_name", {"EnhanceVisibility"; "Color"}, "parser_args", {{true, @isbool}; {"red", @(c) isfloat (c) || ischar (c)}}); for idx = 1:numel(params) param = params(idx); p.addParamValue (param.old_name, param.parser_args{:}); p.addParamValue (param.new_name, param.parser_args{:}); endfor p.addParamValue ("LineStyle", "-", @ischar); p.addParamValue ("LineWidth", 2, @isnumeric); ## FIXME: we use "numel (varargin)" instead of end to work around ## https://savannah.gnu.org/bugs/index.php?44779 p.parse (varargin{args_ind:numel (varargin)}); ## Results is write-protected but we may need to modify them options = p.Results; ## Check if the user used the old parameter names and remap to the ## new names. Error if both are being set. for idx = 1:numel(params) param = params(idx); if (! any (strcmp (param.old_name, p.UsingDefaults))) if (! any (strcmp (param.new_name, p.UsingDefaults))) error ("viscircles: both '%s' (deprecated) and '%s' parameters set", param.old_name, param.new_name); endif options.(param.new_name) = p.Results.(param.old_name); endif endfor theta = linspace (0, 2*pi, 100); x = radii(:).' .* cos (theta(:)) + centers(:,1).'; y = radii(:).' .* sin (theta(:)) + centers(:,2).'; hold_was_on = ishold (hax); unwind_protect hold (hax, "on"); h_tmp = hggroup (hax); if (options.EnhanceVisibility) line (hax, x, y, "Parent", h_tmp, "Color", "white", "LineStyle", "-", "LineWidth", options.LineWidth + 1); endif line (hax, x, y, "Parent", h_tmp, "Color", options.Color, "LineWidth", options.LineWidth, "LineStyle", options.LineStyle); unwind_protect_cleanup if (! hold_was_on) hold (hax, "off"); endif end_unwind_protect if (nargout) h = h_tmp; endif endfunction %!demo %! centers = randi ([0 100], 5, 2); %! radii = randi ([10 100], 5, 1); %! axis equal %! viscircles (centers, radii, %! "Color", "magenta", %! "LineStyle", ":", %! "LineWidth", 5); %! title ("5 random circles"); %! #---------------------------------------------- %! # the figure window shows 5 circles with random %! # radii and positions %!test # old undocumented property %! h = viscircles ([0 0], 1, "EdgeColor", "black"); %! assert (get (get (h, "children")(1), "color"), [0 0 0]) %!test # old undocumented property %! h = viscircles ([0 0], 1, "DrawBackgroundCircle", false); %! assert (numel (get (h, "children")), 1) %!error ... %! viscircles ([0 0], 1, "Color", "magenta", "EdgeColor", "black") %!test %! centers = randi ([0 100], 5, 2); %! radii = randi ([0 100], 5, 1); %! h = viscircles (centers, radii); %! close; %!test %! centers = randi ([0 100], 5, 2); %! radii = randi ([0 100], 5, 1); %! figure (); %! h = viscircles (gca (), centers, radii); %! close; %!test %! centers = randi ([0 100], 5, 2); %! radii = randi ([0 100], 5, 1); %! h = viscircles (centers, radii, "Color", "magenta", %! "LineStyle", ":", "LineWidth", 5); %! close; %!test %! centers = randi ([0 100],5,2); %! radii = randi ([0 100],5,1); %! figure (); %! h = viscircles (centers, radii, "Color", "magenta", %! "LineStyle", ":", "LineWidth", 5); %! close; image-2.16.1/inst/PaxHeaders.61586/integralImage.m0000644000000000000000000000006215005110255016330 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/integralImage.m0000644000175000017500000001265015005110255017732 0ustar00avinoamavinoam00000000000000## Copyright (C) 2019 Avinoam Kalma ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} integralImage (@var{img}) ## @deftypefnx {Function File} {} integralImage (@var{img}, @var{orient}) ## Calculate the integral image. ## ## @var{img} is the input image for integral image calculation. If it ## is an RGB image (or higher dimension), each 2D plane is treated ## separately. ## ## @var{orient} determines which integral image will be ## calculated. Its value must be the string @qcode{"upright"} ## (default) or @qcode{"rotated"}. ## ## The value of the integral image in the @qcode{"upright"} ## orientation, also called "summed-area table", at any poing (x, y) ## is the sum of all the pixels above and to the left of (x, y), ## inclusive, see [1]. ## ## When using the @qcode{"rotated"} option, Rotated Summed Area Table ## (RSAT) is calculated. It is defined as the sum of the pixels of a ## 45 degrees rotated rectangle with the bottom most corner at (x,y): ## ## @example ## RSAT(x,y) = RSAT(x-1,y-1) + RSAT(x+1,y-1) - RSAT(x,y-2) + I(x,y) + I(x,y-1) ## @end example ## ## (see [2]) ## ## References: ## ## [1] Viola, Paul; Jones, Michael (2001). @cite{"Robust Real-time ## Object Detection"}. Compaq Cambridge Research Laboratory (CRL) ## Technical Report, February 2001. ## @url{http://www.hpl.hp.com/techreports/Compaq-DEC/CRL-2001-1.pdf} ## ## [2] Lienhart, Kuranov and Pisarevsky (2002). @cite{"Empirical ## Analysis of Detection Cascades of Boosted Classifiers for Rapid ## Object Detection"}. Microprocessor Research Lab (MRL) Technical ## Report, May 2002. ## @url{http://www.multimedia-computing.de/mediawiki/images/5/52/MRL-TR-May02-revised-Dec02.pdf} ## ## @seealso{cumsum} ## @end deftypefn function J = integralImage (I, orientation = "upright") if (nargin < 1 || nargin > 2) print_usage (); endif if (! isimage (I)) error ("integralImage: first argument should be an image"); endif if (! strcmp (class (I), "double")) I = double (I); endif orientation = lower (orientation); if (strcmp (orientation, "upright")) J = cumsum (cumsum (I, 2)); J = padarray (J, [1 1], "pre"); elseif (strcmp (orientation, "rotated")) if (ndims (I) == 2) J = integralImage_rotate_2D (I); else IR = reshape (I, size (I,1), size (I,2), []); J = zeros (size (IR,1)+1, size (IR,2)+2, size (IR,3)); for i = 1:size (IR,3) J(:,:,i) = integralImage_rotate_2D (IR(:,:,i)); endfor s = size (I); J = reshape (J, [size(J,1) size(J,2) s(3:end)]); endif else error ("orientation should be \"upright\" (default) or \"rotated\""); endif endfunction function J = integralImage_rotate_2D (I) ## FIXME: Can this part be more vectorized? s = size (I); s1 = s + [1,2]; J = zeros (s1); J(2,2:s(2)+1) = I(1,:); s21 = s(2)+1; for y = 3:s1(1) y1 = y-1; J(y,1) = J(y1,2); J(y,2:s21) = J(y1,1:s(2)) + J(y1,3:s1(2)) - J(y-2,2:s21) + I(y1,:) + I(y-2,:); J(y,end) = J(y1,s21); endfor endfunction %!test %! assert (integralImage (10), [0 0; 0 10]); %! assert (integralImage (10, "rotated"), [0 0 0; 0 10 0]); %!test %! J = integralImage (10); %! assert (class(J), "double"); %! J = integralImage (uint8(10)); %! assert (class(J), "double"); %!test %! I = [1, 2; 3, 4]; %! J = integralImage (I); %! J1 = [0 0 0; 0 1 3; 0 4 10]; %! assert (J, J1) %! J = integralImage (I, "rotated"); %! J1 = [0 0 0 0; 0 1 2 0; 1 6 7 2]; %! assert (J, J1) %!test %! I1 = [1, 2; 3, 4]; %! I2 = [5, 6; 7, 8]; %! I3 = [9, 10; 11, 12]; %! I = cat (3, I1, I2, I3); %! J = integralImage (I); %! J1 = [0 0 0; 0 1 3; 0 4 10]; %! J2 = [0 0 0; 0 5 11; 0 12 26]; %! J3 = [0 0 0; 0 9 19; 0 20 42]; %! J0 = cat (3, J1, J2, J3); %! assert (J, J0) %!test %! I1 = [1, 2; 3, 4]; %! I2 = [5, 6; 7, 8]; %! I3 = [9, 10; 11, 12]; %! I = cat (3, I1, I2, I3); %! J = integralImage (I, "rotated"); %! J1 = [0 0 0 0; 0 1 2 0; 1 6 7 2]; %! J2 = [0 0 0 0; 0 5 6 0; 5 18 19 6]; %! J3 = [0 0 0 0; 0 9 10 0; 9 30 31 10]; %! J0 = cat (3, J1, J2, J3); %! assert (J, J0) %!test %! I = magic (5); %! J = integralImage (I); %! J_res = [0 0 0 0 0 0; %! 0 17 41 42 50 65; %! 0 40 69 77 99 130; %! 0 44 79 100 142 195; %! 0 54 101 141 204 260; %! 0 65 130 195 260 325]; %! assert (J, J_res) %! %! J = integralImage (I, "rotated"); %! J_res_R = [0 0 0 0 0 0 0; %! 0 17 24 1 8 15 0; %! 17 64 47 40 38 39 15; %! 64 74 91 104 105 76 39; %! 74 105 149 188 183 130 76; %! 105 170 232 272 236 195 130]; %! assert (J, J_res_R) %!error %! integralImage (); %!error %! integralImage (1, "xxx", 2); %!error %! integralImage ("abcd"); %!error %! integralImage ([1 2; 3 4], "xxx"); image-2.16.1/inst/PaxHeaders.61586/imtranslate.m0000644000000000000000000000006215005110255016103 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imtranslate.m0000644000175000017500000000510415005110255017501 0ustar00avinoamavinoam00000000000000## Copyright (C) 2002 Jeff Orchard ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{Y}} = imtranslate (@var{M}, @var{x}, @var{y}) ## @deftypefnx {Function File} {@var{Y}} = imtranslate (@var{M}, @var{x}, @var{y}, @var{bbox}) ## Translate a 2D image by (x,y) using Fourier interpolation. ## ## @var{M} is a matrix, and is translated to the right by @var{X} pixels ## and translated up by @var{Y} pixels. ## ## @var{bbox} can be either 'crop' or 'wrap' (default). ## ## @end deftypefn function Y = imtranslate (X, nc, nr, bbox = "wrap") if (strcmp (bbox, "crop")) pre = post = [0 0]; if (nc > 0) post(2) = ceil (nc); else pre(2) = ceil (nc); endif if (nr > 0) pre(1) = ceil (nr); else post(1) = ceil (nr); endif pre = abs (pre); post = abs (post); X = padarray (X, abs (pre), "pre"); X = padarray (X, abs (post), "post"); endif [dimy, dimx] = size(X); x = fft2(X); px = exp(-2*pi*i*nc*(0:dimx-1)/dimx); py = exp(-2*pi*i*nr*(0:dimy-1)/dimy)'; % actually to correspond to index notation 'b' should be % replaced with '-b' % but I do not want to brake previous version compatibility % note: it also must be done in the cropping iand padding code P = py * px; y = x .* P; Y = real(ifft2(y)); % fft return complex number % for integer shifts imaginary part is 0 % so real takes care of transfer from complex number to real if (strcmp (bbox, "crop")) Y = Y(pre(1)+1:dimy-post(1) , pre(2)+1:dimx-post(2)); endif endfunction %!test %! obs = imtranslate (ones (5, 5), 2, 1, "crop"); %! exp = zeros (5, 5); %! exp(1:4, 3:5) = 1; %! assert (obs, exp, eps * 10) %! %! obs = imtranslate (ones (5, 5), -2, -1, "crop"); %! exp = zeros (5, 5); %! exp(2:5, 1:3) = 1; %! assert (obs, exp, eps * 10) image-2.16.1/inst/PaxHeaders.61586/imhmin.m0000644000000000000000000000006215005110255015041 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imhmin.m0000644000175000017500000001444715005110255016451 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Hartmut Gimpel ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} @ imhmin (@var{im}, @var{h}) ## @deftypefnx {Function File} {} @ imhmin (@var{im}, @var{h}, @var{conn}) ## Caculate the morphological h-minimum transform of an image @var{im}. ## ## This function removes all regional minima in the grayscale image @var{im} whose ## depth is less or equal to the given threshold level @var{h}, and it increases the depth ## of the remaining regional minima by the value of @var{h}. (A "regional minimum" ## is defined as a connected component of pixels with an equal pixel value ## that is less than the value of all its neighboring pixels. And the ## "depth" of a regional minimum can be thought of as minimum pixel value difference ## between the regional minimum and its neighboring maxima.) ## ## The input image @var{im} needs to be a real and nonsparse numeric array (of any dimension), ## and the height parameter @var{h} a non-negative scalar number. ## ## The definition of "neighborhood" for this morphological operation can be set ## with the connectivity parameter @var{conn}, ## which defaults to 8 for 2D images, to 26 for 3D images and to ## @code{conn(ndims(n), "maximal")} in general. @var{conn} can be given as scalar value ## or as a boolean matrix (see @code{conndef} for details). ## ## The output is a transformed grayscale image of same type and ## shape as the input image @var{im}. ## ## @seealso{imhmax, imregionalmin, imextendedmin, imreconstruct} ## @end deftypefn ## Algorithm: ## * The 'classical' reference for this morphological h-minimum function ## is the book "Morphological Image Analysis" by P. Soille ## (Springer, 2nd edition, 2004), chapter 6.3.4 "Extended and h-extrema". ## It says: "This [h-maximum] is achieved by performing the reconstruction by dilation ## of [a grayscale image] f from f-h: ## HMAX_h(f) = R^delta_f (f - h) ## [...] The h-minima [...] transformations are defined by analogy: ## HMIN_h(f) = R^epsilon_f (f + h)". ## * A more easily accessible reference is for example the following ## web page by Régis Clouard: ## https://clouard.users.greyc.fr/Pantheon/experiments/morphology/index-en.html#extremum ## It says: "It is defined as the [morphological] reconstruction by erosion ## of [a grayscale image] f increased by a height h." ## (We will call the grayscale image im instead of f.) function im2 = imhmin (im, h, varargin) ## retrieve input parameters, set default value: if (nargin == 3) conn = varargin{1}; iptcheckconn (conn, "imhmin", "CONN"); elseif (nargin == 2) conn = conndef (ndims (im), "maximal"); else print_usage (); endif ## check input parameters: if (! isnumeric (im) || ! isreal (im) || issparse (im) ) error ("imhmin: IM must be a real and nonsparse numeric array"); endif if (! isnumeric (h) || ! isscalar (h) || ! isreal (h) || (h<0) ) error ("imhmin: H must be a non-negative scalar number"); endif ## do the actual calculation: ## (Calculate dilations of the inverse image, instead of erosions of the ## original image, because this is what imreconstruct can do. ## Note: "imcomplement(im)-h" is the inverse of "im+h".) im = imcomplement (im); im2 = imreconstruct ((im-h), im, conn); im2 = imcomplement (im2); endfunction %!shared im0, im0_h2_out %! im0 = uint8 ([5 5 5 5 5; %! 5 4 3 4 5; %! 5 3 0 3 5; %! 5 4 3 4 5; %! 5 5 5 5 5]); %! im0_h2_out = uint8 ([5 5 5 5 5; %! 5 4 3 4 5; %! 5 3 2 3 5; %! 5 4 3 4 5; %! 5 5 5 5 5]); ## test input syntax: %!error imhmin () %!error imhmin (im0) %!error imhmin ("hello", 2) %!error imhmin (i.*im0, 2) %!error imhmin (sparse (im0), 2) %!error imhmin (im0, -2) %!error imhmin (im0, 'a') %!error imhmin (im0, ones (2)) %!error imhmin (im0, 2*i) %!assert (imhmin (im0, 2), im0_h2_out) %!assert (imhmin (double (im0), 2), double (im0_h2_out)) %!assert (imhmin (im0, 2, 8), im0_h2_out) %!assert (imhmin (im0, 2, 4), im0_h2_out) %!assert (imhmin (im0, 2, true (3)), im0_h2_out) ## test output class and shape: %!test %! out = imhmin (double (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "double") %!test %! out = imhmin (single (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "single") %!test %! out = imhmin (uint8 (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "uint8") %!test %! out = imhmin (uint16 (im0), 2); %! assert (size (out), size (im0)) %! assert (class (out), "uint16") %!test %! im = cat (3, im0, im0, im0, im0); %! out = imhmin (im, 2); %! assert (size (out), size (im)) ## test calculation result: %!test %! im = 10 .* ones (10); %! im(2:4, 2:4) = 7; %! im(6:8, 6:8) = 2; %! expected_4 = 10 .* ones (10); %! expected_4(6:8, 6:8) = 6; %! expected_2 = 10 .* ones (10); %! expected_2(6:8, 6:8) = 4; %! expected_2(2:4, 2:4) = 9; %! out = imhmin (im, 4); %! assert (out, expected_4, eps) %! out = imhmin (im, 2); %! assert (out, expected_2, eps) %! out = imhmin (0.1.*im, 0.4); %! assert (out, 0.1 .* expected_4, eps) %!test %! im2 = 10 .* ones (10); %! im2(2:4, 2:4) = 7; %! im2(6:9, 6:9)=2; %! im2(5, 5)=2; %! im2(6, 7)=10; %! im2(7, 8)=10; %! expected_4 = 10 .* ones (10); %! expected_4(6:9, 6:9) = 6; %! expected_4(5, 5) = 6; %! expected_4(6, 7) = 10; %! expected_4(7, 8) = 10; %! expected_8 = expected_4; %! expected_8(2:4, 2:4) = 7; %! out2 = imhmin (im2, 4); %! assert (out2, expected_8, eps) %! out2 = imhmin (im2, 4, 4); %! assert (out2, expected_4, eps) %! out2 = imhmin (im2, 4, 8); %! assert (out2, expected_8, eps) image-2.16.1/inst/PaxHeaders.61586/im2uint16.m0000644000000000000000000000006215005110255015316 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/im2uint16.m0000644000175000017500000000624015005110255016716 0ustar00avinoamavinoam00000000000000## Copyright (C) 2007 Søren Hauberg ## Copyright (C) 2012-2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} im2uint16 (@var{img}) ## @deftypefnx {Function File} {} im2uint16 (@var{img}, "indexed") ## Convert image to uint16. ## ## 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: ## ## @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 table ## ## 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 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])); %!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 im2uint16 ([0 1 2], "indexed"); %!error im2uint16 (int16 ([17 8]), "indexed"); %!error im2uint16 (int16 ([-7 8]), "indexed"); %!error im2uint16 ([false true], "indexed"); %!error im2uint16 (65537, "indexed"); image-2.16.1/inst/PaxHeaders.61586/imabsdiff.m0000644000000000000000000000006215005110255015504 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imabsdiff.m0000644000175000017500000000605215005110255017105 0ustar00avinoamavinoam00000000000000## Copyright (C) 2011 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{out} =} imabsdiff (@var{a}, @var{b}) ## @deftypefnx {Function File} {@var{out} =} imabsdiff (@var{a}, @var{b}, @var{class}) ## Return absolute difference of image or constant to an image. ## ## If @var{a} and @var{b} are two images of same size and class, returns the absolute ## difference between @var{b} and @var{a}. ## ## The class of @var{out} will be the same as @var{a} unless @var{a} is logical ## in which case @var{out} will be double. Alternatively, the class can be ## specified with @var{class}. ## ## @emph{Note 1}: you can force output class to be logical by specifying ## @var{class}. This is incompatible with @sc{matlab} which will @emph{not} honour ## request to return a logical matrix. ## ## @emph{Note 2}: the values are truncated to the mininum value of the output ## class. ## @seealso{imadd, imcomplement, imdivide, imlincomb, immultiply, imsubtract} ## @end deftypefn function img = imabsdiff (img, val, out_class = class (img)) if (nargin < 2 || nargin > 3) print_usage; endif ## we want to make subtraction as double so this is it [img, val] = imarithmetics ("imabsdiff", img, val, "double"); converter = str2func (tolower (out_class)); if (nargin < 3 && strcmp (out_class, "logical")) ## it is using logical as default. Use double instead. We only have this ## problem on this function because we are are not actually giving out_class ## to imarithmetics converter = @double; else converter = str2func (tolower (out_class)); endif img = converter (abs (img - val)); endfunction %!assert (imabsdiff (uint8 ([23 250]), uint8 ([26 50])), uint8 ([ 3 200])); # default to first class and abs works %!assert (imabsdiff (uint8 ([23 250]), uint8 ([24 50]), "uint16"), uint16 ([ 1 200])); # defining output class works (not in matlab) %!assert (imabsdiff (uint8 ([23 250]), uint8 ([24 255]), "int8"), int8 ([ 1 5])); # signed integers kinda work (not in matlab) %!assert (imabsdiff (logical ([ 1 0]), logical ([ 1 1])), double ([ 0 1])); # return double for two logical images %!fail ("imabsdiff (uint8 ([23 250]), 30"); # fails subtracting a scalar %!fail ("imabsdiff (uint8 ([23 250]), uint16 ([23 250]))"); # input need to have same class image-2.16.1/inst/PaxHeaders.61586/ordfilt2.m0000644000000000000000000000006215005110255015305 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/ordfilt2.m0000644000175000017500000000467515005110255016717 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Teemu Ikonen ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} ordfilt2 (@var{A}, @var{nth}, @var{domain}) ## @deftypefnx {Function File} {} ordfilt2 (@var{A}, @var{nth}, @var{domain}, @var{S}) ## @deftypefnx {Function File} {} ordfilt2 (@dots{}, @var{padding}) ## Two dimensional ordered filtering. ## ## This function exists only for @sc{matlab} compatibility as is just a wrapper ## to the @code{ordfiltn} which performs the same function on N dimensions. See ## @code{ordfiltn} help text for usage explanation. ## ## @seealso{medfilt2, padarray, ordfiltn} ## @end deftypefn function A = ordfilt2 (A, nth, domain, varargin) if (nargin < 3) print_usage (); elseif (ndims (A) > 2 || ndims (domain) > 2 ) error ("ordfilt2: A and DOMAIN are limited to 2 dimensinos. Use `ordfiltn' for more") endif A = ordfiltn (A, nth, domain, varargin{:}); endfunction %!test %! order = 3; %! domain = ones (3); %! A = zeros (3,3); %! B = ones (3,3); %! C = [1 1 1; 2 2 2; 3 3 3]; %! D = C'; %! E = ones (3,3); %! E(2,2) = 2; %! F = 3 .* ones (3,3); %! F(2,2) = 1; %! G = [-1 2 7; -5 2 8; -7 pi 9]; %! H = [5 2 8; 1 -3 1; 5 1 0]; %! A_out = [0 0 0; 0 0 0; 0 0 0]; %! B_out = [0 0 0; 0 1 0; 0 0 0]; %! C_out = [0 0 0; 0 1 0; 0 0 0]; %! D_out = [0 0 0; 0 1 0; 0 0 0]; %! E_out = [0 0 0; 0 1 0; 0 0 0]; %! F_out = [0 0 0; 0 3 0; 0 0 0]; %! G_out = [0 0 0; -1 -1 0; 0 0 0]; %! H_out = [0 0 0; 0 1 0; 0 0 0]; %! assert (ordfilt2 (A, order, domain), A_out); %! assert (ordfilt2 (B, order, domain), B_out); %! assert (ordfilt2 (C, order, domain), C_out); %! assert (ordfilt2 (D, order, domain), D_out); %! assert (ordfilt2 (E, order, domain), E_out); %! assert (ordfilt2 (F, order, domain), F_out); %! assert (ordfilt2 (G, order, domain), G_out); %! assert (ordfilt2 (H, order, domain), H_out); image-2.16.1/inst/PaxHeaders.61586/entropy.m0000644000000000000000000000006215005110255015260 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/entropy.m0000644000175000017500000001062615005110255016663 0ustar00avinoamavinoam00000000000000## Copyright (C) 2008 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{E} =} entropy (@var{im}) ## @deftypefnx{Function File} {@var{E} =} entropy (@var{im}, @var{nbins}) ## Computes the entropy of an image. ## ## The entropy of the elements of the image @var{im} is computed as ## ## @example ## @var{E} = -sum (@var{P} .* log2 (@var{P})) ## @end example ## ## where @var{P} is the distribution of the elements of @var{im}. The distribution ## is approximated using a histogram with @var{nbins} cells. If @var{im} is ## @code{logical} then two cells are used by default. For other classes 256 cells ## are used by default. ## ## When the entropy is computed, zero-valued cells of the histogram are ignored. ## ## @seealso{entropyfilt} ## @end deftypefn function retval = entropy (I, nbins = 0) if (nargin < 1 || nargin > 2) print_usage (); endif if ( (! isnumeric (I) && ! islogical (I)) || issparse (I) || (! isreal (I)) ) error ("entropy: I must be real, non-sparse and numeric"); endif if (! isscalar (nbins)) error ("entropy: NBINS must be a scalar"); endif ## Get number of histogram bins if (nbins <= 0) if (islogical (I)) nbins = 2; else nbins = 256; endif endif ## transform all non-logical images to uint8 (as Matlab): if (! islogical (I)) I = im2uint8 (I); end ## Compute histogram, using imhist (as Matlab claims to do) P = imhist (I(:), nbins); ## ignore zero-entries of the histogram, and normalize it to a sum of 1 P(P==0) = []; P = P ./ sum (P(:)); ## Compute entropy retval = -sum (P .* log2 (P)); endfunction %!assert (entropy ([0 1]), 1) %!assert (entropy (uint8 ([0 1])), 1) %!assert (entropy ([0 0]), 0) %!assert (entropy ([0]), 0) %!assert (entropy ([1]), 0) %!assert (entropy ([0 .5; 2 0]), 1.5) ## rgb images are treated like nd grayscale images %!assert (entropy (repmat ([0 .5; 2 0], 1, 1, 3)), %! entropy ([0 .5; 2 0])) ## test some 9x9 float input images %!test %! A = zeros (3,3); %! B = ones (3,3); %! C = [1 1 1; 2 2 2; 3 3 3]; %! D = C'; %! E = ones (3,3); %! E(2,2)=2; %! F = 3 .* ones (3,3); %! F(2,2)=1; %! G = [-1 2 7; -5 2 8; -7 pi 9]; %! H = [5 2 8; 1 -3 1; 5 1 0]; %! pG = [1 2] ./ 3; %! G_out = -sum (pG.*log2 (pG)); %! pH = [2 7] ./ 9; %! H_out = -sum (pH.*log2 (pH)); %! assert (entropy (A), 0, eps); %! assert (entropy (B), 0, eps); %! assert (entropy (C), 0, eps); %! assert (entropy (D), 0, eps); %! assert (entropy (E), 0, eps); %! assert (entropy (F), 0, eps); %! assert (entropy (G), G_out, eps); %! assert (entropy (H), H_out, eps); ## test some 9x9 uint8 input images %!test %! A = uint8 (zeros (3,3)); %! B = uint8 (ones (3,3)); %! C = uint8 ([1 1 1; 2 2 2; 3 3 3]); %! D = C'; %! E = uint8 (ones (3,3)); %! E(2,2)=2; %! F = 3 .* uint8 (ones (3,3)); %! F(2,2)=1; %! G = uint8 ([0 2 7; 0 2 8; 0 3 9]); %! H = uint8 ([5 2 8; 1 0 1; 5 1 0]); %! pC = [1 1 1] ./ 3; %! C_out = -sum (pC.*log2 (pC)); %! D_out = C_out; %! pE = [8 1] ./ 9; %! E_out = -sum (pE.*log2 (pE)); %! F_out = E_out; %! pG = [3 2 1 1 1 1] ./ 9; %! G_out = -sum (pG.*log2 (pG)); %! pH = [2 3 1 2 1] ./ 9; %! H_out = -sum (pH.*log2 (pH)); %! assert (entropy (A), 0); %! assert (entropy (B), 0); %! assert (entropy (C), C_out, eps); %! assert (entropy (D), D_out, eps); %! assert (entropy (E), E_out, eps); %! assert (entropy (F), F_out, eps); %! assert (entropy (G), G_out, eps); %! assert (entropy (H), H_out, eps); ## test some 9x9 logical input images %!test %! L1 = false (3,3); %! L1(2,2)=true; %! L2 = true (3,3); %! L2(2,2)=false; %! L3 = logical ([0 1 1; 0 1 1; 0 0 1]); %! p12 = [1 8] ./ 9; %! out12 = -sum (p12.*log2 (p12)); %! p3 = [5 4] ./9; %! out3 = -sum (p3.*log2 (p3)); %! assert (entropy (L1), out12, eps); %! assert (entropy (L2), out12, eps); %! assert (entropy (L3), out3, eps); image-2.16.1/inst/PaxHeaders.61586/graythresh.m0000644000000000000000000000006215005110255015740 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/graythresh.m0000644000175000017500000010017115005110255017336 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Antti Niemistö ## Copyright (C) 2007 Søren Hauberg ## Copyright (C) 2012-2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{level}, @var{sep}] =} graythresh (@var{img}) ## @deftypefnx {Function File} {[@var{level}, @var{sep}] =} graythresh (@var{img}, @var{method}, @var{options}) ## @deftypefnx {Function File} {[@var{level}, @var{sep}] =} graythresh (@var{hist}, @dots{}) ## Compute global image threshold. ## ## Given an image @var{img} finds the optimal threshold value @var{level} for ## conversion to a binary image with @code{im2bw}. Color images are converted ## to grayscale before @var{level} is computed. An image histogram @var{hist} ## can also be used to allow for preprocessing of the histogram. ## ## The optional argument @var{method} is the algorithm to be used (default's to ## Otsu). Some methods may have other @var{options} and/or return an extra ## value @var{sep} (see each entry for details). The available @var{method}s are: ## ## @table @asis ## @item Otsu (default) ## Implements Otsu's method as described in @cite{Nobuyuki Otsu (1979). "A ## threshold selection method from gray-level histograms", IEEE Trans. Sys., ## Man., Cyber. 9 (1): 62-66}. This algorithm chooses the threshold to minimize ## the intraclass variance of the black and white pixels. ## ## The second output, @var{sep} represents the ``goodness'' (or separability) of ## the threshold at @var{level}. It is a value within the range [0 1], the ## lower bound (zero) being attainable by, and only by, histograms having a ## single constant gray level, and the upper bound being attainable by, and only ## by, two-valued pictures. ## ## @item concavity ## Find a global threshold for a grayscale image by choosing the threshold to ## be in the shoulder of the histogram @cite{A. Rosenfeld, and P. De La Torre ## (1983). "Histogram concavity analysis as an aid in threshold selection", IEEE ## Transactions on Systems, Man, and Cybernetics, 13: 231-235}. ## ## @item intermodes ## This assumes a bimodal histogram and chooses the threshold to be the mean of ## the two peaks of the bimodal histogram @cite{J. M. S. Prewitt, and M. L. ## Mendelsohn (1966). "The analysis of cell images", Annals of the New York ## Academy of Sciences, 128: 1035-1053}. ## ## Images with histograms having extremely unequal peaks or a broad and flat ## valley are unsuitable for this method. ## ## @item intermeans ## Iterative procedure based on the iterative intermeans algorithm of @cite{T. ## Ridler, and S. Calvard (1978). "Picture thresholding using an iterative ## selection method", IEEE Transactions on Systems, Man, and Cybernetics, 8: 630-632} ## and @cite{H. J. Trussell (1979). "Comments on 'Picture thresholding using an ## iterative selection method'", IEEE Transactions on Systems, Man, and Cybernetics, ## 9: 311}. ## ## Note that several implementations of this method exist. See the source code ## for details. ## ## @item MaxEntropy ## Implements Kapur-Sahoo-Wong (Maximum Entropy) thresholding method based on the ## entropy of the image histogram @cite{J. N. Kapur, P. K. Sahoo, and A. C. K. Wong ## (1985). "A new method for gray-level picture thresholding using the entropy ## of the histogram", Graphical Models and Image Processing, 29(3): 273-285}. ## ## @item MaxLikelihood ## Find a global threshold for a grayscale image using the maximum likelihood ## via expectation maximization method @cite{A. P. Dempster, N. M. Laird, and D. B. ## Rubin (1977). "Maximum likelihood from incomplete data via the EM algorithm", ## Journal of the Royal Statistical Society, Series B, 39:1-38}. ## ## @item mean ## The mean intensity value. It is mostly used by other methods as a first guess ## threshold. ## ## @item MinError ## An iterative implementation of Kittler and Illingworth's Minimum Error ## thresholding @cite{J. Kittler, and J. Illingworth (1986). "Minimum error ## thresholding", Pattern recognition, 19: 41-47}. ## ## This implementation seems to converge more often than the original. ## Nevertheless, sometimes the algorithm does not converge to a solution. In ## that case a warning is displayed and defaults to the initial estimate of the ## mean method. ## ## @item minimum ## This assumes a bimodal histogram and chooses the threshold to be in the ## valley of the bimodal histogram. This method is also known as the mode ## method @cite{J. M. S. Prewitt, and M. L. Mendelsohn (1966). "The analysis of ## cell images", Annals of the New York Academy of Sciences, 128: 1035-1053}. ## ## Images with histograms having extremely unequal peaks or a broad and flat ## valley are unsuitable for this method. ## ## @item moments ## Find a global threshold for a grayscale image using moment preserving ## thresholding method @cite{W. Tsai (1985). "Moment-preserving thresholding: ## a new approach", Computer Vision, Graphics, and Image Processing, 29: 377-393} ## ## @item percentile ## Assumes a specific fraction of pixels (set at @var{options}) to be background. ## If no value is given, assumes 0.5 (equal distribution of background and foreground) ## @cite{W Doyle (1962). "Operation useful for similarity-invariant pattern ## recognition", Journal of the Association for Computing Machinery 9: 259-267} ## @end table ## ## @seealso{im2bw, otsuthresh} ## @end deftypefn ## Notes: ## * The following methods were adapted from http://www.cs.tut.fi/~ant/histthresh/ ## intermodes percentile minimum ## MaxEntropy MaxLikelihood intermeans ## moments minerror concavity ## * Carnë Draug implemented and vectorized the Otsu's method ## * Carnë Draug vectorized percentile and moments. ## * missing methods from ImageJ ## Yen triangle RenyiEntropy ## Shanbhag Li Huang ## ImageJ function [varargout] = graythresh (img, algo = "otsu", varargin) ## Input checking if (nargin < 1 || nargin > 3) print_usage(); elseif (nargin > 2 && !any (strcmpi (algo, {"percentile"}))) error ("graythresh: algorithm `%s' does not accept any options.", algo); else hist_in = false; if (! isnumeric (img)) error ("graythresh: IMG must be numeric"); elseif (size (img, 3) == 3) ## If the image is RGB convert it to grayscale img = rgb2gray (img); elseif (isfloat(img) && isvector (img) && !issparse (img) && isreal (img) && all (img >= 0)) ## FIXME: we need to rething this approach. This means that ## "images" with a single row or vector will be handled ## as histograms. Also, previously we relied on having ## values outside [0 1] range to distinguish between an ## image and a histogram but for Matlab compatibility we ## need to clip the values so there's backwards ## incompatibility issues when addressing this. Because ## previously any histogram of integer class would be ## identified as an image, we added the 'isfloat' logic ## to limit the issue to images of floating point ## images. Best fix is to implement histthresh to handle ## histograms and this function simply prepares an ## histogram from a user image and then calls histthresh. hist_in = true; ihist = img; endif ## ... else it should a gray image so do nothing. The only thing ## we need to worry is on clipping values outside the [0 1] range ## for floating point images. But that is done later when it ## converts to uint8. endif ## the "mean" is the simplest of all, we can get rid of it right here if (strcmpi (algo, "mean")) varargout{1} = mean (im2double(img)(:)); return endif ## we only need to do this if the input is an image. If an histogram, is ## supplied, no need to do any of this if (!hist_in) ## if image is uint we do nothing. If it's int, then we convert to uint since ## it may mess up calculations later. If it's double we need some bins so we ## choose uint8 by default... but should we adjust the histogram before? if (isa (img, "uint16") || isa (img, "uint8")) ## do nothing elseif (isa (img, "int16")) img = im2uint16 (img); else img = im2uint8 (img); endif ihist = hist (img(:), 0:intmax (class (img))); endif switch tolower (algo) case {"concavity"}, thresh = concavity (ihist); case {"intermeans"}, thresh = intermeans (ihist, floor (mean (img (:)))); case {"intermodes"}, thresh = intermodes (ihist); case {"maxentropy"}, thresh = maxentropy (ihist); case {"maxlikelihood"}, thresh = maxlikelihood (ihist); case {"minerror"}, thresh = minerror_iter (ihist, floor (mean (img (:)))); case {"minimum"}, thresh = minimum (ihist); case {"moments"}, thresh = moments (ihist); case {"otsu"}, thresh = otsu (ihist, nargout > 1); case {"percentile"}, thresh = percentile (ihist, varargin{:}); otherwise, error ("graythresh: unknown method '%s'", algo); endswitch ## normalize the threshold value to the [0 1] range if (numel (ihist) > 1) thresh{1} = double (thresh{1}) / (numel (ihist) - 1); endif ## some algorithms may return more than one value... for i = 1:numel (thresh) varargout{i} = thresh{i}; endfor endfunction function [thresh] = otsu (ihist, compute_good) ## this method is quite well explained at ## http://www.labbookpages.co.uk/software/imgProc/otsuThreshold.html ## ## It does not, however, explain how to compute the goodness of threshold and ## there's not many pages explaining it either. For that, one really needs to ## check the paper. ## ## The implementation on the link above assumes that threshold is to be ## made for values "greater or equal than" but that is not the case (in im2bw ## and also not ImageJ) so we subtract 1 at the end. if (numel (ihist) == 1 || sum (ihist) == 0) thresh{1} = 0; thresh{2} = 0; return; endif bins = 0:(numel (ihist) - 1); total = sum (ihist); ## b = black, w = white b_totals = cumsum ([0 ihist(1:end-1)]); b_weights = b_totals / total; b_means = [0 cumsum(bins(1:end-1) .* ihist(1:end-1))] ./ b_totals; w_totals = total - b_totals; w_weights = w_totals / total; w_means = (cumsum (bins(end:-1:1) .* ihist(end:-1:1)) ./ w_totals(end:-1:1))(end:-1:1); ## between class variance (its maximum is the best threshold) bcv = b_weights .* w_weights .* (b_means - w_means).^2; max_bcv = max (bcv); if (isnan (max_bcv)) ## Couldn't measure variance. This will happen if for example all ## values are the same. See bug #45333. thresh{1} = 0; thresh{2} = 0; else ## In case there's more than one place with best maximum (for ## example, a group of empty bins), we select the one in the ## center (this is compatible with ImageJ). thresh{1} = (mean (find (bcv == max_bcv))) - 2; ## We subtract 2, once for the 1 based indexes and another for the ## greater than or equal problem. if (compute_good) ## Basically we need to divide the between class variance by the ## total variance which is a single value, independent of the ## threshold. From the paper, last of the equation 12: ## eta = sigma²b / sigma²t ## where b = between and t = total norm_hist = ihist / total; total_mean = sum (bins .* norm_hist); total_variance = sum (((bins - total_mean).^2) .* norm_hist); thresh{2} = max (bcv) / total_variance; endif endif endfunction function level = moments (y) n = numel (y) - 1; ## The threshold is chosen such that partial_sumA(y,t)/partial_sumA(y,n) ## is closest to x0. sumY = sum (y); Avec = cumsum (y) / sumY; sumB = partial_sumB (y,n); sumC = partial_sumC (y,n); sumD = partial_sumD (y,n); ## The following finds x0. x2 = (sumB*sumC - sumY*sumD) / (sumY*sumC - sumB^2); x1 = (sumB*sumD - sumC^2) / (sumY*sumC - sumB^2); x0 = .5 - (sumB/sumY + x2/2) / sqrt (x2^2 - 4*x1); ## And finally the threshold [~, ind] = min (abs (Avec-x0)); level{1} = ind-1; endfunction function T = maxentropy(y) n = numel (y) - 1; warning ("off", "Octave:divide-by-zero", "local"); ## The threshold is chosen such that the following expression is minimized. sumY = sum (y); negY = negativeE (y, n); for j = 0:n sumA = partial_sumA (y, j); negE = negativeE (y, j); sum_diff = sumY - sumA; vec(j+1) = negE/sumA - log10 (sumA) + (negY-negE)/(sum_diff) - log10 (sum_diff); end [~,ind] = min (vec); T{1} = ind-1; endfunction function [T] = intermodes (y) ## checked with ImageJ and is slightly different but not by much n = numel (y) - 1; % Smooth the histogram by iterative three point mean filtering. iter = 0; while ~bimodtest(y) h = ones(1,3)/3; y = conv2(y,h,'same'); iter = iter+1; % If the histogram turns out not to be bimodal, set T to zero. if iter > 10000; T{1} = 0; return end end % The threshold is the mean of the two peaks of the histogram. ind = 0; for k = 2:n if y(k-1) < y(k) && y(k+1) < y(k) ind = ind+1; TT(ind) = k-1; end end T{1} = floor(mean(TT)); endfunction ## The threshold is chosen such that 50% (in case of p = 0.5) of ## pixels lie in each category. function [T] = percentile (y, p = 0.5) Avec = cumsum (y) / sum (y); [~, ind] = min (abs (Avec - p)); T{1} = ind -1; endfunction function T = minimum(y) n = numel (y) - 1; % Smooth the histogram by iterative three point mean filtering. iter = 0; while ~bimodtest(y) h = ones(1,3)/3; y = conv2(y,h,'same'); iter = iter+1; % If the histogram turns out not to be bimodal, set T to zero. if iter > 10000; T{1} = 0; return end end peakfound = false; for k = 2:n if y(k-1) < y(k) && y(k+1) < y(k) peakfound = true; end if peakfound && y(k-1) >= y(k) && y(k+1) >= y(k) T{1} = k-1; return end end endfunction function [Tout] = minerror_iter (y, T) n = numel (y) - 1; Tprev = NaN; warning ("off", "Octave:divide-by-zero", "local"); sumA = partial_sumA (y, n); sumB = partial_sumB (y, n); sumC = partial_sumC (y, n); while T ~= Tprev % Calculate some statistics. sumAT = partial_sumA (y, T); sumBT = partial_sumB (y, T); sumCT = partial_sumC (y, T); sumAdiff = sumA - sumAT; mu = sumBT/sumAT; nu = (sumB-sumBT)/(sumAdiff); p = sumAT/sumA; q = (sumAdiff) / sumA; sigma2 = sumCT/sumAT-mu^2; tau2 = (sumC-sumCT) / (sumAdiff) - nu^2; % The terms of the quadratic equation to be solved. w0 = 1/sigma2-1/tau2; w1 = mu/sigma2-nu/tau2; w2 = mu^2/sigma2 - nu^2/tau2 + log10((sigma2*q^2)/(tau2*p^2)); % If the next threshold would be imaginary, return with the current one. sqterm = w1^2-w0*w2; if sqterm < 0 warning('MINERROR:NaN','Warning: th_minerror_iter did not converge.') break endif % The updated threshold is the integer part of the solution of the % quadratic equation. Tprev = T; T = floor((w1+sqrt(sqterm))/w0); % If the threshold turns out to be NaN, return with the previous threshold. if isnan(T) warning('MINERROR:NaN','Warning: th_minerror_iter did not converge.') T = Tprev; end endwhile Tout{1} = T; endfunction #{ ## this is an implementation of the original minerror algorithm but seems ## to converge less often than the iterative version. This one is also from the ## HistThresh toolbox function T = th_minerror(I,n) if nargin == 1 n = 255; end I = double(I); % Calculate the histogram. y = hist(I(:),0:n); % The threshold is chosen such that the following expression is minimized. for j = 0:n mu = partial_sumB(y,j)/partial_sumA(y,j); nu = (partial_sumB(y,n)-partial_sumB(y,j))/(partial_sumA(y,n)-partial_sumA(y,j)); p = partial_sumA(y,j)/partial_sumA(y,n); q = (partial_sumA(y,n)-partial_sumA(y,j)) / partial_sumA(y,n); sigma2 = partial_sumC(y,j)/partial_sumA(y,j)-mu^2; tau2 = (partial_sumC(y,n)-partial_sumC(y,j)) / (partial_sumA(y,n)-partial_sumA(y,j)) - nu^2; vec(j+1) = p*log10(sqrt(sigma2)/p) + q*log10(sqrt(tau2)/q); end vec(vec==-inf) = NaN; [minimum,ind] = min(vec); T = ind-1; endfunction #} function Tout = maxlikelihood (y) n = numel (y) - 1; ## initial estimate for the threshold is found with the minimum algorithm T = minimum (y){1}; sumY = sum (y); sumB = partial_sumB (y, n); sumC = partial_sumC (y, n); sumAT = partial_sumA (y, T); sumBT = partial_sumB (y, T); sumCT = partial_sumC (y, T); ## initial values for the statistics mu = sumBT / sumAT; nu = (sumB - sumBT) / (sumY - sumAT); p = sumAT / sumY; q = (sumY - sumAT) / sumY; sigma2 = sumCT / sumAT - mu^2; tau2 = (sumC - sumCT) / (sumY - sumAT) - nu^2; ## Return if sigma2 or tau2 are zero, to avoid division by zero if (sigma2 == 0 || tau2 == 0) Tout{1} = T; return endif do ## we store the previous values for comparison at the end (we will stop when ## they stop changing) mu_prev = mu; nu_prev = nu; p_prev = p; q_prev = q; sigma2_prev = nu; tau2_prev = nu; for i = 0:n phi(i+1) = p/sqrt((sigma2)) * exp(-((i-mu)^2) / (2*sigma2)) / ... (p/sqrt(sigma2) * exp(-((i-mu)^2) / (2*sigma2)) + ... (q/sqrt(tau2)) * exp(-((i-nu)^2) / (2*tau2))); endfor ind = 0:n; gamma = 1-phi; F = phi*y'; G = gamma*y'; mu = ind.*phi*y'/F; nu = ind.*gamma*y'/G; p = F / sumY; q = G / sumY; sigma2 = ind.^2.*phi*y'/F - mu^2; tau2 = ind.^2.*gamma*y'/G - nu^2; until (abs (mu - mu_prev) <= eps || abs (nu - nu_prev) <= eps || ... abs (p - p_prev) <= eps || abs (q - q_prev) <= eps || ... abs (sigma2 - sigma2_prev) <= eps || abs (tau2 - tau2_prev) <= eps) ## the terms of the quadratic equation to be solved w0 = 1/sigma2-1/tau2; w1 = mu/sigma2-nu/tau2; w2 = mu^2/sigma2 - nu^2/tau2 + log10((sigma2*q^2)/(tau2*p^2)); ## If the threshold would be imaginary, return with threshold set to zero sqterm = w1^2-w0*w2; if (sqterm < 0) Tout{1} = 0; return endif ## The threshold is the integer part of the solution of the quadratic equation Tout{1} = floor((w1+sqrt(sqterm))/w0); endfunction function Tout = intermeans (y, T) n = numel (y) - 1; Tprev = NaN; % The threshold is found iteratively. In each iteration, the means of the % pixels below (mu) the threshold and above (nu) it are found. The % updated threshold is the mean of mu and nu. sumY = sum (y); sumB = partial_sumB (y, n); while T ~= Tprev sumAT = partial_sumA (y, T); sumBT = partial_sumB (y, T); mu = sumBT/sumAT; nu = (sumB-sumBT)/(sumY-sumAT); Tprev = T; T = floor((mu+nu)/2); end Tout{1} = T; endfunction function T = concavity (h) n = numel (h) - 1; H = hconvhull(h); % Find the local maxima of the difference H-h. lmax = flocmax(H-h); % Find the histogram balance around each index. for k = 0:n E(k+1) = hbalance(h,k); end % The threshold is the local maximum with highest balance. E = E.*lmax; [dummy ind] = max(E); T{1} = ind-1; endfunction ################################################################################ ## Auxiliary functions from HistThresh toolbox http://www.cs.tut.fi/~ant/histthresh/ ################################################################################ ## partial sums from C. A. Glasbey, "An analysis of histogram-based thresholding ## algorithms," CVGIP: Graphical Models and Image Processing, vol. 55, pp. 532-537, 1993. function x = partial_sumA (y, j) x = sum (y(1:j+1)); endfunction function x = partial_sumB (y, j) ind = 0:j; x = ind*y(1:j+1)'; endfunction function x = partial_sumC (y, j) ind = 0:j; x = ind.^2*y(1:j+1)'; endfunction function x = partial_sumD (y, j) ind = 0:j; x = ind.^3*y(1:j+1)'; endfunction ## Test if a histogram is bimodal. function b = bimodtest(y) len = length(y); b = false; modes = 0; % Count the number of modes of the histogram in a loop. If the number % exceeds 2, return with boolean return value false. for k = 2:len-1 if y(k-1) < y(k) && y(k+1) < y(k) modes = modes+1; if modes > 2 return end end end % The number of modes could be less than two here if modes == 2 b = true; end endfunction ## Find the local maxima of a vector using a three point neighborhood. function y = flocmax(x) % y binary vector with maxima of x marked as ones len = length(x); y = zeros(1,len); for k = 2:len-1 [dummy,ind] = max(x(k-1:k+1)); if ind == 2 y(k) = 1; end end endfunction ## Calculate the balance measure of the histogram around a histogram index. function E = hbalance(y,ind) % y histogram % ind index about which balance is calculated % % Out: % E balance measure % % References: % % A. Rosenfeld and P. De La Torre, "Histogram concavity analysis as an aid % in threshold selection," IEEE Transactions on Systems, Man, and % Cybernetics, vol. 13, pp. 231-235, 1983. % % P. K. Sahoo, S. Soltani, and A. K. C. Wong, "A survey of thresholding % techniques," Computer Vision, Graphics, and Image Processing, vol. 41, % pp. 233-260, 1988. n = length(y)-1; E = partial_sumA(y,ind)*(partial_sumA(y,n)-partial_sumA(y,ind)); endfunction ## Find the convex hull of a histogram. function H = hconvhull(h) % In: % h histogram % % Out: % H convex hull of histogram % % References: % % A. Rosenfeld and P. De La Torre, "Histogram concavity analysis as an aid % in threshold selection," IEEE Transactions on Systems, Man, and % Cybernetics, vol. 13, pp. 231-235, 1983. len = length(h); K(1) = 1; k = 1; % The vector K gives the locations of the vertices of the convex hull. while K(k)~=len theta = zeros(1,len-K(k)); for i = K(k)+1:len x = i-K(k); y = h(i)-h(K(k)); theta(i-K(k)) = atan2(y,x); end maximum = max(theta); maxloc = find(theta==maximum); k = k+1; K(k) = maxloc(end)+K(k-1); end % Form the convex hull. H = zeros(1,len); for i = 2:length(K) H(K(i-1):K(i)) = h(K(i-1))+(h(K(i))-h(K(i-1)))/(K(i)-K(i-1))*(0:K(i)-K(i-1)); end endfunction ## Entropy function. Note that the function returns the negative of entropy. function x = negativeE(y,j) ## used by the maxentropy method only y = y(1:j+1); y = y(y~=0); x = sum(y.*log10(y)); endfunction ## these were tested with ImageJ %!shared img, histo %! ## this is the old default.img that came with GNU Octave. While the current %! ## is very very similar, is off just enough for us to get precision errors %! img = uint8 (reshape ([138 138 138 142 142 138 142 138 138 117 105 81 69 61 53 40 49 45 40 36 40 45 53 49 65 73 121 166 210 243 247 247 247 239 235 178 154 170 150 150 162 174 190 190 194 186 178 170 154 182 198 174 117 138 138 142 138 142 142 146 142 138 138 130 109 97 81 73 69 57 53 53 57 61 61 69 73 77 105 121 158 219 243 243 247 243 243 243 206 150 158 158 158 150 158 182 186 190 194 186 174 190 206 198 162 138 142 138 142 146 138 142 142 138 146 142 134 142 130 121 101 97 85 85 81 81 81 85 93 85 73 57 61 93 150 194 215 239 243 243 243 223 166 138 158 158 154 142 162 178 190 190 198 186 182 186 174 162 182 146 142 138 142 142 146 142 146 146 146 146 142 142 142 134 125 101 85 73 65 69 73 73 57 40 53 49 57 69 85 125 166 182 178 178 174 150 130 121 146 146 150 142 166 182 190 182 174 166 162 170 194 198 138 138 146 146 138 146 146 146 146 142 150 146 146 142 130 93 65 45 45 49 45 40 49 40 49 49 49 49 61 81 113 142 150 154 154 146 142 134 125 125 138 134 125 146 162 178 178 178 166 186 202 206 186 142 142 142 134 142 146 142 150 142 146 142 146 146 130 81 53 49 49 45 49 40 36 36 32 36 36 36 53 73 89 125 150 146 134 138 146 138 146 138 142 117 117 113 117 146 166 174 178 182 178 178 170 146 142 142 138 142 146 142 142 146 150 138 146 142 130 73 49 40 49 57 65 69 73 61 61 53 57 53 61 77 77 97 113 138 134 130 138 142 150 146 150 134 138 121 121 101 121 150 158 154 142 150 162 166 178 138 138 146 142 142 142 142 146 146 142 142 130 73 57 49 36 49 65 77 85 89 85 81 81 81 85 93 93 97 105 117 125 150 158 154 162 162 166 154 134 150 130 125 113 138 182 174 154 130 178 227 239 239 134 138 142 138 142 142 146 146 138 150 125 61 49 32 32 45 49 57 65 85 101 105 101 101 109 125 117 113 109 138 134 125 166 178 170 162 150 170 162 170 150 146 150 138 125 162 186 182 142 206 247 247 243 138 138 138 138 142 142 146 146 146 130 85 45 45 36 40 53 45 57 69 97 125 130 130 134 138 146 142 134 142 158 138 117 146 174 170 174 178 170 174 170 166 154 162 158 130 134 170 178 158 190 243 247 247 142 142 142 142 142 146 146 142 138 89 53 45 40 45 45 49 57 77 93 125 138 150 154 158 158 162 154 150 166 174 142 73 125 174 178 174 182 182 178 178 174 166 174 174 162 125 154 170 174 170 227 247 251 142 138 142 142 142 142 142 138 105 61 40 40 32 40 40 49 61 89 117 146 154 158 162 170 170 174 162 166 174 182 150 65 146 166 174 186 198 198 198 190 178 178 174 174 158 134 154 198 194 174 202 251 251 146 142 142 142 146 150 138 134 69 40 40 36 32 40 45 45 65 101 134 150 158 166 174 178 174 174 174 170 170 174 142 73 150 162 178 194 202 202 194 194 178 178 154 134 125 138 154 198 194 186 190 243 251 150 146 146 146 146 150 130 109 53 45 28 40 40 36 32 49 73 101 130 154 162 170 170 170 178 182 178 178 174 158 142 121 146 158 178 174 186 190 186 186 174 146 105 109 113 130 150 178 202 190 186 243 251 146 146 146 146 150 142 109 73 49 40 32 40 40 45 40 53 69 93 130 154 162 170 174 178 182 182 186 182 178 154 146 130 138 142 150 170 182 178 174 166 150 117 97 105 113 130 150 150 174 182 190 243 251 146 146 154 146 150 134 105 53 40 45 45 40 40 36 36 40 69 105 134 162 170 174 178 182 182 182 186 190 186 178 170 158 154 150 162 182 182 174 174 174 150 113 109 113 113 130 150 162 186 186 190 239 251 154 150 146 150 146 125 77 49 36 40 36 40 36 28 40 36 77 113 138 150 170 170 174 186 190 190 190 194 190 186 194 190 170 162 174 194 174 182 170 170 158 121 113 113 113 146 158 170 210 215 215 206 243 150 146 150 150 150 113 57 49 40 45 45 49 49 40 32 45 85 113 142 170 178 174 182 194 190 194 194 198 198 198 210 210 182 162 170 190 182 186 170 170 162 130 121 113 121 146 154 150 198 215 206 210 215 150 150 150 150 150 105 49 45 40 49 49 57 40 49 49 53 85 121 158 182 178 174 182 198 194 194 194 194 202 202 194 186 174 154 162 166 178 174 170 170 170 158 117 113 130 150 154 121 182 194 206 215 206 158 150 150 150 146 97 45 36 49 49 49 40 40 49 49 65 97 130 154 174 174 174 186 194 194 194 194 198 198 186 170 158 154 158 138 158 162 170 190 182 174 170 138 138 142 154 134 142 146 170 206 219 215 150 150 158 158 150 85 36 40 40 40 40 45 45 49 49 65 97 130 146 166 166 174 182 190 194 194 194 194 190 182 162 158 150 158 182 186 178 198 206 198 190 174 154 174 174 142 142 170 170 166 202 223 219 158 150 150 150 146 85 40 45 40 40 36 45 53 45 49 53 93 117 130 154 162 174 190 186 194 194 194 190 186 178 162 162 170 174 182 198 210 206 210 198 198 182 170 178 174 158 154 194 194 174 198 210 215 150 154 158 150 150 85 49 45 40 40 32 36 53 40 45 53 81 109 142 158 158 174 178 182 190 190 194 190 190 178 170 174 178 186 190 190 206 215 202 206 194 186 178 182 174 154 170 198 210 186 186 202 215 150 154 150 154 150 97 45 40 40 40 36 36 45 40 45 73 89 113 142 158 158 174 174 182 186 186 194 186 182 178 174 170 105 166 206 186 190 202 198 194 190 182 182 174 166 154 162 198 215 202 182 202 219 154 150 154 150 146 117 61 45 45 45 36 53 53 49 53 77 93 101 125 158 162 174 174 178 174 186 190 182 182 186 182 182 77 125 198 194 186 190 190 178 178 178 162 162 162 154 186 210 227 210 190 206 223 154 150 154 150 154 138 65 45 45 45 40 49 49 40 53 65 77 89 113 150 158 166 166 170 178 182 186 182 170 170 170 162 81 117 186 190 186 182 178 186 174 166 162 150 130 154 194 227 227 219 202 202 219 154 154 150 154 146 146 89 45 40 45 40 49 49 36 40 57 65 89 109 138 146 158 158 170 170 178 182 178 162 150 158 154 113 146 186 182 178 182 178 170 170 162 146 138 138 146 202 223 231 219 210 190 215 130 130 130 130 130 130 109 45 53 40 32 36 40 45 53 61 65 81 97 117 130 138 150 158 158 178 170 162 158 138 142 150 146 166 178 174 174 170 170 170 162 158 138 117 117 142 202 223 239 223 215 186 206 61 61 65 69 69 65 57 36 40 36 32 40 40 53 57 53 57 69 93 105 109 130 138 142 154 162 150 138 142 125 121 150 162 170 170 166 170 170 170 166 162 138 121 113 130 170 202 223 227 231 202 178 182 45 49 45 40 40 40 45 45 45 45 36 40 32 49 61 61 57 65 73 81 101 109 121 130 142 146 121 89 93 117 113 134 154 174 166 162 166 170 170 162 154 150 142 150 223 186 194 215 231 227 206 182 174 49 40 45 45 49 49 45 49 49 49 49 40 36 45 57 69 65 61 65 69 85 93 109 109 117 109 89 57 57 81 97 113 154 162 166 162 170 158 158 162 154 162 174 231 239 178 186 210 231 239 210 194 178 49 36 49 45 49 49 49 45 45 49 49 36 40 40 45 36 53 53 53 57 57 69 69 73 69 61 57 45 45 65 89 105 125 142 146 150 150 154 162 170 174 223 235 247 231 178 178 206 227 227 223 198 190 40 53 36 45 40 40 40 40 45 40 40 45 45 45 45 40 53 49 49 45 53 45 32 36 36 36 36 40 49 45 61 73 89 93 97 113 125 142 186 202 239 239 243 251 239 198 166 194 215 235 227 215 202 40 45 36 32 36 40 40 45 40 40 45 49 45 49 45 49 40 40 45 49 40 45 45 45 49 49 32 40 49 40 49 57 69 81 101 134 170 206 235 243 243 239 247 251 247 210 170 186 202 231 231 227 210 49 45 49 40 40 40 49 45 40 40 45 45 45 40 45 45 45 49 40 49 40 49 45 45 36 40 40 45 45 45 45 65 121 150 210 239 243 243 247 243 243 247 251 251 239 223 178 174 194 219 239 231 219 36 45 45 40 40 49 40 45 49 49 40 40 45 49 40 40 45 49 45 40 49 45 40 40 40 49 40 45 40 49 49 121 162 215 247 247 247 247 247 243 247 251 251 251 247 239 223 194 186 202 215 210 210 36 45 45 40 40 49 40 45 32 36 49 36 45 49 40 40 45 40 36 40 45 45 40 40 40 36 45 32 40 49 57 121 142 215 243 247 243 247 243 247 251 251 251 251 247 247 247 227 186 194 190 190 182 40 32 45 32 45 40 45 45 49 45 40 45 49 36 40 45 32 40 45 45 49 45 45 45 45 53 49 53 45 45 40 69 97 186 239 243 247 247 247 251 251 251 251 251 243 243 231 202 202 206 206 186 170 53 40 40 40 40 40 36 32 32 36 45 53 49 32 36 32 36 32 40 49 40 40 45 40 40 53 45 49 49 40 32 40 49 138 219 235 247 247 251 251 251 251 251 247 243 235 198 206 210 198 190 186 186 73 69 61 57 61 49 53 40 49 45 40 49 49 49 57 57 53 49 53 53 45 40 45 40 45 49 45 49 45 40 32 53 69 101 215 231 247 247 247 247 251 251 251 243 235 219 194 202 202 186 186 190 194], [53 40])); %!assert (graythresh (img, "percentile"), 142/255); %!assert (graythresh (img, "percentile", 0.5), 142/255); %!assert (graythresh (img, "moments"), 142/255); %!assert (graythresh (img, "minimum"), 93/255); %!assert (graythresh (img, "maxentropy"), 150/255); %!assert (graythresh (img, "intermodes"), 99/255); %!assert (graythresh (img, "otsu"), 114.5/255); %! histo = hist (img(:), 0:255); %!assert (graythresh (histo, "otsu"), 114.5/255); ## for the mean our results differ from matlab because we do not calculate it ## from the histogram. Our results should be more accurate. %!assert (graythresh (img, "mean"), 0.51445615982, 0.000000001); # here our results differ from ImageJ ## Test for bug #45333 %!test %! im = repmat (0.5, 100, 100); %! [t, g] = graythresh (im); %! assert (t, 0) %! assert (g, 0) ## test for bug #51976 ## Values outside the range [0 1] in floating images, should be clipped %!test %! im = [-2 1 0; 43 .5 .2]; %! clip_im = [ 0 1 0; 1 .5 .2]; %! t = graythresh (clip_im); %! assert (graythresh (im), t) %! assert (graythresh (single (im)), t) ## test for bug #45333 %!test %! H(1) = 100; %! assert (graythresh (H), 0) image-2.16.1/inst/PaxHeaders.61586/affine2d.m0000644000000000000000000000006215005110255015236 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/affine2d.m0000644000175000017500000001507615005110255016645 0ustar00avinoamavinoam00000000000000## Copyright (C) 2017 Carnë Draug ## Copyright (C) 2015 Motherboard ## ## 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 . classdef affine2d < affine ## -*- texinfo -*- ## @deftypefn {Function File} {@var{tform} =} affine2d (@var{T}) ## @deftypefnx {Function File} {@var{tform} =} affine2d () ## tform is a representation of an affine 2D transform. ## Calling affine2D without parameters, (@code{affine2d ()}) produces the identity ## transformation. ## ## affine2d takes a transpose of the affine matrix as described in ## standard literature it performs the transformation as follows: ## v = (u*T)(1:2) ## where u = [x y 1] and T = [a b 0; c d 0; e f 1] [a b; c d] is a ## transposed rotation\shear matrix, [e f] is the translation vector, ## where e = dx, f = dy. ## ## affine2d methods: ## @table @asis ## @item @qcode{"invert"} ## @code{invert (tform)} - produces the inverse transform of affine2d transform ## @item @qcode{"isRigid"} ## @code{isRigid (tform)} - checks if transform tform is only rotation or translation. ## @item @qcode{"isSimilarity"} ## @code{isSimilarity (tform)} - checks if transform tform is only homogeneous scaling, ## rotation, reflection or translation. ## @item @qcode{"isTranslation"} ## @code{isTranslation (tform)} - checks if transform tform is is a pure translation ## @item @qcode{"outputLimits"} ## @code{outputLimits (tform, xlims, ylims)} - given a bounding box corner ## coordinates in xlims and ylims (top left, right bottom) - returns the new ## bounding box after transformation. ## @item @qcode{"transformPointsForward"} ## @code{transformPointsForward(tform, u, v)} - apply transformation tform ## on the set of u, v points (1xn vectors) ## @code{transformPointsForward(tform, U)} - apply transformation tform ## on U (2xn matrix) ## @item @qcode{"transformPointsInverse"} ## @code{transformPointsInverse(tform, u, v)} - apply the inverse transformation ## of tform on the set of u, v points (1xn vectors) ## @code{transformPointsInverse(tform, U)} - apply the inverse transformation ## of tform on U (2xn matrix) ## ## @end table ## ## @seealso{affine3d} ## @end deftypefn methods function this = affine2d (T) if (nargin > 1) error ("affine2d: usage - affine2d(T), where T is a 3x3 matrix") endif if (nargin == 0) T = eye(3); endif this@affine (T); endfunction ## Given a bounding box corner coordinated in xlims and ylims (top ## left, right bottom) - return the new bounding box after ## transformation. function [limitsX, limitsY] = outputLimits (this, xlims, ylims) if (nargin ~= 3) error ("outputLimits usage: outputLimits (tform, xlims, ylims)") endif xlims2 = [xlims(:); xlims(:)]; ylims2 = [ylims(:); ylims(end:-1:1)(:)]; temp = [xlims2 ylims2 ones(4,1)]; temp = temp*this.T; limitsX = [min(temp(1:4,1)), max(temp(1:4,1))]; limitsY = [min(temp(1:4,2)), max(temp(1:4,2))]; endfunction endmethods endclassdef %!test %! theta = 10; %! A = [cosd(theta) -sind(theta) 0 %! sind(theta) cosd(theta) 0 %! 0 0 1]; %! tform = affine2d (A); %! [X, Y] = transformPointsForward (tform, 5, 10); %! assert (X, 6.6605, 1.e-4) %! assert (Y, 8.9798, 1.e-4) %! %! [U, V] = transformPointsInverse (tform, X, Y); %! assert (U, 5, 5*eps) %! assert (V, 10, 5*eps) %! assert (isRigid (tform)) %! assert (! isTranslation (tform)) %! assert (isSimilarity (tform)) %!test %! theta = 30; %! tform = affine2d([ cosd(theta) sind(theta) 0 %! -sind(theta) cosd(theta) 0 %! 0 0 1]); %! assert (tform.T, [ 0.86603 0.5 0 %! -0.5 0.86603 0 %! 0 0 1], 1.e-5); %! invtform = invert(tform); %! assert (invtform.T, [ 0.86603 -0.5 0 %! 0.5 0.86603 0 %! 0 0 1], 1.e-5); %! assert (isRigid (tform)) %! assert (! isTranslation (tform)) %! assert (isSimilarity (tform)) %!test %! tform = affine2d ([1 0 0; 0 1 0; 5 10 1]); %! [X, Y] = transformPointsForward (tform, [1 2; 3 4; 5 6; 7 8]); %! assert (round (X), [6; 8; 10; 12]) %! assert (round (Y), [12; 14; 16; 18]) %! %! [U, V] = transformPointsInverse (tform, X, Y); %! assert (round (U), [1; 3; 5; 7]) %! assert (round (V), [2; 4; 6; 8]) %! assert (isRigid (tform)) %! assert (isTranslation (tform)) %! assert (isSimilarity (tform)) %!test %! tform = affine2d ([1 1e-16 0; 1e-16 1 0; 5 10 1]); %! assert (isRigid (tform)) %! tform = affine2d ([2 1e-16 0; 1e-16 1 0; 5 10 1]); %! assert (! isRigid (tform)) %!test %! theta = 10; %! A = [cosd(theta) -sind(theta) 0 %! sind(theta) cosd(theta) 0 %! 0 0 1]; %! tform = affine2d (A); %! [xlim, ylim] = outputLimits (tform, [1 240], [1 291]); %! assert (xlim, [1.1585 286.8855], 1.e-4) %! assert (ylim, [-40.6908 286.4054], 1.e-4) %!test %! A = [1 0 0 %! 0 1 0 %! 40 40 1]; %! tform = affine2d (A); %! assert (isRigid (tform)); %! assert (isSimilarity (tform)); %! assert (isTranslation (tform)); %!test %! a = invert (affine2d ([1 2 0; 3 4 0; 10 20 1])); %! b = affine2d(a.T); %! assert (b.T, [-2, 1, 0; 1.5, -0.5, 0; -10, 0, 1], 5*eps) %!assert (isTranslation (affine2d ([1, 0, 0; 0, 1, 0; 40, 40, 1]))) %!assert (! isTranslation (affine2d ([1 0 0; 0 -1 0; 0 0 1]))) %!assert (! isRigid (affine2d ([1 0 0; 0 -1 0; 0 0 1]))) %!error affine2d ([0 0 0; 0 0 0]) %!error affine2d ([0 0 0 0 0 0 0 0 1]) %!error affine2d ([0 0 0; 0 0 0; 0 0 0]) %!error affine2d ([1 0 0; 0 1 1; 0 0 1]) %!error affine2d ([0 0 0; 0 0 0; 0 0 1]) %!error affine2d (1, 2) %!error outputLimits (affine2d()) %!test %! tform = affine2d; %! assert (tform.T, eye (3)) %! assert (tform.Dimensionality, 2) image-2.16.1/inst/PaxHeaders.61586/imcast.m0000644000000000000000000000006215005110255015040 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imcast.m0000644000175000017500000001620115005110255016436 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} 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. ## ## If the image is indexed, the last argument may be the string ## @qcode{"indexed"}. An indexed image may not be of class int16, ## single, or logical (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}. For the ## case of logical, conversion to logical is equivalent to simply ## casting @var{img} to logical data type. Conversion from logical, ## true elements are converted to 1 in the case of floating point, or ## the maximum value in the case of integer types. ## ## @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); case "logical", imout = logical (img); 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 case "logical", imout = logical (img); 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); case "logical", imout = logical (img); 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); case "logical", imout = logical (img); 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 imcast (randi (127, 40, "int8"), "uint8") %!error imcast (randi (255, 40, "uint8"), "uint32") %!error imcast (randi (255, 40, "uint8"), "not a class") %!error imcast (randi ([0 65535], 40, "uint16"), "uint8", "indexed") %!assert (imcast ([0 1 .2; 2 -0 Inf], "logical"), logical ([0 1 1; 1 0 1])) image-2.16.1/inst/PaxHeaders.61586/blockproc.m0000644000000000000000000000006215005110255015536 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/blockproc.m0000644000175000017500000001510015005110255017131 0ustar00avinoamavinoam00000000000000## Copyright (C) 2004 Josep Mones i Teixidor ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {@var{B} = } blockproc (@var{A}, [@var{m},@var{n}], @var{fun}) ## @deftypefnx {Function File} {@var{B} = } blockproc (@var{A}, [@var{m},@var{n}], @var{fun}, @dots{}) ## @deftypefnx {Function File} {@var{B} = } blockproc (@var{A}, [@var{m},@var{n}], [@var{mborder},@var{nborder}], @var{fun}, @dots{}) ## @deftypefnx {Function File} {@var{B} = } blockproc (@var{A}, 'indexed', @dots{}) ## Processes image in blocks using user-supplied function. ## ## @code{B=blockproc(A,[m,n],fun)} divides image @var{A} in ## @var{m}-by-@var{n} blocks, and passes them to user-supplied function ## @var{fun}, which result is concatenated to build returning matrix ## @var{B}. If padding is needed to build @var{m}-by-@var{n}, it is added ## at the bottom and right borders of the image. 0 is used as a padding ## value. ## ## @code{B=blockproc(A,[m,n],fun, @dots{})} behaves as described above but ## passes extra parameters to function @var{fun}. ## ## @code{B=blockproc(A,[m,n],[mborder,nborder],fun, @dots{})} behaves as ## described but uses blocks which overlap with neighbour blocks. ## Overlapping dimensions are @var{mborder} vertically and @var{nborder} ## horizontally. This doesn't change the number of blocks in an image ## (which depends only on size(@var{A}) and [@var{m},@var{n}]). Adding a ## border requires extra padding on all edges of the image. 0 is used as ## a padding value. ## ## @code{B=blockproc(A,'indexed', @dots{})} assumes that @var{A} is an indexed ## image, so it pads the image using proper value: 0 for uint8 and ## uint16 images and 1 for double images. Keep in mind that if 'indexed' ## is not specified padding is always done using 0. ## ## @seealso{colfilt,inline,bestblk} ## @end deftypefn function B = blockproc (A, varargin) if (nargin < 3) print_usage (); endif ## check 'indexed' presence indexed = false; p = 1; if (ischar (varargin{1}) && strcmp (varargin{1}, "indexed")) indexed = true; p += 1; if (isa (A, "uint8") || isa (A, "uint16")) padval = 0; else padval = 1; endif else padval = 0; endif ## check [m, n] if (! isvector (varargin{p})) error ("blockproc: expected [m,n] but param is not a vector."); endif if (length (varargin{p}) != 2) error ("blockproc: expected [m,n] but param has wrong length."); endif sblk = varargin{p}(:); p += 1; ## check [mborder, nborder] if (nargin < p) error ("blockproc: required parameters haven't been supplied."); endif if (isvector (varargin{p}) && isnumeric (varargin{p})) if (length (varargin{p}) != 2) error ("blockproc: expected [mborder,nborder] but param has wrong length."); endif sborder = varargin{p}(:); p += 1; else sborder = [0; 0]; endif ## check fun ## TODO: add proper checks for this one if (nargin < p) error ("blockproc: required parameters haven't been supplied."); endif fun = varargin{p}; if (! isa (fun, "function_handle") && ! isa (fun, "inline function") && ! isa (fun, "inline") && ! ischar (fun)) error ("blockproc: invalid FUN parameter."); endif ## remaining params are params to fun ## extra params are p+1:nargin-1 ## First of all we calc needed padding which will be applied on bottom ## and right borders ## The "-" makes the function output needed elements to fill another ## block directly sp = mod (-size (A)', sblk); if (any (sp)) A = padarray (A, sp, padval, "post"); endif ## we store A size without border padding to iterate later soa = size (A); ## If we have borders then we need more padding if (any (sborder)) A = padarray (A, sborder, padval); endif ## calculate end of block eblk = sblk + sborder * 2 - 1; ## now we can process by blocks ## we try to preserve fun return type by concatenating everything for i = 1:sblk(1):soa(1) ## This assures r has the same class as returned by fun r = feval (fun, A(i:i+eblk(1),1:1+eblk(2)), varargin{p+1:nargin-1}); for j = 1 + sblk(2):sblk(2):soa(2) r = horzcat (r, feval (fun, A(i:i+eblk(1), j:j+eblk(2)), varargin{p+1:nargin-1})); endfor if (i == 1) ## this assures B has the same class as A B = r; else B = vertcat (B, r); endif endfor endfunction %!demo %! blockproc (eye (6), [2, 2], @(x) any (x(:))) %! # Returns a 3-by-3 diagonal %!assert (blockproc (eye (6), [2, 2], "sum"), %! blockproc (eye (6), [2, 2], @sum)) %!assert (blockproc (eye (6), [2, 2], "sum"), %! blockproc (eye (6), [2, 2], @(x) sum (x))) %!assert (blockproc (eye (6), [1,2], @sum), %! kron (eye (3), [1; 1])) %!assert (blockproc (eye (6), [2,2], @(x) any (x(:))), %! eye (3) != 0) %!assert (blockproc (eye (6), [1,2],[1,1], @(x) sum (x(:))), %! [2,1,0; 3,2,0; 2,3,1; 1,3,2; 0,2,3; 0,1,2]) %!assert (blockproc (eye (6), "indexed", [1, 2], [1, 1], @(x) sum (x(:))), %! [8,5,6; 6,2,3; 5,3,4; 4,3,5; 3,2,6; 6,5,8]) %!assert (blockproc (eye (6), [2,3],[4,3], @(x) sum (x(:))), %! ones (3, 2) * 6) % Some int* and uint* tests %!assert (blockproc (eye (6), [2, 2], @(x) int8 (sum (x(:)))), %! eye (3, "int8") * 2) % Padding is 0 even for indexed %!assert (blockproc (uint8 (eye (6)), [1,2], [1,1], @(x) sum (x(:))), %! [2,1,0; 3,2,0; 2,3,1; 1,3,2; 0,2,3; 0,1,2]) %!assert (blockproc (uint8 (eye (6)), "indexed", [1,2], [1,1], @(x) sum (x(:))), %! [2,1,0; 3,2,0; 2,3,1; 1,3,2; 0,2,3; 0,1,2]); %!assert (blockproc (uint16 (eye (6)), [1,2], [1,1], @(x) sum (x(:))), %! [2,1,0; 3,2,0; 2,3,1; 1,3,2; 0,2,3; 0,1,2]); %!assert (blockproc (uint16 (eye (6)), "indexed", [1,2], [1,1], %! @(x) sum (x(:))), %! [2,1,0; 3,2,0; 2,3,1; 1,3,2; 0,2,3; 0,1,2]); # Support "inline" functions as long as Octave itself does (bug #59022) %!assert (blockproc (eye (6), [2, 2], "sum"), %! blockproc (eye (6), [2, 2], inline ("sum (x)", "x"))) image-2.16.1/inst/PaxHeaders.61586/imsmooth.m0000644000000000000000000000006215005110255015417 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imsmooth.m0000644000175000017500000004513115005110255017021 0ustar00avinoamavinoam00000000000000## Copyright (C) 2007 Søren Hauberg ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{J} = imsmooth(@var{I}, @var{name}, @var{options}) ## Smooth the given image using several different algorithms. ## ## The first input argument @var{I} is the image to be smoothed. If it is an RGB ## image, each color plane is treated separately. ## The variable @var{name} must be a string that determines which algorithm will ## be used in the smoothing. It can be any of the following strings ## ## @table @asis ## @item "Gaussian" ## Isotropic Gaussian smoothing. This is the default. ## @item "Average" ## Smoothing using a rectangular averaging linear filter. ## @item "Disk" ## Smoothing using a circular averaging linear filter. ## @item "Median" ## Median filtering. ## @item "Bilateral" ## Gaussian bilateral filtering. ## @item "Perona & Malik" ## @itemx "Perona and Malik" ## @itemx "P&M" ## Smoothing using nonlinear isotropic diffusion as described by Perona and Malik. ## @item "Custom Gaussian" ## Gaussian smoothing with a spatially varying covariance matrix. ## @end table ## ## In all algorithms the computation is done in double precision floating point ## numbers, but the result has the same type as the input. Also, the size of the ## smoothed image is the same as the input image. ## ## @strong{Gaussian - Isotropic Gaussian smoothing} ## ## The image is convolved with a Gaussian filter with spread @var{sigma}. ## By default @var{sigma} is @math{0.5}, but this can be changed. If the third ## input argument is a scalar it is used as the filter spread. ## ## The image is extrapolated symmetrically before the convolution operation. ## ## @strong{Average - Rectangular averaging linear filter} ## ## The image is convolved with @var{N} by @var{M} rectangular averaging filter. ## By default a 3 by 3 filter is used, but this can e changed. If the third ## input argument is a scalar @var{N} a @var{N} by @var{N} filter is used. If the third ## input argument is a two-vector @code{[@var{N}, @var{M}]} a @var{N} by @var{M} ## filter is used. ## ## The image is extrapolated symmetrically before the convolution operation. ## ## @strong{Disk - Circular averaging linear filter} ## ## The image is convolved with circular averaging filter. By default the filter ## has a radius of 5, but this can e changed. If the third input argument is a ## scalar @var{r} the radius will be @var{r}. ## ## The image is extrapolated symmetrically before the convolution operation. ## ## @strong{Median - Median filtering} ## ## Each pixel is replaced with the median of the pixels in the local area. By ## default, this area is 3 by 3, but this can be changed. If the third input ## argument is a scalar @var{N} the area will be @var{N} by @var{N}, and if it's ## a two-vector [@var{N}, @var{M}] the area will be @var{N} by @var{M}. ## ## The image is extrapolated symmetrically before the filtering is performed. ## ## @strong{Bilateral - Gaussian bilateral filtering} ## ## The image is smoothed using Gaussian bilateral filtering as described by ## Tomasi and Manduchi [2]. The filtering result is computed as ## @example ## @var{J}(x0, y0) = k * SUM SUM @var{I}(x,y) * w(x, y, x0, y0, @var{I}(x0,y0), @var{I}(x,y)) ## x y ## @end example ## where @code{k} a normalisation variable, and ## @example ## w(x, y, x0, y0, @var{I}(x0,y0), @var{I}(x,y)) ## = exp(-0.5*d([x0,y0],[x,y])^2/@var{sigma_d}^2) ## * exp(-0.5*d(@var{I}(x0,y0),@var{I}(x,y))^2/@var{sigma_r}^2), ## @end example ## with @code{d} being the Euclidian distance function. The two parameters ## @var{sigma_d} and @var{sigma_r} control the amount of smoothing. @var{sigma_d} ## is the size of the spatial smoothing filter, while @var{sigma_r} is the size ## of the range filter. When @var{sigma_r} is large the filter behaves almost ## like the isotropic Gaussian filter with spread @var{sigma_d}, and when it is ## small edges are preserved better. By default @var{sigma_d} is 2, and @var{sigma_r} ## is @math{10/255} for floating points images (with integer images this is ## multiplied with the maximal possible value representable by the integer class). ## ## The image is extrapolated symmetrically before the filtering is performed. ## ## @strong{Perona and Malik} ## ## The image is smoothed using nonlinear isotropic diffusion as described by Perona and ## Malik [1]. The algorithm iteratively updates the image using ## ## @example ## I += lambda * (g(dN).*dN + g(dS).*dS + g(dE).*dE + g(dW).*dW) ## @end example ## ## @noindent ## where @code{dN} is the spatial derivative of the image in the North direction, ## and so forth. The function @var{g} determines the behaviour of the diffusion. ## If @math{g(x) = 1} this is standard isotropic diffusion. ## ## The above update equation is repeated @var{iter} times, which by default is 10 ## times. If the third input argument is a positive scalar, that number of updates ## will be performed. ## ## The update parameter @var{lambda} affects how much smoothing happens in each ## iteration. The algorithm can only be proved stable is @var{lambda} is between ## 0 and 0.25, and by default it is 0.25. If the fourth input argument is given ## this parameter can be changed. ## ## The function @var{g} in the update equation determines the type of the result. ## By default @code{@var{g}(@var{d}) = exp(-(@var{d}./@var{K}).^2)} where @var{K} = 25. ## This choice gives privileges to high-contrast edges over low-contrast ones. ## An alternative is to set @code{@var{g}(@var{d}) = 1./(1 + (@var{d}./@var{K}).^2)}, ## which gives privileges to wide regions over smaller ones. The choice of @var{g} ## can be controlled through the fifth input argument. If it is the string ## @code{"method1"}, the first mentioned function is used, and if it is @var{"method2"} ## the second one is used. The argument can also be a function handle, in which case ## the given function is used. It should be noted that for stability reasons, ## @var{g} should return values between 0 and 1. ## ## The following example shows how to set ## @code{@var{g}(@var{d}) = exp(-(@var{d}./@var{K}).^2)} where @var{K} = 50. ## The update will be repeated 25 times, with @var{lambda} = 0.25. ## ## @example ## @var{g} = @@(@var{d}) exp(-(@var{d}./50).^2); ## @var{J} = imsmooth(@var{I}, "p&m", 25, 0.25, @var{g}); ## @end example ## ## @strong{Custom Gaussian - Custom Gaussian Smoothing} ## ## The image is smoothed using a Gaussian filter with a spatially varying covariance ## matrix. The third and fourth input arguments contain the Eigenvalues of the ## covariance matrix, while the fifth contains the rotation of the Gaussian. ## These arguments can be matrices of the same size as the input image, or scalars. ## In the last case the scalar is used in all pixels. If the rotation is not given ## it defaults to zero. ## ## The following example shows how to increase the size of an Gaussian ## filter, such that it is small near the upper right corner of the image, and ## large near the lower left corner. ## ## @example ## [@var{lambda1}, @var{lambda2}] = meshgrid (linspace (0, 25, columns (@var{I})), linspace (0, 25, rows (@var{I}))); ## @var{J} = imsmooth (@var{I}, "Custom Gaussian", @var{lambda1}, @var{lambda2}); ## @end example ## ## The implementation uses an elliptic filter, where only neighbouring pixels ## with a Mahalanobis' distance to the current pixel that is less than 3 are ## used to compute the response. The response is computed using double precision ## floating points, but the result is of the same class as the input image. ## ## @strong{References} ## ## [1] P. Perona and J. Malik, ## "Scale-space and edge detection using anisotropic diffusion", ## IEEE Transactions on Pattern Analysis and Machine Intelligence, ## 12(7):629-639, 1990. ## ## [2] C. Tomasi and R. Manduchi, ## "Bilateral Filtering for Gray and Color Images", ## Proceedings of the 1998 IEEE International Conference on Computer Vision, Bombay, India. ## ## @seealso{imfilter, fspecial} ## @end deftypefn ## TODO: Implement Joachim Weickert's anisotropic diffusion (it's soo cool) function J = imsmooth(I, name = "Gaussian", varargin) ## Check inputs if (nargin == 0) print_usage(); endif if (! isimage (I)) error("imsmooth: I must be an image"); endif [imrows, imcols, imchannels, tmp] = size(I); if ((imchannels != 1 && imchannels != 3) || tmp != 1) error("imsmooth: first input argument must be an image"); endif if (nargin == 2 && isscalar (name)) varargin {1} = name; name = "Gaussian"; endif if (!ischar(name)) error("imsmooth: second input must be a string"); endif len = length(varargin); ## Save information for later C = class(I); ## Take action depending on 'name' switch (lower(name)) ############################## ### Gaussian smoothing ### ############################## case "gaussian" ## Check input s = 0.5; if (len > 0) if (isscalar(varargin{1}) && varargin{1} > 0) s = varargin{1}; else error("imsmooth: third input argument must be a positive scalar when performing Gaussian smoothing"); endif endif ## Compute filter h = ceil(3*s); f = exp( (-(-h:h).^2)./(2*s^2) ); f /= sum(f); ## Pad image I = double (padarray (I, [h h], "symmetric")); ## Perform the filtering for i = imchannels:-1:1 J(:,:,i) = conv2(f, f, I(:,:,i), "valid"); endfor ############################ ### Square averaging ### ############################ case "average" ## Check input s = [3, 3]; if (len > 0) if (isscalar(varargin{1}) && varargin{1} > 0) s = [varargin{1}, varargin{1}]; elseif (isvector(varargin{1}) && length(varargin{1}) == 2 && all(varargin{1} > 0)) s = varargin{1}; else error("imsmooth: third input argument must be a positive scalar or two-vector when performing averaging"); endif endif ## Compute filter f2 = ones(1,s(1))/s(1); f1 = ones(1,s(2))/s(2); ## Pad image I = padarray (I, floor ( [s(1) s(2)] /2), "symmetric", "pre"); I = padarray (I, floor (([s(1) s(2)]-1)/2), "symmetric", "post"); I = double (I); ## Perform the filtering for i = imchannels:-1:1 J(:,:,i) = conv2 (f1, f2, I(:,:,i), "valid"); endfor ############################## ### Circular averaging ### ############################## case "disk" ## Check input r = 5; if (len > 0) if (isscalar(varargin{1}) && varargin{1} > 0) r = varargin{1}; else error("imsmooth: third input argument must be a positive scalar when performing averaging"); endif endif ## Compute filter f = fspecial("disk", r); ## Pad image i = double (padarray (I, [r r], "symmetric")); ## Perform the filtering for i = imchannels:-1:1 J(:,:,i) = conv2(I(:,:,i), f, "valid"); endfor ############################ ### Median Filtering ### ############################ case "median" ## Check input s = [3, 3]; if (len > 0) opt = varargin{1}; if (isscalar(opt) && opt > 0) s = [opt, opt]; elseif (isvector(opt) && numel(opt) == 2 && all(opt>0)) s = opt; else error("imsmooth: third input argument must be a positive scalar or two-vector"); endif s = round(s); # just in case the use supplies non-integers. endif ## Perform the filtering for i = imchannels:-1:1 J(:,:,i) = medfilt2(I(:,:,i), s, "symmetric"); endfor ############################### ### Bilateral Filtering ### ############################### case "bilateral" ## Check input if (len > 0 && !isempty(varargin{1})) if (isscalar(varargin{1}) && varargin{1} > 0) sigma_d = varargin{1}; else error("imsmooth: spread of closeness function must be a positive scalar"); endif else sigma_d = 2; endif if (len > 1 && !isempty(varargin{2})) if (isscalar(varargin{2}) && varargin{2} > 0) sigma_r = varargin{2}; else error("imsmooth: spread of similarity function must be a positive scalar"); endif else sigma_r = 10/255; if (isinteger(I)), sigma_r *= intmax(C); endif endif ## Pad image s = max([round(3*sigma_d),1]); I = padarray (I, [s s], "symmetric"); ## Perform the filtering J = __bilateral__(I, sigma_d, sigma_r); ############################ ### Perona and Malik ### ############################ case {"perona & malik", "perona and malik", "p&m"} ## Check input K = 25; method1 = @(d) exp(-(d./K).^2); method2 = @(d) 1./(1 + (d./K).^2); method = method1; lambda = 0.25; iter = 10; if (len > 0 && !isempty(varargin{1})) if (isscalar(varargin{1}) && varargin{1} > 0) iter = varargin{1}; else error("imsmooth: number of iterations must be a positive scalar"); endif endif if (len > 1 && !isempty(varargin{2})) if (isscalar(varargin{2}) && varargin{2} > 0) lambda = varargin{2}; else error("imsmooth: fourth input argument must be a scalar when using 'Perona & Malik'"); endif endif if (len > 2 && !isempty(varargin{3})) fail = false; if (ischar(varargin{3})) if (strcmpi(varargin{3}, "method1")) method = method1; elseif (strcmpi(varargin{3}, "method2")) method = method2; else fail = true; endif elseif (strcmp(typeinfo(varargin{3}), "function handle")) method = varargin{3}; else fail = true; endif if (fail) error("imsmooth: fifth input argument must be a function handle or the string 'method1' or 'method2' when using 'Perona & Malik'"); endif endif ## Perform the filtering I = double(I); for i = imchannels:-1:1 J(:,:,i) = pm(I(:,:,i), iter, lambda, method); endfor ##################################### ### Custom Gaussian Smoothing ### ##################################### case "custom gaussian" ## Check input if (length (varargin) < 2) error ("imsmooth: not enough input arguments"); elseif (length (varargin) == 2) varargin {3} = 0; # default theta value endif arg_names = {"third", "fourth", "fifth"}; for k = 1:3 if (isscalar (varargin {k})) varargin {k} = repmat (varargin {k}, imrows, imcols); elseif (isnumeric (varargin {k}) && ismatrix (varargin {k})) if (rows (varargin {k}) != imrows || columns (varargin {k}) != imcols) error (["imsmooth: %s input argument must have same number of rows " "and columns as the input image"], arg_names {k}); endif else error ("imsmooth: %s input argument must be a scalar or a matrix", arg_names {k}); endif if (!strcmp (class (varargin {k}), "double")) error ("imsmooth: %s input argument must be of class 'double'", arg_names {k}); endif endfor ## Perform the smoothing for i = imchannels:-1:1 J(:,:,i) = __custom_gaussian_smoothing__ (I(:,:,i), varargin {:}); endfor ###################################### ### Mean Shift Based Smoothing ### ###################################### # NOT YET IMPLEMENTED #case "mean shift" # J = mean_shift(I, varargin{:}); ############################# ### Unknown filtering ### ############################# otherwise error("imsmooth: unsupported smoothing type '%s'", name); endswitch ## Cast the result to the same class as the input J = cast(J, C); endfunction ## Perona and Malik for gray-scale images function J = pm(I, iter, lambda, g) ## Initialisation [imrows, imcols] = size(I); J = I; for i = 1:iter ## Pad image padded = padarray (J, [1 1], "circular"); ## Spatial derivatives dN = padded(1:imrows, 2:imcols+1) - J; dS = padded(3:imrows+2, 2:imcols+1) - J; dE = padded(2:imrows+1, 3:imcols+2) - J; dW = padded(2:imrows+1, 1:imcols) - J; gN = g(dN); gS = g(dS); gE = g(dE); gW = g(dW); ## Update J += lambda*(gN.*dN + gS.*dS + gE.*dE + gW.*dW); endfor endfunction ## Mean Shift smoothing for gray-scale images ## XXX: This function doesn't work!! #{ function J = mean_shift(I, s1, s2) sz = [size(I,2), size(I,1)]; ## Mean Shift [x, y] = meshgrid(1:sz(1), 1:sz(2)); f = ones(s1); tmp = conv2(ones(sz(2), sz(1)), f, "same"); # We use normalised convolution to handle the border m00 = conv2(I, f, "same")./tmp; m10 = conv2(I.*x, f, "same")./tmp; m01 = conv2(I.*y, f, "same")./tmp; ms_x = round( m10./m00 ); # konverter ms_x og ms_y til linære indices og arbejd med dem! ms_y = round( m01./m00 ); ms = sub2ind(sz, ms_y, ms_x); %for i = 1:10 i = 0; while (true) disp(++i) ms(ms) = ms; #new_ms = ms(ms); if (i >200), break; endif #idx = ( abs(I(ms)-I(new_ms)) < s2 ); #ms(idx) = new_ms(idx); %for j = 1:length(ms) % if (abs(I(ms(j))-I(ms(ms(j)))) < s2) % ms(j) = ms(ms(j)); % endif %endfor endwhile %endfor ## Compute result J = I(ms); endfunction #} %!test %! ## checking Bilateral Filter %! %! ## constant image remain the same after Bilateral Filter %! A = uint8(255*ones(128,128)); %! B = uint8(imsmooth(A, 'Bilateral', 2, 10)); %! assert (A,B); %! %! ## Bilateral Filter does not smear outlayers %! A = zeros(256,256); %! A(128,128) = 256; %! ## bilateral filter does not smear outlayers %! B = imsmooth(A, 'Bilateral', 2, 10); %! assert (A,B,1.e-140); %! %! ## When sigma_r is large the filter behaves almost %! ## like the isotropic Gaussian filter %! %! A0 = fspecial ('gaussian',100,100); %! A = uint8(A0/max(max(A0))*255); %! B1 = imsmooth(A, 'Bilateral', 2, 100); %! B2 = imsmooth(A, 'Gaussian', 2); %! assert (B1,B2); image-2.16.1/inst/PaxHeaders.61586/label2rgb.m0000644000000000000000000000006215005110255015414 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/label2rgb.m0000644000175000017500000001431315005110255017014 0ustar00avinoamavinoam00000000000000## Copyright (C) 2006 Søren Hauberg ## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} label2rgb (@var{L}) ## @deftypefnx{Function File} {} label2rgb (@var{L}, @var{cmap}) ## @deftypefnx{Function File} {} label2rgb (@var{L}, @var{cmap}, @var{background}) ## @deftypefnx{Function File} {} label2rgb (@var{L}, @var{cmap}, @var{background}, @var{order}) ## Convert labeled image into RGB. ## ## The labeled image @var{L} is converted into an RGB image using the ## colormap @var{cmap}. The label number of each region is used to select ## the color from @var{cmap} which can be specified as: ## ## @itemize @bullet ## @item @var{N}-by-3 colormap matrix where N must be larger than or equal ## to the highest label number; ## @item name of a function that returns a colormap; ## @item handle for a function that returns a colormap (defaults to @code{jet}). ## @end itemize ## ## In a labeled image, zero valued pixels are considered background and ## are colored according to the color @var{background}. It can be specified ## as an RGB triplet values (3 element vector of values between 0 and 1), or ## by name: ## ## @itemize @bullet ## @item @qcode{"w"} or @qcode{"white"} (default) ## @item @qcode{"b"} or @qcode{"blue"}. ## @item @qcode{"c"} or @qcode{"cyan"}. ## @item @qcode{"g"} or @qcode{"green"}. ## @item @qcode{"k"} or @qcode{"black"}. ## @item @qcode{"m"} or @qcode{"magenta"}. ## @item @qcode{"r"} or @qcode{"red"}. ## @item @qcode{"y"} or @qcode{"yellow"}. ## @end itemize ## ## The option @var{order} must be a string with values @qcode{"shuffle"} or ## @qcode{"noshuffle"} (default). If shuffled, the colors in @var{cmap} are ## permuted randomly before the image conversion. ## ## The output RGB image is always of class uint8. ## ## @seealso{bwconncomp, bwlabel, colormap, ind2rgb} ## @end deftypefn function rgb = label2rgb (L, cmap = @jet, background = "w", order = "noshuffle") if (nargin < 1 || nargin > 4) print_usage (); elseif (! isimage (L) || ndims (L) > 4 || size (L, 3) != 1 || any (L(:) != fix (L(:))) || any (L(:) < 0)) error ("label2rgb: L must be a labelled image"); elseif (! ischar (cmap) && ! isa (cmap, "function_handle") && ! iscolormap (cmap)) error ("label2rgb: CMAP must be a colormap, colormap name, or colormap function"); elseif (! ischar (background) && ! iscolormap (background)) error("label2rgb: BACKGROUND must be a colorname or a RGB triplet"); elseif (! any (strcmpi (order, {"noshuffle", "shuffle"}))) error("label2rgb: ORDER must be either 'noshuffle' or 'shuffle'"); endif ## Convert map to a matrix if needed num_objects = max (L(:)); if (ischar (cmap) || isa (cmap, "function_handle")) ## cast to double because of bug #44070 cmap = feval (cmap, double (num_objects)); endif num_colors = rows (cmap); if (num_objects > num_colors) error ("label2rgb: CMAP has not enough colors (%i) for all objects (%i) in L", num_colors, num_objects); endif background = handle_colorspec ("label2rgb", background); ## Should we shuffle the colormap? if (strcmpi (order, "shuffle")) ## Matlab does the shuffling "pseudorandomly". We don't know how it ## actually does the shuffling since it is not documented but using ## the same labeled image and colormap, Matlab always returns the same. cmap = cmap(randperm (num_colors), :); endif ## Check if the background color is in the colormap idx = find (ismember (cmap, background, "rows")); if (! isempty (idx)) if (isscalar (idx)) warning ("label2rgb: region %i has the same color as background", idx); else idx_list = sprintf ("%i, ", idx(1:end-1)); idx_list = sprintf ("%s, and %i", idx_list, idx(end)); warning ("label2rgb: regions %s, have the same color as background", idx_list); endif endif ## We will use ind2rgb for the conversion. An indexed image is interpreted ## differently depending if it's an integer or floating point image. We make ## sure we pass an integer image where value of zero is the color in the ## first row of the colormap (if it was a floating point image, the image ## could not have zero values, and a value of 1 is the color in the first ## row of the colormap). if (! isinteger (L)) if (num_objects <= intmax ("uint8")), L = uint8 (L); elseif (num_objects <= intmax ("uint16")), L = uint16 (L); elseif (num_objects <= intmax ("uint32")), L = uint32 (L); else, L = uint64 (L); endif endif ## Insert the background color at the head of the colormap rgb = ind2rgb (L, [background; cmap]); rgb = im2uint8 (rgb); endfunction %!function map = test_colormap (unused) %! map = [0 0 0; 0.5 0.5 0.5; 0.125 0.125 0.125]; %!endfunction %!shared in, out, cmap %! in = [ 0 1 1 0 2 2 0 3 3 %! 0 1 1 0 2 2 0 3 3]; %! %! out = [255 0 0 255 128 128 255 32 32 %! 255 0 0 255 128 128 255 32 32]; %! out(:,:,2) = out(:,:,3) = out(:,:,1); %! out = uint8(out); %! %! cmap = [0 0 0; 0.5 0.5 0.5; 0.125 0.125 0.125]; %!assert (label2rgb (in, cmap), out); %!assert (label2rgb (uint8 (in), cmap), out); %!assert (label2rgb (in, "test_colormap"), out); %!assert (label2rgb (in, @test_colormap), out); %! %! out(find (in == 0)) = 0; %!assert (label2rgb (in, cmap, "cyan"), out); %!assert (label2rgb (in, cmap, [0 1 1]), out); %! %! in(1) = 10; %!error label2rgb (in, cmap); %!error label2rgb (in, cmap, 89); %!error label2rgb (in, cmap, "g", "wrong"); image-2.16.1/inst/PaxHeaders.61586/analyze75info.m0000644000000000000000000000006215005110255016253 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/analyze75info.m0000644000175000017500000001441515005110255017656 0ustar00avinoamavinoam00000000000000%% Copyright (C) 2012 Adam H Aitkenhead %% Copyright (C) 2012 Carnë Draug %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; either version 3 of the License, or %% (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; If not, see . %% -*- texinfo -*- %% @deftypefn {Function File} {@var{header} =} analyze75info (@var{filename}) %% @deftypefnx {Function File} {@var{header} =} analyze75info (@var{filename}, "ByteOrder", @var{arch}) %% Read header of an Analyze 7.5 file. %% %% @var{filename} must be the path for an Analyze 7.5 file or the path for a %% directory with a single .hdr file can be specified. %% %% The optional argument @code{"ByteOrder"} reads the file with the specified %% @var{arch} ("ieee-be" or "ieee-le" for IEEE big endian or IEEE %% little endian respectively). %% %% @var{header} is a structure with the file information. %% %% @seealso{analyze75read, analyze75write} %% @end deftypefn function header = analyze75info (filename, varargin) if (nargin ~= 1 && nargin ~= 3) print_usage; elseif (~ischar (filename)) error ('analyze75info: `filename'' must be a string'); elseif (nargin == 3) %% Check the byteorder if (strcmpi (varargin{1}, 'byteorder')) error ('analyze75info: second argument must be string `ByteOrder'''); elseif (!all (strcmpi (varargin{3}, {'ieee-le', 'l', 'ieee-be', 'b'}))) error ('analyze75info: valid options for `ByteOrder'' are `ieee-le'' or `ieee-be'''); elseif (any (strcmpi (varargin{3}, {'ieee-be', 'b'}))) warning ('analyze75info: no support for big-endian. Please consider submitting a patch. Attempting to read as little-endian'); end end fileprefix = analyze75filename (filename); %% finally start reading the actual file hdrdirdata = dir ([fileprefix, '.hdr']); imgdirdata = dir ([fileprefix, '.img']); header.ImgFileSize = imgdirdata.bytes; header.Filename = [fileprefix, '.hdr']; header.FileModDate = hdrdirdata.date; header.Format = 'Analyze'; header.FormatVersion = '7.5'; header.ColorType = 'grayscale'; header.ByteOrder = 'ieee-le'; fidH = fopen ([fileprefix, '.hdr']); header.HdrFileSize = fread (fidH, 1, 'int32'); header.HdrDataType = char (fread (fidH, 10, 'char'))'; header.DatabaseName = char (fread (fidH, 18, 'char'))'; header.Extents = fread (fidH, 1, 'int32'); header.SessionError = fread (fidH, 1, 'int16'); header.Regular = char (fread (fidH, 1, 'char')); unused = char (fread (fidH, 1, 'char')); unused = fread (fidH, 1, 'int16'); header.Dimensions = zeros (size (1, 4)); header.Dimensions(1) = fread (fidH, 1, 'int16'); header.Dimensions(2) = fread (fidH, 1, 'int16'); header.Dimensions(3) = fread (fidH, 1, 'int16'); header.Dimensions(4) = fread (fidH, 1, 'int16'); unused = fread (fidH, 3, 'int16'); header.VoxelUnits = char (fread (fidH, 4, 'char'))'; header.CalibrationUnits = char (fread (fidH, 8, 'char'))'; unused = fread (fidH, 1, 'int16'); datatype = fread (fidH, 1, 'int16'); switch datatype case 0, header.ImgDataType = 'DT_UNKNOWN'; case 1, header.ImgDataType = 'DT_BINARY'; case 2, header.ImgDataType = 'DT_UNSIGNED_CHAR'; case 4, header.ImgDataType = 'DT_SIGNED_SHORT'; case 8, header.ImgDataType = 'DT_SIGNED_INT'; case 16, header.ImgDataType = 'DT_FLOAT'; case 32, header.ImgDataType = 'DT_COMPLEX'; case 64, header.ImgDataType = 'DT_DOUBLE'; case 128, header.ImgDataType = 'DT_RGB'; case 255, header.ImgDataType = 'DT_ALL'; otherwise, warning ('analyze75: unable to detect ImgDataType'); end header.BitDepth = fread (fidH, 1, 'int16'); unused = fread (fidH, 1, 'int16'); unused = fread (fidH, 1, 'float'); header.PixelDimensions = zeros (1, 3); header.PixelDimensions(1) = fread (fidH, 1, 'float'); header.PixelDimensions(2) = fread (fidH, 1, 'float'); header.PixelDimensions(3) = fread (fidH, 1, 'float'); unused = fread (fidH, 4, 'float'); header.VoxelOffset = fread (fidH, 1, 'float'); unused = fread (fidH, 3, 'float'); header.CalibrationMax = fread (fidH, 1, 'float'); header.CalibrationMin = fread (fidH, 1, 'float'); header.Compressed = fread (fidH, 1, 'float'); header.Verified = fread (fidH, 1, 'float'); header.GlobalMax = fread (fidH, 1, 'int32'); header.GlobalMin = fread (fidH, 1, 'int32'); header.Descriptor = char (fread (fidH, 80, 'char'))'; header.AuxFile = char (fread (fidH, 24, 'char'))'; header.Orientation = char (fread (fidH, 1, 'char'))'; header.Originator = char (fread (fidH, 10, 'char'))'; header.Generated = char (fread (fidH, 10, 'char'))'; header.Scannumber = char (fread (fidH, 10, 'char'))'; header.PatientID = char (fread (fidH, 10, 'char'))'; header.ExposureDate = char (fread (fidH, 10, 'char'))'; header.ExposureTime = char (fread (fidH, 10, 'char'))'; unused = char (fread (fidH, 3, 'char'))'; header.Views = fread (fidH, 1, 'int32'); header.VolumesAdded = fread (fidH, 1, 'int32'); header.StartField = fread (fidH, 1, 'int32'); header.FieldSkip = fread (fidH, 1, 'int32'); header.OMax = fread (fidH, 1, 'int32'); header.OMin = fread (fidH, 1, 'int32'); header.SMax = fread (fidH, 1, 'int32'); header.SMin = fread (fidH, 1, 'int32'); header.Width = header.Dimensions(1); header.Height = header.Dimensions(2); fclose(fidH); end image-2.16.1/inst/PaxHeaders.61586/bwperim.m0000644000000000000000000000006215005110255015225 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/bwperim.m0000644000175000017500000002300015005110255016616 0ustar00avinoamavinoam00000000000000## Copyright (C) 2013 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} bwperim (@var{bw}) ## @deftypefnx {Function File} {} bwperim (@var{bw}, @var{conn}) ## Find perimeter of objects in binary images. ## ## Values from the matrix @var{bw} are considered part of an object perimeter ## if their value is non-zero and is connected to at least one zero-valued ## element, or to the outside of @var{bw}. ## ## Element connectivity @var{conn}, to define the size of objects, can be ## specified with a numeric scalar (number of elements in the neighborhood): ## ## @table @samp ## @item 4 or 8 ## for 2 dimensional matrices; ## @item 6, 18 or 26 ## for 3 dimensional matrices; ## @end table ## ## or with a binary matrix representing a connectivity array. Defaults to ## @code{conndef (ndims (@var{bw}), "minimal")} which is equivalent to ## @var{conn} of 4 and 6 for 2 and 3 dimensional matrices respectively. ## ## @seealso{bwarea, bwboundaries, imerode, mmgrad} ## @end deftypefn function varargout = bwperim (bw, conn) if (nargin < 1 || nargin > 2) print_usage (); endif if (! isnumeric (bw) && ! islogical (bw)) error("bwperim: BW must be a numeric or logical matrix"); endif bw = logical (bw); if (nargin < 2) conn = conndef (ndims (bw), "minimal"); else conn = conndef (conn); endif ## Recover the elements that would get removed by erosion perim = (! imerode (bw, conn)) & bw; ## Get the borders back which are removed during erosion ## FIXME this is a bit too convoluted and not elegant at all. I am also ## unsure if it is correct for N dimensional stuff and unusual ## connectivities. We should probably be using the output from ## bwboundaries() but bwboundaries() seems buggy in the case of ## holes. tmp_idx = repmat ({":"}, [1 ndims(perim)]); tmp_conn_idx = repmat ({":"}, [1 ndims(conn)]); p_size = size (perim); for dim = 1:min (ndims (perim), ndims (conn)) conn_idx = tmp_conn_idx; conn_idx{dim} = [1 3]; if (! any (conn(conn_idx{:})(:))) continue endif idx = tmp_idx; idx{dim} = [1 p_size(dim)]; perim(idx{:}) = bw(idx{:}); endfor if (nargout > 0) varargout{1} = perim; else imshow (perim); endif endfunction %!test %! in = [ 1 1 1 1 0 1 1 0 1 1 %! 1 1 0 1 1 1 1 1 1 0 %! 1 1 1 0 1 1 1 1 1 1 %! 1 1 1 1 0 1 1 1 0 1 %! 1 1 1 0 1 1 1 1 1 0 %! 1 1 1 1 1 1 0 1 0 1 %! 1 1 1 1 1 1 1 1 1 0 %! 1 1 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 0 0 1 1 %! 1 1 1 1 0 1 0 1 1 0]; %! %! out = [1 1 1 1 0 1 1 0 1 1 %! 1 1 0 1 1 0 0 1 1 0 %! 1 0 1 0 1 0 0 0 1 1 %! 1 0 0 1 0 1 0 1 0 1 %! 1 0 1 0 1 0 1 0 1 0 %! 1 0 0 1 0 1 0 1 0 1 %! 1 0 0 0 0 0 1 0 1 0 %! 1 0 0 0 0 0 1 1 0 1 %! 1 0 0 0 1 1 0 0 1 1 %! 1 1 1 1 0 1 0 1 1 0]; %! assert (bwperim (in), logical (out)) %! assert (bwperim (in, 4), logical (out)) %! %! out = [1 1 1 1 0 1 1 0 1 1 %! 1 1 0 1 1 1 1 1 1 0 %! 1 1 1 0 1 1 0 1 1 1 %! 1 0 1 1 0 1 0 1 0 1 %! 1 0 1 0 1 1 1 1 1 0 %! 1 0 1 1 1 1 0 1 0 1 %! 1 0 0 0 0 1 1 1 1 0 %! 1 0 0 0 0 1 1 1 1 1 %! 1 0 0 1 1 1 0 0 1 1 %! 1 1 1 1 0 1 0 1 1 0]; %! assert (bwperim (in, 8), logical (out)) %! %! out = [1 1 1 1 0 1 1 0 1 1 %! 1 0 0 0 0 1 0 0 1 0 %! 1 0 0 0 0 0 0 1 0 1 %! 1 0 1 0 0 0 0 0 0 1 %! 1 0 0 0 0 1 0 1 0 0 %! 1 0 0 0 1 0 0 0 0 1 %! 1 0 0 0 0 0 0 1 0 0 %! 1 0 0 0 0 1 1 0 0 1 %! 1 0 0 1 0 1 0 0 1 1 %! 1 1 1 1 0 1 0 1 1 0]; %! assert (bwperim (in, [1 0 0; 0 1 0; 0 0 1]), logical (out)) ## test that any non-zero value is valid (even i and Inf) %!test %! in = [ 0 0 0 0 0 0 0 %! 0 0 5 0 0 1 9 %! 0 Inf 9 7 0 0 0 %! 0 1.5 5 7 1 0 0 %! 0 0.5 -1 89 i 0 0 %! 0 4 10 15 1 0 0 %! 0 0 0 0 0 0 0]; %! out = [0 0 0 0 0 0 0 %! 0 0 1 0 0 1 1 %! 0 1 0 1 0 0 0 %! 0 1 0 0 1 0 0 %! 0 1 0 0 1 0 0 %! 0 1 1 1 1 0 0 %! 0 0 0 0 0 0 0]; %! assert (bwperim (in), logical (out)) ## test for 3D %!test %! in = reshape (magic(16), [8 8 4]) > 50; %! out(:,:,1) = [ %! 1 1 0 1 0 1 1 1 %! 0 1 1 1 1 1 0 1 %! 0 1 1 1 1 1 0 1 %! 1 1 0 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 %! 1 1 1 0 1 0 1 1 %! 1 1 1 0 1 0 1 1 %! 1 0 1 1 1 1 1 0]; %! out(:,:,2) = [ %! 1 1 0 1 0 1 1 1 %! 0 1 1 0 1 1 0 1 %! 0 1 0 0 0 1 0 1 %! 1 0 1 0 0 0 1 1 %! 1 0 0 1 0 1 0 1 %! 1 0 1 0 1 0 1 1 %! 1 1 1 0 1 0 1 1 %! 1 0 1 1 1 1 1 0]; %! out(:,:,3) = [ %! 1 1 0 1 0 1 1 1 %! 0 1 1 0 1 1 0 1 %! 0 1 0 0 0 1 0 1 %! 1 0 0 0 0 0 1 1 %! 1 0 0 1 0 1 0 1 %! 1 0 1 0 1 0 1 1 %! 1 1 1 0 1 0 1 1 %! 1 0 1 1 1 1 1 0]; %! out(:,:,4) = [ %! 1 1 0 1 0 1 1 1 %! 0 1 1 1 1 1 0 1 %! 0 1 1 1 1 1 0 1 %! 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 0 %! 1 1 1 0 1 0 1 1 %! 1 1 1 0 1 0 1 1 %! 1 0 1 1 1 1 1 0]; %! assert (bwperim (in), logical (out)) %! %! out(:,:,1) = [ %! 1 1 0 1 0 1 1 1 %! 0 1 1 1 1 1 0 1 %! 0 1 1 1 1 1 0 1 %! 1 1 0 1 1 1 1 1 %! 1 1 1 1 1 1 1 1 %! 1 1 1 0 1 0 1 1 %! 1 1 1 0 1 0 1 1 %! 1 0 1 1 1 1 1 0]; %! out(:,:,2) = [ %! 1 1 0 1 0 1 1 1 %! 0 1 1 1 1 1 0 1 %! 0 1 1 0 0 1 0 1 %! 1 1 1 1 0 1 1 1 %! 1 0 1 1 1 1 1 1 %! 1 0 1 0 1 0 1 1 %! 1 1 1 0 1 0 1 1 %! 1 0 1 1 1 1 1 0]; %! out(:,:,3) = [ %! 1 1 0 1 0 1 1 1 %! 0 1 1 1 1 1 0 1 %! 0 1 0 0 0 1 0 1 %! 1 1 0 0 0 1 1 1 %! 1 0 1 1 1 1 1 1 %! 1 0 1 0 1 0 1 1 %! 1 1 1 0 1 0 1 1 %! 1 0 1 1 1 1 1 0]; %! out(:,:,4) = [ %! 1 1 0 1 0 1 1 1 %! 0 1 1 1 1 1 0 1 %! 0 1 1 1 1 1 0 1 %! 1 1 1 1 1 1 1 1 %! 1 1 1 1 1 1 1 0 %! 1 1 1 0 1 0 1 1 %! 1 1 1 0 1 0 1 1 %! 1 0 1 1 1 1 1 0]; %! assert (bwperim (in, 18), logical (out)) %!error bwperim ("text") %!error bwperim (rand (10), 5) %!error bwperim (rand (10), "text") %!test %! a = false (5); %! a(1:4,2:4) = true; %! %! p = false (5); %! p(1:4,[2 4]) = true; %! assert (bwperim (a, [0 0 0; 1 1 1; 0 0 0]), p) ## This is not a bug. Since connectivity defaults to maximum for the ## number of dimensions, a single slice will be displayed completely ## (this is obvious but very easy to forget) %!test %! a = false (8, 8, 5); %! a(4:5,4:5,2:4) = true; %! a(2:7,2:7,3) = true; %! assert (bwperim (a, 26), a) %! %! ## It is easy to forget that is correct %! b = a; %! b(4:5, 4:5, 3) = false; %! assert (bwperim (a), b) %! %! c = a; %! c(3:6,3:6,3) = false; %! assert (bwperim (a, 4), c) ## test dimensions of length 1 (1x1, Nx1, etc) (bug #50153) %!test %! conn_self = logical ([0 0 0; 0 1 0; 0 0 0]); %! assert (bwperim (true), true) %! assert (bwperim (true, conn_self), false) %! assert (bwperim (true (1, 6)), true (1, 6)) %! assert (bwperim (true (1, 6), conn_self), false (1, 6)) %! assert (bwperim (true (6, 1)), true (6, 1)) %! %! bw_3d = true (1, 1, 6); %! assert (bwperim (bw_3d), bw_3d) %! assert (bwperim (bw_3d, conn_self), false (1, 1, 6)) %! assert (bwperim (bw_3d, true (3)), bw_3d) %! %! perim_3d = bw_3d; %! perim_3d(1, 1, 2:end-1) = false; %! conn_3d = false (3, 3, 3); %! conn_3d(2, 2, :) = true; %! assert (bwperim (true (1, 1, 6), conn_3d), perim_3d) image-2.16.1/inst/PaxHeaders.61586/imattributes.m0000644000000000000000000000006215005110255016274 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imattributes.m0000644000175000017500000001214715005110255017677 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Carnë Draug ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as ## published by the Free Software Foundation; either version 3 of the ## License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} imattributes () ## @deftypefnx {Function File} {} imattributes (@var{himage}) ## Get information about image attributes. ## ## Return attributes for the image in the current figure or in the image ## handle @var{himage}. Returns a struct with the fields: ## ## @table @asis ## @item @qcode{"Width"} ## Number of columns. ## ## @item @qcode{"Height"} ## Number of rows. ## ## @item @qcode{"Class"} ## Note that some classes are converted to double for display. ## ## @item @qcode{"Image type"} ## One of @qcode{"binary"}, @qcode{"truecolor"}, @qcode{"intensity"}, or ## @qcode{"indexed"}. ## ## @item @qcode{"Minimum intensity"} ## @itemx @qcode{"Maximum intensity"} ## These values are not returned for images of type @qcode{"truecolor"} ## and @qcode{"binary"}. ## ## For indexed images, the returned values are the lowest and highest index ## for the colormap, @emph{not} the used index for the lowest or highest ## intensity or their values. This weird behaviour is kept for Matlab ## compatibility. ## @end table ## ## This function is meant to be used in an interactive session, and not ## programatically. The properties of an image should be measured from the ## image variable itself not from the figure object. In addition this ## function is purposely Matlab incompatible on their return value which ## returns a cell array of strings which is only useful for display. ## ## @end deftypefn function attr = imattributes (imgh = gcf ()) if (nargin > 1) print_usage (); elseif (isa (imgh, "imagemodel")) ## FIXME we don't even have a imagemodel class yet but when we do, this ## is already here error ("imattributes: support for imagemodel objects not yet implemented"); endif while (! isempty (get (imgh, "children"))) imgh = get (imgh, "children"); endwhile cdata = get (imgh, "cdata"); cdatamapping = get (imgh, "cdatamapping"); if (isbool (cdata)) img_type = "binary"; elseif (ndims (cdata) == 3) img_type = "truecolor"; elseif (strcmpi (cdatamapping, "direct")) img_type = "indexed"; else img_type = "intensity"; endif ## Implementation note: this function returns a struct while Matlab returns ## a cell array of strings (even for the numeric values). It is completely ## useless in programs, so I can only assume it is meant to be used ## interactively. If so, a cell array is useless for us because Octave does ## not display cell arrays columns aligned but a struct looks good. attr = struct ( "Width (columns)", columns (cdata), "Height (rows)", rows (cdata), "Class", class (cdata), "Image type", img_type ); ## Matlab compatibility: for indexed images, we still give the lowest ## and highest index to the colormap. if (! any (strcmp (img_type, {"binary", "truecolor"}))) attr = setfield (attr, "Minimum intensity", min (cdata(:))); attr = setfield (attr, "Maximum intensity", max (cdata(:))); endif endfunction %!shared x, map, img, rgb, bw %! [x, map] = imread ("default.img"); %! rgb = ind2rgb (x, map); %! img = ind2gray (x, map); %! bw = im2bw (img); %!test %! h = imshow (img); %! a = imattributes (h); %! assert ([a.("Height (rows)") a.("Width (columns)")], [53 40]); %! assert (a.Class, "uint8"); %! assert (a.("Image type"), "intensity"); %! assert (a.("Minimum intensity"), uint8 (28)); %! assert (a.("Maximum intensity"), uint8 (250)); ## FIXME this is a bug upstream, the original class is not always preserved %!xtest %! h = imshow (rgb); %! a = imattributes (h); %! assert ([a.("Height (rows)") a.("Width (columns)")], [53 40]); %! assert (a.Class, "uint8"); %! assert (a.("Image type"), "truecolor"); %! assert (isfield (a, "Minimum intensity"), false); %! assert (isfield (a, "Maximum intensity"), false); %!test %! h = imshow (bw); %! a = imattributes (h); %! assert ([a.("Height (rows)") a.("Width (columns)")], [53 40]); %! assert (a.Class, "logical"); %! assert (a.("Image type"), "binary"); %! assert (isfield (a, "Minimum intensity"), false); %! assert (isfield (a, "Maximum intensity"), false); %!test %! h = imshow (x, map); %! a = imattributes (h); %! assert ([a.("Height (rows)") a.("Width (columns)")], [53 40]); %! assert (a.Class, "uint8"); %! assert (a.("Image type"), "indexed"); %! assert (a.("Minimum intensity"), uint8 (0)); %! assert (a.("Maximum intensity"), uint8 (55)); %!test %! h = imshow (img); %! a1 = imattributes (); %! a2 = imattributes (h); %! assert (a1, a2); image-2.16.1/inst/PaxHeaders.61586/imnoise.m0000644000000000000000000000006215005110255015223 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imnoise.m0000644000175000017500000001233115005110255016621 0ustar00avinoamavinoam00000000000000## Copyright (C) 2000 Paul Kienzle ## Copyright (C) 2004 Stefan van der Walt ## Copyright (C) 2012 Carlo de Falco ## Copyright (C) 2012 Carnë Draug ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} imnoise (@var{A}, @var{type}) ## @deftypefnx {Function File} {} imnoise (@dots{}, @var{options}) ## Add noise to image. ## ## @end deftypefn ## @deftypefn {Function File} {} imnoise (@var{A}, "gaussian", @var{mean}, @var{variance}) ## Additive gaussian noise with @var{mean} and @var{variance} defaulting to 0 ## and 0.01. ## ## @end deftypefn ## @deftypefn {Function File} {} imnoise (@var{A}, "poisson") ## Creates poisson noise in the image using the intensity value of each pixel as ## mean. ## ## @end deftypefn ## @deftypefn {Function File} {} imnoise (@var{A}, "salt & pepper", @var{density}) ## Create "salt and pepper"/"lost pixels" in @var{density}*100 percent of the ## image. @var{density} defaults to 0.05. ## ## @end deftypefn ## @deftypefn {Function File} {} imnoise (@var{A}, "speckle", @var{variance}) ## Multiplicative gaussian noise with @var{B} = @var{A} + @var{A} * noise with ## mean 0 and @var{variance} defaulting to 0.04. ## ## @seealso{rand, randn, randp} ## @end deftypefn function A = imnoise (A, stype, a, b) ## we do not set defaults right at the start because they are different ## depending on the method used to generate noise if (nargin < 2 || nargin > 4) print_usage; elseif (! isimage (A)) error ("imnoise: first argument must be an image."); elseif (! ischar (stype)) error ("imnoise: second argument must be a string with name of noise type."); endif in_class = class (A); fix_class = false; # for cases when we need to use im2double switch (lower (stype)) case "poisson" switch (in_class) case ("double") A = randp (A * 1e12) / 1e12; case ("single") A = single (randp (A * 1e6) / 1e6); case {"uint8", "uint16"} A = cast (randp (A), in_class); otherwise A = imnoise (im2double (A), "poisson"); fix_class = true; endswitch case "gaussian" A = im2double (A); fix_class = true; if (nargin < 3), a = 0.00; endif if (nargin < 4), b = 0.01; endif A = A + (a + randn (size (A)) * sqrt (b)); ## Variance of Gaussian data with mean 0 is E[X^2] case {"salt & pepper", "salt and pepper"} if (nargin < 3), a = 0.05; endif noise = rand (size (A)); if (isfloat (A)) black = 0; white = 1; else black = intmin (in_class); white = intmax (in_class); endif A(noise <= a/2) = black; A(noise >= 1-a/2) = white; case "speckle" A = im2double (A); fix_class = true; if (nargin < 3), a = 0.04; endif A = A .* (1 + randn (size (A)) * sqrt (a)); otherwise error ("imnoise: unknown or unimplemented type of noise `%s'", stype); endswitch if (fix_class) A = imcast (A, in_class); elseif (isfloat (A)) ## this includes not even cases where the noise made it go outside of the ## [0 1] range, but also images that were already originally outside that ## range. This is by design and matlab compatibility. And we do this after ## fixing class because the imcast functions already take care of such ## adjustment A(A < 0) = cast (0, class (A)); A(A > 1) = cast (1, class (A)); endif endfunction %!assert(var(imnoise(ones(10)/2,'gaussian')(:)),0.01,0.005) # probabilistic %!assert(length(find(imnoise(ones(10)/2,'salt & pepper')~=0.5)),5,10) # probabilistic %!assert(var(imnoise(ones(10)/2,'speckle')(:)),0.01,0.005) # probabilistic %!test %! A = imnoise (.5 * ones (100), 'poisson'); %! assert (class (A), 'double') %!test %! A = imnoise (.5 * ones (100, 'single'), 'poisson'); %! assert (class (A), 'single') %!test %! A = imnoise (128 * ones (100, 'uint8'), 'poisson'); %! assert (class (A), 'uint8') %!test %! A = imnoise (256 * ones (100, 'uint16'), 'poisson'); %! assert (class (A), 'uint16') %!demo %! A = imnoise (2^7 * ones (100, 'uint8'), 'poisson'); %! subplot (2, 2, 1) %! imshow (A) %! title ('uint8 image with poisson noise') %! A = imnoise (2^15 * ones (100, 'uint16'), 'poisson'); %! subplot (2, 2, 2) %! imshow (A) %! title ('uint16 image with poisson noise') %! A = imnoise (.5 * ones (100), 'poisson'); %! subplot (2, 2, 3) %! imshow (A) %! title ('double image with poisson noise') %! A = imnoise (.5 * ones (100, 'single'), 'poisson'); %! subplot (2, 2, 4) %! imshow (A) %! title ('single image with poisson noise') image-2.16.1/inst/PaxHeaders.61586/lab2single.m0000644000000000000000000000006215005110255015602 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/lab2single.m0000644000175000017500000000765615005110255017216 0ustar00avinoamavinoam00000000000000## Copyright (C) 2016 Carnë Draug ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} lab2double (@var{lab}) ## Convert L*a*b* data to single precision. ## ## @var{lab} must be a L*a*b* image or colormap, i.e., its dimensions ## must be MxNx3xK or Mx3. Its type must be double, single, uint16, ## or uint8. ## ## When converted to single, L* values range from 0 to 100, while a* and ## b* range from -128 to 127. When converting from uint16, the upper limit ## is 65280 (higher values will be converted above the range). ## ## @seealso{lab2double, lab2rgb, lab2single, lab2uint8, lab2uin16, lab2xyz} ## @end deftypefn function [lab] = lab2single (lab) if (nargin () != 1) print_usage (); endif lab = lab2cls (lab, "single"); endfunction ## Instead of testing the lab2single function here, we test the ## conversion from single type. The actual tests for lab2single, ## are spread all other lab2* functions. This makes the tests ## simpler. %!test %! l_max_f = 100 + (25500 / 65280); %! ab_max_f = 127 + (255 / 256); %! cm = [ %! -Inf %! Inf %! NaN %! l_max_f %! ab_max_f %! -200 %! -129 %! -128 %! -128+(255/65280)*(0.499) %! -128+(255/65280)*(0.500) %! -128+(255/65280)*(0.501) %! -127 %! -1 %! 0 %! (100/65280)*(0.499999) %! (100/65280)*(0.51) %! (100/65280)*(0.500001) %! 1 %! 99 %! 100 %! 101 %! 126 %! 127 %! 128 %! 254 %! 255 %! 256 %! 257]; %! cm = repmat (single (cm), [1 3]); %! im2d = reshape (cm, [7 4 3]); %! imnd = permute (im2d, [1 4 3 2]); %! %! cm_uint8 = uint8 ([ %! 0 0 0 %! 255 255 255 %! 255 255 255 %! 255 228 228 %! 255 255 255 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 1 1 %! 0 127 127 %! 0 128 128 %! 0 128 128 %! 0 128 128 %! 0 128 128 %! 3 129 129 %! 252 227 227 %! 255 228 228 %! 255 229 229 %! 255 254 254 %! 255 255 255 %! 255 255 255 %! 255 255 255 %! 255 255 255 %! 255 255 255 %! 255 255 255]); %! %! assert (lab2uint8 (cm), cm_uint8) %! im2d_uint8 = reshape (cm_uint8, [7 4 3]); %! assert (lab2uint8 (im2d), im2d_uint8) %! assert (lab2uint8 (imnd), permute (im2d_uint8, [1 4 3 2])) %! %! cm_uint16 = uint16 ([ %! 0 0 0 %! 65535 65535 65535 %! 65535 65535 65535 %! 65535 58468 58468 %! 65535 65535 65535 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 0 0 %! 0 1 1 %! 0 1 1 %! 0 256 256 %! 0 32512 32512 %! 0 32768 32768 %! 0 32768 32768 %! 1 32768 32768 %! 1 32768 32768 %! 653 33024 33024 %! 64627 58112 58112 %! 65280 58368 58368 %! 65535 58624 58624 %! 65535 65024 65024 %! 65535 65280 65280 %! 65535 65535 65535 %! 65535 65535 65535 %! 65535 65535 65535 %! 65535 65535 65535 %! 65535 65535 65535]); %! %! assert (lab2uint16 (cm), cm_uint16) %! im2d_uint16 = reshape (cm_uint16, [7 4 3]); %! assert (lab2uint16 (im2d), im2d_uint16) %! assert (lab2uint16 (imnd), permute (im2d_uint16, [1 4 3 2])) %! %! assert (lab2double (cm), double (cm)) %! assert (lab2double (im2d), double (im2d)) %! assert (lab2double (imnd), double (imnd)) image-2.16.1/inst/PaxHeaders.61586/imboxfilt.m0000644000000000000000000000006215005110255015555 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/imboxfilt.m0000644000175000017500000004172315005110255017162 0ustar00avinoamavinoam00000000000000## Copyright (C) 2023 ## Johannes Wirbser and ## Sarah Tiefert ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} @var{J} = imboxfilt(@var{img}) ## @deftypefnx {Function File} @var{J} = imboxfilt(@dots{_}, @var{fs}) ## @deftypefnx {Function File} @var{J} = imboxfilt(@dots{}, @var{name}, @var{value}, @dots{} ) ## Produces box filtering @var{J} of image @var{img}, is quicker than imfilter. ## ## Parameters: ## @table @samp ## @item @var{img} ## The image to be filtered. Must be a matrix of numeric values. ## @item @var{fs} ## Size for the filter matrix. ## Must be a positive, odd integer or 2-element vector of positive odd ## integers. If @var{fs} is scalar, a squared box filter is used. ## Default is 3x3. ## @item @var{name}, @var{value} ## Additional options, given as name-value pairs: ## @table @samp ## @item padding ## Determines how the image is padded. Value can be one of the following: ## @table @samp ## @item S ## Pads the image with the scalar value S. ## @item "replicate" (default) ## Pads the image with the border pixel value. ## @item "symmetric" ## Pads the image by mirroring it at the image border. ## @item "circular" ## Pads the image with pixel values from the opposite image border, ## essentially treating the image as periodic. ## @end table ## @item NormalizationFactor ## Normalization factor used, has to be a numeric scalar. ## Default is 1/(l*w), where l is the length and ## w is the width of the filter matrix. ## @end table ## @end table ## The computation is performed using double precision floating point number, ## but the class of the input image is preserved. ## The function uses convolution based filtering or filtering with the integral ## image, depending on an internal heuristic. ## ## @seealso{imfilter, imgaussfilt} ## @end deftypefn function filtered_img = imboxfilt (img, varargin) if (nargin < 1) print_usage (); endif if (islogical (img) || ! isimage (img) ) # test if img is a proper image error ("imboxfilt: img needs to be an image"); endif ## convert img into double and remember original type img_class = class (img); img = double (img); ## get optional inputs from varargin, or retun default values opt_arg = varargin; [filter_size, padding, normalization] = handle_optional_input (opt_arg); ## do the filtering ## determine which filtering method should be used. ## this is based on our internal testing of the algorithms if ((filter_size(1) + filter_size(2)) > 200) filtered_img = integral_image_filtering (img, filter_size, ... padding, normalization); else filtered_img = convolution_based_filtering (img, filter_size, ... padding, normalization); endif ## turn result back to original datatype filtered_img = cast (filtered_img, img_class); endfunction ## helper functions: function res = isodd (value) res = all ((mod (value, 2) == 1)); endfunction function [filter_size, padding, normalization] = handle_optional_input (opt_arg) num_opt_arg = length (opt_arg); # how many optional arguments? size_isgiven = isodd (num_opt_arg); # did the user input a size? nv_pair_isgiven = num_opt_arg > 1; # did the user input a name value pair? filter_size = get_filter_size (size_isgiven, opt_arg); [padding, normalization] = get_name_value_pairs (size_isgiven, ... nv_pair_isgiven, ... opt_arg, ... filter_size); endfunction function filter_size = get_filter_size (size_isgiven, opt_arg) filter_size = [3, 3]; if (size_isgiven) filter_size = opt_arg{1}; ## check for corectness of filter_size if (! isodd (filter_size) || ... # only allow odd numbers any (filter_size < 0) || ... # only allow positive numbers any (filter_size != round (filter_size))) # only allow integers error ("imboxfilt: fs has to be an odd, positive integer"); endif if (! isscalar (filter_size)) dim = size (filter_size); if ((dim(1) + dim(2)) > 3) # does the given vector have the right format? error (["imboxfilt: fs must be scalar or vector " ... "with two values"]); endif endif endif if (isscalar (filter_size)) filter_size = [filter_size, filter_size]; endif endfunction function [padding, normalization] = get_name_value_pairs (size_isgiven, ... nv_pair_isgiven, opt_arg, filter_size) padding_options = {"replicate", "circular", "symmetric"}; padding = "replicate"; normalization = 1/9; if (isscalar (filter_size)) normalization = 1 / (filter_size * filter_size); else normalization = 1 / (filter_size(1) * filter_size(2)); endif indexFirstName = 1 + double (size_isgiven); if (nv_pair_isgiven) ## check what kind of name value pairs are given and test input for idx = (indexFirstName:2:length (opt_arg)) name = opt_arg{idx}; value = opt_arg{idx+1}; if ! ischar (name) error ("imboxfilt: name must be string") endif if (strcmpi (name, "padding")) if ((! isnumeric(value) || ! isscalar(value)) ... && ! any (strcmpi (padding_options, value))) error (["imboxfilt: padding option must be a numeric scalar, ", ... "'replicate', 'circular' or 'symmetric'"]) endif padding = value; elseif(strcmpi (name, "normalizationFactor")) if (! isnumeric (value) || ! isscalar (value)) error ("imboxfilt: NormalizationFactor must be a numeric scalar") endif normalization = value; else error (["imboxfilt: cannot handle option '", name, "'"]); endif endfor endif endfunction function [filtered_img] = convolution_based_filtering (img, filter_size, ... padding, normalization) ## remember original image shape: img_shape = size (img); ## pad image padded_image = pad_image (img, filter_size, padding); ## create boxfilter filter1 = ones (filter_size(1), 1); filter2 = ones (1, filter_size(2)) * normalization; ## filter Image [image_rows, image_columns, image_channels, extra_dimensions] = size (img); ## change shape to 3 dim matrix for channels = (image_channels*extra_dimensions):-1:1 ## filter the Image with filter1 im1(:,:,channels) = conv2 (padded_image(:,:,channels), filter1, "valid"); ## filter the Image with filter2 filtered_img(:,:,channels) = conv2 (im1(:,:,channels), filter2, "valid"); endfor ## turn result back to original shape filtered_img = reshape (filtered_img, img_shape); endfunction function [filtered_img] = integral_image_filtering (img, filter_size, ... padding, normalization) ## filter the image by using the integral image method. ## the integral image is used to more easily calculate the mean of the image. ## to do so, we get subimages A, B, C and D from the integral image. ## mean of the image = (D + A - B - C) * normalization ## remember original image shape: img_shape = size(img); ## pad image padded_image = pad_image(img, filter_size, padding); ## determinen start position of Image D imgD_start_xaxis = (1+filter_size(1)); imgD_start_yaxis = (1+filter_size(2)); ## get size values to conserve original shape: [image_rows, image_columns, image_channels, extra_dimensions] = size (img); ## change shape to 3 dim matrix for channel = (image_channels*extra_dimensions):-1:1 int_img = integralImage (padded_image (:,:, channel)); [intimgRows, intimgColums] = size (int_img); ## get images A, B, C, and D imgD = int_img(imgD_start_xaxis:intimgRows, imgD_start_yaxis:intimgColums); imgA = int_img(1:size (img)(1), 1:size (img)(2)); imgB = int_img(1:size (img)(1), imgD_start_yaxis:intimgColums); imgC = int_img(imgD_start_xaxis:intimgRows, 1:size(img)(2)); ## calculate and normalize image filtered_img(:, :, channel) = (imgD + imgA - imgB - imgC) * normalization; endfor ## turn result back to original shape filtered_img = reshape (filtered_img, img_shape); endfunction function padded_image = pad_image(img, filter_size, padding) ## create padding if (isscalar(filter_size)) [padding_width, padding_hight] = deal (floor(filter_size/2)); filter_size = [filter_size, filter_size]; else padding_width = floor (filter_size(1)/2); padding_hight = floor (filter_size(2)/2); endif padded_image = padarray(img, [padding_width, padding_hight], padding); endfunction ## just img test, correct syntax %!assert (imboxfilt(ones (5) * 9)); %!assert (isa (imboxfilt (uint8 (ones (5))), "uint8")); %!assert (isa (imboxfilt (uint16 (ones (5))), "uint16")); %!assert (isa (imboxfilt (uint32 (ones (5))), "uint32")); %!assert (isa (imboxfilt (uint64 (ones (5))), "uint64")); %!assert (isa (imboxfilt (int8 (ones (5))), "int8")); %!assert (isa (imboxfilt (int16 (ones (5))), "int16")); %!assert (isa (imboxfilt (int32 (ones (5))), "int32")); %!assert (isa (imboxfilt (single (ones (5))), "single")); %!assert (isa (imboxfilt (double (ones (5))), "double")); ## illegal datatypes for img %!error (imboxfilt (true (5))); %!error (imboxfilt (5i+9)); %!error (imboxfilt ({"sdg","sdgsd"})); %!error (imboxfilt ("sdjgkhsdkl")); %!error (imboxfilt (struct("imgD_start_xaxis", "34", "imgD_start_yaxis", "67"))) ## just img test, illegal syntax %!error (imboxfilt ()); %!error (imboxfilt ("asdf")); ## tests for filter_size ## does the correct syntax work? %!test %! padded_img = ones (3); %! assert (imboxfilt (padded_img, 9)); %!test %! padded_img = ones (3); %! assert (imboxfilt (padded_img, [3, 7])); ## throw error if filter_size isn't an odd integer %!error (imboxfilt (ones (3), 2)); %!error (imboxfilt (ones (3), "asdf")); %!error (imboxfilt (ones (3), 2.4)); %!error (imboxfilt (ones (3), 3.5)); %!error (imboxfilt (ones (3), -3)); %!error (imboxfilt (ones (3), [3,-5])); %!error (imboxfilt (ones (3), [3.5, 3])); %!error (imboxfilt (ones (3), [3, 4])); %!error (imboxfilt (ones (3), [6, 11])); ## throw error if filter_size vector is too long: %!error (imboxfilt(ones (3), [3, 5, 7])); ## tests for padding, valid input %!assert (imboxfilt (ones (5), "padding", "circular")); %!assert (imboxfilt (ones (5), "padding", "symmetric")); %!assert (imboxfilt (ones (3), "padding", "replicate")); %!assert (imboxfilt (ones (3), "Padding", "Replicate")); %!assert (imboxfilt (ones (3), "Padding", 5)); ## tests for padding, invalid input %!error (imboxfilt (ones (3), "circular")); %!error (imboxfilt (ones (3), "symmetric", "padding")); %!error (imboxfilt (ones (3), "padding")); %!error (imboxfilt (ones (3), "padding", "ciircular")); %!error (imboxfilt (ones (3), "padding", [2, 3])); ## test for normalization, valid input ## simple input %!assert (imboxfilt (ones (5), "NormalizationFactor", 2)); %!assert (imboxfilt (ones (5), "normalizationfactor", 2.2)); %!assert (imboxfilt (ones (5), "Normalizationfactor", -1)); %!assert (imboxfilt (ones (5), "normalizationFactor", -1.5)); # input with filter_size: %!assert (imboxfilt (ones (5), 7, "NormalizationFactor", 3/9)); ## input with padding %!assert (imboxfilt (ones (5), "NormalizationFactor", 1 , "padding", "circular")); %!assert (imboxfilt (ones (5), "padding", "circular", "NormalizationFactor", 1)); ## input with everything %!assert (imboxfilt (ones (5), [5, 7], "NormalizationFactor", 1, "padding", "circular")); %!assert (imboxfilt (ones (5), [5, 7], "padding", "circular", "NormalizationFactor", 1)); ## normalization, invalid input options %!error (imboxfilt (ones (5), "n", 1)); %!error (imboxfilt (ones (5), "NormalizationFactor")); %!error (imboxfilt (ones (5), "Normalizastionfactor", 3)); %!error (imboxfilt (ones (5), "NormalizationFactor", [2, 4])); %!error (imboxfilt (ones (5), "Normalizationfactor", "asdf")); %!error (imboxfilt (ones (5), "normalizationFactor", "2")); %!error (imboxfilt (ones (5), "NormalizationFactor", 1 , "padding", "circular", "NormalizastionFactor", [2, 1])); %!error (imboxfilt (ones (5), "padding", "NormalizationFactor", 2, "circular", "NormalizastionFactor", 1)); %!error (imboxfilt (ones (5), "NormalizationFactor", [1, 4], "padding", "circular", "NormalizastionFactor", 2)); ## normalization, simple functionality: %!assert (imboxfilt (ones (5), "normalizationfactor", 1), ones (5) * 9); %!assert (imboxfilt (ones (5), "normalizationfactor", 2), ones (5) * 18); %!assert (imboxfilt (ones (5), "normalizationfactor", 100), ones (5) * 900); %!assert (imboxfilt (ones (5), "normalizationfactor", 0.1), ones (5) * 0.9, eps); %!assert (imboxfilt (ones (5), "normalizationfactor", 0), zeros (5)); ## tests for combinations of parameters %!assert (imboxfilt (ones (8))); %!assert (imboxfilt (ones (8), 3)); %!assert (imboxfilt (ones (8), "padding", "circular")); %!assert (imboxfilt (ones (8), 3, "padding", "circular")); %!assert (imboxfilt (ones (8), "NormalizationFactor", 5)); %!assert (imboxfilt (ones (8), 3, "NormalizationFactor", 5)); %!assert (imboxfilt (ones (8), "padding", "circular", "NormalizationFactor", 5)); %!assert (imboxfilt (ones (8), 3, "padding", "circular", "NormalizationFactor", 5)); %!error (imboxfilt (ones (8), 3, 3)); %!error (imboxfilt (ones (8), 3, 3, "padding", "circular")); ## functional tests: # does integral img filtering work? %!assert (imboxfilt(ones (200), [89, 121]), ones (200)) %!assert (imboxfilt(ones (200), 199), ones (200)) % does imboxfilt filter correctly? %!test %! input = ones (3) * 3; %! expected = input; %! output = imboxfilt (input); %! assert (output, expected); %!test %! input = [1, 1, 1; 10, 10, 10; 100, 100, 100]; %! expected= [37, 37, 37; 37, 37, 37; 37, 37, 37]; %! output = imboxfilt (input, 3, "padding", "circular"); %! assert (output, expected, eps); %!test %! input = [1, 1, 1; 10, 10, 10; 100, 100, 100]; %! expected = [4, 4, 4; 37, 37, 37; 70, 70, 70]; %! output = imboxfilt (input, 3, "padding", "replicate"); %! assert (output, expected, eps); %!test %! input = [1 1 1 1 1 1 %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 1 1 1 1 1]; %! expected = [1.16, 1.24, 1.32, 1.32, 1.24, 1.16; %! 1.24, 1.36, 1.48, 1.48, 1.36, 1.24; %! 1.32, 1.48, 1.64, 1.64, 1.48, 1.32; %! 1.32, 1.48, 1.64, 1.64, 1.48, 1.32; %! 1.24, 1.36, 1.48, 1.48, 1.36, 1.24; %! 1.16, 1.24, 1.32, 1.32, 1.24, 1.16]; %! output = imboxfilt (input, 5, "padding", "replicate"); %! assert (output, expected, eps); %!test %! input = [1 1 1 1 1 1 %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 2 2 2 2 1; %! 1 1 1 1 1 1]; %! expected = [1.36 1.36 1.48 1.48 1.36 1.36 %! 1.36 1.36 1.48 1.48 1.36 1.36 %! 1.48 1.48 1.64 1.64 1.48 1.48 %! 1.48 1.48 1.64 1.64 1.48 1.48 %! 1.36 1.36 1.48 1.48 1.36 1.36 %! 1.36 1.36 1.48 1.48 1.36 1.36]; %! output = imboxfilt (input, 5, "padding", "symmetric"); %! assert (output, expected, eps); ## correctly calculating the average? %!test %! input = [5 6 5 6 ; %! 6 5 6 5 ; %! 5 6 5 6 ; %! 6 5 6 5]; %! expected = [49/9, 49/9, 50/9, 50/9; %! 49/9, 49/9, 50/9, 50/9; %! 50/9, 50/9, 49/9, 49/9; %! 50/9, 50/9, 49/9, 49/9]; %! output = imboxfilt (input, 3); %! assert (imboxfilt (input, 3), expected, 0.0001); # sadly the test only works with this kind of tolerance ## test 3d-matrix as image %!test %! m2d = ones (8, 8); %! padded_img = cat (3,m2d * 5, m2d * 17, m2d * 29); %! expected = padded_img; %! output = imboxfilt (padded_img); %! assert (output, expected, eps); ## test for 4 dim image: %!test %! input = zeros (3, 3, 3, 3); %! input (2,2,:,:) = 9; %! expected = ones (3, 3, 3, 3); %! output = imboxfilt (input); %! assert (output, expected, eps); ## test for 4 dim image, channels are independent %!test %! a1 = ones (5); %! a2 = ones (5) * 2; %! a3 = ones (5) * 3; %! a4 = ones (5) * 4; %! padded_img = cat (4, a1, a2, a3, a4); %! expected = padded_img; %! output = imboxfilt (padded_img); %! assert (output, expected, eps); ## test asymmetrical filter %!test %! im = zeros (11); %! im(6, 6) = 1; %! out = imboxfilt(im, [3, 7]); %! assert (sum (out(6,:)), 1/3, eps); %! assert (sum (out(:,6)), 1/7, eps); image-2.16.1/inst/PaxHeaders.61586/normxcorr2.m0000644000000000000000000000006215005110255015673 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/inst/normxcorr2.m0000644000175000017500000001277615005110255017306 0ustar00avinoamavinoam00000000000000## Copyright (C) 2014 Benjamin Eltzner ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software ## Foundation; either version 3 of the License, or (at your option) any later ## version. ## ## This program is distributed in the hope that it will be useful, but WITHOUT ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## details. ## ## You should have received a copy of the GNU General Public License along with ## this program; if not, see . ## -*- texinfo -*- ## @deftypefn {Function File} {} normxcorr2 (@var{template}, @var{img}) ## Compute normalized cross-correlation. ## ## Returns the cross-correlation coefficient of matrices @var{template} ## and @var{img}, a matrix of (roughly) 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. ## ## @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 ## ## Despite the function name, this function will accept input with an arbitrary ## number of dimensions. ## ## Note that the size of the cross-correlation array is slightly bigger ## than @var{img} because the input image is padded during the calculation. ## ## @seealso{conv2, convn, corr2, xcorr, xcorr2} ## @end deftypefn ## Author: Benjamin Eltzner function c = normxcorr2 (a, b) if (nargin != 2) print_usage (); 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 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))); ## remove small machine precision errors after substraction b(b < 0) = 0; a = sumsq (a(:)); c = reshape (c ./ sqrt (b * a), size (c)); c(isinf (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*100); ## 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 normxcorr2 (rand (20), rand (5)); %!error normxcorr2 (rand (5)); %!error normxcorr2 (rand (5), rand (20), 2); ## test for no small imaginary parts in result (bug #46160) %!test %! a = [ 252 168 50 1 59; %! 114 0 0 0 0] ./ 255; %! b = [ 1 171 255 255 255 255 240 71 131 254 255 255 255; %! 0 109 254 255 255 233 59 0 131 254 255 255 255; %! 76 13 195 253 194 34 0 19 217 255 255 255 255; %! 110 0 0 0 0 0 3 181 255 255 255 255 255; %! 153 0 0 0 0 2 154 254 255 255 255 255 255]./255; %! c = normxcorr2 (a, b); %! assert (max (imag (c(:))), 0); ## test for inf results on image regions with constant value (bug #50122) %!test %! img = [1 1 1 0]; %! t = [1 1 0]; %! c = normxcorr2 (t, img); %! assert (c(3), 0) image-2.16.1/PaxHeaders.61586/COPYING0000644000000000000000000000006215005110255013460 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/COPYING0000644000175000017500000010451315005110255015062 0ustar00avinoamavinoam00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . image-2.16.1/PaxHeaders.61586/NEWS0000644000000000000000000000006215005110255013124 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/NEWS0000644000175000017500000011003015005110255014515 0ustar00avinoamavinoam00000000000000 Summary of important user-visible changes for image 2.16.1 (2025/05/04): ------------------------------------------------------------------------- ** image 2.16.1 is a patch release. ** The function std2 now checks to ensure inputs are numeric and converts image data to double before passing to std(). Previous versions of the function could return incorrect values for integer type image data due to integer underflow and/or rounding. Note that this conversion to double can run into accuracy limitations for 64-bit integer data types where the conversion can exceed the value of flintmax("double"). The function also now accepts complex data types (bug #67031). ** connectivity.cc: adding special treatment if ndims or numel equal 0 to avoid heap-buffer-overflow crash (bug #66918). ** Add 'reflect' padding option to imfilter (bug #52119). ** Fix typos in the documentation of edge.m, entropy.m entropyfilt.m, imboxfilt.m imagaussfilt.m & hough.m Summary of important user-visible changes for image 2.16.0 (2025/03/03): ------------------------------------------------------------------------- ** Octave version 7 is now the minimum requirement. ** The function bwselect is now MATLAB compatible. In version 2.10, the parameter connectivity was changed in function bwfill. This change affected bwselect, which uses bwfill, and made bwselect MATLAB incompatible. In current fix, the behavior of connectivity was fixed. To obtain the same results as in previous versions (from 2.10) use a value of 8 instead of 4, and a value of 4 instead of 8 (the default was 8). ** Input validation and error messaging has been improved for the imshear function. ** The following functions are new: imboxfilt imgaussfilt Summary of important user-visible changes for image 2.14.0 (2022/03/23): ------------------------------------------------------------------------- ** The following functions are new: affine affine2d affine3d imapplymatrix imfuse imshowpair ** New features "anti-aliasing" and "custom kernel" were added to imresize. new method "box" was also added. Inaccuracies in imresize bilinear interpolation and in bicubic interpolation were fixed, making imresize much more compatible with Matlab. This function now also accepts complex input images for Matlab compatibility. ** The function grayslice returns different results on input images of class int16. This improves Matlab compatibility. ** Other functions that have been changed for smaller bugfixes, increased Matlab compatibility, or performance: psf2otf imtransform ** Note: In Octave versions 5 and older the installation of this image package version will issue several "doc_cache_create" warnings, regarding affine.m, affine2d.m and affine3d.m. Nevertheless the image package will work fine on those systems, except for not showing the "help" text of the mentioned functions. Summary of important user-visible changes for image 2.12.0 (2020/01/30): ------------------------------------------------------------------------- ** The following functions are new: imref2d integralImage integralImage3 imref3d ** Improved the cubic interpolation method in imremap. This should also cause improved results in the functions imperspectivewarp, imresize, and imrotate when using the cubic or bicubic methods. ** The function labelmatrix was changed for compatibility with the upcoming Octave version 6. ** The function bwmorph now supports the "endpoints" operation. ** The function nonmax_supress was renamed nonmax_suppress to fix its incorrect spelling. The incorrectly named function is kept for backwards compatibility. ** The functions imremap, imperspectivewrap, and imrotate no longer return the `valid' output argument. ** The imgradientxy function will now recognize the methods "central" and "intermediate" for Matlab compatibility. Their previous names "centraldifference" and "intermediatedifference" continue to work for backwards compatibility. ** The stdfilt function will again handle all images of non floating type (a regression introduced in the version 2.10.0). Summary of important user-visible changes for image 2.10.0 (2018/12/26): ------------------------------------------------------------------------- ** The following functions are new: axes2pix colorangle houghpeaks bwpack deconvwnr otsuthresh bwunpack houghlines ** The functions entropyfilt, rangefilt, and stdfilt were completely incorrect and have now been fixed. ** In function regionprops, "Perimeter" was changed to be compatible with Matlab, and "PerimterOld" was added. ** In function regionprops, properties "ConvexHull", "ConvexImage", "ConvexArea", and "Solidity" were added. ** In function bwfill, the parameter connectivity was changed. In previous versions, the parameter referred to background connectivity but now refers to the foreground connectivity. This backwards incompatible changes was done for Matlab compatibility. Effectively, to obtain the same results as in previous versions use a value of 8 instead of 4, and a value of 4 instead of 8 (the default was 8). ** grayslice now expects argument V to be a vector with the actual values used to perform threshold. Previously it expected values in the [0 1] range which would be adjust to the correct type internally. This change was done for Matlab compatibility (which does the same despite their documentation stating otherwise). Other small changes were done to be Matlab compatible in multiple corner cases. ** The function operation "thin" in bwmorph was changed to be Matlab compatible. The previous implementation, based on the book by W. K. Pratt, is available under the name "thin-pratt". ** imcast now also converts images to logical type. ** wiener2 is now able to handle images with arbitrary number of dimensions. ** Other functions that have been changed for smaller bugfixes, increased Matlab compatibility, or performance: bwdist entropy hough_line bwmorph graythresh Summary of important user-visible changes for image 2.8.1 (2018/10/26): ------------------------------------------------------------------------- ** image 2.8.1 is a patch release. ** Fix imcrop for non-square images. This a regression introduced in version 2.8.0. Summary of important user-visible changes for image 2.8.0 (2018/06/19): ------------------------------------------------------------------------- ** The following functions are new: hough imhmax ntsc2rgb imextendedmax imhmin rgb2ntsc imextendedmin imimposemin viscircles imfindcircles imsharpen wiener2 ** Octave version 4.0 is no longer supported. This makes Octave version 4.2.0 the minimum requirement. ** The functions ntsc2rgb and rgb2ntsc have been removed in Octave core version 4.4 and are now part of the image package. In a similar manner, rgb2gray, which has always been part of the image package, is part of Octave core version 4.4. The versions in the image package will only be installed if they are missing from Octave to avoid shadowing the core functions. ** imcrop will now adjust the region to crop to the image boundaries when the region to crop goes beyond the image boundaries. ** ycbcr2rgb, rgb2ycbcr, and rgb2gray will return an image of class single if the input was class single (they were previously returning a double for such cases). ** The function imdither has been removed. This function hadn't work since Octave version 3.8.0 and would need to be rewritten. The function dither from Octave core should be used instead but hasn't been implemented yet. Instead of rewriting imdither, the missing dither function should be written. See bug #41768. ** graythresh will now clip values outside the [0 1] range in images of floating point class. A side effect of this is that it will no longer distinguish between an image of floating point class that has only one one row or column, and an histogram. Breaks backwards compatibility if you were previously relying on something like 'graythresh (img(:), ...)' and img is of class single or double. ** Fix regionprops Perimeter when using a labelled image or a bwconncomp structure, from connectivity different from 8. ** The functions ycbcr2rgb and rgb2ycbcr now support conversion according to the BT.2020 standard. ** Other functions that have been changed for smaller bugfixes, increased Matlab compatibility, or performance: bwperim imlincomb imrotate entropy imreconstruct normxcorr2 graythresh imregionalmax phantom imhist imregionalmin rgb2gray Summary of important user-visible changes for image 2.6.2 (2017/11/09): ------------------------------------------------------------------------- ** image 2.6.2 is a patch release. ** Fix regionprops MajorAxisLength, MinorAxisLength, and Orientation when there's multiple regions in an image; and Orientation for thin regions. ** Fix montage usage with a cell array of image filepaths. ** Fixed installation for upcoming versions of Octave. Enables installation in current development version 4.3.0+. Summary of important user-visible changes for image 2.6.1 (2016/10/21): ------------------------------------------------------------------------- ** image 2.6.1 is a patch release. ** Fix imtranslate regression when used with the "crop" option. ** Fix compilation issues for clang++ users on Mac OSX. Summary of important user-visible changes for image 2.6.0 (2016/10/05): ------------------------------------------------------------------------- ** The following functions are new: imfill lab2rgb rgb2lab imgetfile lab2single rgb2xyz impyramid lab2uint16 watershed imquantize lab2uint8 xyz2lab lab2double lab2xyz xyz2rgb ** For better compatibility with Matlab, the imtransform function now uses 0 instead of NA for the default extrapolation value. ** The regionprops function has been rewritten and should perform several orders of magnitude faster dependending on the number of properties being measured and the number of objects; the new implementation will be specially faster for a large number of objects and multiple properties. In addition to the increased performance, regions can be defined with a bwconncomp structure. The following properties have changed: * FilledArea - new support for ND images * FilledImage - new support for ND images * SubarrayIdx - new property implemented ** stretchlim() now properly supports integer input and will return values in the [0 1] range. It also no longer performs rounding of the saturated fraction, so that TOL is now the saturation limit (rather than a saturation that it tries to approximate). Support for N dimensional images has been added. ** imadjust() now supports images of integer class without requiring an intermediary conversion to a floating point class. Support for N dimensional images has also been added. It now requires that input and output limits be always specified in a [0 1] range, even if the image of a floating point class. ** The function is isgray(), isind(), and isrgb() will return true for images of class single in addition to double. ** im2bw() now accepts a string specifying a graythresh() algorithm as an alternative to a threshold value. This simplifies the very common usage of `im2bw (im, graythresh (im, method))`. ** Fix imcrop() usage when defaulting to current figure and allow the bounding box to be selected by clicking on any two opposite corners of the image. ** fspecial() is now capable to create N dimensional gaussian filters. ** checkerboard() is now capable to create N dimensional checkerboards. ** Other functions that have been changed for smaller bugfixes, increased Matlab compatibility, or performance: edge imdilate imremap grayslice imerode mat2gray im2bw imfilter montage im2col imresize normxcorr2 Summary of important user-visible changes for image 2.4.1 (2015/08/07): ------------------------------------------------------------------------- ** Image 2.4.1 is a bug fixing release. ** Fixed regression on bwhitmiss which was completely broken since version 2.2.0. ** Fixed regressions on rangefilt and stdfilt which made them always throw an error. ** Removed broken support for signed integers to entropyfilt(). Not only it was returning incorrect values and ocasional endless loops, it failed to build in some architectures. Summary of important user-visible changes for image 2.4.0 (2015/04/06): ------------------------------------------------------------------------- ** The following functions are new: bwareafilt imcast imregionalmin bwpropfilt imclearborder otf2psf edgetaper immse psf2otf fftconvn imreconstruct psnr imattributes imregionalmax subimage ** 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: grayslice im2bw ** For better compatibility with Matlab, the imrotate, imremap, and imperspectivewarp functions now use 0 instead of NA for the default extrapolation value. ** The regionprops function now supports the "Eccentricity", "MajorAxisLength", "MinorAxisLength", "EquivDiameter" and "Extrema" properties. The "Orientation" property has also been rewritten for Matlab compatibility and may yield different results than previous versions. ** The conndef function has new function signatures so that it covers all common ways of defining an connectivity array. The following will return the same matrix: mask = conndef (2, "minimal") mask = conndef (4) mask = conndef ([0 1 0; 1 1 1; 0 1 0]) and would throw a detailed error in case of an incorrect connectivity. ** Creating disk shaped strel objects must now specify the N argument for number of periodic lines used to approximate a disk. The value used must be zero to obtain the same results as previous releases of the image package. No other value is at the moment valid, and this is to prevent future backwards incompatibility since Matlab default is 4. Replace any `strel ("disk", radius)' with `strel ("disk", radius, 0)' ** The following functions have been completely rewritten and will perform a lot of faster. bwconncomp bwlabeln Which indirectly will also cause the following to perform faster: bwareaopen bwpropfilt regionprops ** 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: grayslice im2single imsmooth im2double im2uint8 label2rgb im2int16 im2uint16 Summary of important user-visible changes for image 2.2.2 (2014/10/06): ------------------------------------------------------------------------- ** Multiple documentation fixes for compatibility with new versions of Texinfo. ** Fix error with imcrop when image was all zeros. ** Fix endless loop in bwdist when using the quasi-euclidean method in x86 systems. Summary of important user-visible changes for image 2.2.1 (2014/03/08): ------------------------------------------------------------------------- ** imcrop had many alternative interfaces added for more flexibility. Added support in the input for indexed images, figures handles, N-dimensional images, and specific bounding box vector for a non-interactive usage. Output can now also return the bounding box used for the cropping in addition to the cropped image. It will no longer loop forever until it gets two valid coordinates for the bounding box. ** Fixed bug in imcomplement to compute the complement of signed integers correctly. ** Fix imrotate to handle RGB images. ** Fix regression in bwdist when calculating the closest pixel map. Summary of important user-visible changes for image 2.2.0 (2014/01/08): ------------------------------------------------------------------------- ** The imerode and imdilate have been completely rewritten for increased performance and many Matlab compatibility fixes. Performance gains between 1.5-30X have been demonstrated. Main compatibility changes include the addition of the shape option, support for the strel class, and allowing structuring elements and images of different classes. Non-flat grayscale erosion and dilation has also been implemented by making use of the new strel class. ** With the increased performance in imerode and imdilate, all other functions that use them, such imopen and imclose, get an equivalent performance boost. ** Most of bwmorph operations now support N dimensional images and have increased performance. Other Matlab compatibility fixes have been made such as displaying image when there's no output variable. ** The __spatial_filtering__ function has been mostly rewritten and performs in approximattely 1/5 to 2/5 of the previous time, depending on the filter. With this change, all functions dependent on it, rangefilt, entropyfilt, ordfilt2/n, medfilt2/n, and stdfilt, will also perform faster. ** The following functions are new: bwareaopen impixel strel checkerboard imtransform tformfwd cp2tform intlut tforminv findbounds labelmatrix ycbcr2rgb imgradient maketform imgradientxy montage ** The following functions have been moved from the Octave Forge Image package to GNU Octave core: cmpermute cmunique iscolormap rgbplot ** The following functions have been deprecated in the previous release of the Image package and have now been removed: blkproc bmpwrite dilate erode ** The following functions have been deprecated (see their help text for the recommended alternatives): bwborder imrotate_Fourier iptcheckstrs impad iptchecknargin uintlut ** The functions im2col and col2im has been completely rewritten for massive performace increase (increases greater than 500X have been observed, with biggest differences for smaller blocks and sliding option), and support of N-dimensional blocks and images. ** rgb2ycbcr was completely rewritten to accept images of other classes, and colormaps. A new argument was implemented to convert the RGB values according to different standards. ** The use of non logical matrices to specify the neighborhood for the medfilt2 function has been deprecated. Also, when using a vector to specify the size of the neighborhood, the elements were swapped (first element is now the number of rows and the second the number of columns). ** For consistency with other functions that allow specification of padding values, the function padarray now accepts the string "zeros" as a valid option. ** The plot produced by imhist is correctly scaled on the X axis so that the colorbar corresponds to the actual intensity of the stems; the given colormarp is used on the colorbar for indexed images; and the stems no longer display the markers at their top. The Y axis is also adjusted in case of peaks with high values that prevent a good overview of the histogram. ** The option to create poisson noise to an image has been added to imnoise. ** With the addition of the strel class, imdilate and imerode are able to handle strel objects. ** The performance of imresize has been greatly improved when using the nearest neighbor method for N or 1/N scale factors (e.g.: 2, 50, 1/4, 1/7). ** The imperspectivewarp, imremap, imresize, and imrotate functions will now accept any interpolation method from the interp2 function thus extending the available methods to "spline" and "pchip". This in addition to the "bilinear" and "bicubic" methods (same as "linear" and "cubic" respectively) which are kept for matlab compatibility. For the same reason, the "triangle" method (interpolation kernel) has also been added (which is the same as "linear" method). ** Bug fixes on the concavity, intermodes, maxlikelihood, and minimum methods of graythresh. ** The bwdist function will now consider any non zero value as object pixels, the class of the distance matrix has changed to single, and indexes an uint dependent on the matrix size. ** The transform option of imtophat has been removed (it was deprecated in version 2.0.0) in favour of using imbothat. ** The function bwconncomp now returns the indices for each element in each object, no longer the indices for the elements in the object boundaries only. The connectivity default was changed to 8. ** The original Shepp-Logan model in the function phantom as been changed to return all values in the range [0 1] rather than [0 2] by changing the intensity of the first ellipse from 2 to 1. ** Other functions that have been changed for smaller bugfixes, increased Matlab compatibility, or performance: bwlabel bwperim padarray ** The following functions now fully support matrices with an arbitrary number of dimensions: bestblk col2im im2col bwconncomp colfilt nlfilter Summary of important user-visible changes for image 2.0.0 (2012/11/08): ------------------------------------------------------------------------- ** The following functions are new: analyze75info imabsdiff iptcheckconn analyze75read imadd iptcheckmap analyze75write imbothat iptchecknargin blockproc imcrop iptcheckstrs bwlabeln imdivide iptnum2ordinal getrangefromclass imlincomb iscolormap im2int16 immultiply normxcorr2 im2single imsubtract wavelength2rgb ** The following functions have been deprecated in previous releases of the image package and have now been removed: imginfo ** The function `deriche' has been removed. ** The complete set of functions to work with Analyze 7.5 files has been implemented. See `analyze75info', `analyze75read' and `analyze75write'. ** `graythresh' can optionally accept an histogram rather than an image. This allows for preprocessing of the histogram previous to an automatic threshold selection. ** Otsu's method for automatic threshold selection (default for `graythresh') has been completely rewritten and should perform faster. Now, it can also return a second value representing the ``goodness'' of the computed threshold value (within class variance). ** Alternative algorithms for automatic threshold have been implemented in `graythresh' (thanks to Antti Niemistö for releasing HistThresh toolbox http://www.cs.tut.fi/~ant/histthresh/ from where many were ported, under a GPL license). Currently, the following algorithms have been implemented (see graythresh for notes and references): concavity MaxEntropy minimum Otsu intermeans MaxLikelihood MinError percentile intermodes mean moments ** The following functions have been deprecated (see their help text for the recommended alternatives): blkproc bmpwrite dilate erode ** With the new function `imbothat' the transform option of `imtophat' has been deprecated. ** The following functions have had been changed for bug fixes and/or improved matlab compatibility bwarea imhist im2uint8 isind bweuler imnoise im2uint16 mat2gray bwfill conndef isbw rgb2gray cmpermute im2bw isgray cmunique im2double isrgb ** `bwarea' now supports all image classes and considers objects all non zero pixels (not all pixels higher than zero). ** `rgb2gray' now also supports images of the class single and performs a weighted conversion to keep the image luminance instead of a the mean through each color. ** `im2bw' now supports input images of the int16 class and deals better with RGB images since it uses `rgb2gray' internally (see changes to rgb2gray). Threshold is performed on all values greater than value instead of greater than or equal. ** `imhist' is much more compatible with matlab and among other changes, it now uses the whole range of the class for the histogram rather than the minimum and maximum of the input image and displays a colorbar under the histogram. ** `isbw' now defines a black-and-white image as a binary non-sparse matrix. This is compatible with matlab. To use the old behaviour, use the new option for the call "isbw (img, "non-logical"). For backwards compatibility, if a non-logical matrix of 0 and 1 is used as input, `isbw' will still return true but a warning will be issued since this will deprecated later. ** `isgray' now also returns true for matrices of the int16 class. ** `isrgb' now returns false for logical matrix. ** `tiff_tag_read' had several bug fixes and can now check IFDs beyond the first. It can also accept mutiple tag values and IFDs simultaneously and return a matrix of the values found. Its documentation has been expanded (as well as an explanation of TIFF structure on the source) ** For sake of matlab compatibility, the behaviour of `mat2gray' has been greatly changed. Among the changes, it will no longer swap the minimum and maximum options if the first is larger than the later. Instead, will return the image complement after truncation. Also, when the maximum and minimum values are equal, `mat2gray' will truncate all values between 0 and 1. See the help text (or source) for a detailed description of cautions. ** `bwfill' was fixed to always returns a logical matrix. ** `imnoise' has been expanded to accept images of differente classes instead of only double and single. ** The private function `__bwdist` has been renamed `__bwdist__` ** Package is now dependent on GNU Octave version 3.6.0 or later. ** Package is now dependent on the signal package version 1.2.0 or later. ** Package is no longer automatically loaded. ******************************************************************************** ** ** ** NEWS below this point were written after their releases for history ** ** purposes and extracted from the Octave Forge general NEWS. Previous to ** ** the image package version 1.0.11, all Octave Forge packages would be ** ** released at the same time. Previous to the package version 1.0.0 there ** ** were monolithic releases with no actual packages. This means that some ** ** releases actually had no changes in the image package itself or changes ** ** were small compared to the whole Octave Forge project and so, not ** ** mentioned on the NEWS file. Inspection of the actual log in the ** ** repository should be used if exact details are required. ** ** ** ******************************************************************************** Summary of important user-visible changes for image 1.0.15 (2011/09/21): -------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.14 (2011/04/12): -------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.13 (2010/05/22): -------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.12 (2010/03/22): -------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.11 (2010/03/05): -------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.10 (2009/06/07): -------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.9 (2009/05/08): ------------------------------------------------------------------------- ** The following functions are new: entropyfilt ordfiltn rangefilt stdfilt ** The following functions have been removed as they are now part of Octave core: imread imwrite Summary of important user-visible changes for image 1.0.8 (2008/08/31): ------------------------------------------------------------------------- ** Fix build issues with the last release. Summary of important user-visible changes for image 1.0.7 (2008/08/24): ------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.6 (2008/04/29): ------------------------------------------------------------------------- ** The following functions are new: imcomplement rgbplot ** Implemented support for bilateral filtering. ** Build fixes for new versions of ImageMagick. Summary of important user-visible changes for image 1.0.5 (2008/02/16): ------------------------------------------------------------------------- ** The following functions are new: imfilter imsmooth Summary of important user-visible changes for image 1.0.4 (2007/12/12): ------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.3 (2007/10/14): ------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.2 (2007/07/26): ------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.1 (2007/05/26): ------------------------------------------------------------------------- Summary of important user-visible changes for image 1.0.0 (2007/03/28): ------------------------------------------------------------------------- ** First non-monolithic release. ** The following functions are new: __bwarea fspecial impersepectivewap apply graythresh imremap bwarea im2double label2rgb bwperim im2uint8 __magick_read__ deriche im2uint16 ** Fixex for non 8bit images. ** Quantum sizes in imagemagick. ** Compatiability changes to imwrite, isgray and rgb2gray. ** imread, probe depth from bits rather than Red field, allows loading of gray scale images. ** Convert all functions to use texinfo help. Summary of important user-visible changes for image (Octave Forge 2006.07.09): -------------------------------------------------------------------------------- Summary of important user-visible changes for image (Octave Forge 2006.03.17): -------------------------------------------------------------------------------- Summary of important user-visible changes for image (Octave Forge 2006.01.28): -------------------------------------------------------------------------------- ** imread() now return the appropriate numeric class. Colour images are of size MxNx3, gray images MxN. Summary of important user-visible changes for image (Octave Forge 2005.06.13): -------------------------------------------------------------------------------- ** The following functions are new: bwarea imresize Summary of important user-visible changes for image (Octave Forge 2004.11.16): -------------------------------------------------------------------------------- ** No important changes to the image package in this Octave Forge release. Summary of important user-visible changes for image (Octave Forge 2004.09.09): -------------------------------------------------------------------------------- ** The following functions are new: applylut cmunique houghtf poly2mask uintlut bestblk col2im im2col qtdecomp blkproc conndef isrgb qtgetblk bweuler dilate makelut qtsetblk bwmorph erode nlfilter roicolor cmpermute graycomatrix padarray stretchlim ** Implemented initial support for int* types. Summary of important user-visible changes for image (Octave Forge 2004.07.07): -------------------------------------------------------------------------------- ** No important changes to the image package in this Octave Forge release. Summary of important user-visible changes for image (Octave Forge 2004.02.12): -------------------------------------------------------------------------------- ** `imread' now supports 16-bit grayscale images. Summary of important user-visible changes for image (Octave Forge 2003.06.02): -------------------------------------------------------------------------------- ** The following functions are new: rotate_scale Summary of important user-visible changes for image (Octave Forge 2003.02.22): -------------------------------------------------------------------------------- ** No important changes to the image package in this Octave Forge release. Summary of important user-visible changes for image (Octave Forge 2002.11.30): -------------------------------------------------------------------------------- ** The following functions are new: colfilt imginfo imrotate imshear imtranslate ** The `colorgradient' function now allow instantaneous transitions (weight 0) ** The `bwlabel' function has been implemented in C++ and may behave different. Summary of important user-visible changes for image (Octave Forge 2002.05.09): -------------------------------------------------------------------------------- Summary of important user-visible changes for image (Octave Forge 2002.04.20): -------------------------------------------------------------------------------- Summary of important user-visible changes for image (Octave Forge 2002.03.10): -------------------------------------------------------------------------------- Summary of important user-visible changes for image (Octave Forge 2001.11.02): -------------------------------------------------------------------------------- ** First release. image-2.16.1/PaxHeaders.61586/io.sourceforge.octave.image.metainfo.xml0000644000000000000000000000006215005110255022242 xustar0020 atime=1746178221 30 ctime=1746179027.278726152 image-2.16.1/io.sourceforge.octave.image.metainfo.xml0000644000175000017500000000303215005110255023636 0ustar00avinoamavinoam00000000000000 io.sourceforge.octave.image org.octave.Octave Image

Image processing, feature extraction, transformations, morphological operations, filters, and more

Provides functions for processing images, such as feature extraction, image statistics, spatial and geometric transformations, morphological operations, linear filtering, and much more.

image processing feature extraction spatial transform geometric transform morphological operation linear filter convolution bwregion regionprops http://octave.sourceforge.net/image https://savannah.gnu.org/bugs/?func=additem&group=octave GPL-3.0+ Octave-Forge Community octave-maintainers@gnu.org FSFAP